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(chrome::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 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
411 closed_preview_dialogs
[i
]->GetWebUI()->GetController());
412 if (print_preview_ui
)
413 print_preview_ui
->OnPrintPreviewDialogClosed();
416 for (size_t i
= 0; i
< closed_initiators
.size(); ++i
)
417 RemoveInitiator(closed_initiators
[i
]);
420 void PrintPreviewDialogController::OnWebContentsDestroyed(
421 WebContents
* contents
) {
422 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
423 if (!preview_dialog
) {
428 if (contents
== preview_dialog
)
429 RemovePreviewDialog(contents
);
431 RemoveInitiator(contents
);
434 void PrintPreviewDialogController::OnNavigationEntryCommitted(
435 WebContents
* contents
, const content::LoadCommittedDetails
* details
) {
436 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
437 if (!preview_dialog
) {
442 if (contents
== preview_dialog
) {
443 // Preview dialog navigated.
445 content::PageTransition transition_type
=
446 details
->entry
->GetTransitionType();
447 content::NavigationType nav_type
= details
->type
;
449 // New |preview_dialog| is created. Don't update/erase map entry.
450 if (waiting_for_new_preview_page_
&&
451 transition_type
== content::PAGE_TRANSITION_AUTO_TOPLEVEL
&&
452 nav_type
== content::NAVIGATION_TYPE_NEW_PAGE
) {
453 waiting_for_new_preview_page_
= false;
454 SaveInitiatorTitle(preview_dialog
);
458 // Cloud print sign-in causes a reload.
459 if (!waiting_for_new_preview_page_
&&
460 transition_type
== content::PAGE_TRANSITION_RELOAD
&&
461 nav_type
== content::NAVIGATION_TYPE_EXISTING_PAGE
&&
462 IsPrintPreviewURL(details
->previous_url
)) {
470 RemoveInitiator(contents
);
473 WebContents
* PrintPreviewDialogController::CreatePrintPreviewDialog(
474 WebContents
* initiator
) {
475 base::AutoReset
<bool> auto_reset(&is_creating_print_preview_dialog_
, true);
477 Profile::FromBrowserContext(initiator
->GetBrowserContext());
479 // |web_dialog_ui_delegate| deletes itself in
480 // PrintPreviewDialogDelegate::OnDialogClosed().
481 WebDialogDelegate
* web_dialog_delegate
=
482 new PrintPreviewDialogDelegate(initiator
);
483 // |web_dialog_delegate|'s owner is |constrained_delegate|.
484 PrintPreviewWebContentDelegate
* pp_wcd
=
485 new PrintPreviewWebContentDelegate(profile
, initiator
);
486 ConstrainedWebDialogDelegate
* constrained_delegate
=
487 CreateConstrainedWebDialog(profile
,
491 WebContents
* preview_dialog
= constrained_delegate
->GetWebContents();
492 EnableInternalPDFPluginForContents(preview_dialog
);
493 PrintViewManager::CreateForWebContents(preview_dialog
);
495 waiting_for_new_preview_page_
= true;
497 // Add an entry to the map.
498 Operation
* operation
= new Operation
;
499 operation
->preview_dialog
= preview_dialog
;
500 operation
->initiator
= initiator
;
501 operation
->preview_dialog_observer
.StartObserving(this, preview_dialog
);
502 operation
->initiator_observer
.StartObserving(this, initiator
);
503 preview_operations_
.push_back(operation
);
505 return preview_dialog
;
508 void PrintPreviewDialogController::SaveInitiatorTitle(
509 WebContents
* preview_dialog
) {
510 WebContents
* initiator
= GetInitiator(preview_dialog
);
511 if (initiator
&& preview_dialog
->GetWebUI()) {
512 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
513 preview_dialog
->GetWebUI()->GetController());
514 print_preview_ui
->SetInitiatorTitle(
515 PrintViewManager::FromWebContents(initiator
)->RenderSourceName());
519 void PrintPreviewDialogController::RemoveInitiator(
520 WebContents
* initiator
) {
521 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator
);
522 DCHECK(preview_dialog
);
524 // Update the map entry first, so when the print preview dialog gets destroyed
525 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
526 // initiator's observers.
527 EraseInitiatorInfo(preview_dialog
);
529 PrintViewManager::FromWebContents(initiator
)->PrintPreviewDone();
531 // initiator is closed. Close the print preview dialog too.
532 PrintPreviewUI
* print_preview_ui
=
533 static_cast<PrintPreviewUI
*>(preview_dialog
->GetWebUI()->GetController());
534 if (print_preview_ui
)
535 print_preview_ui
->OnInitiatorClosed();
538 void PrintPreviewDialogController::RemovePreviewDialog(
539 WebContents
* preview_dialog
) {
540 for (size_t i
= 0; i
< preview_operations_
.size(); ++i
) {
541 Operation
* operation
= preview_operations_
[i
];
542 if (operation
->preview_dialog
== preview_dialog
) {
543 // Remove the initiator's observers before erasing the mapping.
544 if (operation
->initiator
) {
545 operation
->initiator_observer
.StopObserving();
546 PrintViewManager::FromWebContents(operation
->initiator
)->
550 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to
551 // abort the initiator preview request.
552 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
553 preview_dialog
->GetWebUI()->GetController());
554 if (print_preview_ui
)
555 print_preview_ui
->OnPrintPreviewDialogDestroyed();
557 preview_operations_
.erase(preview_operations_
.begin() + i
);
566 } // namespace printing