Home | History | Annotate | Download | only in error_console
      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
    285