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 18 using content::JavaScriptDialogManager; 19 using content::WebContents; 20 21 namespace { 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). 28 const int kMessageTextMaxRows = 32; 29 const int kMessageTextMaxCols = 132; 30 const int kDefaultPromptMaxRows = 24; 31 const int kDefaultPromptMaxCols = 132; 32 void EnforceMaxTextSize(const base::string16& in_string, 33 base::string16* out_string) { 34 gfx::ElideRectangleString(in_string, kMessageTextMaxRows, 35 kMessageTextMaxCols, false, out_string); 36 } 37 void 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. 45 const int kMessageTextMaxSize = 3000; 46 const int kDefaultPromptMaxSize = 2000; 47 void EnforceMaxTextSize(const base::string16& in_string, 48 base::string16* out_string) { 49 gfx::ElideString(in_string, kMessageTextMaxSize, out_string); 50 } 51 void 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 59 ChromeJavaScriptDialogExtraData::ChromeJavaScriptDialogExtraData() 60 : suppress_javascript_messages_(false) { 61 } 62 63 JavaScriptAppModalDialog::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 86 JavaScriptAppModalDialog::~JavaScriptAppModalDialog() { 87 } 88 89 NativeAppModalDialog* 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 105 bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() { 106 return true; 107 } 108 109 void 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 122 void 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 134 void 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 145 void JavaScriptAppModalDialog::OnClose() { 146 NotifyDelegate(false, base::string16(), false); 147 } 148 149 void JavaScriptAppModalDialog::SetOverridePromptText( 150 const base::string16& override_prompt_text) { 151 override_prompt_text_ = override_prompt_text; 152 use_override_prompt_text_ = true; 153 } 154 155 void 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