summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/startup/default_browser_prompt.cc
blob: d1c9143591d6beab85bf26a5b7c0e8adee3a1323 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/startup/default_browser_prompt.h"

#include <string>

#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/web_contents.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/vector_icons_public.h"

namespace {

// A ShellIntegration::DefaultWebClientObserver that records user metrics for
// the result of making Chrome the default browser.
class SetDefaultBrowserObserver
    : public ShellIntegration::DefaultWebClientObserver {
 public:
  SetDefaultBrowserObserver();
  ~SetDefaultBrowserObserver() override;

 private:
  void SetDefaultWebClientUIState(
      ShellIntegration::DefaultWebClientUIState state) override;
  void OnSetAsDefaultConcluded(bool succeeded) override;
  bool IsOwnedByWorker() override;
  bool IsInteractiveSetDefaultPermitted() override;

  // True if an interactive flow will be used (i.e., Windows 8+).
  bool interactive_;

  // The result of the call to ShellIntegration::SetAsDefaultBrowser() or
  // ShellIntegration::SetAsDefaultBrowserInteractive().
  bool interaction_succeeded_ = false;

  DISALLOW_COPY_AND_ASSIGN(SetDefaultBrowserObserver);
};

SetDefaultBrowserObserver::SetDefaultBrowserObserver()
    : interactive_(ShellIntegration::CanSetAsDefaultBrowser() ==
                   ShellIntegration::SET_DEFAULT_INTERACTIVE) {
  // Log that an attempt is about to be made to set one way or the other.
  if (interactive_)
    UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUI", true);
  else
    UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefault", true);
}

SetDefaultBrowserObserver::~SetDefaultBrowserObserver() {}

void SetDefaultBrowserObserver::SetDefaultWebClientUIState(
    ShellIntegration::DefaultWebClientUIState state) {
  if (interactive_ && interaction_succeeded_ &&
      state == ShellIntegration::STATE_NOT_DEFAULT) {
    // The interactive flow succeeded, yet Chrome is not the default browser.
    // This likely means that the user selected another browser from the panel.
    // Consider this the same as canceling the infobar.
    UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true);
  }
}

void SetDefaultBrowserObserver::OnSetAsDefaultConcluded(bool succeeded) {
  interaction_succeeded_ = succeeded;
  if (interactive_ && !succeeded) {
    // Log that the interactive flow failed.
    UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.SetAsDefaultUIFailed", true);
  }
}

bool SetDefaultBrowserObserver::IsOwnedByWorker() {
  // Instruct the DefaultBrowserWorker to delete this instance when it is done.
  return true;
}

bool SetDefaultBrowserObserver::IsInteractiveSetDefaultPermitted() {
  return true;
}

// The delegate for the infobar shown when Chrome is not the default browser.
class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate {
 public:
  // Creates a default browser infobar and delegate and adds the infobar to
  // |infobar_service|.
  static void Create(InfoBarService* infobar_service, PrefService* prefs);

 private:
  explicit DefaultBrowserInfoBarDelegate(PrefService* prefs);
  ~DefaultBrowserInfoBarDelegate() override;

  void AllowExpiry() { should_expire_ = true; }

  // ConfirmInfoBarDelegate:
  Type GetInfoBarType() const override;
  int GetIconId() const override;
  gfx::VectorIconId GetVectorIconId() const override;
  bool ShouldExpire(const NavigationDetails& details) const override;
  base::string16 GetMessageText() const override;
  base::string16 GetButtonLabel(InfoBarButton button) const override;
  bool OKButtonTriggersUACPrompt() const override;
  bool Accept() override;
  bool Cancel() override;

  // The prefs to use.
  PrefService* prefs_;

  // Whether the user clicked one of the buttons.
  bool action_taken_;

  // Whether the info-bar should be dismissed on the next navigation.
  bool should_expire_;

  // Used to delay the expiration of the info-bar.
  base::WeakPtrFactory<DefaultBrowserInfoBarDelegate> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate);
};

// static
void DefaultBrowserInfoBarDelegate::Create(InfoBarService* infobar_service,
                                           PrefService* prefs) {
  infobar_service->AddInfoBar(
      infobar_service->CreateConfirmInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
          new DefaultBrowserInfoBarDelegate(prefs))));
}

DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate(PrefService* prefs)
    : ConfirmInfoBarDelegate(),
      prefs_(prefs),
      action_taken_(false),
      should_expire_(false),
      weak_factory_(this) {
  // We want the info-bar to stick-around for few seconds and then be hidden
  // on the next navigation after that.
  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
      FROM_HERE, base::Bind(&DefaultBrowserInfoBarDelegate::AllowExpiry,
                            weak_factory_.GetWeakPtr()),
      base::TimeDelta::FromSeconds(8));
}

DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() {
  if (!action_taken_)
    UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.Ignored", true);
}

infobars::InfoBarDelegate::Type DefaultBrowserInfoBarDelegate::GetInfoBarType()
    const {
#if defined(OS_WIN)
  return WARNING_TYPE;
#else
  return PAGE_ACTION_TYPE;
#endif
}

int DefaultBrowserInfoBarDelegate::GetIconId() const {
  return IDR_PRODUCT_LOGO_32;
}

gfx::VectorIconId DefaultBrowserInfoBarDelegate::GetVectorIconId() const {
#if defined(OS_MACOSX) || defined(OS_ANDROID) || defined(OS_IOS)
  return gfx::VectorIconId::VECTOR_ICON_NONE;
#else
  return gfx::VectorIconId::CHROME_PRODUCT;
#endif
}

bool DefaultBrowserInfoBarDelegate::ShouldExpire(
    const NavigationDetails& details) const {
  return should_expire_ && ConfirmInfoBarDelegate::ShouldExpire(details);
}

base::string16 DefaultBrowserInfoBarDelegate::GetMessageText() const {
  return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT);
}

base::string16 DefaultBrowserInfoBarDelegate::GetButtonLabel(
    InfoBarButton button) const {
  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
      IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL :
      IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL);
}

// Setting an app as the default browser doesn't require elevation directly, but
// it does require registering it as the protocol handler for "http", so if
// protocol registration in general requires elevation, this does as well.
bool DefaultBrowserInfoBarDelegate::OKButtonTriggersUACPrompt() const {
  return ShellIntegration::IsElevationNeededForSettingDefaultProtocolClient();
}

bool DefaultBrowserInfoBarDelegate::Accept() {
  action_taken_ = true;
  scoped_refptr<ShellIntegration::DefaultBrowserWorker>(
      new ShellIntegration::DefaultBrowserWorker(new SetDefaultBrowserObserver))
      ->StartSetAsDefault();
  return true;
}

bool DefaultBrowserInfoBarDelegate::Cancel() {
  action_taken_ = true;
  UMA_HISTOGRAM_BOOLEAN("DefaultBrowserWarning.DontSetAsDefault", true);
  // User clicked "Don't ask me again", remember that.
  prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false);
  return true;
}

// A ShellIntegration::DefaultWebClientObserver that handles the check to
// determine whether or not to show the default browser prompt. If Chrome is the
// default browser, then the kCheckDefaultBrowser pref is reset.  Otherwise, the
// prompt is shown.
class CheckDefaultBrowserObserver
    : public ShellIntegration::DefaultWebClientObserver {
 public:
  CheckDefaultBrowserObserver(const base::FilePath& profile_path,
                              bool show_prompt,
                              chrome::HostDesktopType desktop_type);
  ~CheckDefaultBrowserObserver() override;

 private:
  void SetDefaultWebClientUIState(
      ShellIntegration::DefaultWebClientUIState state) override;
  bool IsOwnedByWorker() override;

  void ResetCheckDefaultBrowserPref();
  void ShowPrompt();

  // The path to the profile for which the prompt is to be shown.
  base::FilePath profile_path_;

  // True if the prompt is to be shown if Chrome is not the default browser.
  bool show_prompt_;
  chrome::HostDesktopType desktop_type_;

  DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserObserver);
};

CheckDefaultBrowserObserver::CheckDefaultBrowserObserver(
    const base::FilePath& profile_path,
    bool show_prompt,
    chrome::HostDesktopType desktop_type)
    : profile_path_(profile_path),
      show_prompt_(show_prompt),
      desktop_type_(desktop_type) {}

CheckDefaultBrowserObserver::~CheckDefaultBrowserObserver() {}

void CheckDefaultBrowserObserver::SetDefaultWebClientUIState(
    ShellIntegration::DefaultWebClientUIState state) {
  if (state == ShellIntegration::STATE_IS_DEFAULT) {
    // Notify the user in the future if Chrome ceases to be the user's chosen
    // default browser.
    ResetCheckDefaultBrowserPref();
  } else if (show_prompt_ && state == ShellIntegration::STATE_NOT_DEFAULT &&
             ShellIntegration::CanSetAsDefaultBrowser() !=
                 ShellIntegration::SET_DEFAULT_NOT_ALLOWED) {
    ShowPrompt();
  }
}

bool CheckDefaultBrowserObserver::IsOwnedByWorker() {
  // Instruct the DefaultBrowserWorker to delete this instance when it is done.
  return true;
}

void CheckDefaultBrowserObserver::ResetCheckDefaultBrowserPref() {
  Profile* profile =
      g_browser_process->profile_manager()->GetProfileByPath(profile_path_);
  if (profile)
    profile->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, true);
}

void CheckDefaultBrowserObserver::ShowPrompt() {
  Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type_);
  if (!browser)
    return;  // Reached during ui tests.

  // In ChromeBot tests, there might be a race. This line appears to get
  // called during shutdown and |tab| can be NULL.
  content::WebContents* web_contents =
      browser->tab_strip_model()->GetActiveWebContents();
  if (!web_contents)
    return;

  DefaultBrowserInfoBarDelegate::Create(
      InfoBarService::FromWebContents(web_contents),
      Profile::FromBrowserContext(web_contents->GetBrowserContext())
          ->GetPrefs());
}

}  // namespace

namespace chrome {

void RegisterDefaultBrowserPromptPrefs(PrefRegistrySimple* registry) {
  registry->RegisterStringPref(
      prefs::kBrowserSuppressDefaultBrowserPrompt, std::string());
}

void ShowDefaultBrowserPrompt(Profile* profile, HostDesktopType desktop_type) {
  // Do not check if Chrome is the default browser if there is a policy in
  // control of this setting.
  if (g_browser_process->local_state()->IsManagedPreference(
      prefs::kDefaultBrowserSettingEnabled)) {
    // Handling of the browser.default_browser_setting_enabled policy setting is
    // taken care of in BrowserProcessImpl.
    return;
  }

  // Check if Chrome is the default browser but do not prompt if:
  // - The user said "don't ask me again" on the infobar earlier.
  // - The "suppress_default_browser_prompt_for_version" master preference is
  //     set to the current version.
  bool show_prompt =
      profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser);
  if (show_prompt) {
    const std::string disable_version_string =
        g_browser_process->local_state()->GetString(
            prefs::kBrowserSuppressDefaultBrowserPrompt);
    const Version disable_version(disable_version_string);
    DCHECK(disable_version_string.empty() || disable_version.IsValid());
    if (disable_version.IsValid()) {
      if (disable_version.Equals(Version(version_info::GetVersionNumber())))
        show_prompt = false;
    }
  }

  scoped_refptr<ShellIntegration::DefaultBrowserWorker>(
      new ShellIntegration::DefaultBrowserWorker(
          new CheckDefaultBrowserObserver(profile->GetPath(), show_prompt,
                                          desktop_type)))
      ->StartCheckIsDefault();
}

#if !defined(OS_WIN)
bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) {
  return false;
}
#endif

}  // namespace chrome