javascript_app_modal_dialog.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
6
7#include "base/command_line.h"
8#include "chrome/browser/browser_shutdown.h"
9#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
10#include "chrome/common/chrome_switches.h"
11#include "content/public/browser/web_contents.h"
12#include "content/public/browser/web_contents_view.h"
13#include "ui/gfx/text_elider.h"
14
15#if defined(USE_AURA)
16#include "ui/aura/root_window.h"
17#endif
18
19using content::JavaScriptDialogManager;
20using content::WebContents;
21
22namespace {
23
24// Control maximum sizes of various texts passed to us from javascript.
25#if defined(OS_POSIX) && !defined(OS_MACOSX)
26// Two-dimensional eliding.  Reformat the text of the message dialog
27// inserting line breaks because otherwise a single long line can overflow
28// the message dialog (and crash/hang the GTK, depending on the version).
29const int kMessageTextMaxRows = 32;
30const int kMessageTextMaxCols = 132;
31const int kDefaultPromptMaxRows = 24;
32const int kDefaultPromptMaxCols = 132;
33void EnforceMaxTextSize(const string16& in_string, string16* out_string) {
34  gfx::ElideRectangleString(in_string, kMessageTextMaxRows,
35                           kMessageTextMaxCols, false, out_string);
36}
37void EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
38  gfx::ElideRectangleString(in_string, kDefaultPromptMaxRows,
39                           kDefaultPromptMaxCols, false, out_string);
40}
41#else
42// One-dimensional eliding.  Trust the window system to break the string
43// appropriately, but limit its overall length to something reasonable.
44const int kMessageTextMaxSize = 3000;
45const int kDefaultPromptMaxSize = 2000;
46void EnforceMaxTextSize(const string16& in_string, string16* out_string) {
47  gfx::ElideString(in_string, kMessageTextMaxSize, out_string);
48}
49void EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
50  gfx::ElideString(in_string, kDefaultPromptMaxSize, out_string);
51}
52#endif
53
54}  // namespace
55
56ChromeJavaScriptDialogExtraData::ChromeJavaScriptDialogExtraData()
57    : suppress_javascript_messages_(false) {
58}
59
60JavaScriptAppModalDialog::JavaScriptAppModalDialog(
61    WebContents* web_contents,
62    ExtraDataMap* extra_data_map,
63    const string16& title,
64    content::JavaScriptMessageType javascript_message_type,
65    const string16& message_text,
66    const string16& default_prompt_text,
67    bool display_suppress_checkbox,
68    bool is_before_unload_dialog,
69    bool is_reload,
70    const JavaScriptDialogManager::DialogClosedCallback& callback)
71    : AppModalDialog(web_contents, title),
72      extra_data_map_(extra_data_map),
73      javascript_message_type_(javascript_message_type),
74      display_suppress_checkbox_(display_suppress_checkbox),
75      is_before_unload_dialog_(is_before_unload_dialog),
76      is_reload_(is_reload),
77      callback_(callback),
78      use_override_prompt_text_(false) {
79  EnforceMaxTextSize(message_text, &message_text_);
80  EnforceMaxPromptSize(default_prompt_text, &default_prompt_text_);
81}
82
83JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
84}
85
86NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
87  gfx::NativeWindow parent_window =
88      web_contents()->GetView()->GetTopLevelNativeWindow();
89
90#if defined(USE_AURA)
91  if (!parent_window->GetRootWindow()) {
92    // When we are part of a WebContents that isn't actually being displayed on
93    // the screen, we can't actually attach to it.
94    parent_window = NULL;
95  }
96#endif  // defined(USE_AURA)
97
98  return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this,
99                                                            parent_window);
100}
101
102bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() {
103  return true;
104}
105
106void JavaScriptAppModalDialog::Invalidate() {
107  if (!IsValid())
108    return;
109
110  AppModalDialog::Invalidate();
111  if (!callback_.is_null()) {
112    callback_.Run(false, string16());
113    callback_.Reset();
114  }
115  if (native_dialog())
116    CloseModalDialog();
117}
118
119void JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) {
120  // If we are shutting down and this is an onbeforeunload dialog, cancel the
121  // shutdown.
122  // TODO(sammc): Remove this when kDisableBatchedShutdown is removed.
123  if (is_before_unload_dialog_ &&
124      CommandLine::ForCurrentProcess()->HasSwitch(
125          switches::kDisableBatchedShutdown)) {
126    browser_shutdown::SetTryingToQuit(false);
127  }
128
129  // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
130  // will receive its activation messages before this dialog receives
131  // WM_DESTROY. The parent frame would then try to activate any modal dialogs
132  // that were still open in the ModalDialogQueue, which would send activation
133  // back to this one. The framework should be improved to handle this, so this
134  // is a temporary workaround.
135  CompleteDialog();
136
137  NotifyDelegate(false, string16(), suppress_js_messages);
138}
139
140void JavaScriptAppModalDialog::OnAccept(const string16& prompt_text,
141                                        bool suppress_js_messages) {
142  string16 prompt_text_to_use = prompt_text;
143  // This is only for testing.
144  if (use_override_prompt_text_)
145    prompt_text_to_use = override_prompt_text_;
146
147  CompleteDialog();
148  NotifyDelegate(true, prompt_text_to_use, suppress_js_messages);
149}
150
151void JavaScriptAppModalDialog::OnClose() {
152  NotifyDelegate(false, string16(), false);
153}
154
155void JavaScriptAppModalDialog::SetOverridePromptText(
156    const string16& override_prompt_text) {
157  override_prompt_text_ = override_prompt_text;
158  use_override_prompt_text_ = true;
159}
160
161void JavaScriptAppModalDialog::NotifyDelegate(bool success,
162                                              const string16& user_input,
163                                              bool suppress_js_messages) {
164  if (!IsValid())
165    return;
166
167  if (!callback_.is_null()) {
168    callback_.Run(success, user_input);
169    callback_.Reset();
170  }
171
172  // The callback_ above may delete web_contents_, thus removing the extra
173  // data from the map owned by ChromeJavaScriptDialogManager. Make sure
174  // to only use the data if still present. http://crbug.com/236476
175  ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents());
176  if (extra_data != extra_data_map_->end()) {
177    extra_data->second.last_javascript_message_dismissal_ =
178        base::TimeTicks::Now();
179    extra_data->second.suppress_javascript_messages_ = suppress_js_messages;
180  }
181
182  // On Views, we can end up coming through this code path twice :(.
183  // See crbug.com/63732.
184  AppModalDialog::Invalidate();
185}
186