• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/error_console/error_console.h"
6 
7 #include <vector>
8 
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/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 "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/common/constants.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/extension_set.h"
29 #include "extensions/common/feature_switch.h"
30 
31 namespace extensions {
32 
33 namespace {
34 
35 // The key into the Extension prefs for an Extension's specific reporting
36 // settings.
37 const char kStoreExtensionErrorsPref[] = "store_extension_errors";
38 
39 // This is the default mask for which errors to report. That is, if an extension
40 // does not have specific preference set, this will be used instead.
41 const int kDefaultMask = 0;
42 
43 const char kAppsDeveloperToolsExtensionId[] =
44     "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
45 
46 }  // namespace
47 
OnErrorConsoleDestroyed()48 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
49 }
50 
ErrorConsole(Profile * profile)51 ErrorConsole::ErrorConsole(Profile* profile)
52      : enabled_(false),
53        default_mask_(kDefaultMask),
54        profile_(profile),
55        prefs_(NULL),
56        registry_observer_(this) {
57 // TODO(rdevlin.cronin): Remove once crbug.com/159265 is fixed.
58 #if !defined(ENABLE_EXTENSIONS)
59   return;
60 #endif
61 
62   pref_registrar_.Init(profile_->GetPrefs());
63   pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
64                       base::Bind(&ErrorConsole::OnPrefChanged,
65                                  base::Unretained(this)));
66 
67   registry_observer_.Add(ExtensionRegistry::Get(profile_));
68 
69   CheckEnabled();
70 }
71 
~ErrorConsole()72 ErrorConsole::~ErrorConsole() {
73   FOR_EACH_OBSERVER(Observer, observers_, OnErrorConsoleDestroyed());
74 }
75 
76 // static
Get(Profile * profile)77 ErrorConsole* ErrorConsole::Get(Profile* profile) {
78   return ExtensionSystem::Get(profile)->error_console();
79 }
80 
SetReportingForExtension(const std::string & extension_id,ExtensionError::Type type,bool enabled)81 void ErrorConsole::SetReportingForExtension(const std::string& extension_id,
82                                             ExtensionError::Type type,
83                                             bool enabled) {
84   DCHECK(thread_checker_.CalledOnValidThread());
85   if (!enabled_ || !Extension::IdIsValid(extension_id))
86     return;
87 
88   int mask = default_mask_;
89   // This call can fail if the preference isn't set, but we don't really care
90   // if it does, because we just use the default mask instead.
91   prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &mask);
92 
93   if (enabled)
94     mask |= 1 << type;
95   else
96     mask &= ~(1 << type);
97 
98   prefs_->UpdateExtensionPref(extension_id,
99                               kStoreExtensionErrorsPref,
100                               base::Value::CreateIntegerValue(mask));
101 }
102 
SetReportingAllForExtension(const std::string & extension_id,bool enabled)103 void ErrorConsole::SetReportingAllForExtension(
104     const std::string& extension_id, bool enabled) {
105   DCHECK(thread_checker_.CalledOnValidThread());
106   if (!enabled_ || !Extension::IdIsValid(extension_id))
107     return;
108 
109   int mask = 0;
110   if (enabled)
111     mask = (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
112 
113   prefs_->UpdateExtensionPref(extension_id,
114                               kStoreExtensionErrorsPref,
115                               base::Value::CreateIntegerValue(mask));
116 }
117 
IsReportingEnabledForExtension(const std::string & extension_id) const118 bool ErrorConsole::IsReportingEnabledForExtension(
119     const std::string& extension_id) const {
120   DCHECK(thread_checker_.CalledOnValidThread());
121   if (!enabled_ || !Extension::IdIsValid(extension_id))
122     return false;
123 
124   return GetMaskForExtension(extension_id) != 0;
125 }
126 
UseDefaultReportingForExtension(const std::string & extension_id)127 void ErrorConsole::UseDefaultReportingForExtension(
128     const std::string& extension_id) {
129   DCHECK(thread_checker_.CalledOnValidThread());
130   if (!enabled_ || !Extension::IdIsValid(extension_id))
131     return;
132 
133   prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref, NULL);
134 }
135 
ReportError(scoped_ptr<ExtensionError> error)136 void ErrorConsole::ReportError(scoped_ptr<ExtensionError> error) {
137   DCHECK(thread_checker_.CalledOnValidThread());
138   if (!enabled_ || !Extension::IdIsValid(error->extension_id()))
139     return;
140 
141   int mask = GetMaskForExtension(error->extension_id());
142   if (!(mask & (1 << error->type())))
143     return;
144 
145   const ExtensionError* weak_error = errors_.AddError(error.Pass());
146   FOR_EACH_OBSERVER(Observer, observers_, OnErrorAdded(weak_error));
147 }
148 
GetErrorsForExtension(const std::string & extension_id) const149 const ErrorList& ErrorConsole::GetErrorsForExtension(
150     const std::string& extension_id) const {
151   return errors_.GetErrorsForExtension(extension_id);
152 }
153 
AddObserver(Observer * observer)154 void ErrorConsole::AddObserver(Observer* observer) {
155   DCHECK(thread_checker_.CalledOnValidThread());
156   observers_.AddObserver(observer);
157 }
158 
RemoveObserver(Observer * observer)159 void ErrorConsole::RemoveObserver(Observer* observer) {
160   DCHECK(thread_checker_.CalledOnValidThread());
161   observers_.RemoveObserver(observer);
162 }
163 
IsEnabledForChromeExtensionsPage() const164 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
165   return profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) &&
166          (FeatureSwitch::error_console()->IsEnabled() ||
167           GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV);
168 }
169 
IsEnabledForAppsDeveloperTools() const170 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
171   return ExtensionRegistry::Get(profile_)->enabled_extensions()
172       .Contains(kAppsDeveloperToolsExtensionId);
173 }
174 
CheckEnabled()175 void ErrorConsole::CheckEnabled() {
176   bool should_be_enabled = IsEnabledForChromeExtensionsPage() ||
177                            IsEnabledForAppsDeveloperTools();
178   if (should_be_enabled && !enabled_)
179     Enable();
180   if (!should_be_enabled && enabled_)
181     Disable();
182 }
183 
Enable()184 void ErrorConsole::Enable() {
185   enabled_ = true;
186 
187   // We postpone the initialization of |prefs_| until now because they can be
188   // NULL in unit_tests. Any unit tests that enable the error console should
189   // also create an ExtensionPrefs object.
190   prefs_ = ExtensionPrefs::Get(profile_);
191 
192   notification_registrar_.Add(
193       this,
194       chrome::NOTIFICATION_PROFILE_DESTROYED,
195       content::NotificationService::AllBrowserContextsAndSources());
196 
197   const ExtensionSet& extensions =
198       ExtensionRegistry::Get(profile_)->enabled_extensions();
199   for (ExtensionSet::const_iterator iter = extensions.begin();
200        iter != extensions.end();
201        ++iter) {
202     AddManifestErrorsForExtension(iter->get());
203   }
204 }
205 
Disable()206 void ErrorConsole::Disable() {
207   notification_registrar_.RemoveAll();
208   errors_.RemoveAllErrors();
209   enabled_ = false;
210 }
211 
OnPrefChanged()212 void ErrorConsole::OnPrefChanged() {
213   CheckEnabled();
214 }
215 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)216 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext* browser_context,
217                                        const Extension* extension,
218                                        UnloadedExtensionInfo::Reason reason) {
219   CheckEnabled();
220 }
221 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)222 void ErrorConsole::OnExtensionLoaded(content::BrowserContext* browser_context,
223                                      const Extension* extension) {
224   CheckEnabled();
225 }
226 
OnExtensionInstalled(content::BrowserContext * browser_context,const Extension * extension)227 void ErrorConsole::OnExtensionInstalled(
228     content::BrowserContext* browser_context,
229     const Extension* extension) {
230   // We don't want to have manifest errors from previous installs. We want
231   // to keep runtime errors, though, because extensions are reloaded on a
232   // refresh of chrome:extensions, and we don't want to wipe our history
233   // whenever that happens.
234   errors_.RemoveErrorsForExtensionOfType(extension->id(),
235                                          ExtensionError::MANIFEST_ERROR);
236   AddManifestErrorsForExtension(extension);
237 }
238 
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension)239 void ErrorConsole::OnExtensionUninstalled(
240     content::BrowserContext* browser_context,
241     const Extension* extension) {
242   errors_.Remove(extension->id());
243 }
244 
AddManifestErrorsForExtension(const Extension * extension)245 void ErrorConsole::AddManifestErrorsForExtension(const Extension* extension) {
246   const std::vector<InstallWarning>& warnings =
247       extension->install_warnings();
248   for (std::vector<InstallWarning>::const_iterator iter = warnings.begin();
249        iter != warnings.end(); ++iter) {
250     ReportError(scoped_ptr<ExtensionError>(new ManifestError(
251         extension->id(),
252         base::UTF8ToUTF16(iter->message),
253         base::UTF8ToUTF16(iter->key),
254         base::UTF8ToUTF16(iter->specific))));
255   }
256 }
257 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)258 void ErrorConsole::Observe(int type,
259                            const content::NotificationSource& source,
260                            const content::NotificationDetails& details) {
261   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
262   Profile* profile = content::Source<Profile>(source).ptr();
263   // If incognito profile which we are associated with is destroyed, also
264   // destroy all incognito errors.
265   if (profile->IsOffTheRecord() && profile_->IsSameProfile(profile))
266     errors_.RemoveIncognitoErrors();
267 }
268 
GetMaskForExtension(const std::string & extension_id) const269 int ErrorConsole::GetMaskForExtension(const std::string& extension_id) const {
270   // Registered preferences take priority over everything else.
271   int pref = 0;
272   if (prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &pref))
273     return pref;
274 
275   // If the extension is unpacked, we report all error types by default.
276   const Extension* extension =
277       ExtensionRegistry::Get(profile_)->GetExtensionById(
278           extension_id, ExtensionRegistry::EVERYTHING);
279   if (extension && extension->location() == Manifest::UNPACKED)
280     return (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
281 
282   // Otherwise, use the default mask.
283   return default_mask_;
284 }
285 
286 }  // namespace extensions
287