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"
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
;
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
))
63 content::WebPluginInfo pdf_plugin
;
64 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
65 pdf_plugin_path
, &pdf_plugin
))
68 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
69 preview_dialog
->GetRenderProcessHost()->GetID(),
70 preview_dialog
->GetMainFrame()->GetRoutingID(),
74 // WebDialogDelegate that specifies what the print preview dialog
76 class PrintPreviewDialogDelegate
: public WebDialogDelegate
{
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
;
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.
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 {
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_
);
135 host
= browser
->window()->GetWebContentsModalDialogHost();
138 size
->SetToMax(host
->GetMaximumDialogSize());
139 size
->Enlarge(-2 * kBorder
, -kBorder
);
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
);
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 {
171 // WebContentsDelegate that forwards shortcut keys in the print preview
172 // renderer to the browser.
173 class PrintPreviewWebContentDelegate
: public WebDialogWebContentsDelegate
{
175 PrintPreviewWebContentDelegate(Profile
* profile
, WebContents
* initiator
);
176 virtual ~PrintPreviewWebContentDelegate();
178 // Overridden from WebDialogWebContentsDelegate:
179 virtual void HandleKeyboardEvent(
181 const NativeWebKeyboardEvent
& event
) OVERRIDE
;
184 WebContents
* initiator_
;
186 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate
);
189 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
191 WebContents
* initiator
)
192 : WebDialogWebContentsDelegate(profile
, new ChromeWebContentsHandler
),
193 initiator_(initiator
) {}
195 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
197 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
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
)
205 current_browser
->window()->HandleKeyboardEvent(event
);
213 struct PrintPreviewDialogController::Operation
{
214 class Observer
: public content::WebContentsObserver
,
215 public content::RenderProcessHostObserver
{
220 void StartObserving(PrintPreviewDialogController
* owner
,
221 WebContents
* web_contents
);
222 void StopObserving();
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_
;
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
),
254 PrintPreviewDialogController::Operation::Observer::Observer() : owner_(NULL
),
258 PrintPreviewDialogController::Operation::Observer::~Observer() {
262 void PrintPreviewDialogController::Operation::Observer::StartObserving(
263 PrintPreviewDialogController
* owner
,
264 WebContents
* web_contents
) {
266 Observe(web_contents
);
267 host_
= web_contents
->GetRenderProcessHost();
268 host_
->AddObserver(this);
271 void PrintPreviewDialogController::Operation::Observer::StopObserving() {
274 host_
->RemoveObserver(this);
279 void PrintPreviewDialogController::Operation::Observer::WebContentsDestroyed(
280 WebContents
* web_contents
) {
281 owner_
->OnWebContentsDestroyed(web_contents
);
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
,
295 owner_
->OnRenderProcessExited(host
);
299 PrintPreviewDialogController::Operation::Observer::RenderProcessHostDestroyed(
300 RenderProcessHost
* host
) {
304 PrintPreviewDialogController::PrintPreviewDialogController()
305 : waiting_for_new_preview_page_(false),
306 is_creating_print_preview_dialog_(false) {
310 PrintPreviewDialogController
* PrintPreviewDialogController::GetInstance() {
311 if (!g_browser_process
)
313 return g_browser_process
->print_preview_dialog_controller();
317 void PrintPreviewDialogController::PrintPreview(WebContents
* initiator
) {
318 if (initiator
->ShowingInterstitialPage())
321 PrintPreviewDialogController
* dialog_controller
= GetInstance();
322 if (!dialog_controller
)
324 if (!dialog_controller
->GetOrCreatePreviewDialog(initiator
))
325 PrintViewManager::FromWebContents(initiator
)->PrintPreviewDone();
328 WebContents
* PrintPreviewDialogController::GetOrCreatePreviewDialog(
329 WebContents
* initiator
) {
332 // Get the print preview dialog for |initiator|.
333 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator
);
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
;
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
;
365 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents
* contents
) {
366 return IsPrintPreviewURL(contents
->GetURL());
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
;
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
) {
431 if (contents
== preview_dialog
)
432 RemovePreviewDialog(contents
);
434 RemoveInitiator(contents
);
437 void PrintPreviewDialogController::OnNavigationEntryCommitted(
438 WebContents
* contents
, const content::LoadCommittedDetails
* details
) {
439 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
440 if (!preview_dialog
) {
445 if (contents
== preview_dialog
) {
446 // Preview dialog navigated.
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
);
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
)) {
473 RemoveInitiator(contents
);
476 WebContents
* PrintPreviewDialogController::CreatePrintPreviewDialog(
477 WebContents
* initiator
) {
478 base::AutoReset
<bool> auto_reset(&is_creating_print_preview_dialog_
, true);
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
,
494 WebContents
* preview_dialog
= constrained_delegate
->GetWebContents();
495 EnableInternalPDFPluginForContents(preview_dialog
);
496 PrintViewManager::CreateForWebContents(preview_dialog
);
497 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
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
)->
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
);
575 } // namespace printing