| // Copyright 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "extensions/browser/info_map.h" | 
 |  | 
 | #include "base/strings/string_util.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "extensions/browser/content_verifier.h" | 
 | #include "extensions/common/constants.h" | 
 | #include "extensions/common/extension.h" | 
 | #include "extensions/common/extension_resource.h" | 
 | #include "extensions/common/extension_set.h" | 
 | #include "extensions/common/manifest_handlers/incognito_info.h" | 
 | #include "extensions/common/manifest_handlers/shared_module_info.h" | 
 | #include "extensions/common/permissions/permissions_data.h" | 
 | #include "url/gurl.h" | 
 |  | 
 | using content::BrowserThread; | 
 |  | 
 | namespace extensions { | 
 |  | 
 | namespace { | 
 |  | 
 | void CheckOnValidThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); } | 
 |  | 
 | }  // namespace | 
 |  | 
 | struct InfoMap::ExtraData { | 
 |   // When the extension was installed. | 
 |   base::Time install_time; | 
 |  | 
 |   // True if the user has allowed this extension to run in incognito mode. | 
 |   bool incognito_enabled; | 
 |  | 
 |   // True if the user has disabled notifications for this extension manually. | 
 |   bool notifications_disabled; | 
 |  | 
 |   ExtraData(); | 
 |   ~ExtraData(); | 
 | }; | 
 |  | 
 | InfoMap::ExtraData::ExtraData() | 
 |     : incognito_enabled(false), notifications_disabled(false) { | 
 | } | 
 |  | 
 | InfoMap::ExtraData::~ExtraData() {} | 
 |  | 
 | InfoMap::InfoMap() : signin_process_id_(-1) { | 
 | } | 
 |  | 
 | void InfoMap::AddExtension(const Extension* extension, | 
 |                            base::Time install_time, | 
 |                            bool incognito_enabled, | 
 |                            bool notifications_disabled) { | 
 |   CheckOnValidThread(); | 
 |   extensions_.Insert(extension); | 
 |   disabled_extensions_.Remove(extension->id()); | 
 |  | 
 |   extra_data_[extension->id()].install_time = install_time; | 
 |   extra_data_[extension->id()].incognito_enabled = incognito_enabled; | 
 |   extra_data_[extension->id()].notifications_disabled = notifications_disabled; | 
 | } | 
 |  | 
 | void InfoMap::RemoveExtension(const std::string& extension_id, | 
 |                               const UnloadedExtensionInfo::Reason reason) { | 
 |   CheckOnValidThread(); | 
 |   const Extension* extension = extensions_.GetByID(extension_id); | 
 |   extra_data_.erase(extension_id);  // we don't care about disabled extra data | 
 |   bool was_uninstalled = (reason != UnloadedExtensionInfo::REASON_DISABLE && | 
 |                           reason != UnloadedExtensionInfo::REASON_TERMINATE); | 
 |   if (extension) { | 
 |     if (!was_uninstalled) | 
 |       disabled_extensions_.Insert(extension); | 
 |     extensions_.Remove(extension_id); | 
 |   } else if (was_uninstalled) { | 
 |     // If the extension was uninstalled, make sure it's removed from the map of | 
 |     // disabled extensions. | 
 |     disabled_extensions_.Remove(extension_id); | 
 |   } else { | 
 |     // NOTE: This can currently happen if we receive multiple unload | 
 |     // notifications, e.g. setting incognito-enabled state for a | 
 |     // disabled extension (e.g., via sync).  See | 
 |     // http://code.google.com/p/chromium/issues/detail?id=50582 . | 
 |     NOTREACHED() << extension_id; | 
 |   } | 
 | } | 
 |  | 
 | base::Time InfoMap::GetInstallTime(const std::string& extension_id) const { | 
 |   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id); | 
 |   if (iter != extra_data_.end()) | 
 |     return iter->second.install_time; | 
 |   return base::Time(); | 
 | } | 
 |  | 
 | bool InfoMap::IsIncognitoEnabled(const std::string& extension_id) const { | 
 |   // Keep in sync with duplicate in extensions/browser/process_manager.cc. | 
 |   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id); | 
 |   if (iter != extra_data_.end()) | 
 |     return iter->second.incognito_enabled; | 
 |   return false; | 
 | } | 
 |  | 
 | bool InfoMap::CanCrossIncognito(const Extension* extension) const { | 
 |   // This is duplicated from ExtensionService :(. | 
 |   return IsIncognitoEnabled(extension->id()) && | 
 |          !IncognitoInfo::IsSplitMode(extension); | 
 | } | 
 |  | 
 | void InfoMap::RegisterExtensionProcess(const std::string& extension_id, | 
 |                                        int process_id, | 
 |                                        int site_instance_id) { | 
 |   if (!process_map_.Insert(extension_id, process_id, site_instance_id)) { | 
 |     NOTREACHED() << "Duplicate extension process registration for: " | 
 |                  << extension_id << "," << process_id << "."; | 
 |   } | 
 | } | 
 |  | 
 | void InfoMap::UnregisterExtensionProcess(const std::string& extension_id, | 
 |                                          int process_id, | 
 |                                          int site_instance_id) { | 
 |   if (!process_map_.Remove(extension_id, process_id, site_instance_id)) { | 
 |     NOTREACHED() << "Unknown extension process registration for: " | 
 |                  << extension_id << "," << process_id << "."; | 
 |   } | 
 | } | 
 |  | 
 | void InfoMap::UnregisterAllExtensionsInProcess(int process_id) { | 
 |   process_map_.RemoveAllFromProcess(process_id); | 
 | } | 
 |  | 
 | void InfoMap::GetExtensionsWithAPIPermissionForSecurityOrigin( | 
 |     const GURL& origin, | 
 |     int process_id, | 
 |     APIPermission::ID permission, | 
 |     ExtensionSet* extensions) const { | 
 |   DCHECK(extensions); | 
 |  | 
 |   if (origin.SchemeIs(kExtensionScheme)) { | 
 |     const std::string& id = origin.host(); | 
 |     const Extension* extension = extensions_.GetByID(id); | 
 |     if (extension && | 
 |         extension->permissions_data()->HasAPIPermission(permission) && | 
 |         process_map_.Contains(id, process_id)) { | 
 |       extensions->Insert(extension); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   ExtensionSet::const_iterator i = extensions_.begin(); | 
 |   for (; i != extensions_.end(); ++i) { | 
 |     if ((*i)->web_extent().MatchesSecurityOrigin(origin) && | 
 |         process_map_.Contains((*i)->id(), process_id) && | 
 |         (*i)->permissions_data()->HasAPIPermission(permission)) { | 
 |       extensions->Insert(*i); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool InfoMap::SecurityOriginHasAPIPermission(const GURL& origin, | 
 |                                              int process_id, | 
 |                                              APIPermission::ID permission) | 
 |     const { | 
 |   ExtensionSet extensions; | 
 |   GetExtensionsWithAPIPermissionForSecurityOrigin( | 
 |       origin, process_id, permission, &extensions); | 
 |   return !extensions.is_empty(); | 
 | } | 
 |  | 
 | // This function is security sensitive. Bugs could cause problems that break | 
 | // restrictions on local file access or NaCl's validation caching. If you modify | 
 | // this function, please get a security review from a NaCl person. | 
 | bool InfoMap::MapUrlToLocalFilePath(const GURL& file_url, | 
 |                                     bool use_blocking_api, | 
 |                                     base::FilePath* file_path) { | 
 |   // Check that the URL is recognized by the extension system. | 
 |   const Extension* extension = extensions_.GetExtensionOrAppByURL(file_url); | 
 |   if (!extension) | 
 |     return false; | 
 |  | 
 |   // This is a short-cut which avoids calling a blocking file operation | 
 |   // (GetFilePath()), so that this can be called on the IO thread. It only | 
 |   // handles a subset of the urls. | 
 |   if (!use_blocking_api) { | 
 |     if (file_url.SchemeIs(extensions::kExtensionScheme)) { | 
 |       std::string path = file_url.path(); | 
 |       base::TrimString(path, "/", &path);  // Remove first slash | 
 |       *file_path = extension->path().AppendASCII(path); | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::string path = file_url.path(); | 
 |   ExtensionResource resource; | 
 |  | 
 |   if (SharedModuleInfo::IsImportedPath(path)) { | 
 |     // Check if this is a valid path that is imported for this extension. | 
 |     std::string new_extension_id; | 
 |     std::string new_relative_path; | 
 |     SharedModuleInfo::ParseImportedPath( | 
 |         path, &new_extension_id, &new_relative_path); | 
 |     const Extension* new_extension = extensions_.GetByID(new_extension_id); | 
 |     if (!new_extension) | 
 |       return false; | 
 |  | 
 |     if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) || | 
 |         !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     resource = new_extension->GetResource(new_relative_path); | 
 |   } else { | 
 |     // Check that the URL references a resource in the extension. | 
 |     resource = extension->GetResource(path); | 
 |   } | 
 |  | 
 |   if (resource.empty()) | 
 |     return false; | 
 |  | 
 |   // GetFilePath is a blocking function call. | 
 |   const base::FilePath resource_file_path = resource.GetFilePath(); | 
 |   if (resource_file_path.empty()) | 
 |     return false; | 
 |  | 
 |   *file_path = resource_file_path; | 
 |   return true; | 
 | } | 
 |  | 
 | QuotaService* InfoMap::GetQuotaService() { | 
 |   CheckOnValidThread(); | 
 |   if (!quota_service_) | 
 |     quota_service_.reset(new QuotaService()); | 
 |   return quota_service_.get(); | 
 | } | 
 |  | 
 | void InfoMap::SetSigninProcess(int process_id) { | 
 |   signin_process_id_ = process_id; | 
 | } | 
 |  | 
 | bool InfoMap::IsSigninProcess(int process_id) const { | 
 |   return process_id == signin_process_id_; | 
 | } | 
 |  | 
 | void InfoMap::SetNotificationsDisabled( | 
 |     const std::string& extension_id, | 
 |     bool notifications_disabled) { | 
 |   ExtraDataMap::iterator iter = extra_data_.find(extension_id); | 
 |   if (iter != extra_data_.end()) | 
 |     iter->second.notifications_disabled = notifications_disabled; | 
 | } | 
 |  | 
 | bool InfoMap::AreNotificationsDisabled( | 
 |     const std::string& extension_id) const { | 
 |   ExtraDataMap::const_iterator iter = extra_data_.find(extension_id); | 
 |   if (iter != extra_data_.end()) | 
 |     return iter->second.notifications_disabled; | 
 |   return false; | 
 | } | 
 |  | 
 | void InfoMap::SetContentVerifier(ContentVerifier* verifier) { | 
 |   content_verifier_ = verifier; | 
 | } | 
 |  | 
 | InfoMap::~InfoMap() { | 
 |   if (quota_service_) { | 
 |     BrowserThread::DeleteSoon( | 
 |         BrowserThread::IO, FROM_HERE, quota_service_.release()); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace extensions |