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