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/chrome_notification_types.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
16 #include "chrome/browser/printing/print_view_manager.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_navigator.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/host_desktop.h"
23 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
24 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
25 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
26 #include "chrome/common/chrome_content_client.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/web_modal/web_contents_modal_dialog_host.h"
30 #include "content/public/browser/navigation_controller.h"
31 #include "content/public/browser/navigation_details.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/plugin_service.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/browser/render_process_host_observer.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_contents_delegate.h"
40 #include "content/public/browser/web_contents_observer.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::RenderProcessHost
;
49 using content::WebContents
;
50 using content::WebUIMessageHandler
;
51 using ui::WebDialogDelegate
;
52 using ui::WebDialogWebContentsDelegate
;
56 void EnableInternalPDFPluginForContents(WebContents
* preview_dialog
) {
57 // Always enable the internal PDF plugin for the print preview page.
58 base::FilePath pdf_plugin_path
;
59 if (!PathService::Get(chrome::FILE_PDF_PLUGIN
, &pdf_plugin_path
))
62 content::WebPluginInfo pdf_plugin
;
63 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
64 pdf_plugin_path
, &pdf_plugin
))
67 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
68 preview_dialog
->GetRenderProcessHost()->GetID(),
69 preview_dialog
->GetMainFrame()->GetRoutingID(),
73 // WebDialogDelegate that specifies what the print preview dialog
75 class PrintPreviewDialogDelegate
: public WebDialogDelegate
{
77 explicit PrintPreviewDialogDelegate(WebContents
* initiator
);
78 virtual ~PrintPreviewDialogDelegate();
80 virtual ui::ModalType
GetDialogModalType() const OVERRIDE
;
81 virtual base::string16
GetDialogTitle() const OVERRIDE
;
82 virtual GURL
GetDialogContentURL() const OVERRIDE
;
83 virtual void GetWebUIMessageHandlers(
84 std::vector
<WebUIMessageHandler
*>* handlers
) const OVERRIDE
;
85 virtual void GetDialogSize(gfx::Size
* size
) const OVERRIDE
;
86 virtual std::string
GetDialogArgs() const OVERRIDE
;
87 virtual void OnDialogClosed(const std::string
& json_retval
) OVERRIDE
;
88 virtual void OnCloseContents(WebContents
* source
,
89 bool* out_close_dialog
) OVERRIDE
;
90 virtual bool ShouldShowDialogTitle() const OVERRIDE
;
93 WebContents
* initiator_
;
95 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate
);
98 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents
* initiator
)
99 : initiator_(initiator
) {
102 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
105 ui::ModalType
PrintPreviewDialogDelegate::GetDialogModalType() const {
106 // Not used, returning dummy value.
108 return ui::MODAL_TYPE_WINDOW
;
111 base::string16
PrintPreviewDialogDelegate::GetDialogTitle() const {
112 // Only used on Windows? UI folks prefer no title.
113 return base::string16();
116 GURL
PrintPreviewDialogDelegate::GetDialogContentURL() const {
117 return GURL(chrome::kChromeUIPrintURL
);
120 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
121 std::vector
<WebUIMessageHandler
*>* /* handlers */) const {
122 // PrintPreviewUI adds its own message handlers.
125 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size
* size
) const {
127 const gfx::Size
kMinDialogSize(800, 480);
128 const int kBorder
= 25;
129 *size
= kMinDialogSize
;
131 web_modal::WebContentsModalDialogHost
* host
= NULL
;
132 Browser
* browser
= chrome::FindBrowserWithWebContents(initiator_
);
134 host
= browser
->window()->GetWebContentsModalDialogHost();
137 size
->SetToMax(host
->GetMaximumDialogSize());
138 size
->Enlarge(-2 * kBorder
, -kBorder
);
140 size
->SetToMax(initiator_
->GetView()->GetContainerSize());
141 size
->Enlarge(-2 * kBorder
, -2 * kBorder
);
144 #if defined(OS_MACOSX)
145 // Limit the maximum size on MacOS X.
146 // http://crbug.com/105815
147 const gfx::Size
kMaxDialogSize(1000, 660);
148 size
->SetToMin(kMaxDialogSize
);
152 std::string
PrintPreviewDialogDelegate::GetDialogArgs() const {
153 return std::string();
156 void PrintPreviewDialogDelegate::OnDialogClosed(
157 const std::string
& /* json_retval */) {
160 void PrintPreviewDialogDelegate::OnCloseContents(WebContents
* /* source */,
161 bool* out_close_dialog
) {
162 if (out_close_dialog
)
163 *out_close_dialog
= true;
166 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
170 // WebContentsDelegate that forwards shortcut keys in the print preview
171 // renderer to the browser.
172 class PrintPreviewWebContentDelegate
: public WebDialogWebContentsDelegate
{
174 PrintPreviewWebContentDelegate(Profile
* profile
, WebContents
* initiator
);
175 virtual ~PrintPreviewWebContentDelegate();
177 // Overridden from WebDialogWebContentsDelegate:
178 virtual void HandleKeyboardEvent(
180 const NativeWebKeyboardEvent
& event
) OVERRIDE
;
183 WebContents
* initiator_
;
185 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate
);
188 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
190 WebContents
* initiator
)
191 : WebDialogWebContentsDelegate(profile
, new ChromeWebContentsHandler
),
192 initiator_(initiator
) {}
194 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
196 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
198 const NativeWebKeyboardEvent
& event
) {
199 // Disabled on Mac due to http://crbug.com/112173
200 #if !defined(OS_MACOSX)
201 Browser
* current_browser
= chrome::FindBrowserWithWebContents(initiator_
);
202 if (!current_browser
)
204 current_browser
->window()->HandleKeyboardEvent(event
);
212 struct PrintPreviewDialogController::Operation
{
213 class Observer
: public content::WebContentsObserver
,
214 public content::RenderProcessHostObserver
{
219 void StartObserving(PrintPreviewDialogController
* owner
,
220 WebContents
* web_contents
);
221 void StopObserving();
224 // content::WebContentsObserver
225 virtual void WebContentsDestroyed(WebContents
* web_contents
) OVERRIDE
;
226 virtual void NavigationEntryCommitted(
227 const content::LoadCommittedDetails
& load_details
) OVERRIDE
;
228 // content::RenderProcessHostObserver
229 virtual void RenderProcessExited(RenderProcessHost
* host
,
230 base::ProcessHandle handle
,
231 base::TerminationStatus status
,
232 int exit_code
) OVERRIDE
;
233 virtual void RenderProcessHostDestroyed(RenderProcessHost
* host
) OVERRIDE
;
235 PrintPreviewDialogController
* owner_
;
236 RenderProcessHost
* host_
;
241 WebContents
* preview_dialog
;
242 WebContents
* initiator
;
243 Observer preview_dialog_observer
;
244 Observer initiator_observer
;
246 DISALLOW_COPY_AND_ASSIGN(Operation
);
249 PrintPreviewDialogController::Operation::Operation() : preview_dialog(NULL
),
253 PrintPreviewDialogController::Operation::Observer::Observer() : owner_(NULL
),
257 PrintPreviewDialogController::Operation::Observer::~Observer() {
261 void PrintPreviewDialogController::Operation::Observer::StartObserving(
262 PrintPreviewDialogController
* owner
,
263 WebContents
* web_contents
) {
265 Observe(web_contents
);
266 host_
= web_contents
->GetRenderProcessHost();
267 host_
->AddObserver(this);
270 void PrintPreviewDialogController::Operation::Observer::StopObserving() {
273 host_
->RemoveObserver(this);
278 void PrintPreviewDialogController::Operation::Observer::WebContentsDestroyed(
279 WebContents
* web_contents
) {
280 owner_
->OnWebContentsDestroyed(web_contents
);
284 PrintPreviewDialogController::Operation::Observer::NavigationEntryCommitted(
285 const content::LoadCommittedDetails
& load_details
) {
286 owner_
->OnNavigationEntryCommitted(web_contents(), &load_details
);
289 void PrintPreviewDialogController::Operation::Observer::RenderProcessExited(
290 RenderProcessHost
* host
,
291 base::ProcessHandle handle
,
292 base::TerminationStatus status
,
294 owner_
->OnRenderProcessExited(host
);
298 PrintPreviewDialogController::Operation::Observer::RenderProcessHostDestroyed(
299 RenderProcessHost
* host
) {
303 PrintPreviewDialogController::PrintPreviewDialogController()
304 : waiting_for_new_preview_page_(false),
305 is_creating_print_preview_dialog_(false) {
309 PrintPreviewDialogController
* PrintPreviewDialogController::GetInstance() {
310 if (!g_browser_process
)
312 return g_browser_process
->print_preview_dialog_controller();
316 void PrintPreviewDialogController::PrintPreview(WebContents
* initiator
) {
317 if (initiator
->ShowingInterstitialPage())
320 PrintPreviewDialogController
* dialog_controller
= GetInstance();
321 if (!dialog_controller
)
323 if (!dialog_controller
->GetOrCreatePreviewDialog(initiator
))
324 PrintViewManager::FromWebContents(initiator
)->PrintPreviewDone();
327 WebContents
* PrintPreviewDialogController::GetOrCreatePreviewDialog(
328 WebContents
* initiator
) {
331 // Get the print preview dialog for |initiator|.
332 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator
);
334 return CreatePrintPreviewDialog(initiator
);
336 // Show the initiator holding the existing preview dialog.
337 initiator
->GetDelegate()->ActivateContents(initiator
);
338 return preview_dialog
;
341 WebContents
* PrintPreviewDialogController::GetPrintPreviewForContents(
342 WebContents
* contents
) const {
343 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
344 Operation
* operation
= preview_operations_
[i
];
345 if (operation
->preview_dialog
== contents
||
346 operation
->initiator
== contents
) {
347 return operation
->preview_dialog
;
353 WebContents
* PrintPreviewDialogController::GetInitiator(
354 WebContents
* preview_dialog
) {
355 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
356 Operation
* operation
= preview_operations_
[i
];
357 if (operation
->preview_dialog
== preview_dialog
)
358 return operation
->initiator
;
364 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents
* contents
) {
365 return IsPrintPreviewURL(contents
->GetURL());
369 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL
& url
) {
370 return (url
.SchemeIs(content::kChromeUIScheme
) &&
371 url
.host() == chrome::kChromeUIPrintHost
);
374 void PrintPreviewDialogController::EraseInitiatorInfo(
375 WebContents
* preview_dialog
) {
376 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
377 Operation
* operation
= preview_operations_
[i
];
378 if (operation
->preview_dialog
== preview_dialog
) {
379 operation
->initiator_observer
.StopObserving();
380 operation
->initiator
= NULL
;
386 PrintPreviewDialogController::~PrintPreviewDialogController() {
387 DCHECK_EQ(0U, preview_operations_
.size());
390 void PrintPreviewDialogController::OnRenderProcessExited(
391 RenderProcessHost
* rph
) {
392 // Store contents in a vector and deal with them after iterating through
393 // |preview_operations_| because RemoveFoo() can change |preview_operations_|.
394 std::vector
<WebContents
*> closed_initiators
;
395 std::vector
<WebContents
*> closed_preview_dialogs
;
396 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
397 Operation
* operation
= preview_operations_
[i
];
398 WebContents
* preview_dialog
= operation
->preview_dialog
;
399 WebContents
* initiator
= operation
->initiator
;
400 if (preview_dialog
->GetRenderProcessHost() == rph
) {
401 closed_preview_dialogs
.push_back(preview_dialog
);
402 } else if (initiator
&&
403 initiator
->GetRenderProcessHost() == rph
) {
404 closed_initiators
.push_back(initiator
);
408 for (size_t i
= 0; i
< closed_preview_dialogs
.size(); ++i
) {
409 RemovePreviewDialog(closed_preview_dialogs
[i
]);
410 if (content::WebUI
* web_ui
= closed_preview_dialogs
[i
]->GetWebUI()) {
411 PrintPreviewUI
* print_preview_ui
=
412 static_cast<PrintPreviewUI
*>(web_ui
->GetController());
413 if (print_preview_ui
)
414 print_preview_ui
->OnPrintPreviewDialogClosed();
418 for (size_t i
= 0; i
< closed_initiators
.size(); ++i
)
419 RemoveInitiator(closed_initiators
[i
]);
422 void PrintPreviewDialogController::OnWebContentsDestroyed(
423 WebContents
* contents
) {
424 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
425 if (!preview_dialog
) {
430 if (contents
== preview_dialog
)
431 RemovePreviewDialog(contents
);
433 RemoveInitiator(contents
);
436 void PrintPreviewDialogController::OnNavigationEntryCommitted(
437 WebContents
* contents
, const content::LoadCommittedDetails
* details
) {
438 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
439 if (!preview_dialog
) {
444 if (contents
== preview_dialog
) {
445 // Preview dialog navigated.
447 content::PageTransition transition_type
=
448 details
->entry
->GetTransitionType();
449 content::NavigationType nav_type
= details
->type
;
451 // New |preview_dialog| is created. Don't update/erase map entry.
452 if (waiting_for_new_preview_page_
&&
453 transition_type
== content::PAGE_TRANSITION_AUTO_TOPLEVEL
&&
454 nav_type
== content::NAVIGATION_TYPE_NEW_PAGE
) {
455 waiting_for_new_preview_page_
= false;
456 SaveInitiatorTitle(preview_dialog
);
460 // Cloud print sign-in causes a reload.
461 if (!waiting_for_new_preview_page_
&&
462 transition_type
== content::PAGE_TRANSITION_RELOAD
&&
463 nav_type
== content::NAVIGATION_TYPE_EXISTING_PAGE
&&
464 IsPrintPreviewURL(details
->previous_url
)) {
472 RemoveInitiator(contents
);
475 WebContents
* PrintPreviewDialogController::CreatePrintPreviewDialog(
476 WebContents
* initiator
) {
477 base::AutoReset
<bool> auto_reset(&is_creating_print_preview_dialog_
, true);
479 Profile::FromBrowserContext(initiator
->GetBrowserContext());
481 // |web_dialog_ui_delegate| deletes itself in
482 // PrintPreviewDialogDelegate::OnDialogClosed().
483 WebDialogDelegate
* web_dialog_delegate
=
484 new PrintPreviewDialogDelegate(initiator
);
485 // |web_dialog_delegate|'s owner is |constrained_delegate|.
486 PrintPreviewWebContentDelegate
* pp_wcd
=
487 new PrintPreviewWebContentDelegate(profile
, initiator
);
488 ConstrainedWebDialogDelegate
* constrained_delegate
=
489 CreateConstrainedWebDialog(profile
,
493 WebContents
* preview_dialog
= constrained_delegate
->GetWebContents();
494 EnableInternalPDFPluginForContents(preview_dialog
);
495 PrintViewManager::CreateForWebContents(preview_dialog
);
497 waiting_for_new_preview_page_
= true;
499 // Add an entry to the map.
500 Operation
* operation
= new Operation
;
501 operation
->preview_dialog
= preview_dialog
;
502 operation
->initiator
= initiator
;
503 operation
->preview_dialog_observer
.StartObserving(this, preview_dialog
);
504 operation
->initiator_observer
.StartObserving(this, initiator
);
505 preview_operations_
.push_back(operation
);
507 return preview_dialog
;
510 void PrintPreviewDialogController::SaveInitiatorTitle(
511 WebContents
* preview_dialog
) {
512 WebContents
* initiator
= GetInitiator(preview_dialog
);
513 if (initiator
&& preview_dialog
->GetWebUI()) {
514 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
515 preview_dialog
->GetWebUI()->GetController());
516 print_preview_ui
->SetInitiatorTitle(
517 PrintViewManager::FromWebContents(initiator
)->RenderSourceName());
521 void PrintPreviewDialogController::RemoveInitiator(
522 WebContents
* initiator
) {
523 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator
);
524 DCHECK(preview_dialog
);
526 // Update the map entry first, so when the print preview dialog gets destroyed
527 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
528 // initiator's observers.
529 EraseInitiatorInfo(preview_dialog
);
531 PrintViewManager::FromWebContents(initiator
)->PrintPreviewDone();
533 // initiator is closed. Close the print preview dialog too.
534 if (content::WebUI
* web_ui
= preview_dialog
->GetWebUI()) {
535 PrintPreviewUI
* print_preview_ui
=
536 static_cast<PrintPreviewUI
*>(web_ui
->GetController());
537 if (print_preview_ui
)
538 print_preview_ui
->OnInitiatorClosed();
542 void PrintPreviewDialogController::RemovePreviewDialog(
543 WebContents
* preview_dialog
) {
544 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
545 Operation
* operation
= preview_operations_
[i
];
546 if (operation
->preview_dialog
== preview_dialog
) {
547 // Remove the initiator's observers before erasing the mapping.
548 if (operation
->initiator
) {
549 operation
->initiator_observer
.StopObserving();
550 PrintViewManager::FromWebContents(operation
->initiator
)->
554 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to
555 // abort the initiator preview request.
556 if (content::WebUI
* web_ui
= preview_dialog
->GetWebUI()) {
557 PrintPreviewUI
* print_preview_ui
=
558 static_cast<PrintPreviewUI
*>(web_ui
->GetController());
559 if (print_preview_ui
)
560 print_preview_ui
->OnPrintPreviewDialogDestroyed();
563 preview_operations_
.erase(preview_operations_
.begin() + i
);
572 } // namespace printing