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.h"
11 #include "base/bind_helpers.h"
12 #include "base/lazy_instance.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/stl_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/pref_names.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/common/constants.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
27 #include "extensions/common/feature_switch.h"
29 namespace extensions
{
33 const size_t kMaxErrorsPerExtension
= 100;
35 // Iterate through an error list and remove and delete all errors which were
36 // from an incognito context.
37 void DeleteIncognitoErrorsFromList(ErrorConsole::ErrorList
* list
) {
38 ErrorConsole::ErrorList::iterator iter
= list
->begin();
39 while (iter
!= list
->end()) {
40 if ((*iter
)->from_incognito()) {
42 iter
= list
->erase(iter
);
49 // Iterate through an error list and remove and delete all errors of a given
51 void DeleteErrorsOfTypeFromList(ErrorConsole::ErrorList
* list
,
52 ExtensionError::Type type
) {
53 ErrorConsole::ErrorList::iterator iter
= list
->begin();
54 while (iter
!= list
->end()) {
55 if ((*iter
)->type() == type
) {
57 iter
= list
->erase(iter
);
64 base::LazyInstance
<ErrorConsole::ErrorList
> g_empty_error_list
=
65 LAZY_INSTANCE_INITIALIZER
;
69 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
72 ErrorConsole::ErrorConsole(Profile
* profile
,
73 ExtensionService
* extension_service
)
74 : enabled_(false), profile_(profile
) {
75 // TODO(rdevlin.cronin): Remove once crbug.com/159265 is fixed.
76 #if !defined(ENABLE_EXTENSIONS)
80 // If we don't have the necessary FeatureSwitch enabled, then return
81 // immediately. Since we never register for any notifications, this ensures
82 // the ErrorConsole will never be enabled.
83 if (!FeatureSwitch::error_console()->IsEnabled())
86 pref_registrar_
.Init(profile_
->GetPrefs());
87 pref_registrar_
.Add(prefs::kExtensionsUIDeveloperMode
,
88 base::Bind(&ErrorConsole::OnPrefChanged
,
89 base::Unretained(this)));
91 if (profile_
->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode
))
92 Enable(extension_service
);
95 ErrorConsole::~ErrorConsole() {
96 FOR_EACH_OBSERVER(Observer
, observers_
, OnErrorConsoleDestroyed());
101 ErrorConsole
* ErrorConsole::Get(Profile
* profile
) {
102 return ExtensionSystem::Get(profile
)->error_console();
105 void ErrorConsole::ReportError(scoped_ptr
<ExtensionError
> error
) {
106 DCHECK(thread_checker_
.CalledOnValidThread());
108 if (!enabled_
|| !Extension::IdIsValid(error
->extension_id()))
111 ErrorList
* extension_errors
= &errors_
[error
->extension_id()];
113 // First, check if it's a duplicate.
114 for (ErrorList::iterator iter
= extension_errors
->begin();
115 iter
!= extension_errors
->end(); ++iter
) {
116 // If we find a duplicate error, remove the old error and add the new one,
117 // incrementing the occurrence count of the error. We use the new error
118 // for runtime errors, so we can link to the latest context, inspectable
120 if (error
->IsEqual(*iter
)) {
121 error
->set_occurrences((*iter
)->occurrences() + 1);
123 extension_errors
->erase(iter
);
128 // If there are too many errors for an extension already, limit ourselves to
129 // the most recent ones.
130 if (extension_errors
->size() >= kMaxErrorsPerExtension
) {
131 delete extension_errors
->front();
132 extension_errors
->pop_front();
135 extension_errors
->push_back(error
.release());
138 Observer
, observers_
, OnErrorAdded(extension_errors
->back()));
141 const ErrorConsole::ErrorList
& ErrorConsole::GetErrorsForExtension(
142 const std::string
& extension_id
) const {
143 ErrorMap::const_iterator iter
= errors_
.find(extension_id
);
144 if (iter
!= errors_
.end())
146 return g_empty_error_list
.Get();
149 void ErrorConsole::AddObserver(Observer
* observer
) {
150 DCHECK(thread_checker_
.CalledOnValidThread());
151 observers_
.AddObserver(observer
);
154 void ErrorConsole::RemoveObserver(Observer
* observer
) {
155 DCHECK(thread_checker_
.CalledOnValidThread());
156 observers_
.RemoveObserver(observer
);
159 void ErrorConsole::OnPrefChanged() {
160 bool developer_mode
=
161 profile_
->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode
);
163 if (developer_mode
&& !enabled_
)
164 Enable(ExtensionSystem::Get(profile_
)->extension_service());
165 else if (!developer_mode
&& enabled_
)
169 void ErrorConsole::Enable(ExtensionService
* extension_service
) {
172 notification_registrar_
.Add(
174 chrome::NOTIFICATION_PROFILE_DESTROYED
,
175 content::NotificationService::AllBrowserContextsAndSources());
176 notification_registrar_
.Add(
178 chrome::NOTIFICATION_EXTENSION_UNINSTALLED
,
179 content::Source
<Profile
>(profile_
));
180 notification_registrar_
.Add(
182 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
183 content::Source
<Profile
>(profile_
));
185 if (extension_service
) {
186 // Get manifest errors for extensions already installed.
187 const ExtensionSet
* extensions
= extension_service
->extensions();
188 for (ExtensionSet::const_iterator iter
= extensions
->begin();
189 iter
!= extensions
->end(); ++iter
) {
190 AddManifestErrorsForExtension(iter
->get());
195 void ErrorConsole::Disable() {
196 notification_registrar_
.RemoveAll();
201 void ErrorConsole::AddManifestErrorsForExtension(const Extension
* extension
) {
202 const std::vector
<InstallWarning
>& warnings
=
203 extension
->install_warnings();
204 for (std::vector
<InstallWarning
>::const_iterator iter
= warnings
.begin();
205 iter
!= warnings
.end(); ++iter
) {
206 ReportError(scoped_ptr
<ExtensionError
>(new ManifestError(
208 base::UTF8ToUTF16(iter
->message
),
209 base::UTF8ToUTF16(iter
->key
),
210 base::UTF8ToUTF16(iter
->specific
))));
214 void ErrorConsole::RemoveIncognitoErrors() {
215 for (ErrorMap::iterator iter
= errors_
.begin();
216 iter
!= errors_
.end(); ++iter
) {
217 DeleteIncognitoErrorsFromList(&(iter
->second
));
221 void ErrorConsole::RemoveErrorsForExtension(const std::string
& extension_id
) {
222 ErrorMap::iterator iter
= errors_
.find(extension_id
);
223 if (iter
!= errors_
.end()) {
224 STLDeleteContainerPointers(iter
->second
.begin(), iter
->second
.end());
229 void ErrorConsole::RemoveAllErrors() {
230 for (ErrorMap::iterator iter
= errors_
.begin(); iter
!= errors_
.end(); ++iter
)
231 STLDeleteContainerPointers(iter
->second
.begin(), iter
->second
.end());
235 void ErrorConsole::Observe(int type
,
236 const content::NotificationSource
& source
,
237 const content::NotificationDetails
& details
) {
239 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
240 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
241 // If incognito profile which we are associated with is destroyed, also
242 // destroy all incognito errors.
243 if (profile
->IsOffTheRecord() && profile_
->IsSameProfile(profile
))
244 RemoveIncognitoErrors();
247 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED
:
248 // No need to check the profile here, since we registered to only receive
249 // notifications from our own.
250 RemoveErrorsForExtension(
251 content::Details
<Extension
>(details
).ptr()->id());
253 case chrome::NOTIFICATION_EXTENSION_INSTALLED
: {
254 const InstalledExtensionInfo
* info
=
255 content::Details
<InstalledExtensionInfo
>(details
).ptr();
257 // We don't want to have manifest errors from previous installs. We want
258 // to keep runtime errors, though, because extensions are reloaded on a
259 // refresh of chrome:extensions, and we don't want to wipe our history
260 // whenever that happens.
261 ErrorMap::iterator iter
= errors_
.find(info
->extension
->id());
262 if (iter
!= errors_
.end()) {
263 DeleteErrorsOfTypeFromList(&(iter
->second
),
264 ExtensionError::MANIFEST_ERROR
);
267 AddManifestErrorsForExtension(info
->extension
);
275 } // namespace extensions