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