| // Copyright (c) 2014 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/extensions/ntp_overridden_bubble_delegate.h" |
| |
| #include <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_web_ui.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/prefs/pref_registry.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace { |
| |
| // Whether the user has been notified about extension overriding the new tab |
| // page. |
| const char kNtpBubbleAcknowledged[] = "ack_ntp_bubble"; |
| |
| // Whether existing NTP extensions have been automatically acknowledged. |
| const char kDidAcknowledgeExistingNtpExtensions[] = |
| "ack_existing_ntp_extensions"; |
| |
| // Whether to acknowledge existing extensions overriding the NTP for the active |
| // profile. Active on ChromeOS to rollout the NTP bubble without prompting for |
| // previously-installed extensions. |
| bool g_acknowledge_existing_extensions = |
| #if defined(OS_CHROMEOS) |
| true; |
| #else |
| false; |
| #endif |
| |
| base::LazyInstance<std::set<std::pair<Profile*, std::string>>>::Leaky |
| g_ntp_overridden_shown = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| NtpOverriddenBubbleDelegate::NtpOverriddenBubbleDelegate(Profile* profile) |
| : extensions::ExtensionMessageBubbleController::Delegate(profile), |
| profile_(profile) { |
| set_acknowledged_flag_pref_name(kNtpBubbleAcknowledged); |
| } |
| |
| NtpOverriddenBubbleDelegate::~NtpOverriddenBubbleDelegate() {} |
| |
| // static |
| void NtpOverriddenBubbleDelegate::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterBooleanPref(kDidAcknowledgeExistingNtpExtensions, false, |
| PrefRegistry::NO_REGISTRATION_FLAGS); |
| } |
| |
| // static |
| void NtpOverriddenBubbleDelegate::MaybeAcknowledgeExistingNtpExtensions( |
| Profile* profile) { |
| if (!g_acknowledge_existing_extensions) |
| return; |
| |
| ExtensionRegistry* registry = ExtensionRegistry::Get(profile); |
| PrefService* profile_prefs = profile->GetPrefs(); |
| // Only acknowledge existing extensions once per profile. |
| if (profile_prefs->GetBoolean(kDidAcknowledgeExistingNtpExtensions)) |
| return; |
| |
| profile_prefs->SetBoolean(kDidAcknowledgeExistingNtpExtensions, true); |
| ExtensionPrefs* prefs = ExtensionPrefs::Get(profile); |
| for (const auto& extension : registry->enabled_extensions()) { |
| const URLOverrides::URLOverrideMap& overrides = |
| URLOverrides::GetChromeURLOverrides(extension.get()); |
| if (overrides.find(chrome::kChromeUINewTabHost) != overrides.end()) { |
| prefs->UpdateExtensionPref(extension->id(), kNtpBubbleAcknowledged, |
| std::make_unique<base::Value>(true)); |
| } |
| } |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldIncludeExtension( |
| const extensions::Extension* extension) { |
| if (!extension_id_.empty() && extension_id_ != extension->id()) |
| return false; |
| |
| GURL url(chrome::kChromeUINewTabURL); |
| if (!ExtensionWebUI::HandleChromeURLOverride(&url, profile())) |
| return false; // No override for newtab found. |
| |
| if (extension->id() != url.host_piece()) |
| return false; |
| |
| if (HasBubbleInfoBeenAcknowledged(extension->id())) |
| return false; |
| |
| extension_id_ = extension->id(); |
| return true; |
| } |
| |
| void NtpOverriddenBubbleDelegate::AcknowledgeExtension( |
| const std::string& extension_id, |
| ExtensionMessageBubbleController::BubbleAction user_action) { |
| if (user_action != ExtensionMessageBubbleController::ACTION_EXECUTE) |
| SetBubbleInfoBeenAcknowledged(extension_id, true); |
| } |
| |
| void NtpOverriddenBubbleDelegate::PerformAction( |
| const extensions::ExtensionIdList& list) { |
| for (size_t i = 0; i < list.size(); ++i) { |
| service()->DisableExtension( |
| list[i], extensions::disable_reason::DISABLE_USER_ACTION); |
| } |
| } |
| |
| base::string16 NtpOverriddenBubbleDelegate::GetTitle() const { |
| return l10n_util::GetStringUTF16( |
| IDS_EXTENSIONS_NTP_CONTROLLED_TITLE_HOME_PAGE_BUBBLE); |
| } |
| |
| base::string16 NtpOverriddenBubbleDelegate::GetMessageBody( |
| bool anchored_to_browser_action, |
| int extension_count) const { |
| base::string16 body = |
| l10n_util::GetStringUTF16(IDS_EXTENSIONS_NTP_CONTROLLED_FIRST_LINE); |
| body += l10n_util::GetStringUTF16( |
| IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION); |
| return body; |
| } |
| |
| base::string16 NtpOverriddenBubbleDelegate::GetOverflowText( |
| const base::string16& overflow_count) const { |
| // Does not have more than one extension in the list at a time. |
| NOTREACHED(); |
| return base::string16(); |
| } |
| |
| GURL NtpOverriddenBubbleDelegate::GetLearnMoreUrl() const { |
| return GURL(chrome::kExtensionControlledSettingLearnMoreURL); |
| } |
| |
| base::string16 NtpOverriddenBubbleDelegate::GetActionButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS); |
| } |
| |
| base::string16 NtpOverriddenBubbleDelegate::GetDismissButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES); |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldCloseOnDeactivate() const { |
| return true; |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldAcknowledgeOnDeactivate() const { |
| return base::FeatureList::IsEnabled( |
| ::features::kAcknowledgeNtpOverrideOnDeactivate); |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldShowExtensionList() const { |
| return false; |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldHighlightExtensions() const { |
| return false; |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldLimitToEnabledExtensions() const { |
| return true; |
| } |
| |
| bool NtpOverriddenBubbleDelegate::ShouldShow( |
| const ExtensionIdList& extensions) const { |
| DCHECK_EQ(1u, extensions.size()); |
| return !g_ntp_overridden_shown.Get().count( |
| std::make_pair(profile_, extensions[0])); |
| } |
| |
| void NtpOverriddenBubbleDelegate::OnShown(const ExtensionIdList& extensions) { |
| DCHECK_EQ(1u, extensions.size()); |
| DCHECK(!g_ntp_overridden_shown.Get().count( |
| std::make_pair(profile_, extensions[0]))); |
| g_ntp_overridden_shown.Get().insert(std::make_pair(profile_, extensions[0])); |
| } |
| |
| void NtpOverriddenBubbleDelegate::OnAction() { |
| // We clear the profile set because the user chooses to remove or disable the |
| // extension. Thus if that extension or another takes effect, it is worth |
| // mentioning to the user (ShouldShow() would return true) because it is |
| // contrary to the user's choice. |
| g_ntp_overridden_shown.Get().clear(); |
| } |
| |
| void NtpOverriddenBubbleDelegate::ClearProfileSetForTesting() { |
| g_ntp_overridden_shown.Get().clear(); |
| } |
| |
| void NtpOverriddenBubbleDelegate::LogExtensionCount(size_t count) { |
| } |
| |
| void NtpOverriddenBubbleDelegate::LogAction( |
| ExtensionMessageBubbleController::BubbleAction action) { |
| UMA_HISTOGRAM_ENUMERATION( |
| "ExtensionOverrideBubble.NtpOverriddenUserSelection", |
| action, |
| ExtensionMessageBubbleController::ACTION_BOUNDARY); |
| } |
| |
| bool NtpOverriddenBubbleDelegate::SupportsPolicyIndicator() { |
| return true; |
| } |
| |
| void NtpOverriddenBubbleDelegate:: |
| set_acknowledge_existing_extensions_for_testing( |
| bool acknowledge_existing_extensions) { |
| g_acknowledge_existing_extensions = acknowledge_existing_extensions; |
| } |
| |
| } // namespace extensions |