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/command_line.h"
13 #include "base/path_service.h"
14 #include "base/utf_string_conversions.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_notification_types.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/url_constants.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/navigation_details.h"
34 #include "content/public/browser/navigation_entry.h"
35 #include "content/public/browser/notification_details.h"
36 #include "content/public/browser/notification_source.h"
37 #include "content/public/browser/plugin_service.h"
38 #include "content/public/browser/render_process_host.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_contents_delegate.h"
42 #include "content/public/browser/web_contents_view.h"
43 #include "ui/web_dialogs/web_dialog_delegate.h"
44 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
45 #include "webkit/plugins/webplugininfo.h"
47 using content::NativeWebKeyboardEvent
;
48 using content::NavigationController
;
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 webkit::WebPluginInfo pdf_plugin
;
63 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
64 pdf_plugin_path
, &pdf_plugin
))
67 ChromePluginServiceFilter::GetInstance()->OverridePluginForTab(
68 preview_dialog
->GetRenderProcessHost()->GetID(),
69 preview_dialog
->GetRenderViewHost()->GetRoutingID(),
73 // WebDialogDelegate that specifies what the print preview dialog
75 class PrintPreviewDialogDelegate
: public WebDialogDelegate
{
77 explicit PrintPreviewDialogDelegate(WebContents
* initiator_tab
);
78 virtual ~PrintPreviewDialogDelegate();
80 virtual ui::ModalType
GetDialogModalType() const OVERRIDE
;
81 virtual 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_tab_
;
95 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate
);
98 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(
99 WebContents
* initiator_tab
) {
100 initiator_tab_
= initiator_tab
;
103 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
106 ui::ModalType
PrintPreviewDialogDelegate::GetDialogModalType() const {
107 // Not used, returning dummy value.
109 return ui::MODAL_TYPE_WINDOW
;
112 string16
PrintPreviewDialogDelegate::GetDialogTitle() const {
113 // Only used on Windows? UI folks prefer no title.
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
= 50;
131 initiator_tab_
->GetView()->GetContainerBounds(&rect
);
132 size
->set_width(std::max(rect
.width(), kMinDialogSize
.width()) - kBorder
);
133 size
->set_height(std::max(rect
.height(), kMinDialogSize
.height()) - kBorder
);
135 #if defined(OS_MACOSX)
136 // Limit the maximum size on MacOS X.
137 // http://crbug.com/105815
138 const gfx::Size
kMaxDialogSize(1000, 660);
139 size
->set_width(std::min(size
->width(), kMaxDialogSize
.width()));
140 size
->set_height(std::min(size
->height(), kMaxDialogSize
.height()));
144 std::string
PrintPreviewDialogDelegate::GetDialogArgs() const {
145 return std::string();
148 void PrintPreviewDialogDelegate::OnDialogClosed(
149 const std::string
& /* json_retval */) {
152 void PrintPreviewDialogDelegate::OnCloseContents(WebContents
* /* source */,
153 bool* out_close_dialog
) {
154 if (out_close_dialog
)
155 *out_close_dialog
= true;
158 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
159 // Not used, returning dummy value.
164 // WebContentsDelegate that forwards shortcut keys in the print preview
165 // renderer to the browser.
166 class PrintPreviewWebContentDelegate
: public WebDialogWebContentsDelegate
{
168 PrintPreviewWebContentDelegate(Profile
* profile
, WebContents
* initiator_tab
);
169 virtual ~PrintPreviewWebContentDelegate();
171 // Overridden from WebDialogWebContentsDelegate:
172 virtual void HandleKeyboardEvent(
174 const NativeWebKeyboardEvent
& event
) OVERRIDE
;
179 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate
);
182 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
184 WebContents
* initiator_tab
)
185 : WebDialogWebContentsDelegate(profile
, new ChromeWebContentsHandler
),
186 tab_(initiator_tab
) {}
188 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
190 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
192 const NativeWebKeyboardEvent
& event
) {
193 // Disabled on Mac due to http://crbug.com/112173
194 #if !defined(OS_MACOSX)
195 Browser
* current_browser
= chrome::FindBrowserWithWebContents(tab_
);
196 if (!current_browser
)
198 current_browser
->window()->HandleKeyboardEvent(event
);
206 PrintPreviewDialogController::PrintPreviewDialogController()
207 : waiting_for_new_preview_page_(false),
208 is_creating_print_preview_dialog_(false) {
212 PrintPreviewDialogController
* PrintPreviewDialogController::GetInstance() {
213 if (!g_browser_process
)
215 return g_browser_process
->print_preview_dialog_controller();
219 void PrintPreviewDialogController::PrintPreview(WebContents
* initiator_tab
) {
220 if (initiator_tab
->ShowingInterstitialPage())
223 PrintPreviewDialogController
* dialog_controller
= GetInstance();
224 if (!dialog_controller
)
226 if (!dialog_controller
->GetOrCreatePreviewDialog(initiator_tab
))
227 PrintViewManager::FromWebContents(initiator_tab
)->PrintPreviewDone();
230 WebContents
* PrintPreviewDialogController::GetOrCreatePreviewDialog(
231 WebContents
* initiator_tab
) {
232 DCHECK(initiator_tab
);
234 // Get the print preview dialog for |initiator_tab|.
235 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator_tab
);
237 return CreatePrintPreviewDialog(initiator_tab
);
239 // Show the initiator tab holding the existing preview dialog.
240 initiator_tab
->GetDelegate()->ActivateContents(initiator_tab
);
241 return preview_dialog
;
244 WebContents
* PrintPreviewDialogController::GetPrintPreviewForContents(
245 WebContents
* contents
) const {
246 // |preview_dialog_map_| is keyed by the preview dialog, so if find()
247 // succeeds, then |contents| is the preview dialog.
248 PrintPreviewDialogMap::const_iterator it
= preview_dialog_map_
.find(contents
);
249 if (it
!= preview_dialog_map_
.end())
252 for (it
= preview_dialog_map_
.begin();
253 it
!= preview_dialog_map_
.end();
255 // If |contents| is an initiator tab.
256 if (contents
== it
->second
) {
257 // Return the associated preview dialog.
264 WebContents
* PrintPreviewDialogController::GetInitiatorTab(
265 WebContents
* preview_dialog
) {
266 PrintPreviewDialogMap::iterator it
= preview_dialog_map_
.find(preview_dialog
);
267 return (it
!= preview_dialog_map_
.end()) ? it
->second
: NULL
;
270 void PrintPreviewDialogController::Observe(
272 const content::NotificationSource
& source
,
273 const content::NotificationDetails
& details
) {
274 if (type
== content::NOTIFICATION_RENDERER_PROCESS_CLOSED
) {
275 OnRendererProcessClosed(
276 content::Source
<content::RenderProcessHost
>(source
).ptr());
277 } else if (type
== content::NOTIFICATION_WEB_CONTENTS_DESTROYED
) {
278 OnWebContentsDestroyed(content::Source
<WebContents
>(source
).ptr());
280 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED
, type
);
281 WebContents
* contents
=
282 content::Source
<NavigationController
>(source
)->GetWebContents();
285 content::Details
<content::LoadCommittedDetails
>(details
).ptr());
290 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents
* contents
) {
291 return IsPrintPreviewURL(contents
->GetURL());
295 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL
& url
) {
296 return (url
.SchemeIs(chrome::kChromeUIScheme
) &&
297 url
.host() == chrome::kChromeUIPrintHost
);
300 void PrintPreviewDialogController::EraseInitiatorTabInfo(
301 WebContents
* preview_dialog
) {
302 PrintPreviewDialogMap::iterator it
= preview_dialog_map_
.find(preview_dialog
);
303 if (it
== preview_dialog_map_
.end())
306 RemoveObservers(it
->second
);
307 preview_dialog_map_
[preview_dialog
] = NULL
;
310 PrintPreviewDialogController::~PrintPreviewDialogController() {}
312 void PrintPreviewDialogController::OnRendererProcessClosed(
313 content::RenderProcessHost
* rph
) {
314 // Store contents in a vector and deal with them after iterating through
315 // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
316 std::vector
<WebContents
*> closed_initiator_tabs
;
317 std::vector
<WebContents
*> closed_preview_dialogs
;
318 for (PrintPreviewDialogMap::iterator iter
= preview_dialog_map_
.begin();
319 iter
!= preview_dialog_map_
.end(); ++iter
) {
320 WebContents
* preview_dialog
= iter
->first
;
321 WebContents
* initiator_tab
= iter
->second
;
322 if (preview_dialog
->GetRenderProcessHost() == rph
) {
323 closed_preview_dialogs
.push_back(preview_dialog
);
324 } else if (initiator_tab
&&
325 initiator_tab
->GetRenderProcessHost() == rph
) {
326 closed_initiator_tabs
.push_back(initiator_tab
);
330 for (size_t i
= 0; i
< closed_preview_dialogs
.size(); ++i
) {
331 RemovePreviewDialog(closed_preview_dialogs
[i
]);
332 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
333 closed_preview_dialogs
[i
]->GetWebUI()->GetController());
334 if (print_preview_ui
)
335 print_preview_ui
->OnPrintPreviewDialogClosed();
338 for (size_t i
= 0; i
< closed_initiator_tabs
.size(); ++i
)
339 RemoveInitiatorTab(closed_initiator_tabs
[i
]);
342 void PrintPreviewDialogController::OnWebContentsDestroyed(
343 WebContents
* contents
) {
344 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
345 if (!preview_dialog
) {
350 if (contents
== preview_dialog
)
351 RemovePreviewDialog(contents
);
353 RemoveInitiatorTab(contents
);
356 void PrintPreviewDialogController::OnNavEntryCommitted(
357 WebContents
* contents
, content::LoadCommittedDetails
* details
) {
358 WebContents
* preview_dialog
= GetPrintPreviewForContents(contents
);
359 if (!preview_dialog
) {
364 if (contents
== preview_dialog
) {
365 // Preview dialog navigated.
367 content::PageTransition transition_type
=
368 details
->entry
->GetTransitionType();
369 content::NavigationType nav_type
= details
->type
;
371 // New |preview_dialog| is created. Don't update/erase map entry.
372 if (waiting_for_new_preview_page_
&&
373 transition_type
== content::PAGE_TRANSITION_AUTO_TOPLEVEL
&&
374 nav_type
== content::NAVIGATION_TYPE_NEW_PAGE
) {
375 waiting_for_new_preview_page_
= false;
376 SaveInitiatorTabTitle(preview_dialog
);
380 // Cloud print sign-in causes a reload.
381 if (!waiting_for_new_preview_page_
&&
382 transition_type
== content::PAGE_TRANSITION_RELOAD
&&
383 nav_type
== content::NAVIGATION_TYPE_EXISTING_PAGE
&&
384 IsPrintPreviewURL(details
->previous_url
)) {
392 RemoveInitiatorTab(contents
);
395 WebContents
* PrintPreviewDialogController::CreatePrintPreviewDialog(
396 WebContents
* initiator_tab
) {
397 base::AutoReset
<bool> auto_reset(&is_creating_print_preview_dialog_
, true);
399 Profile::FromBrowserContext(initiator_tab
->GetBrowserContext());
400 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame
)) {
401 // Chrome Frame only ever runs on the native desktop, so it is safe to
402 // create the popup on the native desktop.
403 Browser
* current_browser
= new Browser(
404 Browser::CreateParams(Browser::TYPE_POPUP
, profile
,
405 chrome::HOST_DESKTOP_TYPE_NATIVE
));
406 if (!current_browser
) {
407 NOTREACHED() << "Failed to create popup browser window";
412 // |web_dialog_ui_delegate| deletes itself in
413 // PrintPreviewDialogDelegate::OnDialogClosed().
414 WebDialogDelegate
* web_dialog_delegate
=
415 new PrintPreviewDialogDelegate(initiator_tab
);
416 // |web_dialog_delegate|'s owner is |constrained_delegate|.
417 PrintPreviewWebContentDelegate
* pp_wcd
=
418 new PrintPreviewWebContentDelegate(profile
, initiator_tab
);
419 ConstrainedWebDialogDelegate
* constrained_delegate
=
420 CreateConstrainedWebDialog(profile
,
424 WebContents
* preview_dialog
= constrained_delegate
->GetWebContents();
425 EnableInternalPDFPluginForContents(preview_dialog
);
426 PrintViewManager::CreateForWebContents(preview_dialog
);
428 // Add an entry to the map.
429 preview_dialog_map_
[preview_dialog
] = initiator_tab
;
430 waiting_for_new_preview_page_
= true;
432 AddObservers(initiator_tab
);
433 AddObservers(preview_dialog
);
435 return preview_dialog
;
438 void PrintPreviewDialogController::SaveInitiatorTabTitle(
439 WebContents
* preview_dialog
) {
440 WebContents
* initiator_tab
= GetInitiatorTab(preview_dialog
);
441 if (initiator_tab
&& preview_dialog
->GetWebUI()) {
442 PrintPreviewUI
* print_preview_ui
= static_cast<PrintPreviewUI
*>(
443 preview_dialog
->GetWebUI()->GetController());
444 print_preview_ui
->SetInitiatorTabTitle(
445 PrintViewManager::FromWebContents(initiator_tab
)->RenderSourceName());
449 void PrintPreviewDialogController::AddObservers(WebContents
* contents
) {
450 registrar_
.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
451 content::Source
<WebContents
>(contents
));
452 registrar_
.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
453 content::Source
<NavigationController
>(&contents
->GetController()));
455 // Multiple sites may share the same RenderProcessHost, so check if this
456 // notification has already been added.
457 content::Source
<content::RenderProcessHost
> rph_source(
458 contents
->GetRenderProcessHost());
459 if (!registrar_
.IsRegistered(this,
460 content::NOTIFICATION_RENDERER_PROCESS_CLOSED
, rph_source
)) {
461 registrar_
.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
466 void PrintPreviewDialogController::RemoveObservers(WebContents
* contents
) {
467 registrar_
.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
468 content::Source
<WebContents
>(contents
));
469 registrar_
.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
470 content::Source
<NavigationController
>(&contents
->GetController()));
472 // Multiple sites may share the same RenderProcessHost, so check if this
473 // notification has already been added.
474 content::Source
<content::RenderProcessHost
> rph_source(
475 contents
->GetRenderProcessHost());
476 if (registrar_
.IsRegistered(this,
477 content::NOTIFICATION_RENDERER_PROCESS_CLOSED
, rph_source
)) {
478 registrar_
.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
483 void PrintPreviewDialogController::RemoveInitiatorTab(
484 WebContents
* initiator_tab
) {
485 WebContents
* preview_dialog
= GetPrintPreviewForContents(initiator_tab
);
486 DCHECK(preview_dialog
);
487 // Update the map entry first, so when the print preview dialog gets destroyed
488 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
489 // initiator tab's observers.
490 preview_dialog_map_
[preview_dialog
] = NULL
;
491 RemoveObservers(initiator_tab
);
493 PrintViewManager::FromWebContents(initiator_tab
)->PrintPreviewDone();
495 // Initiator tab is closed. Close the print preview dialog too.
496 PrintPreviewUI
* print_preview_ui
=
497 static_cast<PrintPreviewUI
*>(preview_dialog
->GetWebUI()->GetController());
498 if (print_preview_ui
)
499 print_preview_ui
->OnInitiatorTabClosed();
502 void PrintPreviewDialogController::RemovePreviewDialog(
503 WebContents
* preview_dialog
) {
504 // Remove the initiator tab's observers before erasing the mapping.
505 WebContents
* initiator_tab
= GetInitiatorTab(preview_dialog
);
507 RemoveObservers(initiator_tab
);
508 PrintViewManager::FromWebContents(initiator_tab
)->PrintPreviewDone();
511 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to abort
512 // the initiator tab preview request.
513 PrintPreviewUI
* print_preview_ui
=
514 static_cast<PrintPreviewUI
*>(preview_dialog
->GetWebUI()->GetController());
515 if (print_preview_ui
)
516 print_preview_ui
->OnPrintPreviewDialogDestroyed();
518 preview_dialog_map_
.erase(preview_dialog
);
519 RemoveObservers(preview_dialog
);
522 } // namespace printing