普通文本  |  188行  |  7.03 KB

// 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