1 // Copyright 2013 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.
5 #include "chrome/browser/extensions/error_console/error_console.h"
10 #include "base/bind_helpers.h"
11 #include "base/lazy_instance.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/error_console/error_console_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/features/feature_channel.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/crx_file/id_util.h"
21 #include "components/version_info/version_info.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/common/constants.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_set.h"
31 #include "extensions/common/feature_switch.h"
33 namespace extensions
{
37 // The key into the Extension prefs for an Extension's specific reporting
39 const char kStoreExtensionErrorsPref
[] = "store_extension_errors";
41 // This is the default mask for which errors to report. That is, if an extension
42 // does not have specific preference set, this will be used instead.
43 const int kDefaultMask
= 0;
45 const char kAppsDeveloperToolsExtensionId
[] =
46 "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
50 void ErrorConsole::Observer::OnErrorAdded(const ExtensionError
* error
) {
53 void ErrorConsole::Observer::OnErrorsRemoved(
54 const std::set
<std::string
>& extension_ids
) {
57 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
60 ErrorConsole::ErrorConsole(Profile
* profile
)
62 default_mask_(kDefaultMask
),
65 registry_observer_(this) {
66 pref_registrar_
.Init(profile_
->GetPrefs());
67 pref_registrar_
.Add(prefs::kExtensionsUIDeveloperMode
,
68 base::Bind(&ErrorConsole::OnPrefChanged
,
69 base::Unretained(this)));
71 registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
76 ErrorConsole::~ErrorConsole() {
77 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorConsoleDestroyed());
81 ErrorConsole
* ErrorConsole::Get(content::BrowserContext
* browser_context
) {
82 return ErrorConsoleFactory::GetForBrowserContext(browser_context
);
85 void ErrorConsole::SetReportingForExtension(const std::string
& extension_id
,
86 ExtensionError::Type type
,
88 DCHECK(thread_checker_
.CalledOnValidThread());
89 if (!enabled_
|| !crx_file::id_util::IdIsValid(extension_id
))
92 int mask
= default_mask_
;
93 // This call can fail if the preference isn't set, but we don't really care
94 // if it does, because we just use the default mask instead.
95 prefs_
->ReadPrefAsInteger(extension_id
, kStoreExtensionErrorsPref
, &mask
);
100 mask
&= ~(1 << type
);
102 prefs_
->UpdateExtensionPref(extension_id
,
103 kStoreExtensionErrorsPref
,
104 new base::FundamentalValue(mask
));
107 void ErrorConsole::SetReportingAllForExtension(
108 const std::string
& extension_id
, bool enabled
) {
109 DCHECK(thread_checker_
.CalledOnValidThread());
110 if (!enabled_
|| !crx_file::id_util::IdIsValid(extension_id
))
113 int mask
= enabled
? (1 << ExtensionError::NUM_ERROR_TYPES
) - 1 : 0;
115 prefs_
->UpdateExtensionPref(extension_id
,
116 kStoreExtensionErrorsPref
,
117 new base::FundamentalValue(mask
));
120 bool ErrorConsole::IsReportingEnabledForExtension(
121 const std::string
& extension_id
) const {
122 DCHECK(thread_checker_
.CalledOnValidThread());
123 if (!enabled_
|| !crx_file::id_util::IdIsValid(extension_id
))
126 return GetMaskForExtension(extension_id
) != 0;
129 void ErrorConsole::UseDefaultReportingForExtension(
130 const std::string
& extension_id
) {
131 DCHECK(thread_checker_
.CalledOnValidThread());
132 if (!enabled_
|| !crx_file::id_util::IdIsValid(extension_id
))
135 prefs_
->UpdateExtensionPref(extension_id
, kStoreExtensionErrorsPref
, NULL
);
138 void ErrorConsole::ReportError(scoped_ptr
<ExtensionError
> error
) {
139 DCHECK(thread_checker_
.CalledOnValidThread());
140 if (!enabled_
|| !crx_file::id_util::IdIsValid(error
->extension_id()))
143 int mask
= GetMaskForExtension(error
->extension_id());
144 if (!(mask
& (1 << error
->type())))
147 const ExtensionError
* weak_error
= errors_
.AddError(error
.Pass());
148 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorAdded(weak_error
));
151 void ErrorConsole::RemoveErrors(const ErrorMap::Filter
& filter
) {
152 std::set
<std::string
> affected_ids
;
153 errors_
.RemoveErrors(filter
, &affected_ids
);
154 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorsRemoved(affected_ids
));
157 const ErrorList
& ErrorConsole::GetErrorsForExtension(
158 const std::string
& extension_id
) const {
159 return errors_
.GetErrorsForExtension(extension_id
);
162 void ErrorConsole::AddObserver(Observer
* observer
) {
163 DCHECK(thread_checker_
.CalledOnValidThread());
164 observers_
.AddObserver(observer
);
167 void ErrorConsole::RemoveObserver(Observer
* observer
) {
168 DCHECK(thread_checker_
.CalledOnValidThread());
169 observers_
.RemoveObserver(observer
);
172 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
173 if (!profile_
->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode
))
174 return false; // Only enabled in developer mode.
175 if (GetCurrentChannel() > version_info::Channel::DEV
&&
176 !FeatureSwitch::error_console()->IsEnabled())
177 return false; // Restricted to dev channel or opt-in.
182 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
183 return ExtensionRegistry::Get(profile_
)->enabled_extensions()
184 .Contains(kAppsDeveloperToolsExtensionId
);
187 void ErrorConsole::CheckEnabled() {
188 bool should_be_enabled
= IsEnabledForChromeExtensionsPage() ||
189 IsEnabledForAppsDeveloperTools();
190 if (should_be_enabled
&& !enabled_
)
192 if (!should_be_enabled
&& enabled_
)
196 void ErrorConsole::Enable() {
199 // We postpone the initialization of |prefs_| until now because they can be
200 // NULL in unit_tests. Any unit tests that enable the error console should
201 // also create an ExtensionPrefs object.
202 prefs_
= ExtensionPrefs::Get(profile_
);
204 notification_registrar_
.Add(
206 chrome::NOTIFICATION_PROFILE_DESTROYED
,
207 content::NotificationService::AllBrowserContextsAndSources());
209 const ExtensionSet
& extensions
=
210 ExtensionRegistry::Get(profile_
)->enabled_extensions();
211 for (ExtensionSet::const_iterator iter
= extensions
.begin();
212 iter
!= extensions
.end();
214 AddManifestErrorsForExtension(iter
->get());
218 void ErrorConsole::Disable() {
219 notification_registrar_
.RemoveAll();
220 errors_
.RemoveAllErrors();
224 void ErrorConsole::OnPrefChanged() {
228 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext
* browser_context
,
229 const Extension
* extension
,
230 UnloadedExtensionInfo::Reason reason
) {
234 void ErrorConsole::OnExtensionLoaded(content::BrowserContext
* browser_context
,
235 const Extension
* extension
) {
239 void ErrorConsole::OnExtensionInstalled(
240 content::BrowserContext
* browser_context
,
241 const Extension
* extension
,
243 // We don't want to have manifest errors from previous installs. We want
244 // to keep runtime errors, though, because extensions are reloaded on a
245 // refresh of chrome:extensions, and we don't want to wipe our history
246 // whenever that happens.
247 errors_
.RemoveErrors(ErrorMap::Filter::ErrorsForExtensionWithType(
248 extension
->id(), ExtensionError::MANIFEST_ERROR
), nullptr);
249 AddManifestErrorsForExtension(extension
);
252 void ErrorConsole::OnExtensionUninstalled(
253 content::BrowserContext
* browser_context
,
254 const Extension
* extension
,
255 extensions::UninstallReason reason
) {
256 errors_
.RemoveErrors(ErrorMap::Filter::ErrorsForExtension(extension
->id()),
260 void ErrorConsole::AddManifestErrorsForExtension(const Extension
* extension
) {
261 const std::vector
<InstallWarning
>& warnings
=
262 extension
->install_warnings();
263 for (std::vector
<InstallWarning
>::const_iterator iter
= warnings
.begin();
264 iter
!= warnings
.end(); ++iter
) {
265 ReportError(scoped_ptr
<ExtensionError
>(new ManifestError(
267 base::UTF8ToUTF16(iter
->message
),
268 base::UTF8ToUTF16(iter
->key
),
269 base::UTF8ToUTF16(iter
->specific
))));
273 void ErrorConsole::Observe(int type
,
274 const content::NotificationSource
& source
,
275 const content::NotificationDetails
& details
) {
276 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED
, type
);
277 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
278 // If incognito profile which we are associated with is destroyed, also
279 // destroy all incognito errors.
280 if (profile
->IsOffTheRecord() && profile_
->IsSameProfile(profile
))
281 errors_
.RemoveErrors(ErrorMap::Filter::IncognitoErrors(), nullptr);
284 int ErrorConsole::GetMaskForExtension(const std::string
& extension_id
) const {
285 // Registered preferences take priority over everything else.
287 if (prefs_
->ReadPrefAsInteger(extension_id
, kStoreExtensionErrorsPref
, &pref
))
290 // If the extension is unpacked, we report all error types by default.
291 const Extension
* extension
=
292 ExtensionRegistry::Get(profile_
)->GetExtensionById(
293 extension_id
, ExtensionRegistry::EVERYTHING
);
294 if (extension
&& extension
->location() == Manifest::UNPACKED
)
295 return (1 << ExtensionError::NUM_ERROR_TYPES
) - 1;
297 // Otherwise, use the default mask.
298 return default_mask_
;
301 } // namespace extensions