/* -*- 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 mozilla_dom_ReferrerInfo_h #define mozilla_dom_ReferrerInfo_h #include "nsCOMPtr.h" #include "nsIReferrerInfo.h" #include "nsReadableUtils.h" #include "mozilla/Maybe.h" #include "mozilla/HashFunctions.h" #include "mozilla/dom/ReferrerPolicyBinding.h" #define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1" // 041a129f-10ce-4bda-a60d-e027a26d5ed0 #define REFERRERINFO_CID \ { \ 0x041a129f, 0x10ce, 0x4bda, { \ 0xa6, 0x0d, 0xe0, 0x27, 0xa2, 0x6d, 0x5e, 0xd0 \ } \ } class nsIHttpChannel; class nsIURI; class nsIChannel; class nsILoadInfo; class nsINode; class nsIPrincipal; namespace mozilla { class StyleSheet; class URLAndReferrerInfo; namespace net { class HttpBaseChannel; class nsHttpChannel; } // namespace net } // namespace mozilla namespace mozilla::dom { /** * The ReferrerInfo class holds the raw referrer and potentially a referrer * policy which allows to query the computed referrer which should be applied to * a channel as the actual referrer value. * * The ReferrerInfo class solely contains readonly fields and represents a 1:1 * sync to the referrer header of the corresponding channel. In turn that means * the class is immutable - so any modifications require to clone the current * ReferrerInfo. * * For example if a request undergoes a redirect, the new channel * will need a new ReferrerInfo clone with members being updated accordingly. */ class ReferrerInfo : public nsIReferrerInfo { public: typedef enum ReferrerPolicy ReferrerPolicyEnum; ReferrerInfo(); explicit ReferrerInfo( nsIURI* aOriginalReferrer, ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty, bool aSendReferrer = true, const Maybe<nsCString>& aComputedReferrer = Maybe<nsCString>()); // Creates already initialized ReferrerInfo from an element or a document. explicit ReferrerInfo(const Element&); explicit ReferrerInfo(const Document&); // create an exact copy of the ReferrerInfo already_AddRefed<ReferrerInfo> Clone() const; // create an copy of the ReferrerInfo with new referrer policy already_AddRefed<ReferrerInfo> CloneWithNewPolicy( ReferrerPolicyEnum aPolicy) const; // create an copy of the ReferrerInfo with new send referrer already_AddRefed<ReferrerInfo> CloneWithNewSendReferrer( bool aSendReferrer) const; // create an copy of the ReferrerInfo with new original referrer already_AddRefed<ReferrerInfo> CloneWithNewOriginalReferrer( nsIURI* aOriginalReferrer) const; // Record the telemetry for the referrer policy. void RecordTelemetry(nsIHttpChannel* aChannel); /* * Helper function to create a new ReferrerInfo object from other. We will not * pass in any computed values and override referrer policy if needed * * @param aOther the other referrerInfo object to init from. * @param aPolicyOverride referrer policy to override if necessary. */ static already_AddRefed<nsIReferrerInfo> CreateFromOtherAndPolicyOverride( nsIReferrerInfo* aOther, ReferrerPolicyEnum aPolicyOverride); /* * Helper function to create a new ReferrerInfo object from a given document * and override referrer policy if needed (for example, when parsing link * header or speculative loading). * * @param aDocument the document to init referrerInfo object. * @param aPolicyOverride referrer policy to override if necessary. */ static already_AddRefed<nsIReferrerInfo> CreateFromDocumentAndPolicyOverride( Document* aDoc, ReferrerPolicyEnum aPolicyOverride); /* * Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm * from the Referrer Policy specification. * * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer */ static already_AddRefed<nsIReferrerInfo> CreateForFetch( nsIPrincipal* aPrincipal, Document* aDoc); /** * Helper function to create new ReferrerInfo object from a given external * stylesheet. The returned nsIReferrerInfo object will be used for any * requests or resources referenced by the sheet. * * @param aSheet the stylesheet to init referrerInfo. * @param aPolicy referrer policy from header if there's any. */ static already_AddRefed<nsIReferrerInfo> CreateForExternalCSSResources( StyleSheet* aExternalSheet, ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty); /** * Helper function to create new ReferrerInfo object from a given document. * The returned nsIReferrerInfo object will be used for any requests or * resources referenced by internal stylesheet (for example style="" or * wrapped by <style> tag), as well as SVG resources. * * @param aDocument the document to init referrerInfo object. */ static already_AddRefed<nsIReferrerInfo> CreateForInternalCSSAndSVGResources( Document* aDocument); /** * Check whether the given referrer's scheme is allowed to be computed and * sent. The allowlist schemes are: http, https. */ static bool IsReferrerSchemeAllowed(nsIURI* aReferrer); /* * The Referrer Policy should be inherited for nested browsing contexts that * are not created from responses. Such as: srcdoc, data, blob. */ static bool ShouldResponseInheritReferrerInfo(nsIChannel* aChannel); /* * Check whether referrer is allowed to send in secure to insecure scenario. */ static nsresult HandleSecureToInsecureReferral(nsIURI* aOriginalURI, nsIURI* aURI, ReferrerPolicyEnum aPolicy, bool& aAllowed); /** * Returns true if the given channel is cross-origin request * * Computing whether the request is cross-origin may be expensive, so please * do that in cases where we're going to use this information later on. */ static bool IsCrossOriginRequest(nsIHttpChannel* aChannel); /** * Returns true if the given channel is cross-site request. */ static bool IsCrossSiteRequest(nsIHttpChannel* aChannel); /** * Returns true if the given channel is suppressed by Referrer-Policy header * and should set "null" to Origin header. */ static bool ShouldSetNullOriginHeader(net::HttpBaseChannel* aChannel, nsIURI* aOriginURI); /** * Getter for network.http.sendRefererHeader. */ static uint32_t GetUserReferrerSendingPolicy(); /** * Getter for network.http.referer.XOriginPolicy. */ static uint32_t GetUserXOriginSendingPolicy(); /** * Getter for network.http.referer.trimmingPolicy. */ static uint32_t GetUserTrimmingPolicy(); /** * Getter for network.http.referer.XOriginTrimmingPolicy. */ static uint32_t GetUserXOriginTrimmingPolicy(); /** * Return default referrer policy which is controlled by user * prefs: * network.http.referer.defaultPolicy for regular mode * network.http.referer.defaultPolicy.trackers for third-party trackers * in regular mode * network.http.referer.defaultPolicy.pbmode for private mode * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers * in private mode */ static ReferrerPolicyEnum GetDefaultReferrerPolicy( nsIHttpChannel* aChannel = nullptr, nsIURI* aURI = nullptr, bool aPrivateBrowsing = false); /** * Return default referrer policy for third party which is controlled by user * prefs: * network.http.referer.defaultPolicy.trackers for regular mode * network.http.referer.defaultPolicy.trackers.pbmode for private mode */ static ReferrerPolicyEnum GetDefaultThirdPartyReferrerPolicy( bool aPrivateBrowsing = false); /* * Helper function to parse ReferrerPolicy from meta tag referrer content. * For example: <meta name="referrer" content="origin"> * * @param aContent content string to be transformed into ReferrerPolicyEnum, * e.g. "origin". */ static ReferrerPolicyEnum ReferrerPolicyFromMetaString( const nsAString& aContent); /* * Helper function to parse ReferrerPolicy from string content of * referrerpolicy attribute. * For example: <a href="http://example.com" referrerpolicy="no-referrer"> * * @param aContent content string to be transformed into ReferrerPolicyEnum, * e.g. "no-referrer". */ static ReferrerPolicyEnum ReferrerPolicyAttributeFromString( const nsAString& aContent); /* * Helper function to parse ReferrerPolicy from string content of * Referrer-Policy header. * For example: Referrer-Policy: origin no-referrer * https://www.w3.org/tr/referrer-policy/#parse-referrer-policy-from-header * * @param aContent content string to be transformed into ReferrerPolicyEnum. * e.g. "origin no-referrer" */ static ReferrerPolicyEnum ReferrerPolicyFromHeaderString( const nsAString& aContent); /* * Helper function to convert ReferrerPolicy enum to string * * @param aPolicy referrer policy to convert. */ static const char* ReferrerPolicyToString(ReferrerPolicyEnum aPolicy); /** * Hash function for this object */ HashNumber Hash() const; NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIREFERRERINFO NS_DECL_NSISERIALIZABLE private: virtual ~ReferrerInfo() = default; ReferrerInfo(const ReferrerInfo& rhs); /* * Trimming policy when compute referrer, indicate how much information in the * referrer will be sent. Order matters here. */ enum TrimmingPolicy : uint32_t { ePolicyFullURI = 0, ePolicySchemeHostPortPath = 1, ePolicySchemeHostPort = 2, }; /* * Referrer sending policy, indicates type of action could trigger to send * referrer header, not send at all, send only with user's action (click on a * link) or send even with inline content request (image request). * Order matters here. */ enum ReferrerSendingPolicy : uint32_t { ePolicyNotSend = 0, ePolicySendWhenUserTrigger = 1, ePolicySendInlineContent = 2, }; /* * Sending referrer when cross origin policy, indicates when referrer should * be send when compare 2 origins. Order matters here. */ enum XOriginSendingPolicy : uint32_t { ePolicyAlwaysSend = 0, ePolicySendWhenSameDomain = 1, ePolicySendWhenSameHost = 2, }; /* * Handle user controlled pref network.http.referer.XOriginPolicy */ nsresult HandleUserXOriginSendingPolicy(nsIURI* aURI, nsIURI* aReferrer, bool& aAllowed) const; /* * Handle user controlled pref network.http.sendRefererHeader */ nsresult HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel, bool& aAllowed) const; /* * Compute trimming policy from user controlled prefs. * This function is called when we already made sure a nonempty referrer is * allowed to send. */ TrimmingPolicy ComputeTrimmingPolicy(nsIHttpChannel* aChannel) const; // HttpBaseChannel could access IsInitialized() and ComputeReferrer(); friend class mozilla::net::HttpBaseChannel; /* * Compute referrer for a given channel. The computation result then will be * stored in this class and then used to set the actual referrer header of * the channel. The computation could be controlled by several user prefs * which are defined in StaticPrefList.yaml (see StaticPrefList.yaml for more * details): * network.http.sendRefererHeader * network.http.referer.spoofSource * network.http.referer.hideOnionSource * network.http.referer.XOriginPolicy * network.http.referer.trimmingPolicy * network.http.referer.XOriginTrimmingPolicy */ nsresult ComputeReferrer(nsIHttpChannel* aChannel); /* * Check whether the ReferrerInfo has been initialized or not. */ bool IsInitialized() { return mInitialized; } // nsHttpChannel, Document could access IsPolicyOverrided(); friend class mozilla::net::nsHttpChannel; friend class mozilla::dom::Document; /* * Check whether if unset referrer policy is overrided by default or not */ bool IsPolicyOverrided() { return mOverridePolicyByDefault; } /* * Get origin string from a given valid referrer URI (http, https) * * @aReferrer - the full referrer URI * @aResult - the resulting aReferrer in string format. */ nsresult GetOriginFromReferrerURI(nsIURI* aReferrer, nsACString& aResult) const; /* * Trim a given referrer with a given a trimming policy, */ nsresult TrimReferrerWithPolicy(nsIURI* aReferrer, TrimmingPolicy aTrimmingPolicy, nsACString& aResult) const; /** * Returns true if we should ignore less restricted referrer policies, * including 'unsafe_url', 'no_referrer_when_downgrade' and * 'origin_when_cross_origin', for the given channel. We only apply this * restriction for cross-site requests. For the same-site request, we will * still allow overriding the default referrer policy with less restricted * one. * * Note that the channel triggered by the system and the extension will be * exempt from this restriction. */ bool ShouldIgnoreLessRestrictedPolicies( nsIHttpChannel* aChannel, const ReferrerPolicyEnum aPolicy) const; /* * Limit referrer length using the following ruleset: * - If the length of referrer URL is over max length, strip down to origin. * - If the origin is still over max length, remove the referrer entirely. * * This function comlements TrimReferrerPolicy and needs to be called right * after TrimReferrerPolicy. * * @aChannel - used to query information needed for logging to the console. * @aReferrer - the full referrer URI; needs to be identical to aReferrer * passed to TrimReferrerPolicy. * @aTrimmingPolicy - represents the trimming policy which was applied to the * referrer; needs to be identical to aTrimmingPolicy * passed to TrimReferrerPolicy. * @aInAndOutTrimmedReferrer - an in and outgoing argument representing the * referrer value. Please pass the result of * TrimReferrerWithPolicy as * aInAndOutTrimmedReferrer which will then be * reduced to the origin or completely truncated * in case the referrer value exceeds the length * limitation. */ nsresult LimitReferrerLength(nsIHttpChannel* aChannel, nsIURI* aReferrer, TrimmingPolicy aTrimmingPolicy, nsACString& aInAndOutTrimmedReferrer) const; /** * The helper function to read the old data format before gecko 100 for * deserialization. */ nsresult ReadTailDataBeforeGecko100(const uint32_t& aData, nsIObjectInputStream* aInputStream); /* * Write message to the error console */ void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg, const nsTArray<nsString>& aParams) const; friend class mozilla::URLAndReferrerInfo; nsCOMPtr<nsIURI> mOriginalReferrer; ReferrerPolicyEnum mPolicy; // The referrer policy that has been set originally for the channel. Note that // the policy may have been overridden by the default referrer policy, so we // need to keep track of this if we need to recover the original referrer // policy. ReferrerPolicyEnum mOriginalPolicy; // Indicates if the referrer should be sent or not even when it's available // (default is true). bool mSendReferrer; // Since the ReferrerInfo is immutable, we use this member as a helper to // ensure no one can call e.g. init() twice to modify state of the // ReferrerInfo. bool mInitialized; // Indicates if unset referrer policy is overrided by default bool mOverridePolicyByDefault; // Store a computed referrer for a given channel Maybe<nsCString> mComputedReferrer; #ifdef DEBUG // Indicates if the telemetry has been recorded. This is used to make sure the // telemetry will be only recored once. bool mTelemetryRecorded = false; #endif // DEBUG }; } // namespace mozilla::dom #endif // mozilla_dom_ReferrerInfo_h