default_browser_prompt.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/startup/default_browser_prompt.h" 6 7#include "base/memory/weak_ptr.h" 8#include "base/message_loop.h" 9#include "base/metrics/histogram.h" 10#include "base/prefs/pref_registry_simple.h" 11#include "base/prefs/pref_service.h" 12#include "base/version.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/first_run/first_run.h" 15#include "chrome/browser/infobars/confirm_infobar_delegate.h" 16#include "chrome/browser/infobars/infobar_service.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/shell_integration.h" 19#include "chrome/browser/ui/browser.h" 20#include "chrome/browser/ui/browser_finder.h" 21#include "chrome/browser/ui/tabs/tab_strip_model.h" 22#include "chrome/common/chrome_version_info.h" 23#include "chrome/common/pref_names.h" 24#include "chrome/installer/util/master_preferences.h" 25#include "chrome/installer/util/master_preferences_constants.h" 26#include "content/public/browser/browser_thread.h" 27#include "content/public/browser/navigation_details.h" 28#include "content/public/browser/web_contents.h" 29#include "grit/chromium_strings.h" 30#include "grit/generated_resources.h" 31#include "grit/theme_resources.h" 32#include "ui/base/l10n/l10n_util.h" 33#include "ui/base/resource/resource_bundle.h" 34 35using content::BrowserThread; 36 37namespace { 38 39// Calls the appropriate function for setting Chrome as the default browser. 40// This requires IO access (registry) and may result in interaction with a 41// modal system UI. 42void SetChromeAsDefaultBrowser(bool interactive_flow, PrefService* prefs) { 43 if (interactive_flow) { 44 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefaultUI", 1); 45 if (!ShellIntegration::SetAsDefaultBrowserInteractive()) { 46 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefaultUIFailed", 1); 47 } else if (ShellIntegration::GetDefaultBrowser() == 48 ShellIntegration::NOT_DEFAULT) { 49 // If the interaction succeeded but we are still not the default browser 50 // it likely means the user simply selected another browser from the 51 // panel. We will respect this choice and write it down as 'no, thanks'. 52 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); 53 } 54 } else { 55 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1); 56 ShellIntegration::SetAsDefaultBrowser(); 57 } 58} 59 60// The delegate for the infobar shown when Chrome is not the default browser. 61class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate { 62 public: 63 // Creates a default browser delegate and adds it to |infobar_service|. 64 static void Create(InfoBarService* infobar_service, 65 PrefService* prefs, 66 bool interactive_flow_required); 67 68 private: 69 DefaultBrowserInfoBarDelegate(InfoBarService* infobar_service, 70 PrefService* prefs, 71 bool interactive_flow_required); 72 virtual ~DefaultBrowserInfoBarDelegate(); 73 74 void AllowExpiry() { should_expire_ = true; } 75 76 // ConfirmInfoBarDelegate: 77 virtual gfx::Image* GetIcon() const OVERRIDE; 78 virtual string16 GetMessageText() const OVERRIDE; 79 virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; 80 virtual bool NeedElevation(InfoBarButton button) const OVERRIDE; 81 virtual bool Accept() OVERRIDE; 82 virtual bool Cancel() OVERRIDE; 83 virtual bool ShouldExpireInternal( 84 const content::LoadCommittedDetails& details) const OVERRIDE; 85 86 // The prefs to use. 87 PrefService* prefs_; 88 89 // Whether the user clicked one of the buttons. 90 bool action_taken_; 91 92 // Whether the info-bar should be dismissed on the next navigation. 93 bool should_expire_; 94 95 // Whether changing the default application will require entering the 96 // modal-UI flow. 97 const bool interactive_flow_required_; 98 99 // Used to delay the expiration of the info-bar. 100 base::WeakPtrFactory<DefaultBrowserInfoBarDelegate> weak_factory_; 101 102 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate); 103}; 104 105// static 106void DefaultBrowserInfoBarDelegate::Create(InfoBarService* infobar_service, 107 PrefService* prefs, 108 bool interactive_flow_required) { 109 infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>( 110 new DefaultBrowserInfoBarDelegate(infobar_service, prefs, 111 interactive_flow_required))); 112} 113 114DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate( 115 InfoBarService* infobar_service, 116 PrefService* prefs, 117 bool interactive_flow_required) 118 : ConfirmInfoBarDelegate(infobar_service), 119 prefs_(prefs), 120 action_taken_(false), 121 should_expire_(false), 122 interactive_flow_required_(interactive_flow_required), 123 weak_factory_(this) { 124 // We want the info-bar to stick-around for few seconds and then be hidden 125 // on the next navigation after that. 126 base::MessageLoop::current()->PostDelayedTask( 127 FROM_HERE, 128 base::Bind(&DefaultBrowserInfoBarDelegate::AllowExpiry, 129 weak_factory_.GetWeakPtr()), 130 base::TimeDelta::FromSeconds(8)); 131} 132 133DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() { 134 if (!action_taken_) 135 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1); 136} 137 138gfx::Image* DefaultBrowserInfoBarDelegate::GetIcon() const { 139 return &ResourceBundle::GetSharedInstance().GetNativeImageNamed( 140 IDR_PRODUCT_LOGO_32); 141} 142 143string16 DefaultBrowserInfoBarDelegate::GetMessageText() const { 144 return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT); 145} 146 147string16 DefaultBrowserInfoBarDelegate::GetButtonLabel( 148 InfoBarButton button) const { 149 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 150 IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL : 151 IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL); 152} 153 154bool DefaultBrowserInfoBarDelegate::NeedElevation(InfoBarButton button) const { 155 return button == BUTTON_OK; 156} 157 158bool DefaultBrowserInfoBarDelegate::Accept() { 159 action_taken_ = true; 160 BrowserThread::PostTask( 161 BrowserThread::FILE, 162 FROM_HERE, 163 base::Bind(&SetChromeAsDefaultBrowser, interactive_flow_required_, 164 prefs_)); 165 166 return true; 167} 168 169bool DefaultBrowserInfoBarDelegate::Cancel() { 170 action_taken_ = true; 171 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); 172 // User clicked "Don't ask me again", remember that. 173 prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false); 174 return true; 175} 176 177bool DefaultBrowserInfoBarDelegate::ShouldExpireInternal( 178 const content::LoadCommittedDetails& details) const { 179 return should_expire_; 180} 181 182void NotifyNotDefaultBrowserCallback(chrome::HostDesktopType desktop_type) { 183 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type); 184 if (!browser) 185 return; // Reached during ui tests. 186 187 // In ChromeBot tests, there might be a race. This line appears to get 188 // called during shutdown and |tab| can be NULL. 189 content::WebContents* web_contents = 190 browser->tab_strip_model()->GetActiveWebContents(); 191 if (!web_contents) 192 return; 193 194 bool interactive_flow = ShellIntegration::CanSetAsDefaultBrowser() == 195 ShellIntegration::SET_DEFAULT_INTERACTIVE; 196 Profile* profile = 197 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 198 DefaultBrowserInfoBarDelegate::Create( 199 InfoBarService::FromWebContents(web_contents), profile->GetPrefs(), 200 interactive_flow); 201} 202 203void CheckDefaultBrowserCallback(chrome::HostDesktopType desktop_type) { 204 if (ShellIntegration::GetDefaultBrowser() == ShellIntegration::NOT_DEFAULT) { 205 ShellIntegration::DefaultWebClientSetPermission default_change_mode = 206 ShellIntegration::CanSetAsDefaultBrowser(); 207 208 if (default_change_mode != ShellIntegration::SET_DEFAULT_NOT_ALLOWED) { 209 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 210 base::Bind(&NotifyNotDefaultBrowserCallback, desktop_type)); 211 } 212 } 213} 214 215} // namespace 216 217namespace chrome { 218 219void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) { 220 registry->RegisterStringPref( 221 prefs::kBrowserSuppressDefaultBrowserPrompt, std::string()); 222} 223 224void ShowDefaultBrowserPrompt(Profile* profile, HostDesktopType desktop_type) { 225 // We do not check if we are the default browser if: 226 // - The user said "don't ask me again" on the infobar earlier. 227 // - There is a policy in control of this setting. 228 // - The "suppress_default_browser_prompt_for_version" master preference is 229 // set to the current version. 230 if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser)) 231 return; 232 233 if (g_browser_process->local_state()->IsManagedPreference( 234 prefs::kDefaultBrowserSettingEnabled)) { 235 if (g_browser_process->local_state()->GetBoolean( 236 prefs::kDefaultBrowserSettingEnabled)) { 237 BrowserThread::PostTask( 238 BrowserThread::FILE, FROM_HERE, 239 base::Bind( 240 base::IgnoreResult(&ShellIntegration::SetAsDefaultBrowser))); 241 } else { 242 // TODO(pastarmovj): We can't really do anything meaningful here yet but 243 // just prevent showing the infobar. 244 } 245 return; 246 } 247 248 const std::string disable_version_string = 249 g_browser_process->local_state()->GetString( 250 prefs::kBrowserSuppressDefaultBrowserPrompt); 251 const Version disable_version(disable_version_string); 252 253 DCHECK(disable_version_string.empty() || disable_version.IsValid()); 254 if (disable_version.IsValid()) { 255 const chrome::VersionInfo version_info; 256 const Version chrome_version(version_info.Version()); 257 if (disable_version.Equals(chrome_version)) 258 return; 259 } 260 261 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 262 base::Bind(&CheckDefaultBrowserCallback, 263 desktop_type)); 264 265} 266 267#if !defined(OS_WIN) 268bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) { 269 return false; 270} 271#endif 272 273} // namespace chrome 274