// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/printing/background_printing_manager.h" #include "base/stl_util.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/printing/print_job.h" #include "chrome/browser/printing/print_preview_dialog_controller.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" using content::BrowserThread; using content::WebContents; namespace printing { BackgroundPrintingManager::BackgroundPrintingManager() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } BackgroundPrintingManager::~BackgroundPrintingManager() { DCHECK(CalledOnValidThread()); // The might be some WebContentses still in |printing_contents_set_| at this // point. E.g. when the last remaining tab closes and there is still a print // preview WebContents trying to print. In which case it will fail to print. // TODO(thestig): Handle this case better. } void BackgroundPrintingManager::OwnPrintPreviewDialog( WebContents* preview_dialog) { DCHECK(CalledOnValidThread()); DCHECK(PrintPreviewDialogController::IsPrintPreviewDialog(preview_dialog)); CHECK(!HasPrintPreviewDialog(preview_dialog)); printing_contents_set_.insert(preview_dialog); content::Source preview_source(preview_dialog); registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED, preview_source); // OwnInitiatorWebContents() may have already added this notification. if (!registrar_.IsRegistered(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, preview_source)) { registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, preview_source); } // If a WebContents that is printing crashes, the user cannot destroy it since // it is hidden. Thus listen for crashes here and delete it. // // Multiple sites may share the same RenderProcessHost, so check if this // notification has already been added. content::Source rph_source( preview_dialog->GetRenderProcessHost()); if (!registrar_.IsRegistered(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) { registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source); } // Activate the initiator. PrintPreviewDialogController* dialog_controller = PrintPreviewDialogController::GetInstance(); if (!dialog_controller) return; WebContents* initiator = dialog_controller->GetInitiator(preview_dialog); if (!initiator) return; initiator->GetDelegate()->ActivateContents(initiator); } void BackgroundPrintingManager::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) { OnRendererProcessClosed( content::Source(source).ptr()); } else if (type == chrome::NOTIFICATION_PRINT_JOB_RELEASED) { OnPrintJobReleased(content::Source(source).ptr()); } else { DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type); OnWebContentsDestroyed(content::Source(source).ptr()); } } void BackgroundPrintingManager::OnRendererProcessClosed( content::RenderProcessHost* rph) { WebContentsSet printing_contents_pending_deletion_set; WebContentsSet::const_iterator it; for (it = begin(); it != end(); ++it) { WebContents* preview_contents = *it; if (preview_contents->GetRenderProcessHost() == rph) { printing_contents_pending_deletion_set.insert(preview_contents); } } for (it = printing_contents_pending_deletion_set.begin(); it != printing_contents_pending_deletion_set.end(); ++it) { DeletePreviewContents(*it); } } void BackgroundPrintingManager::OnPrintJobReleased( WebContents* preview_contents) { DeletePreviewContents(preview_contents); } void BackgroundPrintingManager::OnWebContentsDestroyed( WebContents* preview_contents) { // Always need to remove this notification since the WebContents is gone. content::Source preview_source(preview_contents); registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, preview_source); if (!HasPrintPreviewDialog(preview_contents)) { NOTREACHED(); return; } // Remove NOTIFICATION_RENDERER_PROCESS_CLOSED if |preview_contents| is the // last WebContents associated with |rph|. bool shared_rph = (HasSharedRenderProcessHost(printing_contents_set_, preview_contents) || HasSharedRenderProcessHost(printing_contents_pending_deletion_set_, preview_contents)); if (!shared_rph) { content::RenderProcessHost* rph = preview_contents->GetRenderProcessHost(); registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, content::Source(rph)); } // Remove other notifications and remove the WebContents from its // WebContentsSet. if (printing_contents_set_.erase(preview_contents) == 1) { registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED, preview_source); } else { // DeletePreviewContents already deleted the notification. printing_contents_pending_deletion_set_.erase(preview_contents); } } void BackgroundPrintingManager::DeletePreviewContents( WebContents* preview_contents) { registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED, content::Source(preview_contents)); printing_contents_set_.erase(preview_contents); printing_contents_pending_deletion_set_.insert(preview_contents); base::MessageLoop::current()->DeleteSoon(FROM_HERE, preview_contents); } bool BackgroundPrintingManager::HasSharedRenderProcessHost( const WebContentsSet& set, WebContents* preview_contents) { content::RenderProcessHost* rph = preview_contents->GetRenderProcessHost(); for (WebContentsSet::const_iterator it = set.begin(); it != set.end(); ++it) { WebContents* iter_contents = *it; if (iter_contents == preview_contents) continue; if (iter_contents->GetRenderProcessHost() == rph) return true; } return false; } BackgroundPrintingManager::WebContentsSet::const_iterator BackgroundPrintingManager::begin() { return printing_contents_set_.begin(); } BackgroundPrintingManager::WebContentsSet::const_iterator BackgroundPrintingManager::end() { return printing_contents_set_.end(); } bool BackgroundPrintingManager::HasPrintPreviewDialog( WebContents* preview_dialog) { return (ContainsKey(printing_contents_set_, preview_dialog) || ContainsKey(printing_contents_pending_deletion_set_, preview_dialog)); } } // namespace printing