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