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