javascript_dialog_manager.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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_dialog_manager.h" 6 7#include "base/compiler_specific.h" 8#include "base/i18n/rtl.h" 9#include "base/memory/singleton.h" 10#include "base/utf_string_conversions.h" 11#include "chrome/browser/extensions/extension_host.h" 12#include "chrome/browser/extensions/extension_service.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h" 15#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h" 16#include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h" 17#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h" 18#include "chrome/common/chrome_constants.h" 19#include "chrome/common/chrome_notification_types.h" 20#include "content/public/browser/notification_observer.h" 21#include "content/public/browser/notification_registrar.h" 22#include "content/public/browser/notification_service.h" 23#include "content/public/common/content_client.h" 24#include "content/public/common/javascript_message_type.h" 25#include "grit/generated_resources.h" 26#include "net/base/net_util.h" 27#include "ui/base/l10n/l10n_util.h" 28 29using content::JavaScriptDialogManager; 30using content::WebContents; 31 32namespace { 33 34class ChromeJavaScriptDialogManager : public JavaScriptDialogManager, 35 public content::NotificationObserver { 36 public: 37 static ChromeJavaScriptDialogManager* GetInstance(); 38 39 explicit ChromeJavaScriptDialogManager( 40 extensions::ExtensionHost* extension_host); 41 virtual ~ChromeJavaScriptDialogManager(); 42 43 virtual void RunJavaScriptDialog( 44 WebContents* web_contents, 45 const GURL& origin_url, 46 const std::string& accept_lang, 47 content::JavaScriptMessageType message_type, 48 const string16& message_text, 49 const string16& default_prompt_text, 50 const DialogClosedCallback& callback, 51 bool* did_suppress_message) OVERRIDE; 52 53 virtual void RunBeforeUnloadDialog( 54 WebContents* web_contents, 55 const string16& message_text, 56 bool is_reload, 57 const DialogClosedCallback& callback) OVERRIDE; 58 59 virtual bool HandleJavaScriptDialog( 60 WebContents* web_contents, 61 bool accept, 62 const string16* prompt_override) OVERRIDE; 63 64 virtual void ResetJavaScriptState(WebContents* web_contents) OVERRIDE; 65 66 private: 67 ChromeJavaScriptDialogManager(); 68 69 friend struct DefaultSingletonTraits<ChromeJavaScriptDialogManager>; 70 71 // Overridden from content::NotificationObserver: 72 virtual void Observe(int type, 73 const content::NotificationSource& source, 74 const content::NotificationDetails& details) OVERRIDE; 75 76 string16 GetTitle(const GURL& origin_url, 77 const std::string& accept_lang, 78 bool is_alert); 79 80 void CancelPendingDialogs(WebContents* web_contents); 81 82 // Wrapper around a DialogClosedCallback so that we can intercept it before 83 // passing it onto the original callback. 84 void OnDialogClosed(DialogClosedCallback callback, 85 bool success, 86 const string16& user_input); 87 88 // Mapping between the WebContents and their extra data. The key 89 // is a void* because the pointer is just a cookie and is never dereferenced. 90 JavaScriptAppModalDialog::ExtraDataMap javascript_dialog_extra_data_; 91 92 // Extension Host which owns the ChromeJavaScriptDialogManager instance. 93 // It's used to get a extension name from a URL. 94 // If it's not owned by any Extension, it should be NULL. 95 extensions::ExtensionHost* extension_host_; 96 97 content::NotificationRegistrar registrar_; 98 99 DISALLOW_COPY_AND_ASSIGN(ChromeJavaScriptDialogManager); 100}; 101 102//////////////////////////////////////////////////////////////////////////////// 103// ChromeJavaScriptDialogManager, public: 104 105ChromeJavaScriptDialogManager::ChromeJavaScriptDialogManager() 106 : extension_host_(NULL) { 107} 108 109ChromeJavaScriptDialogManager::~ChromeJavaScriptDialogManager() { 110 extension_host_ = NULL; 111} 112 113ChromeJavaScriptDialogManager::ChromeJavaScriptDialogManager( 114 extensions::ExtensionHost* extension_host) 115 : extension_host_(extension_host) { 116 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, 117 content::Source<Profile>(extension_host_->profile())); 118} 119 120// static 121ChromeJavaScriptDialogManager* ChromeJavaScriptDialogManager::GetInstance() { 122 return Singleton<ChromeJavaScriptDialogManager>::get(); 123} 124 125void ChromeJavaScriptDialogManager::RunJavaScriptDialog( 126 WebContents* web_contents, 127 const GURL& origin_url, 128 const std::string& accept_lang, 129 content::JavaScriptMessageType message_type, 130 const string16& message_text, 131 const string16& default_prompt_text, 132 const DialogClosedCallback& callback, 133 bool* did_suppress_message) { 134 *did_suppress_message = false; 135 136 ChromeJavaScriptDialogExtraData* extra_data = 137 &javascript_dialog_extra_data_[web_contents]; 138 139 if (extra_data->suppress_javascript_messages_) { 140 *did_suppress_message = true; 141 return; 142 } 143 144 base::TimeDelta time_since_last_message = base::TimeTicks::Now() - 145 extra_data->last_javascript_message_dismissal_; 146 bool display_suppress_checkbox = false; 147 // Show a checkbox offering to suppress further messages if this message is 148 // being displayed within kJavascriptMessageExpectedDelay of the last one. 149 if (time_since_last_message < 150 base::TimeDelta::FromMilliseconds( 151 chrome::kJavascriptMessageExpectedDelay)) { 152 display_suppress_checkbox = true; 153 } 154 155 bool is_alert = message_type == content::JAVASCRIPT_MESSAGE_TYPE_ALERT; 156 string16 dialog_title = GetTitle(origin_url, accept_lang, is_alert); 157 158 if (extension_host_) 159 extension_host_->WillRunJavaScriptDialog(); 160 161 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( 162 web_contents, 163 &javascript_dialog_extra_data_, 164 dialog_title, 165 message_type, 166 message_text, 167 default_prompt_text, 168 display_suppress_checkbox, 169 false, // is_before_unload_dialog 170 false, // is_reload 171 base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed, 172 base::Unretained(this), callback))); 173} 174 175void ChromeJavaScriptDialogManager::RunBeforeUnloadDialog( 176 WebContents* web_contents, 177 const string16& message_text, 178 bool is_reload, 179 const DialogClosedCallback& callback) { 180 const string16 title = l10n_util::GetStringUTF16(is_reload ? 181 IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE); 182 const string16 footer = l10n_util::GetStringUTF16(is_reload ? 183 IDS_BEFORERELOAD_MESSAGEBOX_FOOTER : IDS_BEFOREUNLOAD_MESSAGEBOX_FOOTER); 184 185 string16 full_message = message_text + ASCIIToUTF16("\n\n") + footer; 186 187 if (extension_host_) 188 extension_host_->WillRunJavaScriptDialog(); 189 190 AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( 191 web_contents, 192 &javascript_dialog_extra_data_, 193 title, 194 content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM, 195 full_message, 196 string16(), // default_prompt_text 197 false, // display_suppress_checkbox 198 true, // is_before_unload_dialog 199 is_reload, 200 base::Bind(&ChromeJavaScriptDialogManager::OnDialogClosed, 201 base::Unretained(this), callback))); 202} 203 204bool ChromeJavaScriptDialogManager::HandleJavaScriptDialog( 205 WebContents* web_contents, 206 bool accept, 207 const string16* prompt_override) { 208 AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance(); 209 if (!dialog_queue->HasActiveDialog() || 210 !dialog_queue->active_dialog()->IsJavaScriptModalDialog() || 211 dialog_queue->active_dialog()->web_contents() != web_contents) { 212 return false; 213 } 214 JavaScriptAppModalDialog* dialog = static_cast<JavaScriptAppModalDialog*>( 215 dialog_queue->active_dialog()); 216 if (accept) { 217 if (prompt_override) 218 dialog->SetOverridePromptText(*prompt_override); 219 dialog->native_dialog()->AcceptAppModalDialog(); 220 } else { 221 dialog->native_dialog()->CancelAppModalDialog(); 222 } 223 return true; 224} 225 226void ChromeJavaScriptDialogManager::ResetJavaScriptState( 227 WebContents* web_contents) { 228 CancelPendingDialogs(web_contents); 229 javascript_dialog_extra_data_.erase(web_contents); 230} 231 232void ChromeJavaScriptDialogManager::Observe( 233 int type, 234 const content::NotificationSource& source, 235 const content::NotificationDetails& details) { 236 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED); 237 extension_host_ = NULL; 238} 239 240string16 ChromeJavaScriptDialogManager::GetTitle(const GURL& origin_url, 241 const std::string& accept_lang, 242 bool is_alert) { 243 // If the URL hasn't any host, return the default string. 244 if (!origin_url.has_host()) { 245 return l10n_util::GetStringUTF16( 246 is_alert ? IDS_JAVASCRIPT_ALERT_DEFAULT_TITLE 247 : IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE); 248 } 249 250 // If the URL is a chrome extension one, return the extension name. 251 if (extension_host_) { 252 const extensions::Extension* extension = extension_host_-> 253 profile()->GetExtensionService()->extensions()-> 254 GetExtensionOrAppByURL(ExtensionURLInfo(origin_url)); 255 if (extension) { 256 return UTF8ToUTF16(base::StringPiece(extension->name())); 257 } 258 } 259 260 // Otherwise, return the formatted URL. 261 // In this case, force URL to have LTR directionality. 262 string16 url_string = net::FormatUrl(origin_url, accept_lang); 263 return l10n_util::GetStringFUTF16( 264 is_alert ? IDS_JAVASCRIPT_ALERT_TITLE 265 : IDS_JAVASCRIPT_MESSAGEBOX_TITLE, 266 base::i18n::GetDisplayStringInLTRDirectionality(url_string)); 267} 268 269void ChromeJavaScriptDialogManager::CancelPendingDialogs( 270 WebContents* web_contents) { 271 AppModalDialogQueue* queue = AppModalDialogQueue::GetInstance(); 272 AppModalDialog* active_dialog = queue->active_dialog(); 273 if (active_dialog && active_dialog->web_contents() == web_contents) 274 active_dialog->Invalidate(); 275 for (AppModalDialogQueue::iterator i = queue->begin(); 276 i != queue->end(); ++i) { 277 if ((*i)->web_contents() == web_contents) 278 (*i)->Invalidate(); 279 } 280} 281 282void ChromeJavaScriptDialogManager::OnDialogClosed( 283 DialogClosedCallback callback, 284 bool success, 285 const string16& user_input) { 286 if (extension_host_) 287 extension_host_->DidCloseJavaScriptDialog(); 288 callback.Run(success, user_input); 289} 290 291} // namespace 292 293content::JavaScriptDialogManager* GetJavaScriptDialogManagerInstance() { 294 return ChromeJavaScriptDialogManager::GetInstance(); 295} 296 297content::JavaScriptDialogManager* CreateJavaScriptDialogManagerInstance( 298 extensions::ExtensionHost* extension_host) { 299 return new ChromeJavaScriptDialogManager(extension_host); 300} 301