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