Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_dialog_controller.cc
blob6f92e20b33589818f5088704fdaaec6943d0ae84
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/printing/print_view_manager.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_paths.h"
27 #include "chrome/common/url_constants.h"
28 #include "components/web_modal/web_contents_modal_dialog_host.h"
29 #include "content/public/browser/host_zoom_map.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/notification_details.h"
34 #include "content/public/browser/notification_source.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_delegate.h"
38 #include "extensions/browser/guest_view/guest_view_base.h"
39 #include "ui/web_dialogs/web_dialog_delegate.h"
41 using content::NavigationController;
42 using content::WebContents;
43 using content::WebUIMessageHandler;
45 namespace {
47 // A ui::WebDialogDelegate that specifies the print preview dialog appearance.
48 class PrintPreviewDialogDelegate : public ui::WebDialogDelegate {
49 public:
50 explicit PrintPreviewDialogDelegate(WebContents* initiator);
51 ~PrintPreviewDialogDelegate() override;
53 ui::ModalType GetDialogModalType() const override;
54 base::string16 GetDialogTitle() const override;
55 GURL GetDialogContentURL() const override;
56 void GetWebUIMessageHandlers(
57 std::vector<WebUIMessageHandler*>* handlers) const override;
58 void GetDialogSize(gfx::Size* size) const override;
59 std::string GetDialogArgs() const override;
60 void OnDialogClosed(const std::string& json_retval) override;
61 void OnCloseContents(WebContents* source, bool* out_close_dialog) override;
62 bool ShouldShowDialogTitle() const override;
64 private:
65 WebContents* initiator_;
67 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
70 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
71 : initiator_(initiator) {
74 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
77 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
78 // Not used, returning dummy value.
79 NOTREACHED();
80 return ui::MODAL_TYPE_WINDOW;
83 base::string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
84 // Only used on Windows? UI folks prefer no title.
85 return base::string16();
88 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
89 return GURL(chrome::kChromeUIPrintURL);
92 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
93 std::vector<WebUIMessageHandler*>* /* handlers */) const {
94 // PrintPreviewUI adds its own message handlers.
97 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size* size) const {
98 DCHECK(size);
99 const gfx::Size kMinDialogSize(800, 480);
100 const int kBorder = 25;
101 *size = kMinDialogSize;
103 web_modal::WebContentsModalDialogHost* host = NULL;
104 content::WebContents* outermost_web_contents = initiator_;
105 const extensions::GuestViewBase* guest_view =
106 extensions::GuestViewBase::FromWebContents(outermost_web_contents);
107 while (guest_view && guest_view->attached()) {
108 outermost_web_contents = guest_view->embedder_web_contents();
109 guest_view =
110 extensions::GuestViewBase::FromWebContents(outermost_web_contents);
112 Browser* browser = chrome::FindBrowserWithWebContents(outermost_web_contents);
113 if (browser)
114 host = browser->window()->GetWebContentsModalDialogHost();
116 if (host) {
117 size->SetToMax(host->GetMaximumDialogSize());
118 size->Enlarge(-2 * kBorder, -kBorder);
119 } else {
120 size->SetToMax(outermost_web_contents->GetContainerBounds().size());
121 size->Enlarge(-2 * kBorder, -2 * kBorder);
124 #if defined(OS_MACOSX)
125 // Limit the maximum size on MacOS X.
126 // http://crbug.com/105815
127 const gfx::Size kMaxDialogSize(1000, 660);
128 size->SetToMin(kMaxDialogSize);
129 #endif
132 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
133 return std::string();
136 void PrintPreviewDialogDelegate::OnDialogClosed(
137 const std::string& /* json_retval */) {
140 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
141 bool* out_close_dialog) {
142 if (out_close_dialog)
143 *out_close_dialog = true;
146 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
147 return false;
150 } // namespace
152 namespace printing {
154 PrintPreviewDialogController::PrintPreviewDialogController()
155 : waiting_for_new_preview_page_(false),
156 is_creating_print_preview_dialog_(false) {
159 // static
160 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
161 if (!g_browser_process)
162 return NULL;
163 return g_browser_process->print_preview_dialog_controller();
166 // static
167 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
168 if (initiator->ShowingInterstitialPage())
169 return;
171 PrintPreviewDialogController* dialog_controller = GetInstance();
172 if (!dialog_controller)
173 return;
174 if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
175 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
178 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
179 WebContents* initiator) {
180 DCHECK(initiator);
182 // Get the print preview dialog for |initiator|.
183 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
184 if (!preview_dialog)
185 return CreatePrintPreviewDialog(initiator);
187 // Show the initiator holding the existing preview dialog.
188 initiator->GetDelegate()->ActivateContents(initiator);
189 return preview_dialog;
192 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
193 WebContents* contents) const {
194 // |preview_dialog_map_| is keyed by the preview dialog, so if find()
195 // succeeds, then |contents| is the preview dialog.
196 PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.find(contents);
197 if (it != preview_dialog_map_.end())
198 return contents;
200 for (it = preview_dialog_map_.begin();
201 it != preview_dialog_map_.end();
202 ++it) {
203 // If |contents| is an initiator.
204 if (contents == it->second) {
205 // Return the associated preview dialog.
206 return it->first;
209 return NULL;
212 WebContents* PrintPreviewDialogController::GetInitiator(
213 WebContents* preview_dialog) {
214 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
215 return (it != preview_dialog_map_.end()) ? it->second : NULL;
218 void PrintPreviewDialogController::Observe(
219 int type,
220 const content::NotificationSource& source,
221 const content::NotificationDetails& details) {
222 if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
223 OnRendererProcessClosed(
224 content::Source<content::RenderProcessHost>(source).ptr());
225 } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
226 OnWebContentsDestroyed(content::Source<WebContents>(source).ptr());
227 } else {
228 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
229 WebContents* contents =
230 content::Source<NavigationController>(source)->GetWebContents();
231 OnNavEntryCommitted(
232 contents,
233 content::Details<content::LoadCommittedDetails>(details).ptr());
237 void PrintPreviewDialogController::ForEachPreviewDialog(
238 base::Callback<void(content::WebContents*)> callback) {
239 for (PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.begin();
240 it != preview_dialog_map_.end();
241 ++it) {
242 callback.Run(it->first);
246 // static
247 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
248 return IsPrintPreviewURL(contents->GetURL());
251 // static
252 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
253 return (url.SchemeIs(content::kChromeUIScheme) &&
254 url.host() == chrome::kChromeUIPrintHost);
257 void PrintPreviewDialogController::EraseInitiatorInfo(
258 WebContents* preview_dialog) {
259 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
260 if (it == preview_dialog_map_.end())
261 return;
263 RemoveObservers(it->second);
264 preview_dialog_map_[preview_dialog] = NULL;
267 PrintPreviewDialogController::~PrintPreviewDialogController() {}
269 void PrintPreviewDialogController::OnRendererProcessClosed(
270 content::RenderProcessHost* rph) {
271 // Store contents in a vector and deal with them after iterating through
272 // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
273 std::vector<WebContents*> closed_initiators;
274 std::vector<WebContents*> closed_preview_dialogs;
275 for (PrintPreviewDialogMap::iterator iter = preview_dialog_map_.begin();
276 iter != preview_dialog_map_.end(); ++iter) {
277 WebContents* preview_dialog = iter->first;
278 WebContents* initiator = iter->second;
279 if (preview_dialog->GetRenderProcessHost() == rph) {
280 closed_preview_dialogs.push_back(preview_dialog);
281 } else if (initiator &&
282 initiator->GetRenderProcessHost() == rph) {
283 closed_initiators.push_back(initiator);
287 for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
288 RemovePreviewDialog(closed_preview_dialogs[i]);
289 if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
290 PrintPreviewUI* print_preview_ui =
291 static_cast<PrintPreviewUI*>(web_ui->GetController());
292 if (print_preview_ui)
293 print_preview_ui->OnPrintPreviewDialogClosed();
297 for (size_t i = 0; i < closed_initiators.size(); ++i)
298 RemoveInitiator(closed_initiators[i]);
301 void PrintPreviewDialogController::OnWebContentsDestroyed(
302 WebContents* contents) {
303 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
304 if (!preview_dialog) {
305 NOTREACHED();
306 return;
309 if (contents == preview_dialog)
310 RemovePreviewDialog(contents);
311 else
312 RemoveInitiator(contents);
315 void PrintPreviewDialogController::OnNavEntryCommitted(
316 WebContents* contents, content::LoadCommittedDetails* details) {
317 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
318 if (!preview_dialog) {
319 NOTREACHED();
320 return;
323 if (contents == preview_dialog) {
324 // Preview dialog navigated.
325 if (details) {
326 ui::PageTransition transition_type =
327 details->entry->GetTransitionType();
328 content::NavigationType nav_type = details->type;
330 // New |preview_dialog| is created. Don't update/erase map entry.
331 if (waiting_for_new_preview_page_ &&
332 transition_type == ui::PAGE_TRANSITION_AUTO_TOPLEVEL &&
333 nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
334 waiting_for_new_preview_page_ = false;
335 SaveInitiatorTitle(preview_dialog);
336 return;
339 // Cloud print sign-in causes a reload.
340 if (!waiting_for_new_preview_page_ &&
341 transition_type == ui::PAGE_TRANSITION_RELOAD &&
342 nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
343 IsPrintPreviewURL(details->previous_url)) {
344 return;
347 NOTREACHED();
348 return;
351 RemoveInitiator(contents);
354 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
355 WebContents* initiator) {
356 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
358 // The dialog delegates are deleted when the dialog is closed.
359 ConstrainedWebDialogDelegate* web_dialog_delegate =
360 ShowConstrainedWebDialog(initiator->GetBrowserContext(),
361 new PrintPreviewDialogDelegate(initiator),
362 initiator);
364 WebContents* preview_dialog = web_dialog_delegate->GetWebContents();
366 // Clear the zoom level for the print preview dialog so it isn't affected by
367 // the default zoom level. This also controls the zoom level of the OOP PDF
368 // extension when iframed by the print preview dialog.
369 GURL print_url(chrome::kChromeUIPrintURL);
370 content::HostZoomMap::Get(preview_dialog->GetSiteInstance())
371 ->SetZoomLevelForHostAndScheme(print_url.scheme(), print_url.host(), 0);
372 PrintViewManager::CreateForWebContents(preview_dialog);
373 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
374 preview_dialog);
376 // Add an entry to the map.
377 preview_dialog_map_[preview_dialog] = initiator;
378 waiting_for_new_preview_page_ = true;
380 AddObservers(initiator);
381 AddObservers(preview_dialog);
383 return preview_dialog;
386 void PrintPreviewDialogController::SaveInitiatorTitle(
387 WebContents* preview_dialog) {
388 WebContents* initiator = GetInitiator(preview_dialog);
389 if (initiator && preview_dialog->GetWebUI()) {
390 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
391 preview_dialog->GetWebUI()->GetController());
392 print_preview_ui->SetInitiatorTitle(
393 PrintViewManager::FromWebContents(initiator)->RenderSourceName());
397 void PrintPreviewDialogController::AddObservers(WebContents* contents) {
398 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
399 content::Source<WebContents>(contents));
400 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
401 content::Source<NavigationController>(&contents->GetController()));
403 // Multiple sites may share the same RenderProcessHost, so check if this
404 // notification has already been added.
405 content::Source<content::RenderProcessHost> rph_source(
406 contents->GetRenderProcessHost());
407 if (!registrar_.IsRegistered(this,
408 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
409 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
410 rph_source);
414 void PrintPreviewDialogController::RemoveObservers(WebContents* contents) {
415 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
416 content::Source<WebContents>(contents));
417 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
418 content::Source<NavigationController>(&contents->GetController()));
420 // Multiple sites may share the same RenderProcessHost, so check if this
421 // notification has already been added.
422 content::Source<content::RenderProcessHost> rph_source(
423 contents->GetRenderProcessHost());
424 if (registrar_.IsRegistered(this,
425 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
426 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
427 rph_source);
431 void PrintPreviewDialogController::RemoveInitiator(
432 WebContents* initiator) {
433 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
434 DCHECK(preview_dialog);
435 // Update the map entry first, so when the print preview dialog gets destroyed
436 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
437 // initiator's observers.
438 preview_dialog_map_[preview_dialog] = NULL;
439 RemoveObservers(initiator);
441 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
443 // initiator is closed. Close the print preview dialog too.
444 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
445 PrintPreviewUI* print_preview_ui =
446 static_cast<PrintPreviewUI*>(web_ui->GetController());
447 if (print_preview_ui)
448 print_preview_ui->OnInitiatorClosed();
452 void PrintPreviewDialogController::RemovePreviewDialog(
453 WebContents* preview_dialog) {
454 // Remove the initiator's observers before erasing the mapping.
455 WebContents* initiator = GetInitiator(preview_dialog);
456 if (initiator) {
457 RemoveObservers(initiator);
458 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
461 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to abort
462 // the initiator preview request.
463 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
464 PrintPreviewUI* print_preview_ui =
465 static_cast<PrintPreviewUI*>(web_ui->GetController());
466 if (print_preview_ui)
467 print_preview_ui->OnPrintPreviewDialogDestroyed();
470 preview_dialog_map_.erase(preview_dialog);
471 RemoveObservers(preview_dialog);
474 } // namespace printing