1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/app_modal_dialogs/js_modal_dialog.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/utf_string_conversions.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_shutdown.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
12dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/text/text_elider.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Control maximum sizes of various texts passed to us from javascript.
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#ifdef OS_LINUX
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Two-dimensional eliding.  Reformat the text of the message dialog
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// inserting line breaks because otherwise a single long line can overflow
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// the message dialog (and crash/hang the GTK, depending on the version).
2421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenconst int kMessageTextMaxRows = 32;
2521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenconst int kMessageTextMaxCols = 132;
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxRows = 24;
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxCols = 132;
28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxTextSize(const string16& in_string, string16* out_string) {
29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ElideRectangleString(in_string, kMessageTextMaxRows,
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           kMessageTextMaxCols, false, out_string);
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ElideRectangleString(in_string, kDefaultPromptMaxRows,
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           kDefaultPromptMaxCols, false, out_string);
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#else
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// One-dimensional eliding.  Trust the window system to break the string
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// appropriately, but limit its overall length to something reasonable.
39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kMessageTextMaxSize = 3000;
40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxSize = 2000;
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxTextSize(const string16& in_string, string16* out_string) {
42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ElideString(in_string, kMessageTextMaxSize, out_string);
43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ui::ElideString(in_string, kDefaultPromptMaxSize, out_string);
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochJavaScriptAppModalDialog::JavaScriptAppModalDialog(
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    JavaScriptAppModalDialogDelegate* delegate,
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::wstring& title,
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int dialog_flags,
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::wstring& message_text,
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::wstring& default_prompt_text,
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool display_suppress_checkbox,
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool is_before_unload_dialog,
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    IPC::Message* reply_msg)
60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    : AppModalDialog(delegate->AsTabContents(), title),
61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      delegate_(delegate),
62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      extension_host_(delegate->AsExtensionHost()),
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dialog_flags_(dialog_flags),
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      display_suppress_checkbox_(display_suppress_checkbox),
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_before_unload_dialog_(is_before_unload_dialog),
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      reply_msg_(reply_msg) {
6721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  string16 elided_text;
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EnforceMaxTextSize(WideToUTF16(message_text), &elided_text);
6921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  message_text_ = UTF16ToWide(elided_text);
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EnforceMaxPromptSize(WideToUTF16Hack(default_prompt_text),
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                       &default_prompt_text_);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK((tab_contents_ != NULL) != (extension_host_ != NULL));
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InitNotifications();
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochJavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
803345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickNativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  gfx::NativeWindow parent_window = tab_contents_ ?
823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      tab_contents_->GetMessageBoxRootWindow() :
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      extension_host_->GetMessageBoxRootWindow();
843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this,
853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                                            parent_window);
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::Observe(NotificationType type,
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       const NotificationSource& source,
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       const NotificationDetails& details) {
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (skip_this_dialog_)
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (NotificationType::EXTENSION_HOST_DESTROYED == type &&
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Details<ExtensionHost>(extension_host_) != details)
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we reach here, we know the notification is relevant to us, either
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // because we're only observing applicable sources or because we passed the
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // check above. Both of those indicate that we should ignore this dialog.
101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Also clear the delegate, since it's now invalid.
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  skip_this_dialog_ = true;
103731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  delegate_ = NULL;
1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (native_dialog_)
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CloseModalDialog();
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::InitNotifications() {
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure we get relevant navigation notifications so we know when our
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // parent contents will disappear or navigate to a different page.
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents_) {
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   Source<NavigationController>(&tab_contents_->controller()));
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   Source<TabContents>(tab_contents_));
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (extension_host_) {
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // EXTENSION_HOST_DESTROYED uses the Profile as its source, but we care
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // about the ExtensionHost (which is passed in the details).
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   NotificationService::AllSources());
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
126731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) {
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we are shutting down and this is an onbeforeunload dialog, cancel the
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // shutdown.
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (is_before_unload_dialog_)
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    browser_shutdown::SetTryingToQuit(false);
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // will receive its activation messages before this dialog receives
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // WM_DESTROY. The parent frame would then try to activate any modal dialogs
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // that were still open in the ModalDialogQueue, which would send activation
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // back to this one. The framework should be improved to handle this, so this
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // is a temporary workaround.
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CompleteDialog();
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  NotifyDelegate(false, L"", suppress_js_messages);
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::OnAccept(const std::wstring& prompt_text,
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        bool suppress_js_messages) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CompleteDialog();
14621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  NotifyDelegate(true, prompt_text, suppress_js_messages);
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::OnClose() {
15021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  NotifyDelegate(false, L"", false);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
15321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid JavaScriptAppModalDialog::NotifyDelegate(bool success,
1549d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block                                              const std::wstring& prompt_text,
1559d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block                                              bool suppress_js_messages) {
1569d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block  if (skip_this_dialog_)
1579d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block    return;
1589d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block
1599d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block  delegate_->OnMessageBoxClosed(reply_msg_, success, prompt_text);
1609d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block  if (suppress_js_messages)
1619d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block    delegate_->SetSuppressMessageBoxes(true);
1629d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block
16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // On Views, we can end up coming through this code path twice :(.
16421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // See crbug.com/63732.
1659d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block  skip_this_dialog_ = true;
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
167