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 <utility>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/lazy_instance.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/extensions/error_console/error_console_factory.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/crx_file/id_util.h"
18 #include "components/prefs/pref_service.h"
19 #include "components/version_info/version_info.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/common/constants.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_set.h"
25 #include "extensions/common/feature_switch.h"
26 #include "extensions/common/features/feature_channel.h"
27 
28 namespace extensions {
29 
30 namespace {
31 
32 // The key into the Extension prefs for an Extension's specific reporting
33 // settings.
34 const char kStoreExtensionErrorsPref[] = "store_extension_errors";
35 
36 // This is the default mask for which errors to report. That is, if an extension
37 // does not have specific preference set, this will be used instead.
38 const int kDefaultMask = 0;
39 
40 const char kAppsDeveloperToolsExtensionId[] =
41     "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
42 
43 }  // namespace
44 
OnErrorAdded(const ExtensionError * error)45 void ErrorConsole::Observer::OnErrorAdded(const ExtensionError* error) {
46 }
47 
OnErrorsRemoved(const std::set<std::string> & extension_ids)48 void ErrorConsole::Observer::OnErrorsRemoved(
49     const std::set<std::string>& extension_ids) {
50 }
51 
OnErrorConsoleDestroyed()52 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
53 }
54 
ErrorConsole(Profile * profile)55 ErrorConsole::ErrorConsole(Profile* profile)
56     : enabled_(false),
57       default_mask_(kDefaultMask),
58       profile_(profile),
59       prefs_(nullptr) {
60   pref_registrar_.Init(profile_->GetPrefs());
61   pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
62                       base::Bind(&ErrorConsole::OnPrefChanged,
63                                  base::Unretained(this)));
64 
65   registry_observer_.Add(ExtensionRegistry::Get(profile_));
66 
67   CheckEnabled();
68 }
69 
~ErrorConsole()70 ErrorConsole::~ErrorConsole() {
71   for (auto& observer : observers_)
72     observer.OnErrorConsoleDestroyed();
73 }
74 
75 // static
Get(content::BrowserContext * browser_context)76 ErrorConsole* ErrorConsole::Get(content::BrowserContext* browser_context) {
77   return ErrorConsoleFactory::GetForBrowserContext(browser_context);
78 }
79 
SetReportingForExtension(const std::string & extension_id,ExtensionError::Type type,bool enabled)80 void ErrorConsole::SetReportingForExtension(const std::string& extension_id,
81                                             ExtensionError::Type type,
82                                             bool enabled) {
83   DCHECK(thread_checker_.CalledOnValidThread());
84   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
85     return;
86 
87   int mask = default_mask_;
88   // This call can fail if the preference isn't set, but we don't really care
89   // if it does, because we just use the default mask instead.
90   prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &mask);
91 
92   if (enabled)
93     mask |= 1 << type;
94   else
95     mask &= ~(1 << type);
96 
97   prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref,
98                               std::make_unique<base::Value>(mask));
99 }
100 
SetReportingAllForExtension(const std::string & extension_id,bool enabled)101 void ErrorConsole::SetReportingAllForExtension(
102     const std::string& extension_id, bool enabled) {
103   DCHECK(thread_checker_.CalledOnValidThread());
104   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
105     return;
106 
107   int mask = enabled ? (1 << ExtensionError::NUM_ERROR_TYPES) - 1 : 0;
108 
109   prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref,
110                               std::make_unique<base::Value>(mask));
111 }
112 
IsReportingEnabledForExtension(const std::string & extension_id) const113 bool ErrorConsole::IsReportingEnabledForExtension(
114     const std::string& extension_id) const {
115   DCHECK(thread_checker_.CalledOnValidThread());
116   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
117     return false;
118 
119   return GetMaskForExtension(extension_id) != 0;
120 }
121 
UseDefaultReportingForExtension(const std::string & extension_id)122 void ErrorConsole::UseDefaultReportingForExtension(
123     const std::string& extension_id) {
124   DCHECK(thread_checker_.CalledOnValidThread());
125   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
126     return;
127 
128   prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref, nullptr);
129 }
130 
ReportError(std::unique_ptr<ExtensionError> error)131 void ErrorConsole::ReportError(std::unique_ptr<ExtensionError> error) {
132   DCHECK(thread_checker_.CalledOnValidThread());
133   if (!enabled_ || !crx_file::id_util::IdIsValid(error->extension_id()))
134     return;
135 
136   DCHECK_GE(error->level(), extension_misc::kMinimumSeverityToReportError)
137       << "Errors less than severity warning should not be reported.";
138 
139   int mask = GetMaskForExtension(error->extension_id());
140   if (!(mask & (1 << error->type())))
141     return;
142 
143   const ExtensionError* weak_error = errors_.AddError(std::move(error));
144   for (auto& observer : observers_)
145     observer.OnErrorAdded(weak_error);
146 }
147 
RemoveErrors(const ErrorMap::Filter & filter)148 void ErrorConsole::RemoveErrors(const ErrorMap::Filter& filter) {
149   std::set<std::string> affected_ids;
150   errors_.RemoveErrors(filter, &affected_ids);
151   for (auto& observer : observers_)
152     observer.OnErrorsRemoved(affected_ids);
153 }
154 
GetErrorsForExtension(const std::string & extension_id) const155 const ErrorList& ErrorConsole::GetErrorsForExtension(
156     const std::string& extension_id) const {
157   return errors_.GetErrorsForExtension(extension_id);
158 }
159 
AddObserver(Observer * observer)160 void ErrorConsole::AddObserver(Observer* observer) {
161   DCHECK(thread_checker_.CalledOnValidThread());
162   observers_.AddObserver(observer);
163 }
164 
RemoveObserver(Observer * observer)165 void ErrorConsole::RemoveObserver(Observer* observer) {
166   DCHECK(thread_checker_.CalledOnValidThread());
167   observers_.RemoveObserver(observer);
168 }
169 
IsEnabledForChromeExtensionsPage() const170 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
171   return profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
172 }
173 
IsEnabledForAppsDeveloperTools() const174 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
175   return ExtensionRegistry::Get(profile_)->enabled_extensions()
176       .Contains(kAppsDeveloperToolsExtensionId);
177 }
178 
CheckEnabled()179 void ErrorConsole::CheckEnabled() {
180   bool should_be_enabled = IsEnabledForChromeExtensionsPage() ||
181                            IsEnabledForAppsDeveloperTools();
182   if (should_be_enabled && !enabled_)
183     Enable();
184   if (!should_be_enabled && enabled_)
185     Disable();
186 }
187 
Enable()188 void ErrorConsole::Enable() {
189   enabled_ = true;
190 
191   // We postpone the initialization of |prefs_| until now because they can be
192   // nullptr in unit_tests. Any unit tests that enable the error console should
193   // also create an ExtensionPrefs object.
194   prefs_ = ExtensionPrefs::Get(profile_);
195 
196   profile_observer_.Add(profile_);
197   if (profile_->HasPrimaryOTRProfile())
198     profile_observer_.Add(profile_->GetPrimaryOTRProfile());
199 
200   const ExtensionSet& extensions =
201       ExtensionRegistry::Get(profile_)->enabled_extensions();
202   for (ExtensionSet::const_iterator iter = extensions.begin();
203        iter != extensions.end();
204        ++iter) {
205     AddManifestErrorsForExtension(iter->get());
206   }
207 }
208 
Disable()209 void ErrorConsole::Disable() {
210   profile_observer_.RemoveAll();
211   errors_.RemoveAllErrors();
212   enabled_ = false;
213 }
214 
OnPrefChanged()215 void ErrorConsole::OnPrefChanged() {
216   CheckEnabled();
217 }
218 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionReason reason)219 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext* browser_context,
220                                        const Extension* extension,
221                                        UnloadedExtensionReason reason) {
222   CheckEnabled();
223 }
224 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)225 void ErrorConsole::OnExtensionLoaded(content::BrowserContext* browser_context,
226                                      const Extension* extension) {
227   CheckEnabled();
228 }
229 
OnExtensionInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update)230 void ErrorConsole::OnExtensionInstalled(
231     content::BrowserContext* browser_context,
232     const Extension* extension,
233     bool is_update) {
234   // We don't want to have manifest errors from previous installs. We want
235   // to keep runtime errors, though, because extensions are reloaded on a
236   // refresh of chrome:extensions, and we don't want to wipe our history
237   // whenever that happens.
238   errors_.RemoveErrors(ErrorMap::Filter::ErrorsForExtensionWithType(
239       extension->id(), ExtensionError::MANIFEST_ERROR), nullptr);
240   AddManifestErrorsForExtension(extension);
241 }
242 
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension,extensions::UninstallReason reason)243 void ErrorConsole::OnExtensionUninstalled(
244     content::BrowserContext* browser_context,
245     const Extension* extension,
246     extensions::UninstallReason reason) {
247   errors_.RemoveErrors(ErrorMap::Filter::ErrorsForExtension(extension->id()),
248                        nullptr);
249 }
250 
AddManifestErrorsForExtension(const Extension * extension)251 void ErrorConsole::AddManifestErrorsForExtension(const Extension* extension) {
252   const std::vector<InstallWarning>& warnings =
253       extension->install_warnings();
254   for (auto iter = warnings.begin(); iter != warnings.end(); ++iter) {
255     ReportError(std::unique_ptr<ExtensionError>(new ManifestError(
256         extension->id(), base::UTF8ToUTF16(iter->message),
257         base::UTF8ToUTF16(iter->key), base::UTF8ToUTF16(iter->specific))));
258   }
259 }
260 
OnOffTheRecordProfileCreated(Profile * off_the_record)261 void ErrorConsole::OnOffTheRecordProfileCreated(Profile* off_the_record) {
262   profile_observer_.Add(off_the_record);
263 }
264 
OnProfileWillBeDestroyed(Profile * profile)265 void ErrorConsole::OnProfileWillBeDestroyed(Profile* profile) {
266   profile_observer_.Remove(profile);
267   // If incognito profile which we are associated with is destroyed, also
268   // destroy all incognito errors.
269   if (profile->IsOffTheRecord() && profile_->IsSameOrParent(profile))
270     errors_.RemoveErrors(ErrorMap::Filter::IncognitoErrors(), nullptr);
271 }
272 
GetMaskForExtension(const std::string & extension_id) const273 int ErrorConsole::GetMaskForExtension(const std::string& extension_id) const {
274   // Registered preferences take priority over everything else.
275   int pref = 0;
276   if (prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &pref))
277     return pref;
278 
279   // If the extension is unpacked, we report all error types by default.
280   const Extension* extension =
281       ExtensionRegistry::Get(profile_)->GetExtensionById(
282           extension_id, ExtensionRegistry::EVERYTHING);
283   if (extension && extension->location() == Manifest::UNPACKED)
284     return (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
285 
286   // Otherwise, use the default mask.
287   return default_mask_;
288 }
289 
290 }  // namespace extensions
291