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 <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_set.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/common/constants.h"
     26 #include "extensions/common/extension.h"
     27 #include "extensions/common/feature_switch.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
    276