default_browser_prompt.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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_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
34
35namespace {
36
37// Calls the appropriate function for setting Chrome as the default browser.
38// This requires IO access (registry) and may result in interaction with a
39// modal system UI.
40void SetChromeAsDefaultBrowser(bool interactive_flow, PrefService* prefs) {
41  if (interactive_flow) {
42    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefaultUI", 1);
43    if (!ShellIntegration::SetAsDefaultBrowserInteractive()) {
44      UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefaultUIFailed", 1);
45    } else if (ShellIntegration::GetDefaultBrowser() ==
46               ShellIntegration::NOT_DEFAULT) {
47      // If the interaction succeeded but we are still not the default browser
48      // it likely means the user simply selected another browser from the
49      // panel. We will respect this choice and write it down as 'no, thanks'.
50      UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1);
51    }
52  } else {
53    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1);
54    ShellIntegration::SetAsDefaultBrowser();
55  }
56}
57
58// The delegate for the infobar shown when Chrome is not the default browser.
59class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate {
60 public:
61  // Creates a default browser delegate and adds it to |infobar_service|.
62  static void Create(InfoBarService* infobar_service,
63                     PrefService* prefs,
64                     bool interactive_flow_required);
65
66 private:
67  DefaultBrowserInfoBarDelegate(InfoBarService* infobar_service,
68                                PrefService* prefs,
69                                bool interactive_flow_required);
70  virtual ~DefaultBrowserInfoBarDelegate();
71
72  void AllowExpiry() { should_expire_ = true; }
73
74  // ConfirmInfoBarDelegate:
75  virtual int GetIconID() const OVERRIDE;
76  virtual string16 GetMessageText() const OVERRIDE;
77  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
78  virtual bool NeedElevation(InfoBarButton button) const OVERRIDE;
79  virtual bool Accept() OVERRIDE;
80  virtual bool Cancel() OVERRIDE;
81  virtual bool ShouldExpireInternal(
82      const content::LoadCommittedDetails& details) const OVERRIDE;
83
84  // The prefs to use.
85  PrefService* prefs_;
86
87  // Whether the user clicked one of the buttons.
88  bool action_taken_;
89
90  // Whether the info-bar should be dismissed on the next navigation.
91  bool should_expire_;
92
93  // Whether changing the default application will require entering the
94  // modal-UI flow.
95  const bool interactive_flow_required_;
96
97  // Used to delay the expiration of the info-bar.
98  base::WeakPtrFactory<DefaultBrowserInfoBarDelegate> weak_factory_;
99
100  DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate);
101};
102
103// static
104void DefaultBrowserInfoBarDelegate::Create(InfoBarService* infobar_service,
105                                           PrefService* prefs,
106                                           bool interactive_flow_required) {
107  infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
108      new DefaultBrowserInfoBarDelegate(infobar_service, prefs,
109                                        interactive_flow_required)));
110}
111
112DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate(
113    InfoBarService* infobar_service,
114    PrefService* prefs,
115    bool interactive_flow_required)
116    : ConfirmInfoBarDelegate(infobar_service),
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
131DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() {
132  if (!action_taken_)
133    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1);
134}
135
136int DefaultBrowserInfoBarDelegate::GetIconID() const {
137  return IDR_PRODUCT_LOGO_32;
138}
139
140string16 DefaultBrowserInfoBarDelegate::GetMessageText() const {
141  return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT);
142}
143
144string16 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
151bool DefaultBrowserInfoBarDelegate::NeedElevation(InfoBarButton button) const {
152  return button == BUTTON_OK;
153}
154
155bool 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
165bool 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
173bool DefaultBrowserInfoBarDelegate::ShouldExpireInternal(
174    const content::LoadCommittedDetails& details) const {
175  return should_expire_;
176}
177
178void 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
198void 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
213namespace chrome {
214
215void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) {
216  registry->RegisterStringPref(
217      prefs::kBrowserSuppressDefaultBrowserPrompt, std::string());
218}
219
220void 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)
262bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) {
263  return false;
264}
265#endif
266
267}  // namespace chrome
268