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