/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: ft=cpp tw=78 sw=2 et ts=2 sts=2 cin
 * 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/. */

#include "nsISupports.idl"
#include "nsIContentPolicy.idl"

interface nsIDOMDocument;
interface nsINode;
interface nsIPrincipal;

%{C++
#include "nsTArray.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/LoadTainting.h"
%}

[ref] native const_nsIPrincipalArray(const nsTArray<nsCOMPtr<nsIPrincipal>>);
native OriginAttributes(mozilla::OriginAttributes);
[ref] native const_OriginAttributesRef(const mozilla::OriginAttributes);

typedef unsigned long nsSecurityFlags;

/**
 * An nsILoadOwner represents per-load information about who started the load.
 */
[scriptable, builtinclass, uuid(2ab4c77c-78e6-11e5-8bcf-feff819cdc9f)]
interface nsILoadInfo : nsISupports
{
  /**
   * No special security flags:
   */
  const unsigned long SEC_NORMAL = 0;

  /**
   * The following five flags determine the security mode and hence what kind of
   * security checks should be performed throughout the lifetime of the channel.
   *
   *    * SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS
   *    * SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
   *    * SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
   *    * SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
   *    * SEC_REQUIRE_CORS_DATA_INHERITS
   *
   * Exactly one of these flags are required to be set in order to allow
   * the channel to perform the correct security checks (SOP, CORS, ...) and
   * return the correct result principal. If none or more than one of these
   * flags are set AsyncOpen2 will fail.
   */

  /*
   * Enforce the same origin policy where data: loads inherit
   * the principal.
   */
  const unsigned long SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS = (1<<0);

  /*
   * Enforce the same origin policy but data: loads are blocked.
   */
  const unsigned long SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED = (1<<1);

  /**
   * Allow loads from other origins. Loads from data: will inherit
   * the principal of the origin that triggered the load.
   * Commonly used by plain <img>, <video>, <link rel=stylesheet> etc.
   */
   const unsigned long SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS = (1<<2);

  /**
   * Allow loads from other origins. Loads from data: will be allowed,
   * but the resulting resource will get a null principal.
   * Used in blink/webkit for <iframe>s. Likely also the mode
   * that should be used by most Chrome code.
   */
  const unsigned long SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL = (1<<3);

  /**
   * Allow loads from any origin, but require CORS for cross-origin
   * loads. Loads from data: are allowed and the result will inherit
   * the principal of the origin that triggered the load.
   * Commonly used by <img crossorigin>, <video crossorigin>,
   * XHR, fetch(), etc.
   */
  const unsigned long SEC_REQUIRE_CORS_DATA_INHERITS = (1<<4);

  /**
   * Use this flag in addition to SEC_REQUIRE_CORS_DATA_INHERITS
   * to make cross-origin CORS loads happen with credentials
   * (such as cookies and client side certs).
   */
  const unsigned long SEC_REQUIRE_CORS_WITH_CREDENTIALS = (1<<5);

  /**
   * Force inheriting of the Principal. The resulting resource will use the
   * principal of the document which is doing the load. Setting this flag
   * will cause GetChannelResultPrincipal to return the same principal as
   * the loading principal that's passed in when creating the channel.
   *
   * This will happen independently of the scheme of the URI that the
   * channel is loading.
   *
   * So if the loading document comes from "http://a.com/", and the channel
   * is loading the URI "http://b.com/whatever", GetChannelResultPrincipal
   * will return a principal from "http://a.com/".
   *
   * This flag can not be used together with SEC_SANDBOXED.
   */
  const unsigned long SEC_FORCE_INHERIT_PRINCIPAL = (1<<6);

  /**
   * Sandbox the load. The resulting resource will use a freshly created
   * null principal. So GetChannelResultPrincipal will always return a
   * null principal whenever this flag is set.
   *
   * This will happen independently of the scheme of the URI that the
   * channel is loading.
   *
   * This flag can not be used together with SEC_FORCE_INHERIT_PRINCIPAL.
   */
  const unsigned long SEC_SANDBOXED = (1<<7);

  /**
   * Inherit the Principal for about:blank.
   */
  const unsigned long SEC_ABOUT_BLANK_INHERITS = (1<<8);

  /**
   * Allow chrome: to bypass security checks.
   */
  const unsigned long SEC_ALLOW_CHROME = (1<<9);

  /**
   * The loadingPrincipal is the principal that is responsible for the load.
   * It is *NOT* the principal tied to the resource/URI that this
   * channel is loading, it's the principal of the resource's
   * caller or requester. For example, if this channel is loading
   * an image from http://b.com that is embedded in a document
   * who's origin is http://a.com, the loadingPrincipal is http://a.com.
   *
   * The loadingPrincipal will never be null.
   */
  readonly attribute nsIPrincipal loadingPrincipal;

  /**
   * A C++-friendly version of loadingPrincipal.
   */
  [noscript, notxpcom, nostdcall, binaryname(LoadingPrincipal)]
  nsIPrincipal binaryLoadingPrincipal();

  /**
   * The triggeringPrincipal is the principal that triggerd the load.
   * Most likely the triggeringPrincipal and the loadingPrincipal are the same,
   * in which case triggeringPrincipal returns the loadingPrincipal.
   * In some cases the loadingPrincipal and the triggeringPrincipal are different
   * however, e.g. a stylesheet may import a subresource. In that case the
   * stylesheet principal is the triggeringPrincipal and the document that loads
   * the stylesheet provides a loadingContext and hence the loadingPrincipal.
   *
   * If triggeringPrincipal and loadingPrincipal are the same, then
   * triggeringPrincipal returns loadingPrincipal.
   */
  readonly attribute nsIPrincipal triggeringPrincipal;

  /**
   * A C++-friendly version of triggeringPrincipal.
   */
  [noscript, notxpcom, nostdcall, binaryname(TriggeringPrincipal)]
  nsIPrincipal binaryTriggeringPrincipal();

  /**
   * The loadingDocument of the channel.
   *
   * The loadingDocument of a channel is the document that requested the
   * load of the resource. It is *not* the resource itself, it's the
   * resource's caller or requester in which the load is happening.
   *
   * <script> example: Assume a document who's origin is http://a.com embeds
   * a script from http://b.com. The loadingDocument for the channel
   * associated with the http://b.com script load is the document with origin
   * http://a.com
   *
   * <iframe> example: Assume a document with origin http://a.com embeds
   * <iframe src="http://b.com">. The loadingDocument for the channel associated
   * with the http://b.com network request is the document who's origin is
   * http://a.com. Now assume the iframe to http://b.com then further embeds
   * <script src="http://c.com">. The loadingDocument for the channel associated
   * with the http://c.com network request is the iframe with origin http://b.com.
   *
   * Warning: The loadingDocument can be null!
   */
  readonly attribute nsIDOMDocument loadingDocument;

  /**
   * A C++-friendly version of loadingDocument (loadingNode).
   * This is the node most proximally responsible for the load.
   */
  [noscript, notxpcom, nostdcall, binaryname(LoadingNode)]
  nsINode binaryLoadingNode();

  /**
   * The securityFlags of that channel.
   */
  readonly attribute nsSecurityFlags securityFlags;

%{ C++
  inline nsSecurityFlags GetSecurityFlags()
  {
    nsSecurityFlags result;
    mozilla::DebugOnly<nsresult> rv = GetSecurityFlags(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Allows to query only the security mode bits from above.
   */
  [infallible] readonly attribute unsigned long securityMode;

  /**
   * Determines whether credentials are sent with CORS requests.
   * Using this flag requires SEC_REQUIRE_CORS_DATA_INHERITS also to be set.
   */
  [infallible] readonly attribute boolean requireCorsWithCredentials;

  /**
   * If forceInheritPrincipal is true, the data coming from the channel should
   * use loadingPrincipal for its principal, even when the data is loaded over
   * http:// or another protocol that would normally use a URI-based principal.
   * This attribute will never be true when loadingSandboxed is true.
   */
  [infallible] readonly attribute boolean forceInheritPrincipal;

  /**
   * If loadingSandboxed is true, the data coming from the channel is
   * being loaded sandboxed, so it should have a nonce origin and
   * hence should use a NullPrincipal.
   */
  [infallible] readonly attribute boolean loadingSandboxed;

  /**
   * If aboutBlankInherits is true, then about:blank should inherit
   * the principal.
   */
  [infallible] readonly attribute boolean aboutBlankInherits;

  /**
   * If allowChrome is true, then use nsIScriptSecurityManager::ALLOW_CHROME
   * when calling CheckLoadURIWithPrincipal().
   */
  [infallible] readonly attribute boolean allowChrome;

  /**
   * The external contentPolicyType of the channel, used for security checks
   * like Mixed Content Blocking and Content Security Policy.
   *
   * Specifically, content policy types with _INTERNAL_ in their name will
   * never get returned from this attribute.
   */
  readonly attribute nsContentPolicyType externalContentPolicyType;

%{ C++
  inline nsContentPolicyType GetExternalContentPolicyType()
  {
    nsContentPolicyType result;
    mozilla::DebugOnly<nsresult> rv = GetExternalContentPolicyType(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * The internal contentPolicyType of the channel, used for constructing
   * RequestContext values when creating a fetch event for an intercepted
   * channel.
   *
   * This should not be used for the purposes of security checks, since
   * the content policy implementations cannot be expected to deal with
   * _INTERNAL_ values.  Please use the contentPolicyType attribute above
   * for that purpose.
   */
  [noscript, notxpcom]
  nsContentPolicyType internalContentPolicyType();

  /**
   * Returns true if document or any of the documents ancestors
   * up to the toplevel document make use of the CSP directive
   * 'upgrade-insecure-requests'. Used to identify upgrade
   * requests in e10s where the loadingDocument is not available.
   *
   * Warning: If the loadingDocument is null, then the
   * upgradeInsecureRequests is false.
   */
  readonly attribute boolean upgradeInsecureRequests;

%{ C++
  inline bool GetUpgradeInsecureRequests()
  {
    bool result;
    mozilla::DebugOnly<nsresult> rv = GetUpgradeInsecureRequests(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Typically these are the window IDs of the window in which the element being
   * loaded lives. However, if the element being loaded is <frame
   * src="foo.html"> (or, more generally, if the element QIs to
   * nsIFrameLoaderOwner) then the window IDs are for the window containing the
   * foo.html document. In this case, parentOuterWindowID is the window ID of
   * the window containing the <frame> element.
   *
   * Note that these window IDs can be 0 if the window is not
   * available. parentOuterWindowID will be the same as outerWindowID if the
   * window has no parent.
   */
  readonly attribute unsigned long long innerWindowID;
  readonly attribute unsigned long long outerWindowID;
  readonly attribute unsigned long long parentOuterWindowID;

%{ C++
  inline uint64_t GetInnerWindowID()
  {
    uint64_t result;
    mozilla::DebugOnly<nsresult> rv = GetInnerWindowID(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }

  inline uint64_t GetOuterWindowID()
  {
    uint64_t result;
    mozilla::DebugOnly<nsresult> rv = GetOuterWindowID(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }

  inline uint64_t GetParentOuterWindowID()
  {
    uint64_t result;
    mozilla::DebugOnly<nsresult> rv = GetParentOuterWindowID(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Customized OriginAttributes within LoadInfo to allow overwriting of the
   * default originAttributes from the loadingPrincipal.
   */
  [implicit_jscontext, binaryname(ScriptableOriginAttributes)]
  attribute jsval originAttributes;

  [noscript, nostdcall, binaryname(GetOriginAttributes)]
  OriginAttributes binaryGetOriginAttributes();

  [noscript, nostdcall, binaryname(SetOriginAttributes)]
  void binarySetOriginAttributes(in const_OriginAttributesRef aOriginAttrs);

%{ C++
  inline mozilla::OriginAttributes GetOriginAttributes()
  {
    mozilla::OriginAttributes result;
    mozilla::DebugOnly<nsresult> rv = GetOriginAttributes(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Whenever a channel is openend by asyncOpen2() [or also open2()],
   * lets set this flag so that redirects of such channels are also
   * openend using asyncOpen2() [open2()].
   *
   * Please note, once the flag is set to true it must remain true
   * throughout the lifetime of the channel. Trying to set it
   * to anything else than true will be discareded.
   *
   */
  attribute bool enforceSecurity;

%{ C++
  inline bool GetEnforceSecurity()
  {
    bool result;
    mozilla::DebugOnly<nsresult> rv = GetEnforceSecurity(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Whenever a channel is evaluated by the ContentSecurityManager
   * the first time, we set this flag to true to indicate that
   * subsequent calls of AsyncOpen2() do not have to enforce all
   * security checks again. E.g., after a redirect there is no
   * need to set up CORS again. We need this separate flag
   * because the redirectChain might also contain internal
   * redirects which might pollute the redirectChain so we can't
   * rely on the size of the redirectChain-array to query whether
   * a channel got redirected or not.
   *
   * Please note, once the flag is set to true it must remain true
   * throughout the lifetime of the channel. Trying to set it
   * to anything else than true will be discareded.
   *
   */
  attribute bool initialSecurityCheckDone;

%{ C++
  inline bool GetInitialSecurityCheckDone()
  {
    bool result;
    mozilla::DebugOnly<nsresult> rv = GetInitialSecurityCheckDone(&result);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    return result;
  }
%}

  /**
   * Whenever a channel gets redirected, append the principal of the
   * channel [before the channels got redirected] to the loadinfo,
   * so that at every point this array lets us reason about all the
   * redirects this channel went through.
   */
  void appendRedirectedPrincipal(in nsIPrincipal aPrincipal);

  /**
   * An array of nsIPrincipal that store the redirects associated with this
   * channel. This array is filled whether or not the channel has ever been
   * opened. The last element of the array is associated with the most recent
   * channel.
   */
  [implicit_jscontext]
  readonly attribute jsval redirectChain;

  /**
   * A C++-friendly version of redirectChain.
   * Please note that this array has the same lifetime as the
   * loadInfo object - use with caution!
   */
  [noscript, notxpcom, nostdcall, binaryname(RedirectChain)]
  const_nsIPrincipalArray binaryRedirectChain();

  /**
  * Constants reflecting the channel tainting.  These are mainly defined here
  * for script.  Internal C++ code should use the enum defined in LoadTainting.h.
  * See LoadTainting.h for documentation.
  */
  const unsigned long TAINTING_BASIC = 0;
  const unsigned long TAINTING_CORS = 1;
  const unsigned long TAINTING_OPAQUE = 2;

  /**
   * Determine the associated channel's current tainting.  Note, this can
   * change due to a service worker intercept, so it should be checked after
   * OnStartRequest() fires.
   */
  readonly attribute unsigned long tainting;

  /**
   * Note a new tainting level and possibly increase the current tainting
   * to match.  If the tainting level is already greater than the given
   * value, then there is no effect.  It is not possible to reduce the tainting
   * level on an existing channel/loadinfo.
   */
  void maybeIncreaseTainting(in unsigned long aTainting);

  /**
   * Various helper code to provide more convenient C++ access to the tainting
   * attribute and maybeIncreaseTainting().
   */
%{C++
  static_assert(TAINTING_BASIC == static_cast<uint32_t>(mozilla::LoadTainting::Basic),
                "basic tainting enums should match");
  static_assert(TAINTING_CORS == static_cast<uint32_t>(mozilla::LoadTainting::CORS),
                "cors tainting enums should match");
  static_assert(TAINTING_OPAQUE == static_cast<uint32_t>(mozilla::LoadTainting::Opaque),
                "opaque tainting enums should match");

  mozilla::LoadTainting GetTainting()
  {
    uint32_t tainting = TAINTING_BASIC;
    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetTainting(&tainting)));
    return static_cast<mozilla::LoadTainting>(tainting);
  }

  void MaybeIncreaseTainting(mozilla::LoadTainting aTainting)
  {
    uint32_t tainting = static_cast<uint32_t>(aTainting);
    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(MaybeIncreaseTainting(tainting)));
  }
%}
};
