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