blob: f0aa96a0022fe09eb4226bdc1d77a630dac23c66 [file] [log] [blame]
// 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<WebContents> 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<content::RenderProcessHost> 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<content::RenderProcessHost>(source).ptr());
} else if (type == chrome::NOTIFICATION_PRINT_JOB_RELEASED) {
OnPrintJobReleased(content::Source<WebContents>(source).ptr());
} else {
DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DESTROYED, type);
OnWebContentsDestroyed(content::Source<WebContents>(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<WebContents> 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<content::RenderProcessHost>(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<WebContents>(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