// 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