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