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