1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/printing/background_printing_manager.h"
6
7#include "base/stl_util.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/printing/print_job.h"
10#include "chrome/browser/printing/print_preview_dialog_controller.h"
11#include "content/public/browser/browser_thread.h"
12#include "content/public/browser/notification_details.h"
13#include "content/public/browser/notification_source.h"
14#include "content/public/browser/render_view_host.h"
15#include "content/public/browser/web_contents.h"
16#include "content/public/browser/web_contents_delegate.h"
17#include "content/public/browser/web_contents_observer.h"
18
19using content::BrowserThread;
20using content::WebContents;
21
22namespace printing {
23
24class BackgroundPrintingManager::Observer
25    : public content::WebContentsObserver {
26 public:
27  Observer(BackgroundPrintingManager* manager, WebContents* web_contents);
28
29 private:
30  virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
31  virtual void WebContentsDestroyed() OVERRIDE;
32
33  BackgroundPrintingManager* manager_;
34};
35
36BackgroundPrintingManager::Observer::Observer(
37    BackgroundPrintingManager* manager, WebContents* web_contents)
38    : content::WebContentsObserver(web_contents),
39      manager_(manager) {
40}
41
42void BackgroundPrintingManager::Observer::RenderProcessGone(
43    base::TerminationStatus status) {
44  manager_->DeletePreviewContents(web_contents());
45}
46void BackgroundPrintingManager::Observer::WebContentsDestroyed() {
47  manager_->DeletePreviewContents(web_contents());
48}
49
50BackgroundPrintingManager::BackgroundPrintingManager() {
51  DCHECK_CURRENTLY_ON(BrowserThread::UI);
52}
53
54BackgroundPrintingManager::~BackgroundPrintingManager() {
55  DCHECK(CalledOnValidThread());
56  // The might be some WebContentses still in |printing_contents_map_| at this
57  // point (e.g. when the last remaining tab closes and there is still a print
58  // preview WebContents trying to print). In such a case it will fail to print,
59  // but we should at least clean up the observers.
60  // TODO(thestig): Handle this case better.
61  STLDeleteValues(&printing_contents_map_);
62}
63
64void BackgroundPrintingManager::OwnPrintPreviewDialog(
65    WebContents* preview_dialog) {
66  DCHECK(CalledOnValidThread());
67  DCHECK(PrintPreviewDialogController::IsPrintPreviewDialog(preview_dialog));
68  CHECK(!HasPrintPreviewDialog(preview_dialog));
69
70  printing_contents_map_[preview_dialog] = new Observer(this, preview_dialog);
71
72  // Watch for print jobs finishing. Everything else is watched for by the
73  // Observer. TODO(avi, cait): finish the job of removing this last
74  // notification.
75  registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED,
76                 content::Source<WebContents>(preview_dialog));
77
78  // Activate the initiator.
79  PrintPreviewDialogController* dialog_controller =
80      PrintPreviewDialogController::GetInstance();
81  if (!dialog_controller)
82    return;
83  WebContents* initiator = dialog_controller->GetInitiator(preview_dialog);
84  if (!initiator)
85    return;
86  initiator->GetDelegate()->ActivateContents(initiator);
87}
88
89void BackgroundPrintingManager::Observe(
90    int type,
91    const content::NotificationSource& source,
92    const content::NotificationDetails& details) {
93  DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_RELEASED, type);
94  DeletePreviewContents(content::Source<WebContents>(source).ptr());
95}
96
97void BackgroundPrintingManager::DeletePreviewContents(
98    WebContents* preview_contents) {
99  WebContentsObserverMap::iterator i =
100      printing_contents_map_.find(preview_contents);
101  if (i == printing_contents_map_.end()) {
102    // Everyone is racing to be the first to delete the |preview_contents|. If
103    // this case is hit, someone else won the race, so there is no need to
104    // continue. <http://crbug.com/100806>
105    return;
106  }
107
108  // Stop all observation ...
109  registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED,
110                    content::Source<WebContents>(preview_contents));
111  Observer* observer = i->second;
112  printing_contents_map_.erase(i);
113  delete observer;
114
115  // ... and mortally wound the contents. (Deletion immediately is not a good
116  // idea in case this was called from RenderViewGone.)
117  base::MessageLoop::current()->DeleteSoon(FROM_HERE, preview_contents);
118}
119
120std::set<content::WebContents*> BackgroundPrintingManager::CurrentContentSet() {
121  std::set<content::WebContents*> result;
122  for (WebContentsObserverMap::iterator i = printing_contents_map_.begin();
123       i != printing_contents_map_.end(); ++i) {
124    result.insert(i->first);
125  }
126  return result;
127}
128
129bool BackgroundPrintingManager::HasPrintPreviewDialog(
130    WebContents* preview_dialog) {
131  return ContainsKey(printing_contents_map_, preview_dialog);
132}
133
134}  // namespace printing
135