default_browser_prompt.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 MessageLoop::current()->PostDelayedTask( 127 FROM_HERE, base::Bind(&DefaultBrowserInfoBarDelegate::AllowExpiry, 128 weak_factory_.GetWeakPtr()), 129 base::TimeDelta::FromSeconds(8)); 130} 131 132DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() { 133 if (!action_taken_) 134 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1); 135} 136 137gfx::Image* DefaultBrowserInfoBarDelegate::GetIcon() const { 138 return &ResourceBundle::GetSharedInstance().GetNativeImageNamed( 139 IDR_PRODUCT_LOGO_32); 140} 141 142string16 DefaultBrowserInfoBarDelegate::GetMessageText() const { 143 return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT); 144} 145 146string16 DefaultBrowserInfoBarDelegate::GetButtonLabel( 147 InfoBarButton button) const { 148 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 149 IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL : 150 IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL); 151} 152 153bool DefaultBrowserInfoBarDelegate::NeedElevation(InfoBarButton button) const { 154 return button == BUTTON_OK; 155} 156 157bool DefaultBrowserInfoBarDelegate::Accept() { 158 action_taken_ = true; 159 BrowserThread::PostTask( 160 BrowserThread::FILE, 161 FROM_HERE, 162 base::Bind(&SetChromeAsDefaultBrowser, interactive_flow_required_, 163 prefs_)); 164 165 return true; 166} 167 168bool DefaultBrowserInfoBarDelegate::Cancel() { 169 action_taken_ = true; 170 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); 171 // User clicked "Don't ask me again", remember that. 172 prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false); 173 return true; 174} 175 176bool DefaultBrowserInfoBarDelegate::ShouldExpireInternal( 177 const content::LoadCommittedDetails& details) const { 178 return should_expire_; 179} 180 181void NotifyNotDefaultBrowserCallback(chrome::HostDesktopType desktop_type) { 182 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type); 183 if (!browser) 184 return; // Reached during ui tests. 185 186 // In ChromeBot tests, there might be a race. This line appears to get 187 // called during shutdown and |tab| can be NULL. 188 content::WebContents* web_contents = 189 browser->tab_strip_model()->GetActiveWebContents(); 190 if (!web_contents) 191 return; 192 193 bool interactive_flow = ShellIntegration::CanSetAsDefaultBrowser() == 194 ShellIntegration::SET_DEFAULT_INTERACTIVE; 195 Profile* profile = 196 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 197 DefaultBrowserInfoBarDelegate::Create( 198 InfoBarService::FromWebContents(web_contents), profile->GetPrefs(), 199 interactive_flow); 200} 201 202void CheckDefaultBrowserCallback(chrome::HostDesktopType desktop_type) { 203 if (ShellIntegration::GetDefaultBrowser() == ShellIntegration::NOT_DEFAULT) { 204 ShellIntegration::DefaultWebClientSetPermission default_change_mode = 205 ShellIntegration::CanSetAsDefaultBrowser(); 206 207 if (default_change_mode != ShellIntegration::SET_DEFAULT_NOT_ALLOWED) { 208 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 209 base::Bind(&NotifyNotDefaultBrowserCallback, desktop_type)); 210 } 211 } 212} 213 214} // namespace 215 216namespace chrome { 217 218void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) { 219 registry->RegisterStringPref( 220 prefs::kBrowserSuppressDefaultBrowserPrompt, std::string()); 221} 222 223void ShowDefaultBrowserPrompt(Profile* profile, HostDesktopType desktop_type) { 224 // We do not check if we are the default browser if: 225 // - The user said "don't ask me again" on the infobar earlier. 226 // - There is a policy in control of this setting. 227 // - The "suppress_default_browser_prompt_for_version" master preference is 228 // set to the current version. 229 if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser)) 230 return; 231 232 if (g_browser_process->local_state()->IsManagedPreference( 233 prefs::kDefaultBrowserSettingEnabled)) { 234 if (g_browser_process->local_state()->GetBoolean( 235 prefs::kDefaultBrowserSettingEnabled)) { 236 BrowserThread::PostTask( 237 BrowserThread::FILE, FROM_HERE, 238 base::Bind( 239 base::IgnoreResult(&ShellIntegration::SetAsDefaultBrowser))); 240 } else { 241 // TODO(pastarmovj): We can't really do anything meaningful here yet but 242 // just prevent showing the infobar. 243 } 244 return; 245 } 246 247 const std::string disable_version_string = 248 g_browser_process->local_state()->GetString( 249 prefs::kBrowserSuppressDefaultBrowserPrompt); 250 const Version disable_version(disable_version_string); 251 252 DCHECK(disable_version_string.empty() || disable_version.IsValid()); 253 if (disable_version.IsValid()) { 254 const chrome::VersionInfo version_info; 255 const Version chrome_version(version_info.Version()); 256 if (disable_version.Equals(chrome_version)) 257 return; 258 } 259 260 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 261 base::Bind(&CheckDefaultBrowserCallback, 262 desktop_type)); 263 264} 265 266#if !defined(OS_WIN) 267bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) { 268 return false; 269} 270#endif 271 272} // namespace chrome 273