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/webui/set_as_default_browser_ui.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/memory/weak_ptr.h" 10#include "base/metrics/histogram.h" 11#include "base/path_service.h" 12#include "base/prefs/pref_service.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/shell_integration.h" 15#include "chrome/browser/ui/browser.h" 16#include "chrome/browser/ui/browser_dialogs.h" 17#include "chrome/browser/ui/browser_finder.h" 18#include "chrome/browser/ui/browser_list.h" 19#include "chrome/browser/ui/browser_list_observer.h" 20#include "chrome/browser/ui/browser_window.h" 21#include "chrome/browser/ui/chrome_pages.h" 22#include "chrome/browser/ui/singleton_tabs.h" 23#include "chrome/browser/ui/sync/sync_promo_ui.h" 24#include "chrome/browser/ui/tabs/tab_strip_model.h" 25#include "chrome/common/pref_names.h" 26#include "chrome/common/url_constants.h" 27#include "chrome/grit/generated_resources.h" 28#include "chrome/grit/locale_settings.h" 29#include "chrome/installer/util/install_util.h" 30#include "content/public/browser/browser_thread.h" 31#include "content/public/browser/web_contents.h" 32#include "content/public/browser/web_contents_delegate.h" 33#include "content/public/browser/web_ui.h" 34#include "content/public/browser/web_ui_data_source.h" 35#include "content/public/browser/web_ui_message_handler.h" 36#include "grit/browser_resources.h" 37#include "ui/base/l10n/l10n_font_util.h" 38#include "ui/base/l10n/l10n_util.h" 39#include "ui/gfx/font.h" 40#include "ui/views/widget/widget.h" 41#include "ui/web_dialogs/web_dialog_delegate.h" 42 43using content::BrowserThread; 44using content::WebContents; 45using content::WebUIMessageHandler; 46 47namespace { 48 49const char kSetAsDefaultBrowserHistogram[] = "DefaultBrowser.InteractionResult"; 50 51// The enum permits registering in UMA the three possible outcomes (do not 52// reorder these). 53// ACCEPTED: user pressed Next and made Chrome default. 54// DECLINED: user simply closed the dialog without making Chrome default. 55// REGRETTED: user pressed Next but then elected a different default browser. 56enum MakeChromeDefaultResult { 57 MAKE_CHROME_DEFAULT_ACCEPTED = 0, 58 MAKE_CHROME_DEFAULT_DECLINED = 1, 59 MAKE_CHROME_DEFAULT_REGRETTED = 2, 60 // MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE = 3, // Deprecated. 61 MAKE_CHROME_DEFAULT_MAX 62}; 63 64content::WebUIDataSource* CreateSetAsDefaultBrowserUIHTMLSource() { 65 content::WebUIDataSource* data_source = content::WebUIDataSource::Create( 66 chrome::kChromeUIMetroFlowHost); 67 data_source->SetUseJsonJSFormatV2(); 68 data_source->AddLocalizedString("page-title", IDS_METRO_FLOW_TAB_TITLE); 69 data_source->AddLocalizedString("flowTitle", IDS_METRO_FLOW_TITLE_SHORT); 70 data_source->AddLocalizedString("flowDescription", 71 IDS_METRO_FLOW_DESCRIPTION); 72 data_source->AddLocalizedString("flowNext", 73 IDS_METRO_FLOW_SET_DEFAULT); 74 data_source->AddLocalizedString("chromeLogoString", 75 IDS_METRO_FLOW_LOGO_STRING_ALT); 76 data_source->SetJsonPath("strings.js"); 77 data_source->AddResourcePath("set_as_default_browser.js", 78 IDR_SET_AS_DEFAULT_BROWSER_JS); 79 data_source->SetDefaultResource(IDR_SET_AS_DEFAULT_BROWSER_HTML); 80 return data_source; 81} 82 83// A simple class serving as a delegate for passing down the result of the 84// interaction. 85class ResponseDelegate { 86 public: 87 virtual void SetDialogInteractionResult(MakeChromeDefaultResult result) = 0; 88 89 protected: 90 virtual ~ResponseDelegate() { } 91}; 92 93// Event handler for SetAsDefaultBrowserUI. Capable of setting Chrome as the 94// default browser on button click, closing itself and triggering Chrome 95// restart. 96class SetAsDefaultBrowserHandler 97 : public WebUIMessageHandler, 98 public base::SupportsWeakPtr<SetAsDefaultBrowserHandler>, 99 public ShellIntegration::DefaultWebClientObserver { 100 public: 101 explicit SetAsDefaultBrowserHandler( 102 const base::WeakPtr<ResponseDelegate>& response_delegate); 103 virtual ~SetAsDefaultBrowserHandler(); 104 105 // WebUIMessageHandler implementation. 106 virtual void RegisterMessages() OVERRIDE; 107 108 // ShellIntegration::DefaultWebClientObserver implementation. 109 virtual void SetDefaultWebClientUIState( 110 ShellIntegration::DefaultWebClientUIState state) OVERRIDE; 111 virtual void OnSetAsDefaultConcluded(bool close_chrome) OVERRIDE; 112 virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE; 113 114 private: 115 // Handler for the 'Next' (or 'make Chrome the Metro browser') button. 116 void HandleLaunchSetDefaultBrowserFlow(const base::ListValue* args); 117 118 // Close this web ui. 119 void ConcludeInteraction(MakeChromeDefaultResult interaction_result); 120 121 scoped_refptr<ShellIntegration::DefaultBrowserWorker> default_browser_worker_; 122 bool set_default_returned_; 123 bool set_default_result_; 124 base::WeakPtr<ResponseDelegate> response_delegate_; 125 126 DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserHandler); 127}; 128 129SetAsDefaultBrowserHandler::SetAsDefaultBrowserHandler( 130 const base::WeakPtr<ResponseDelegate>& response_delegate) 131 : default_browser_worker_(new ShellIntegration::DefaultBrowserWorker(this)), 132 set_default_returned_(false), set_default_result_(false), 133 response_delegate_(response_delegate) { 134} 135 136SetAsDefaultBrowserHandler::~SetAsDefaultBrowserHandler() { 137 default_browser_worker_->ObserverDestroyed(); 138} 139 140void SetAsDefaultBrowserHandler::RegisterMessages() { 141 web_ui()->RegisterMessageCallback( 142 "SetAsDefaultBrowser:LaunchSetDefaultBrowserFlow", 143 base::Bind(&SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow, 144 base::Unretained(this))); 145} 146 147void SetAsDefaultBrowserHandler::SetDefaultWebClientUIState( 148 ShellIntegration::DefaultWebClientUIState state) { 149 // The callback is expected to be invoked once the procedure has completed. 150 DCHECK_CURRENTLY_ON(BrowserThread::UI); 151 if (!set_default_returned_) 152 return; 153 154 if (state == ShellIntegration::STATE_NOT_DEFAULT && set_default_result_) { 155 // The operation concluded, but Chrome is still not the default. 156 // If the call has succeeded, this suggests user has decided not to make 157 // chrome the default. 158 ConcludeInteraction(MAKE_CHROME_DEFAULT_REGRETTED); 159 } else if (state == ShellIntegration::STATE_IS_DEFAULT) { 160 ConcludeInteraction(MAKE_CHROME_DEFAULT_ACCEPTED); 161 } 162 163 // Otherwise, keep the dialog open since the user probably didn't make a 164 // choice. 165} 166 167void SetAsDefaultBrowserHandler::OnSetAsDefaultConcluded(bool call_result) { 168 set_default_returned_ = true; 169 set_default_result_ = call_result; 170} 171 172bool SetAsDefaultBrowserHandler::IsInteractiveSetDefaultPermitted() { 173 return true; 174} 175 176void SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow( 177 const base::ListValue* args) { 178 set_default_returned_ = false; 179 set_default_result_ = false; 180 default_browser_worker_->StartSetAsDefault(); 181} 182 183void SetAsDefaultBrowserHandler::ConcludeInteraction( 184 MakeChromeDefaultResult interaction_result) { 185 DCHECK_CURRENTLY_ON(BrowserThread::UI); 186 187 if (response_delegate_) 188 response_delegate_->SetDialogInteractionResult(interaction_result); 189 190 WebContents* contents = web_ui()->GetWebContents(); 191 192 if (contents) { 193 content::WebContentsDelegate* delegate = contents->GetDelegate(); 194 if (delegate) 195 delegate->CloseContents(contents); 196 } 197} 198 199// A web dialog delegate implementation for when 'Make Chrome Metro' UI 200// is displayed on a dialog. 201class SetAsDefaultBrowserDialogImpl : public ui::WebDialogDelegate, 202 public ResponseDelegate, 203 public chrome::BrowserListObserver { 204 public: 205 SetAsDefaultBrowserDialogImpl(Profile* profile, Browser* browser); 206 virtual ~SetAsDefaultBrowserDialogImpl(); 207 // Show a modal web dialog with kChromeUIMetroFlowURL page. 208 void ShowDialog(); 209 210 protected: 211 // Overridden from WebDialogDelegate: 212 virtual ui::ModalType GetDialogModalType() const OVERRIDE; 213 virtual base::string16 GetDialogTitle() const OVERRIDE; 214 virtual GURL GetDialogContentURL() const OVERRIDE; 215 virtual void GetWebUIMessageHandlers( 216 std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE; 217 virtual void GetDialogSize(gfx::Size* size) const OVERRIDE; 218 virtual std::string GetDialogArgs() const OVERRIDE; 219 virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE; 220 virtual void OnCloseContents(WebContents* source, 221 bool* out_close_dialog) OVERRIDE; 222 virtual bool ShouldShowDialogTitle() const OVERRIDE; 223 virtual bool HandleContextMenu( 224 const content::ContextMenuParams& params) OVERRIDE; 225 226 // Overridden from ResponseDelegate: 227 virtual void SetDialogInteractionResult(MakeChromeDefaultResult result); 228 229 // Overridden from BrowserListObserver: 230 virtual void OnBrowserRemoved(Browser* browser) OVERRIDE; 231 232 private: 233 Profile* profile_; 234 Browser* browser_; 235 mutable bool owns_handler_; 236 base::WeakPtrFactory<ResponseDelegate> response_delegate_ptr_factory_; 237 SetAsDefaultBrowserHandler* handler_; 238 MakeChromeDefaultResult dialog_interaction_result_; 239 240 DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserDialogImpl); 241}; 242 243SetAsDefaultBrowserDialogImpl::SetAsDefaultBrowserDialogImpl(Profile* profile, 244 Browser* browser) 245 : profile_(profile), 246 browser_(browser), 247 owns_handler_(true), 248 response_delegate_ptr_factory_(this), 249 handler_(new SetAsDefaultBrowserHandler( 250 response_delegate_ptr_factory_.GetWeakPtr())), 251 dialog_interaction_result_(MAKE_CHROME_DEFAULT_DECLINED) { 252 BrowserList::AddObserver(this); 253} 254 255SetAsDefaultBrowserDialogImpl::~SetAsDefaultBrowserDialogImpl() { 256 if (browser_) 257 BrowserList::RemoveObserver(this); 258 if (owns_handler_) 259 delete handler_; 260} 261 262void SetAsDefaultBrowserDialogImpl::ShowDialog() { 263 // Use a NULL parent window to make sure that the dialog will have an item 264 // in the Windows task bar. The code below will make it highlight if the 265 // dialog is not in the foreground. 266 gfx::NativeWindow native_window = chrome::ShowWebDialog(NULL, profile_, this); 267 views::Widget* widget = views::Widget::GetWidgetForNativeWindow( 268 native_window); 269 widget->FlashFrame(true); 270} 271 272ui::ModalType SetAsDefaultBrowserDialogImpl::GetDialogModalType() const { 273 return ui::MODAL_TYPE_SYSTEM; 274} 275 276base::string16 SetAsDefaultBrowserDialogImpl::GetDialogTitle() const { 277 return l10n_util::GetStringUTF16(IDS_METRO_FLOW_TAB_TITLE); 278} 279 280GURL SetAsDefaultBrowserDialogImpl::GetDialogContentURL() const { 281 std::string url_string(chrome::kChromeUIMetroFlowURL); 282 return GURL(url_string); 283} 284 285void SetAsDefaultBrowserDialogImpl::GetWebUIMessageHandlers( 286 std::vector<WebUIMessageHandler*>* handlers) const { 287 handlers->push_back(handler_); 288 owns_handler_ = false; 289} 290 291void SetAsDefaultBrowserDialogImpl::GetDialogSize(gfx::Size* size) const { 292 PrefService* prefs = profile_->GetPrefs(); 293 gfx::Font approximate_web_font( 294 prefs->GetString(prefs::kWebKitSansSerifFontFamily), 295 prefs->GetInteger(prefs::kWebKitDefaultFontSize)); 296 297 *size = ui::GetLocalizedContentsSizeForFont( 298 IDS_METRO_FLOW_WIDTH_CHARS, IDS_METRO_FLOW_HEIGHT_LINES, 299 approximate_web_font); 300} 301 302std::string SetAsDefaultBrowserDialogImpl::GetDialogArgs() const { 303 return "[]"; 304} 305 306void SetAsDefaultBrowserDialogImpl::OnDialogClosed( 307 const std::string& json_retval) { 308 // Register the user's response in UMA. 309 UMA_HISTOGRAM_ENUMERATION(kSetAsDefaultBrowserHistogram, 310 dialog_interaction_result_, 311 MAKE_CHROME_DEFAULT_MAX); 312 313 // If the user explicitly elected *not to* make Chrome default, we won't 314 // ask again. 315 if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_REGRETTED) { 316 PrefService* prefs = profile_->GetPrefs(); 317 prefs->SetBoolean(prefs::kCheckDefaultBrowser, false); 318 } 319 320 // Carry on with a normal chrome session. For the purpose of surfacing this 321 // dialog the actual browser window had to remain hidden. Now it's time to 322 // show it. 323 if (browser_) { 324 BrowserWindow* window = browser_->window(); 325 WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents(); 326 window->Show(); 327 if (contents) 328 contents->SetInitialFocus(); 329 } 330 331 delete this; 332} 333 334void SetAsDefaultBrowserDialogImpl::OnCloseContents(WebContents* source, 335 bool* out_close_dialog) { 336 *out_close_dialog = true; 337} 338 339bool SetAsDefaultBrowserDialogImpl::ShouldShowDialogTitle() const { 340 return true; 341} 342 343bool SetAsDefaultBrowserDialogImpl::HandleContextMenu( 344 const content::ContextMenuParams& params) { 345 return true; 346} 347 348void SetAsDefaultBrowserDialogImpl::SetDialogInteractionResult( 349 MakeChromeDefaultResult result) { 350 dialog_interaction_result_ = result; 351} 352 353void SetAsDefaultBrowserDialogImpl::OnBrowserRemoved(Browser* browser) { 354 if (browser_ == browser) { 355 browser_ = NULL; 356 BrowserList::RemoveObserver(this); 357 } 358} 359 360} // namespace 361 362SetAsDefaultBrowserUI::SetAsDefaultBrowserUI(content::WebUI* web_ui) 363 : ui::WebDialogUI(web_ui) { 364 content::WebUIDataSource::Add( 365 Profile::FromWebUI(web_ui), CreateSetAsDefaultBrowserUIHTMLSource()); 366} 367 368// static 369void SetAsDefaultBrowserUI::Show(Profile* profile, Browser* browser) { 370 DCHECK_CURRENTLY_ON(BrowserThread::UI); 371 SetAsDefaultBrowserDialogImpl* dialog = 372 new SetAsDefaultBrowserDialogImpl(profile, browser); 373 dialog->ShowDialog(); 374} 375