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