1 // Use of this source code is governed by a BSD-style license that can be
2 // found in the LICENSE file.
3 
4 #include "third_party/blink/renderer/core/feature_policy/feature_policy_parser.h"
5 
6 #include <algorithm>
7 #include <utility>
8 
9 #include <bitset>
10 #include "base/metrics/histogram_macros.h"
11 #include "net/http/structured_headers.h"
12 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
13 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
14 #include "third_party/blink/renderer/core/frame/web_feature.h"
15 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
16 #include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
17 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
18 #include "third_party/blink/renderer/platform/json/json_values.h"
19 #include "third_party/blink/renderer/platform/network/http_parsers.h"
20 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
21 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
22 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
23 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
24 #include "url/origin.h"
25 
26 namespace blink {
27 namespace {
28 
29 class ParsedFeaturePolicies final
30     : public GarbageCollected<ParsedFeaturePolicies>,
31       public Supplement<ExecutionContext> {
32  public:
33   static const char kSupplementName[];
34 
From(ExecutionContext & context)35   static ParsedFeaturePolicies& From(ExecutionContext& context) {
36     ParsedFeaturePolicies* policies =
37         Supplement<ExecutionContext>::From<ParsedFeaturePolicies>(context);
38     if (!policies) {
39       policies = MakeGarbageCollected<ParsedFeaturePolicies>(context);
40       Supplement<ExecutionContext>::ProvideTo(context, policies);
41     }
42     return *policies;
43   }
44 
ParsedFeaturePolicies(ExecutionContext & context)45   explicit ParsedFeaturePolicies(ExecutionContext& context)
46       : Supplement<ExecutionContext>(context),
47         policies_(
48             static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) +
49             1) {}
50 
Observed(mojom::blink::FeaturePolicyFeature feature)51   bool Observed(mojom::blink::FeaturePolicyFeature feature) {
52     size_t feature_index = static_cast<size_t>(feature);
53     if (policies_[feature_index])
54       return true;
55     policies_[feature_index] = true;
56     return false;
57   }
58 
59  private:
60   // Tracks which feature policies have already been parsed, so as not to count
61   // them multiple times.
62   Vector<bool> policies_;
63 };
64 
65 const char ParsedFeaturePolicies::kSupplementName[] = "ParsedFeaturePolicies";
66 
67 class FeatureObserver {
68  public:
69   // Returns whether the feature has been observed before or not.
70   bool FeatureObserved(mojom::blink::FeaturePolicyFeature feature);
71 
72  private:
73   std::bitset<
74       static_cast<size_t>(mojom::blink::FeaturePolicyFeature::kMaxValue) + 1>
75       features_specified_;
76 };
77 
78 class ParsingContext {
79   STACK_ALLOCATED();
80 
81  public:
ParsingContext(PolicyParserMessageBuffer & logger,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,const FeatureNameMap & feature_names,ExecutionContext * execution_context)82   ParsingContext(PolicyParserMessageBuffer& logger,
83                  scoped_refptr<const SecurityOrigin> self_origin,
84                  scoped_refptr<const SecurityOrigin> src_origin,
85                  const FeatureNameMap& feature_names,
86                  ExecutionContext* execution_context)
87       : logger_(logger),
88         self_origin_(self_origin),
89         src_origin_(src_origin),
90         feature_names_(feature_names),
91         execution_context_(execution_context) {}
92 
93   ~ParsingContext() = default;
94 
95   ParsedFeaturePolicy ParseFeaturePolicy(const String& policy);
96   ParsedFeaturePolicy ParsePermissionsPolicy(const String& policy);
97 
98  private:
99   // Following is the intermediate represetnation(IR) of feature policy.
100   // Parsing of syntax structures is done in this IR, but semantic checks, e.g.
101   // whether feature_name is valid, are not yet performed.
102   struct FeaturePolicyDeclarationNode {
103     String feature_name;
104     Vector<String> allowlist;
105   };
106   using FeaturePolicyNode = Vector<FeaturePolicyDeclarationNode>;
107 
108   ParsedFeaturePolicy ParseIR(const FeaturePolicyNode& root);
109   FeaturePolicyNode ParseFeaturePolicyToIR(const String& policy);
110   FeaturePolicyNode ParsePermissionsPolicyToIR(const String& policy);
111 
112   // normally 1 char = 1 byte
113   // max length to parse = 2^16 = 64 kB
114   static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16;
115 
116   base::Optional<ParsedFeaturePolicyDeclaration> ParseFeature(
117       const FeaturePolicyDeclarationNode&);
118 
119   struct ParsedAllowlist {
120     std::vector<url::Origin> allowed_origins;
121     bool matches_all_origins{false};
122     bool matches_opaque_src{false};
123 
ParsedAllowlistblink::__anon3cd072200111::ParsingContext::ParsedAllowlist124     ParsedAllowlist() : allowed_origins({}) {}
125   };
126 
127   base::Optional<mojom::blink::FeaturePolicyFeature> ParseFeatureName(
128       const String& feature_name);
129 
130   // Parse allowlist for feature.
131   ParsedAllowlist ParseAllowlist(const Vector<String>& origin_strings);
132 
133   void ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature);
134 
135   // This function should be called after Allowlist Histograms related flags
136   // have been captured.
137   void RecordAllowlistTypeUsage(size_t origin_count);
138   // The use of various allowlist types should only be recorded once per page.
139   // For simplicity, this recording assumes that the ParseHeader method is
140   // called once when creating a new document, and similarly the ParseAttribute
141   // method is called once for a frame. It is possible for multiple calls, but
142   // the additional complexity to guarantee only one record isn't warranted as
143   // yet.
144   void ReportAllowlistTypeUsage();
145 
146   PolicyParserMessageBuffer& logger_;
147   scoped_refptr<const SecurityOrigin> self_origin_;
148   scoped_refptr<const SecurityOrigin> src_origin_;
149   const FeatureNameMap& feature_names_;
150   ExecutionContext* execution_context_;
151 
152   // Flags for the types of items which can be used in allowlists.
153   bool allowlist_includes_star_ = false;
154   bool allowlist_includes_self_ = false;
155   bool allowlist_includes_src_ = false;
156   bool allowlist_includes_none_ = false;
157   bool allowlist_includes_origin_ = false;
158 
159   HashSet<FeaturePolicyAllowlistType> allowlist_types_used_;
160 
161   FeatureObserver feature_observer_;
162 };
163 
FeatureObserved(mojom::blink::FeaturePolicyFeature feature)164 bool FeatureObserver::FeatureObserved(
165     mojom::blink::FeaturePolicyFeature feature) {
166   if (features_specified_[static_cast<size_t>(feature)]) {
167     return true;
168   } else {
169     features_specified_.set(static_cast<size_t>(feature));
170     return false;
171   }
172 }
173 
ReportFeatureUsage(mojom::blink::FeaturePolicyFeature feature)174 void ParsingContext::ReportFeatureUsage(
175     mojom::blink::FeaturePolicyFeature feature) {
176   if (src_origin_) {
177     if (!execution_context_ ||
178         !ParsedFeaturePolicies::From(*execution_context_).Observed(feature)) {
179       UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.Allow",
180                                 feature);
181     }
182   } else {
183     UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.Header", feature);
184   }
185 }
186 
RecordAllowlistTypeUsage(size_t origin_count)187 void ParsingContext::RecordAllowlistTypeUsage(size_t origin_count) {
188   // Record the type of allowlist used.
189   if (origin_count == 0) {
190     allowlist_types_used_.insert(FeaturePolicyAllowlistType::kEmpty);
191   } else if (origin_count == 1) {
192     if (allowlist_includes_star_)
193       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kStar);
194     else if (allowlist_includes_self_)
195       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kSelf);
196     else if (allowlist_includes_src_)
197       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kSrc);
198     else if (allowlist_includes_none_)
199       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kNone);
200     else
201       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kOrigins);
202   } else {
203     if (allowlist_includes_origin_) {
204       if (allowlist_includes_star_ || allowlist_includes_none_ ||
205           allowlist_includes_src_ || allowlist_includes_self_)
206         allowlist_types_used_.insert(FeaturePolicyAllowlistType::kMixed);
207       else
208         allowlist_types_used_.insert(FeaturePolicyAllowlistType::kOrigins);
209     } else {
210       allowlist_types_used_.insert(FeaturePolicyAllowlistType::kKeywordsOnly);
211     }
212   }
213   // Reset all flags.
214   allowlist_includes_star_ = false;
215   allowlist_includes_self_ = false;
216   allowlist_includes_src_ = false;
217   allowlist_includes_none_ = false;
218   allowlist_includes_origin_ = false;
219 }
220 
ReportAllowlistTypeUsage()221 void ParsingContext::ReportAllowlistTypeUsage() {
222   for (const FeaturePolicyAllowlistType allowlist_type :
223        allowlist_types_used_) {
224     if (src_origin_) {
225       UMA_HISTOGRAM_ENUMERATION(
226           "Blink.UseCounter.FeaturePolicy.AttributeAllowlistType",
227           allowlist_type);
228     } else {
229       UMA_HISTOGRAM_ENUMERATION(
230           "Blink.UseCounter.FeaturePolicy.HeaderAllowlistType", allowlist_type);
231     }
232   }
233 }
234 
235 base::Optional<mojom::blink::FeaturePolicyFeature>
ParseFeatureName(const String & feature_name)236 ParsingContext::ParseFeatureName(const String& feature_name) {
237   DCHECK(!feature_name.IsEmpty());
238   if (!feature_names_.Contains(feature_name)) {
239     logger_.Warn("Unrecognized feature: '" + feature_name + "'.");
240     return base::nullopt;
241   }
242   if (DisabledByOriginTrial(feature_name, execution_context_)) {
243     logger_.Warn("Origin trial controlled feature not enabled: '" +
244                  feature_name + "'.");
245     return base::nullopt;
246   }
247   mojom::blink::FeaturePolicyFeature feature = feature_names_.at(feature_name);
248 
249   return feature;
250 }
251 
ParseAllowlist(const Vector<String> & origin_strings)252 ParsingContext::ParsedAllowlist ParsingContext::ParseAllowlist(
253     const Vector<String>& origin_strings) {
254   ParsedAllowlist allowlist;
255   if (origin_strings.IsEmpty()) {
256     // If a policy entry has no listed origins (e.g. "feature_name1" in
257     // allow="feature_name1; feature_name2 value"), enable the feature for:
258     //     a. |self_origin|, if we are parsing a header policy (i.e.,
259     //       |src_origin| is null);
260     //     b. |src_origin|, if we are parsing an allow attribute (i.e.,
261     //       |src_origin| is not null), |src_origin| is not opaque; or
262     //     c. the opaque origin of the frame, if |src_origin| is opaque.
263     if (!src_origin_) {
264       allowlist.allowed_origins.push_back(self_origin_->ToUrlOrigin());
265     } else if (!src_origin_->IsOpaque()) {
266       allowlist.allowed_origins.push_back(src_origin_->ToUrlOrigin());
267     } else {
268       allowlist.matches_opaque_src = true;
269     }
270   } else {
271     for (const String& origin_string : origin_strings) {
272       DCHECK(!origin_string.IsEmpty());
273 
274       if (!origin_string.ContainsOnlyASCIIOrEmpty()) {
275         logger_.Warn("Non-ASCII characters in origin.");
276         continue;
277       }
278 
279       // Determine the target of the declaration. This may be a specific
280       // origin, either explicitly written, or one of the special keywords
281       // 'self' or 'src'. ('src' can only be used in the iframe allow
282       // attribute.)
283       url::Origin target_origin;
284 
285       // If the iframe will have an opaque origin (for example, if it is
286       // sandboxed, or has a data: URL), then 'src' needs to refer to the
287       // opaque origin of the frame, which is not known yet. In this case,
288       // the |matches_opaque_src| flag on the declaration is set, rather than
289       // adding an origin to the allowlist.
290       bool target_is_opaque = false;
291       bool target_is_all = false;
292 
293       // 'self' origin is used if the origin is exactly 'self'.
294       if (EqualIgnoringASCIICase(origin_string, "'self'")) {
295         allowlist_includes_self_ = true;
296         target_origin = self_origin_->ToUrlOrigin();
297       }
298       // 'src' origin is used if |src_origin| is available and the
299       // origin is a match for 'src'. |src_origin| is only set
300       // when parsing an iframe allow attribute.
301       else if (src_origin_ && EqualIgnoringASCIICase(origin_string, "'src'")) {
302         allowlist_includes_src_ = true;
303         if (!src_origin_->IsOpaque()) {
304           target_origin = src_origin_->ToUrlOrigin();
305         } else {
306           target_is_opaque = true;
307         }
308       } else if (EqualIgnoringASCIICase(origin_string, "'none'")) {
309         allowlist_includes_none_ = true;
310         continue;
311       } else if (origin_string == "*") {
312         allowlist_includes_star_ = true;
313         target_is_all = true;
314       }
315       // Otherwise, parse the origin string and verify that the result is
316       // valid. Invalid strings will produce an opaque origin, which will
317       // result in an error message.
318       else {
319         scoped_refptr<SecurityOrigin> parsed_origin =
320             SecurityOrigin::CreateFromString(origin_string);
321         if (!parsed_origin->IsOpaque()) {
322           target_origin = parsed_origin->ToUrlOrigin();
323           allowlist_includes_origin_ = true;
324         } else {
325           logger_.Warn("Unrecognized origin: '" + origin_string + "'.");
326           continue;
327         }
328       }
329 
330       if (target_is_all) {
331         allowlist.matches_all_origins = true;
332         allowlist.matches_opaque_src = true;
333       } else if (target_is_opaque) {
334         allowlist.matches_opaque_src = true;
335       } else {
336         allowlist.allowed_origins.push_back(target_origin);
337       }
338     }
339   }
340 
341   // Size reduction: remove all items in the allowlist if target is all.
342   if (allowlist.matches_all_origins)
343     allowlist.allowed_origins.clear();
344 
345   // Sort |allowed_origins| in alphabetical order.
346   std::sort(allowlist.allowed_origins.begin(), allowlist.allowed_origins.end());
347 
348   RecordAllowlistTypeUsage(origin_strings.size());
349 
350   return allowlist;
351 }
352 
ParseFeature(const FeaturePolicyDeclarationNode & declaration_node)353 base::Optional<ParsedFeaturePolicyDeclaration> ParsingContext::ParseFeature(
354     const FeaturePolicyDeclarationNode& declaration_node) {
355   base::Optional<mojom::blink::FeaturePolicyFeature> feature =
356       ParseFeatureName(declaration_node.feature_name);
357   if (!feature)
358     return base::nullopt;
359 
360   ParsedAllowlist parsed_allowlist = ParseAllowlist(declaration_node.allowlist);
361 
362   // If same feature appeared more than once, only the first one counts.
363   if (feature_observer_.FeatureObserved(*feature))
364     return base::nullopt;
365 
366   ParsedFeaturePolicyDeclaration parsed_feature(*feature);
367   parsed_feature.allowed_origins = std::move(parsed_allowlist.allowed_origins);
368   parsed_feature.matches_all_origins = parsed_allowlist.matches_all_origins;
369   parsed_feature.matches_opaque_src = parsed_allowlist.matches_opaque_src;
370 
371   return parsed_feature;
372 }
373 
ParseFeaturePolicy(const String & policy)374 ParsedFeaturePolicy ParsingContext::ParseFeaturePolicy(const String& policy) {
375   return ParseIR(ParseFeaturePolicyToIR(policy));
376 }
377 
ParsePermissionsPolicy(const String & policy)378 ParsedFeaturePolicy ParsingContext::ParsePermissionsPolicy(
379     const String& policy) {
380   return ParseIR(ParsePermissionsPolicyToIR(policy));
381 }
382 
ParseIR(const ParsingContext::FeaturePolicyNode & root)383 ParsedFeaturePolicy ParsingContext::ParseIR(
384     const ParsingContext::FeaturePolicyNode& root) {
385   ParsedFeaturePolicy parsed_policy;
386   for (const ParsingContext::FeaturePolicyDeclarationNode& declaration_node :
387        root) {
388     base::Optional<ParsedFeaturePolicyDeclaration> parsed_feature =
389         ParseFeature(declaration_node);
390     if (parsed_feature) {
391       ReportFeatureUsage(parsed_feature->feature);
392       parsed_policy.push_back(*parsed_feature);
393     }
394   }
395   ReportAllowlistTypeUsage();
396   return parsed_policy;
397 }
398 
ParseFeaturePolicyToIR(const String & policy)399 ParsingContext::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
400     const String& policy) {
401   ParsingContext::FeaturePolicyNode root;
402 
403   if (policy.length() > MAX_LENGTH_PARSE) {
404     logger_.Error("Feature policy declaration exceeds size limit(" +
405                   String::Number(policy.length()) + ">" +
406                   String::Number(MAX_LENGTH_PARSE) + ")");
407     return {};
408   }
409 
410   // RFC2616, section 4.2 specifies that headers appearing multiple times can be
411   // combined with a comma. Walk the header string, and parse each comma
412   // separated chunk as a separate header.
413   Vector<String> policy_items;
414   // policy_items = [ policy *( "," [ policy ] ) ]
415   policy.Split(',', policy_items);
416 
417   if (policy_items.size() > 1) {
418     UseCounter::Count(
419         execution_context_,
420         mojom::blink::WebFeature::kFeaturePolicyCommaSeparatedDeclarations);
421   }
422 
423   for (const String& item : policy_items) {
424     Vector<String> feature_entries;
425     // feature_entries = [ feature_entry *( ";" [ feature_entry ] ) ]
426     item.Split(';', feature_entries);
427 
428     if (feature_entries.size() > 1) {
429       UseCounter::Count(execution_context_,
430                         mojom::blink::WebFeature::
431                             kFeaturePolicySemicolonSeparatedDeclarations);
432     }
433 
434     for (const String& feature_entry : feature_entries) {
435       Vector<String> tokens;
436       feature_entry.Split(' ', tokens);
437 
438       if (tokens.IsEmpty())
439         continue;
440 
441       ParsingContext::FeaturePolicyDeclarationNode declaration_node;
442       // Break tokens into head & tail, where
443       // head = feature_name
444       // tail = allowlist
445       // After feature_name has been set, take tail of tokens vector by
446       // erasing the first element.
447       declaration_node.feature_name = std::move(tokens.front());
448       tokens.erase(tokens.begin());
449       declaration_node.allowlist = std::move(tokens);
450       root.push_back(declaration_node);
451     }
452   }
453 
454   return root;
455 }
456 
ParsePermissionsPolicyToIR(const String & policy)457 ParsingContext::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
458     const String& policy) {
459   if (policy.length() > MAX_LENGTH_PARSE) {
460     logger_.Error("Permissions policy declaration exceeds size limit(" +
461                   String::Number(policy.length()) + ">" +
462                   String::Number(MAX_LENGTH_PARSE) + ")");
463     return {};
464   }
465 
466   auto root = net::structured_headers::ParseDictionary(policy.Utf8());
467   if (!root) {
468     logger_.Error(
469         "Parse of permission policy failed because of errors reported by "
470         "strctured header parser.");
471     return {};
472   }
473 
474   ParsingContext::FeaturePolicyNode ir_root;
475   for (const auto& feature_entry : root.value()) {
476     const auto& key = feature_entry.first;
477     const char* feature_name = key.c_str();
478     const auto& value = feature_entry.second;
479 
480     if (!value.params.empty()) {
481       logger_.Warn(
482           String::Format("Feature %s's parameters are ignored.", feature_name));
483     }
484 
485     Vector<String> allowlist;
486     for (const auto& parameterized_item : value.member) {
487       if (!parameterized_item.params.empty()) {
488         logger_.Warn(String::Format("Feature %s's parameters are ignored.",
489                                     feature_name));
490       }
491 
492       String allowlist_item;
493       if (parameterized_item.item.is_token()) {
494         // All special keyword appears as token, i.e. self, src and *.
495         const std::string& token_value = parameterized_item.item.GetString();
496         if (token_value != "*" && token_value != "self") {
497           logger_.Warn(String::Format(
498               "Invalid allowlist item(%s) for feature %s. Allowlist item "
499               "must be *, self or quoted url.",
500               token_value.c_str(), feature_name));
501           continue;
502         }
503 
504         if (token_value == "*") {
505           allowlist_item = "*";
506         } else {
507           allowlist_item = String::Format("'%s'", token_value.c_str());
508         }
509       } else if (parameterized_item.item.is_string()) {
510         allowlist_item = parameterized_item.item.GetString().c_str();
511       } else {
512         logger_.Warn(
513             String::Format("Invalid allowlist item for feature %s. Allowlist "
514                            "item must be *, self, or quoted url.",
515                            feature_name));
516         continue;
517       }
518       if (!allowlist_item.IsEmpty())
519         allowlist.push_back(allowlist_item);
520     }
521 
522     if (allowlist.IsEmpty())
523       allowlist.push_back("'none'");
524 
525     ir_root.push_back(
526         ParsingContext::FeaturePolicyDeclarationNode{feature_name, allowlist});
527   }
528 
529   return ir_root;
530 }
531 
532 }  // namespace
533 
ParseHeader(const String & feature_policy_header,const String & permissions_policy_header,scoped_refptr<const SecurityOrigin> origin,PolicyParserMessageBuffer & feature_policy_logger,PolicyParserMessageBuffer & permissions_policy_logger,ExecutionContext * execution_context)534 ParsedFeaturePolicy FeaturePolicyParser::ParseHeader(
535     const String& feature_policy_header,
536     const String& permissions_policy_header,
537     scoped_refptr<const SecurityOrigin> origin,
538     PolicyParserMessageBuffer& feature_policy_logger,
539     PolicyParserMessageBuffer& permissions_policy_logger,
540     ExecutionContext* execution_context) {
541   ParsedFeaturePolicy permissions_policy =
542       ParsingContext(permissions_policy_logger, origin, nullptr,
543                      GetDefaultFeatureNameMap(), execution_context)
544           .ParsePermissionsPolicy(permissions_policy_header);
545   ParsedFeaturePolicy feature_policy =
546       ParsingContext(feature_policy_logger, origin, nullptr,
547                      GetDefaultFeatureNameMap(), execution_context)
548           .ParseFeaturePolicy(feature_policy_header);
549 
550   FeatureObserver observer;
551   for (const auto& policy_declaration : permissions_policy) {
552     bool feature_observed =
553         observer.FeatureObserved(policy_declaration.feature);
554     DCHECK(!feature_observed);
555   }
556   for (const auto& policy_declaration : feature_policy) {
557     if (!observer.FeatureObserved(policy_declaration.feature)) {
558       permissions_policy.push_back(policy_declaration);
559     } else {
560       feature_policy_logger.Warn(String::Format(
561           "Feature %s has been specified in both Feature-Policy and "
562           "Permissions-Policy header. Value defined in Permissions-Policy "
563           "header will be used.",
564           GetNameForFeature(policy_declaration.feature).Ascii().c_str()));
565     }
566   }
567 
568   return permissions_policy;
569 }
570 
ParseAttribute(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,ExecutionContext * execution_context)571 ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute(
572     const String& policy,
573     scoped_refptr<const SecurityOrigin> self_origin,
574     scoped_refptr<const SecurityOrigin> src_origin,
575     PolicyParserMessageBuffer& logger,
576     ExecutionContext* execution_context) {
577   return ParsingContext(logger, self_origin, src_origin,
578                         GetDefaultFeatureNameMap(), execution_context)
579       .ParseFeaturePolicy(policy);
580 }
581 
ParseFeaturePolicyForTest(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,const FeatureNameMap & feature_names,ExecutionContext * execution_context)582 ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest(
583     const String& policy,
584     scoped_refptr<const SecurityOrigin> self_origin,
585     scoped_refptr<const SecurityOrigin> src_origin,
586     PolicyParserMessageBuffer& logger,
587     const FeatureNameMap& feature_names,
588     ExecutionContext* execution_context) {
589   return ParsingContext(logger, self_origin, src_origin, feature_names,
590                         execution_context)
591       .ParseFeaturePolicy(policy);
592 }
593 
ParsePermissionsPolicyForTest(const String & policy,scoped_refptr<const SecurityOrigin> self_origin,scoped_refptr<const SecurityOrigin> src_origin,PolicyParserMessageBuffer & logger,const FeatureNameMap & feature_names,ExecutionContext * execution_context)594 ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest(
595     const String& policy,
596     scoped_refptr<const SecurityOrigin> self_origin,
597     scoped_refptr<const SecurityOrigin> src_origin,
598     PolicyParserMessageBuffer& logger,
599     const FeatureNameMap& feature_names,
600     ExecutionContext* execution_context) {
601   return ParsingContext(logger, self_origin, src_origin, feature_names,
602                         execution_context)
603       .ParsePermissionsPolicy(policy);
604 }
605 
IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,const ParsedFeaturePolicy & policy)606 bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,
607                        const ParsedFeaturePolicy& policy) {
608   return std::any_of(policy.begin(), policy.end(),
609                      [feature](const auto& declaration) {
610                        return declaration.feature == feature;
611                      });
612 }
613 
RemoveFeatureIfPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)614 bool RemoveFeatureIfPresent(mojom::blink::FeaturePolicyFeature feature,
615                             ParsedFeaturePolicy& policy) {
616   auto new_end = std::remove_if(policy.begin(), policy.end(),
617                                 [feature](const auto& declaration) {
618                                   return declaration.feature == feature;
619                                 });
620   if (new_end == policy.end())
621     return false;
622   policy.erase(new_end, policy.end());
623   return true;
624 }
625 
DisallowFeatureIfNotPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)626 bool DisallowFeatureIfNotPresent(mojom::blink::FeaturePolicyFeature feature,
627                                  ParsedFeaturePolicy& policy) {
628   if (IsFeatureDeclared(feature, policy))
629     return false;
630   ParsedFeaturePolicyDeclaration allowlist(feature);
631   policy.push_back(allowlist);
632   return true;
633 }
634 
AllowFeatureEverywhereIfNotPresent(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)635 bool AllowFeatureEverywhereIfNotPresent(
636     mojom::blink::FeaturePolicyFeature feature,
637     ParsedFeaturePolicy& policy) {
638   if (IsFeatureDeclared(feature, policy))
639     return false;
640   ParsedFeaturePolicyDeclaration allowlist(feature);
641   allowlist.matches_all_origins = true;
642   allowlist.matches_opaque_src = true;
643   policy.push_back(allowlist);
644   return true;
645 }
646 
DisallowFeature(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)647 void DisallowFeature(mojom::blink::FeaturePolicyFeature feature,
648                      ParsedFeaturePolicy& policy) {
649   RemoveFeatureIfPresent(feature, policy);
650   DisallowFeatureIfNotPresent(feature, policy);
651 }
652 
IsFeatureForMeasurementOnly(mojom::blink::FeaturePolicyFeature feature)653 bool IsFeatureForMeasurementOnly(mojom::blink::FeaturePolicyFeature feature) {
654   return feature == mojom::blink::FeaturePolicyFeature::kWebShare;
655 }
656 
AllowFeatureEverywhere(mojom::blink::FeaturePolicyFeature feature,ParsedFeaturePolicy & policy)657 void AllowFeatureEverywhere(mojom::blink::FeaturePolicyFeature feature,
658                             ParsedFeaturePolicy& policy) {
659   RemoveFeatureIfPresent(feature, policy);
660   AllowFeatureEverywhereIfNotPresent(feature, policy);
661 }
662 
GetAvailableFeatures(ExecutionContext * execution_context)663 const Vector<String> GetAvailableFeatures(ExecutionContext* execution_context) {
664   Vector<String> available_features;
665   for (const auto& feature : GetDefaultFeatureNameMap()) {
666     if (!DisabledByOriginTrial(feature.key, execution_context) &&
667         !IsFeatureForMeasurementOnly(feature.value)) {
668       available_features.push_back(feature.key);
669     }
670   }
671   return available_features;
672 }
673 
GetNameForFeature(mojom::blink::FeaturePolicyFeature feature)674 const String& GetNameForFeature(mojom::blink::FeaturePolicyFeature feature) {
675   for (const auto& entry : GetDefaultFeatureNameMap()) {
676     if (entry.value == feature)
677       return entry.key;
678   }
679   return g_empty_string;
680 }
681 
682 }  // namespace blink
683