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