blob: 4249abe3e33dbe71ade4a6aa1be4470a2371a629 [file] [log] [blame]
// 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/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics_action.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/version.h"
#include "build/build_config.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/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/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/user_metrics.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 {
// 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:
// Possible user interactions with the default browser info bar.
// Do not modify the ordering as it is important for UMA.
enum InfoBarUserInteraction {
// The user clicked the "Set as default" button.
START_SET_AS_DEFAULT,
// The user doesn't want to be reminded again.
DONT_ASK_AGAIN,
// The user did not interact with the info bar.
IGNORE_INFO_BAR,
NUM_INFO_BAR_USER_INTERACTION_TYPES
};
explicit DefaultBrowserInfoBarDelegate(PrefService* prefs);
~DefaultBrowserInfoBarDelegate() override;
void AllowExpiry() { should_expire_ = true; }
// ConfirmInfoBarDelegate:
Type GetInfoBarType() const override;
infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() 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_ENUMERATION("DefaultBrowser.InfoBar.UserInteraction",
InfoBarUserInteraction::IGNORE_INFO_BAR,
NUM_INFO_BAR_USER_INTERACTION_TYPES);
}
infobars::InfoBarDelegate::Type DefaultBrowserInfoBarDelegate::GetInfoBarType()
const {
#if defined(OS_WIN)
return WARNING_TYPE;
#else
return PAGE_ACTION_TYPE;
#endif
}
infobars::InfoBarDelegate::InfoBarIdentifier
DefaultBrowserInfoBarDelegate::GetIdentifier() const {
return DEFAULT_BROWSER_INFOBAR_DELEGATE;
}
int DefaultBrowserInfoBarDelegate::GetIconId() const {
return IDR_PRODUCT_LOGO_32;
}
gfx::VectorIconId DefaultBrowserInfoBarDelegate::GetVectorIconId() const {
#if defined(OS_MACOSX) || defined(OS_ANDROID)
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 shell_integration::IsElevationNeededForSettingDefaultProtocolClient();
}
bool DefaultBrowserInfoBarDelegate::Accept() {
action_taken_ = true;
content::RecordAction(
base::UserMetricsAction("DefaultBrowserInfoBar_Accept"));
UMA_HISTOGRAM_ENUMERATION("DefaultBrowser.InfoBar.UserInteraction",
InfoBarUserInteraction::START_SET_AS_DEFAULT,
NUM_INFO_BAR_USER_INTERACTION_TYPES);
// The worker pointer is reference counted. While it is running, the
// message loops of the FILE and UI thread will hold references to it
// and it will be automatically freed once all its tasks have finished.
scoped_refptr<shell_integration::DefaultBrowserWorker>(
new shell_integration::DefaultBrowserWorker(nullptr,
/*delete_observer=*/false))
->StartSetAsDefault();
return true;
}
bool DefaultBrowserInfoBarDelegate::Cancel() {
action_taken_ = true;
content::RecordAction(
base::UserMetricsAction("DefaultBrowserInfoBar_DontAskAgain"));
UMA_HISTOGRAM_ENUMERATION("DefaultBrowser.InfoBar.UserInteraction",
InfoBarUserInteraction::DONT_ASK_AGAIN,
NUM_INFO_BAR_USER_INTERACTION_TYPES);
// User clicked "Don't ask me again", remember that.
prefs_->SetBoolean(prefs::kCheckDefaultBrowser, false);
return true;
}
// A shell_integration::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 shell_integration::DefaultWebClientObserver {
public:
CheckDefaultBrowserObserver(const base::FilePath& profile_path,
bool show_prompt);
~CheckDefaultBrowserObserver() override;
private:
void SetDefaultWebClientUIState(
shell_integration::DefaultWebClientUIState state) 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_;
DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserObserver);
};
CheckDefaultBrowserObserver::CheckDefaultBrowserObserver(
const base::FilePath& profile_path,
bool show_prompt)
: profile_path_(profile_path), show_prompt_(show_prompt) {}
CheckDefaultBrowserObserver::~CheckDefaultBrowserObserver() {}
void CheckDefaultBrowserObserver::SetDefaultWebClientUIState(
shell_integration::DefaultWebClientUIState state) {
if (state == shell_integration::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 == shell_integration::STATE_NOT_DEFAULT &&
shell_integration::CanSetAsDefaultBrowser() !=
shell_integration::SET_DEFAULT_NOT_ALLOWED) {
ShowPrompt();
}
}
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::FindLastActive();
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) {
// 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;
}
PrefService* prefs = profile->GetPrefs();
// Reset preferences if kResetCheckDefaultBrowser is true.
if (prefs->GetBoolean(prefs::kResetCheckDefaultBrowser)) {
prefs->SetBoolean(prefs::kResetCheckDefaultBrowser, false);
prefs->SetBoolean(prefs::kCheckDefaultBrowser, true);
}
// 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 = prefs->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() &&
disable_version == Version(version_info::GetVersionNumber())) {
show_prompt = false;
}
}
scoped_refptr<shell_integration::DefaultBrowserWorker>(
new shell_integration::DefaultBrowserWorker(
new CheckDefaultBrowserObserver(profile->GetPath(), show_prompt),
/*delete_observer=*/true))
->StartCheckIsDefault();
}
#if !defined(OS_WIN)
bool ShowFirstRunDefaultBrowserPrompt(Profile* profile) {
return false;
}
#endif
} // namespace chrome