text: // Copyright 2014 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 "core/frame/csp/CSPDirectiveList.h"
  
  #include "bindings/core/v8/SourceLocation.h"
  #include "core/dom/Document.h"
  #include "core/dom/SecurityContext.h"
  #include "core/dom/SpaceSplitString.h"
  #include "core/frame/Deprecation.h"
  #include "core/frame/LocalFrame.h"
  #include "core/frame/UseCounter.h"
  #include "core/html/HTMLScriptElement.h"
  #include "core/inspector/ConsoleMessage.h"
  #include "platform/Crypto.h"
  #include "platform/RuntimeEnabledFeatures.h"
  #include "platform/network/ContentSecurityPolicyParsers.h"
  #include "platform/weborigin/KURL.h"
  #include "platform/wtf/text/Base64.h"
  #include "platform/wtf/text/ParsingUtilities.h"
  #include "platform/wtf/text/StringBuilder.h"
  #include "platform/wtf/text/StringUTF8Adaptor.h"
  #include "platform/wtf/text/WTFString.h"
  
  namespace blink {
  
  namespace {
  
  String GetSha256String(const String& content) {
    DigestValue digest;
    StringUTF8Adaptor utf8_content(content);
    bool digest_success = ComputeDigest(kHashAlgorithmSha256, utf8_content.Data(),
                                        utf8_content.length(), digest);
    if (!digest_success) {
      return "sha256-...";
    }
  
    return "sha256-" + Base64Encode(reinterpret_cast<char*>(digest.data()),
                                    digest.size(), kBase64DoNotInsertLFs);
  }
  
  ContentSecurityPolicyHashAlgorithm ConvertHashAlgorithmToCSPHashAlgorithm(
      HashAlgorithm algorithm) {
    switch (algorithm) {
      case kHashAlgorithmSha1:
        // Sha1 is not supported.
        return kContentSecurityPolicyHashAlgorithmNone;
      case kHashAlgorithmSha256:
        return kContentSecurityPolicyHashAlgorithmSha256;
      case kHashAlgorithmSha384:
        return kContentSecurityPolicyHashAlgorithmSha384;
      case kHashAlgorithmSha512:
        return kContentSecurityPolicyHashAlgorithmSha512;
    }
    NOTREACHED();
    return kContentSecurityPolicyHashAlgorithmNone;
  }
  
  // IntegrityMetadata (from SRI) has base64-encoded digest values, but CSP uses
  // binary format. This converts from the former to the latter.
  bool ParseBase64Digest(String base64, DigestValue& hash) {
    Vector<char> hash_vector;
    // We accept base64url-encoded data here by normalizing it to base64.
    if (!Base64Decode(NormalizeToBase64(base64), hash_vector))
      return false;
    if (hash_vector.IsEmpty() || hash_vector.size() > kMaxDigestSize)
      return false;
    hash.Append(reinterpret_cast<uint8_t*>(hash_vector.data()),
                hash_vector.size());
    return true;
  }
  
  }  // namespace
  
  CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy,
                                     ContentSecurityPolicyHeaderType type,
                                     ContentSecurityPolicyHeaderSource source)
      : policy_(policy),
        header_type_(type),
        header_source_(source),
        has_sandbox_policy_(false),
        strict_mixed_content_checking_enforced_(false),
        upgrade_insecure_requests_(false),
        treat_as_public_address_(false),
        require_sri_for_(RequireSRIForToken::kNone) {}
  
  CSPDirectiveList* CSPDirectiveList::Create(
      ContentSecurityPolicy* policy,
      const UChar* begin,
      const UChar* end,
      ContentSecurityPolicyHeaderType type,
      ContentSecurityPolicyHeaderSource source) {
    CSPDirectiveList* directives = new CSPDirectiveList(policy, type, source);
    directives->Parse(begin, end);
  
    if (!directives->CheckEval(
            directives->OperativeDirective(directives->script_src_.Get()))) {
      String message =
          "Refused to evaluate a string as JavaScript because 'unsafe-eval' is "
          "not an allowed source of script in the following Content Security "
          "Policy directive: \"" +
          directives->OperativeDirective(directives->script_src_.Get())
              ->GetText() +
          "\".\n";
      directives->SetEvalDisabledErrorMessage(message);
    }
  
    if (directives->IsReportOnly() &&
        source != kContentSecurityPolicyHeaderSourceMeta &&
        directives->ReportEndpoints().IsEmpty())
      policy->ReportMissingReportURI(String(begin, end - begin));
  
    return directives;
  }
  
  void CSPDirectiveList::ReportViolation(
      const String& directive_text,
      const ContentSecurityPolicy::DirectiveType& effective_type,
      const String& console_message,
      const KURL& blocked_url,
      ResourceRequest::RedirectStatus redirect_status) const {
    String message =
        IsReportOnly() ? "[Report Only] " + console_message : console_message;
    policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
                                                 kErrorMessageLevel, message));
    policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                             report_endpoints_, header_, header_type_,
                             ContentSecurityPolicy::kURLViolation,
                             std::unique_ptr<SourceLocation>(),
                             nullptr,  // localFrame
                             redirect_status);
  }
  
  void CSPDirectiveList::ReportViolationWithFrame(
      const String& directive_text,
      const ContentSecurityPolicy::DirectiveType& effective_type,
      const String& console_message,
      const KURL& blocked_url,
      LocalFrame* frame) const {
    String message =
        IsReportOnly() ? "[Report Only] " + console_message : console_message;
    policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
                                                 kErrorMessageLevel, message),
                          frame);
    policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                             report_endpoints_, header_, header_type_,
                             ContentSecurityPolicy::kURLViolation,
                             std::unique_ptr<SourceLocation>(), frame);
  }
  
  void CSPDirectiveList::ReportViolationWithLocation(
      const String& directive_text,
      const ContentSecurityPolicy::DirectiveType& effective_type,
      const String& console_message,
      const KURL& blocked_url,
      const String& context_url,
      const WTF::OrdinalNumber& context_line,
      Element* element,
      const String& source) const {
    String message =
        IsReportOnly() ? "[Report Only] " + console_message : console_message;
    std::unique_ptr<SourceLocation> source_location =
        SourceLocation::Capture(context_url, context_line.OneBasedInt(), 0);
    policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
                                                 kErrorMessageLevel, message,
                                                 source_location->Clone()));
    policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                             report_endpoints_, header_, header_type_,
                             ContentSecurityPolicy::kInlineViolation,
                             std::move(source_location), nullptr,  // localFrame
                             RedirectStatus::kNoRedirect, element, source);
  }
  
  void CSPDirectiveList::ReportEvalViolation(
      const String& directive_text,
      const ContentSecurityPolicy::DirectiveType& effective_type,
      const String& message,
      const KURL& blocked_url,
      ScriptState* script_state,
      const ContentSecurityPolicy::ExceptionStatus exception_status,
      const String& content) const {
    String report_message = IsReportOnly() ? "[Report Only] " + message : message;
    // Print a console message if it won't be redundant with a
    // JavaScript exception that the caller will throw. (Exceptions will
    // never get thrown in report-only mode because the caller won't see
    // a violation.)
    if (IsReportOnly() ||
        exception_status == ContentSecurityPolicy::kWillNotThrowException) {
      ConsoleMessage* console_message = ConsoleMessage::Create(
          kSecurityMessageSource, kErrorMessageLevel, report_message);
      policy_->LogToConsole(console_message);
    }
    policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                             report_endpoints_, header_, header_type_,
                             ContentSecurityPolicy::kEvalViolation,
                             std::unique_ptr<SourceLocation>(), nullptr,
                             RedirectStatus::kFollowedRedirect, nullptr, content);
  }
  
  bool CSPDirectiveList::CheckEval(SourceListDirective* directive) const {
    return !directive || directive->AllowEval();
  }
  
  bool CSPDirectiveList::IsMatchingNoncePresent(SourceListDirective* directive,
                                                const String& nonce) const {
    return directive && directive->AllowNonce(nonce);
  }
  
  bool CSPDirectiveList::AreAllMatchingHashesPresent(
      SourceListDirective* directive,
      const IntegrityMetadataSet& hashes) const {
    if (!directive || hashes.IsEmpty())
      return false;
    for (const std::pair<WTF::String, HashAlgorithm>& hash : hashes) {
      // Convert the hash from integrity metadata format to CSP format.
      CSPHashValue csp_hash;
      csp_hash.first = ConvertHashAlgorithmToCSPHashAlgorithm(hash.second);
      if (!ParseBase64Digest(hash.first, csp_hash.second))
        return false;
      // All integrity hashes must be listed in the CSP.
      if (!directive->AllowHash(csp_hash))
        return false;
    }
    return true;
  }
  
  bool CSPDirectiveList::CheckHash(SourceListDirective* directive,
                                   const CSPHashValue& hash_value) const {
    return !directive || directive->AllowHash(hash_value);
  }
  
  bool CSPDirectiveList::CheckHashedAttributes(
      SourceListDirective* directive) const {
    return !directive || directive->AllowHashedAttributes();
  }
  
  bool CSPDirectiveList::CheckDynamic(SourceListDirective* directive) const {
    return !directive || directive->AllowDynamic();
  }
  
  void CSPDirectiveList::ReportMixedContent(
      const KURL& mixed_url,
      ResourceRequest::RedirectStatus redirect_status) const {
    if (StrictMixedContentChecking()) {
      policy_->ReportViolation(
          ContentSecurityPolicy::GetDirectiveName(
              ContentSecurityPolicy::DirectiveType::kBlockAllMixedContent),
          ContentSecurityPolicy::DirectiveType::kBlockAllMixedContent, String(),
          mixed_url, report_endpoints_, header_, header_type_,
          ContentSecurityPolicy::kURLViolation, std::unique_ptr<SourceLocation>(),
          nullptr,  // contextFrame,
          redirect_status);
    }
  }
  
  bool CSPDirectiveList::CheckSource(
      SourceListDirective* directive,
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status) const {
    // If |url| is empty, fall back to the policy URL to ensure that <object>'s
    // without a `src` can be blocked/allowed, as they can still load plugins
    // even though they don't actually have a URL.
    return !directive || directive->Allows(url.IsEmpty() ? policy_->Url() : url,
                                           redirect_status);
  }
  
  bool CSPDirectiveList::CheckAncestors(SourceListDirective* directive,
                                        LocalFrame* frame) const {
    if (!frame || !directive)
      return true;
  
    for (Frame* current = frame->Tree().Parent(); current;
         current = current->Tree().Parent()) {
      // The |current| frame might be a remote frame which has no URL, so use
      // its origin instead.  This should suffice for this check since it
      // doesn't do path comparisons.  See https://crbug.com/582544.
      //
      // TODO(mkwst): Move this check up into the browser process.  See
      // https://crbug.com/555418.
      KURL url(NullURL(),
               current->GetSecurityContext()->GetSecurityOrigin()->ToString());
      if (!directive->Allows(url, ResourceRequest::RedirectStatus::kNoRedirect))
        return false;
    }
    return true;
  }
  
  bool CSPDirectiveList::CheckRequestWithoutIntegrity(
      WebURLRequest::RequestContext context) const {
    if (require_sri_for_ == RequireSRIForToken::kNone)
      return true;
    // SRI specification
    // (https://w3c.github.io/webappsec-subresource-integrity/#apply-algorithm-to-request)
    // says to match token with request's destination with the token.
    // Keep this logic aligned with ContentSecurityPolicy::allowRequest
    if ((require_sri_for_ & RequireSRIForToken::kScript) &&
        (context == WebURLRequest::kRequestContextScript ||
         context == WebURLRequest::kRequestContextImport ||
         context == WebURLRequest::kRequestContextServiceWorker ||
         context == WebURLRequest::kRequestContextSharedWorker ||
         context == WebURLRequest::kRequestContextWorker)) {
      return false;
    }
    if ((require_sri_for_ & RequireSRIForToken::kStyle) &&
        context == WebURLRequest::kRequestContextStyle)
      return false;
    return true;
  }
  
  bool CSPDirectiveList::CheckRequestWithoutIntegrityAndReportViolation(
      WebURLRequest::RequestContext context,
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status) const {
    if (CheckRequestWithoutIntegrity(context))
      return true;
    String resource_type;
    switch (context) {
      case WebURLRequest::kRequestContextScript:
      case WebURLRequest::kRequestContextImport:
        resource_type = "script";
        break;
      case WebURLRequest::kRequestContextStyle:
        resource_type = "stylesheet";
        break;
      case WebURLRequest::kRequestContextServiceWorker:
        resource_type = "service worker";
        break;
      case WebURLRequest::kRequestContextSharedWorker:
        resource_type = "shared worker";
        break;
      case WebURLRequest::kRequestContextWorker:
        resource_type = "worker";
        break;
      default:
        break;
    }
  
    ReportViolation(ContentSecurityPolicy::GetDirectiveName(
                        ContentSecurityPolicy::DirectiveType::kRequireSRIFor),
                    ContentSecurityPolicy::DirectiveType::kRequireSRIFor,
                    "Refused to load the " + resource_type + " '" +
                        url.ElidedString() +
                        "' because 'require-sri-for' directive requires "
                        "integrity attribute be present for all " +
                        resource_type + "s.",
                    url, redirect_status);
    return DenyIfEnforcingPolicy();
  }
  
  bool CSPDirectiveList::AllowRequestWithoutIntegrity(
      WebURLRequest::RequestContext context,
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    if (reporting_policy == SecurityViolationReportingPolicy::kReport)
      return CheckRequestWithoutIntegrityAndReportViolation(context, url,
                                                            redirect_status);
    return DenyIfEnforcingPolicy() || CheckRequestWithoutIntegrity(context);
  }
  
  bool CSPDirectiveList::CheckMediaType(MediaListDirective* directive,
                                        const String& type,
                                        const String& type_attribute) const {
    if (!directive)
      return true;
    if (type_attribute.IsEmpty() || type_attribute.StripWhiteSpace() != type)
      return false;
    return directive->Allows(type);
  }
  
  SourceListDirective* CSPDirectiveList::OperativeDirective(
      SourceListDirective* directive) const {
    return directive ? directive : default_src_.Get();
  }
  
  SourceListDirective* CSPDirectiveList::OperativeDirective(
      SourceListDirective* directive,
      SourceListDirective* override) const {
    return directive ? directive : override;
  }
  
  bool CSPDirectiveList::CheckEvalAndReportViolation(
      SourceListDirective* directive,
      const String& console_message,
      ScriptState* script_state,
      ContentSecurityPolicy::ExceptionStatus exception_status,
      const String& content) const {
    if (CheckEval(directive))
      return true;
  
    String suffix = String();
    if (directive == default_src_)
      suffix =
          " Note that 'script-src' was not explicitly set, so 'default-src' is "
          "used as a fallback.";
  
    ReportEvalViolation(
        directive->GetText(), ContentSecurityPolicy::DirectiveType::kScriptSrc,
        console_message + "\"" + directive->GetText() + "\"." + suffix + "\n",
        KURL(), script_state, exception_status,
        directive->AllowReportSample() ? content : g_empty_string);
    if (!IsReportOnly()) {
      policy_->ReportBlockedScriptExecutionToInspector(directive->GetText());
      return false;
    }
    return true;
  }
  
  bool CSPDirectiveList::CheckMediaTypeAndReportViolation(
      MediaListDirective* directive,
      const String& type,
      const String& type_attribute,
      const String& console_message) const {
    if (CheckMediaType(directive, type, type_attribute))
      return true;
  
    String message = console_message + "\'" + directive->GetText() + "\'.";
    if (type_attribute.IsEmpty())
      message = message +
                " When enforcing the 'plugin-types' directive, the plugin's "
                "media type must be explicitly declared with a 'type' attribute "
                "on the containing element (e.g. '<object type=\"[TYPE GOES "
                "HERE]\" ...>').";
  
    // 'RedirectStatus::NoRedirect' is safe here, as we do the media type check
    // before actually loading data; this means that we shouldn't leak redirect
    // targets, as we won't have had a chance to redirect yet.
    ReportViolation(
        directive->GetText(), ContentSecurityPolicy::DirectiveType::kPluginTypes,
        message + "\n", NullURL(), ResourceRequest::RedirectStatus::kNoRedirect);
    return DenyIfEnforcingPolicy();
  }
  
  bool CSPDirectiveList::CheckInlineAndReportViolation(
      SourceListDirective* directive,
      const String& console_message,
      Element* element,
      const String& source,
      const String& context_url,
      const WTF::OrdinalNumber& context_line,
      bool is_script,
      const String& hash_value) const {
    if (!directive || directive->AllowAllInline())
      return true;
  
    String suffix = String();
    if (directive->AllowInline() && directive->IsHashOrNoncePresent()) {
      // If inline is allowed, but a hash or nonce is present, we ignore
      // 'unsafe-inline'. Throw a reasonable error.
      suffix =
          " Note that 'unsafe-inline' is ignored if either a hash or nonce value "
          "is present in the source list.";
    } else {
      suffix =
          " Either the 'unsafe-inline' keyword, a hash ('" + hash_value +
          "'), or a nonce ('nonce-...') is required to enable inline execution.";
      if (directive == default_src_)
        suffix = suffix + " Note also that '" +
                 String(is_script ? "script" : "style") +
                 "-src' was not explicitly set, so 'default-src' is used as a "
                 "fallback.";
    }
  
    ReportViolationWithLocation(
        directive->GetText(),
        is_script ? ContentSecurityPolicy::DirectiveType::kScriptSrc
                  : ContentSecurityPolicy::DirectiveType::kStyleSrc,
        console_message + "\"" + directive->GetText() + "\"." + suffix + "\n",
        KURL(), context_url, context_line, element,
        directive->AllowReportSample() ? source : g_empty_string);
  
    if (!IsReportOnly()) {
      if (is_script)
        policy_->ReportBlockedScriptExecutionToInspector(directive->GetText());
      return false;
    }
    return true;
  }
  
  bool CSPDirectiveList::CheckSourceAndReportViolation(
      SourceListDirective* directive,
      const KURL& url,
      const ContentSecurityPolicy::DirectiveType& effective_type,
      ResourceRequest::RedirectStatus redirect_status) const {
    if (!directive)
      return true;
  
    // We ignore URL-based whitelists if we're allowing dynamic script injection.
    if (CheckSource(directive, url, redirect_status) && !CheckDynamic(directive))
      return true;
  
    // We should never have a violation against `child-src` or `default-src`
    // directly; the effective directive should always be one of the explicit
    // fetch directives.
    DCHECK_NE(ContentSecurityPolicy::DirectiveType::kChildSrc, effective_type);
    DCHECK_NE(ContentSecurityPolicy::DirectiveType::kDefaultSrc, effective_type);
  
    String prefix;
    if (ContentSecurityPolicy::DirectiveType::kBaseURI == effective_type)
      prefix = "Refused to set the document's base URI to '";
    else if (ContentSecurityPolicy::DirectiveType::kWorkerSrc == effective_type)
      prefix = "Refused to create a worker from '";
    else if (ContentSecurityPolicy::DirectiveType::kConnectSrc == effective_type)
      prefix = "Refused to connect to '";
    else if (ContentSecurityPolicy::DirectiveType::kFontSrc == effective_type)
      prefix = "Refused to load the font '";
    else if (ContentSecurityPolicy::DirectiveType::kFormAction == effective_type)
      prefix = "Refused to send form data to '";
    else if (ContentSecurityPolicy::DirectiveType::kFrameSrc == effective_type)
      prefix = "Refused to frame '";
    else if (ContentSecurityPolicy::DirectiveType::kImgSrc == effective_type)
      prefix = "Refused to load the image '";
    else if (ContentSecurityPolicy::DirectiveType::kMediaSrc == effective_type)
      prefix = "Refused to load media from '";
    else if (ContentSecurityPolicy::DirectiveType::kManifestSrc == effective_type)
      prefix = "Refused to load manifest from '";
    else if (ContentSecurityPolicy::DirectiveType::kObjectSrc == effective_type)
      prefix = "Refused to load plugin data from '";
    else if (ContentSecurityPolicy::DirectiveType::kScriptSrc == effective_type)
      prefix = "Refused to load the script '";
    else if (ContentSecurityPolicy::DirectiveType::kStyleSrc == effective_type)
      prefix = "Refused to load the stylesheet '";
  
    String suffix = String();
    if (CheckDynamic(directive))
      suffix =
          " 'strict-dynamic' is present, so host-based whitelisting is disabled.";
  
    String directive_name = directive->GetName();
    String effective_directive_name =
        ContentSecurityPolicy::GetDirectiveName(effective_type);
    if (directive_name != effective_directive_name) {
      suffix = suffix + " Note that '" + effective_directive_name +
               "' was not explicitly set, so '" + directive_name +
               "' is used as a fallback.";
    }
  
    ReportViolation(directive->GetText(), effective_type,
                    prefix + url.ElidedString() +
                        "' because it violates the following Content Security "
                        "Policy directive: \"" +
                        directive->GetText() + "\"." + suffix + "\n",
                    url, redirect_status);
    return DenyIfEnforcingPolicy();
  }
  
  bool CSPDirectiveList::CheckAncestorsAndReportViolation(
      SourceListDirective* directive,
      LocalFrame* frame,
      const KURL& url) const {
    if (CheckAncestors(directive, frame))
      return true;
  
    ReportViolationWithFrame(
        directive->GetText(),
        ContentSecurityPolicy::DirectiveType::kFrameAncestors,
        "Refused to display '" + url.ElidedString() +
            "' in a frame because an ancestor violates the "
            "following Content Security Policy directive: "
            "\"" +
            directive->GetText() + "\".",
        url, frame);
    return DenyIfEnforcingPolicy();
  }
  
  bool CSPDirectiveList::AllowJavaScriptURLs(
      Element* element,
      const String& source,
      const String& context_url,
      const WTF::OrdinalNumber& context_line,
      SecurityViolationReportingPolicy reporting_policy) const {
    SourceListDirective* directive = OperativeDirective(script_src_.Get());
    if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
      return CheckInlineAndReportViolation(
          directive,
          "Refused to execute JavaScript URL because it violates the following "
          "Content Security Policy directive: ",
          element, source, context_url, context_line, true, "sha256-...");
    }
  
    return !directive || directive->AllowAllInline();
  }
  
  bool CSPDirectiveList::AllowInlineEventHandlers(
      Element* element,
      const String& source,
      const String& context_url,
      const WTF::OrdinalNumber& context_line,
      SecurityViolationReportingPolicy reporting_policy) const {
    SourceListDirective* directive = OperativeDirective(script_src_.Get());
    if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
      return CheckInlineAndReportViolation(
          OperativeDirective(script_src_.Get()),
          "Refused to execute inline event handler because it violates the "
          "following Content Security Policy directive: ",
          element, source, context_url, context_line, true, "sha256-...");
    }
  
    return !directive || directive->AllowAllInline();
  }
  
  bool CSPDirectiveList::AllowInlineScript(
      Element* element,
      const String& context_url,
      const String& nonce,
      const WTF::OrdinalNumber& context_line,
      SecurityViolationReportingPolicy reporting_policy,
      const String& content) const {
    SourceListDirective* directive = OperativeDirective(script_src_.Get());
    if (IsMatchingNoncePresent(directive, nonce))
      return true;
    if (element && isHTMLScriptElement(element) &&
        !toHTMLScriptElement(element)->Loader()->IsParserInserted() &&
        AllowDynamic()) {
      return true;
    }
    if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
      return CheckInlineAndReportViolation(
          directive,
          "Refused to execute inline script because it violates the following "
          "Content Security Policy directive: ",
          element, content, context_url, context_line, true,
          GetSha256String(content));
    }
  
    return !directive || directive->AllowAllInline();
  }
  
  bool CSPDirectiveList::AllowInlineStyle(
      Element* element,
      const String& context_url,
      const String& nonce,
      const WTF::OrdinalNumber& context_line,
      SecurityViolationReportingPolicy reporting_policy,
      const String& content) const {
    SourceListDirective* directive = OperativeDirective(style_src_.Get());
    if (IsMatchingNoncePresent(directive, nonce))
      return true;
    if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
      return CheckInlineAndReportViolation(
          directive,
          "Refused to apply inline style because it violates the following "
          "Content Security Policy directive: ",
          element, content, context_url, context_line, false,
          GetSha256String(content));
    }
  
    return !directive || directive->AllowAllInline();
  }
  
  bool CSPDirectiveList::AllowEval(
      ScriptState* script_state,
      SecurityViolationReportingPolicy reporting_policy,
      ContentSecurityPolicy::ExceptionStatus exception_status,
      const String& content) const {
    if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
      return CheckEvalAndReportViolation(
          OperativeDirective(script_src_.Get()),
          "Refused to evaluate a string as JavaScript because 'unsafe-eval' is "
          "not an allowed source of script in the following Content Security "
          "Policy directive: ",
          script_state, exception_status, content);
    }
    return CheckEval(OperativeDirective(script_src_.Get()));
  }
  
  bool CSPDirectiveList::AllowPluginType(
      const String& type,
      const String& type_attribute,
      const KURL& url,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckMediaTypeAndReportViolation(
                     plugin_types_.Get(), type, type_attribute,
                     "Refused to load '" + url.ElidedString() + "' (MIME type '" +
                         type_attribute +
                         "') because it violates the following Content Security "
                         "Policy Directive: ")
               : CheckMediaType(plugin_types_.Get(), type, type_attribute);
  }
  
  bool CSPDirectiveList::AllowScriptFromSource(
      const KURL& url,
      const String& nonce,
      const IntegrityMetadataSet& hashes,
      ParserDisposition parser_disposition,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    SourceListDirective* directive = OperativeDirective(script_src_.Get());
    if (IsMatchingNoncePresent(directive, nonce))
      return true;
    if (parser_disposition == kNotParserInserted && AllowDynamic())
      return true;
    if (AreAllMatchingHashesPresent(directive, hashes))
      return true;
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     directive, url,
                     ContentSecurityPolicy::DirectiveType::kScriptSrc,
                     redirect_status)
               : CheckSource(directive, url, redirect_status);
  }
  
  bool CSPDirectiveList::AllowObjectFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    if (url.ProtocolIsAbout())
      return true;
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(object_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kObjectSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(object_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowFrameFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    if (url.ProtocolIsAbout())
      return true;
  
    // 'frame-src' overrides 'child-src', which overrides the default
    // sources. So, we do this nested set of calls to 'operativeDirective()' to
    // grab 'frame-src' if it exists, 'child-src' if it doesn't, and 'defaut-src'
    // if neither are available.
    SourceListDirective* which_directive = OperativeDirective(
        frame_src_.Get(), OperativeDirective(child_src_.Get()));
  
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     which_directive, url,
                     ContentSecurityPolicy::DirectiveType::kFrameSrc,
                     redirect_status)
               : CheckSource(which_directive, url, redirect_status);
  }
  
  bool CSPDirectiveList::AllowImageFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(img_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kImgSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(img_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowStyleFromSource(
      const KURL& url,
      const String& nonce,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    if (IsMatchingNoncePresent(OperativeDirective(style_src_.Get()), nonce))
      return true;
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(style_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kStyleSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(style_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowFontFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(font_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kFontSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(font_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowMediaFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(media_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kMediaSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(media_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowManifestFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(manifest_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kManifestSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(manifest_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowConnectToSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     OperativeDirective(connect_src_.Get()), url,
                     ContentSecurityPolicy::DirectiveType::kConnectSrc,
                     redirect_status)
               : CheckSource(OperativeDirective(connect_src_.Get()), url,
                             redirect_status);
  }
  
  bool CSPDirectiveList::AllowFormAction(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     form_action_.Get(), url,
                     ContentSecurityPolicy::DirectiveType::kFormAction,
                     redirect_status)
               : CheckSource(form_action_.Get(), url, redirect_status);
  }
  
  bool CSPDirectiveList::AllowBaseURI(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    bool result =
        reporting_policy == SecurityViolationReportingPolicy::kReport
            ? CheckSourceAndReportViolation(
                  base_uri_.Get(), url,
                  ContentSecurityPolicy::DirectiveType::kBaseURI, redirect_status)
            : CheckSource(base_uri_.Get(), url, redirect_status);
  
    if (result &&
        !CheckSource(OperativeDirective(base_uri_.Get()), url, redirect_status)) {
      UseCounter::Count(policy_->GetDocument(),
                        WebFeature::kBaseWouldBeBlockedByDefaultSrc);
    }
  
    return result;
  }
  
  bool CSPDirectiveList::AllowWorkerFromSource(
      const KURL& url,
      ResourceRequest::RedirectStatus redirect_status,
      SecurityViolationReportingPolicy reporting_policy) const {
    SourceListDirective* worker_src = OperativeDirective(
        worker_src_.Get(), OperativeDirective(script_src_.Get()));
  
    if (AllowDynamicWorker())
      return true;
  
    // In CSP2, workers are controlled via 'child-src'. CSP3 moves them to
    // 'script-src'. In order to avoid breaking sites that allowed workers via
    // 'child-src' that would have been blocked via 'script-src', we'll
    // temporarily check whether a worker blocked via 'script-src' would have been
    // allowed under 'child-src'. If the new 'worker-src' directive is present,
    // however, we'll assume that the developer knows what they're asking for, and
    // skip the extra fallback.
    //
    // That is, we'll block 'https://example.com/worker' given the policy
    // "worker-src 'none'", "worker-src 'none'; child-src https://example.com",
    // but we'll allow it given the policy
    // "script-src https://not-example.com; child-src https://example.com"
    // (because 'child-src' allows it) or "child-src https://not-example.com"
    // (because the absent 'script-src' allows it).
    //
    // TODO(mkwst): Remove this once other vendors follow suit.
    // https://crbug.com/662930
    if (!CheckSource(worker_src, url, redirect_status) && !worker_src_ &&
        child_src_ && CheckSource(child_src_, url, redirect_status)) {
      Deprecation::CountDeprecation(
          policy_->GetDocument(),
          WebFeature::kChildSrcAllowedWorkerThatScriptSrcBlocked);
      return true;
    }
  
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckSourceAndReportViolation(
                     worker_src, url,
                     ContentSecurityPolicy::DirectiveType::kWorkerSrc,
                     redirect_status)
               : CheckSource(worker_src, url, redirect_status);
  }
  
  bool CSPDirectiveList::AllowAncestors(
      LocalFrame* frame,
      const KURL& url,
      SecurityViolationReportingPolicy reporting_policy) const {
    return reporting_policy == SecurityViolationReportingPolicy::kReport
               ? CheckAncestorsAndReportViolation(frame_ancestors_.Get(), frame,
                                                  url)
               : CheckAncestors(frame_ancestors_.Get(), frame);
  }
  
  bool CSPDirectiveList::AllowScriptHash(
      const CSPHashValue& hash_value,
      ContentSecurityPolicy::InlineType type) const {
    if (type == ContentSecurityPolicy::InlineType::kAttribute) {
      if (!policy_->ExperimentalFeaturesEnabled())
        return false;
      if (!CheckHashedAttributes(OperativeDirective(script_src_.Get())))
        return false;
    }
    return CheckHash(OperativeDirective(script_src_.Get()), hash_value);
  }
  
  bool CSPDirectiveList::AllowStyleHash(
      const CSPHashValue& hash_value,
      ContentSecurityPolicy::InlineType type) const {
    if (type != ContentSecurityPolicy::InlineType::kBlock)
      return false;
    return CheckHash(OperativeDirective(style_src_.Get()), hash_value);
  }
  
  bool CSPDirectiveList::AllowDynamic() const {
    return CheckDynamic(OperativeDirective(script_src_.Get()));
  }
  
  bool CSPDirectiveList::AllowDynamicWorker() const {
    SourceListDirective* worker_src = OperativeDirective(
        worker_src_.Get(), OperativeDirective(script_src_.Get()));
    return CheckDynamic(worker_src);
  }
  
  const String& CSPDirectiveList::PluginTypesText() const {
    DCHECK(HasPluginTypes());
    return plugin_types_->GetText();
  }
  
  bool CSPDirectiveList::ShouldSendCSPHeader(Resource::Type type) const {
    // TODO(mkwst): Revisit this once the CORS prefetch issue with the 'CSP'
    //              header is worked out, one way or another:
    //              https://github.com/whatwg/fetch/issues/52
    return false;
  }
  
  // policy            = directive-list
  // directive-list    = [ directive *( ";" [ directive ] ) ]
  //
  void CSPDirectiveList::Parse(const UChar* begin, const UChar* end) {
    header_ = String(begin, end - begin).StripWhiteSpace();
  
    if (begin == end)
      return;
  
    const UChar* position = begin;
    while (position < end) {
      const UChar* directive_begin = position;
      skipUntil<UChar>(position, end, ';');
  
      String name, value;
      if (ParseDirective(directive_begin, position, name, value)) {
        DCHECK(!name.IsEmpty());
        AddDirective(name, value);
      }
  
      DCHECK(position == end || *position == ';');
      skipExactly<UChar>(position, end, ';');
    }
  }
  
  // directive         = *WSP [ directive-name [ WSP directive-value ] ]
  // directive-name    = 1*( ALPHA / DIGIT / "-" )
  // directive-value   = *( WSP / <VCHAR except ";"> )
  //
  bool CSPDirectiveList::ParseDirective(const UChar* begin,
                                        const UChar* end,
                                        String& name,
                                        String& value) {
    DCHECK(name.IsEmpty());
    DCHECK(value.IsEmpty());
  
    const UChar* position = begin;
    skipWhile<UChar, IsASCIISpace>(position, end);
  
    // Empty directive (e.g. ";;;"). Exit early.
    if (position == end)
      return false;
  
    const UChar* name_begin = position;
    skipWhile<UChar, IsCSPDirectiveNameCharacter>(position, end);
  
    // The directive-name must be non-empty.
    if (name_begin == position) {
      skipWhile<UChar, IsNotASCIISpace>(position, end);
      policy_->ReportUnsupportedDirective(
          String(name_begin, position - name_begin));
      return false;
    }
  
    name = String(name_begin, position - name_begin);
  
    if (position == end)
      return true;
  
    if (!skipExactly<UChar, IsASCIISpace>(position, end)) {
      skipWhile<UChar, IsNotASCIISpace>(position, end);
      policy_->ReportUnsupportedDirective(
          String(name_begin, position - name_begin));
      return false;
    }
  
    skipWhile<UChar, IsASCIISpace>(position, end);
  
    const UChar* value_begin = position;
    skipWhile<UChar, IsCSPDirectiveValueCharacter>(position, end);
  
    if (position != end) {
      policy_->ReportInvalidDirectiveValueCharacter(
          name, String(value_begin, end - value_begin));
      return false;
    }
  
    // The directive-value may be empty.
    if (value_begin == position)
      return true;
  
    value = String(value_begin, position - value_begin);
    return true;
  }
  
  void CSPDirectiveList::ParseRequireSRIFor(const String& name,
                                            const String& value) {
    if (require_sri_for_ != 0) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
    StringBuilder token_errors;
    unsigned number_of_token_errors = 0;
    Vector<UChar> characters;
    value.AppendTo(characters);
  
    const UChar* position = characters.data();
    const UChar* end = position + characters.size();
  
    while (position < end) {
      skipWhile<UChar, IsASCIISpace>(position, end);
  
      const UChar* token_begin = position;
      skipWhile<UChar, IsNotASCIISpace>(position, end);
  
      if (token_begin < position) {
        String token = String(token_begin, position - token_begin);
        if (EqualIgnoringASCIICase(token, "script")) {
          require_sri_for_ |= RequireSRIForToken::kScript;
        } else if (EqualIgnoringASCIICase(token, "style")) {
          require_sri_for_ |= RequireSRIForToken::kStyle;
        } else {
          if (number_of_token_errors)
            token_errors.Append(", \'");
          else
            token_errors.Append('\'');
          token_errors.Append(token);
          token_errors.Append('\'');
          number_of_token_errors++;
        }
      }
    }
  
    if (number_of_token_errors == 0)
      return;
  
    String invalid_tokens_error_message;
    if (number_of_token_errors > 1)
      token_errors.Append(" are invalid 'require-sri-for' tokens.");
    else
      token_errors.Append(" is an invalid 'require-sri-for' token.");
  
    invalid_tokens_error_message = token_errors.ToString();
  
    DCHECK(!invalid_tokens_error_message.IsEmpty());
  
    policy_->ReportInvalidRequireSRIForTokens(invalid_tokens_error_message);
  }
  
  void CSPDirectiveList::ParseReportURI(const String& name, const String& value) {
    if (!report_endpoints_.IsEmpty()) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
  
    // Remove report-uri in meta policies, per
    // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
    if (header_source_ == kContentSecurityPolicyHeaderSourceMeta) {
      policy_->ReportInvalidDirectiveInMeta(name);
      return;
    }
  
    Vector<UChar> characters;
    value.AppendTo(characters);
  
    const UChar* position = characters.data();
    const UChar* end = position + characters.size();
  
    while (position < end) {
      skipWhile<UChar, IsASCIISpace>(position, end);
  
      const UChar* url_begin = position;
      skipWhile<UChar, IsNotASCIISpace>(position, end);
  
      if (url_begin < position) {
        String url = String(url_begin, position - url_begin);
        report_endpoints_.push_back(url);
      }
    }
  
    if (report_endpoints_.size() > 1) {
      UseCounter::Count(policy_->GetDocument(),
                        WebFeature::kReportUriMultipleEndpoints);
    } else {
      UseCounter::Count(policy_->GetDocument(),
                        WebFeature::kReportUriSingleEndpoint);
    }
  }
  
  template <class CSPDirectiveType>
  void CSPDirectiveList::SetCSPDirective(const String& name,
                                         const String& value,
                                         Member<CSPDirectiveType>& directive) {
    if (directive) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
  
    // Remove frame-ancestors directives in meta policies, per
    // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
    if (header_source_ == kContentSecurityPolicyHeaderSourceMeta &&
        ContentSecurityPolicy::GetDirectiveType(name) ==
            ContentSecurityPolicy::DirectiveType::kFrameAncestors) {
      policy_->ReportInvalidDirectiveInMeta(name);
      return;
    }
  
    directive = new CSPDirectiveType(name, value, policy_);
  }
  
  void CSPDirectiveList::ApplySandboxPolicy(const String& name,
                                            const String& sandbox_policy) {
    // Remove sandbox directives in meta policies, per
    // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
    if (header_source_ == kContentSecurityPolicyHeaderSourceMeta) {
      policy_->ReportInvalidDirectiveInMeta(name);
      return;
    }
    if (IsReportOnly()) {
      policy_->ReportInvalidInReportOnly(name);
      return;
    }
    if (has_sandbox_policy_) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
    has_sandbox_policy_ = true;
    String invalid_tokens;
    SpaceSplitString policy_tokens =
        SpaceSplitString(AtomicString(sandbox_policy));
    policy_->EnforceSandboxFlags(
        ParseSandboxPolicy(policy_tokens, invalid_tokens));
    if (!invalid_tokens.IsNull())
      policy_->ReportInvalidSandboxFlags(invalid_tokens);
  }
  
  void CSPDirectiveList::TreatAsPublicAddress(const String& name,
                                              const String& value) {
    if (IsReportOnly()) {
      policy_->ReportInvalidInReportOnly(name);
      return;
    }
    if (treat_as_public_address_) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
    treat_as_public_address_ = true;
    policy_->TreatAsPublicAddress();
    if (!value.IsEmpty())
      policy_->ReportValueForEmptyDirective(name, value);
  }
  
  void CSPDirectiveList::EnforceStrictMixedContentChecking(const String& name,
                                                           const String& value) {
    if (strict_mixed_content_checking_enforced_) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
    if (!value.IsEmpty())
      policy_->ReportValueForEmptyDirective(name, value);
  
    strict_mixed_content_checking_enforced_ = true;
  
    if (!IsReportOnly())
      policy_->EnforceStrictMixedContentChecking();
  }
  
  void CSPDirectiveList::EnableInsecureRequestsUpgrade(const String& name,
                                                       const String& value) {
    if (IsReportOnly()) {
      policy_->ReportInvalidInReportOnly(name);
      return;
    }
    if (upgrade_insecure_requests_) {
      policy_->ReportDuplicateDirective(name);
      return;
    }
    upgrade_insecure_requests_ = true;
  
    policy_->UpgradeInsecureRequests();
    if (!value.IsEmpty())
      policy_->ReportValueForEmptyDirective(name, value);
  }
  
  void CSPDirectiveList::AddDirective(const String& name, const String& value) {
    DCHECK(!name.IsEmpty());
  
    ContentSecurityPolicy::DirectiveType type =
        ContentSecurityPolicy::GetDirectiveType(name);
    if (type == ContentSecurityPolicy::DirectiveType::kDefaultSrc) {
      SetCSPDirective<SourceListDirective>(name, value, default_src_);
      // TODO(mkwst) It seems unlikely that developers would use different
      // algorithms for scripts and styles. We may want to combine the
      // usesScriptHashAlgorithms() and usesStyleHashAlgorithms.
      policy_->UsesScriptHashAlgorithms(default_src_->HashAlgorithmsUsed());
      policy_->UsesStyleHashAlgorithms(default_src_->HashAlgorithmsUsed());
    } else if (type == ContentSecurityPolicy::DirectiveType::kScriptSrc) {
      SetCSPDirective<SourceListDirective>(name, value, script_src_);
      policy_->UsesScriptHashAlgorithms(script_src_->HashAlgorithmsUsed());
    } else if (type == ContentSecurityPolicy::DirectiveType::kObjectSrc) {
      SetCSPDirective<SourceListDirective>(name, value, object_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kFrameAncestors) {
      SetCSPDirective<SourceListDirective>(name, value, frame_ancestors_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kFrameSrc) {
      SetCSPDirective<SourceListDirective>(name, value, frame_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kImgSrc) {
      SetCSPDirective<SourceListDirective>(name, value, img_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kStyleSrc) {
      SetCSPDirective<SourceListDirective>(name, value, style_src_);
      policy_->UsesStyleHashAlgorithms(style_src_->HashAlgorithmsUsed());
    } else if (type == ContentSecurityPolicy::DirectiveType::kFontSrc) {
      SetCSPDirective<SourceListDirective>(name, value, font_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kMediaSrc) {
      SetCSPDirective<SourceListDirective>(name, value, media_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kConnectSrc) {
      SetCSPDirective<SourceListDirective>(name, value, connect_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kSandbox) {
      ApplySandboxPolicy(name, value);
    } else if (type == ContentSecurityPolicy::DirectiveType::kReportURI) {
      ParseReportURI(name, value);
    } else if (type == ContentSecurityPolicy::DirectiveType::kBaseURI) {
      SetCSPDirective<SourceListDirective>(name, value, base_uri_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kChildSrc) {
      SetCSPDirective<SourceListDirective>(name, value, child_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kWorkerSrc) {
      SetCSPDirective<SourceListDirective>(name, value, worker_src_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kFormAction) {
      SetCSPDirective<SourceListDirective>(name, value, form_action_);
    } else if (type == ContentSecurityPolicy::DirectiveType::kPluginTypes) {
      SetCSPDirective<MediaListDirective>(name, value, plugin_types_);
    } else if (type ==
               ContentSecurityPolicy::DirectiveType::kUpgradeInsecureRequests) {
      EnableInsecureRequestsUpgrade(name, value);
    } else if (type ==
               ContentSecurityPolicy::DirectiveType::kBlockAllMixedContent) {
      EnforceStrictMixedContentChecking(name, value);
    } else if (type == ContentSecurityPolicy::DirectiveType::kManifestSrc) {
      SetCSPDirective<SourceListDirective>(name, value, manifest_src_);
    } else if (type ==
               ContentSecurityPolicy::DirectiveType::kTreatAsPublicAddress) {
      TreatAsPublicAddress(name, value);
    } else if (type == ContentSecurityPolicy::DirectiveType::kRequireSRIFor &&
               policy_->ExperimentalFeaturesEnabled()) {
      ParseRequireSRIFor(name, value);
    } else {
      policy_->ReportUnsupportedDirective(name);
    }
  }
  
  SourceListDirective* CSPDirectiveList::OperativeDirective(
      const ContentSecurityPolicy::DirectiveType& type) const {
    switch (type) {
      // Directives that do not have a default directive.
      case ContentSecurityPolicy::DirectiveType::kBaseURI:
        return base_uri_.Get();
      case ContentSecurityPolicy::DirectiveType::kDefaultSrc:
        return default_src_.Get();
      case ContentSecurityPolicy::DirectiveType::kFrameAncestors:
        return frame_ancestors_.Get();
      case ContentSecurityPolicy::DirectiveType::kFormAction:
        return form_action_.Get();
      // Directives that have one default directive.
      case ContentSecurityPolicy::DirectiveType::kChildSrc:
        return OperativeDirective(child_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kConnectSrc:
        return OperativeDirective(connect_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kFontSrc:
        return OperativeDirective(font_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kImgSrc:
        return OperativeDirective(img_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kManifestSrc:
        return OperativeDirective(manifest_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kMediaSrc:
        return OperativeDirective(media_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kObjectSrc:
        return OperativeDirective(object_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kScriptSrc:
        return OperativeDirective(script_src_.Get());
      case ContentSecurityPolicy::DirectiveType::kStyleSrc:
        return OperativeDirective(style_src_.Get());
      // Directives that default to 'child-src' (which defaults to 'default-src')
      case ContentSecurityPolicy::DirectiveType::kFrameSrc:
        return OperativeDirective(frame_src_.Get(),
                                  OperativeDirective(child_src_.Get()));
      // Directives that default to 'script-src' (which defaults to 'default-src')
      case ContentSecurityPolicy::DirectiveType::kWorkerSrc:
        return OperativeDirective(worker_src_.Get(),
                                  OperativeDirective(script_src_.Get()));
      default:
        return nullptr;
    }
  }
  
  SourceListDirectiveVector CSPDirectiveList::GetSourceVector(
      const ContentSecurityPolicy::DirectiveType& type,
      const CSPDirectiveListVector& policies) {
    SourceListDirectiveVector source_list_directives;
    for (const auto& policy : policies) {
      if (SourceListDirective* directive = policy->OperativeDirective(type)) {
        if (directive->IsNone())
          return SourceListDirectiveVector(1, directive);
        source_list_directives.push_back(directive);
      }
    }
  
    return source_list_directives;
  }
  
  bool CSPDirectiveList::Subsumes(const CSPDirectiveListVector& other) {
    // A white-list of directives that we consider for subsumption.
    // See more about source lists here:
    // https://w3c.github.io/webappsec-csp/#framework-directive-source-list
    ContentSecurityPolicy::DirectiveType directives[] = {
        ContentSecurityPolicy::DirectiveType::kChildSrc,
        ContentSecurityPolicy::DirectiveType::kConnectSrc,
        ContentSecurityPolicy::DirectiveType::kFontSrc,
        ContentSecurityPolicy::DirectiveType::kFrameSrc,
        ContentSecurityPolicy::DirectiveType::kImgSrc,
        ContentSecurityPolicy::DirectiveType::kManifestSrc,
        ContentSecurityPolicy::DirectiveType::kMediaSrc,
        ContentSecurityPolicy::DirectiveType::kObjectSrc,
        ContentSecurityPolicy::DirectiveType::kScriptSrc,
        ContentSecurityPolicy::DirectiveType::kStyleSrc,
        ContentSecurityPolicy::DirectiveType::kWorkerSrc,
        ContentSecurityPolicy::DirectiveType::kBaseURI,
        ContentSecurityPolicy::DirectiveType::kFrameAncestors,
        ContentSecurityPolicy::DirectiveType::kFormAction};
  
    for (const auto& directive : directives) {
      // There should only be one SourceListDirective for each directive in
      // Embedding-CSP.
      SourceListDirectiveVector required_list =
          GetSourceVector(directive, CSPDirectiveListVector(1, this));
      if (!required_list.size())
        continue;
      SourceListDirective* required = required_list[0];
      // Aggregate all serialized source lists of the returned CSP into a vector
      // based on a directive type, defaulting accordingly (for example, to
      // `default-src`).
      SourceListDirectiveVector returned = GetSourceVector(directive, other);
      // TODO(amalika): Add checks for plugin-types, sandbox, disown-opener,
      // navigation-to, worker-src.
      if (!required->Subsumes(returned))
        return false;
    }
  
    if (!HasPluginTypes())
      return true;
  
    HeapVector<Member<MediaListDirective>> plugin_types_other;
    for (const auto& policy : other) {
      if (policy->HasPluginTypes())
        plugin_types_other.push_back(policy->plugin_types_);
    }
  
    return plugin_types_->Subsumes(plugin_types_other);
  }
  
  WebContentSecurityPolicy CSPDirectiveList::ExposeForNavigationalChecks() const {
    WebContentSecurityPolicy policy;
    policy.disposition = static_cast<WebContentSecurityPolicyType>(header_type_);
    policy.source = static_cast<WebContentSecurityPolicySource>(header_source_);
    std::vector<WebContentSecurityPolicyDirective> directives;
    for (const auto& directive :
         {child_src_, default_src_, form_action_, frame_src_}) {
      if (directive) {
        directives.push_back(WebContentSecurityPolicyDirective{
            directive->DirectiveName(),
            directive->ExposeForNavigationalChecks()});
      }
    }
    if (upgrade_insecure_requests_) {
      directives.push_back(WebContentSecurityPolicyDirective{
          blink::WebString("upgrade-insecure-requests"),
          WebContentSecurityPolicySourceList()});
    }
    policy.directives = directives;
    policy.report_endpoints = ReportEndpoints();
    policy.header = Header();
  
    return policy;
  }
  
  DEFINE_TRACE(CSPDirectiveList) {
    visitor->Trace(policy_);
    visitor->Trace(plugin_types_);
    visitor->Trace(base_uri_);
    visitor->Trace(child_src_);
    visitor->Trace(connect_src_);
    visitor->Trace(default_src_);
    visitor->Trace(font_src_);
    visitor->Trace(form_action_);
    visitor->Trace(frame_ancestors_);
    visitor->Trace(frame_src_);
    visitor->Trace(img_src_);
    visitor->Trace(media_src_);
    visitor->Trace(manifest_src_);
    visitor->Trace(object_src_);
    visitor->Trace(script_src_);
    visitor->Trace(style_src_);
    visitor->Trace(worker_src_);
  }
  
  }  // namespace blink
  