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.
5 #include "chrome/browser/printing/print_preview_dialog_controller.h"
11 #include "base/auto_reset.h"
12 #include "base/path_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
17 #include "chrome/browser/printing/print_view_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_finder.h"
21 #include "chrome/browser/ui/browser_navigator.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/host_desktop.h"
24 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
25 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
26 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
27 #include "chrome/common/chrome_content_client.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/url_constants.h"
30 #include "components/web_modal/web_contents_modal_dialog_host.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_details.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/notification_details.h"
35 #include "content/public/browser/notification_source.h"
36 #include "content/public/browser/plugin_service.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "content/public/browser/render_view_host.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_contents_delegate.h"
41 #include "content/public/browser/web_contents_view.h"
42 #include "content/public/common/webplugininfo.h"
43 #include "ui/web_dialogs/web_dialog_delegate.h"
44 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
46 using content::NativeWebKeyboardEvent;
47 using content::NavigationController;
48 using content::WebContents;
49 using content::WebUIMessageHandler;
50 using ui::WebDialogDelegate;
51 using ui::WebDialogWebContentsDelegate;
55 void EnableInternalPDFPluginForContents(WebContents* preview_dialog) {
56 // Always enable the internal PDF plugin for the print preview page.
57 base::FilePath pdf_plugin_path;
58 if (!PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_plugin_path))
61 content::WebPluginInfo pdf_plugin;
62 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
63 pdf_plugin_path, &pdf_plugin))
66 ChromePluginServiceFilter::GetInstance()->OverridePluginForTab(
67 preview_dialog->GetRenderProcessHost()->GetID(),
68 preview_dialog->GetRenderViewHost()->GetRoutingID(),
72 // WebDialogDelegate that specifies what the print preview dialog
74 class PrintPreviewDialogDelegate : public WebDialogDelegate {
76 explicit PrintPreviewDialogDelegate(WebContents* initiator);
77 virtual ~PrintPreviewDialogDelegate();
79 virtual ui::ModalType GetDialogModalType() const OVERRIDE;
80 virtual string16 GetDialogTitle() const OVERRIDE;
81 virtual GURL GetDialogContentURL() const OVERRIDE;
82 virtual void GetWebUIMessageHandlers(
83 std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
84 virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
85 virtual std::string GetDialogArgs() const OVERRIDE;
86 virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
87 virtual void OnCloseContents(WebContents* source,
88 bool* out_close_dialog) OVERRIDE;
89 virtual bool ShouldShowDialogTitle() const OVERRIDE;
92 WebContents* initiator_;
94 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
97 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
98 : initiator_(initiator) {
101 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
104 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
105 // Not used, returning dummy value.
107 return ui::MODAL_TYPE_WINDOW;
110 string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
111 // Only used on Windows? UI folks prefer no title.
115 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
116 return GURL(chrome::kChromeUIPrintURL);
119 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
120 std::vector<WebUIMessageHandler*>* /* handlers */) const {
121 // PrintPreviewUI adds its own message handlers.
124 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size* size) const {
126 const gfx::Size kMinDialogSize(800, 480);
127 const int kBorder = 25;
128 *size = kMinDialogSize;
130 web_modal::WebContentsModalDialogHost* host = NULL;
131 Browser* browser = chrome::FindBrowserWithWebContents(initiator_);
133 host = browser->window()->GetWebContentsModalDialogHost();
136 size->SetToMax(host->GetMaximumDialogSize());
137 size->Enlarge(-2 * kBorder, -kBorder);
139 size->SetToMax(initiator_->GetView()->GetContainerSize());
140 size->Enlarge(-2 * kBorder, -2 * kBorder);
143 #if defined(OS_MACOSX)
144 // Limit the maximum size on MacOS X.
145 // http://crbug.com/105815
146 const gfx::Size kMaxDialogSize(1000, 660);
147 size->SetToMin(kMaxDialogSize);
151 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
152 return std::string();
155 void PrintPreviewDialogDelegate::OnDialogClosed(
156 const std::string& /* json_retval */) {
159 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
160 bool* out_close_dialog) {
161 if (out_close_dialog)
162 *out_close_dialog = true;
165 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
169 // WebContentsDelegate that forwards shortcut keys in the print preview
170 // renderer to the browser.
171 class PrintPreviewWebContentDelegate : public WebDialogWebContentsDelegate {
173 PrintPreviewWebContentDelegate(Profile* profile, WebContents* initiator);
174 virtual ~PrintPreviewWebContentDelegate();
176 // Overridden from WebDialogWebContentsDelegate:
177 virtual void HandleKeyboardEvent(
179 const NativeWebKeyboardEvent& event) OVERRIDE;
182 WebContents* initiator_;
184 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
187 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
189 WebContents* initiator)
190 : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
191 initiator_(initiator) {}
193 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
195 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
197 const NativeWebKeyboardEvent& event) {
198 // Disabled on Mac due to http://crbug.com/112173
199 #if !defined(OS_MACOSX)
200 Browser* current_browser = chrome::FindBrowserWithWebContents(initiator_);
201 if (!current_browser)
203 current_browser->window()->HandleKeyboardEvent(event);
211 PrintPreviewDialogController::PrintPreviewDialogController()
212 : waiting_for_new_preview_page_(false),
213 is_creating_print_preview_dialog_(false) {
217 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
218 if (!g_browser_process)
220 return g_browser_process->print_preview_dialog_controller();
224 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
225 if (initiator->ShowingInterstitialPage())
228 PrintPreviewDialogController* dialog_controller = GetInstance();
229 if (!dialog_controller)
231 if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
232 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
235 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
236 WebContents* initiator) {
239 // Get the print preview dialog for |initiator|.
240 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
242 return CreatePrintPreviewDialog(initiator);
244 // Show the initiator holding the existing preview dialog.
245 initiator->GetDelegate()->ActivateContents(initiator);
246 return preview_dialog;
249 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
250 WebContents* contents) const {
251 // |preview_dialog_map_| is keyed by the preview dialog, so if find()
252 // succeeds, then |contents| is the preview dialog.
253 PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.find(contents);
254 if (it != preview_dialog_map_.end())
257 for (it = preview_dialog_map_.begin();
258 it != preview_dialog_map_.end();
260 // If |contents| is an initiator.
261 if (contents == it->second) {
262 // Return the associated preview dialog.
269 WebContents* PrintPreviewDialogController::GetInitiator(
270 WebContents* preview_dialog) {
271 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
272 return (it != preview_dialog_map_.end()) ? it->second : NULL;
275 void PrintPreviewDialogController::Observe(
277 const content::NotificationSource& source,
278 const content::NotificationDetails& details) {
279 if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
280 OnRendererProcessClosed(
281 content::Source<content::RenderProcessHost>(source).ptr());
282 } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
283 OnWebContentsDestroyed(content::Source<WebContents>(source).ptr());
285 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
286 WebContents* contents =
287 content::Source<NavigationController>(source)->GetWebContents();
290 content::Details<content::LoadCommittedDetails>(details).ptr());
295 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
296 return IsPrintPreviewURL(contents->GetURL());
300 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
301 return (url.SchemeIs(chrome::kChromeUIScheme) &&
302 url.host() == chrome::kChromeUIPrintHost);
305 void PrintPreviewDialogController::EraseInitiatorInfo(
306 WebContents* preview_dialog) {
307 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
308 if (it == preview_dialog_map_.end())
311 RemoveObservers(it->second);
312 preview_dialog_map_[preview_dialog] = NULL;
315 PrintPreviewDialogController::~PrintPreviewDialogController() {}
317 void PrintPreviewDialogController::OnRendererProcessClosed(
318 content::RenderProcessHost* rph) {
319 // Store contents in a vector and deal with them after iterating through
320 // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
321 std::vector<WebContents*> closed_initiators;
322 std::vector<WebContents*> closed_preview_dialogs;
323 for (PrintPreviewDialogMap::iterator iter = preview_dialog_map_.begin();
324 iter != preview_dialog_map_.end(); ++iter) {
325 WebContents* preview_dialog = iter->first;
326 WebContents* initiator = iter->second;
327 if (preview_dialog->GetRenderProcessHost() == rph) {
328 closed_preview_dialogs.push_back(preview_dialog);
329 } else if (initiator &&
330 initiator->GetRenderProcessHost() == rph) {
331 closed_initiators.push_back(initiator);
335 for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
336 RemovePreviewDialog(closed_preview_dialogs[i]);
337 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
338 closed_preview_dialogs[i]->GetWebUI()->GetController());
339 if (print_preview_ui)
340 print_preview_ui->OnPrintPreviewDialogClosed();
343 for (size_t i = 0; i < closed_initiators.size(); ++i)
344 RemoveInitiator(closed_initiators[i]);
347 void PrintPreviewDialogController::OnWebContentsDestroyed(
348 WebContents* contents) {
349 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
350 if (!preview_dialog) {
355 if (contents == preview_dialog)
356 RemovePreviewDialog(contents);
358 RemoveInitiator(contents);
361 void PrintPreviewDialogController::OnNavEntryCommitted(
362 WebContents* contents, content::LoadCommittedDetails* details) {
363 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
364 if (!preview_dialog) {
369 if (contents == preview_dialog) {
370 // Preview dialog navigated.
372 content::PageTransition transition_type =
373 details->entry->GetTransitionType();
374 content::NavigationType nav_type = details->type;
376 // New |preview_dialog| is created. Don't update/erase map entry.
377 if (waiting_for_new_preview_page_ &&
378 transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
379 nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
380 waiting_for_new_preview_page_ = false;
381 SaveInitiatorTitle(preview_dialog);
385 // Cloud print sign-in causes a reload.
386 if (!waiting_for_new_preview_page_ &&
387 transition_type == content::PAGE_TRANSITION_RELOAD &&
388 nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
389 IsPrintPreviewURL(details->previous_url)) {
397 RemoveInitiator(contents);
400 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
401 WebContents* initiator) {
402 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
404 Profile::FromBrowserContext(initiator->GetBrowserContext());
406 // |web_dialog_ui_delegate| deletes itself in
407 // PrintPreviewDialogDelegate::OnDialogClosed().
408 WebDialogDelegate* web_dialog_delegate =
409 new PrintPreviewDialogDelegate(initiator);
410 // |web_dialog_delegate|'s owner is |constrained_delegate|.
411 PrintPreviewWebContentDelegate* pp_wcd =
412 new PrintPreviewWebContentDelegate(profile, initiator);
413 ConstrainedWebDialogDelegate* constrained_delegate =
414 CreateConstrainedWebDialog(profile,
418 WebContents* preview_dialog = constrained_delegate->GetWebContents();
419 EnableInternalPDFPluginForContents(preview_dialog);
420 PrintViewManager::CreateForWebContents(preview_dialog);
422 // Add an entry to the map.
423 preview_dialog_map_[preview_dialog] = initiator;
424 waiting_for_new_preview_page_ = true;
426 AddObservers(initiator);
427 AddObservers(preview_dialog);
429 return preview_dialog;
432 void PrintPreviewDialogController::SaveInitiatorTitle(
433 WebContents* preview_dialog) {
434 WebContents* initiator = GetInitiator(preview_dialog);
435 if (initiator && preview_dialog->GetWebUI()) {
436 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
437 preview_dialog->GetWebUI()->GetController());
438 print_preview_ui->SetInitiatorTitle(
439 PrintViewManager::FromWebContents(initiator)->RenderSourceName());
443 void PrintPreviewDialogController::AddObservers(WebContents* contents) {
444 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
445 content::Source<WebContents>(contents));
446 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
447 content::Source<NavigationController>(&contents->GetController()));
449 // Multiple sites may share the same RenderProcessHost, so check if this
450 // notification has already been added.
451 content::Source<content::RenderProcessHost> rph_source(
452 contents->GetRenderProcessHost());
453 if (!registrar_.IsRegistered(this,
454 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
455 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
460 void PrintPreviewDialogController::RemoveObservers(WebContents* contents) {
461 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
462 content::Source<WebContents>(contents));
463 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
464 content::Source<NavigationController>(&contents->GetController()));
466 // Multiple sites may share the same RenderProcessHost, so check if this
467 // notification has already been added.
468 content::Source<content::RenderProcessHost> rph_source(
469 contents->GetRenderProcessHost());
470 if (registrar_.IsRegistered(this,
471 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
472 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
477 void PrintPreviewDialogController::RemoveInitiator(
478 WebContents* initiator) {
479 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
480 DCHECK(preview_dialog);
481 // Update the map entry first, so when the print preview dialog gets destroyed
482 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
483 // initiator's observers.
484 preview_dialog_map_[preview_dialog] = NULL;
485 RemoveObservers(initiator);
487 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
489 // initiator is closed. Close the print preview dialog too.
490 PrintPreviewUI* print_preview_ui =
491 static_cast<PrintPreviewUI*>(preview_dialog->GetWebUI()->GetController());
492 if (print_preview_ui)
493 print_preview_ui->OnInitiatorClosed();
496 void PrintPreviewDialogController::RemovePreviewDialog(
497 WebContents* preview_dialog) {
498 // Remove the initiator's observers before erasing the mapping.
499 WebContents* initiator = GetInitiator(preview_dialog);
501 RemoveObservers(initiator);
502 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
505 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to abort
506 // the initiator preview request.
507 PrintPreviewUI* print_preview_ui =
508 static_cast<PrintPreviewUI*>(preview_dialog->GetWebUI()->GetController());
509 if (print_preview_ui)
510 print_preview_ui->OnPrintPreviewDialogDestroyed();
512 preview_dialog_map_.erase(preview_dialog);
513 RemoveObservers(preview_dialog);
516 } // namespace printing