File: feature_cache.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (188 lines) | stat: -rw-r--r-- 7,329 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/renderer/feature_cache.h"

#include "base/command_line.h"
#include "base/containers/map_util.h"
#include "base/ranges/algorithm.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_provider.h"
#include "extensions/renderer/dispatcher.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) {
    // Since we only cache based on extension id and context type, instead of
    // all attributes of a context (like URL), we need to double-check if the
    // feature is actually available to the context. This is still a win, since
    // we only perform this check on the (much smaller) set of features that
    // *may* be available, rather than all known features.
    // TODO(devlin): Optimize this - we should be able to tell if a feature may
    // change based on additional context attributes.
    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;
  }
}

void FeatureCache::InvalidateAllExtensions() {
  extension_cache_.clear();
}

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) {
    if (auto* data = base::FindOrNull(webui_cache_, origin)) {
      return *data;
    }
    return webui_cache_
        .emplace(origin, CreateCacheEntry(context_type, extension, origin,
                                          context_id, context_data))
        .first->second;
  }

  DCHECK(extension);
  ExtensionCacheMapKey key(extension->id(), context_type);
  if (auto* data = base::FindOrNull(extension_cache_, key)) {
    return *data;
  }
  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;
  // We ignore the URL if this is an extension context in order to maximize
  // cache hits. For WebUI and untrusted WebUI, we key on origin.
  // Note: Currently, we only ever have matches based on origin, so this is
  // okay. If this changes, we'll have to get more creative about our WebUI
  // caching.
  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& [name, feature] : api_feature_provider->GetAllFeatures()) {
    // Exclude internal APIs.
    if (feature->IsInternal())
      continue;

    // Exclude child features (like events or specific functions).
    // TODO(devlin): Optimize this - instead of skipping child features and then
    // checking IsAnyFeatureAvailableToContext() (which checks child features),
    // we should just check all features directly.
    if (api_feature_provider->GetParent(*feature) != nullptr)
      continue;

    // Skip chrome.test if this isn't a test.
    if (name == "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.get());
      }
      continue;
    }

    features.available_features.push_back(feature.get());
  }

  base::ranges::sort(features.dev_mode_restricted_features,
                     base::ranges::less{}, &Feature::name);
  base::ranges::sort(features.available_features, base::ranges::less{},
                     &Feature::name);
  DCHECK(base::ranges::unique(features.dev_mode_restricted_features) ==
         features.dev_mode_restricted_features.end());
  DCHECK(base::ranges::unique(features.available_features) ==
         features.available_features.end());

  return features;
}

}  // namespace extensions