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