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