Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_dialog_controller.cc
blob9661c7fb05207bb673d1549fceab801ca2a6dea8
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/task_management/web_contents_tags.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_paths.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/guest_view/browser/guest_view_base.h"
30 #include "components/web_modal/web_contents_modal_dialog_host.h"
31 #include "content/public/browser/host_zoom_map.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/navigation_details.h"
34 #include "content/public/browser/navigation_entry.h"
35 #include "content/public/browser/notification_details.h"
36 #include "content/public/browser/notification_source.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 "ui/web_dialogs/web_dialog_delegate.h"
42 using content::NavigationController;
43 using content::WebContents;
44 using content::WebUIMessageHandler;
46 namespace {
48 // A ui::WebDialogDelegate that specifies the print preview dialog appearance.
49 class PrintPreviewDialogDelegate : public ui::WebDialogDelegate {
50 public:
51 explicit PrintPreviewDialogDelegate(WebContents* initiator);
52 ~PrintPreviewDialogDelegate() override;
54 ui::ModalType GetDialogModalType() const override;
55 base::string16 GetDialogTitle() const override;
56 GURL GetDialogContentURL() const override;
57 void GetWebUIMessageHandlers(
58 std::vector<WebUIMessageHandler*>* handlers) const override;
59 void GetDialogSize(gfx::Size* size) const override;
60 std::string GetDialogArgs() const override;
61 void OnDialogClosed(const std::string& json_retval) override;
62 void OnCloseContents(WebContents* source, bool* out_close_dialog) override;
63 bool ShouldShowDialogTitle() const override;
65 private:
66 WebContents* initiator_;
68 DISALLOW_COPY_AND_ASSIGN(PrintPreviewDialogDelegate);
71 PrintPreviewDialogDelegate::PrintPreviewDialogDelegate(WebContents* initiator)
72 : initiator_(initiator) {
75 PrintPreviewDialogDelegate::~PrintPreviewDialogDelegate() {
78 ui::ModalType PrintPreviewDialogDelegate::GetDialogModalType() const {
79 // Not used, returning dummy value.
80 NOTREACHED();
81 return ui::MODAL_TYPE_WINDOW;
84 base::string16 PrintPreviewDialogDelegate::GetDialogTitle() const {
85 // Only used on Windows? UI folks prefer no title.
86 return base::string16();
89 GURL PrintPreviewDialogDelegate::GetDialogContentURL() const {
90 return GURL(chrome::kChromeUIPrintURL);
93 void PrintPreviewDialogDelegate::GetWebUIMessageHandlers(
94 std::vector<WebUIMessageHandler*>* /* handlers */) const {
95 // PrintPreviewUI adds its own message handlers.
98 void PrintPreviewDialogDelegate::GetDialogSize(gfx::Size* size) const {
99 DCHECK(size);
100 const gfx::Size kMinDialogSize(800, 480);
101 const int kBorder = 25;
102 *size = kMinDialogSize;
104 web_modal::WebContentsModalDialogHost* host = nullptr;
105 content::WebContents* outermost_web_contents =
106 guest_view::GuestViewBase::GetTopLevelWebContents(initiator_);
107 Browser* browser = chrome::FindBrowserWithWebContents(outermost_web_contents);
108 if (browser)
109 host = browser->window()->GetWebContentsModalDialogHost();
111 if (host)
112 size->SetToMax(host->GetMaximumDialogSize());
113 else
114 size->SetToMax(outermost_web_contents->GetContainerBounds().size());
115 size->Enlarge(-2 * kBorder, -kBorder);
117 #if defined(OS_MACOSX)
118 // Limit the maximum size on MacOS X.
119 // http://crbug.com/105815
120 const gfx::Size kMaxDialogSize(1000, 660);
121 size->SetToMin(kMaxDialogSize);
122 #endif
125 std::string PrintPreviewDialogDelegate::GetDialogArgs() const {
126 return std::string();
129 void PrintPreviewDialogDelegate::OnDialogClosed(
130 const std::string& /* json_retval */) {
133 void PrintPreviewDialogDelegate::OnCloseContents(WebContents* /* source */,
134 bool* out_close_dialog) {
135 *out_close_dialog = true;
138 bool PrintPreviewDialogDelegate::ShouldShowDialogTitle() const {
139 return false;
142 } // namespace
144 namespace printing {
146 PrintPreviewDialogController::PrintPreviewDialogController()
147 : waiting_for_new_preview_page_(false),
148 is_creating_print_preview_dialog_(false) {
151 // static
152 PrintPreviewDialogController* PrintPreviewDialogController::GetInstance() {
153 if (!g_browser_process)
154 return nullptr;
155 return g_browser_process->print_preview_dialog_controller();
158 // static
159 void PrintPreviewDialogController::PrintPreview(WebContents* initiator) {
160 if (initiator->ShowingInterstitialPage())
161 return;
163 PrintPreviewDialogController* dialog_controller = GetInstance();
164 if (!dialog_controller)
165 return;
166 if (!dialog_controller->GetOrCreatePreviewDialog(initiator))
167 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
170 WebContents* PrintPreviewDialogController::GetOrCreatePreviewDialog(
171 WebContents* initiator) {
172 DCHECK(initiator);
174 // Get the print preview dialog for |initiator|.
175 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
176 if (!preview_dialog)
177 return CreatePrintPreviewDialog(initiator);
179 // Show the initiator holding the existing preview dialog.
180 initiator->GetDelegate()->ActivateContents(initiator);
181 return preview_dialog;
184 WebContents* PrintPreviewDialogController::GetPrintPreviewForContents(
185 WebContents* contents) const {
186 // |preview_dialog_map_| is keyed by the preview dialog, so if find()
187 // succeeds, then |contents| is the preview dialog.
188 PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.find(contents);
189 if (it != preview_dialog_map_.end())
190 return contents;
192 for (it = preview_dialog_map_.begin();
193 it != preview_dialog_map_.end();
194 ++it) {
195 // If |contents| is an initiator.
196 if (contents == it->second) {
197 // Return the associated preview dialog.
198 return it->first;
201 return nullptr;
204 WebContents* PrintPreviewDialogController::GetInitiator(
205 WebContents* preview_dialog) {
206 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
207 return (it != preview_dialog_map_.end()) ? it->second : nullptr;
210 void PrintPreviewDialogController::Observe(
211 int type,
212 const content::NotificationSource& source,
213 const content::NotificationDetails& details) {
214 if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
215 OnRendererProcessClosed(
216 content::Source<content::RenderProcessHost>(source).ptr());
217 } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
218 OnWebContentsDestroyed(content::Source<WebContents>(source).ptr());
219 } else {
220 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
221 WebContents* contents =
222 content::Source<NavigationController>(source)->GetWebContents();
223 OnNavEntryCommitted(
224 contents,
225 content::Details<content::LoadCommittedDetails>(details).ptr());
229 void PrintPreviewDialogController::ForEachPreviewDialog(
230 base::Callback<void(content::WebContents*)> callback) {
231 for (PrintPreviewDialogMap::const_iterator it = preview_dialog_map_.begin();
232 it != preview_dialog_map_.end();
233 ++it) {
234 callback.Run(it->first);
238 // static
239 bool PrintPreviewDialogController::IsPrintPreviewDialog(WebContents* contents) {
240 return IsPrintPreviewURL(contents->GetURL());
243 // static
244 bool PrintPreviewDialogController::IsPrintPreviewURL(const GURL& url) {
245 return (url.SchemeIs(content::kChromeUIScheme) &&
246 url.host() == chrome::kChromeUIPrintHost);
249 void PrintPreviewDialogController::EraseInitiatorInfo(
250 WebContents* preview_dialog) {
251 PrintPreviewDialogMap::iterator it = preview_dialog_map_.find(preview_dialog);
252 if (it == preview_dialog_map_.end())
253 return;
255 RemoveObservers(it->second);
256 preview_dialog_map_[preview_dialog] = nullptr;
259 PrintPreviewDialogController::~PrintPreviewDialogController() {}
261 void PrintPreviewDialogController::OnRendererProcessClosed(
262 content::RenderProcessHost* rph) {
263 // Store contents in a vector and deal with them after iterating through
264 // |preview_dialog_map_| because RemoveFoo() can change |preview_dialog_map_|.
265 std::vector<WebContents*> closed_initiators;
266 std::vector<WebContents*> closed_preview_dialogs;
267 for (PrintPreviewDialogMap::iterator iter = preview_dialog_map_.begin();
268 iter != preview_dialog_map_.end(); ++iter) {
269 WebContents* preview_dialog = iter->first;
270 WebContents* initiator = iter->second;
271 if (preview_dialog->GetRenderProcessHost() == rph) {
272 closed_preview_dialogs.push_back(preview_dialog);
273 } else if (initiator &&
274 initiator->GetRenderProcessHost() == rph) {
275 closed_initiators.push_back(initiator);
279 for (size_t i = 0; i < closed_preview_dialogs.size(); ++i) {
280 RemovePreviewDialog(closed_preview_dialogs[i]);
281 if (content::WebUI* web_ui = closed_preview_dialogs[i]->GetWebUI()) {
282 PrintPreviewUI* print_preview_ui =
283 static_cast<PrintPreviewUI*>(web_ui->GetController());
284 if (print_preview_ui)
285 print_preview_ui->OnPrintPreviewDialogClosed();
289 for (size_t i = 0; i < closed_initiators.size(); ++i)
290 RemoveInitiator(closed_initiators[i]);
293 void PrintPreviewDialogController::OnWebContentsDestroyed(
294 WebContents* contents) {
295 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
296 if (!preview_dialog) {
297 NOTREACHED();
298 return;
301 if (contents == preview_dialog)
302 RemovePreviewDialog(contents);
303 else
304 RemoveInitiator(contents);
307 void PrintPreviewDialogController::OnNavEntryCommitted(
308 WebContents* contents, content::LoadCommittedDetails* details) {
309 WebContents* preview_dialog = GetPrintPreviewForContents(contents);
310 if (!preview_dialog) {
311 NOTREACHED();
312 return;
315 if (contents == preview_dialog) {
316 // Preview dialog navigated.
317 if (details) {
318 ui::PageTransition transition_type =
319 details->entry->GetTransitionType();
320 content::NavigationType nav_type = details->type;
322 // New |preview_dialog| is created. Don't update/erase map entry.
323 if (waiting_for_new_preview_page_ &&
324 transition_type == ui::PAGE_TRANSITION_AUTO_TOPLEVEL &&
325 nav_type == content::NAVIGATION_TYPE_NEW_PAGE) {
326 waiting_for_new_preview_page_ = false;
327 SaveInitiatorTitle(preview_dialog);
328 return;
331 // Cloud print sign-in causes a reload.
332 if (!waiting_for_new_preview_page_ &&
333 transition_type == ui::PAGE_TRANSITION_RELOAD &&
334 nav_type == content::NAVIGATION_TYPE_EXISTING_PAGE &&
335 IsPrintPreviewURL(details->previous_url)) {
336 return;
339 NOTREACHED();
340 return;
343 RemoveInitiator(contents);
346 WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
347 WebContents* initiator) {
348 base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
350 // The dialog delegates are deleted when the dialog is closed.
351 ConstrainedWebDialogDelegate* web_dialog_delegate =
352 ShowConstrainedWebDialog(initiator->GetBrowserContext(),
353 new PrintPreviewDialogDelegate(initiator),
354 initiator);
356 WebContents* preview_dialog = web_dialog_delegate->GetWebContents();
358 // Clear the zoom level for the print preview dialog so it isn't affected by
359 // the default zoom level. This also controls the zoom level of the OOP PDF
360 // extension when iframed by the print preview dialog.
361 GURL print_url(chrome::kChromeUIPrintURL);
362 content::HostZoomMap::Get(preview_dialog->GetSiteInstance())
363 ->SetZoomLevelForHostAndScheme(print_url.scheme(), print_url.host(), 0);
364 PrintViewManager::CreateForWebContents(preview_dialog);
365 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
366 preview_dialog);
368 // Add an entry to the map.
369 preview_dialog_map_[preview_dialog] = initiator;
370 waiting_for_new_preview_page_ = true;
372 // Make the print preview WebContents show up in the task manager.
373 task_management::WebContentsTags::CreateForPrintingContents(preview_dialog);
375 AddObservers(initiator);
376 AddObservers(preview_dialog);
378 return preview_dialog;
381 void PrintPreviewDialogController::SaveInitiatorTitle(
382 WebContents* preview_dialog) {
383 WebContents* initiator = GetInitiator(preview_dialog);
384 if (initiator && preview_dialog->GetWebUI()) {
385 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
386 preview_dialog->GetWebUI()->GetController());
387 print_preview_ui->SetInitiatorTitle(
388 PrintViewManager::FromWebContents(initiator)->RenderSourceName());
392 void PrintPreviewDialogController::AddObservers(WebContents* contents) {
393 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
394 content::Source<WebContents>(contents));
395 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
396 content::Source<NavigationController>(&contents->GetController()));
398 // Multiple sites may share the same RenderProcessHost, so check if this
399 // notification has already been added.
400 content::Source<content::RenderProcessHost> rph_source(
401 contents->GetRenderProcessHost());
402 if (!registrar_.IsRegistered(this,
403 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
404 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
405 rph_source);
409 void PrintPreviewDialogController::RemoveObservers(WebContents* contents) {
410 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
411 content::Source<WebContents>(contents));
412 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
413 content::Source<NavigationController>(&contents->GetController()));
415 // Multiple sites may share the same RenderProcessHost, so check if this
416 // notification has already been added.
417 content::Source<content::RenderProcessHost> rph_source(
418 contents->GetRenderProcessHost());
419 if (registrar_.IsRegistered(this,
420 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, rph_source)) {
421 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
422 rph_source);
426 void PrintPreviewDialogController::RemoveInitiator(
427 WebContents* initiator) {
428 WebContents* preview_dialog = GetPrintPreviewForContents(initiator);
429 DCHECK(preview_dialog);
430 // Update the map entry first, so when the print preview dialog gets destroyed
431 // and reaches RemovePreviewDialog(), it does not attempt to also remove the
432 // initiator's observers.
433 preview_dialog_map_[preview_dialog] = nullptr;
434 RemoveObservers(initiator);
436 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
438 // initiator is closed. Close the print preview dialog too.
439 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
440 PrintPreviewUI* print_preview_ui =
441 static_cast<PrintPreviewUI*>(web_ui->GetController());
442 if (print_preview_ui)
443 print_preview_ui->OnInitiatorClosed();
447 void PrintPreviewDialogController::RemovePreviewDialog(
448 WebContents* preview_dialog) {
449 // Remove the initiator's observers before erasing the mapping.
450 WebContents* initiator = GetInitiator(preview_dialog);
451 if (initiator) {
452 RemoveObservers(initiator);
453 PrintViewManager::FromWebContents(initiator)->PrintPreviewDone();
456 // Print preview WebContents is destroyed. Notify |PrintPreviewUI| to abort
457 // the initiator preview request.
458 if (content::WebUI* web_ui = preview_dialog->GetWebUI()) {
459 PrintPreviewUI* print_preview_ui =
460 static_cast<PrintPreviewUI*>(web_ui->GetController());
461 if (print_preview_ui)
462 print_preview_ui->OnPrintPreviewDialogDestroyed();
465 preview_dialog_map_.erase(preview_dialog);
466 RemoveObservers(preview_dialog);
469 } // namespace printing