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