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