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