Fix infinite recursion on hiding panel when created during fullscreen mode.
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_dialog_controller.cc
bloba7c96364787b36a617503acf8cf49ac42a54eaa7
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"
7 #include <algorithm>
8 #include <string>
10 #include "base/auto_reset.h"
11 #include "base/path_service.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.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/plugin_service.h"
35 #include "content/public/browser/render_frame_host.h"
36 #include "content/public/browser/render_process_host.h"
37 #include "content/public/browser/render_process_host_observer.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_observer.h"
42 #include "content/public/browser/web_contents_view.h"
43 #include "content/public/common/webplugininfo.h"
44 #include "ui/web_dialogs/web_dialog_delegate.h"
45 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
47 using content::NativeWebKeyboardEvent;
48 using content::NavigationController;
49 using content::RenderProcessHost;
50 using content::WebContents;
51 using content::WebUIMessageHandler;
52 using ui::WebDialogDelegate;
53 using ui::WebDialogWebContentsDelegate;
55 namespace {
57 void EnableInternalPDFPluginForContents(WebContents* preview_dialog) {
58 // Always enable the internal PDF plugin for the print preview page.
59 base::FilePath pdf_plugin_path;
60 if (!PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_plugin_path))
61 return;
63 content::WebPluginInfo pdf_plugin;
64 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
65 pdf_plugin_path, &pdf_plugin))
66 return;
68 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
69 preview_dialog->GetRenderProcessHost()->GetID(),
70 preview_dialog->GetMainFrame()->GetRoutingID(),
71 GURL(), pdf_plugin);
74 // WebDialogDelegate that specifies what the print preview dialog
75 // will look like.
76 class PrintPreviewDialogDelegate : public WebDialogDelegate {
77 public:
78 explicit PrintPreviewDialogDelegate(WebContents* initiator);
79 virtual ~PrintPreviewDialogDelegate();
81 virtual ui::ModalType GetDialogModalType() const OVERRIDE;
82 virtual base::string16 GetDialogTitle() const OVERRIDE;
83 virtual GURL GetDialogContentURL() const OVERRIDE;
84 virtual void GetWebUIMessageHandlers(
85 std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
86 virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
87 virtual std::string GetDialogArgs() const OVERRIDE;
88 virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
89 virtual void OnCloseContents(WebContents* source,
90 bool* out_close_dialog) OVERRIDE;
91 virtual bool ShouldShowDialogTitle() const OVERRIDE;
93 private:
94 WebContents* initiator_;
96 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
99 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
100 : initiator_(initiator) {
103 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
106 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
107 // Not used, returning dummy value.
108 NOTREACHED();
109 return ui::MODAL_TYPE_WINDOW;
112 base::string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
113 // Only used on Windows? UI folks prefer no title.
114 return base::string16();
117 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
118 return GURL(chrome::kChromeUIPrintURL);
121 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
122 std::vector<WebUIMessageHandler*>* /* handlers */) const {
123 // PrintPreviewUI adds its own message handlers.
126 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size* size) const {
127 DCHECK(size);
128 const gfx::Size kMinDialogSize(800, 480);
129 const int kBorder = 25;
130 *size = kMinDialogSize;
132 web_modal::WebContentsModalDialogHost* host = NULL;
133 Browser* browser = chrome::FindBrowserWithWebContents(initiator_);
134 if (browser)
135 host = browser->window()->GetWebContentsModalDialogHost();
137 if (host) {
138 size->SetToMax(host->GetMaximumDialogSize());
139 size->Enlarge(-2 * kBorder, -kBorder);
140 } else {
141 size->SetToMax(initiator_->GetView()->GetContainerSize());
142 size->Enlarge(-2 * kBorder, -2 * kBorder);
145 #if defined(OS_MACOSX)
146 // Limit the maximum size on MacOS X.
147 // http://crbug.com/105815
148 const gfx::Size kMaxDialogSize(1000, 660);
149 size->SetToMin(kMaxDialogSize);
150 #endif
153 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
154 return std::string();
157 void PrintPreviewDialogDelegate::OnDialogClosed(
158 const std::string& /* json_retval */) {
161 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
162 bool* out_close_dialog) {
163 if (out_close_dialog)
164 *out_close_dialog = true;
167 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
168 return false;
171 // WebContentsDelegate that forwards shortcut keys in the print preview
172 // renderer to the browser.
173 class PrintPreviewWebContentDelegate : public WebDialogWebContentsDelegate {
174 public:
175 PrintPreviewWebContentDelegate(Profile* profile, WebContents* initiator);
176 virtual ~PrintPreviewWebContentDelegate();
178 // Overridden from WebDialogWebContentsDelegate:
179 virtual void HandleKeyboardEvent(
180 WebContents* source,
181 const NativeWebKeyboardEvent& event) OVERRIDE;
183 private:
184 WebContents* initiator_;
186 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
189 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
190 Profile* profile,
191 WebContents* initiator)
192 : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
193 initiator_(initiator) {}
195 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
197 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
198 WebContents* source,
199 const NativeWebKeyboardEvent& event) {
200 // Disabled on Mac due to http://crbug.com/112173
201 #if !defined(OS_MACOSX)
202 Browser* current_browser = chrome::FindBrowserWithWebContents(initiator_);
203 if (!current_browser)
204 return;
205 current_browser->window()->HandleKeyboardEvent(event);
206 #endif
209 } // namespace
211 namespace printing {
213 struct PrintPreviewDialogController::Operation {
214 class Observer : public content::WebContentsObserver,
215 public content::RenderProcessHostObserver {
216 public:
217 Observer();
218 virtual ~Observer();
220 void StartObserving(PrintPreviewDialogController* owner,
221 WebContents* web_contents);
222 void StopObserving();
224 private:
225 // content::WebContentsObserver
226 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
227 virtual void NavigationEntryCommitted(
228 const content::LoadCommittedDetails& load_details) OVERRIDE;
229 // content::RenderProcessHostObserver
230 virtual void RenderProcessExited(RenderProcessHost* host,
231 base::ProcessHandle handle,
232 base::TerminationStatus status,
233 int exit_code) OVERRIDE;
234 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
236 PrintPreviewDialogController* owner_;
237 RenderProcessHost* host_;
240 Operation();
242 WebContents* preview_dialog;
243 WebContents* initiator;
244 Observer preview_dialog_observer;
245 Observer initiator_observer;
247 DISALLOW_COPY_AND_ASSIGN(Operation);
250 PrintPreviewDialogController::Operation::Operation() : preview_dialog(NULL),
251 initiator(NULL) {
254 PrintPreviewDialogController::Operation::Observer::Observer() : owner_(NULL),
255 host_(NULL) {
258 PrintPreviewDialogController::Operation::Observer::~Observer() {
259 StopObserving();
262 void PrintPreviewDialogController::Operation::Observer::StartObserving(
263 PrintPreviewDialogController* owner,
264 WebContents* web_contents) {
265 owner_ = owner;
266 Observe(web_contents);
267 host_ = web_contents->GetRenderProcessHost();
268 host_->AddObserver(this);
271 void PrintPreviewDialogController::Operation::Observer::StopObserving() {
272 Observe(NULL);
273 if (host_) {
274 host_->RemoveObserver(this);
275 host_ = NULL;
279 void PrintPreviewDialogController::Operation::Observer::WebContentsDestroyed(
280 WebContents* web_contents) {
281 owner_->OnWebContentsDestroyed(web_contents);
284 void
285 PrintPreviewDialogController::Operation::Observer::NavigationEntryCommitted(
286 const content::LoadCommittedDetails& load_details) {
287 owner_->OnNavigationEntryCommitted(web_contents(), &load_details);
290 void PrintPreviewDialogController::Operation::Observer::RenderProcessExited(
291 RenderProcessHost* host,
292 base::ProcessHandle handle,
293 base::TerminationStatus status,
294 int exit_code) {
295 owner_->OnRenderProcessExited(host);
298 void
299 PrintPreviewDialogController::Operation::Observer::RenderProcessHostDestroyed(
300 RenderProcessHost* host) {
301 host_ = NULL;
304 PrintPreviewDialogController::PrintPreviewDialogController()
305 : waiting_for_new_preview_page_(false),
306 is_creating_print_preview_dialog_(false) {
309 // static
310 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
311 if (!g_browser_process)
312 return NULL;
313 return g_browser_process->print_preview_dialog_controller();
316 // static
317 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
318 if (initiator->ShowingInterstitialPage())
319 return;
321 PrintPreviewDialogController* dialog_controller = GetInstance();
322 if (!dialog_controller)
323 return;
324 if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
325 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
328 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
329 WebContents* initiator) {
330 DCHECK(initiator);
332 // Get the print preview dialog for |initiator|.
333 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
334 if (!preview_dialog)
335 return CreatePrintPreviewDialog(initiator);
337 // Show the initiator holding the existing preview dialog.
338 initiator->GetDelegate()->ActivateContents(initiator);
339 return preview_dialog;
342 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
343 WebContents* contents) const {
344 for (size_t i = 0; i < preview_operations_.size(); ++i) {
345 Operation* operation = preview_operations_[i];
346 if (operation->preview_dialog == contents ||
347 operation->initiator == contents) {
348 return operation->preview_dialog;
351 return NULL;
354 WebContents* PrintPreviewDialogController::GetInitiator(
355 WebContents* preview_dialog) {
356 for (size_t i = 0; i < preview_operations_.size(); ++i) {
357 Operation* operation = preview_operations_[i];
358 if (operation->preview_dialog == preview_dialog)
359 return operation->initiator;
361 return NULL;
364 // static
365 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
366 return IsPrintPreviewURL(contents->GetURL());
369 // static
370 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
371 return (url.SchemeIs(content::kChromeUIScheme) &&
372 url.host() == chrome::kChromeUIPrintHost);
375 void PrintPreviewDialogController::EraseInitiatorInfo(
376 WebContents* preview_dialog) {
377 for (size_t i = 0; i < preview_operations_.size(); ++i) {
378 Operation* operation = preview_operations_[i];
379 if (operation->preview_dialog == preview_dialog) {
380 operation->initiator_observer.StopObserving();
381 operation->initiator = NULL;
382 return;
387 PrintPreviewDialogController::~PrintPreviewDialogController() {
388 DCHECK_EQ(0U, preview_operations_.size());
391 void PrintPreviewDialogController::OnRenderProcessExited(
392 RenderProcessHost* rph) {
393 // Store contents in a vector and deal with them after iterating through
394 // |preview_operations_| because RemoveFoo() can change |preview_operations_|.
395 std::vector<WebContents*> closed_initiators;
396 std::vector<WebContents*> closed_preview_dialogs;
397 for (size_t i = 0; i < preview_operations_.size(); ++i) {
398 Operation* operation = preview_operations_[i];
399 WebContents* preview_dialog = operation->preview_dialog;
400 WebContents* initiator = operation->initiator;
401 if (preview_dialog->GetRenderProcessHost() == rph) {
402 closed_preview_dialogs.push_back(preview_dialog);
403 } else if (initiator &&
404 initiator->GetRenderProcessHost() == rph) {
405 closed_initiators.push_back(initiator);
409 for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
410 RemovePreviewDialog(closed_preview_dialogs[i]);
411 if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
412 PrintPreviewUI* print_preview_ui =
413 static_cast<PrintPreviewUI*>(web_ui->GetController());
414 if (print_preview_ui)
415 print_preview_ui->OnPrintPreviewDialogClosed();
419 for (size_t i = 0; i < closed_initiators.size(); ++i)
420 RemoveInitiator(closed_initiators[i]);
423 void PrintPreviewDialogController::OnWebContentsDestroyed(
424 WebContents* contents) {
425 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
426 if (!preview_dialog) {
427 NOTREACHED();
428 return;
431 if (contents == preview_dialog)
432 RemovePreviewDialog(contents);
433 else
434 RemoveInitiator(contents);
437 void PrintPreviewDialogController::OnNavigationEntryCommitted(
438 WebContents* contents, const content::LoadCommittedDetails* details) {
439 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
440 if (!preview_dialog) {
441 NOTREACHED();
442 return;
445 if (contents == preview_dialog) {
446 // Preview dialog navigated.
447 if (details) {
448 content::PageTransition transition_type =
449 details->entry->GetTransitionType();
450 content::NavigationType nav_type = details->type;
452 // New |preview_dialog| is created. Don't update/erase map entry.
453 if (waiting_for_new_preview_page_ &&
454 transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
455 nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
456 waiting_for_new_preview_page_ = false;
457 SaveInitiatorTitle(preview_dialog);
458 return;
461 // Cloud print sign-in causes a reload.
462 if (!waiting_for_new_preview_page_ &&
463 transition_type == content::PAGE_TRANSITION_RELOAD &&
464 nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
465 IsPrintPreviewURL(details->previous_url)) {
466 return;
469 NOTREACHED();
470 return;
473 RemoveInitiator(contents);
476 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
477 WebContents* initiator) {
478 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
479 Profile* profile =
480 Profile::FromBrowserContext(initiator->GetBrowserContext());
482 // |web_dialog_ui_delegate| deletes itself in
483 // PrintPreviewDialogDelegate::OnDialogClosed().
484 WebDialogDelegate* web_dialog_delegate =
485 new PrintPreviewDialogDelegate(initiator);
486 // |web_dialog_delegate|'s owner is |constrained_delegate|.
487 PrintPreviewWebContentDelegate* pp_wcd =
488 new PrintPreviewWebContentDelegate(profile, initiator);
489 ConstrainedWebDialogDelegate* constrained_delegate =
490 CreateConstrainedWebDialog(profile,
491 web_dialog_delegate,
492 pp_wcd,
493 initiator);
494 WebContents* preview_dialog = constrained_delegate->GetWebContents();
495 EnableInternalPDFPluginForContents(preview_dialog);
496 PrintViewManager::CreateForWebContents(preview_dialog);
497 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
498 preview_dialog);
500 waiting_for_new_preview_page_ = true;
502 // Add an entry to the map.
503 Operation* operation = new Operation;
504 operation->preview_dialog = preview_dialog;
505 operation->initiator = initiator;
506 operation->preview_dialog_observer.StartObserving(this, preview_dialog);
507 operation->initiator_observer.StartObserving(this, initiator);
508 preview_operations_.push_back(operation);
510 return preview_dialog;
513 void PrintPreviewDialogController::SaveInitiatorTitle(
514 WebContents* preview_dialog) {
515 WebContents* initiator = GetInitiator(preview_dialog);
516 if (initiator && preview_dialog->GetWebUI()) {
517 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
518 preview_dialog->GetWebUI()->GetController());
519 print_preview_ui->SetInitiatorTitle(
520 PrintViewManager::FromWebContents(initiator)->RenderSourceName());
524 void PrintPreviewDialogController::RemoveInitiator(
525 WebContents* initiator) {
526 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
527 DCHECK(preview_dialog);
529 // Update the map entry first, so when the print preview dialog gets destroyed
530 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
531 // initiator's observers.
532 EraseInitiatorInfo(preview_dialog);
534 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
536 // initiator is closed. Close the print preview dialog too.
537 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
538 PrintPreviewUI* print_preview_ui =
539 static_cast<PrintPreviewUI*>(web_ui->GetController());
540 if (print_preview_ui)
541 print_preview_ui->OnInitiatorClosed();
545 void PrintPreviewDialogController::RemovePreviewDialog(
546 WebContents* preview_dialog) {
547 for (size_t i = 0; i < preview_operations_.size(); ++i) {
548 Operation* operation = preview_operations_[i];
549 if (operation->preview_dialog == preview_dialog) {
550 // Remove the initiator's observers before erasing the mapping.
551 if (operation->initiator) {
552 operation->initiator_observer.StopObserving();
553 PrintViewManager::FromWebContents(operation->initiator)->
554 PrintPreviewDone();
557 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to
558 // abort the initiator preview request.
559 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
560 PrintPreviewUI* print_preview_ui =
561 static_cast<PrintPreviewUI*>(web_ui->GetController());
562 if (print_preview_ui)
563 print_preview_ui->OnPrintPreviewDialogDestroyed();
566 preview_operations_.erase(preview_operations_.begin() + i);
567 delete operation;
569 return;
572 NOTREACHED();
575 } // namespace printing