diff options
Diffstat (limited to 'dom/xhr/XMLHttpRequestMainThread.h')
-rw-r--r-- | dom/xhr/XMLHttpRequestMainThread.h | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h new file mode 100644 index 0000000000..3f2d395991 --- /dev/null +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -0,0 +1,879 @@ +/* -*- 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_XMLHttpRequestMainThread_h +#define mozilla_dom_XMLHttpRequestMainThread_h + +#include <bitset> +#include "nsISupportsUtils.h" +#include "nsIURI.h" +#include "mozilla/dom/Document.h" +#include "nsIStreamListener.h" +#include "nsIChannelEventSink.h" +#include "nsIAsyncVerifyRedirectCallback.h" +#include "nsIDOMEventListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIHttpHeaderVisitor.h" +#include "nsIProgressEventSink.h" +#include "nsJSUtils.h" +#include "nsTArray.h" +#include "nsITimer.h" +#include "nsIPrincipal.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsISizeOfEventTarget.h" +#include "nsIInputStream.h" +#include "nsIContentSecurityPolicy.h" +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/NotNull.h" +#include "mozilla/dom/MutableBlobStorage.h" +#include "mozilla/dom/BodyExtractor.h" +#include "mozilla/dom/ClientInfo.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FormData.h" +#include "mozilla/dom/MimeType.h" +#include "mozilla/dom/PerformanceStorage.h" +#include "mozilla/dom/ServiceWorkerDescriptor.h" +#include "mozilla/dom/URLSearchParams.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/XMLHttpRequest.h" +#include "mozilla/dom/XMLHttpRequestBinding.h" +#include "mozilla/dom/XMLHttpRequestEventTarget.h" +#include "mozilla/dom/XMLHttpRequestString.h" +#include "mozilla/Encoding.h" +#include "nsBaseChannel.h" + +#ifdef Status +/* Xlib headers insist on this for some reason... Nuke it because + it'll override our member name */ +typedef Status __StatusTmp; +# undef Status +typedef __StatusTmp Status; +#endif + +class nsIHttpChannel; +class nsIJARChannel; +class nsILoadGroup; + +namespace mozilla { +class ProfileChunkedBuffer; + +namespace dom { + +class DOMString; +class XMLHttpRequestUpload; +class SerializedStackHolder; +struct OriginAttributesDictionary; + +// A helper for building up an ArrayBuffer object's data +// before creating the ArrayBuffer itself. Will do doubling +// based reallocation, up to an optional maximum growth given. +// +// When all the data has been appended, call GetArrayBuffer, +// passing in the JSContext* for which the ArrayBuffer object +// is to be created. This also implicitly resets the builder. +class ArrayBufferBuilder { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayBufferBuilder); + + ArrayBufferBuilder(); + + // Will truncate if aNewCap is < Length(). + bool SetCapacity(uint32_t aNewCap); + + // Append aDataLen bytes from data to the current buffer. If we + // need to grow the buffer, grow by doubling the size up to a + // maximum of aMaxGrowth (if given). If aDataLen is greater than + // what the new capacity would end up as, then grow by aDataLen. + // + // The data parameter must not overlap with anything beyond the + // builder's current valid contents [0..length) + bool Append(const uint8_t* aNewData, uint32_t aDataLen, + uint32_t aMaxGrowth = 0); + + uint32_t Length(); + uint32_t Capacity(); + + JSObject* TakeArrayBuffer(JSContext* aCx); + + // Memory mapping to starting position of file(aFile) in the zip + // package(aJarFile). + // + // The file in the zip package has to be uncompressed and the starting + // position of the file must be aligned according to array buffer settings + // in JS engine. + nsresult MapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile); + + private: + ~ArrayBufferBuilder(); + + ArrayBufferBuilder(const ArrayBufferBuilder&) = delete; + ArrayBufferBuilder& operator=(const ArrayBufferBuilder&) = delete; + ArrayBufferBuilder& operator=(const ArrayBufferBuilder&&) = delete; + + bool SetCapacityInternal(uint32_t aNewCap, const MutexAutoLock& aProofOfLock) + MOZ_REQUIRES(mMutex); + + static bool AreOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1, + const uint8_t* aStart2, uint32_t aLength2); + + Mutex mMutex; + + // All of these are protected by mMutex. + uint8_t* mDataPtr MOZ_GUARDED_BY(mMutex); + uint32_t mCapacity MOZ_GUARDED_BY(mMutex); + uint32_t mLength MOZ_GUARDED_BY(mMutex); + void* mMapPtr MOZ_GUARDED_BY(mMutex); + + // This is used in assertions only. + bool mNeutered; +}; + +class nsXMLHttpRequestXPCOMifier; + +class RequestHeaders { + struct RequestHeader { + nsCString mName; + nsCString mValue; + }; + nsTArray<RequestHeader> mHeaders; + RequestHeader* Find(const nsACString& aName); + + public: + class CharsetIterator { + bool mValid; + int32_t mCurPos, mCurLen, mCutoff; + nsACString& mSource; + + public: + explicit CharsetIterator(nsACString& aSource); + bool Equals(const nsACString& aOther, + const nsCStringComparator& aCmp) const; + void Replace(const nsACString& aReplacement); + bool Next(); + }; + + bool IsEmpty() const; + bool Has(const char* aName); + bool Has(const nsACString& aName); + void Get(const char* aName, nsACString& aValue); + void Get(const nsACString& aName, nsACString& aValue); + void Set(const char* aName, const nsACString& aValue); + void Set(const nsACString& aName, const nsACString& aValue); + void MergeOrSet(const char* aName, const nsACString& aValue); + void MergeOrSet(const nsACString& aName, const nsACString& aValue); + void Clear(); + void ApplyToChannel(nsIHttpChannel* aChannel, bool aStripRequestBodyHeader, + bool aStripAuth) const; + void GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const; +}; + +class nsXHRParseEndListener; +class XMLHttpRequestDoneNotifier; + +// Make sure that any non-DOM interfaces added here are also added to +// nsXMLHttpRequestXPCOMifier. +class XMLHttpRequestMainThread final : public XMLHttpRequest, + public nsIStreamListener, + public nsIChannelEventSink, + public nsIProgressEventSink, + public nsIInterfaceRequestor, + public nsITimerCallback, + public nsISizeOfEventTarget, + public nsINamed, + public MutableBlobStorageCallback { + friend class nsXHRParseEndListener; + friend class nsXMLHttpRequestXPCOMifier; + friend class XMLHttpRequestDoneNotifier; + + public: + // Make sure that any additions done to ErrorType enum are also mirrored in + // XHR_ERROR_TYPE enum of TelemetrySend.sys.mjs. + enum class ErrorType : uint16_t { + eOK, + eRequest, + eUnreachable, + eChannelOpen, + eRedirect, + eTerminated, + ENUM_MAX + }; + + explicit XMLHttpRequestMainThread(nsIGlobalObject* aGlobalObject); + + void Construct(nsIPrincipal* aPrincipal, + nsICookieJarSettings* aCookieJarSettings, bool aForWorker, + nsIURI* aBaseURI = nullptr, nsILoadGroup* aLoadGroup = nullptr, + PerformanceStorage* aPerformanceStorage = nullptr, + nsICSPEventListener* aCSPEventListener = nullptr); + + void InitParameters(bool aAnon, bool aSystem); + + void SetParameters(bool aAnon, bool aSystem) { + mIsAnon = aAnon || aSystem; + mIsSystem = aSystem; + } + + void SetClientInfoAndController( + const ClientInfo& aClientInfo, + const Maybe<ServiceWorkerDescriptor>& aController); + + NS_DECL_ISUPPORTS_INHERITED + + // nsIStreamListener + NS_DECL_NSISTREAMLISTENER + + // nsIRequestObserver + NS_DECL_NSIREQUESTOBSERVER + + // nsIChannelEventSink + NS_DECL_NSICHANNELEVENTSINK + + // nsIProgressEventSink + NS_DECL_NSIPROGRESSEVENTSINK + + // nsIInterfaceRequestor + NS_DECL_NSIINTERFACEREQUESTOR + + // nsITimerCallback + NS_DECL_NSITIMERCALLBACK + + // nsINamed + NS_DECL_NSINAMED + + // nsISizeOfEventTarget + virtual size_t SizeOfEventTargetIncludingThis( + MallocSizeOf aMallocSizeOf) const override; + + // states + virtual uint16_t ReadyState() const override; + + // request + nsresult CreateChannel(); + nsresult InitiateFetch(already_AddRefed<nsIInputStream> aUploadStream, + int64_t aUploadLength, nsACString& aUploadContentType); + + virtual void Open(const nsACString& aMethod, const nsAString& aUrl, + ErrorResult& aRv) override; + + virtual void Open(const nsACString& aMethod, const nsAString& aUrl, + bool aAsync, const nsAString& aUsername, + const nsAString& aPassword, ErrorResult& aRv) override; + + void Open(const nsACString& aMethod, const nsACString& aUrl, bool aAsync, + const nsAString& aUsername, const nsAString& aPassword, + ErrorResult& aRv); + + virtual void SetRequestHeader(const nsACString& aName, + const nsACString& aValue, + ErrorResult& aRv) override; + + virtual uint32_t Timeout() const override { return mTimeoutMilliseconds; } + + virtual void SetTimeout(uint32_t aTimeout, ErrorResult& aRv) override; + + virtual bool WithCredentials() const override; + + virtual void SetWithCredentials(bool aWithCredentials, + ErrorResult& aRv) override; + + virtual XMLHttpRequestUpload* GetUpload(ErrorResult& aRv) override; + + private: + virtual ~XMLHttpRequestMainThread(); + + nsresult MaybeSilentSendFailure(nsresult aRv); + void SendInternal(const BodyExtractorBase* aBody, + bool aBodyIsDocumentOrString, ErrorResult& aRv); + + bool IsCrossSiteCORSRequest() const; + bool IsDeniedCrossSiteCORSRequest(); + + void ResumeTimeout(); + + void MaybeLowerChannelPriority(); + + public: + bool CanSend(ErrorResult& aRv); + + virtual void Send( + const Nullable< + DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>& + aData, + ErrorResult& aRv) override; + + virtual void SendInputStream(nsIInputStream* aInputStream, + ErrorResult& aRv) override { + if (!CanSend(aRv)) { + return; + } + BodyExtractor<nsIInputStream> body(aInputStream); + SendInternal(&body, false, aRv); + } + + void RequestErrorSteps(const ProgressEventType aEventType, + const nsresult aOptionalException, ErrorResult& aRv); + + void Abort() { + IgnoredErrorResult rv; + AbortInternal(rv); + MOZ_ASSERT(!rv.Failed() || rv.ErrorCodeIs(NS_ERROR_DOM_ABORT_ERR)); + } + + virtual void Abort(ErrorResult& aRv) override; + + // response + virtual void GetResponseURL(nsAString& aUrl) override; + + virtual uint32_t GetStatus(ErrorResult& aRv) override; + + virtual void GetStatusText(nsACString& aStatusText, + ErrorResult& aRv) override; + + virtual void GetResponseHeader(const nsACString& aHeader, nsACString& aResult, + ErrorResult& aRv) override; + + void GetResponseHeader(const nsAString& aHeader, nsAString& aResult, + ErrorResult& aRv) { + nsAutoCString result; + GetResponseHeader(NS_ConvertUTF16toUTF8(aHeader), result, aRv); + if (result.IsVoid()) { + aResult.SetIsVoid(true); + } else { + // The result value should be inflated: + CopyASCIItoUTF16(result, aResult); + } + } + + virtual void GetAllResponseHeaders(nsACString& aResponseHeaders, + ErrorResult& aRv) override; + + bool IsSafeHeader(const nsACString& aHeaderName, + NotNull<nsIHttpChannel*> aHttpChannel) const; + + virtual void OverrideMimeType(const nsAString& aMimeType, + ErrorResult& aRv) override; + + virtual XMLHttpRequestResponseType ResponseType() const override { + return XMLHttpRequestResponseType(mResponseType); + } + + virtual void SetResponseType(XMLHttpRequestResponseType aType, + ErrorResult& aRv) override; + + void SetResponseTypeRaw(XMLHttpRequestResponseType aType) { + mResponseType = aType; + } + + virtual void GetResponse(JSContext* aCx, + JS::MutableHandle<JS::Value> aResponse, + ErrorResult& aRv) override; + + virtual void GetResponseText(DOMString& aResponseText, + ErrorResult& aRv) override; + + // GetResponse* for workers: + already_AddRefed<BlobImpl> GetResponseBlobImpl(); + already_AddRefed<ArrayBufferBuilder> GetResponseArrayBufferBuilder(); + nsresult GetResponseTextForJSON(nsAString& aString); + void GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot, + ErrorResult& aRv); + + virtual Document* GetResponseXML(ErrorResult& aRv) override; + + virtual bool MozBackgroundRequest() const override; + + void SetMozBackgroundRequestExternal(bool aMozBackgroundRequest, + ErrorResult& aRv); + + virtual void SetMozBackgroundRequest(bool aMozBackgroundRequest, + ErrorResult& aRv) override; + + void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack); + + void SetSource(UniquePtr<ProfileChunkedBuffer> aSource); + + nsresult ErrorDetail() const { return mErrorLoadDetail; } + + virtual uint16_t ErrorCode() const override { + return static_cast<uint16_t>(mErrorLoad); + } + + virtual bool MozAnon() const override; + + virtual bool MozSystem() const override; + + virtual nsIChannel* GetChannel() const override { return mChannel; } + + // We need a GetInterface callable from JS for chrome JS + virtual void GetInterface(JSContext* aCx, JS::Handle<JS::Value> aIID, + JS::MutableHandle<JS::Value> aRetval, + ErrorResult& aRv) override; + + // This fires a trusted readystatechange event, which is not cancelable and + // doesn't bubble. + nsresult FireReadystatechangeEvent(); + void DispatchProgressEvent(DOMEventTargetHelper* aTarget, + const ProgressEventType& aType, int64_t aLoaded, + int64_t aTotal); + + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( + XMLHttpRequestMainThread, XMLHttpRequest) + virtual bool IsCertainlyAliveForCC() const override; + + bool AllowUploadProgress(); + + virtual void DisconnectFromOwner() override; + + static void SetDontWarnAboutSyncXHR(bool aVal) { + sDontWarnAboutSyncXHR = aVal; + } + static bool DontWarnAboutSyncXHR() { return sDontWarnAboutSyncXHR; } + + virtual void SetOriginAttributes( + const mozilla::dom::OriginAttributesDictionary& aAttrs) override; + + void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, + nsresult aResult) override; + + void LocalFileToBlobCompleted(BlobImpl* aBlobImpl); + +#ifdef DEBUG + // For logging when there's trouble + RefPtr<ThreadSafeWorkerRef> mTSWorkerRef = nullptr; +#endif + + protected: + nsresult DetectCharset(); + nsresult AppendToResponseText(Span<const uint8_t> aBuffer, + bool aLast = false); + static nsresult StreamReaderFunc(nsIInputStream* in, void* closure, + const char* fromRawSegment, + uint32_t toOffset, uint32_t count, + uint32_t* writeCount); + nsresult CreateResponseParsedJSON(JSContext* aCx); + // Change the state of the object with this. The broadcast argument + // determines if the onreadystatechange listener should be called. + nsresult ChangeState(uint16_t aState, bool aBroadcast = true); + already_AddRefed<nsILoadGroup> GetLoadGroup() const; + + // Finds a preload for this XHR. If it is found it's removed from the preload + // table of the document and marked as used. The called doesn't need to do + // any more comparative checks. + already_AddRefed<PreloaderBase> FindPreload(); + // If no or unknown mime type is set on the channel this method ensures it's + // set to "text/xml". + void EnsureChannelContentType(); + + // Gets the value of the final content-type header from the channel. + bool GetContentType(nsACString& aValue) const; + + already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel(); + already_AddRefed<nsIJARChannel> GetCurrentJARChannel(); + + void TruncateResponseText(); + + bool IsSystemXHR() const; + bool InUploadPhase() const; + + void OnBodyParseEnd(); + void ChangeStateToDone(bool aWasSync); + void ChangeStateToDoneInternal(); + + void StartProgressEventTimer(); + void StopProgressEventTimer(); + + void MaybeCreateBlobStorage(); + + nsresult OnRedirectVerifyCallback(nsresult result, bool stripAuth = false); + + nsIEventTarget* GetTimerEventTarget(); + + nsresult DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable); + + void DispatchOrStoreEvent(DOMEventTargetHelper* aTarget, Event* aEvent); + + already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier(); + + void SuspendEventDispatching(); + void ResumeEventDispatching(); + + void AbortInternal(ErrorResult& aRv); + + Maybe<nsBaseChannel::ContentRange> GetRequestedContentRange() const; + void GetContentRangeHeader(nsACString&) const; + + struct PendingEvent { + RefPtr<DOMEventTargetHelper> mTarget; + RefPtr<Event> mEvent; + }; + + nsTArray<PendingEvent> mPendingEvents; + + nsCOMPtr<nsISupports> mContext; + nsCOMPtr<nsIPrincipal> mPrincipal; + nsCOMPtr<nsIChannel> mChannel; + nsCString mRequestMethod; + nsCOMPtr<nsIURI> mRequestURL; + RefPtr<Document> mResponseXML; + + nsCOMPtr<nsIStreamListener> mXMLParserStreamListener; + + nsCOMPtr<nsICookieJarSettings> mCookieJarSettings; + + RefPtr<PerformanceStorage> mPerformanceStorage; + nsCOMPtr<nsICSPEventListener> mCSPEventListener; + + // used to implement getAllResponseHeaders() + class nsHeaderVisitor : public nsIHttpHeaderVisitor { + struct HeaderEntry final { + nsCString mName; + nsCString mValue; + + HeaderEntry(const nsACString& aName, const nsACString& aValue) + : mName(aName), mValue(aValue) {} + + bool operator==(const HeaderEntry& aOther) const { + return mName == aOther.mName; + } + + bool operator<(const HeaderEntry& aOther) const { + uint32_t selfLen = mName.Length(); + uint32_t otherLen = aOther.mName.Length(); + uint32_t min = XPCOM_MIN(selfLen, otherLen); + for (uint32_t i = 0; i < min; ++i) { + unsigned char self = mName[i]; + unsigned char other = aOther.mName[i]; + MOZ_ASSERT(!(self >= 'A' && self <= 'Z')); + MOZ_ASSERT(!(other >= 'A' && other <= 'Z')); + if (self == other) { + continue; + } + if (self >= 'a' && self <= 'z') { + self -= 0x20; + } + if (other >= 'a' && other <= 'z') { + other -= 0x20; + } + return self < other; + } + return selfLen < otherLen; + } + }; + + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHTTPHEADERVISITOR + nsHeaderVisitor(const XMLHttpRequestMainThread& aXMLHttpRequest, + NotNull<nsIHttpChannel*> aHttpChannel); + const nsACString& Headers() { + for (uint32_t i = 0; i < mHeaderList.Length(); i++) { + HeaderEntry& header = mHeaderList.ElementAt(i); + + mHeaders.Append(header.mName); + mHeaders.AppendLiteral(": "); + mHeaders.Append(header.mValue); + mHeaders.AppendLiteral("\r\n"); + } + return mHeaders; + } + + private: + virtual ~nsHeaderVisitor(); + + nsTArray<HeaderEntry> mHeaderList; + nsCString mHeaders; + const XMLHttpRequestMainThread& mXHR; + NotNull<nsCOMPtr<nsIHttpChannel>> mHttpChannel; + }; + + // The bytes of our response body. Only used for DEFAULT, ARRAYBUFFER and + // BLOB responseTypes + nsCString mResponseBody; + + // The text version of our response body. This is incrementally decoded into + // as we receive network data. However for the DEFAULT responseType we + // lazily decode into this from mResponseBody only when .responseText is + // accessed. + // Only used for DEFAULT and TEXT responseTypes. + XMLHttpRequestString mResponseText; + + // For DEFAULT responseType we use this to keep track of how far we've + // lazily decoded from mResponseBody to mResponseText + uint32_t mResponseBodyDecodedPos; + + // Decoder used for decoding into mResponseText + // Only used for DEFAULT, TEXT and JSON responseTypes. + // In cases where we've only received half a surrogate, the decoder itself + // carries the state to remember this. Next time we receive more data we + // simply feed the new data into the decoder which will handle the second + // part of the surrogate. + mozilla::UniquePtr<mozilla::Decoder> mDecoder; + + void MatchCharsetAndDecoderToResponseDocument(); + + XMLHttpRequestResponseType mResponseType; + + RefPtr<BlobImpl> mResponseBlobImpl; + + // This is the cached blob-response, created only at the first GetResponse() + // call. + RefPtr<Blob> mResponseBlob; + + // We stream data to mBlobStorage when response type is "blob". + RefPtr<MutableBlobStorage> mBlobStorage; + + nsString mOverrideMimeType; + + /** + * The notification callbacks the channel had when Send() was + * called. We want to forward things here as needed. + */ + nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks; + /** + * Sink interfaces that we implement that mNotificationCallbacks may + * want to also be notified for. These are inited lazily if we're + * asked for the relevant interface. + */ + nsCOMPtr<nsIChannelEventSink> mChannelEventSink; + nsCOMPtr<nsIProgressEventSink> mProgressEventSink; + + nsCOMPtr<nsIURI> mBaseURI; + nsCOMPtr<nsILoadGroup> mLoadGroup; + + Maybe<ClientInfo> mClientInfo; + Maybe<ServiceWorkerDescriptor> mController; + + uint16_t mState; + + // If true, this object is used by the worker's XMLHttpRequest. + bool mForWorker; + + bool mFlagSynchronous; + bool mFlagAborted; + bool mFlagParseBody; + bool mFlagSyncLooping; + bool mFlagBackgroundRequest; + bool mFlagHadUploadListenersOnSend; + bool mFlagACwithCredentials; + bool mFlagTimedOut; + bool mFlagDeleted; + + // The XHR2 spec's send() flag. Set when the XHR begins uploading, until it + // finishes downloading (or an error/abort has occurred during either phase). + // Used to guard against the user trying to alter headers/etc when it's too + // late, and ensure the XHR only handles one in-flight request at once. + bool mFlagSend; + + RefPtr<XMLHttpRequestUpload> mUpload; + int64_t mUploadTransferred; + int64_t mUploadTotal; + bool mUploadComplete; + bool mProgressSinceLastProgressEvent; + + // Timeout support + PRTime mRequestSentTime; + uint32_t mTimeoutMilliseconds; + nsCOMPtr<nsITimer> mTimeoutTimer; + void StartTimeoutTimer(); + void HandleTimeoutCallback(); + void CancelTimeoutTimer(); + + nsCOMPtr<nsIRunnable> mResumeTimeoutRunnable; + + nsCOMPtr<nsITimer> mSyncTimeoutTimer; + + enum SyncTimeoutType { eErrorOrExpired, eTimerStarted, eNoTimerNeeded }; + + SyncTimeoutType MaybeStartSyncTimeoutTimer(); + void HandleSyncTimeoutTimer(); + void CancelSyncTimeoutTimer(); + + ErrorType mErrorLoad; + nsresult mErrorLoadDetail; + bool mErrorParsingXML; + bool mWaitingForOnStopRequest; + bool mProgressTimerIsActive; + bool mIsHtml; + bool mWarnAboutSyncHtml; + int64_t mLoadTotal; // -1 if not known. + // Number of HTTP message body bytes received so far. This quantity is + // in the same units as Content-Length and mLoadTotal, and hence counts + // compressed bytes when the channel has gzip Content-Encoding. If the + // channel does not have Content-Encoding, this will be the same as + // mDataReceived except between the OnProgress that changes mLoadTransferred + // and the corresponding OnDataAvailable (which changes mDataReceived). + // Ordering of OnProgress and OnDataAvailable is undefined. + int64_t mLoadTransferred; + nsCOMPtr<nsITimer> mProgressNotifier; + void HandleProgressTimerCallback(); + + bool mIsSystem; + bool mIsAnon; + + /** + * Close the XMLHttpRequest's channels. + */ + void CloseRequest(nsresult detail); + + void TerminateOngoingFetch(nsresult detail); + + /** + * Close the XMLHttpRequest's channels and dispatch appropriate progress + * events. + * + * @param aType The progress event type. + */ + void CloseRequestWithError(const ErrorProgressEventType& aType); + + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; + nsCOMPtr<nsIChannel> mNewRedirectChannel; + + JS::Heap<JS::Value> mResultJSON; + + RefPtr<ArrayBufferBuilder> mArrayBufferBuilder; + JS::Heap<JSObject*> mResultArrayBuffer; + bool mIsMappedArrayBuffer; + + void ResetResponse(); + + bool ShouldBlockAuthPrompt(); + + RequestHeaders mAuthorRequestHeaders; + + // Helper object to manage our XPCOM scriptability bits + nsXMLHttpRequestXPCOMifier* mXPCOMifier; + + // When this is set to true, the event dispatching is suspended. This is + // useful to change the correct state when XHR is working sync. + bool mEventDispatchingSuspended; + + // True iff mDecoder has processed the end of the stream. + // Used in lazy decoding to distinguish between having + // processed all the bytes but not the EOF and having + // processed all the bytes and the EOF. + bool mEofDecoded; + + // This flag will be set in `Send()` when we successfully reuse a "fetch" + // preload to satisfy this XHR. + bool mFromPreload = false; + + // Our parse-end listener, if we are parsing. + RefPtr<nsXHRParseEndListener> mParseEndListener; + + XMLHttpRequestDoneNotifier* mDelayedDoneNotifier; + void DisconnectDoneNotifier(); + + // Any stack information for the point the XHR was opened. This is deleted + // after the XHR is opened, to avoid retaining references to the worker. + UniquePtr<SerializedStackHolder> mOriginStack; + + static bool sDontWarnAboutSyncXHR; +}; + +class MOZ_STACK_CLASS AutoDontWarnAboutSyncXHR { + public: + AutoDontWarnAboutSyncXHR() + : mOldVal(XMLHttpRequestMainThread::DontWarnAboutSyncXHR()) { + XMLHttpRequestMainThread::SetDontWarnAboutSyncXHR(true); + } + + ~AutoDontWarnAboutSyncXHR() { + XMLHttpRequestMainThread::SetDontWarnAboutSyncXHR(mOldVal); + } + + private: + bool mOldVal; +}; + +// A shim class designed to expose the non-DOM interfaces of +// XMLHttpRequest via XPCOM stuff. +class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener, + public nsIChannelEventSink, + public nsIAsyncVerifyRedirectCallback, + public nsIProgressEventSink, + public nsIInterfaceRequestor, + public nsITimerCallback, + public nsINamed { + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier, + nsIStreamListener) + + explicit nsXMLHttpRequestXPCOMifier(XMLHttpRequestMainThread* aXHR) + : mXHR(aXHR) {} + + private: + ~nsXMLHttpRequestXPCOMifier() { + if (mXHR) { + mXHR->mXPCOMifier = nullptr; + } + } + + public: + NS_FORWARD_NSISTREAMLISTENER(mXHR->) + NS_FORWARD_NSIREQUESTOBSERVER(mXHR->) + NS_FORWARD_NSICHANNELEVENTSINK(mXHR->) + NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->) + NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->) + NS_FORWARD_NSITIMERCALLBACK(mXHR->) + NS_FORWARD_NSINAMED(mXHR->) + + NS_DECL_NSIINTERFACEREQUESTOR + + private: + RefPtr<XMLHttpRequestMainThread> mXHR; +}; + +class XMLHttpRequestDoneNotifier : public Runnable { + public: + explicit XMLHttpRequestDoneNotifier(XMLHttpRequestMainThread* aXHR) + : Runnable("XMLHttpRequestDoneNotifier"), mXHR(aXHR) {} + + NS_IMETHOD Run() override { + if (mXHR) { + RefPtr<XMLHttpRequestMainThread> xhr = mXHR; + // ChangeStateToDoneInternal ends up calling Disconnect(); + xhr->ChangeStateToDoneInternal(); + MOZ_ASSERT(!mXHR); + } + return NS_OK; + } + + void Disconnect() { mXHR = nullptr; } + + private: + RefPtr<XMLHttpRequestMainThread> mXHR; +}; + +class nsXHRParseEndListener : public nsIDOMEventListener { + public: + NS_DECL_ISUPPORTS + NS_IMETHOD HandleEvent(Event* event) override { + if (mXHR) { + mXHR->OnBodyParseEnd(); + } + mXHR = nullptr; + return NS_OK; + } + + explicit nsXHRParseEndListener(XMLHttpRequestMainThread* aXHR) : mXHR(aXHR) {} + + void SetIsStale() { mXHR = nullptr; } + + private: + virtual ~nsXHRParseEndListener() = default; + + XMLHttpRequestMainThread* mXHR; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_XMLHttpRequestMainThread_h |