Fix build break
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_dialog_controller.cc
blob85b782663bff959a4ad0a52717e813ee05156008
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>
9 #include <vector>
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;
54 namespace {
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))
60 return;
62 webkit::WebPluginInfo pdf_plugin;
63 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
64 pdf_plugin_path, &pdf_plugin))
65 return;
67 ChromePluginServiceFilter::GetInstance()->OverridePluginForTab(
68 preview_dialog->GetRenderProcessHost()->GetID(),
69 preview_dialog->GetRenderViewHost()->GetRoutingID(),
70 GURL(), pdf_plugin);
73 // WebDialogDelegate that specifies what the print preview dialog
74 // will look like.
75 class PrintPreviewDialogDelegate : public WebDialogDelegate {
76 public:
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;
92 private:
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.
108 NOTREACHED();
109 return ui::MODAL_TYPE_WINDOW;
112 string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
113 // Only used on Windows? UI folks prefer no title.
114 return 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 = 50;
130 gfx::Rect rect;
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()));
141 #endif
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.
160 NOTREACHED();
161 return false;
164 // WebContentsDelegate that forwards shortcut keys in the print preview
165 // renderer to the browser.
166 class PrintPreviewWebContentDelegate : public WebDialogWebContentsDelegate {
167 public:
168 PrintPreviewWebContentDelegate(Profile* profile, WebContents* initiator_tab);
169 virtual ~PrintPreviewWebContentDelegate();
171 // Overridden from WebDialogWebContentsDelegate:
172 virtual void HandleKeyboardEvent(
173 WebContents* source,
174 const NativeWebKeyboardEvent& event) OVERRIDE;
176 private:
177 WebContents* tab_;
179 DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
182 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
183 Profile* profile,
184 WebContents* initiator_tab)
185 : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
186 tab_(initiator_tab) {}
188 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
190 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
191 WebContents* source,
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)
197 return;
198 current_browser->window()->HandleKeyboardEvent(event);
199 #endif
202 } // namespace
204 namespace printing {
206 PrintPreviewDialogController::PrintPreviewDialogController()
207 : waiting_for_new_preview_page_(false),
208 is_creating_print_preview_dialog_(false) {
211 // static
212 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
213 if (!g_browser_process)
214 return NULL;
215 return g_browser_process->print_preview_dialog_controller();
218 // static
219 void PrintPreviewDialogController::PrintPreview(WebContents* initiator_tab) {
220 if (initiator_tab->ShowingInterstitialPage())
221 return;
223 PrintPreviewDialogController* dialog_controller = GetInstance();
224 if (!dialog_controller)
225 return;
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);
236 if (!preview_dialog)
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())
250 return contents;
252 for (it = preview_dialog_map_.begin();
253 it != preview_dialog_map_.end();
254 ++it) {
255 // If |contents| is an initiator tab.
256 if (contents == it->second) {
257 // Return the associated preview dialog.
258 return it->first;
261 return NULL;
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(
271 int type,
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());
279 } else {
280 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
281 WebContents* contents =
282 content::Source<NavigationController>(source)->GetWebContents();
283 OnNavEntryCommitted(
284 contents,
285 content::Details<content::LoadCommittedDetails>(details).ptr());
289 // static
290 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
291 return IsPrintPreviewURL(contents->GetURL());
294 // static
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())
304 return;
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) {
346 NOTREACHED();
347 return;
350 if (contents == preview_dialog)
351 RemovePreviewDialog(contents);
352 else
353 RemoveInitiatorTab(contents);
356 void PrintPreviewDialogController::OnNavEntryCommitted(
357 WebContents* contents, content::LoadCommittedDetails* details) {
358 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
359 if (!preview_dialog) {
360 NOTREACHED();
361 return;
364 if (contents == preview_dialog) {
365 // Preview dialog navigated.
366 if (details) {
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);
377 return;
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)) {
385 return;
388 NOTREACHED();
389 return;
392 RemoveInitiatorTab(contents);
395 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
396 WebContents* initiator_tab) {
397 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
398 Profile* profile =
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";
408 return NULL;
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,
421 web_dialog_delegate,
422 pp_wcd,
423 initiator_tab);
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,
462 rph_source);
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,
479 rph_source);
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);
506 if (initiator_tab) {
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