Upstream version 10.39.225.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 <vector>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/lazy_instance.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/chrome_version_info.h"
18 #include "chrome/common/extensions/features/feature_channel.h"
19 #include "chrome/common/pref_names.h"
20 #include "components/crx_file/id_util.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/browser/extension_prefs.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   pref_registrar_.Init(profile_->GetPrefs());
59   pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
60                       base::Bind(&ErrorConsole::OnPrefChanged,
61                                  base::Unretained(this)));
62
63   registry_observer_.Add(ExtensionRegistry::Get(profile_));
64
65   CheckEnabled();
66 }
67
68 ErrorConsole::~ErrorConsole() {
69   FOR_EACH_OBSERVER(Observer, observers_, OnErrorConsoleDestroyed());
70 }
71
72 // static
73 ErrorConsole* ErrorConsole::Get(Profile* profile) {
74   return ExtensionSystem::Get(profile)->error_console();
75 }
76
77 void ErrorConsole::SetReportingForExtension(const std::string& extension_id,
78                                             ExtensionError::Type type,
79                                             bool enabled) {
80   DCHECK(thread_checker_.CalledOnValidThread());
81   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
82     return;
83
84   int mask = default_mask_;
85   // This call can fail if the preference isn't set, but we don't really care
86   // if it does, because we just use the default mask instead.
87   prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &mask);
88
89   if (enabled)
90     mask |= 1 << type;
91   else
92     mask &= ~(1 << type);
93
94   prefs_->UpdateExtensionPref(extension_id,
95                               kStoreExtensionErrorsPref,
96                               new base::FundamentalValue(mask));
97 }
98
99 void ErrorConsole::SetReportingAllForExtension(
100     const std::string& extension_id, bool enabled) {
101   DCHECK(thread_checker_.CalledOnValidThread());
102   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
103     return;
104
105   int mask = 0;
106   if (enabled)
107     mask = (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
108
109   prefs_->UpdateExtensionPref(extension_id,
110                               kStoreExtensionErrorsPref,
111                               new base::FundamentalValue(mask));
112 }
113
114 bool ErrorConsole::IsReportingEnabledForExtension(
115     const std::string& extension_id) const {
116   DCHECK(thread_checker_.CalledOnValidThread());
117   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
118     return false;
119
120   return GetMaskForExtension(extension_id) != 0;
121 }
122
123 void ErrorConsole::UseDefaultReportingForExtension(
124     const std::string& extension_id) {
125   DCHECK(thread_checker_.CalledOnValidThread());
126   if (!enabled_ || !crx_file::id_util::IdIsValid(extension_id))
127     return;
128
129   prefs_->UpdateExtensionPref(extension_id, kStoreExtensionErrorsPref, NULL);
130 }
131
132 void ErrorConsole::ReportError(scoped_ptr<ExtensionError> error) {
133   DCHECK(thread_checker_.CalledOnValidThread());
134   if (!enabled_ || !crx_file::id_util::IdIsValid(error->extension_id()))
135     return;
136
137   int mask = GetMaskForExtension(error->extension_id());
138   if (!(mask & (1 << error->type())))
139     return;
140
141   const ExtensionError* weak_error = errors_.AddError(error.Pass());
142   FOR_EACH_OBSERVER(Observer, observers_, OnErrorAdded(weak_error));
143 }
144
145 const ErrorList& ErrorConsole::GetErrorsForExtension(
146     const std::string& extension_id) const {
147   return errors_.GetErrorsForExtension(extension_id);
148 }
149
150 void ErrorConsole::AddObserver(Observer* observer) {
151   DCHECK(thread_checker_.CalledOnValidThread());
152   observers_.AddObserver(observer);
153 }
154
155 void ErrorConsole::RemoveObserver(Observer* observer) {
156   DCHECK(thread_checker_.CalledOnValidThread());
157   observers_.RemoveObserver(observer);
158 }
159
160 bool ErrorConsole::IsEnabledForChromeExtensionsPage() const {
161   return profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) &&
162          (FeatureSwitch::error_console()->IsEnabled() ||
163           GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV);
164 }
165
166 bool ErrorConsole::IsEnabledForAppsDeveloperTools() const {
167   return ExtensionRegistry::Get(profile_)->enabled_extensions()
168       .Contains(kAppsDeveloperToolsExtensionId);
169 }
170
171 void ErrorConsole::CheckEnabled() {
172   bool should_be_enabled = IsEnabledForChromeExtensionsPage() ||
173                            IsEnabledForAppsDeveloperTools();
174   if (should_be_enabled && !enabled_)
175     Enable();
176   if (!should_be_enabled && enabled_)
177     Disable();
178 }
179
180 void ErrorConsole::Enable() {
181   enabled_ = true;
182
183   // We postpone the initialization of |prefs_| until now because they can be
184   // NULL in unit_tests. Any unit tests that enable the error console should
185   // also create an ExtensionPrefs object.
186   prefs_ = ExtensionPrefs::Get(profile_);
187
188   notification_registrar_.Add(
189       this,
190       chrome::NOTIFICATION_PROFILE_DESTROYED,
191       content::NotificationService::AllBrowserContextsAndSources());
192
193   const ExtensionSet& extensions =
194       ExtensionRegistry::Get(profile_)->enabled_extensions();
195   for (ExtensionSet::const_iterator iter = extensions.begin();
196        iter != extensions.end();
197        ++iter) {
198     AddManifestErrorsForExtension(iter->get());
199   }
200 }
201
202 void ErrorConsole::Disable() {
203   notification_registrar_.RemoveAll();
204   errors_.RemoveAllErrors();
205   enabled_ = false;
206 }
207
208 void ErrorConsole::OnPrefChanged() {
209   CheckEnabled();
210 }
211
212 void ErrorConsole::OnExtensionUnloaded(content::BrowserContext* browser_context,
213                                        const Extension* extension,
214                                        UnloadedExtensionInfo::Reason reason) {
215   CheckEnabled();
216 }
217
218 void ErrorConsole::OnExtensionLoaded(content::BrowserContext* browser_context,
219                                      const Extension* extension) {
220   CheckEnabled();
221 }
222
223 void ErrorConsole::OnExtensionInstalled(
224     content::BrowserContext* browser_context,
225     const Extension* extension,
226     bool is_update) {
227   // We don't want to have manifest errors from previous installs. We want
228   // to keep runtime errors, though, because extensions are reloaded on a
229   // refresh of chrome:extensions, and we don't want to wipe our history
230   // whenever that happens.
231   errors_.RemoveErrorsForExtensionOfType(extension->id(),
232                                          ExtensionError::MANIFEST_ERROR);
233   AddManifestErrorsForExtension(extension);
234 }
235
236 void ErrorConsole::OnExtensionUninstalled(
237     content::BrowserContext* browser_context,
238     const Extension* extension,
239     extensions::UninstallReason reason) {
240   errors_.Remove(extension->id());
241 }
242
243 void ErrorConsole::AddManifestErrorsForExtension(const Extension* extension) {
244   const std::vector<InstallWarning>& warnings =
245       extension->install_warnings();
246   for (std::vector<InstallWarning>::const_iterator iter = warnings.begin();
247        iter != warnings.end(); ++iter) {
248     ReportError(scoped_ptr<ExtensionError>(new ManifestError(
249         extension->id(),
250         base::UTF8ToUTF16(iter->message),
251         base::UTF8ToUTF16(iter->key),
252         base::UTF8ToUTF16(iter->specific))));
253   }
254 }
255
256 void ErrorConsole::Observe(int type,
257                            const content::NotificationSource& source,
258                            const content::NotificationDetails& details) {
259   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
260   Profile* profile = content::Source<Profile>(source).ptr();
261   // If incognito profile which we are associated with is destroyed, also
262   // destroy all incognito errors.
263   if (profile->IsOffTheRecord() && profile_->IsSameProfile(profile))
264     errors_.RemoveIncognitoErrors();
265 }
266
267 int ErrorConsole::GetMaskForExtension(const std::string& extension_id) const {
268   // Registered preferences take priority over everything else.
269   int pref = 0;
270   if (prefs_->ReadPrefAsInteger(extension_id, kStoreExtensionErrorsPref, &pref))
271     return pref;
272
273   // If the extension is unpacked, we report all error types by default.
274   const Extension* extension =
275       ExtensionRegistry::Get(profile_)->GetExtensionById(
276           extension_id, ExtensionRegistry::EVERYTHING);
277   if (extension && extension->location() == Manifest::UNPACKED)
278     return (1 << ExtensionError::NUM_ERROR_TYPES) - 1;
279
280   // Otherwise, use the default mask.
281   return default_mask_;
282 }
283
284 }  // namespace extensions