Upstream version 5.34.104.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
10 #include "base/auto_reset.h"
11 #include "base/path_service.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
16 #include "chrome/browser/printing/print_view_manager.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_navigator.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/host_desktop.h"
23 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
24 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
25 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
26 #include "chrome/common/chrome_content_client.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/web_modal/web_contents_modal_dialog_host.h"
30 #include "content/public/browser/navigation_controller.h"
31 #include "content/public/browser/navigation_details.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/plugin_service.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/browser/render_process_host_observer.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_contents_delegate.h"
40 #include "content/public/browser/web_contents_observer.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::RenderProcessHost;
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_->GetView()->GetContainerSize());
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 struct PrintPreviewDialogController::Operation {
213   class Observer : public content::WebContentsObserver,
214                    public content::RenderProcessHostObserver {
215    public:
216     Observer();
217     virtual ~Observer();
218
219     void StartObserving(PrintPreviewDialogController* owner,
220                         WebContents* web_contents);
221     void StopObserving();
222
223    private:
224     // content::WebContentsObserver
225     virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
226     virtual void NavigationEntryCommitted(
227         const content::LoadCommittedDetails& load_details) OVERRIDE;
228     // content::RenderProcessHostObserver
229     virtual void RenderProcessExited(RenderProcessHost* host,
230                                      base::ProcessHandle handle,
231                                      base::TerminationStatus status,
232                                      int exit_code) OVERRIDE;
233     virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
234
235     PrintPreviewDialogController* owner_;
236     RenderProcessHost* host_;
237   };
238
239   Operation();
240
241   WebContents* preview_dialog;
242   WebContents* initiator;
243   Observer preview_dialog_observer;
244   Observer initiator_observer;
245
246   DISALLOW_COPY_AND_ASSIGN(Operation);
247 };
248
249 PrintPreviewDialogController::Operation::Operation() : preview_dialog(NULL),
250                                                        initiator(NULL) {
251 }
252
253 PrintPreviewDialogController::Operation::Observer::Observer() : owner_(NULL),
254                                                                 host_(NULL) {
255 }
256
257 PrintPreviewDialogController::Operation::Observer::~Observer() {
258   StopObserving();
259 }
260
261 void PrintPreviewDialogController::Operation::Observer::StartObserving(
262     PrintPreviewDialogController* owner,
263     WebContents* web_contents) {
264   owner_ = owner;
265   Observe(web_contents);
266   host_ = web_contents->GetRenderProcessHost();
267   host_->AddObserver(this);
268 }
269
270 void PrintPreviewDialogController::Operation::Observer::StopObserving() {
271   Observe(NULL);
272   if (host_) {
273     host_->RemoveObserver(this);
274     host_ = NULL;
275   }
276 }
277
278 void PrintPreviewDialogController::Operation::Observer::WebContentsDestroyed(
279     WebContents* web_contents) {
280   owner_->OnWebContentsDestroyed(web_contents);
281 }
282
283 void
284 PrintPreviewDialogController::Operation::Observer::NavigationEntryCommitted(
285     const content::LoadCommittedDetails& load_details) {
286   owner_->OnNavigationEntryCommitted(web_contents(), &load_details);
287 }
288
289 void PrintPreviewDialogController::Operation::Observer::RenderProcessExited(
290     RenderProcessHost* host,
291     base::ProcessHandle handle,
292     base::TerminationStatus status,
293     int exit_code) {
294   owner_->OnRenderProcessExited(host);
295 }
296
297 void
298 PrintPreviewDialogController::Operation::Observer::RenderProcessHostDestroyed(
299     RenderProcessHost* host) {
300   host_ = NULL;
301 }
302
303 PrintPreviewDialogController::PrintPreviewDialogController()
304     : waiting_for_new_preview_page_(false),
305       is_creating_print_preview_dialog_(false) {
306 }
307
308 // static
309 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
310   if (!g_browser_process)
311     return NULL;
312   return g_browser_process->print_preview_dialog_controller();
313 }
314
315 // static
316 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
317   if (initiator->ShowingInterstitialPage())
318     return;
319
320   PrintPreviewDialogController* dialog_controller = GetInstance();
321   if (!dialog_controller)
322     return;
323   if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
324     PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
325 }
326
327 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
328     WebContents* initiator) {
329   DCHECK(initiator);
330
331   // Get the print preview dialog for |initiator|.
332   WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
333   if (!preview_dialog)
334     return CreatePrintPreviewDialog(initiator);
335
336   // Show the initiator holding the existing preview dialog.
337   initiator->GetDelegate()->ActivateContents(initiator);
338   return preview_dialog;
339 }
340
341 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
342     WebContents* contents) const {
343   for (size_t i = 0; i < preview_operations_.size(); ++i) {
344     Operation* operation = preview_operations_[i];
345     if (operation->preview_dialog == contents ||
346         operation->initiator == contents) {
347       return operation->preview_dialog;
348     }
349   }
350   return NULL;
351 }
352
353 WebContents* PrintPreviewDialogController::GetInitiator(
354     WebContents* preview_dialog) {
355   for (size_t i = 0; i < preview_operations_.size(); ++i) {
356     Operation* operation = preview_operations_[i];
357     if (operation->preview_dialog == preview_dialog)
358       return operation->initiator;
359   }
360   return NULL;
361 }
362
363 // static
364 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
365   return IsPrintPreviewURL(contents->GetURL());
366 }
367
368 // static
369 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
370   return (url.SchemeIs(content::kChromeUIScheme) &&
371           url.host() == chrome::kChromeUIPrintHost);
372 }
373
374 void PrintPreviewDialogController::EraseInitiatorInfo(
375     WebContents* preview_dialog) {
376   for (size_t i = 0; i < preview_operations_.size(); ++i) {
377     Operation* operation = preview_operations_[i];
378     if (operation->preview_dialog == preview_dialog) {
379       operation->initiator_observer.StopObserving();
380       operation->initiator = NULL;
381       return;
382     }
383   }
384 }
385
386 PrintPreviewDialogController::~PrintPreviewDialogController() {
387   DCHECK_EQ(0U, preview_operations_.size());
388 }
389
390 void PrintPreviewDialogController::OnRenderProcessExited(
391     RenderProcessHost* rph) {
392   // Store contents in a vector and deal with them after iterating through
393   // |preview_operations_| because RemoveFoo() can change |preview_operations_|.
394   std::vector<WebContents*> closed_initiators;
395   std::vector<WebContents*> closed_preview_dialogs;
396   for (size_t i = 0; i < preview_operations_.size(); ++i) {
397     Operation* operation = preview_operations_[i];
398     WebContents* preview_dialog = operation->preview_dialog;
399     WebContents* initiator = operation->initiator;
400     if (preview_dialog->GetRenderProcessHost() == rph) {
401       closed_preview_dialogs.push_back(preview_dialog);
402     } else if (initiator &&
403                initiator->GetRenderProcessHost() == rph) {
404       closed_initiators.push_back(initiator);
405     }
406   }
407
408   for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
409     RemovePreviewDialog(closed_preview_dialogs[i]);
410     if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
411       PrintPreviewUI* print_preview_ui =
412           static_cast<PrintPreviewUI*>(web_ui->GetController());
413       if (print_preview_ui)
414         print_preview_ui->OnPrintPreviewDialogClosed();
415     }
416   }
417
418   for (size_t i = 0; i < closed_initiators.size(); ++i)
419     RemoveInitiator(closed_initiators[i]);
420 }
421
422 void PrintPreviewDialogController::OnWebContentsDestroyed(
423     WebContents* contents) {
424   WebContents* preview_dialog = GetPrintPreviewForContents(contents);
425   if (!preview_dialog) {
426     NOTREACHED();
427     return;
428   }
429
430   if (contents == preview_dialog)
431     RemovePreviewDialog(contents);
432   else
433     RemoveInitiator(contents);
434 }
435
436 void PrintPreviewDialogController::OnNavigationEntryCommitted(
437     WebContents* contents, const content::LoadCommittedDetails* details) {
438   WebContents* preview_dialog = GetPrintPreviewForContents(contents);
439   if (!preview_dialog) {
440     NOTREACHED();
441     return;
442   }
443
444   if (contents == preview_dialog) {
445     // Preview dialog navigated.
446     if (details) {
447       content::PageTransition transition_type =
448           details->entry->GetTransitionType();
449       content::NavigationType nav_type = details->type;
450
451       // New |preview_dialog| is created. Don't update/erase map entry.
452       if (waiting_for_new_preview_page_ &&
453           transition_type == content::PAGE_TRANSITION_AUTO_TOPLEVEL &&
454           nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
455         waiting_for_new_preview_page_ = false;
456         SaveInitiatorTitle(preview_dialog);
457         return;
458       }
459
460       // Cloud print sign-in causes a reload.
461       if (!waiting_for_new_preview_page_ &&
462           transition_type == content::PAGE_TRANSITION_RELOAD &&
463           nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
464           IsPrintPreviewURL(details->previous_url)) {
465         return;
466       }
467     }
468     NOTREACHED();
469     return;
470   }
471
472   RemoveInitiator(contents);
473 }
474
475 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
476     WebContents* initiator) {
477   base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
478   Profile* profile =
479       Profile::FromBrowserContext(initiator->GetBrowserContext());
480
481   // |web_dialog_ui_delegate| deletes itself in
482   // PrintPreviewDialogDelegate::OnDialogClosed().
483   WebDialogDelegate* web_dialog_delegate =
484       new PrintPreviewDialogDelegate(initiator);
485   // |web_dialog_delegate|'s owner is |constrained_delegate|.
486   PrintPreviewWebContentDelegate* pp_wcd =
487       new PrintPreviewWebContentDelegate(profile, initiator);
488   ConstrainedWebDialogDelegate* constrained_delegate =
489       CreateConstrainedWebDialog(profile,
490                                  web_dialog_delegate,
491                                  pp_wcd,
492                                  initiator);
493   WebContents* preview_dialog = constrained_delegate->GetWebContents();
494   EnableInternalPDFPluginForContents(preview_dialog);
495   PrintViewManager::CreateForWebContents(preview_dialog);
496
497   waiting_for_new_preview_page_ = true;
498
499   // Add an entry to the map.
500   Operation* operation = new Operation;
501   operation->preview_dialog = preview_dialog;
502   operation->initiator = initiator;
503   operation->preview_dialog_observer.StartObserving(this, preview_dialog);
504   operation->initiator_observer.StartObserving(this, initiator);
505   preview_operations_.push_back(operation);
506
507   return preview_dialog;
508 }
509
510 void PrintPreviewDialogController::SaveInitiatorTitle(
511     WebContents* preview_dialog) {
512   WebContents* initiator = GetInitiator(preview_dialog);
513   if (initiator && preview_dialog->GetWebUI()) {
514     PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
515         preview_dialog->GetWebUI()->GetController());
516     print_preview_ui->SetInitiatorTitle(
517         PrintViewManager::FromWebContents(initiator)->RenderSourceName());
518   }
519 }
520
521 void PrintPreviewDialogController::RemoveInitiator(
522     WebContents* initiator) {
523   WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
524   DCHECK(preview_dialog);
525
526   // Update the map entry first, so when the print preview dialog gets destroyed
527   // and reaches RemovePreviewDialog(), it does not attempt to also remove the
528   // initiator's observers.
529   EraseInitiatorInfo(preview_dialog);
530
531   PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
532
533   // initiator is closed. Close the print preview dialog too.
534   if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
535     PrintPreviewUI* print_preview_ui =
536         static_cast<PrintPreviewUI*>(web_ui->GetController());
537     if (print_preview_ui)
538       print_preview_ui->OnInitiatorClosed();
539   }
540 }
541
542 void PrintPreviewDialogController::RemovePreviewDialog(
543     WebContents* preview_dialog) {
544   for (size_t i = 0; i < preview_operations_.size(); ++i) {
545     Operation* operation = preview_operations_[i];
546     if (operation->preview_dialog == preview_dialog) {
547       // Remove the initiator's observers before erasing the mapping.
548       if (operation->initiator) {
549         operation->initiator_observer.StopObserving();
550         PrintViewManager::FromWebContents(operation->initiator)->
551             PrintPreviewDone();
552       }
553
554       // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to
555       // abort the initiator preview request.
556       if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
557         PrintPreviewUI* print_preview_ui =
558             static_cast<PrintPreviewUI*>(web_ui->GetController());
559         if (print_preview_ui)
560           print_preview_ui->OnPrintPreviewDialogDestroyed();
561       }
562
563       preview_operations_.erase(preview_operations_.begin() + i);
564       delete operation;
565
566       return;
567     }
568   }
569   NOTREACHED();
570 }
571
572 }  // namespace printing