Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / printing / print_preview_dialog_controller.cc
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.
4
5 #include "chrome/browser/printing/print_preview_dialog_controller.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/auto_reset.h"
12 #include "base/path_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
17 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
18 #include "chrome/browser/printing/print_view_manager.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/browser_navigator.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/host_desktop.h"
25 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
26 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
27 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
28 #include "chrome/common/chrome_content_client.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/url_constants.h"
31 #include "components/web_modal/web_contents_modal_dialog_host.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_frame_host.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/render_view_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/browser/web_contents_delegate.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"
46
47 using content::NativeWebKeyboardEvent;
48 using content::NavigationController;
49 using content::WebContents;
50 using content::WebUIMessageHandler;
51 using ui::WebDialogDelegate;
52 using ui::WebDialogWebContentsDelegate;
53
54 namespace {
55
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;
61
62   content::WebPluginInfo pdf_plugin;
63   if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
64       pdf_plugin_path, &pdf_plugin))
65     return;
66
67   ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
68       preview_dialog->GetRenderProcessHost()->GetID(),
69       preview_dialog->GetMainFrame()->GetRoutingID(),
70       GURL(), pdf_plugin);
71 }
72
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);
78   virtual ~PrintPreviewDialogDelegate();
79
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;
91
92  private:
93   WebContents* initiator_;
94
95   DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
96 };
97
98 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
99     : initiator_(initiator) {
100 }
101
102 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
103 }
104
105 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
106   // Not used, returning dummy value.
107   NOTREACHED();
108   return ui::MODAL_TYPE_WINDOW;
109 }
110
111 base::string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
112   // Only used on Windows? UI folks prefer no title.
113   return base::string16();
114 }
115
116 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
117   return GURL(chrome::kChromeUIPrintURL);
118 }
119
120 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
121     std::vector<WebUIMessageHandler*>* /* handlers */) const {
122   // PrintPreviewUI adds its own message handlers.
123 }
124
125 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size* size) const {
126   DCHECK(size);
127   const gfx::Size kMinDialogSize(800, 480);
128   const int kBorder = 25;
129   *size = kMinDialogSize;
130
131   web_modal::WebContentsModalDialogHost* host = NULL;
132   Browser* browser = chrome::FindBrowserWithWebContents(initiator_);
133   if (browser)
134     host = browser->window()->GetWebContentsModalDialogHost();
135
136   if (host) {
137     size->SetToMax(host->GetMaximumDialogSize());
138     size->Enlarge(-2 * kBorder, -kBorder);
139   } else {
140     size->SetToMax(initiator_->GetContainerBounds().size());
141     size->Enlarge(-2 * kBorder, -2 * kBorder);
142   }
143
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);
149 #endif
150 }
151
152 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
153   return std::string();
154 }
155
156 void PrintPreviewDialogDelegate::OnDialogClosed(
157     const std::string& /* json_retval */) {
158 }
159
160 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
161                                                  bool* out_close_dialog) {
162   if (out_close_dialog)
163     *out_close_dialog = true;
164 }
165
166 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
167   return false;
168 }
169
170 // WebContentsDelegate that forwards shortcut keys in the print preview
171 // renderer to the browser.
172 class PrintPreviewWebContentDelegate : public WebDialogWebContentsDelegate {
173  public:
174   PrintPreviewWebContentDelegate(Profile* profile, WebContents* initiator);
175   virtual ~PrintPreviewWebContentDelegate();
176
177   // Overridden from WebDialogWebContentsDelegate:
178   virtual void HandleKeyboardEvent(
179       WebContents* source,
180       const NativeWebKeyboardEvent& event) OVERRIDE;
181
182  private:
183   WebContents* initiator_;
184
185   DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
186 };
187
188 PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
189     Profile* profile,
190     WebContents* initiator)
191     : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
192       initiator_(initiator) {}
193
194 PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
195
196 void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
197     WebContents* source,
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)
203     return;
204   current_browser->window()->HandleKeyboardEvent(event);
205 #endif
206 }
207
208 }  // namespace
209
210 namespace printing {
211
212 PrintPreviewDialogController::PrintPreviewDialogController()
213     : waiting_for_new_preview_page_(false),
214       is_creating_print_preview_dialog_(false) {
215 }
216
217 // static
218 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
219   if (!g_browser_process)
220     return NULL;
221   return g_browser_process->print_preview_dialog_controller();
222 }
223
224 // static
225 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
226   if (initiator->ShowingInterstitialPage())
227     return;
228
229   PrintPreviewDialogController* dialog_controller = GetInstance();
230   if (!dialog_controller)
231     return;
232   if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
233     PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
234 }
235
236 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
237     WebContents* initiator) {
238   DCHECK(initiator);
239
240   // Get the print preview dialog for |initiator|.
241   WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
242   if (!preview_dialog)
243     return CreatePrintPreviewDialog(initiator);
244
245   // Show the initiator holding the existing preview dialog.
246   initiator->GetDelegate()->ActivateContents(initiator);
247   return preview_dialog;
248 }
249
250 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
251     WebContents* contents) const {
252   // |preview_dialog_map_| is keyed by the preview dialog, so if find()
253   // succeeds, then |contents| is the preview dialog.
254   PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.find(contents);
255   if (it != preview_dialog_map_.end())
256     return contents;
257
258   for (it = preview_dialog_map_.begin();
259        it != preview_dialog_map_.end();
260        ++it) {
261     // If |contents| is an initiator.
262     if (contents == it->second) {
263       // Return the associated preview dialog.
264       return it->first;
265     }
266   }
267   return NULL;
268 }
269
270 WebContents* PrintPreviewDialogController::GetInitiator(
271     WebContents* preview_dialog) {
272   PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
273   return (it != preview_dialog_map_.end()) ? it->second : NULL;
274 }
275
276 void PrintPreviewDialogController::Observe(
277     int type,
278     const content::NotificationSource& source,
279     const content::NotificationDetails& details) {
280   if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
281     OnRendererProcessClosed(
282         content::Source<content::RenderProcessHost>(source).ptr());
283   } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
284     OnWebContentsDestroyed(content::Source<WebContents>(source).ptr());
285   } else {
286     DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
287     WebContents* contents =
288         content::Source<NavigationController>(source)->GetWebContents();
289     OnNavEntryCommitted(
290         contents,
291         content::Details<content::LoadCommittedDetails>(details).ptr());
292   }
293 }
294
295 void PrintPreviewDialogController::ForEachPreviewDialog(
296     base::Callback<void(content::WebContents*)> callback) {
297   for (PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.begin();
298        it != preview_dialog_map_.end();
299        ++it) {
300     callback.Run(it->first);
301   }
302 }
303
304 // static
305 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
306   return IsPrintPreviewURL(contents->GetURL());
307 }
308
309 // static
310 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
311   return (url.SchemeIs(content::kChromeUIScheme) &&
312           url.host() == chrome::kChromeUIPrintHost);
313 }
314
315 void PrintPreviewDialogController::EraseInitiatorInfo(
316     WebContents* preview_dialog) {
317   PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
318   if (it == preview_dialog_map_.end())
319     return;
320
321   RemoveObservers(it->second);
322   preview_dialog_map_[preview_dialog] = NULL;
323 }
324
325 PrintPreviewDialogController::~PrintPreviewDialogController() {}
326
327 void PrintPreviewDialogController::OnRendererProcessClosed(
328     content::RenderProcessHost* rph) {
329   // Store contents in a vector and deal with them after iterating through
330   // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
331   std::vector<WebContents*> closed_initiators;
332   std::vector<WebContents*> closed_preview_dialogs;
333   for (PrintPreviewDialogMap::iterator iter = preview_dialog_map_.begin();
334        iter != preview_dialog_map_.end(); ++iter) {
335     WebContents* preview_dialog = iter->first;
336     WebContents* initiator = iter->second;
337     if (preview_dialog->GetRenderProcessHost() == rph) {
338       closed_preview_dialogs.push_back(preview_dialog);
339     } else if (initiator &&
340                initiator->GetRenderProcessHost() == rph) {
341       closed_initiators.push_back(initiator);
342     }
343   }
344
345   for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
346     RemovePreviewDialog(closed_preview_dialogs[i]);
347     if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
348       PrintPreviewUI* print_preview_ui =
349           static_cast<PrintPreviewUI*>(web_ui->GetController());
350       if (print_preview_ui)
351         print_preview_ui->OnPrintPreviewDialogClosed();
352     }
353   }
354
355   for (size_t i = 0; i < closed_initiators.size(); ++i)
356     RemoveInitiator(closed_initiators[i]);
357 }
358
359 void PrintPreviewDialogController::OnWebContentsDestroyed(
360     WebContents* contents) {
361   WebContents* preview_dialog = GetPrintPreviewForContents(contents);
362   if (!preview_dialog) {
363     NOTREACHED();
364     return;
365   }
366
367   if (contents == preview_dialog)
368     RemovePreviewDialog(contents);
369   else
370     RemoveInitiator(contents);
371 }
372
373 void PrintPreviewDialogController::OnNavEntryCommitted(
374     WebContents* contents, content::LoadCommittedDetails* details) {
375   WebContents* preview_dialog = GetPrintPreviewForContents(contents);
376   if (!preview_dialog) {
377     NOTREACHED();
378     return;
379   }
380
381   if (contents == preview_dialog) {
382     // Preview dialog navigated.
383     if (details) {
384       content::PageTransition transition_type =
385           details->entry->GetTransitionType();
386       content::NavigationType nav_type = details->type;
387
388       // New |preview_dialog| is created. Don't update/erase map entry.
389       if (waiting_for_new_preview_page_ &&
390           transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
391           nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
392         waiting_for_new_preview_page_ = false;
393         SaveInitiatorTitle(preview_dialog);
394         return;
395       }
396
397       // Cloud print sign-in causes a reload.
398       if (!waiting_for_new_preview_page_ &&
399           transition_type == content::PAGE_TRANSITION_RELOAD &&
400           nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
401           IsPrintPreviewURL(details->previous_url)) {
402         return;
403       }
404     }
405     NOTREACHED();
406     return;
407   }
408
409   RemoveInitiator(contents);
410 }
411
412 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
413     WebContents* initiator) {
414   base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
415   Profile* profile =
416       Profile::FromBrowserContext(initiator->GetBrowserContext());
417
418   // |web_dialog_ui_delegate| deletes itself in
419   // PrintPreviewDialogDelegate::OnDialogClosed().
420   WebDialogDelegate* web_dialog_delegate =
421       new PrintPreviewDialogDelegate(initiator);
422   // |web_dialog_delegate|'s owner is |constrained_delegate|.
423   PrintPreviewWebContentDelegate* pp_wcd =
424       new PrintPreviewWebContentDelegate(profile, initiator);
425   ConstrainedWebDialogDelegate* constrained_delegate =
426       CreateConstrainedWebDialog(profile,
427                                  web_dialog_delegate,
428                                  pp_wcd,
429                                  initiator);
430   WebContents* preview_dialog = constrained_delegate->GetWebContents();
431   EnableInternalPDFPluginForContents(preview_dialog);
432   PrintViewManager::CreateForWebContents(preview_dialog);
433   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
434       preview_dialog);
435
436   // Add an entry to the map.
437   preview_dialog_map_[preview_dialog] = initiator;
438   waiting_for_new_preview_page_ = true;
439
440   AddObservers(initiator);
441   AddObservers(preview_dialog);
442
443   return preview_dialog;
444 }
445
446 void PrintPreviewDialogController::SaveInitiatorTitle(
447     WebContents* preview_dialog) {
448   WebContents* initiator = GetInitiator(preview_dialog);
449   if (initiator && preview_dialog->GetWebUI()) {
450     PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
451         preview_dialog->GetWebUI()->GetController());
452     print_preview_ui->SetInitiatorTitle(
453         PrintViewManager::FromWebContents(initiator)->RenderSourceName());
454   }
455 }
456
457 void PrintPreviewDialogController::AddObservers(WebContents* contents) {
458   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
459                  content::Source<WebContents>(contents));
460   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
461       content::Source<NavigationController>(&contents->GetController()));
462
463   // Multiple sites may share the same RenderProcessHost, so check if this
464   // notification has already been added.
465   content::Source<content::RenderProcessHost> rph_source(
466       contents->GetRenderProcessHost());
467   if (!registrar_.IsRegistered(this,
468       content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
469     registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
470                    rph_source);
471   }
472 }
473
474 void PrintPreviewDialogController::RemoveObservers(WebContents* contents) {
475   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
476                     content::Source<WebContents>(contents));
477   registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
478       content::Source<NavigationController>(&contents->GetController()));
479
480   // Multiple sites may share the same RenderProcessHost, so check if this
481   // notification has already been added.
482   content::Source<content::RenderProcessHost> rph_source(
483       contents->GetRenderProcessHost());
484   if (registrar_.IsRegistered(this,
485       content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
486     registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
487                       rph_source);
488   }
489 }
490
491 void PrintPreviewDialogController::RemoveInitiator(
492     WebContents* initiator) {
493   WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
494   DCHECK(preview_dialog);
495   // Update the map entry first, so when the print preview dialog gets destroyed
496   // and reaches RemovePreviewDialog(), it does not attempt to also remove the
497   // initiator's observers.
498   preview_dialog_map_[preview_dialog] = NULL;
499   RemoveObservers(initiator);
500
501   PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
502
503   // initiator is closed. Close the print preview dialog too.
504   if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
505     PrintPreviewUI* print_preview_ui =
506         static_cast<PrintPreviewUI*>(web_ui->GetController());
507     if (print_preview_ui)
508       print_preview_ui->OnInitiatorClosed();
509   }
510 }
511
512 void PrintPreviewDialogController::RemovePreviewDialog(
513     WebContents* preview_dialog) {
514   // Remove the initiator's observers before erasing the mapping.
515   WebContents* initiator = GetInitiator(preview_dialog);
516   if (initiator) {
517     RemoveObservers(initiator);
518     PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
519   }
520
521   // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to abort
522   // the initiator preview request.
523   if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
524     PrintPreviewUI* print_preview_ui =
525         static_cast<PrintPreviewUI*>(web_ui->GetController());
526     if (print_preview_ui)
527       print_preview_ui->OnPrintPreviewDialogDestroyed();
528   }
529
530   preview_dialog_map_.erase(preview_dialog);
531   RemoveObservers(preview_dialog);
532 }
533
534 }  // namespace printing