#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
using content::BrowserThread;
using content::WebContents;
namespace printing {
+class BackgroundPrintingManager::Observer
+ : public content::WebContentsObserver {
+ public:
+ Observer(BackgroundPrintingManager* manager, WebContents* web_contents);
+
+ private:
+ virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
+ virtual void WebContentsDestroyed() OVERRIDE;
+
+ BackgroundPrintingManager* manager_;
+};
+
+BackgroundPrintingManager::Observer::Observer(
+ BackgroundPrintingManager* manager, WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ manager_(manager) {
+}
+
+void BackgroundPrintingManager::Observer::RenderProcessGone(
+ base::TerminationStatus status) {
+ manager_->DeletePreviewContents(web_contents());
+}
+void BackgroundPrintingManager::Observer::WebContentsDestroyed() {
+ manager_->DeletePreviewContents(web_contents());
+}
+
BackgroundPrintingManager::BackgroundPrintingManager() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(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.
+ // The might be some WebContentses still in |printing_contents_map_| at this
+ // point (e.g. when the last remaining tab closes and there is still a print
+ // preview WebContents trying to print). In such a case it will fail to print,
+ // but we should at least clean up the observers.
// TODO(thestig): Handle this case better.
+ STLDeleteValues(&printing_contents_map_);
}
void BackgroundPrintingManager::OwnPrintPreviewDialog(
DCHECK(PrintPreviewDialogController::IsPrintPreviewDialog(preview_dialog));
CHECK(!HasPrintPreviewDialog(preview_dialog));
- printing_contents_set_.insert(preview_dialog);
+ printing_contents_map_[preview_dialog] = new Observer(this, 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);
- }
+ // Watch for print jobs finishing. Everything else is watched for by the
+ // Observer. TODO(avi, cait): finish the job of removing this last
+ // notification.
+ registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_RELEASED,
+ content::Source<WebContents>(preview_dialog));
// Activate the initiator.
PrintPreviewDialogController* dialog_controller =
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);
- }
+ DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_RELEASED, type);
+ DeletePreviewContents(content::Source<WebContents>(source).ptr());
}
-void BackgroundPrintingManager::OnPrintJobReleased(
- WebContents* preview_contents) {
- DeletePreviewContents(preview_contents);
-}
-
-void BackgroundPrintingManager::OnWebContentsDestroyed(
+void BackgroundPrintingManager::DeletePreviewContents(
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();
+ WebContentsObserverMap::iterator i =
+ printing_contents_map_.find(preview_contents);
+ if (i == printing_contents_map_.end()) {
+ // Everyone is racing to be the first to delete the |preview_contents|. If
+ // this case is hit, someone else won the race, so there is no need to
+ // continue. <http://crbug.com/100806>
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) {
+ // Stop all observation ...
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);
+ Observer* observer = i->second;
+ printing_contents_map_.erase(i);
+ delete observer;
+
+ // ... and mortally wound the contents. (Deletion immediately is not a good
+ // idea in case this was called from RenderViewGone.)
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;
+std::set<content::WebContents*> BackgroundPrintingManager::CurrentContentSet() {
+ std::set<content::WebContents*> result;
+ for (WebContentsObserverMap::iterator i = printing_contents_map_.begin();
+ i != printing_contents_map_.end(); ++i) {
+ result.insert(i->first);
}
- return false;
-}
-
-BackgroundPrintingManager::WebContentsSet::const_iterator
- BackgroundPrintingManager::begin() {
- return printing_contents_set_.begin();
-}
-
-BackgroundPrintingManager::WebContentsSet::const_iterator
- BackgroundPrintingManager::end() {
- return printing_contents_set_.end();
+ return result;
}
bool BackgroundPrintingManager::HasPrintPreviewDialog(
WebContents* preview_dialog) {
- return (ContainsKey(printing_contents_set_, preview_dialog) ||
- ContainsKey(printing_contents_pending_deletion_set_, preview_dialog));
+ return ContainsKey(printing_contents_map_, preview_dialog);
}
} // namespace printing