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.h"
11 #include "base/bind_helpers.h"
12 #include "base/lazy_instance.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/stl_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/extensions/features/feature_channel.h"
21 #include "chrome/common/pref_names.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_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/common/constants.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_set.h"
30 #include "extensions/common/feature_switch.h"
32 namespace extensions
{
36 // The key into the Extension prefs for an Extension's specific reporting
38 const char kStoreExtensionErrorsPref
[] = "store_extension_errors";
40 // This is the default mask for which errors to report. That is, if an extension
41 // does not have specific preference set, this will be used instead.
42 const int kDefaultMask
= 0;
44 const char kAppsDeveloperToolsExtensionId
[] =
45 "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
49 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
52 ErrorConsole::ErrorConsole(Profile
* profile
)
54 default_mask_(kDefaultMask
),
57 registry_observer_(this) {
58 // TODO(rdevlin.cronin): Remove once crbug.com/159265 is fixed.
59 #if !defined(ENABLE_EXTENSIONS)
63 pref_registrar_
.Init(profile_
->GetPrefs());
64 pref_registrar_
.Add(prefs::kExtensionsUIDeveloperMode
,
65 base::Bind(&ErrorConsole::OnPrefChanged
,
66 base::Unretained(this)));
68 registry_observer_
.Add(ExtensionRegistry::Get(profile_
));
73 ErrorConsole::~ErrorConsole() {
74 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorConsoleDestroyed());
78 ErrorConsole
* ErrorConsole::Get(Profile
* profile
) {
79 return ExtensionSystem::Get(profile
)->error_console();
82 void ErrorConsole::SetReportingForExtension(const std::string
& extension_id
,
83 ExtensionError::Type type
,
85 DCHECK(thread_checker_
.CalledOnValidThread());
86 if (!enabled_
|| !Extension::IdIsValid(extension_id
))
89 int mask
= default_mask_
;
90 // This call can fail if the preference isn't set, but we don't really care
91 // if it does, because we just use the default mask instead.
92 prefs_
->ReadPrefAsInteger(extension_id
, kStoreExtensionErrorsPref
, &mask
);
99 prefs_
->UpdateExtensionPref(extension_id
,
100 kStoreExtensionErrorsPref
,
101 base::Value::CreateIntegerValue(mask
));
104 void ErrorConsole::SetReportingAllForExtension(
105 const std::string
& extension_id
, bool enabled
) {
106 DCHECK(thread_checker_
.CalledOnValidThread());
107 if (!enabled_
|| !Extension::IdIsValid(extension_id
))
112 mask
= (1 << ExtensionError::NUM_ERROR_TYPES
) - 1;
114 prefs_
->UpdateExtensionPref(extension_id
,
115 kStoreExtensionErrorsPref
,
116 base::Value::CreateIntegerValue(mask
));
119 bool ErrorConsole::IsReportingEnabledForExtension(
120 const std::string
& extension_id
) const {
121 DCHECK(thread_checker_
.CalledOnValidThread());
122 if (!enabled_
|| !Extension::IdIsValid(extension_id
))
125 return GetMaskForExtension(extension_id
) != 0;
128 void ErrorConsole::UseDefaultReportingForExtension(
129 const std::string
& extension_id
) {
130 DCHECK(thread_checker_
.CalledOnValidThread());
131 if (!enabled_
|| !Extension::IdIsValid(extension_id
))
134 prefs_
->UpdateExtensionPref(extension_id
, kStoreExtensionErrorsPref
, NULL
);
137 void ErrorConsole::ReportError(scoped_ptr
<ExtensionError
> error
) {
138 DCHECK(thread_checker_
.CalledOnValidThread());
139 if (!enabled_
|| !Extension::IdIsValid(error
->extension_id()))
142 int mask
= GetMaskForExtension(error
->extension_id());
143 if (!(mask
& (1 << error
->type())))
146 const ExtensionError
* weak_error
= errors_
.AddError(error
.Pass());
147 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorAdded(weak_error
));
150 const ErrorList
& ErrorConsole::GetErrorsForExtension(
151 const std::string
& extension_id
) const {
152 return errors_
.GetErrorsForExtension(extension_id
);
155 void ErrorConsole::AddObserver(Observer
* observer
) {
156 DCHECK(thread_checker_
.CalledOnValidThread());
157 observers_
.AddObserver(observer
);
160 void ErrorConsole::RemoveObserver(Observer
* observer
) {
161 DCHECK(thread_checker_
.CalledOnValidThread());
162 observers_
.RemoveObserver(observer
);
165 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
166 return profile_
->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode
) &&
167 (FeatureSwitch::error_console()->IsEnabled() ||
168 GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV
);
171 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
172 return ExtensionRegistry::Get(profile_
)->enabled_extensions()
173 .Contains(kAppsDeveloperToolsExtensionId
);
176 void ErrorConsole::CheckEnabled() {
177 bool should_be_enabled
= IsEnabledForChromeExtensionsPage() ||
178 IsEnabledForAppsDeveloperTools();
179 if (should_be_enabled
&& !enabled_
)
181 if (!should_be_enabled
&& enabled_
)
185 void ErrorConsole::Enable() {
188 // We postpone the initialization of |prefs_| until now because they can be
189 // NULL in unit_tests. Any unit tests that enable the error console should
190 // also create an ExtensionPrefs object.
191 prefs_
= ExtensionPrefs::Get(profile_
);
193 notification_registrar_
.Add(
195 chrome::NOTIFICATION_PROFILE_DESTROYED
,
196 content::NotificationService::AllBrowserContextsAndSources());
197 notification_registrar_
.Add(
199 chrome::NOTIFICATION_EXTENSION_UNINSTALLED
,
200 content::Source
<Profile
>(profile_
));
201 notification_registrar_
.Add(
203 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
204 content::Source
<Profile
>(profile_
));
206 const ExtensionSet
& extensions
=
207 ExtensionRegistry::Get(profile_
)->enabled_extensions();
208 for (ExtensionSet::const_iterator iter
= extensions
.begin();
209 iter
!= extensions
.end();
211 AddManifestErrorsForExtension(iter
->get());
215 void ErrorConsole::Disable() {
216 notification_registrar_
.RemoveAll();
217 errors_
.RemoveAllErrors();
221 void ErrorConsole::OnPrefChanged() {
225 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext
* browser_context
,
226 const Extension
* extension
,
227 UnloadedExtensionInfo::Reason reason
) {
231 void ErrorConsole::OnExtensionLoaded(content::BrowserContext
* browser_context
,
232 const Extension
* extension
) {
236 void ErrorConsole::AddManifestErrorsForExtension(const Extension
* extension
) {
237 const std::vector
<InstallWarning
>& warnings
=
238 extension
->install_warnings();
239 for (std::vector
<InstallWarning
>::const_iterator iter
= warnings
.begin();
240 iter
!= warnings
.end(); ++iter
) {
241 ReportError(scoped_ptr
<ExtensionError
>(new ManifestError(
243 base::UTF8ToUTF16(iter
->message
),
244 base::UTF8ToUTF16(iter
->key
),
245 base::UTF8ToUTF16(iter
->specific
))));
249 void ErrorConsole::Observe(int type
,
250 const content::NotificationSource
& source
,
251 const content::NotificationDetails
& details
) {
253 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
254 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
255 // If incognito profile which we are associated with is destroyed, also
256 // destroy all incognito errors.
257 if (profile
->IsOffTheRecord() && profile_
->IsSameProfile(profile
))
258 errors_
.RemoveIncognitoErrors();
261 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED
:
262 // No need to check the profile here, since we registered to only receive
263 // notifications from our own.
264 errors_
.Remove(content::Details
<Extension
>(details
).ptr()->id());
266 case chrome::NOTIFICATION_EXTENSION_INSTALLED
: {
267 const InstalledExtensionInfo
* info
=
268 content::Details
<InstalledExtensionInfo
>(details
).ptr();
270 // We don't want to have manifest errors from previous installs. We want
271 // to keep runtime errors, though, because extensions are reloaded on a
272 // refresh of chrome:extensions, and we don't want to wipe our history
273 // whenever that happens.
274 errors_
.RemoveErrorsForExtensionOfType(info
->extension
->id(),
275 ExtensionError::MANIFEST_ERROR
);
277 AddManifestErrorsForExtension(info
->extension
);
285 int ErrorConsole::GetMaskForExtension(const std::string
& extension_id
) const {
286 // Registered preferences take priority over everything else.
288 if (prefs_
->ReadPrefAsInteger(extension_id
, kStoreExtensionErrorsPref
, &pref
))
291 // If the extension is unpacked, we report all error types by default.
292 const Extension
* extension
=
293 ExtensionRegistry::Get(profile_
)->GetExtensionById(
294 extension_id
, ExtensionRegistry::EVERYTHING
);
295 if (extension
&& extension
->location() == Manifest::UNPACKED
)
296 return (1 << ExtensionError::NUM_ERROR_TYPES
) - 1;
298 // Otherwise, use the default mask.
299 return default_mask_
;
302 } // namespace extensions