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