Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_dialog_controller.cc
blob9a93111661548f986ed6e4e9f2e851000e922c04
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.
5 #include "chrome/browser/printing/print_preview_dialog_controller.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
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"
45 using content::NavigationController;
46 using content::WebContents;
47 using content::WebUIMessageHandler;
49 namespace {
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;
57 content::WebPluginInfo pdf_plugin;
58 if (!content::PluginService::GetInstance()->GetPluginInfoByPath(
59 pdf_plugin_path, &pdf_plugin))
60 return;
62 ChromePluginServiceFilter::GetInstance()->OverridePluginForFrame(
63 preview_dialog->GetRenderProcessHost()->GetID(),
64 preview_dialog->GetMainFrame()->GetRoutingID(),
65 GURL(), pdf_plugin);
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;
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;
85 private:
86 WebContents* initiator_;
88 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
91 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
92 : initiator_(initiator) {
95 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
98 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
99 // Not used, returning dummy value.
100 NOTREACHED();
101 return ui::MODAL_TYPE_WINDOW;
104 base::string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
105 // Only used on Windows? UI folks prefer no title.
106 return base::string16();
109 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
110 return GURL(chrome::kChromeUIPrintURL);
113 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
114 std::vector<WebUIMessageHandler*>* /* handlers */) const {
115 // PrintPreviewUI adds its own message handlers.
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;
124 web_modal::WebContentsModalDialogHost* host = NULL;
125 Browser* browser = chrome::FindBrowserWithWebContents(initiator_);
126 if (browser)
127 host = browser->window()->GetWebContentsModalDialogHost();
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);
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
145 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
146 return std::string();
149 void PrintPreviewDialogDelegate::OnDialogClosed(
150 const std::string& /* json_retval */) {
153 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
154 bool* out_close_dialog) {
155 if (out_close_dialog)
156 *out_close_dialog = true;
159 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
160 return false;
163 } // namespace
165 namespace printing {
167 PrintPreviewDialogController::PrintPreviewDialogController()
168 : waiting_for_new_preview_page_(false),
169 is_creating_print_preview_dialog_(false) {
172 // static
173 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
174 if (!g_browser_process)
175 return NULL;
176 return g_browser_process->print_preview_dialog_controller();
179 // static
180 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
181 if (initiator->ShowingInterstitialPage())
182 return;
184 PrintPreviewDialogController* dialog_controller = GetInstance();
185 if (!dialog_controller)
186 return;
187 if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
188 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
191 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
192 WebContents* initiator) {
193 DCHECK(initiator);
195 // Get the print preview dialog for |initiator|.
196 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
197 if (!preview_dialog)
198 return CreatePrintPreviewDialog(initiator);
200 // Show the initiator holding the existing preview dialog.
201 initiator->GetDelegate()->ActivateContents(initiator);
202 return preview_dialog;
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;
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;
222 return NULL;
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;
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());
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);
259 // static
260 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
261 return IsPrintPreviewURL(contents->GetURL());
264 // static
265 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
266 return (url.SchemeIs(content::kChromeUIScheme) &&
267 url.host() == chrome::kChromeUIPrintHost);
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;
276 RemoveObservers(it->second);
277 preview_dialog_map_[preview_dialog] = NULL;
280 PrintPreviewDialogController::~PrintPreviewDialogController() {}
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);
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();
310 for (size_t i = 0; i < closed_initiators.size(); ++i)
311 RemoveInitiator(closed_initiators[i]);
314 void PrintPreviewDialogController::OnWebContentsDestroyed(
315 WebContents* contents) {
316 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
317 if (!preview_dialog) {
318 NOTREACHED();
319 return;
322 if (contents == preview_dialog)
323 RemovePreviewDialog(contents);
324 else
325 RemoveInitiator(contents);
328 void PrintPreviewDialogController::OnNavEntryCommitted(
329 WebContents* contents, content::LoadCommittedDetails* details) {
330 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
331 if (!preview_dialog) {
332 NOTREACHED();
333 return;
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;
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;
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;
360 NOTREACHED();
361 return;
364 RemoveInitiator(contents);
367 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
368 WebContents* initiator) {
369 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
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);
377 WebContents* preview_dialog = web_dialog_delegate->GetWebContents();
378 EnableInternalPDFPluginForContents(preview_dialog);
379 PrintViewManager::CreateForWebContents(preview_dialog);
380 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
381 preview_dialog);
383 // Add an entry to the map.
384 preview_dialog_map_[preview_dialog] = initiator;
385 waiting_for_new_preview_page_ = true;
387 AddObservers(initiator);
388 AddObservers(preview_dialog);
390 return preview_dialog;
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());
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()));
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);
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()));
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);
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);
448 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
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();
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();
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();
477 preview_dialog_map_.erase(preview_dialog);
478 RemoveObservers(preview_dialog);
481 } // namespace printing