/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsCSPUtils_h___
#define nsCSPUtils_h___

#include "nsCOMPtr.h"
#include "nsIContentSecurityPolicy.h"
#include "nsILoadInfo.h"
#include "nsIURI.h"
#include "nsLiteralString.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include "mozilla/Logging.h"

class nsIChannel;

namespace mozilla::dom {
struct CSP;
class Document;
}  // namespace mozilla::dom

/* =============== Logging =================== */

void CSP_LogLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
                         const nsAString& aSourceName,
                         const nsAString& aSourceLine, uint32_t aLineNumber,
                         uint32_t aColumnNumber, uint32_t aFlags,
                         const nsACString& aCategory, uint64_t aInnerWindowID,
                         bool aFromPrivateWindow);

void CSP_GetLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
                         nsAString& outResult);

void CSP_LogStrMessage(const nsAString& aMsg);

void CSP_LogMessage(const nsAString& aMessage, const nsAString& aSourceName,
                    const nsAString& aSourceLine, uint32_t aLineNumber,
                    uint32_t aColumnNumber, uint32_t aFlags,
                    const nsACString& aCategory, uint64_t aInnerWindowID,
                    bool aFromPrivateWindow);

/* =============== Constant and Type Definitions ================== */

#define INLINE_STYLE_VIOLATION_OBSERVER_TOPIC \
  "violated base restriction: Inline Stylesheets will not apply"
#define INLINE_SCRIPT_VIOLATION_OBSERVER_TOPIC \
  "violated base restriction: Inline Scripts will not execute"
#define EVAL_VIOLATION_OBSERVER_TOPIC \
  "violated base restriction: Code will not be created from strings"
#define WASM_EVAL_VIOLATION_OBSERVER_TOPIC                                \
  "violated base restriction: WebAssembly code will not be created from " \
  "dynamically"
#define SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC "Inline Script had invalid nonce"
#define STYLE_NONCE_VIOLATION_OBSERVER_TOPIC "Inline Style had invalid nonce"
#define SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC "Inline Script had invalid hash"
#define STYLE_HASH_VIOLATION_OBSERVER_TOPIC "Inline Style had invalid hash"

// these strings map to the CSPDirectives in nsIContentSecurityPolicy
// NOTE: When implementing a new directive, you will need to add it here but
// also add a corresponding entry to the constants in
// nsIContentSecurityPolicy.idl and also create an entry for the new directive
// in nsCSPDirective::toDomCSPStruct() and add it to CSPDictionaries.webidl.
// Order of elements below important! Make sure it matches the order as in
// nsIContentSecurityPolicy.idl
static const char* CSPStrDirectives[] = {
    "-error-",                    // NO_DIRECTIVE
    "default-src",                // DEFAULT_SRC_DIRECTIVE
    "script-src",                 // SCRIPT_SRC_DIRECTIVE
    "object-src",                 // OBJECT_SRC_DIRECTIVE
    "style-src",                  // STYLE_SRC_DIRECTIVE
    "img-src",                    // IMG_SRC_DIRECTIVE
    "media-src",                  // MEDIA_SRC_DIRECTIVE
    "frame-src",                  // FRAME_SRC_DIRECTIVE
    "font-src",                   // FONT_SRC_DIRECTIVE
    "connect-src",                // CONNECT_SRC_DIRECTIVE
    "report-uri",                 // REPORT_URI_DIRECTIVE
    "frame-ancestors",            // FRAME_ANCESTORS_DIRECTIVE
    "reflected-xss",              // REFLECTED_XSS_DIRECTIVE
    "base-uri",                   // BASE_URI_DIRECTIVE
    "form-action",                // FORM_ACTION_DIRECTIVE
    "manifest-src",               // MANIFEST_SRC_DIRECTIVE
    "upgrade-insecure-requests",  // UPGRADE_IF_INSECURE_DIRECTIVE
    "child-src",                  // CHILD_SRC_DIRECTIVE
    "block-all-mixed-content",    // BLOCK_ALL_MIXED_CONTENT
    "sandbox",                    // SANDBOX_DIRECTIVE
    "worker-src",                 // WORKER_SRC_DIRECTIVE
    "navigate-to",                // NAVIGATE_TO_DIRECTIVE
    "script-src-elem",            // SCRIPT_SRC_ELEM_DIRECTIVE
    "script-src-attr",            // SCRIPT_SRC_ATTR_DIRECTIVE
    "style-src-elem",             // STYLE_SRC_ELEM_DIRECTIVE
    "style-src-attr",             // STYLE_SRC_ATTR_DIRECTIVE
};

inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) {
  return CSPStrDirectives[static_cast<uint32_t>(aDir)];
}

inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir) {
  nsString lowerDir = PromiseFlatString(aDir);
  ToLowerCase(lowerDir);

  uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
  for (uint32_t i = 1; i < numDirs; i++) {
    if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
      return static_cast<CSPDirective>(i);
    }
  }
  return nsIContentSecurityPolicy::NO_DIRECTIVE;
}

#define FOR_EACH_CSP_KEYWORD(MACRO)                             \
  MACRO(CSP_SELF, "'self'")                                     \
  MACRO(CSP_UNSAFE_INLINE, "'unsafe-inline'")                   \
  MACRO(CSP_UNSAFE_EVAL, "'unsafe-eval'")                       \
  MACRO(CSP_UNSAFE_HASHES, "'unsafe-hashes'")                   \
  MACRO(CSP_NONE, "'none'")                                     \
  MACRO(CSP_NONCE, "'nonce-")                                   \
  MACRO(CSP_REPORT_SAMPLE, "'report-sample'")                   \
  MACRO(CSP_STRICT_DYNAMIC, "'strict-dynamic'")                 \
  MACRO(CSP_UNSAFE_ALLOW_REDIRECTS, "'unsafe-allow-redirects'") \
  MACRO(CSP_WASM_UNSAFE_EVAL, "'wasm-unsafe-eval'")

enum CSPKeyword {
#define KEYWORD_ENUM(id_, string_) id_,
  FOR_EACH_CSP_KEYWORD(KEYWORD_ENUM)
#undef KEYWORD_ENUM

  // CSP_LAST_KEYWORD_VALUE always needs to be the last element in the enum
  // because we use it to calculate the size for the char* array.
  CSP_LAST_KEYWORD_VALUE,

  // Putting CSP_HASH after the delimitor, because CSP_HASH is not a valid
  // keyword (hash uses e.g. sha256, sha512) but we use CSP_HASH internally
  // to identify allowed hashes in ::allows.
  CSP_HASH
};

// The keywords, in UTF-8 form.
static const char* gCSPUTF8Keywords[] = {
#define KEYWORD_UTF8_LITERAL(id_, string_) string_,
    FOR_EACH_CSP_KEYWORD(KEYWORD_UTF8_LITERAL)
#undef KEYWORD_UTF8_LITERAL
};

// The keywords, in UTF-16 form.
static const char16_t* gCSPUTF16Keywords[] = {
#define KEYWORD_UTF16_LITERAL(id_, string_) u"" string_,
    FOR_EACH_CSP_KEYWORD(KEYWORD_UTF16_LITERAL)
#undef KEYWORD_UTF16_LITERAL
};

#undef FOR_EACH_CSP_KEYWORD

inline const char* CSP_EnumToUTF8Keyword(enum CSPKeyword aKey) {
  // Make sure all elements in enum CSPKeyword got added to gCSPUTF8Keywords.
  static_assert((sizeof(gCSPUTF8Keywords) / sizeof(gCSPUTF8Keywords[0]) ==
                 CSP_LAST_KEYWORD_VALUE),
                "CSP_LAST_KEYWORD_VALUE != length(gCSPUTF8Keywords)");

  if (static_cast<uint32_t>(aKey) <
      static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) {
    return gCSPUTF8Keywords[static_cast<uint32_t>(aKey)];
  }
  return "error: invalid keyword in CSP_EnumToUTF8Keyword";
}

inline const char16_t* CSP_EnumToUTF16Keyword(enum CSPKeyword aKey) {
  // Make sure all elements in enum CSPKeyword got added to gCSPUTF16Keywords.
  static_assert((sizeof(gCSPUTF16Keywords) / sizeof(gCSPUTF16Keywords[0]) ==
                 CSP_LAST_KEYWORD_VALUE),
                "CSP_LAST_KEYWORD_VALUE != length(gCSPUTF16Keywords)");

  if (static_cast<uint32_t>(aKey) <
      static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) {
    return gCSPUTF16Keywords[static_cast<uint32_t>(aKey)];
  }
  return u"error: invalid keyword in CSP_EnumToUTF16Keyword";
}

inline CSPKeyword CSP_UTF16KeywordToEnum(const nsAString& aKey) {
  nsString lowerKey = PromiseFlatString(aKey);
  ToLowerCase(lowerKey);

  for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
    if (lowerKey.Equals(gCSPUTF16Keywords[i])) {
      return static_cast<CSPKeyword>(i);
    }
  }
  NS_ASSERTION(false, "Can not convert unknown Keyword to Enum");
  return CSP_LAST_KEYWORD_VALUE;
}

nsresult CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
                                 const nsAString& aHeaderValue,
                                 bool aReportOnly);

/* =============== Helpers ================== */

class nsCSPHostSrc;

nsCSPHostSrc* CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI);
bool CSP_IsEmptyDirective(const nsAString& aValue, const nsAString& aDir);
bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);

class nsCSPSrcVisitor;

void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr);
bool CSP_ShouldResponseInheritCSP(nsIChannel* aChannel);

void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc,
                           const nsAString& aPolicyStr);

/* =============== nsCSPSrc ================== */

class nsCSPBaseSrc {
 public:
  nsCSPBaseSrc();
  virtual ~nsCSPBaseSrc();

  virtual bool permits(nsIURI* aUri, const nsAString& aNonce,
                       bool aWasRedirected, bool aReportOnly,
                       bool aUpgradeInsecure, bool aParserCreated) const;
  virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
                      bool aParserCreated) const;
  virtual bool visit(nsCSPSrcVisitor* aVisitor) const = 0;
  virtual void toString(nsAString& outStr) const = 0;

  virtual void invalidate() const { mInvalidated = true; }

  virtual bool isReportSample() const { return false; }

  virtual bool isHash() const { return false; }

 protected:
  // invalidate srcs if 'script-dynamic' is present or also invalidate
  // unsafe-inline' if nonce- or hash-source specified
  mutable bool mInvalidated;
};

/* =============== nsCSPSchemeSrc ============ */

class nsCSPSchemeSrc : public nsCSPBaseSrc {
 public:
  explicit nsCSPSchemeSrc(const nsAString& aScheme);
  virtual ~nsCSPSchemeSrc();

  bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
               bool aReportOnly, bool aUpgradeInsecure,
               bool aParserCreated) const override;
  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

  inline void getScheme(nsAString& outStr) const { outStr.Assign(mScheme); };

 private:
  nsString mScheme;
};

/* =============== nsCSPHostSrc ============== */

class nsCSPHostSrc : public nsCSPBaseSrc {
 public:
  explicit nsCSPHostSrc(const nsAString& aHost);
  virtual ~nsCSPHostSrc();

  bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
               bool aReportOnly, bool aUpgradeInsecure,
               bool aParserCreated) const override;
  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

  void setScheme(const nsAString& aScheme);
  void setPort(const nsAString& aPort);
  void appendPath(const nsAString& aPath);

  inline void setGeneratedFromSelfKeyword() const {
    mGeneratedFromSelfKeyword = true;
  }

  inline void setIsUniqueOrigin() const { mIsUniqueOrigin = true; }

  inline void setWithinFrameAncestorsDir(bool aValue) const {
    mWithinFrameAncstorsDir = aValue;
  }

  inline void getScheme(nsAString& outStr) const { outStr.Assign(mScheme); };

  inline void getHost(nsAString& outStr) const { outStr.Assign(mHost); };

  inline void getPort(nsAString& outStr) const { outStr.Assign(mPort); };

  inline void getPath(nsAString& outStr) const { outStr.Assign(mPath); };

 private:
  nsString mScheme;
  nsString mHost;
  nsString mPort;
  nsString mPath;
  mutable bool mGeneratedFromSelfKeyword;
  mutable bool mIsUniqueOrigin;
  mutable bool mWithinFrameAncstorsDir;
};

/* =============== nsCSPKeywordSrc ============ */

class nsCSPKeywordSrc : public nsCSPBaseSrc {
 public:
  explicit nsCSPKeywordSrc(CSPKeyword aKeyword);
  virtual ~nsCSPKeywordSrc();

  bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
              bool aParserCreated) const override;
  bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
               bool aReportOnly, bool aUpgradeInsecure,
               bool aParserCreated) const override;
  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

  inline CSPKeyword getKeyword() const { return mKeyword; };

  inline void invalidate() const override {
    // keywords that need to invalidated
    if (mKeyword == CSP_SELF || mKeyword == CSP_UNSAFE_INLINE ||
        mKeyword == CSP_REPORT_SAMPLE) {
      mInvalidated = true;
    }
  }

  bool isReportSample() const override { return mKeyword == CSP_REPORT_SAMPLE; }

 private:
  CSPKeyword mKeyword;
};

/* =============== nsCSPNonceSource =========== */

class nsCSPNonceSrc : public nsCSPBaseSrc {
 public:
  explicit nsCSPNonceSrc(const nsAString& aNonce);
  virtual ~nsCSPNonceSrc();

  bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
               bool aReportOnly, bool aUpgradeInsecure,
               bool aParserCreated) const override;
  bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
              bool aParserCreated) const override;
  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

  inline void getNonce(nsAString& outStr) const { outStr.Assign(mNonce); };

  inline void invalidate() const override {
    // overwrite nsCSPBaseSRC::invalidate() and explicitily
    // do *not* invalidate, because 'strict-dynamic' should
    // not invalidate nonces.
  }

 private:
  nsString mNonce;
};

/* =============== nsCSPHashSource ============ */

class nsCSPHashSrc : public nsCSPBaseSrc {
 public:
  nsCSPHashSrc(const nsAString& algo, const nsAString& hash);
  virtual ~nsCSPHashSrc();

  bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
              bool aParserCreated) const override;
  void toString(nsAString& outStr) const override;
  bool visit(nsCSPSrcVisitor* aVisitor) const override;

  inline void getAlgorithm(nsAString& outStr) const {
    outStr.Assign(mAlgorithm);
  };

  inline void getHash(nsAString& outStr) const { outStr.Assign(mHash); };

  inline void invalidate() const override {
    // overwrite nsCSPBaseSRC::invalidate() and explicitily
    // do *not* invalidate, because 'strict-dynamic' should
    // not invalidate hashes.
  }

  bool isHash() const final { return true; }

 private:
  nsString mAlgorithm;
  nsString mHash;
};

/* =============== nsCSPReportURI ============ */

class nsCSPReportURI : public nsCSPBaseSrc {
 public:
  explicit nsCSPReportURI(nsIURI* aURI);
  virtual ~nsCSPReportURI();

  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

 private:
  nsCOMPtr<nsIURI> mReportURI;
};

/* =============== nsCSPSandboxFlags ================== */

class nsCSPSandboxFlags : public nsCSPBaseSrc {
 public:
  explicit nsCSPSandboxFlags(const nsAString& aFlags);
  virtual ~nsCSPSandboxFlags();

  bool visit(nsCSPSrcVisitor* aVisitor) const override;
  void toString(nsAString& outStr) const override;

 private:
  nsString mFlags;
};

/* =============== nsCSPSrcVisitor ================== */

class nsCSPSrcVisitor {
 public:
  virtual bool visitSchemeSrc(const nsCSPSchemeSrc& src) = 0;

  virtual bool visitHostSrc(const nsCSPHostSrc& src) = 0;

  virtual bool visitKeywordSrc(const nsCSPKeywordSrc& src) = 0;

  virtual bool visitNonceSrc(const nsCSPNonceSrc& src) = 0;

  virtual bool visitHashSrc(const nsCSPHashSrc& src) = 0;

 protected:
  explicit nsCSPSrcVisitor() = default;
  virtual ~nsCSPSrcVisitor() = default;
};

/* =============== nsCSPDirective ============= */

class nsCSPDirective {
 public:
  explicit nsCSPDirective(CSPDirective aDirective);
  virtual ~nsCSPDirective();

  virtual bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
                       nsIURI* aUri, const nsAString& aNonce,
                       bool aWasRedirected, bool aReportOnly,
                       bool aUpgradeInsecure, bool aParserCreated) const;
  virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
                      bool aParserCreated) const;
  virtual void toString(nsAString& outStr) const;
  void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;

  virtual void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs) {
    mSrcs = aSrcs.Clone();
  }

  inline bool isDefaultDirective() const {
    return mDirective == nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
  }

  virtual bool equals(CSPDirective aDirective) const;

  void getReportURIs(nsTArray<nsString>& outReportURIs) const;

  bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;

  virtual void getDirName(nsAString& outStr) const;

  bool hasReportSampleKeyword() const;

 protected:
  CSPDirective mDirective;
  nsTArray<nsCSPBaseSrc*> mSrcs;
};

/* =============== nsCSPChildSrcDirective ============= */

/*
 * In CSP 3 child-src is deprecated. For backwards compatibility
 * child-src needs to restrict:
 *   (*) frames, in case frame-src is not expicitly specified
 *   (*) workers, in case worker-src is not expicitly specified
 */
class nsCSPChildSrcDirective : public nsCSPDirective {
 public:
  explicit nsCSPChildSrcDirective(CSPDirective aDirective);
  virtual ~nsCSPChildSrcDirective();

  void setRestrictFrames() { mRestrictFrames = true; }

  void setRestrictWorkers() { mRestrictWorkers = true; }

  virtual bool equals(CSPDirective aDirective) const override;

 private:
  bool mRestrictFrames;
  bool mRestrictWorkers;
};

/* =============== nsCSPScriptSrcDirective ============= */

/*
 * In CSP 3 worker-src restricts workers, for backwards compatibily
 * script-src has to restrict workers as the ultimate fallback if
 * neither worker-src nor child-src is present in a CSP.
 */
class nsCSPScriptSrcDirective : public nsCSPDirective {
 public:
  explicit nsCSPScriptSrcDirective(CSPDirective aDirective);
  virtual ~nsCSPScriptSrcDirective();

  void setRestrictWorkers() { mRestrictWorkers = true; }
  void setRestrictScriptElem() { mRestrictScriptElem = true; }
  void setRestrictScriptAttr() { mRestrictScriptAttr = true; }

  bool equals(CSPDirective aDirective) const override;

 private:
  bool mRestrictWorkers = false;
  bool mRestrictScriptElem = false;
  bool mRestrictScriptAttr = false;
};

/* =============== nsCSPStyleSrcDirective ============= */

/*
 * In CSP 3 style-src is use as a fallback for style-src-elem and
 * style-src-attr.
 */
class nsCSPStyleSrcDirective : public nsCSPDirective {
 public:
  explicit nsCSPStyleSrcDirective(CSPDirective aDirective);
  virtual ~nsCSPStyleSrcDirective();

  void setRestrictStyleElem() { mRestrictStyleElem = true; }
  void setRestrictStyleAttr() { mRestrictStyleAttr = true; }

  bool equals(CSPDirective aDirective) const override;

 private:
  bool mRestrictStyleElem = false;
  bool mRestrictStyleAttr = false;
};

/* =============== nsBlockAllMixedContentDirective === */

class nsBlockAllMixedContentDirective : public nsCSPDirective {
 public:
  explicit nsBlockAllMixedContentDirective(CSPDirective aDirective);
  ~nsBlockAllMixedContentDirective();

  bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri,
               const nsAString& aNonce, bool aWasRedirected, bool aReportOnly,
               bool aUpgradeInsecure, bool aParserCreated) const override {
    return false;
  }

  bool permits(nsIURI* aUri) const { return false; }

  bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
              bool aParserCreated) const override {
    return false;
  }

  void toString(nsAString& outStr) const override;

  void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs) override {
    MOZ_ASSERT(false, "block-all-mixed-content does not hold any srcs");
  }

  void getDirName(nsAString& outStr) const override;
};

/* =============== nsUpgradeInsecureDirective === */

/*
 * Upgrading insecure requests includes the following actors:
 * (1) CSP:
 *     The CSP implementation allowlists the http-request
 *     in case the policy is executed in enforcement mode.
 *     The CSP implementation however does not allow http
 *     requests to succeed if executed in report-only mode.
 *     In such a case the CSP implementation reports the
 *     error back to the page.
 *
 * (2) MixedContent:
 *     The evalution of MixedContent allowlists all http
 *     requests with the promise that the http requests
 *     gets upgraded to https before any data is fetched
 *     from the network.
 *
 * (3) CORS:
 *     Does not consider the http request to be of a
 *     different origin in case the scheme is the only
 *     difference in otherwise matching URIs.
 *
 * (4) nsHttpChannel:
 *     Before connecting, the channel gets redirected
 *     to use https.
 *
 * (5) WebSocketChannel:
 *     Similar to the httpChannel, the websocketchannel
 *     gets upgraded from ws to wss.
 */
class nsUpgradeInsecureDirective : public nsCSPDirective {
 public:
  explicit nsUpgradeInsecureDirective(CSPDirective aDirective);
  ~nsUpgradeInsecureDirective();

  bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri,
               const nsAString& aNonce, bool aWasRedirected, bool aReportOnly,
               bool aUpgradeInsecure, bool aParserCreated) const override {
    return false;
  }

  bool permits(nsIURI* aUri) const { return false; }

  bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
              bool aParserCreated) const override {
    return false;
  }

  void toString(nsAString& outStr) const override;

  void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs) override {
    MOZ_ASSERT(false, "upgrade-insecure-requests does not hold any srcs");
  }

  void getDirName(nsAString& outStr) const override;
};

/* =============== nsCSPPolicy ================== */

class nsCSPPolicy {
 public:
  nsCSPPolicy();
  virtual ~nsCSPPolicy();

  bool permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo, nsIURI* aUri,
               bool aWasRedirected, bool aSpecific,
               nsAString& outViolatedDirective) const;
  bool allows(CSPDirective aDirective, enum CSPKeyword aKeyword,
              const nsAString& aHashOrNonce, bool aParserCreated) const;
  void toString(nsAString& outStr) const;
  void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;

  inline void addDirective(nsCSPDirective* aDir) {
    mDirectives.AppendElement(aDir);
  }

  inline void addUpgradeInsecDir(nsUpgradeInsecureDirective* aDir) {
    mUpgradeInsecDir = aDir;
    addDirective(aDir);
  }

  bool hasDirective(CSPDirective aDir) const;

  inline void setDeliveredViaMetaTagFlag(bool aFlag) {
    mDeliveredViaMetaTag = aFlag;
  }

  inline bool getDeliveredViaMetaTagFlag() const {
    return mDeliveredViaMetaTag;
  }

  inline void setReportOnlyFlag(bool aFlag) { mReportOnly = aFlag; }

  inline bool getReportOnlyFlag() const { return mReportOnly; }

  void getReportURIs(nsTArray<nsString>& outReportURIs) const;

  void getDirectiveStringAndReportSampleForContentType(
      CSPDirective aDirective, nsAString& outDirective,
      bool* aReportSample) const;

  void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;

  uint32_t getSandboxFlags() const;

  inline uint32_t getNumDirectives() const { return mDirectives.Length(); }

  bool visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const;

  bool allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
                        bool aEnforceAllowlist) const;

 private:
  nsUpgradeInsecureDirective* mUpgradeInsecDir;
  nsTArray<nsCSPDirective*> mDirectives;
  bool mReportOnly;
  bool mDeliveredViaMetaTag;
};

#endif /* nsCSPUtils_h___ */
