#include "extensions/renderer/feature_cache.h"
#include <algorithm>
#include "base/command_line.h"
#include "content/public/common/content_switches.h"
#include "extensions/common/context_data.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_api.h"
#include "extensions/common/features/feature_developer_mode_only.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/renderer_context_data.h"
namespace extensions {
FeatureCache::ExtensionFeatureData::ExtensionFeatureData() = default;
FeatureCache::ExtensionFeatureData::ExtensionFeatureData(
const ExtensionFeatureData&) = default;
FeatureCache::ExtensionFeatureData::~ExtensionFeatureData() = default;
FeatureCache::FeatureCache() = default;
FeatureCache::~FeatureCache() = default;
FeatureCache::FeatureNameVector FeatureCache::GetAvailableFeatures(
Feature::Context context_type,
const Extension* extension,
const GURL& url,
const ContextData& context_data) {
bool is_webui_or_untrusted_webui =
context_type == Feature::WEBUI_CONTEXT ||
context_type == Feature::WEBUI_UNTRUSTED_CONTEXT;
DCHECK_NE(is_webui_or_untrusted_webui, !!extension)
<< "WebUI contexts shouldn't have extensions.";
DCHECK_NE(Feature::WEB_PAGE_CONTEXT, context_type)
<< "FeatureCache shouldn't be used for web contexts.";
DCHECK_NE(Feature::UNSPECIFIED_CONTEXT, context_type)
<< "FeatureCache shouldn't be used for unspecified contexts.";
const ExtensionFeatureData& features = GetFeaturesFromCache(
context_type, extension, url.DeprecatedGetOriginAsURL(),
kRendererProfileId, context_data);
FeatureNameVector names;
names.reserve(features.available_features.size());
for (const Feature* feature : features.available_features) {
if (ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
*feature, extension, context_type, url,
CheckAliasStatus::NOT_ALLOWED, kRendererProfileId, context_data)) {
names.push_back(feature->name());
}
}
return names;
}
FeatureCache::FeatureNameVector
FeatureCache::GetDeveloperModeRestrictedFeatures(
Feature::Context context_type,
const Extension* extension,
const GURL& url,
const ContextData& context_data) {
const ExtensionFeatureData& features = GetFeaturesFromCache(
context_type, extension, url.DeprecatedGetOriginAsURL(),
kRendererProfileId, context_data);
FeatureNameVector names;
names.reserve(features.dev_mode_restricted_features.size());
for (const Feature* feature : features.dev_mode_restricted_features) {
names.push_back(feature->name());
}
return names;
}
void FeatureCache::InvalidateExtension(const ExtensionId& extension_id) {
for (auto iter = extension_cache_.begin(); iter != extension_cache_.end();) {
if (iter->first.first == extension_id)
iter = extension_cache_.erase(iter);
else
++iter;
}
}
const FeatureCache::ExtensionFeatureData& FeatureCache::GetFeaturesFromCache(
Feature::Context context_type,
const Extension* extension,
const GURL& origin,
int context_id,
const ContextData& context_data) {
if (context_type == Feature::WEBUI_CONTEXT ||
context_type == Feature::WEBUI_UNTRUSTED_CONTEXT) {
auto iter = webui_cache_.find(origin);
if (iter != webui_cache_.end())
return iter->second;
return webui_cache_
.emplace(origin, CreateCacheEntry(context_type, extension, origin,
context_id, context_data))
.first->second;
}
DCHECK(extension);
ExtensionCacheMapKey key(extension->id(), context_type);
auto iter = extension_cache_.find(key);
if (iter != extension_cache_.end())
return iter->second;
return extension_cache_
.emplace(key, CreateCacheEntry(context_type, extension, origin,
context_id, context_data))
.first->second;
}
FeatureCache::ExtensionFeatureData FeatureCache::CreateCacheEntry(
Feature::Context context_type,
const Extension* extension,
const GURL& origin,
int context_id,
const ContextData& context_data) {
ExtensionFeatureData features;
const FeatureProvider* api_feature_provider =
FeatureProvider::GetAPIFeatures();
GURL empty_url;
const bool should_use_url =
(context_type == Feature::WEBUI_CONTEXT ||
context_type == Feature::WEBUI_UNTRUSTED_CONTEXT);
const GURL& url_to_use = should_use_url ? origin : empty_url;
for (const auto& map_entry : api_feature_provider->GetAllFeatures()) {
const Feature* feature = map_entry.second.get();
if (feature->IsInternal())
continue;
if (api_feature_provider->GetParent(*feature) != nullptr)
continue;
if (map_entry.first == "test" &&
!base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestType)) {
continue;
}
if (!ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
*feature, extension, context_type, url_to_use,
CheckAliasStatus::NOT_ALLOWED, context_id, context_data)) {
if (feature
->IsAvailableToContextIgnoringDevMode(
extension, context_type, url_to_use,
Feature::GetCurrentPlatform(), context_id, context_data)
.is_available()) {
features.dev_mode_restricted_features.push_back(feature);
}
continue;
}
features.available_features.push_back(feature);
}
std::sort(
features.dev_mode_restricted_features.begin(),
features.dev_mode_restricted_features.end(),
[](const Feature* a, const Feature* b) { return a->name() < b->name(); });
std::sort(
features.available_features.begin(), features.available_features.end(),
[](const Feature* a, const Feature* b) { return a->name() < b->name(); });
DCHECK(std::unique(features.dev_mode_restricted_features.begin(),
features.dev_mode_restricted_features.end()) ==
features.dev_mode_restricted_features.end());
DCHECK(std::unique(features.available_features.begin(),
features.available_features.end()) ==
features.available_features.end());
return features;
}
}