1 // Copyright (c) 2011 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/js_modal_dialog.h" 6 7 #include "base/string_util.h" 8 #include "base/utf_string_conversions.h" 9 #include "chrome/browser/browser_shutdown.h" 10 #include "chrome/browser/extensions/extension_host.h" 11 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h" 12 #include "content/browser/tab_contents/tab_contents.h" 13 #include "content/common/notification_service.h" 14 #include "content/common/notification_type.h" 15 #include "ui/base/text/text_elider.h" 16 17 namespace { 18 19 // Control maximum sizes of various texts passed to us from javascript. 20 #ifdef OS_LINUX 21 // Two-dimensional eliding. Reformat the text of the message dialog 22 // inserting line breaks because otherwise a single long line can overflow 23 // the message dialog (and crash/hang the GTK, depending on the version). 24 const int kMessageTextMaxRows = 32; 25 const int kMessageTextMaxCols = 132; 26 const int kDefaultPromptMaxRows = 24; 27 const int kDefaultPromptMaxCols = 132; 28 void EnforceMaxTextSize(const string16& in_string, string16* out_string) { 29 ui::ElideRectangleString(in_string, kMessageTextMaxRows, 30 kMessageTextMaxCols, false, out_string); 31 } 32 void EnforceMaxPromptSize(const string16& in_string, string16* out_string) { 33 ui::ElideRectangleString(in_string, kDefaultPromptMaxRows, 34 kDefaultPromptMaxCols, false, out_string); 35 } 36 #else 37 // One-dimensional eliding. Trust the window system to break the string 38 // appropriately, but limit its overall length to something reasonable. 39 const int kMessageTextMaxSize = 3000; 40 const int kDefaultPromptMaxSize = 2000; 41 void EnforceMaxTextSize(const string16& in_string, string16* out_string) { 42 ui::ElideString(in_string, kMessageTextMaxSize, out_string); 43 } 44 void EnforceMaxPromptSize(const string16& in_string, string16* out_string) { 45 ui::ElideString(in_string, kDefaultPromptMaxSize, out_string); 46 } 47 #endif 48 49 } // namespace 50 51 JavaScriptAppModalDialog::JavaScriptAppModalDialog( 52 JavaScriptAppModalDialogDelegate* delegate, 53 const std::wstring& title, 54 int dialog_flags, 55 const std::wstring& message_text, 56 const std::wstring& default_prompt_text, 57 bool display_suppress_checkbox, 58 bool is_before_unload_dialog, 59 IPC::Message* reply_msg) 60 : AppModalDialog(delegate->AsTabContents(), title), 61 delegate_(delegate), 62 extension_host_(delegate->AsExtensionHost()), 63 dialog_flags_(dialog_flags), 64 display_suppress_checkbox_(display_suppress_checkbox), 65 is_before_unload_dialog_(is_before_unload_dialog), 66 reply_msg_(reply_msg) { 67 string16 elided_text; 68 EnforceMaxTextSize(WideToUTF16(message_text), &elided_text); 69 message_text_ = UTF16ToWide(elided_text); 70 EnforceMaxPromptSize(WideToUTF16Hack(default_prompt_text), 71 &default_prompt_text_); 72 73 DCHECK((tab_contents_ != NULL) != (extension_host_ != NULL)); 74 InitNotifications(); 75 } 76 77 JavaScriptAppModalDialog::~JavaScriptAppModalDialog() { 78 } 79 80 NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() { 81 gfx::NativeWindow parent_window = tab_contents_ ? 82 tab_contents_->GetMessageBoxRootWindow() : 83 extension_host_->GetMessageBoxRootWindow(); 84 return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this, 85 parent_window); 86 } 87 88 void JavaScriptAppModalDialog::Observe(NotificationType type, 89 const NotificationSource& source, 90 const NotificationDetails& details) { 91 if (skip_this_dialog_) 92 return; 93 94 if (NotificationType::EXTENSION_HOST_DESTROYED == type && 95 Details<ExtensionHost>(extension_host_) != details) 96 return; 97 98 // If we reach here, we know the notification is relevant to us, either 99 // because we're only observing applicable sources or because we passed the 100 // check above. Both of those indicate that we should ignore this dialog. 101 // Also clear the delegate, since it's now invalid. 102 skip_this_dialog_ = true; 103 delegate_ = NULL; 104 if (native_dialog_) 105 CloseModalDialog(); 106 } 107 108 void JavaScriptAppModalDialog::InitNotifications() { 109 // Make sure we get relevant navigation notifications so we know when our 110 // parent contents will disappear or navigate to a different page. 111 if (tab_contents_) { 112 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, 113 Source<NavigationController>(&tab_contents_->controller())); 114 registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, 115 Source<TabContents>(tab_contents_)); 116 } else if (extension_host_) { 117 // EXTENSION_HOST_DESTROYED uses the Profile as its source, but we care 118 // about the ExtensionHost (which is passed in the details). 119 registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, 120 NotificationService::AllSources()); 121 } else { 122 NOTREACHED(); 123 } 124 } 125 126 void JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) { 127 // If we are shutting down and this is an onbeforeunload dialog, cancel the 128 // shutdown. 129 if (is_before_unload_dialog_) 130 browser_shutdown::SetTryingToQuit(false); 131 132 // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame 133 // will receive its activation messages before this dialog receives 134 // WM_DESTROY. The parent frame would then try to activate any modal dialogs 135 // that were still open in the ModalDialogQueue, which would send activation 136 // back to this one. The framework should be improved to handle this, so this 137 // is a temporary workaround. 138 CompleteDialog(); 139 140 NotifyDelegate(false, L"", suppress_js_messages); 141 } 142 143 void JavaScriptAppModalDialog::OnAccept(const std::wstring& prompt_text, 144 bool suppress_js_messages) { 145 CompleteDialog(); 146 NotifyDelegate(true, prompt_text, suppress_js_messages); 147 } 148 149 void JavaScriptAppModalDialog::OnClose() { 150 NotifyDelegate(false, L"", false); 151 } 152 153 void JavaScriptAppModalDialog::NotifyDelegate(bool success, 154 const std::wstring& prompt_text, 155 bool suppress_js_messages) { 156 if (skip_this_dialog_) 157 return; 158 159 delegate_->OnMessageBoxClosed(reply_msg_, success, prompt_text); 160 if (suppress_js_messages) 161 delegate_->SetSuppressMessageBoxes(true); 162 163 // On Views, we can end up coming through this code path twice :(. 164 // See crbug.com/63732. 165 skip_this_dialog_ = true; 166 } 167