- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / error_console / error_console.cc
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 <list>
8 #include <vector>
9
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/extensions/extension.h"
21 #include "chrome/common/extensions/extension_set.h"
22 #include "chrome/common/extensions/feature_switch.h"
23 #include "chrome/common/pref_names.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_source.h"
27 #include "extensions/common/constants.h"
28
29 namespace extensions {
30
31 namespace {
32
33 const size_t kMaxErrorsPerExtension = 100;
34
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()) {
41       delete *iter;
42       iter = list->erase(iter);
43     } else {
44       ++iter;
45     }
46   }
47 }
48
49 // Iterate through an error list and remove and delete all errors of a given
50 // |type|.
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) {
56       delete *iter;
57       iter = list->erase(iter);
58     } else {
59       ++iter;
60     }
61   }
62 }
63
64 base::LazyInstance<ErrorConsole::ErrorList> g_empty_error_list =
65     LAZY_INSTANCE_INITIALIZER;
66
67 }  // namespace
68
69 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
70 }
71
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)
77   return;
78 #endif
79
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())
84     return;
85
86   pref_registrar_.Init(profile_->GetPrefs());
87   pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
88                       base::Bind(&ErrorConsole::OnPrefChanged,
89                                  base::Unretained(this)));
90
91   if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode))
92     Enable(extension_service);
93 }
94
95 ErrorConsole::~ErrorConsole() {
96   FOR_EACH_OBSERVER(Observer, observers_, OnErrorConsoleDestroyed());
97   RemoveAllErrors();
98 }
99
100 // static
101 ErrorConsole* ErrorConsole::Get(Profile* profile) {
102   return ExtensionSystem::Get(profile)->error_console();
103 }
104
105 void ErrorConsole::ReportError(scoped_ptr<ExtensionError> error) {
106   DCHECK(thread_checker_.CalledOnValidThread());
107
108   if (!enabled_ || !Extension::IdIsValid(error->extension_id()))
109     return;
110
111   ErrorList* extension_errors = &errors_[error->extension_id()];
112
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
119     // view, etc.
120     if (error->IsEqual(*iter)) {
121       error->set_occurrences((*iter)->occurrences() + 1);
122       delete *iter;
123       extension_errors->erase(iter);
124       break;
125     }
126   }
127
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();
133   }
134
135   extension_errors->push_back(error.release());
136
137   FOR_EACH_OBSERVER(
138       Observer, observers_, OnErrorAdded(extension_errors->back()));
139 }
140
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())
145     return iter->second;
146   return g_empty_error_list.Get();
147 }
148
149 void ErrorConsole::AddObserver(Observer* observer) {
150   DCHECK(thread_checker_.CalledOnValidThread());
151   observers_.AddObserver(observer);
152 }
153
154 void ErrorConsole::RemoveObserver(Observer* observer) {
155   DCHECK(thread_checker_.CalledOnValidThread());
156   observers_.RemoveObserver(observer);
157 }
158
159 void ErrorConsole::OnPrefChanged() {
160   bool developer_mode =
161       profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
162
163   if (developer_mode && !enabled_)
164     Enable(ExtensionSystem::Get(profile_)->extension_service());
165   else if (!developer_mode && enabled_)
166     Disable();
167 }
168
169 void ErrorConsole::Enable(ExtensionService* extension_service) {
170   enabled_ = true;
171
172   notification_registrar_.Add(
173       this,
174       chrome::NOTIFICATION_PROFILE_DESTROYED,
175       content::NotificationService::AllBrowserContextsAndSources());
176   notification_registrar_.Add(
177       this,
178       chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
179       content::Source<Profile>(profile_));
180   notification_registrar_.Add(
181       this,
182       chrome::NOTIFICATION_EXTENSION_INSTALLED,
183       content::Source<Profile>(profile_));
184
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());
191     }
192   }
193 }
194
195 void ErrorConsole::Disable() {
196   notification_registrar_.RemoveAll();
197   RemoveAllErrors();
198   enabled_ = false;
199 }
200
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(
207         extension->id(),
208         base::UTF8ToUTF16(iter->message),
209         base::UTF8ToUTF16(iter->key),
210         base::UTF8ToUTF16(iter->specific))));
211   }
212 }
213
214 void ErrorConsole::RemoveIncognitoErrors() {
215   for (ErrorMap::iterator iter = errors_.begin();
216        iter != errors_.end(); ++iter) {
217     DeleteIncognitoErrorsFromList(&(iter->second));
218   }
219 }
220
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());
225     errors_.erase(iter);
226   }
227 }
228
229 void ErrorConsole::RemoveAllErrors() {
230   for (ErrorMap::iterator iter = errors_.begin(); iter != errors_.end(); ++iter)
231     STLDeleteContainerPointers(iter->second.begin(), iter->second.end());
232   errors_.clear();
233 }
234
235 void ErrorConsole::Observe(int type,
236                            const content::NotificationSource& source,
237                            const content::NotificationDetails& details) {
238   switch (type) {
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();
245       break;
246     }
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());
252       break;
253     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
254       const InstalledExtensionInfo* info =
255           content::Details<InstalledExtensionInfo>(details).ptr();
256
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);
265       }
266
267       AddManifestErrorsForExtension(info->extension);
268       break;
269     }
270     default:
271       NOTREACHED();
272   }
273 }
274
275 }  // namespace extensions