Home | History | Annotate | Download | only in browser
      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 "extensions/browser/info_map.h"
      6 
      7 #include "base/strings/string_util.h"
      8 #include "content/public/browser/browser_thread.h"
      9 #include "extensions/browser/content_verifier.h"
     10 #include "extensions/common/constants.h"
     11 #include "extensions/common/extension.h"
     12 #include "extensions/common/extension_resource.h"
     13 #include "extensions/common/extension_set.h"
     14 #include "extensions/common/manifest_handlers/incognito_info.h"
     15 #include "extensions/common/manifest_handlers/shared_module_info.h"
     16 #include "extensions/common/permissions/permissions_data.h"
     17 #include "url/gurl.h"
     18 
     19 using content::BrowserThread;
     20 
     21 namespace extensions {
     22 
     23 namespace {
     24 
     25 void CheckOnValidThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
     26 
     27 }  // namespace
     28 
     29 struct InfoMap::ExtraData {
     30   // When the extension was installed.
     31   base::Time install_time;
     32 
     33   // True if the user has allowed this extension to run in incognito mode.
     34   bool incognito_enabled;
     35 
     36   // True if the user has disabled notifications for this extension manually.
     37   bool notifications_disabled;
     38 
     39   ExtraData();
     40   ~ExtraData();
     41 };
     42 
     43 InfoMap::ExtraData::ExtraData()
     44     : incognito_enabled(false), notifications_disabled(false) {
     45 }
     46 
     47 InfoMap::ExtraData::~ExtraData() {}
     48 
     49 InfoMap::InfoMap() : signin_process_id_(-1) {
     50 }
     51 
     52 void InfoMap::AddExtension(const Extension* extension,
     53                            base::Time install_time,
     54                            bool incognito_enabled,
     55                            bool notifications_disabled) {
     56   CheckOnValidThread();
     57   extensions_.Insert(extension);
     58   disabled_extensions_.Remove(extension->id());
     59 
     60   extra_data_[extension->id()].install_time = install_time;
     61   extra_data_[extension->id()].incognito_enabled = incognito_enabled;
     62   extra_data_[extension->id()].notifications_disabled = notifications_disabled;
     63 }
     64 
     65 void InfoMap::RemoveExtension(const std::string& extension_id,
     66                               const UnloadedExtensionInfo::Reason reason) {
     67   CheckOnValidThread();
     68   const Extension* extension = extensions_.GetByID(extension_id);
     69   extra_data_.erase(extension_id);  // we don't care about disabled extra data
     70   bool was_uninstalled = (reason != UnloadedExtensionInfo::REASON_DISABLE &&
     71                           reason != UnloadedExtensionInfo::REASON_TERMINATE);
     72   if (extension) {
     73     if (!was_uninstalled)
     74       disabled_extensions_.Insert(extension);
     75     extensions_.Remove(extension_id);
     76   } else if (was_uninstalled) {
     77     // If the extension was uninstalled, make sure it's removed from the map of
     78     // disabled extensions.
     79     disabled_extensions_.Remove(extension_id);
     80   } else {
     81     // NOTE: This can currently happen if we receive multiple unload
     82     // notifications, e.g. setting incognito-enabled state for a
     83     // disabled extension (e.g., via sync).  See
     84     // http://code.google.com/p/chromium/issues/detail?id=50582 .
     85     NOTREACHED() << extension_id;
     86   }
     87 }
     88 
     89 base::Time InfoMap::GetInstallTime(const std::string& extension_id) const {
     90   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
     91   if (iter != extra_data_.end())
     92     return iter->second.install_time;
     93   return base::Time();
     94 }
     95 
     96 bool InfoMap::IsIncognitoEnabled(const std::string& extension_id) const {
     97   // Keep in sync with duplicate in extensions/browser/process_manager.cc.
     98   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
     99   if (iter != extra_data_.end())
    100     return iter->second.incognito_enabled;
    101   return false;
    102 }
    103 
    104 bool InfoMap::CanCrossIncognito(const Extension* extension) const {
    105   // This is duplicated from ExtensionService :(.
    106   return IsIncognitoEnabled(extension->id()) &&
    107          !IncognitoInfo::IsSplitMode(extension);
    108 }
    109 
    110 void InfoMap::RegisterExtensionProcess(const std::string& extension_id,
    111                                        int process_id,
    112                                        int site_instance_id) {
    113   if (!process_map_.Insert(extension_id, process_id, site_instance_id)) {
    114     NOTREACHED() << "Duplicate extension process registration for: "
    115                  << extension_id << "," << process_id << ".";
    116   }
    117 }
    118 
    119 void InfoMap::UnregisterExtensionProcess(const std::string& extension_id,
    120                                          int process_id,
    121                                          int site_instance_id) {
    122   if (!process_map_.Remove(extension_id, process_id, site_instance_id)) {
    123     NOTREACHED() << "Unknown extension process registration for: "
    124                  << extension_id << "," << process_id << ".";
    125   }
    126 }
    127 
    128 void InfoMap::UnregisterAllExtensionsInProcess(int process_id) {
    129   process_map_.RemoveAllFromProcess(process_id);
    130 }
    131 
    132 void InfoMap::GetExtensionsWithAPIPermissionForSecurityOrigin(
    133     const GURL& origin,
    134     int process_id,
    135     APIPermission::ID permission,
    136     ExtensionSet* extensions) const {
    137   DCHECK(extensions);
    138 
    139   if (origin.SchemeIs(kExtensionScheme)) {
    140     const std::string& id = origin.host();
    141     const Extension* extension = extensions_.GetByID(id);
    142     if (extension &&
    143         extension->permissions_data()->HasAPIPermission(permission) &&
    144         process_map_.Contains(id, process_id)) {
    145       extensions->Insert(extension);
    146     }
    147     return;
    148   }
    149 
    150   ExtensionSet::const_iterator i = extensions_.begin();
    151   for (; i != extensions_.end(); ++i) {
    152     if ((*i)->web_extent().MatchesSecurityOrigin(origin) &&
    153         process_map_.Contains((*i)->id(), process_id) &&
    154         (*i)->permissions_data()->HasAPIPermission(permission)) {
    155       extensions->Insert(*i);
    156     }
    157   }
    158 }
    159 
    160 bool InfoMap::SecurityOriginHasAPIPermission(const GURL& origin,
    161                                              int process_id,
    162                                              APIPermission::ID permission)
    163     const {
    164   ExtensionSet extensions;
    165   GetExtensionsWithAPIPermissionForSecurityOrigin(
    166       origin, process_id, permission, &extensions);
    167   return !extensions.is_empty();
    168 }
    169 
    170 // This function is security sensitive. Bugs could cause problems that break
    171 // restrictions on local file access or NaCl's validation caching. If you modify
    172 // this function, please get a security review from a NaCl person.
    173 bool InfoMap::MapUrlToLocalFilePath(const GURL& file_url,
    174                                     bool use_blocking_api,
    175                                     base::FilePath* file_path) {
    176   // Check that the URL is recognized by the extension system.
    177   const Extension* extension = extensions_.GetExtensionOrAppByURL(file_url);
    178   if (!extension)
    179     return false;
    180 
    181   // This is a short-cut which avoids calling a blocking file operation
    182   // (GetFilePath()), so that this can be called on the IO thread. It only
    183   // handles a subset of the urls.
    184   if (!use_blocking_api) {
    185     if (file_url.SchemeIs(extensions::kExtensionScheme)) {
    186       std::string path = file_url.path();
    187       base::TrimString(path, "/", &path);  // Remove first slash
    188       *file_path = extension->path().AppendASCII(path);
    189       return true;
    190     }
    191     return false;
    192   }
    193 
    194   std::string path = file_url.path();
    195   ExtensionResource resource;
    196 
    197   if (SharedModuleInfo::IsImportedPath(path)) {
    198     // Check if this is a valid path that is imported for this extension.
    199     std::string new_extension_id;
    200     std::string new_relative_path;
    201     SharedModuleInfo::ParseImportedPath(
    202         path, &new_extension_id, &new_relative_path);
    203     const Extension* new_extension = extensions_.GetByID(new_extension_id);
    204     if (!new_extension)
    205       return false;
    206 
    207     if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) ||
    208         !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) {
    209       return false;
    210     }
    211 
    212     resource = new_extension->GetResource(new_relative_path);
    213   } else {
    214     // Check that the URL references a resource in the extension.
    215     resource = extension->GetResource(path);
    216   }
    217 
    218   if (resource.empty())
    219     return false;
    220 
    221   // GetFilePath is a blocking function call.
    222   const base::FilePath resource_file_path = resource.GetFilePath();
    223   if (resource_file_path.empty())
    224     return false;
    225 
    226   *file_path = resource_file_path;
    227   return true;
    228 }
    229 
    230 QuotaService* InfoMap::GetQuotaService() {
    231   CheckOnValidThread();
    232   if (!quota_service_)
    233     quota_service_.reset(new QuotaService());
    234   return quota_service_.get();
    235 }
    236 
    237 void InfoMap::SetSigninProcess(int process_id) {
    238   signin_process_id_ = process_id;
    239 }
    240 
    241 bool InfoMap::IsSigninProcess(int process_id) const {
    242   return process_id == signin_process_id_;
    243 }
    244 
    245 void InfoMap::SetNotificationsDisabled(
    246     const std::string& extension_id,
    247     bool notifications_disabled) {
    248   ExtraDataMap::iterator iter = extra_data_.find(extension_id);
    249   if (iter != extra_data_.end())
    250     iter->second.notifications_disabled = notifications_disabled;
    251 }
    252 
    253 bool InfoMap::AreNotificationsDisabled(
    254     const std::string& extension_id) const {
    255   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
    256   if (iter != extra_data_.end())
    257     return iter->second.notifications_disabled;
    258   return false;
    259 }
    260 
    261 void InfoMap::SetContentVerifier(ContentVerifier* verifier) {
    262   content_verifier_ = verifier;
    263 }
    264 
    265 InfoMap::~InfoMap() {
    266   if (quota_service_) {
    267     BrowserThread::DeleteSoon(
    268         BrowserThread::IO, FROM_HERE, quota_service_.release());
    269   }
    270 }
    271 
    272 }  // namespace extensions
    273