Fetch.h (11491B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_Fetch_h 8 #define mozilla_dom_Fetch_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/dom/AbortSignal.h" 12 #include "mozilla/dom/BodyConsumer.h" 13 #include "mozilla/dom/FetchStreamReader.h" 14 #include "mozilla/dom/Promise.h" 15 #include "mozilla/dom/ReadableStream.h" 16 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h" 17 #include "mozilla/dom/RequestBinding.h" 18 #include "mozilla/dom/workerinternals/RuntimeService.h" 19 #include "nsCOMPtr.h" 20 #include "nsError.h" 21 #include "nsProxyRelease.h" 22 #include "nsString.h" 23 24 class nsIGlobalObject; 25 class nsIEventTarget; 26 27 namespace mozilla { 28 class ErrorResult; 29 30 namespace ipc { 31 class PrincipalInfo; 32 } // namespace ipc 33 34 namespace dom { 35 36 class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; 37 class 38 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString; 39 class BlobImpl; 40 class InternalRequest; 41 class 42 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; 43 44 class ReadableStreamDefaultReader; 45 class RequestOrUTF8String; 46 class WorkerPrivate; 47 48 enum class CallerType : uint32_t; 49 50 already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal, 51 const RequestOrUTF8String& aInput, 52 const RequestInit& aInit, 53 CallerType aCallerType, 54 ErrorResult& aRv); 55 56 nsresult UpdateRequestReferrer(nsIGlobalObject* aGlobal, 57 InternalRequest* aRequest); 58 59 namespace fetch { 60 using BodyInit = 61 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; 62 using ResponseBodyInit = 63 BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString; 64 using OwningBodyInit = 65 OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString; 66 }; // namespace fetch 67 68 /* 69 * Creates an nsIInputStream based on the fetch specifications 'extract a byte 70 * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract. 71 * Stores content type in out param aContentType. 72 */ 73 nsresult ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit, 74 nsIInputStream** aStream, 75 nsCString& aContentType, 76 uint64_t& aContentLength); 77 78 /* 79 * Non-owning version. 80 */ 81 nsresult ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit, 82 nsIInputStream** aStream, 83 nsCString& aContentType, 84 uint64_t& aContentLength); 85 86 /* 87 * Non-owning version. This method should go away when BodyInit will contain 88 * ReadableStream. 89 */ 90 nsresult ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit, 91 nsIInputStream** aStream, 92 nsCString& aContentType, 93 uint64_t& aContentLength); 94 95 /* 96 * FetchBody's body consumption uses nsIInputStreamPump to read from the 97 * underlying stream to a block of memory, which is then adopted by 98 * ContinueConsumeBody() and converted to the right type based on the JS 99 * function called. 100 * 101 * Use of the nsIInputStreamPump complicates things on the worker thread. 102 * The solution used here is similar to WebSockets. 103 * The difference is that we are only interested in completion and not data 104 * events, and nsIInputStreamPump can only deliver completion on the main 105 * thread. 106 * 107 * Before starting the pump on the main thread, we addref the FetchBody to keep 108 * it alive. Then we add a feature, to track the status of the worker. 109 * 110 * ContinueConsumeBody() is the function that cleans things up in both success 111 * and error conditions and so all callers call it with the appropriate status. 112 * 113 * Once the read is initiated on the main thread there are two possibilities. 114 * 115 * 1) Pump finishes before worker has finished Running. 116 * In this case we adopt the data and dispatch a runnable to the worker, 117 * which derefs FetchBody and removes the feature and resolves the Promise. 118 * 119 * 2) Pump still working while worker has stopped Running. 120 * The feature is Notify()ed and ContinueConsumeBody() is called with 121 * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to 122 * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly 123 * held by it) until pump->Cancel() is called. OnStreamComplete() will not 124 * do anything if the error code is NS_BINDING_ABORTED, so we don't have to 125 * worry about keeping anything alive. 126 * 127 * The pump is always released on the main thread. 128 */ 129 130 class FetchBodyBase : public nsISupports { 131 public: 132 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 133 NS_DECL_CYCLE_COLLECTION_CLASS(FetchBodyBase) 134 135 protected: 136 virtual ~FetchBodyBase() = default; 137 138 RefPtr<ReadableStream> mReadableStreamBody; 139 }; 140 141 template <class Derived> 142 class FetchBody : public FetchBodyBase, public AbortFollower { 143 public: 144 using FetchBodyBase::QueryInterface; 145 146 NS_INLINE_DECL_REFCOUNTING_INHERITED(FetchBody, FetchBodyBase) 147 148 bool BodyUsed() const; 149 150 already_AddRefed<Promise> ArrayBuffer(JSContext* aCx, ErrorResult& aRv) { 151 return ConsumeBody(aCx, BodyConsumer::ConsumeType::ArrayBuffer, aRv); 152 } 153 154 already_AddRefed<Promise> Blob(JSContext* aCx, ErrorResult& aRv) { 155 return ConsumeBody(aCx, BodyConsumer::ConsumeType::Blob, aRv); 156 } 157 158 already_AddRefed<Promise> Bytes(JSContext* aCx, ErrorResult& aRv) { 159 return ConsumeBody(aCx, BodyConsumer::ConsumeType::Bytes, aRv); 160 } 161 162 already_AddRefed<Promise> FormData(JSContext* aCx, ErrorResult& aRv) { 163 return ConsumeBody(aCx, BodyConsumer::ConsumeType::FormData, aRv); 164 } 165 166 already_AddRefed<Promise> Json(JSContext* aCx, ErrorResult& aRv) { 167 return ConsumeBody(aCx, BodyConsumer::ConsumeType::JSON, aRv); 168 } 169 170 already_AddRefed<Promise> Text(JSContext* aCx, ErrorResult& aRv) { 171 return ConsumeBody(aCx, BodyConsumer::ConsumeType::Text, aRv); 172 } 173 174 already_AddRefed<ReadableStream> GetBody(JSContext* aCx, ErrorResult& aRv); 175 void GetMimeType(nsACString& aMimeType, nsACString& aMixedCaseMimeType); 176 177 const nsACString& BodyBlobURISpec() const; 178 179 const nsAString& BodyLocalPath() const; 180 181 // If the body contains a ReadableStream body object, this method produces a 182 // tee() of it. 183 // 184 // This is marked as a script boundary minimize changes required for 185 // annotation while we work out how to correctly annotate this code. 186 // Tracked in Bug 1750650. 187 MOZ_CAN_RUN_SCRIPT_BOUNDARY 188 void MaybeTeeReadableStreamBody(JSContext* aCx, ReadableStream** aBodyOut, 189 FetchStreamReader** aStreamReader, 190 nsIInputStream** aInputStream, 191 ErrorResult& aRv); 192 193 // Utility public methods accessed by various runnables. 194 195 // This method _must_ be called in order to set the body as used. If the body 196 // is a ReadableStream, this method will start reading the stream. 197 // More in details, this method does: 198 // 1) It uses an internal flag to track if the body is used. This is tracked 199 // separately from the ReadableStream disturbed state due to purely native 200 // streams. 201 // 2) If there is a ReadableStream reflector for the native stream it is 202 // Locked. 203 // 3) If there is a JS ReadableStream then we begin pumping it into the native 204 // body stream. This effectively locks and disturbs the stream. 205 // 206 // Note that JSContext is used only if there is a ReadableStream (this can 207 // happen because the body is a ReadableStream or because attribute body has 208 // already been used by content). If something goes wrong using 209 // ReadableStream, errors will be reported via ErrorResult and not as JS 210 // exceptions in JSContext. This is done in order to have a centralized error 211 // reporting way. 212 // 213 // Exceptions generated when reading from the ReadableStream are directly sent 214 // to the Console. 215 void SetBodyUsed(JSContext* aCx, ErrorResult& aRv); 216 217 virtual AbortSignalImpl* GetSignalImpl() const = 0; 218 219 virtual AbortSignalImpl* GetSignalImplToConsumeBody() const = 0; 220 221 // AbortFollower 222 void RunAbortAlgorithm() override; 223 224 already_AddRefed<Promise> ConsumeBody(JSContext* aCx, 225 BodyConsumer::ConsumeType aType, 226 ErrorResult& aRv); 227 228 protected: 229 nsCOMPtr<nsIGlobalObject> mOwner; 230 231 // This is the Reader used to retrieve data from the body. This needs to be 232 // traversed by subclasses. 233 RefPtr<FetchStreamReader> mFetchStreamReader; 234 235 explicit FetchBody(nsIGlobalObject* aOwner); 236 237 virtual ~FetchBody(); 238 239 void SetReadableStreamBody(JSContext* aCx, ReadableStream* aBody); 240 241 private: 242 Derived* DerivedClass() const { 243 return static_cast<Derived*>(const_cast<FetchBody*>(this)); 244 } 245 246 void LockStream(JSContext* aCx, ReadableStream* aStream, ErrorResult& aRv); 247 248 void AssertIsOnTargetThread() { 249 MOZ_ASSERT(NS_IsMainThread() == !GetCurrentThreadWorkerPrivate()); 250 } 251 252 // Only ever set once, always on target thread. 253 bool mBodyUsed; 254 255 // The main-thread event target for runnable dispatching. 256 nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget; 257 }; 258 259 class EmptyBody final : public FetchBody<EmptyBody> { 260 NS_DECL_ISUPPORTS_INHERITED 261 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(EmptyBody, 262 FetchBody<EmptyBody>) 263 264 public: 265 static already_AddRefed<EmptyBody> Create( 266 nsIGlobalObject* aGlobal, mozilla::ipc::PrincipalInfo* aPrincipalInfo, 267 AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType, 268 const nsACString& aMixedCaseMimeType, ErrorResult& aRv); 269 270 nsIGlobalObject* GetParentObject() const { return mOwner; } 271 272 AbortSignalImpl* GetSignalImpl() const override { return mAbortSignalImpl; } 273 AbortSignalImpl* GetSignalImplToConsumeBody() const final { return nullptr; } 274 275 const UniquePtr<mozilla::ipc::PrincipalInfo>& GetPrincipalInfo() const { 276 return mPrincipalInfo; 277 } 278 279 void GetMimeType(nsACString& aMimeType, nsACString& aMixedCaseMimeType) { 280 aMimeType = mMimeType; 281 aMixedCaseMimeType = mMixedCaseMimeType; 282 } 283 284 void GetBody(nsIInputStream** aStream, int64_t* aBodyLength = nullptr); 285 286 using FetchBody::BodyBlobURISpec; 287 288 const nsACString& BodyBlobURISpec() const { return EmptyCString(); } 289 290 using FetchBody::BodyLocalPath; 291 292 const nsAString& BodyLocalPath() const { return EmptyString(); } 293 294 private: 295 EmptyBody(nsIGlobalObject* aGlobal, 296 mozilla::ipc::PrincipalInfo* aPrincipalInfo, 297 AbortSignalImpl* aAbortSignalImpl, const nsACString& aMimeType, 298 const nsACString& aMixedCaseMimeType, 299 already_AddRefed<nsIInputStream> aBodyStream); 300 301 ~EmptyBody(); 302 303 UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo; 304 RefPtr<AbortSignalImpl> mAbortSignalImpl; 305 nsCString mMimeType; 306 nsCString mMixedCaseMimeType; 307 nsCOMPtr<nsIInputStream> mBodyStream; 308 }; 309 } // namespace dom 310 } // namespace mozilla 311 312 #endif // mozilla_dom_Fetch_h