1 // Copyright (c) 2014 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/extensions/ntp_overridden_bubble_delegate.h"
6
7 #include <memory>
8
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_web_ui.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/common/chrome_features.h"
17 #include "chrome/common/url_constants.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "components/prefs/pref_registry.h"
20 #include "components/prefs/pref_registry_simple.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/browser/extension_system.h"
23 #include "ui/base/l10n/l10n_util.h"
24
25 namespace {
26
27 // Whether existing NTP extensions have been automatically acknowledged.
28 const char kDidAcknowledgeExistingNtpExtensions[] =
29 "ack_existing_ntp_extensions";
30
31 // Whether to acknowledge existing extensions overriding the NTP for the active
32 // profile. Active on MacOS to rollout the NTP bubble without prompting for
33 // previously-installed extensions.
34 bool g_acknowledge_existing_extensions =
35 #if defined(OS_MAC)
36 true;
37 #else
38 false;
39 #endif
40
41 base::LazyInstance<std::set<std::pair<Profile*, std::string>>>::Leaky
42 g_ntp_overridden_shown = LAZY_INSTANCE_INITIALIZER;
43
44 } // namespace
45
46 namespace extensions {
47
48 const char NtpOverriddenBubbleDelegate::kNtpBubbleAcknowledged[] =
49 "ack_ntp_bubble";
50
NtpOverriddenBubbleDelegate(Profile * profile)51 NtpOverriddenBubbleDelegate::NtpOverriddenBubbleDelegate(Profile* profile)
52 : extensions::ExtensionMessageBubbleController::Delegate(profile),
53 profile_(profile) {
54 set_acknowledged_flag_pref_name(kNtpBubbleAcknowledged);
55 }
56
~NtpOverriddenBubbleDelegate()57 NtpOverriddenBubbleDelegate::~NtpOverriddenBubbleDelegate() {}
58
59 // static
RegisterPrefs(PrefRegistrySimple * registry)60 void NtpOverriddenBubbleDelegate::RegisterPrefs(PrefRegistrySimple* registry) {
61 registry->RegisterBooleanPref(kDidAcknowledgeExistingNtpExtensions, false,
62 PrefRegistry::NO_REGISTRATION_FLAGS);
63 }
64
65 // static
MaybeAcknowledgeExistingNtpExtensions(Profile * profile)66 void NtpOverriddenBubbleDelegate::MaybeAcknowledgeExistingNtpExtensions(
67 Profile* profile) {
68 if (!g_acknowledge_existing_extensions)
69 return;
70
71 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
72 PrefService* profile_prefs = profile->GetPrefs();
73 // Only acknowledge existing extensions once per profile.
74 if (profile_prefs->GetBoolean(kDidAcknowledgeExistingNtpExtensions))
75 return;
76
77 profile_prefs->SetBoolean(kDidAcknowledgeExistingNtpExtensions, true);
78 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile);
79 for (const auto& extension : registry->enabled_extensions()) {
80 const URLOverrides::URLOverrideMap& overrides =
81 URLOverrides::GetChromeURLOverrides(extension.get());
82 if (overrides.find(chrome::kChromeUINewTabHost) != overrides.end()) {
83 prefs->UpdateExtensionPref(extension->id(), kNtpBubbleAcknowledged,
84 std::make_unique<base::Value>(true));
85 }
86 }
87 }
88
ShouldIncludeExtension(const extensions::Extension * extension)89 bool NtpOverriddenBubbleDelegate::ShouldIncludeExtension(
90 const extensions::Extension* extension) {
91 if (!extension_id_.empty() && extension_id_ != extension->id())
92 return false;
93
94 GURL url(chrome::kChromeUINewTabURL);
95 if (!ExtensionWebUI::HandleChromeURLOverride(&url, profile()))
96 return false; // No override for newtab found.
97
98 if (extension->id() != url.host_piece())
99 return false;
100
101 if (HasBubbleInfoBeenAcknowledged(extension->id()))
102 return false;
103
104 extension_id_ = extension->id();
105 return true;
106 }
107
AcknowledgeExtension(const std::string & extension_id,ExtensionMessageBubbleController::BubbleAction user_action)108 void NtpOverriddenBubbleDelegate::AcknowledgeExtension(
109 const std::string& extension_id,
110 ExtensionMessageBubbleController::BubbleAction user_action) {
111 if (user_action != ExtensionMessageBubbleController::ACTION_EXECUTE)
112 SetBubbleInfoBeenAcknowledged(extension_id, true);
113 }
114
PerformAction(const extensions::ExtensionIdList & list)115 void NtpOverriddenBubbleDelegate::PerformAction(
116 const extensions::ExtensionIdList& list) {
117 for (size_t i = 0; i < list.size(); ++i) {
118 service()->DisableExtension(
119 list[i], extensions::disable_reason::DISABLE_USER_ACTION);
120 }
121 }
122
GetTitle() const123 base::string16 NtpOverriddenBubbleDelegate::GetTitle() const {
124 return l10n_util::GetStringUTF16(
125 IDS_EXTENSIONS_NTP_CONTROLLED_TITLE_HOME_PAGE_BUBBLE);
126 }
127
GetMessageBody(bool anchored_to_browser_action,int extension_count) const128 base::string16 NtpOverriddenBubbleDelegate::GetMessageBody(
129 bool anchored_to_browser_action,
130 int extension_count) const {
131 base::string16 body =
132 l10n_util::GetStringUTF16(IDS_EXTENSIONS_NTP_CONTROLLED_FIRST_LINE);
133 body += l10n_util::GetStringUTF16(
134 IDS_EXTENSIONS_SETTINGS_API_THIRD_LINE_CONFIRMATION);
135 return body;
136 }
137
GetOverflowText(const base::string16 & overflow_count) const138 base::string16 NtpOverriddenBubbleDelegate::GetOverflowText(
139 const base::string16& overflow_count) const {
140 // Does not have more than one extension in the list at a time.
141 NOTREACHED();
142 return base::string16();
143 }
144
GetLearnMoreUrl() const145 GURL NtpOverriddenBubbleDelegate::GetLearnMoreUrl() const {
146 return GURL(chrome::kExtensionControlledSettingLearnMoreURL);
147 }
148
GetActionButtonLabel() const149 base::string16 NtpOverriddenBubbleDelegate::GetActionButtonLabel() const {
150 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_RESTORE_SETTINGS);
151 }
152
GetDismissButtonLabel() const153 base::string16 NtpOverriddenBubbleDelegate::GetDismissButtonLabel() const {
154 return l10n_util::GetStringUTF16(IDS_EXTENSION_CONTROLLED_KEEP_CHANGES);
155 }
156
ShouldCloseOnDeactivate() const157 bool NtpOverriddenBubbleDelegate::ShouldCloseOnDeactivate() const {
158 return true;
159 }
160
ShouldAcknowledgeOnDeactivate() const161 bool NtpOverriddenBubbleDelegate::ShouldAcknowledgeOnDeactivate() const {
162 return base::FeatureList::IsEnabled(
163 ::features::kAcknowledgeNtpOverrideOnDeactivate);
164 }
165
ShouldShowExtensionList() const166 bool NtpOverriddenBubbleDelegate::ShouldShowExtensionList() const {
167 return false;
168 }
169
ShouldHighlightExtensions() const170 bool NtpOverriddenBubbleDelegate::ShouldHighlightExtensions() const {
171 return false;
172 }
173
ShouldLimitToEnabledExtensions() const174 bool NtpOverriddenBubbleDelegate::ShouldLimitToEnabledExtensions() const {
175 return true;
176 }
177
ShouldShow(const ExtensionIdList & extensions) const178 bool NtpOverriddenBubbleDelegate::ShouldShow(
179 const ExtensionIdList& extensions) const {
180 DCHECK_EQ(1u, extensions.size());
181 return !g_ntp_overridden_shown.Get().count(
182 std::make_pair(profile_, extensions[0]));
183 }
184
OnShown(const ExtensionIdList & extensions)185 void NtpOverriddenBubbleDelegate::OnShown(const ExtensionIdList& extensions) {
186 DCHECK_EQ(1u, extensions.size());
187 DCHECK(!g_ntp_overridden_shown.Get().count(
188 std::make_pair(profile_, extensions[0])));
189 g_ntp_overridden_shown.Get().insert(std::make_pair(profile_, extensions[0]));
190 }
191
OnAction()192 void NtpOverriddenBubbleDelegate::OnAction() {
193 // We clear the profile set because the user chooses to remove or disable the
194 // extension. Thus if that extension or another takes effect, it is worth
195 // mentioning to the user (ShouldShow() would return true) because it is
196 // contrary to the user's choice.
197 g_ntp_overridden_shown.Get().clear();
198 }
199
ClearProfileSetForTesting()200 void NtpOverriddenBubbleDelegate::ClearProfileSetForTesting() {
201 g_ntp_overridden_shown.Get().clear();
202 }
203
LogExtensionCount(size_t count)204 void NtpOverriddenBubbleDelegate::LogExtensionCount(size_t count) {
205 }
206
LogAction(ExtensionMessageBubbleController::BubbleAction action)207 void NtpOverriddenBubbleDelegate::LogAction(
208 ExtensionMessageBubbleController::BubbleAction action) {
209 UMA_HISTOGRAM_ENUMERATION(
210 "ExtensionOverrideBubble.NtpOverriddenUserSelection",
211 action,
212 ExtensionMessageBubbleController::ACTION_BOUNDARY);
213 }
214
SupportsPolicyIndicator()215 bool NtpOverriddenBubbleDelegate::SupportsPolicyIndicator() {
216 return true;
217 }
218
219 void NtpOverriddenBubbleDelegate::
set_acknowledge_existing_extensions_for_testing(bool acknowledge_existing_extensions)220 set_acknowledge_existing_extensions_for_testing(
221 bool acknowledge_existing_extensions) {
222 g_acknowledge_existing_extensions = acknowledge_existing_extensions;
223 }
224
225 } // namespace extensions
226