1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
/* 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_net_EarlyHintPreloader_h
#define mozilla_net_EarlyHintPreloader_h
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/Maybe.h"
#include "mozilla/PreloadHashKey.h"
#include "NeckoCommon.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsHashtablesFwd.h"
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsIMultiPartChannel.h"
#include "nsIRedirectResultListener.h"
#include "nsIStreamListener.h"
#include "nsITimer.h"
#include "nsNetUtil.h"
class nsAttrValue;
class nsICookieJarSettings;
class nsILoadContext;
class nsIPrincipal;
class nsIReferrerInfo;
namespace mozilla::dom {
class CanonicalBrowsingContext;
}
namespace mozilla::net {
class EarlyHintPreloader;
class EarlyHintConnectArgs;
class ParentChannelListener;
struct LinkHeader;
// class keeping track of all ongoing early hints
class OngoingEarlyHints final {
public:
NS_INLINE_DECL_REFCOUNTING(OngoingEarlyHints)
OngoingEarlyHints() = default;
// returns whether a preload with that key already existed
bool Contains(const PreloadHashKey& aKey);
bool Add(const PreloadHashKey& aKey, RefPtr<EarlyHintPreloader> aPreloader);
void CancelAll(const nsACString& aReason);
// registers all channels and returns the ids
void RegisterLinksAndGetConnectArgs(
dom::ContentParentId aCpId, nsTArray<EarlyHintConnectArgs>& aOutLinks);
private:
~OngoingEarlyHints() = default;
// We need to do two things requiring two separate variables to keep track of
// preloads:
// - deduplicate Link headers when starting preloads, therefore we store them
// hashset with PreloadHashKey to look up whether we started the preload
// already
// - pass link headers in order they were received when passing all started
// preloads to the content process, therefore we store them in a nsTArray
nsTHashSet<PreloadHashKey> mStartedPreloads;
nsTArray<RefPtr<EarlyHintPreloader>> mPreloaders;
};
class EarlyHintPreloader final : public nsIStreamListener,
public nsIChannelEventSink,
public nsIRedirectResultListener,
public nsIInterfaceRequestor,
public nsIMultiPartChannelListener,
public nsINamed,
public nsITimerCallback {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIREDIRECTRESULTLISTENER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIMULTIPARTCHANNELLISTENER
// required by NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_DECL_NSITIMERCALLBACK
public:
// Create and insert a preload into OngoingEarlyHints if the same preload
// wasn't already issued and the LinkHeader can be parsed correctly.
static void MaybeCreateAndInsertPreload(
OngoingEarlyHints* aOngoingEarlyHints, const LinkHeader& aHeader,
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
nsICookieJarSettings* aCookieJarSettings,
const nsACString& aReferrerPolicy, const nsACString& aCSPHeader,
uint64_t aBrowsingContextID,
dom::CanonicalBrowsingContext* aLoadingBrowsingContext,
bool aIsModulepreload);
// register Channel to EarlyHintRegistrar. Returns true and sets connect args
// if successful
bool Register(dom::ContentParentId aCpId, EarlyHintConnectArgs& aOut);
// Allows EarlyHintRegistrar to check if the correct content process accesses
// this preload. Preventing compromised content processes to access Early Hint
// preloads from other origins
bool IsFromContentParent(dom::ContentParentId aCpId) const;
// Should be called by the preloader service when the preload is not
// needed after all, because the final response returns a non-2xx status
// code. If aDeleteEntry is false, the calling function MUST make sure that
// the EarlyHintPreloader is not in the EarlyHintRegistrar anymore. Because
// after this function, the EarlyHintPreloader can't connect back to the
// parent anymore.
nsresult CancelChannel(nsresult aStatus, const nsACString& aReason,
bool aDeleteEntry);
void OnParentReady(nsIParentChannel* aParent);
private:
void SetParentChannel();
void InvokeStreamListenerFunctions();
EarlyHintPreloader();
~EarlyHintPreloader();
static Maybe<PreloadHashKey> GenerateHashKey(ASDestination aAs, nsIURI* aURI,
nsIPrincipal* aPrincipal,
CORSMode corsMode,
bool aIsModulepreload);
static nsSecurityFlags ComputeSecurityFlags(CORSMode aCORSMode,
ASDestination aAs);
// call to start the preload
nsresult OpenChannel(nsIURI* aURI, nsIPrincipal* aPrincipal,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType,
nsIReferrerInfo* aReferrerInfo,
nsICookieJarSettings* aCookieJarSettings,
uint64_t aBrowsingContextID);
void PriorizeAsPreload();
void SetLinkHeader(const LinkHeader& aLinkHeader);
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIChannel> mRedirectChannel;
dom::ContentParentId mCpId;
EarlyHintConnectArgs mConnectArgs;
// Copy behavior from DocumentLoadListener.h:
// https://searchfox.org/mozilla-central/rev/c0bed29d643393af6ebe77aa31455f283f169202/netwerk/ipc/DocumentLoadListener.h#487-512
// The set of nsIStreamListener functions that got called on this
// listener, so that we can replay them onto the replacement channel's
// listener. This should generally only be OnStartRequest, since we
// Suspend() the channel at that point, but it can fail sometimes
// so we have to support holding a list.
nsTArray<StreamListenerFunction> mStreamListenerFunctions;
// Set to true once OnStartRequest is called
bool mOnStartRequestCalled = false;
// Set to true if we suspended mChannel in the OnStartRequest call
bool mSuspended = false;
nsCOMPtr<nsIParentChannel> mParent;
// Set to true after we've received the last OnStopRequest, and shouldn't
// setup a reference from the ParentChannelListener to the replacement
// channel.
bool mIsFinished = false;
RefPtr<ParentChannelListener> mParentListener;
nsCOMPtr<nsITimer> mTimer;
// Hold the load context to provide data to web extension and anti tracking.
// See Bug 1836289 and Bug 1875268
nsCOMPtr<nsILoadContext> mLoadContext;
private:
// IMPORTANT: when adding new values, always add them to the end, otherwise
// it will mess up telemetry.
enum EHPreloaderState : uint32_t {
ePreloaderCreated = 0,
ePreloaderOpened,
ePreloaderUsed,
ePreloaderCancelled,
ePreloaderTimeout,
};
EHPreloaderState mState = ePreloaderCreated;
void SetState(EHPreloaderState aState) { mState = aState; }
};
inline nsISupports* ToSupports(EarlyHintPreloader* aObj) {
return static_cast<nsIInterfaceRequestor*>(aObj);
}
} // namespace mozilla::net
#endif // mozilla_net_EarlyHintPreloader_h
|