summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/feature_policy
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-12-10 16:19:40 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-12-10 16:01:50 +0000
commit51f6c2793adab2d864b3d2b360000ef8db1d3e92 (patch)
tree835b3b4446b012c75e80177cef9fbe6972cc7dbe /chromium/third_party/blink/renderer/core/feature_policy
parent6036726eb981b6c4b42047513b9d3f4ac865daac (diff)
downloadqtwebengine-chromium-51f6c2793adab2d864b3d2b360000ef8db1d3e92.tar.gz
BASELINE: Update Chromium to 71.0.3578.93
Change-Id: I6a32086c33670e1b033f8b10e6bf1fd4da1d105d Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/feature_policy')
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/BUILD.gn16
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/DEPS4
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/OWNERS1
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/README.md21
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/document_policy.h39
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/feature_policy.cc293
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/feature_policy.h95
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/feature_policy_fuzzer.cc26
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc542
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/iframe_policy.h56
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/policy.cc85
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/policy.h59
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/policy.idl16
-rw-r--r--chromium/third_party/blink/renderer/core/feature_policy/policy_test.cc170
14 files changed, 1423 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/BUILD.gn b/chromium/third_party/blink/renderer/core/feature_policy/BUILD.gn
new file mode 100644
index 00000000000..ff3dd58753f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2018 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.
+
+import("//third_party/blink/renderer/core/core.gni")
+
+blink_core_sources("feature_policy") {
+ sources = [
+ "document_policy.h",
+ "feature_policy.cc",
+ "feature_policy.h",
+ "iframe_policy.h",
+ "policy.cc",
+ "policy.h",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/DEPS b/chromium/third_party/blink/renderer/core/feature_policy/DEPS
new file mode 100644
index 00000000000..2b1b586d7c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ # Feature policy needs to construct url::Origin objects
+ "+url",
+]
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/OWNERS b/chromium/third_party/blink/renderer/core/feature_policy/OWNERS
new file mode 100644
index 00000000000..329373c8198
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/OWNERS
@@ -0,0 +1 @@
+file://third_party/blink/common/feature_policy/OWNERS
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/README.md b/chromium/third_party/blink/renderer/core/feature_policy/README.md
new file mode 100644
index 00000000000..c35a27cca0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/README.md
@@ -0,0 +1,21 @@
+This directory contains the renderer-specific portions of the [Feature
+Policy API](https://wicg.github.io/feature-policy/).
+
+This includes:
+
+* The parser for the HTTP Feature-Policy header and the iframe allow attribute.
+
+* Helpers for manipulating the parsed declarations.
+
+* Implementation of the `document.policy` and `iframe.policy` interfaces.
+
+
+## Other feature policy resources
+
+* The core feature policy algorithms can be found in `/common/feature\_policy/`.
+
+* The feature list enum is found in `/public/mojom/feature\_policy/`.
+
+* The recommended API for checking whether features should be enabled or not is
+Document::IsFeatureEnabled() (or SecurityContext::IsFeatureEnabled in a non-
+document context).
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/document_policy.h b/chromium/third_party/blink/renderer/core/feature_policy/document_policy.h
new file mode 100644
index 00000000000..3743ef9b32f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/document_policy.h
@@ -0,0 +1,39 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOCUMENT_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOCUMENT_POLICY_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/feature_policy/policy.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace blink {
+
+// DocumentPolicy inherits Policy. It represents the feature policy
+// introspection of a document.
+class CORE_EXPORT DocumentPolicy final : public Policy {
+ public:
+ // Create a new DocumentPolicy, which is associated with |document|.
+ explicit DocumentPolicy(Document* document) : document_(document) {}
+
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(document_);
+ ScriptWrappable::Trace(visitor);
+ }
+
+ protected:
+ const FeaturePolicy* GetPolicy() const override {
+ return document_->GetFeaturePolicy();
+ }
+ Document* GetDocument() const override { return document_; }
+
+ private:
+ Member<Document> document_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_DOCUMENT_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.cc
new file mode 100644
index 00000000000..40161ba7252
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -0,0 +1,293 @@
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/feature_policy/feature_policy.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram_macros.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/bit_vector.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "url/origin.h"
+
+namespace blink {
+
+ParsedFeaturePolicy ParseFeaturePolicyHeader(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> origin,
+ Vector<String>* messages) {
+ return ParseFeaturePolicy(policy, origin, nullptr, messages,
+ GetDefaultFeatureNameMap());
+}
+
+ParsedFeaturePolicy ParseFeaturePolicyAttribute(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages) {
+ return ParseFeaturePolicy(policy, self_origin, src_origin, messages,
+ GetDefaultFeatureNameMap());
+}
+
+ParsedFeaturePolicy ParseFeaturePolicy(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ const FeatureNameMap& feature_names) {
+ ParsedFeaturePolicy allowlists;
+ BitVector features_specified(
+ static_cast<int>(mojom::FeaturePolicyFeature::kMaxValue));
+
+ // RFC2616, section 4.2 specifies that headers appearing multiple times can be
+ // combined with a comma. Walk the header string, and parse each comma
+ // separated chunk as a separate header.
+ Vector<String> policy_items;
+ // policy_items = [ policy *( "," [ policy ] ) ]
+ policy.Split(',', policy_items);
+ for (const String& item : policy_items) {
+ Vector<String> entry_list;
+ // entry_list = [ entry *( ";" [ entry ] ) ]
+ item.Split(';', entry_list);
+ for (const String& entry : entry_list) {
+ // Split removes extra whitespaces by default
+ // "name value1 value2" or "name".
+ Vector<String> tokens;
+ entry.Split(' ', tokens);
+ // Empty policy. Skip.
+ if (tokens.IsEmpty())
+ continue;
+ if (!feature_names.Contains(tokens[0])) {
+ if (messages)
+ messages->push_back("Unrecognized feature: '" + tokens[0] + "'.");
+ continue;
+ }
+
+ mojom::FeaturePolicyFeature feature = feature_names.at(tokens[0]);
+ // If a policy has already been specified for the current feature, drop
+ // the new policy.
+ if (features_specified.QuickGet(static_cast<int>(feature)))
+ continue;
+
+ // Count the use of this feature policy.
+ if (!src_origin) {
+ UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.Header",
+ feature);
+ }
+
+ ParsedFeaturePolicyDeclaration allowlist;
+ allowlist.feature = feature;
+ features_specified.QuickSet(static_cast<int>(feature));
+ std::vector<url::Origin> origins;
+ // If a policy entry has no (optional) values (e,g,
+ // allow="feature_name1; feature_name2 value"), enable the feature for:
+ // a. |self_origin|, if we are parsing a header policy (i.e.,
+ // |src_origin| is null);
+ // b. |src_origin|, if we are parsing an allow attribute (i.e.,
+ // |src_origin| is not null), |src_origin| is not opaque; or
+ // c. the opaque origin of the frame, if |src_origin| is opaque.
+ if (tokens.size() == 1) {
+ if (!src_origin) {
+ origins.push_back(self_origin->ToUrlOrigin());
+ } else if (!src_origin->IsOpaque()) {
+ origins.push_back(src_origin->ToUrlOrigin());
+ } else {
+ allowlist.matches_opaque_src = true;
+ }
+ }
+
+ for (wtf_size_t i = 1; i < tokens.size(); i++) {
+ if (!tokens[i].ContainsOnlyASCII()) {
+ messages->push_back("Non-ASCII characters in origin.");
+ continue;
+ }
+ if (EqualIgnoringASCIICase(tokens[i], "'self'")) {
+ origins.push_back(self_origin->ToUrlOrigin());
+ } else if (src_origin && EqualIgnoringASCIICase(tokens[i], "'src'")) {
+ // Only the iframe allow attribute can define |src_origin|.
+ // When parsing feature policy header, 'src' is disallowed and
+ // |src_origin| = nullptr.
+ // If the iframe will have an opaque origin (for example, if it is
+ // sandboxed, or has a data: URL), then 'src' needs to refer to the
+ // opaque origin of the frame, which is not known yet. In this case,
+ // the |matches_opaque_src| flag on the declaration is set, rather
+ // than adding an origin to the allowlist.
+ if (src_origin->IsOpaque()) {
+ allowlist.matches_opaque_src = true;
+ } else {
+ origins.push_back(src_origin->ToUrlOrigin());
+ }
+ } else if (EqualIgnoringASCIICase(tokens[i], "'none'")) {
+ continue;
+ } else if (tokens[i] == "*") {
+ allowlist.matches_all_origins = true;
+ break;
+ } else {
+ scoped_refptr<SecurityOrigin> target_origin =
+ SecurityOrigin::CreateFromString(tokens[i]);
+ if (!target_origin->IsOpaque())
+ origins.push_back(target_origin->ToUrlOrigin());
+ else if (messages)
+ messages->push_back("Unrecognized origin: '" + tokens[i] + "'.");
+ }
+ }
+ allowlist.origins = origins;
+ allowlists.push_back(allowlist);
+ }
+ }
+ return allowlists;
+}
+
+bool IsFeatureDeclared(mojom::FeaturePolicyFeature feature,
+ const ParsedFeaturePolicy& policy) {
+ return std::any_of(policy.begin(), policy.end(),
+ [feature](const auto& declaration) {
+ return declaration.feature == feature;
+ });
+}
+
+bool RemoveFeatureIfPresent(mojom::FeaturePolicyFeature feature,
+ ParsedFeaturePolicy& policy) {
+ auto new_end = std::remove_if(policy.begin(), policy.end(),
+ [feature](const auto& declaration) {
+ return declaration.feature == feature;
+ });
+ if (new_end == policy.end())
+ return false;
+ policy.erase(new_end, policy.end());
+ return true;
+}
+
+bool DisallowFeatureIfNotPresent(mojom::FeaturePolicyFeature feature,
+ ParsedFeaturePolicy& policy) {
+ if (IsFeatureDeclared(feature, policy))
+ return false;
+ ParsedFeaturePolicyDeclaration allowlist;
+ allowlist.feature = feature;
+ allowlist.matches_all_origins = false;
+ allowlist.matches_opaque_src = false;
+ policy.push_back(allowlist);
+ return true;
+}
+
+bool AllowFeatureEverywhereIfNotPresent(mojom::FeaturePolicyFeature feature,
+ ParsedFeaturePolicy& policy) {
+ if (IsFeatureDeclared(feature, policy))
+ return false;
+ ParsedFeaturePolicyDeclaration allowlist;
+ allowlist.feature = feature;
+ allowlist.matches_all_origins = true;
+ allowlist.matches_opaque_src = true;
+ policy.push_back(allowlist);
+ return true;
+}
+
+void DisallowFeature(mojom::FeaturePolicyFeature feature,
+ ParsedFeaturePolicy& policy) {
+ RemoveFeatureIfPresent(feature, policy);
+ DisallowFeatureIfNotPresent(feature, policy);
+}
+
+void AllowFeatureEverywhere(mojom::FeaturePolicyFeature feature,
+ ParsedFeaturePolicy& policy) {
+ RemoveFeatureIfPresent(feature, policy);
+ AllowFeatureEverywhereIfNotPresent(feature, policy);
+}
+
+// This method defines the feature names which will be recognized by the parser
+// for the Feature-Policy HTTP header and the <iframe> "allow" attribute, as
+// well as the features which will be recognized by the document or iframe
+// policy object.
+//
+// Features which are implemented behind a flag should generally also have the
+// same flag controlling whether they are in this map. Note that features which
+// are shipping as part of an origin trial should add their feature names to
+// this map unconditionally, as the trial token could be added after the HTTP
+// header needs to be parsed. This also means that top-level documents which
+// simply want to embed another page which uses an origin trial feature, without
+// using the feature themselves, can use feature policy to allow use of the
+// feature in subframes. (The framed document will still require a valid origin
+// trial token to use the feature in this scenario.)
+const FeatureNameMap& GetDefaultFeatureNameMap() {
+ DEFINE_STATIC_LOCAL(FeatureNameMap, default_feature_name_map, ());
+ if (default_feature_name_map.IsEmpty()) {
+ default_feature_name_map.Set("autoplay",
+ mojom::FeaturePolicyFeature::kAutoplay);
+ default_feature_name_map.Set("camera",
+ mojom::FeaturePolicyFeature::kCamera);
+ default_feature_name_map.Set("encrypted-media",
+ mojom::FeaturePolicyFeature::kEncryptedMedia);
+ default_feature_name_map.Set("fullscreen",
+ mojom::FeaturePolicyFeature::kFullscreen);
+ default_feature_name_map.Set("geolocation",
+ mojom::FeaturePolicyFeature::kGeolocation);
+ default_feature_name_map.Set("microphone",
+ mojom::FeaturePolicyFeature::kMicrophone);
+ default_feature_name_map.Set("midi",
+ mojom::FeaturePolicyFeature::kMidiFeature);
+ default_feature_name_map.Set("speaker",
+ mojom::FeaturePolicyFeature::kSpeaker);
+ default_feature_name_map.Set("sync-xhr",
+ mojom::FeaturePolicyFeature::kSyncXHR);
+ // Under origin trial: Should be made conditional on WebVR and WebXR
+ // runtime flags once it is out of trial.
+ default_feature_name_map.Set("vr", mojom::FeaturePolicyFeature::kWebVr);
+ if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) {
+ default_feature_name_map.Set("animations",
+ mojom::FeaturePolicyFeature::kAnimations);
+ default_feature_name_map.Set("document-write",
+ mojom::FeaturePolicyFeature::kDocumentWrite);
+ default_feature_name_map.Set(
+ "image-compression", mojom::FeaturePolicyFeature::kImageCompression);
+ default_feature_name_map.Set("lazyload",
+ mojom::FeaturePolicyFeature::kLazyLoad);
+ default_feature_name_map.Set(
+ "legacy-image-formats",
+ mojom::FeaturePolicyFeature::kLegacyImageFormats);
+ default_feature_name_map.Set(
+ "max-downscaling-image",
+ mojom::FeaturePolicyFeature::kMaxDownscalingImage);
+ default_feature_name_map.Set("unsized-media",
+ mojom::FeaturePolicyFeature::kUnsizedMedia);
+ default_feature_name_map.Set(
+ "vertical-scroll", mojom::FeaturePolicyFeature::kVerticalScroll);
+ default_feature_name_map.Set("sync-script",
+ mojom::FeaturePolicyFeature::kSyncScript);
+ }
+ if (RuntimeEnabledFeatures::PaymentRequestEnabled()) {
+ default_feature_name_map.Set("payment",
+ mojom::FeaturePolicyFeature::kPayment);
+ }
+ if (RuntimeEnabledFeatures::PictureInPictureAPIEnabled()) {
+ default_feature_name_map.Set(
+ "picture-in-picture", mojom::FeaturePolicyFeature::kPictureInPicture);
+ }
+ if (RuntimeEnabledFeatures::SensorEnabled()) {
+ default_feature_name_map.Set("accelerometer",
+ mojom::FeaturePolicyFeature::kAccelerometer);
+ default_feature_name_map.Set(
+ "ambient-light-sensor",
+ mojom::FeaturePolicyFeature::kAmbientLightSensor);
+ default_feature_name_map.Set("gyroscope",
+ mojom::FeaturePolicyFeature::kGyroscope);
+ default_feature_name_map.Set("magnetometer",
+ mojom::FeaturePolicyFeature::kMagnetometer);
+ }
+ if (RuntimeEnabledFeatures::WebUSBEnabled()) {
+ default_feature_name_map.Set("usb", mojom::FeaturePolicyFeature::kUsb);
+ }
+ }
+ return default_feature_name_map;
+}
+
+const String& GetNameForFeature(mojom::FeaturePolicyFeature feature) {
+ for (const auto& entry : GetDefaultFeatureNameMap()) {
+ if (entry.value == feature)
+ return entry.key;
+ }
+ return g_empty_string;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.h b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.h
new file mode 100644
index 00000000000..b0b71c79165
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy.h
@@ -0,0 +1,95 @@
+// Copyright 2016 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_FEATURE_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_FEATURE_POLICY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <memory>
+
+namespace blink {
+
+// Returns a map between feature name (string) and mojom::FeaturePolicyFeature
+// (enum).
+typedef HashMap<String, mojom::FeaturePolicyFeature> FeatureNameMap;
+CORE_EXPORT const FeatureNameMap& GetDefaultFeatureNameMap();
+
+// Converts a header policy string into a vector of allowlists, one for each
+// feature specified. Unrecognized features are filtered out. If |messages|
+// is not null, then any message in the input will cause a warning message to be
+// appended to it.
+// Example of a feature policy string:
+// "vibrate a.com b.com; fullscreen 'none'; payment 'self', payment *".
+CORE_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicyHeader(const String& policy,
+ scoped_refptr<const SecurityOrigin>,
+ Vector<String>* messages);
+
+// Converts a container policy string into a vector of allowlists, given self
+// and src origins provided, one for each feature specified. Unrecognized
+// features are filtered out. If |messages| is not null, then any message in the
+// input will cause as warning message to be appended to it.
+// Example of a feature policy string:
+// "vibrate a.com 'src'; fullscreen 'none'; payment 'self', payment *".
+CORE_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicyAttribute(const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages);
+
+// Converts a feature policy string into a vector of allowlists (see comments
+// above), with an explicit FeatureNameMap. This algorithm is called by both
+// header policy parsing and container policy parsing. |self_origin| and
+// |src_origin| are both nullable.
+CORE_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicy(const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ const FeatureNameMap& feature_names);
+
+// Returns true iff any declaration in the policy is for the given feature.
+CORE_EXPORT bool IsFeatureDeclared(mojom::FeaturePolicyFeature,
+ const ParsedFeaturePolicy&);
+
+// Removes any declaration in the policy for the given feature. Returns true if
+// the policy was modified.
+CORE_EXPORT bool RemoveFeatureIfPresent(mojom::FeaturePolicyFeature,
+ ParsedFeaturePolicy&);
+
+// If no declaration in the policy exists already for the feature, adds a
+// declaration which disallows the feature in all origins. Returns true if the
+// policy was modified.
+CORE_EXPORT bool DisallowFeatureIfNotPresent(mojom::FeaturePolicyFeature,
+ ParsedFeaturePolicy&);
+
+// If no declaration in the policy exists already for the feature, adds a
+// declaration which allows the feature in all origins. Returns true if the
+// policy was modified.
+CORE_EXPORT bool AllowFeatureEverywhereIfNotPresent(mojom::FeaturePolicyFeature,
+ ParsedFeaturePolicy&);
+
+// Replaces any existing declarations in the policy for the given feature with
+// a declaration which disallows the feature in all origins.
+CORE_EXPORT void DisallowFeature(mojom::FeaturePolicyFeature,
+ ParsedFeaturePolicy&);
+
+// Replaces any existing declarations in the policy for the given feature with
+// a declaration which allows the feature in all origins.
+CORE_EXPORT void AllowFeatureEverywhere(mojom::FeaturePolicyFeature,
+ ParsedFeaturePolicy&);
+
+CORE_EXPORT const String& GetNameForFeature(mojom::FeaturePolicyFeature);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_FEATURE_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_fuzzer.cc b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_fuzzer.cc
new file mode 100644
index 00000000000..89610c88b14
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_fuzzer.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 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 "third_party/blink/renderer/core/feature_policy/feature_policy.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static blink::BlinkFuzzerTestSupport test_support =
+ blink::BlinkFuzzerTestSupport();
+ WTF::Vector<WTF::String> messages;
+ // TODO(csharrison): Be smarter about parsing this origin for performance.
+ scoped_refptr<const blink::SecurityOrigin> origin =
+ blink::SecurityOrigin::CreateFromString("https://example.com/");
+ blink::ParseFeaturePolicyHeader(WTF::String(data, size), origin.get(),
+ &messages);
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
new file mode 100644
index 00000000000..b9c847ce617
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
@@ -0,0 +1,542 @@
+// Copyright 2016 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 "third_party/blink/renderer/core/feature_policy/feature_policy.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+// Origin strings used for tests
+#define ORIGIN_A "https://example.com/"
+#define ORIGIN_B "https://example.net/"
+#define ORIGIN_C "https://example.org/"
+
+class GURL;
+
+namespace blink {
+
+namespace {
+
+const char* const kValidPolicies[] = {
+ "", // An empty policy.
+ " ", // An empty policy.
+ ";;", // Empty policies.
+ ",,", // Empty policies.
+ " ; ;", // Empty policies.
+ " , ,", // Empty policies.
+ ",;,", // Empty policies.
+ "geolocation 'none'",
+ "geolocation 'self'",
+ "geolocation 'src'", // Only valid for iframe allow attribute.
+ "geolocation", // Only valid for iframe allow attribute.
+ "geolocation; fullscreen; payment",
+ "geolocation *",
+ "geolocation " ORIGIN_A "",
+ "geolocation " ORIGIN_B "",
+ "geolocation " ORIGIN_A " " ORIGIN_B "",
+ "geolocation 'none' " ORIGIN_A " " ORIGIN_B "",
+ "geolocation " ORIGIN_A " 'none' " ORIGIN_B "",
+ "geolocation 'none' 'none' 'none'",
+ "geolocation " ORIGIN_A " *",
+ "fullscreen " ORIGIN_A "; payment 'self'",
+ "fullscreen " ORIGIN_A "; payment *, geolocation 'self'"};
+
+const char* const kInvalidPolicies[] = {
+ "badfeaturename",
+ "badfeaturename 'self'",
+ "1.0",
+ "geolocation data://badorigin",
+ "geolocation https://bad;origin",
+ "geolocation https:/bad,origin",
+ "geolocation https://example.com, https://a.com",
+ "geolocation *, payment data://badorigin",
+ "geolocation ws://xn--fd\xbcwsw3taaaaaBaa333aBBBBBBJBBJBBBt"};
+
+} // namespace
+
+class FeaturePolicyParserTest : public testing::Test {
+ protected:
+ FeaturePolicyParserTest() = default;
+
+ ~FeaturePolicyParserTest() override = default;
+
+ scoped_refptr<const SecurityOrigin> origin_a_ =
+ SecurityOrigin::CreateFromString(ORIGIN_A);
+ scoped_refptr<const SecurityOrigin> origin_b_ =
+ SecurityOrigin::CreateFromString(ORIGIN_B);
+ scoped_refptr<const SecurityOrigin> origin_c_ =
+ SecurityOrigin::CreateFromString(ORIGIN_C);
+
+ url::Origin expected_url_origin_a_ = url::Origin::Create(GURL(ORIGIN_A));
+ url::Origin expected_url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
+ url::Origin expected_url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
+
+ const FeatureNameMap test_feature_name_map = {
+ {"fullscreen", blink::mojom::FeaturePolicyFeature::kFullscreen},
+ {"payment", blink::mojom::FeaturePolicyFeature::kPayment},
+ {"geolocation", blink::mojom::FeaturePolicyFeature::kGeolocation}};
+};
+
+TEST_F(FeaturePolicyParserTest, ParseValidPolicy) {
+ Vector<String> messages;
+ for (const char* policy_string : kValidPolicies) {
+ messages.clear();
+ ParseFeaturePolicy(policy_string, origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(0UL, messages.size());
+ }
+}
+
+TEST_F(FeaturePolicyParserTest, ParseInvalidPolicy) {
+ Vector<String> messages;
+ for (const char* policy_string : kInvalidPolicies) {
+ messages.clear();
+ ParseFeaturePolicy(policy_string, origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_NE(0UL, messages.size());
+ }
+}
+
+TEST_F(FeaturePolicyParserTest, PolicyParsedCorrectly) {
+ Vector<String> messages;
+
+ // Empty policy.
+ ParsedFeaturePolicy parsed_policy = ParseFeaturePolicy(
+ "", origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(0UL, parsed_policy.size());
+
+ // Simple policy with 'self'.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation 'self'", origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Simple policy with *.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation *", origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Complicated policy.
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation *; "
+ "fullscreen https://example.net https://example.org; "
+ "payment 'self'",
+ origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[1].origins[1].IsSameOriginWith(expected_url_origin_c_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Multiple policies.
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation * https://example.net; "
+ "fullscreen https://example.net none https://example.org,"
+ "payment 'self' badorigin",
+ origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[1].origins[1].IsSameOriginWith(expected_url_origin_c_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Header policies with no optional origin lists.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation;fullscreen;payment", origin_a_.get(),
+ nullptr, &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+}
+
+TEST_F(FeaturePolicyParserTest, PolicyParsedCorrectlyForOpaqueOrigins) {
+ Vector<String> messages;
+
+ scoped_refptr<SecurityOrigin> opaque_origin =
+ SecurityOrigin::CreateUniqueOpaque();
+
+ // Empty policy.
+ ParsedFeaturePolicy parsed_policy =
+ ParseFeaturePolicy("", origin_a_.get(), opaque_origin.get(), &messages,
+ test_feature_name_map);
+ EXPECT_EQ(0UL, parsed_policy.size());
+
+ // Simple policy.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation", origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Simple policy with 'src'.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation 'src'", origin_a_.get(),
+ opaque_origin.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Simple policy with *.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation *", origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Policy with explicit origins
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation https://example.net https://example.org", origin_a_.get(),
+ opaque_origin.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[0].origins[1].IsSameOriginWith(expected_url_origin_c_));
+
+ // Policy with multiple origins, including 'src'.
+ parsed_policy = ParseFeaturePolicy("geolocation https://example.net 'src'",
+ origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_b_));
+}
+
+// Test histogram counting the use of feature policies in header.
+TEST_F(FeaturePolicyParserTest, HeaderHistogram) {
+ const char* histogram_name = "Blink.UseCounter.FeaturePolicy.Header";
+ HistogramTester tester;
+ Vector<String> messages;
+
+ ParseFeaturePolicy("payment; fullscreen", origin_a_.get(), nullptr, &messages,
+ test_feature_name_map);
+ tester.ExpectTotalCount(histogram_name, 2);
+ tester.ExpectBucketCount(
+ histogram_name,
+ static_cast<int>(blink::mojom::FeaturePolicyFeature::kPayment), 1);
+ tester.ExpectBucketCount(
+ histogram_name,
+ static_cast<int>(blink::mojom::FeaturePolicyFeature::kFullscreen), 1);
+}
+
+// Test counting the use of each feature policy only once per header.
+TEST_F(FeaturePolicyParserTest, HistogramMultiple) {
+ const char* histogram_name = "Blink.UseCounter.FeaturePolicy.Header";
+ HistogramTester tester;
+ Vector<String> messages;
+
+ // If the same feature is listed multiple times, it should only be counted
+ // once.
+ ParseFeaturePolicy("geolocation 'self'; payment; geolocation *",
+ origin_a_.get(), nullptr, &messages,
+ test_feature_name_map);
+ ParseFeaturePolicy("fullscreen 'self', fullscreen *", origin_a_.get(),
+ nullptr, &messages, test_feature_name_map);
+ tester.ExpectTotalCount(histogram_name, 3);
+ tester.ExpectBucketCount(
+ histogram_name,
+ static_cast<int>(blink::mojom::FeaturePolicyFeature::kGeolocation), 1);
+ tester.ExpectBucketCount(
+ histogram_name,
+ static_cast<int>(blink::mojom::FeaturePolicyFeature::kFullscreen), 1);
+}
+
+// Test policy mutation methods
+class FeaturePolicyMutationTest : public testing::Test {
+ protected:
+ FeaturePolicyMutationTest() = default;
+
+ ~FeaturePolicyMutationTest() override = default;
+
+ url::Origin url_origin_a_ = url::Origin::Create(GURL(ORIGIN_A));
+ url::Origin url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
+ url::Origin url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
+
+ // Returns true if the policy contains a declaration for the feature which
+ // allows it in all origins.
+ bool IsFeatureAllowedEverywhere(mojom::FeaturePolicyFeature feature,
+ const ParsedFeaturePolicy& policy) {
+ const auto& result = std::find_if(policy.begin(), policy.end(),
+ [feature](const auto& declaration) {
+ return declaration.feature == feature;
+ });
+ if (result == policy.end())
+ return false;
+
+ return result->feature == feature && result->matches_all_origins &&
+ result->matches_opaque_src && result->origins.empty();
+ }
+
+ // Returns true if the policy contains a declaration for the feature which
+ // disallows it in all origins.
+ bool IsFeatureDisallowedEverywhere(mojom::FeaturePolicyFeature feature,
+ const ParsedFeaturePolicy& policy) {
+ const auto& result = std::find_if(policy.begin(), policy.end(),
+ [feature](const auto& declaration) {
+ return declaration.feature == feature;
+ });
+ if (result == policy.end())
+ return false;
+
+ return result->feature == feature && !result->matches_all_origins &&
+ !result->matches_opaque_src && result->origins.empty();
+ }
+
+ ParsedFeaturePolicy test_policy = {{mojom::FeaturePolicyFeature::kFullscreen,
+ false,
+ false,
+ {url_origin_a_, url_origin_b_}},
+ {mojom::FeaturePolicyFeature::kGeolocation,
+ false,
+ false,
+ {url_origin_a_}}};
+ ParsedFeaturePolicy empty_policy = {};
+};
+TEST_F(FeaturePolicyMutationTest, TestIsFeatureDeclared) {
+ EXPECT_TRUE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+ EXPECT_TRUE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kUsb, test_policy));
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kNotFound, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestIsFeatureDeclaredWithEmptyPolicy) {
+ EXPECT_FALSE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen,
+ empty_policy));
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kNotFound, empty_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestRemoveAbsentFeature) {
+ ASSERT_EQ(2UL, test_policy.size());
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kPayment, test_policy));
+ EXPECT_FALSE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kPayment,
+ test_policy));
+ ASSERT_EQ(2UL, test_policy.size());
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kPayment, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestRemoveFromEmptyPolicy) {
+ ASSERT_EQ(0UL, empty_policy.size());
+ EXPECT_FALSE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kPayment,
+ test_policy));
+ ASSERT_EQ(0UL, empty_policy.size());
+}
+
+TEST_F(FeaturePolicyMutationTest, TestRemoveFeatureIfPresent) {
+ ASSERT_EQ(2UL, test_policy.size());
+ EXPECT_TRUE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+ EXPECT_TRUE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kFullscreen,
+ test_policy));
+ EXPECT_EQ(1UL, test_policy.size());
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+
+ // Attempt to remove the feature again
+ EXPECT_FALSE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kFullscreen,
+ test_policy));
+ EXPECT_EQ(1UL, test_policy.size());
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestRemoveFeatureIfPresentOnSecondFeature) {
+ ASSERT_EQ(2UL, test_policy.size());
+ EXPECT_TRUE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+ EXPECT_TRUE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+ ASSERT_EQ(1UL, test_policy.size());
+ EXPECT_FALSE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+
+ // Attempt to remove the feature again
+ EXPECT_FALSE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+ EXPECT_EQ(1UL, test_policy.size());
+ EXPECT_FALSE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestRemoveAllFeatures) {
+ ASSERT_EQ(2UL, test_policy.size());
+ EXPECT_TRUE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kFullscreen,
+ test_policy));
+ EXPECT_TRUE(RemoveFeatureIfPresent(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+ EXPECT_EQ(0UL, test_policy.size());
+ EXPECT_FALSE(
+ IsFeatureDeclared(mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+ EXPECT_FALSE(IsFeatureDeclared(mojom::FeaturePolicyFeature::kGeolocation,
+ test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestDisallowIfNotPresent) {
+ ParsedFeaturePolicy copy = test_policy;
+ // Try to disallow a feature which already exists
+ EXPECT_FALSE(DisallowFeatureIfNotPresent(
+ mojom::FeaturePolicyFeature::kFullscreen, copy));
+ ASSERT_EQ(copy, test_policy);
+
+ // Disallow a new feature
+ EXPECT_TRUE(
+ DisallowFeatureIfNotPresent(mojom::FeaturePolicyFeature::kPayment, copy));
+ EXPECT_EQ(3UL, copy.size());
+ // Verify that the feature is, in fact, now disallowed everywhere
+ EXPECT_TRUE(IsFeatureDisallowedEverywhere(
+ mojom::FeaturePolicyFeature::kPayment, copy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestAllowEverywhereIfNotPresent) {
+ ParsedFeaturePolicy copy = test_policy;
+ // Try to allow a feature which already exists
+ EXPECT_FALSE(AllowFeatureEverywhereIfNotPresent(
+ mojom::FeaturePolicyFeature::kFullscreen, copy));
+ ASSERT_EQ(copy, test_policy);
+
+ // Allow a new feature
+ EXPECT_TRUE(AllowFeatureEverywhereIfNotPresent(
+ mojom::FeaturePolicyFeature::kPayment, copy));
+ EXPECT_EQ(3UL, copy.size());
+ // Verify that the feature is, in fact, allowed everywhere
+ EXPECT_TRUE(
+ IsFeatureAllowedEverywhere(mojom::FeaturePolicyFeature::kPayment, copy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestDisallowUnconditionally) {
+ // Try to disallow a feature which already exists
+ DisallowFeature(mojom::FeaturePolicyFeature::kFullscreen, test_policy);
+ // Should not have changed the number of declarations
+ EXPECT_EQ(2UL, test_policy.size());
+ // Verify that the feature is, in fact, now disallowed everywhere
+ EXPECT_TRUE(IsFeatureDisallowedEverywhere(
+ mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestDisallowNewFeatureUnconditionally) {
+ // Try to disallow a feature which does not yet exist
+ DisallowFeature(mojom::FeaturePolicyFeature::kPayment, test_policy);
+ // Should have added a new declaration
+ EXPECT_EQ(3UL, test_policy.size());
+ // Verify that the feature is, in fact, now disallowed everywhere
+ EXPECT_TRUE(IsFeatureDisallowedEverywhere(
+ mojom::FeaturePolicyFeature::kPayment, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestAllowUnconditionally) {
+ // Try to allow a feature which already exists
+ AllowFeatureEverywhere(mojom::FeaturePolicyFeature::kFullscreen, test_policy);
+ // Should not have changed the number of declarations
+ EXPECT_EQ(2UL, test_policy.size());
+ // Verify that the feature is, in fact, now allowed everywhere
+ EXPECT_TRUE(IsFeatureAllowedEverywhere(
+ mojom::FeaturePolicyFeature::kFullscreen, test_policy));
+}
+
+TEST_F(FeaturePolicyMutationTest, TestAllowNewFeatureUnconditionally) {
+ // Try to allow a feature which does not yet exist
+ AllowFeatureEverywhere(mojom::FeaturePolicyFeature::kPayment, test_policy);
+ // Should have added a new declaration
+ EXPECT_EQ(3UL, test_policy.size());
+ // Verify that the feature is, in fact, now allowed everywhere
+ EXPECT_TRUE(IsFeatureAllowedEverywhere(mojom::FeaturePolicyFeature::kPayment,
+ test_policy));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/iframe_policy.h b/chromium/third_party/blink/renderer/core/feature_policy/iframe_policy.h
new file mode 100644
index 00000000000..e53ec6427e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/iframe_policy.h
@@ -0,0 +1,56 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_IFRAME_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_IFRAME_POLICY_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/feature_policy/policy.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+// IFramePolicy inherits Policy. It represents the feature policy introspection
+// of an iframe contained in a document. It is tynthetic from the parent policy
+// and the iframe container policy (parsed from the allow attribute).
+class IFramePolicy final : public Policy {
+ public:
+ ~IFramePolicy() override = default;
+
+ // Create a new IFramePolicy, which is synthetic for a frame contained within
+ // a document.
+ IFramePolicy(Document* parent_document,
+ const ParsedFeaturePolicy& container_policy,
+ scoped_refptr<const SecurityOrigin> src_origin)
+ : parent_document_(parent_document) {
+ DCHECK(src_origin);
+ UpdateContainerPolicy(container_policy, src_origin);
+ }
+
+ void UpdateContainerPolicy(
+ const ParsedFeaturePolicy& container_policy,
+ scoped_refptr<const SecurityOrigin> src_origin) override {
+ policy_ = FeaturePolicy::CreateFromParentPolicy(
+ parent_document_->GetFeaturePolicy(), container_policy,
+ src_origin->ToUrlOrigin());
+ }
+
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(parent_document_);
+ Policy::Trace(visitor);
+ }
+
+ protected:
+ const FeaturePolicy* GetPolicy() const override { return policy_.get(); }
+ Document* GetDocument() const override { return parent_document_; }
+
+ private:
+ Member<Document> parent_document_;
+ std::unique_ptr<FeaturePolicy> policy_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_IFRAME_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/policy.cc b/chromium/third_party/blink/renderer/core/feature_policy/policy.cc
new file mode 100644
index 00000000000..474a4771017
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/policy.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 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 "third_party/blink/renderer/core/feature_policy/policy.h"
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/feature_policy/feature_policy.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+bool Policy::allowsFeature(const String& feature) const {
+ if (GetDefaultFeatureNameMap().Contains(feature)) {
+ return GetPolicy()->IsFeatureEnabled(
+ GetDefaultFeatureNameMap().at(feature));
+ }
+
+ AddWarningForUnrecognizedFeature(feature);
+ return false;
+}
+
+bool Policy::allowsFeature(const String& feature, const String& url) const {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(url);
+ if (!origin || origin->IsOpaque()) {
+ GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
+ kOtherMessageSource, kWarningMessageLevel,
+ "Invalid origin url for feature '" + feature + "': " + url + "."));
+ return false;
+ }
+
+ if (!GetDefaultFeatureNameMap().Contains(feature)) {
+ AddWarningForUnrecognizedFeature(feature);
+ return false;
+ }
+
+ return GetPolicy()->IsFeatureEnabledForOrigin(
+ GetDefaultFeatureNameMap().at(feature), origin->ToUrlOrigin());
+}
+
+Vector<String> Policy::allowedFeatures() const {
+ Vector<String> allowed_features;
+ for (const auto& entry : GetDefaultFeatureNameMap()) {
+ if (GetPolicy()->IsFeatureEnabled(entry.value))
+ allowed_features.push_back(entry.key);
+ }
+ return allowed_features;
+}
+
+Vector<String> Policy::getAllowlistForFeature(const String& feature) const {
+ if (GetDefaultFeatureNameMap().Contains(feature)) {
+ const FeaturePolicy::Allowlist allowlist =
+ GetPolicy()->GetAllowlistForFeature(
+ GetDefaultFeatureNameMap().at(feature));
+ if (allowlist.MatchesAll())
+ return Vector<String>({"*"});
+ Vector<String> result;
+ for (const auto& origin : allowlist.Origins()) {
+ result.push_back(WTF::String::FromUTF8(origin.Serialize().c_str()));
+ }
+ return result;
+ }
+
+ AddWarningForUnrecognizedFeature(feature);
+ return Vector<String>();
+}
+
+void Policy::AddWarningForUnrecognizedFeature(const String& feature) const {
+ GetDocument()->AddConsoleMessage(
+ ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
+ "Unrecognized feature: '" + feature + "'."));
+}
+
+void Policy::Trace(blink::Visitor* visitor) {
+ ScriptWrappable::Trace(visitor);
+}
+
+void Policy::UpdateContainerPolicy(
+ const ParsedFeaturePolicy& container_policy,
+ scoped_refptr<const SecurityOrigin> src_origin) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/policy.h b/chromium/third_party/blink/renderer/core/feature_policy/policy.h
new file mode 100644
index 00000000000..1f066f24c00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/policy.h
@@ -0,0 +1,59 @@
+// Copyright 2017 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
+
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace blink {
+
+class Document;
+class SecurityOrigin;
+
+// Policy provides an interface for feature policy introspection of a document
+// (DocumentPolicy) or an iframe (IFramePolicy).
+class CORE_EXPORT Policy : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ ~Policy() override = default;
+
+ // Implementation of methods of the policy interface:
+ // Returns whether or not the given feature is allowed on the origin of the
+ // document that owns the policy.
+ bool allowsFeature(const String& feature) const;
+ // Returns whether or not the given feature is allowed on the origin of the
+ // given URL.
+ bool allowsFeature(const String& feature, const String& url) const;
+ // Returns a list of feature names that are allowed on the self origin.
+ Vector<String> allowedFeatures() const;
+ // Returns a list of feature name that are allowed on the origin of the given
+ // URL.
+ Vector<String> getAllowlistForFeature(const String& url) const;
+
+ // Inform the Policy object when the container policy on its frame element has
+ // changed.
+ virtual void UpdateContainerPolicy(
+ const ParsedFeaturePolicy& container_policy = {},
+ scoped_refptr<const SecurityOrigin> src_origin = nullptr);
+
+ void Trace(blink::Visitor*) override;
+
+ protected:
+ virtual const FeaturePolicy* GetPolicy() const = 0;
+ // Get the containing document.
+ virtual Document* GetDocument() const = 0;
+
+ private:
+ // Add console message to the containing document.
+ void AddWarningForUnrecognizedFeature(const String& message) const;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FEATURE_POLICY_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/policy.idl b/chromium/third_party/blink/renderer/core/feature_policy/policy.idl
new file mode 100644
index 00000000000..d2d5ad3169d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/policy.idl
@@ -0,0 +1,16 @@
+// Copyright 2017 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.
+
+// https://wicg.github.io/feature-policy
+// TODO(iclelland): add spec for JS exposure in the spec for Feature Policy.
+// Please refer to this doc for more details for now:
+// https://docs.google.com/a/chromium.org/document/d/1wvk3cXkblNnbkMcsKayseK-k0SMGiP9b9fQFgfpqQpc/edit?usp=sharing
+[
+ NoInterfaceObject,
+ OriginTrialEnabled=FeaturePolicyJavaScriptInterface
+] interface Policy {
+ [MeasureAs=FeaturePolicyJSAPI] boolean allowsFeature(DOMString feature, optional DOMString url);
+ [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> allowedFeatures();
+ [MeasureAs=FeaturePolicyJSAPI] sequence<DOMString> getAllowlistForFeature(DOMString feature);
+};
diff --git a/chromium/third_party/blink/renderer/core/feature_policy/policy_test.cc b/chromium/third_party/blink/renderer/core/feature_policy/policy_test.cc
new file mode 100644
index 00000000000..93d689f3e1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/feature_policy/policy_test.cc
@@ -0,0 +1,170 @@
+// Copyright 2017 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 "third_party/blink/renderer/core/feature_policy/document_policy.h"
+#include "third_party/blink/renderer/core/feature_policy/iframe_policy.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/feature_policy/feature_policy.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+namespace {
+constexpr char kSelfOrigin[] = "https://selforigin.com";
+constexpr char kOriginA[] = "https://example.com";
+constexpr char kOriginB[] = "https://example.net";
+} // namespace
+
+using testing::UnorderedElementsAre;
+
+class PolicyTest : public testing::Test {
+ public:
+ void SetUp() override {
+ document_ = Document::CreateForTest();
+ document_->SetSecurityOrigin(SecurityOrigin::CreateFromString(kSelfOrigin));
+ document_->ApplyFeaturePolicyFromHeader(
+ "fullscreen *; payment 'self'; midi 'none'; camera 'self' "
+ "https://example.com https://example.net");
+ }
+
+ Policy* GetPolicy() const { return policy_; }
+
+ protected:
+ Persistent<Document> document_;
+ Persistent<Policy> policy_;
+};
+
+class DocumentPolicyTest : public PolicyTest {
+ public:
+ void SetUp() override {
+ PolicyTest::SetUp();
+ policy_ = new DocumentPolicy(document_);
+ }
+};
+
+class IFramePolicyTest : public PolicyTest {
+ public:
+ void SetUp() override {
+ PolicyTest::SetUp();
+ policy_ = new IFramePolicy(document_, {},
+ SecurityOrigin::CreateFromString(kSelfOrigin));
+ }
+};
+
+TEST_F(DocumentPolicyTest, TestAllowsFeature) {
+ EXPECT_FALSE(GetPolicy()->allowsFeature("badfeature"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("midi"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("midi", kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("fullscreen"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("fullscreen", kOriginA));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("payment"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("payment", kOriginA));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("payment", kOriginB));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("camera"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("camera", kOriginA));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("camera", kOriginB));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("camera", "https://badorigin.com"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("geolocation", kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("sync-xhr"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("sync-xhr", kOriginA));
+}
+
+TEST_F(DocumentPolicyTest, TestGetAllowList) {
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("camera"),
+ UnorderedElementsAre(kSelfOrigin, kOriginA, kOriginB));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("payment"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("geolocation"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("fullscreen"),
+ UnorderedElementsAre("*"));
+ EXPECT_TRUE(GetPolicy()->getAllowlistForFeature("badfeature").IsEmpty());
+ EXPECT_TRUE(GetPolicy()->getAllowlistForFeature("midi").IsEmpty());
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("sync-xhr"),
+ UnorderedElementsAre("*"));
+}
+
+TEST_F(DocumentPolicyTest, TestAllowedFeatures) {
+ Vector<String> allowed_features = GetPolicy()->allowedFeatures();
+ EXPECT_TRUE(allowed_features.Contains("fullscreen"));
+ EXPECT_TRUE(allowed_features.Contains("payment"));
+ EXPECT_TRUE(allowed_features.Contains("camera"));
+ // "geolocation" has default policy as allowed on self origin.
+ EXPECT_TRUE(allowed_features.Contains("geolocation"));
+ EXPECT_FALSE(allowed_features.Contains("badfeature"));
+ EXPECT_FALSE(allowed_features.Contains("midi"));
+ // "sync-xhr" is allowed on all origins
+ EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
+}
+
+TEST_F(IFramePolicyTest, TestAllowsFeature) {
+ EXPECT_FALSE(GetPolicy()->allowsFeature("badfeature"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("midi"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("midi", kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("fullscreen"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("fullscreen", kOriginA));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("fullscreen", kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("payment"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("payment", kOriginA));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("payment", kOriginB));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("camera"));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("camera", kOriginA));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("camera", kOriginB));
+ EXPECT_FALSE(GetPolicy()->allowsFeature("camera", "https://badorigin.com"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("geolocation", kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("sync-xhr"));
+ EXPECT_TRUE(GetPolicy()->allowsFeature("sync-xhr", kOriginA));
+}
+
+TEST_F(IFramePolicyTest, TestGetAllowList) {
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("camera"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("payment"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("geolocation"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("fullscreen"),
+ UnorderedElementsAre(kSelfOrigin));
+ EXPECT_TRUE(GetPolicy()->getAllowlistForFeature("badfeature").IsEmpty());
+ EXPECT_TRUE(GetPolicy()->getAllowlistForFeature("midi").IsEmpty());
+ EXPECT_THAT(GetPolicy()->getAllowlistForFeature("sync-xhr"),
+ UnorderedElementsAre("*"));
+}
+
+TEST_F(IFramePolicyTest, TestAllowedFeatures) {
+ Vector<String> allowed_features = GetPolicy()->allowedFeatures();
+ EXPECT_TRUE(allowed_features.Contains("fullscreen"));
+ EXPECT_TRUE(allowed_features.Contains("payment"));
+ EXPECT_TRUE(allowed_features.Contains("camera"));
+ // "geolocation" has default policy as allowed on self origin.
+ EXPECT_TRUE(allowed_features.Contains("geolocation"));
+ EXPECT_FALSE(allowed_features.Contains("badfeature"));
+ EXPECT_FALSE(allowed_features.Contains("midi"));
+ // "sync-xhr" is allowed on all origins
+ EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
+}
+
+TEST_F(IFramePolicyTest, TestCombinedPolicy) {
+ ParsedFeaturePolicy container_policy = ParseFeaturePolicyAttribute(
+ "geolocation 'src'; payment 'none'; midi; camera 'src'",
+ SecurityOrigin::CreateFromString(kSelfOrigin),
+ SecurityOrigin::CreateFromString(kOriginA), nullptr);
+ GetPolicy()->UpdateContainerPolicy(
+ container_policy, SecurityOrigin::CreateFromString(kOriginA));
+ Vector<String> allowed_features = GetPolicy()->allowedFeatures();
+ EXPECT_TRUE(allowed_features.Contains("fullscreen"));
+ EXPECT_FALSE(allowed_features.Contains("payment"));
+ EXPECT_TRUE(allowed_features.Contains("geolocation"));
+ EXPECT_FALSE(allowed_features.Contains("midi"));
+ EXPECT_TRUE(allowed_features.Contains("camera"));
+ // "geolocation" has default policy as allowed on self origin.
+ EXPECT_FALSE(allowed_features.Contains("badfeature"));
+ // "sync-xhr" is still implicitly allowed on all origins
+ EXPECT_TRUE(allowed_features.Contains("sync-xhr"));
+}
+
+} // namespace blink