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