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 23 BackgroundPrintingManager::BackgroundPrintingManager() { 24 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 25 } 26 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 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 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 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 109 void BackgroundPrintingManager::OnPrintJobReleased( 110 WebContents* preview_contents) { 111 DeletePreviewContents(preview_contents); 112 } 113 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 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 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 172 BackgroundPrintingManager::begin() { 173 return printing_contents_set_.begin(); 174 } 175 176 BackgroundPrintingManager::WebContentsSet::const_iterator 177 BackgroundPrintingManager::end() { 178 return printing_contents_set_.end(); 179 } 180 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