Home | History | Annotate | Download | only in startup
      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/infobar_service.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/shell_integration.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/browser/ui/browser_finder.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/common/chrome_version_info.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/grit/chromium_strings.h"
     24 #include "chrome/grit/generated_resources.h"
     25 #include "chrome/installer/util/master_preferences.h"
     26 #include "chrome/installer/util/master_preferences_constants.h"
     27 #include "components/infobars/core/confirm_infobar_delegate.h"
     28 #include "components/infobars/core/infobar.h"
     29 #include "content/public/browser/browser_thread.h"
     30 #include "content/public/browser/navigation_details.h"
     31 #include "content/public/browser/web_contents.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 OKButtonTriggersUACPrompt() const OVERRIDE;
     80   virtual bool Accept() OVERRIDE;
     81   virtual bool Cancel() OVERRIDE;
     82   virtual bool ShouldExpireInternal(
     83       const NavigationDetails& 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::OKButtonTriggersUACPrompt() const {
    152   return true;
    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 NavigationDetails& 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 #if !defined(OS_WIN)
    261 bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) {
    262   return false;
    263 }
    264 #endif
    265 
    266 }  // namespace chrome
    267