• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using content::BrowserThread;
19 using content::WebContents;
20 
21 namespace printing {
22 
BackgroundPrintingManager()23 BackgroundPrintingManager::BackgroundPrintingManager() {
24   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
25 }
26 
~BackgroundPrintingManager()27 BackgroundPrintingManager::~BackgroundPrintingManager() {
28   DCHECK(CalledOnValidThread());
29   // The might be some WebContentses still in |printing_contents_set_| at this
30   // point. E.g. when the last remaining tab closes and there is still a print
31   // preview WebContents trying to print. In which case it will fail to print.
32   // TODO(thestig): Handle this case better.
33 }
34 
OwnPrintPreviewDialog(WebContents * preview_dialog)35 void BackgroundPrintingManager::OwnPrintPreviewDialog(
36     WebContents* preview_dialog) {
37   DCHECK(CalledOnValidThread());
38   DCHECK(PrintPreviewDialogController::IsPrintPreviewDialog(preview_dialog));
39   CHECK(!HasPrintPreviewDialog(preview_dialog));
40 
41   printing_contents_set_.insert(preview_dialog);
42 
43   content::Source<WebContents> preview_source(preview_dialog);
44   registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED, preview_source);
45 
46   // OwnInitiatorWebContents() may have already added this notification.
47   if (!registrar_.IsRegistered(this,
48       content::NOTIFICATION_WEB_CONTENTS_DESTROYED, preview_source)) {
49     registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
50                    preview_source);
51   }
52 
53   // If a WebContents that is printing crashes, the user cannot destroy it since
54   // it is hidden. Thus listen for crashes here and delete it.
55   //
56   // Multiple sites may share the same RenderProcessHost, so check if this
57   // notification has already been added.
58   content::Source<content::RenderProcessHost> rph_source(
59       preview_dialog->GetRenderProcessHost());
60   if (!registrar_.IsRegistered(this,
61       content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
62     registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
63                    rph_source);
64   }
65 
66   // Activate the initiator.
67   PrintPreviewDialogController* dialog_controller =
68       PrintPreviewDialogController::GetInstance();
69   if (!dialog_controller)
70     return;
71   WebContents* initiator = dialog_controller->GetInitiator(preview_dialog);
72   if (!initiator)
73     return;
74   initiator->GetDelegate()->ActivateContents(initiator);
75 }
76 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)77 void BackgroundPrintingManager::Observe(
78     int type,
79     const content::NotificationSource& source,
80     const content::NotificationDetails& details) {
81   if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
82     OnRendererProcessClosed(
83         content::Source<content::RenderProcessHost>(source).ptr());
84   } else if (type == chrome::NOTIFICATION_PRINT_JOB_RELEASED) {
85     OnPrintJobReleased(content::Source<WebContents>(source).ptr());
86   } else {
87     DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
88     OnWebContentsDestroyed(content::Source<WebContents>(source).ptr());
89   }
90 }
91 
OnRendererProcessClosed(content::RenderProcessHost * rph)92 void BackgroundPrintingManager::OnRendererProcessClosed(
93     content::RenderProcessHost* rph) {
94   WebContentsSet printing_contents_pending_deletion_set;
95   WebContentsSet::const_iterator it;
96   for (it = begin(); it != end(); ++it) {
97     WebContents* preview_contents = *it;
98     if (preview_contents->GetRenderProcessHost() == rph) {
99       printing_contents_pending_deletion_set.insert(preview_contents);
100     }
101   }
102   for (it = printing_contents_pending_deletion_set.begin();
103        it != printing_contents_pending_deletion_set.end();
104        ++it) {
105     DeletePreviewContents(*it);
106   }
107 }
108 
OnPrintJobReleased(WebContents * preview_contents)109 void BackgroundPrintingManager::OnPrintJobReleased(
110     WebContents* preview_contents) {
111   DeletePreviewContents(preview_contents);
112 }
113 
OnWebContentsDestroyed(WebContents * preview_contents)114 void BackgroundPrintingManager::OnWebContentsDestroyed(
115     WebContents* preview_contents) {
116   // Always need to remove this notification since the WebContents is gone.
117   content::Source<WebContents> preview_source(preview_contents);
118   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
119                     preview_source);
120 
121   if (!HasPrintPreviewDialog(preview_contents)) {
122     NOTREACHED();
123     return;
124   }
125 
126   // Remove NOTIFICATION_RENDERER_PROCESS_CLOSED if |preview_contents| is the
127   // last WebContents associated with |rph|.
128   bool shared_rph =
129       (HasSharedRenderProcessHost(printing_contents_set_, preview_contents) ||
130        HasSharedRenderProcessHost(printing_contents_pending_deletion_set_,
131                                   preview_contents));
132   if (!shared_rph) {
133     content::RenderProcessHost* rph = preview_contents->GetRenderProcessHost();
134     registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
135                       content::Source<content::RenderProcessHost>(rph));
136   }
137 
138   // Remove other notifications and remove the WebContents from its
139   // WebContentsSet.
140   if (printing_contents_set_.erase(preview_contents) == 1) {
141     registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED,
142                       preview_source);
143   } else {
144     // DeletePreviewContents already deleted the notification.
145     printing_contents_pending_deletion_set_.erase(preview_contents);
146   }
147 }
148 
DeletePreviewContents(WebContents * preview_contents)149 void BackgroundPrintingManager::DeletePreviewContents(
150     WebContents* preview_contents) {
151   registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED,
152                     content::Source<WebContents>(preview_contents));
153   printing_contents_set_.erase(preview_contents);
154   printing_contents_pending_deletion_set_.insert(preview_contents);
155   base::MessageLoop::current()->DeleteSoon(FROM_HERE, preview_contents);
156 }
157 
HasSharedRenderProcessHost(const WebContentsSet & set,WebContents * preview_contents)158 bool BackgroundPrintingManager::HasSharedRenderProcessHost(
159     const WebContentsSet& set, WebContents* preview_contents) {
160   content::RenderProcessHost* rph = preview_contents->GetRenderProcessHost();
161   for (WebContentsSet::const_iterator it = set.begin(); it != set.end(); ++it) {
162     WebContents* iter_contents = *it;
163     if (iter_contents == preview_contents)
164       continue;
165     if (iter_contents->GetRenderProcessHost() == rph)
166       return true;
167   }
168   return false;
169 }
170 
171 BackgroundPrintingManager::WebContentsSet::const_iterator
begin()172     BackgroundPrintingManager::begin() {
173   return printing_contents_set_.begin();
174 }
175 
176 BackgroundPrintingManager::WebContentsSet::const_iterator
end()177     BackgroundPrintingManager::end() {
178   return printing_contents_set_.end();
179 }
180 
HasPrintPreviewDialog(WebContents * preview_dialog)181 bool BackgroundPrintingManager::HasPrintPreviewDialog(
182     WebContents* preview_dialog) {
183   return (ContainsKey(printing_contents_set_, preview_dialog) ||
184           ContainsKey(printing_contents_pending_deletion_set_, preview_dialog));
185 }
186 
187 }  // namespace printing
188