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/profiles/profile.h"
17 #include "chrome/common/chrome_version_info.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 "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/notification_source.h"
24 #include "extensions/browser/extension_prefs.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 pref_registrar_.Init(profile_->GetPrefs());
59 pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
60 base::Bind(&ErrorConsole::OnPrefChanged,
61 base::Unretained(this)));
63 registry_observer_.Add(ExtensionRegistry::Get(profile_));
68 ErrorConsole::~ErrorConsole() {
69 FOR_EACH_OBSERVER(Observer, observers_, OnErrorConsoleDestroyed());
73 ErrorConsole* ErrorConsole::Get(Profile* profile) {
74 return ExtensionSystem::Get(profile)->error_console();
77 void ErrorConsole::SetReportingForExtension(const std::string& extension_id,
78 ExtensionError::Type type,
80 DCHECK(thread_checker_.CalledOnValidThread());
81 if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
84 int mask = default_mask_;
85 // This call can fail if the preference isn't set, but we don't really care
86 // if it does, because we just use the default mask instead.
87 prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &mask);
94 prefs_->UpdateExtensionPref(extension_id,
95 kStoreExtensionErrorsPref,
96 new base::FundamentalValue(mask));
99 void ErrorConsole::SetReportingAllForExtension(
100 const std::string& extension_id, bool enabled) {
101 DCHECK(thread_checker_.CalledOnValidThread());
102 if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
107 mask = (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
109 prefs_->UpdateExtensionPref(extension_id,
110 kStoreExtensionErrorsPref,
111 new base::FundamentalValue(mask));
114 bool ErrorConsole::IsReportingEnabledForExtension(
115 const std::string& extension_id) const {
116 DCHECK(thread_checker_.CalledOnValidThread());
117 if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
120 return GetMaskForExtension(extension_id) != 0;
123 void ErrorConsole::UseDefaultReportingForExtension(
124 const std::string& extension_id) {
125 DCHECK(thread_checker_.CalledOnValidThread());
126 if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
129 prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref, NULL);
132 void ErrorConsole::ReportError(scoped_ptr<ExtensionError> error) {
133 DCHECK(thread_checker_.CalledOnValidThread());
134 if (!enabled_ || !crx_file::id_util::IdIsValid(error->extension_id()))
137 int mask = GetMaskForExtension(error->extension_id());
138 if (!(mask & (1 << error->type())))
141 const ExtensionError* weak_error = errors_.AddError(error.Pass());
142 FOR_EACH_OBSERVER(Observer, observers_, OnErrorAdded(weak_error));
145 const ErrorList& ErrorConsole::GetErrorsForExtension(
146 const std::string& extension_id) const {
147 return errors_.GetErrorsForExtension(extension_id);
150 void ErrorConsole::AddObserver(Observer* observer) {
151 DCHECK(thread_checker_.CalledOnValidThread());
152 observers_.AddObserver(observer);
155 void ErrorConsole::RemoveObserver(Observer* observer) {
156 DCHECK(thread_checker_.CalledOnValidThread());
157 observers_.RemoveObserver(observer);
160 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
161 return profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) &&
162 (FeatureSwitch::error_console()->IsEnabled() ||
163 GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV);
166 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
167 return ExtensionRegistry::Get(profile_)->enabled_extensions()
168 .Contains(kAppsDeveloperToolsExtensionId);
171 void ErrorConsole::CheckEnabled() {
172 bool should_be_enabled = IsEnabledForChromeExtensionsPage() ||
173 IsEnabledForAppsDeveloperTools();
174 if (should_be_enabled && !enabled_)
176 if (!should_be_enabled && enabled_)
180 void ErrorConsole::Enable() {
183 // We postpone the initialization of |prefs_| until now because they can be
184 // NULL in unit_tests. Any unit tests that enable the error console should
185 // also create an ExtensionPrefs object.
186 prefs_ = ExtensionPrefs::Get(profile_);
188 notification_registrar_.Add(
190 chrome::NOTIFICATION_PROFILE_DESTROYED,
191 content::NotificationService::AllBrowserContextsAndSources());
193 const ExtensionSet& extensions =
194 ExtensionRegistry::Get(profile_)->enabled_extensions();
195 for (ExtensionSet::const_iterator iter = extensions.begin();
196 iter != extensions.end();
198 AddManifestErrorsForExtension(iter->get());
202 void ErrorConsole::Disable() {
203 notification_registrar_.RemoveAll();
204 errors_.RemoveAllErrors();
208 void ErrorConsole::OnPrefChanged() {
212 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext* browser_context,
213 const Extension* extension,
214 UnloadedExtensionInfo::Reason reason) {
218 void ErrorConsole::OnExtensionLoaded(content::BrowserContext* browser_context,
219 const Extension* extension) {
223 void ErrorConsole::OnExtensionInstalled(
224 content::BrowserContext* browser_context,
225 const Extension* extension,
227 // We don't want to have manifest errors from previous installs. We want
228 // to keep runtime errors, though, because extensions are reloaded on a
229 // refresh of chrome:extensions, and we don't want to wipe our history
230 // whenever that happens.
231 errors_.RemoveErrorsForExtensionOfType(extension->id(),
232 ExtensionError::MANIFEST_ERROR);
233 AddManifestErrorsForExtension(extension);
236 void ErrorConsole::OnExtensionUninstalled(
237 content::BrowserContext* browser_context,
238 const Extension* extension,
239 extensions::UninstallReason reason) {
240 errors_.Remove(extension->id());
243 void ErrorConsole::AddManifestErrorsForExtension(const Extension* extension) {
244 const std::vector<InstallWarning>& warnings =
245 extension->install_warnings();
246 for (std::vector<InstallWarning>::const_iterator iter = warnings.begin();
247 iter != warnings.end(); ++iter) {
248 ReportError(scoped_ptr<ExtensionError>(new ManifestError(
250 base::UTF8ToUTF16(iter->message),
251 base::UTF8ToUTF16(iter->key),
252 base::UTF8ToUTF16(iter->specific))));
256 void ErrorConsole::Observe(int type,
257 const content::NotificationSource& source,
258 const content::NotificationDetails& details) {
259 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
260 Profile* profile = content::Source<Profile>(source).ptr();
261 // If incognito profile which we are associated with is destroyed, also
262 // destroy all incognito errors.
263 if (profile->IsOffTheRecord() && profile_->IsSameProfile(profile))
264 errors_.RemoveIncognitoErrors();
267 int ErrorConsole::GetMaskForExtension(const std::string& extension_id) const {
268 // Registered preferences take priority over everything else.
270 if (prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &pref))
273 // If the extension is unpacked, we report all error types by default.
274 const Extension* extension =
275 ExtensionRegistry::Get(profile_)->GetExtensionById(
276 extension_id, ExtensionRegistry::EVERYTHING);
277 if (extension && extension->location() == Manifest::UNPACKED)
278 return (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
280 // Otherwise, use the default mask.
281 return default_mask_;
284 } // namespace extensions