Home | History | Annotate | Download | only in printing
      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