Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / error_console / error_console.cc
blob1e6f0b5a597609b34ec00b603ea45ee5470a5a31
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"
7 #include <vector>
9 #include "base/bind.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 {
35 namespace {
37 // The key into the Extension prefs for an Extension's specific reporting
38 // settings.
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";
48 } // namespace
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)
61 : enabled_(false),
62 default_mask_(kDefaultMask),
63 profile_(profile),
64 prefs_(NULL),
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_));
73 CheckEnabled();
76 ErrorConsole::~ErrorConsole() {
77 FOR_EACH_OBSERVER(Observer, observers_, OnErrorConsoleDestroyed());
80 // static
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,
87 bool enabled) {
88 DCHECK(thread_checker_.CalledOnValidThread());
89 if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
90 return;
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);
97 if (enabled)
98 mask |= 1 << type;
99 else
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))
111 return;
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))
124 return false;
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))
133 return;
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()))
141 return;
143 int mask = GetMaskForExtension(error->extension_id());
144 if (!(mask & (1 << error->type())))
145 return;
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.
179 return true;
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_)
191 Enable();
192 if (!should_be_enabled && enabled_)
193 Disable();
196 void ErrorConsole::Enable() {
197 enabled_ = true;
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(
205 this,
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();
213 ++iter) {
214 AddManifestErrorsForExtension(iter->get());
218 void ErrorConsole::Disable() {
219 notification_registrar_.RemoveAll();
220 errors_.RemoveAllErrors();
221 enabled_ = false;
224 void ErrorConsole::OnPrefChanged() {
225 CheckEnabled();
228 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext* browser_context,
229 const Extension* extension,
230 UnloadedExtensionInfo::Reason reason) {
231 CheckEnabled();
234 void ErrorConsole::OnExtensionLoaded(content::BrowserContext* browser_context,
235 const Extension* extension) {
236 CheckEnabled();
239 void ErrorConsole::OnExtensionInstalled(
240 content::BrowserContext* browser_context,
241 const Extension* extension,
242 bool is_update) {
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()),
257 nullptr);
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(
266 extension->id(),
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.
286 int pref = 0;
287 if (prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &pref))
288 return 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