michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsXMLHttpRequest_h__ michael@0: #define nsXMLHttpRequest_h__ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsIXMLHttpRequest.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsIChannelEventSink.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIHttpHeaderVisitor.h" michael@0: #include "nsIProgressEventSink.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsISizeOfEventTarget.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIInputStream.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/TypedArray.h" michael@0: #include "mozilla/dom/XMLHttpRequestBinding.h" michael@0: michael@0: #ifdef Status michael@0: /* Xlib headers insist on this for some reason... Nuke it because michael@0: it'll override our member name */ michael@0: #undef Status michael@0: #endif michael@0: michael@0: class AsyncVerifyRedirectCallbackForwarder; michael@0: class BlobSet; michael@0: class nsDOMFile; michael@0: class nsFormData; michael@0: class nsIJARChannel; michael@0: class nsILoadGroup; michael@0: class nsIUnicodeDecoder; michael@0: class nsIJSID; michael@0: michael@0: namespace mozilla { michael@0: michael@0: // A helper for building up an ArrayBuffer object's data michael@0: // before creating the ArrayBuffer itself. Will do doubling michael@0: // based reallocation, up to an optional maximum growth given. michael@0: // michael@0: // When all the data has been appended, call getArrayBuffer, michael@0: // passing in the JSContext* for which the ArrayBuffer object michael@0: // is to be created. This also implicitly resets the builder, michael@0: // or it can be reset explicitly at any point by calling reset(). michael@0: class ArrayBufferBuilder michael@0: { michael@0: uint8_t* mDataPtr; michael@0: uint32_t mCapacity; michael@0: uint32_t mLength; michael@0: public: michael@0: ArrayBufferBuilder(); michael@0: ~ArrayBufferBuilder(); michael@0: michael@0: void reset(); michael@0: michael@0: // Will truncate if aNewCap is < length(). michael@0: bool setCapacity(uint32_t aNewCap); michael@0: michael@0: // Append aDataLen bytes from data to the current buffer. If we michael@0: // need to grow the buffer, grow by doubling the size up to a michael@0: // maximum of aMaxGrowth (if given). If aDataLen is greater than michael@0: // what the new capacity would end up as, then grow by aDataLen. michael@0: // michael@0: // The data parameter must not overlap with anything beyond the michael@0: // builder's current valid contents [0..length) michael@0: bool append(const uint8_t* aNewData, uint32_t aDataLen, michael@0: uint32_t aMaxGrowth = 0); michael@0: michael@0: uint32_t length() { return mLength; } michael@0: uint32_t capacity() { return mCapacity; } michael@0: michael@0: JSObject* getArrayBuffer(JSContext* aCx); michael@0: michael@0: protected: michael@0: static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1, michael@0: const uint8_t* aStart2, uint32_t aLength2); michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: class nsXHREventTarget : public mozilla::DOMEventTargetHelper, michael@0: public nsIXMLHttpRequestEventTarget michael@0: { michael@0: protected: michael@0: nsXHREventTarget(mozilla::DOMEventTargetHelper* aOwner) michael@0: : mozilla::DOMEventTargetHelper(aOwner) michael@0: { michael@0: } michael@0: michael@0: nsXHREventTarget() michael@0: { michael@0: } michael@0: michael@0: public: michael@0: typedef mozilla::dom::XMLHttpRequestResponseType michael@0: XMLHttpRequestResponseType; michael@0: typedef mozilla::ErrorResult michael@0: ErrorResult; michael@0: michael@0: virtual ~nsXHREventTarget() {} michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXHREventTarget, michael@0: mozilla::DOMEventTargetHelper) michael@0: NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET michael@0: NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper) michael@0: michael@0: IMPL_EVENT_HANDLER(loadstart) michael@0: IMPL_EVENT_HANDLER(progress) michael@0: IMPL_EVENT_HANDLER(abort) michael@0: IMPL_EVENT_HANDLER(error) michael@0: IMPL_EVENT_HANDLER(load) michael@0: IMPL_EVENT_HANDLER(timeout) michael@0: IMPL_EVENT_HANDLER(loadend) michael@0: michael@0: virtual void DisconnectFromOwner(); michael@0: }; michael@0: michael@0: class nsXMLHttpRequestUpload MOZ_FINAL : public nsXHREventTarget, michael@0: public nsIXMLHttpRequestUpload michael@0: { michael@0: public: michael@0: nsXMLHttpRequestUpload(mozilla::DOMEventTargetHelper* aOwner) michael@0: : nsXHREventTarget(aOwner) michael@0: { michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::) michael@0: NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget) michael@0: NS_DECL_NSIXMLHTTPREQUESTUPLOAD michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: nsISupports* GetParentObject() michael@0: { michael@0: return GetOwner(); michael@0: } michael@0: michael@0: bool HasListeners() michael@0: { michael@0: return mListenerManager && mListenerManager->HasListeners(); michael@0: } michael@0: }; michael@0: michael@0: class nsXMLHttpRequestXPCOMifier; michael@0: michael@0: // Make sure that any non-DOM interfaces added here are also added to michael@0: // nsXMLHttpRequestXPCOMifier. michael@0: class nsXMLHttpRequest : public nsXHREventTarget, michael@0: public nsIXMLHttpRequest, michael@0: public nsIJSXMLHttpRequest, michael@0: public nsIStreamListener, michael@0: public nsIChannelEventSink, michael@0: public nsIProgressEventSink, michael@0: public nsIInterfaceRequestor, michael@0: public nsSupportsWeakReference, michael@0: public nsITimerCallback, michael@0: public nsISizeOfEventTarget michael@0: { michael@0: friend class nsXHRParseEndListener; michael@0: friend class nsXMLHttpRequestXPCOMifier; michael@0: michael@0: public: michael@0: nsXMLHttpRequest(); michael@0: virtual ~nsXMLHttpRequest(); michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE michael@0: { michael@0: return mozilla::dom::XMLHttpRequestBinding::Wrap(cx, this); michael@0: } michael@0: nsISupports* GetParentObject() michael@0: { michael@0: return GetOwner(); michael@0: } michael@0: michael@0: // The WebIDL constructors. michael@0: static already_AddRefed michael@0: Constructor(const mozilla::dom::GlobalObject& aGlobal, michael@0: JSContext* aCx, michael@0: const mozilla::dom::MozXMLHttpRequestParameters& aParams, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: nsCOMPtr principal = michael@0: do_QueryInterface(aGlobal.GetAsSupports()); michael@0: if (!global || ! principal) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr req = new nsXMLHttpRequest(); michael@0: req->Construct(principal->GetPrincipal(), global); michael@0: req->InitParameters(aParams.mMozAnon, aParams.mMozSystem); michael@0: return req.forget(); michael@0: } michael@0: michael@0: static already_AddRefed michael@0: Constructor(const mozilla::dom::GlobalObject& aGlobal, michael@0: JSContext* aCx, michael@0: const nsAString& ignored, michael@0: ErrorResult& aRv) michael@0: { michael@0: // Pretend like someone passed null, so we can pick up the default values michael@0: mozilla::dom::MozXMLHttpRequestParameters params; michael@0: if (!params.Init(aCx, JS::NullHandleValue)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: return Constructor(aGlobal, aCx, params, aRv); michael@0: } michael@0: michael@0: void Construct(nsIPrincipal* aPrincipal, michael@0: nsIGlobalObject* aGlobalObject, michael@0: nsIURI* aBaseURI = nullptr) michael@0: { michael@0: MOZ_ASSERT(aPrincipal); michael@0: MOZ_ASSERT_IF(nsCOMPtr win = do_QueryInterface( michael@0: aGlobalObject), win->IsInnerWindow()); michael@0: mPrincipal = aPrincipal; michael@0: BindToOwner(aGlobalObject); michael@0: mBaseURI = aBaseURI; michael@0: } michael@0: michael@0: void InitParameters(bool aAnon, bool aSystem); michael@0: michael@0: void SetParameters(bool aAnon, bool aSystem) michael@0: { michael@0: mIsAnon = aAnon || aSystem; michael@0: mIsSystem = aSystem; michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: // nsIXMLHttpRequest michael@0: NS_DECL_NSIXMLHTTPREQUEST michael@0: michael@0: NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::) michael@0: michael@0: // nsIStreamListener michael@0: NS_DECL_NSISTREAMLISTENER michael@0: michael@0: // nsIRequestObserver michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: michael@0: // nsIChannelEventSink michael@0: NS_DECL_NSICHANNELEVENTSINK michael@0: michael@0: // nsIProgressEventSink michael@0: NS_DECL_NSIPROGRESSEVENTSINK michael@0: michael@0: // nsIInterfaceRequestor michael@0: NS_DECL_NSIINTERFACEREQUESTOR michael@0: michael@0: // nsITimerCallback michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: // nsISizeOfEventTarget michael@0: virtual size_t michael@0: SizeOfEventTargetIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget) michael@0: michael@0: #ifdef DEBUG michael@0: void StaticAssertions(); michael@0: #endif michael@0: michael@0: // event handler michael@0: IMPL_EVENT_HANDLER(readystatechange) michael@0: michael@0: // states michael@0: uint16_t ReadyState(); michael@0: michael@0: // request michael@0: void Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) michael@0: { michael@0: Open(aMethod, aUrl, true, michael@0: mozilla::dom::Optional(), michael@0: mozilla::dom::Optional(), michael@0: aRv); michael@0: } michael@0: void Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync, michael@0: const mozilla::dom::Optional& aUser, michael@0: const mozilla::dom::Optional& aPassword, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), michael@0: aAsync, aUser, aPassword); michael@0: } michael@0: void SetRequestHeader(const nsACString& aHeader, const nsACString& aValue, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = SetRequestHeader(aHeader, aValue); michael@0: } michael@0: uint32_t Timeout() michael@0: { michael@0: return mTimeoutMilliseconds; michael@0: } michael@0: void SetTimeout(uint32_t aTimeout, ErrorResult& aRv); michael@0: bool WithCredentials(); michael@0: void SetWithCredentials(bool aWithCredentials, ErrorResult& aRv); michael@0: nsXMLHttpRequestUpload* Upload(); michael@0: michael@0: private: michael@0: class RequestBody michael@0: { michael@0: public: michael@0: RequestBody() : mType(Uninitialized) michael@0: { michael@0: } michael@0: RequestBody(const mozilla::dom::ArrayBuffer* aArrayBuffer) : mType(ArrayBuffer) michael@0: { michael@0: mValue.mArrayBuffer = aArrayBuffer; michael@0: } michael@0: RequestBody(const mozilla::dom::ArrayBufferView* aArrayBufferView) : mType(ArrayBufferView) michael@0: { michael@0: mValue.mArrayBufferView = aArrayBufferView; michael@0: } michael@0: RequestBody(nsIDOMBlob* aBlob) : mType(Blob) michael@0: { michael@0: mValue.mBlob = aBlob; michael@0: } michael@0: RequestBody(nsIDocument* aDocument) : mType(Document) michael@0: { michael@0: mValue.mDocument = aDocument; michael@0: } michael@0: RequestBody(const nsAString& aString) : mType(DOMString) michael@0: { michael@0: mValue.mString = &aString; michael@0: } michael@0: RequestBody(nsFormData& aFormData) : mType(FormData) michael@0: { michael@0: mValue.mFormData = &aFormData; michael@0: } michael@0: RequestBody(nsIInputStream* aStream) : mType(InputStream) michael@0: { michael@0: mValue.mStream = aStream; michael@0: } michael@0: michael@0: enum Type { michael@0: Uninitialized, michael@0: ArrayBuffer, michael@0: ArrayBufferView, michael@0: Blob, michael@0: Document, michael@0: DOMString, michael@0: FormData, michael@0: InputStream michael@0: }; michael@0: union Value { michael@0: const mozilla::dom::ArrayBuffer* mArrayBuffer; michael@0: const mozilla::dom::ArrayBufferView* mArrayBufferView; michael@0: nsIDOMBlob* mBlob; michael@0: nsIDocument* mDocument; michael@0: const nsAString* mString; michael@0: nsFormData* mFormData; michael@0: nsIInputStream* mStream; michael@0: }; michael@0: michael@0: Type GetType() const michael@0: { michael@0: MOZ_ASSERT(mType != Uninitialized); michael@0: return mType; michael@0: } michael@0: Value GetValue() const michael@0: { michael@0: MOZ_ASSERT(mType != Uninitialized); michael@0: return mValue; michael@0: } michael@0: michael@0: private: michael@0: Type mType; michael@0: Value mValue; michael@0: }; michael@0: michael@0: static nsresult GetRequestBody(nsIVariant* aVariant, michael@0: const Nullable& aBody, michael@0: nsIInputStream** aResult, michael@0: uint64_t* aContentLength, michael@0: nsACString& aContentType, michael@0: nsACString& aCharset); michael@0: michael@0: nsresult Send(nsIVariant* aVariant, const Nullable& aBody); michael@0: nsresult Send(const Nullable& aBody) michael@0: { michael@0: return Send(nullptr, aBody); michael@0: } michael@0: nsresult Send(const RequestBody& aBody) michael@0: { michael@0: return Send(Nullable(aBody)); michael@0: } michael@0: michael@0: public: michael@0: void Send(JSContext* /*aCx*/, ErrorResult& aRv) michael@0: { michael@0: aRv = Send(Nullable()); michael@0: } michael@0: void Send(JSContext* /*aCx*/, michael@0: const mozilla::dom::ArrayBuffer& aArrayBuffer, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = Send(RequestBody(&aArrayBuffer)); michael@0: } michael@0: void Send(JSContext* /*aCx*/, michael@0: const mozilla::dom::ArrayBufferView& aArrayBufferView, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = Send(RequestBody(&aArrayBufferView)); michael@0: } michael@0: void Send(JSContext* /*aCx*/, nsIDOMBlob* aBlob, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(aBlob, "Null should go to string version"); michael@0: aRv = Send(RequestBody(aBlob)); michael@0: } michael@0: void Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) michael@0: { michael@0: aRv = Send(RequestBody(&aDoc)); michael@0: } michael@0: void Send(JSContext* aCx, const nsAString& aString, ErrorResult& aRv) michael@0: { michael@0: if (DOMStringIsNull(aString)) { michael@0: Send(aCx, aRv); michael@0: } michael@0: else { michael@0: aRv = Send(RequestBody(aString)); michael@0: } michael@0: } michael@0: void Send(JSContext* /*aCx*/, nsFormData& aFormData, ErrorResult& aRv) michael@0: { michael@0: aRv = Send(RequestBody(aFormData)); michael@0: } michael@0: void Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(aStream, "Null should go to string version"); michael@0: nsCOMPtr wjs = do_QueryInterface(aStream); michael@0: if (wjs) { michael@0: JSObject* data = wjs->GetJSObject(); michael@0: if (!data) { michael@0: aRv.Throw(NS_ERROR_DOM_TYPE_ERR); michael@0: return; michael@0: } michael@0: JS::Rooted dataAsValue(aCx, JS::ObjectValue(*data)); michael@0: mozilla::dom::binding_detail::FakeDependentString dataAsString; michael@0: if (ConvertJSValueToString(aCx, dataAsValue, &dataAsValue, mozilla::dom::eNull, michael@0: mozilla::dom::eNull, dataAsString)) { michael@0: Send(aCx, dataAsString, aRv); michael@0: } else { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: } michael@0: return; michael@0: } michael@0: aRv = Send(RequestBody(aStream)); michael@0: } michael@0: void SendAsBinary(const nsAString& aBody, ErrorResult& aRv); michael@0: michael@0: void Abort(); michael@0: michael@0: // response michael@0: uint32_t Status(); michael@0: void GetStatusText(nsCString& aStatusText); michael@0: void GetResponseHeader(const nsACString& aHeader, nsACString& aResult, michael@0: ErrorResult& aRv); michael@0: void GetResponseHeader(const nsAString& aHeader, nsString& aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCString result; michael@0: GetResponseHeader(NS_ConvertUTF16toUTF8(aHeader), result, aRv); michael@0: if (result.IsVoid()) { michael@0: aResult.SetIsVoid(true); michael@0: } michael@0: else { michael@0: // The result value should be inflated: michael@0: CopyASCIItoUTF16(result, aResult); michael@0: } michael@0: } michael@0: void GetAllResponseHeaders(nsCString& aResponseHeaders); michael@0: bool IsSafeHeader(const nsACString& aHeaderName, nsIHttpChannel* aHttpChannel); michael@0: void OverrideMimeType(const nsAString& aMimeType) michael@0: { michael@0: // XXX Should we do some validation here? michael@0: mOverrideMimeType = aMimeType; michael@0: } michael@0: XMLHttpRequestResponseType ResponseType() michael@0: { michael@0: return XMLHttpRequestResponseType(mResponseType); michael@0: } michael@0: void SetResponseType(XMLHttpRequestResponseType aType, ErrorResult& aRv); michael@0: void GetResponse(JSContext* aCx, JS::MutableHandle aResponse, michael@0: ErrorResult& aRv); michael@0: void GetResponseText(nsString& aResponseText, ErrorResult& aRv); michael@0: nsIDocument* GetResponseXML(ErrorResult& aRv); michael@0: michael@0: bool MozBackgroundRequest(); michael@0: void SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv); michael@0: michael@0: bool MozAnon(); michael@0: bool MozSystem(); michael@0: michael@0: nsIChannel* GetChannel() michael@0: { michael@0: return mChannel; michael@0: } michael@0: michael@0: // We need a GetInterface callable from JS for chrome JS michael@0: void GetInterface(JSContext* aCx, nsIJSID* aIID, michael@0: JS::MutableHandle aRetval, ErrorResult& aRv); michael@0: michael@0: // This creates a trusted readystatechange event, which is not cancelable and michael@0: // doesn't bubble. michael@0: nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent); michael@0: void DispatchProgressEvent(mozilla::DOMEventTargetHelper* aTarget, michael@0: const nsAString& aType, michael@0: bool aLengthComputable, michael@0: uint64_t aLoaded, uint64_t aTotal); michael@0: michael@0: // Dispatch the "progress" event on the XHR or XHR.upload object if we've michael@0: // received data since the last "progress" event. Also dispatches michael@0: // "uploadprogress" as needed. michael@0: void MaybeDispatchProgressEvents(bool aFinalProgress); michael@0: michael@0: // This is called by the factory constructor. michael@0: nsresult Init(); michael@0: michael@0: nsresult init(nsIPrincipal* principal, michael@0: nsIScriptContext* scriptContext, michael@0: nsPIDOMWindow* globalObject, michael@0: nsIURI* baseURI); michael@0: michael@0: void SetRequestObserver(nsIRequestObserver* aObserver); michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsXMLHttpRequest, michael@0: nsXHREventTarget) michael@0: bool AllowUploadProgress(); michael@0: void RootJSResultObjects(); michael@0: michael@0: virtual void DisconnectFromOwner() MOZ_OVERRIDE; michael@0: michael@0: static void SetDontWarnAboutSyncXHR(bool aVal) michael@0: { michael@0: sDontWarnAboutSyncXHR = aVal; michael@0: } michael@0: static bool DontWarnAboutSyncXHR() michael@0: { michael@0: return sDontWarnAboutSyncXHR; michael@0: } michael@0: protected: michael@0: nsresult DetectCharset(); michael@0: nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen); michael@0: static NS_METHOD StreamReaderFunc(nsIInputStream* in, michael@0: void* closure, michael@0: const char* fromRawSegment, michael@0: uint32_t toOffset, michael@0: uint32_t count, michael@0: uint32_t *writeCount); michael@0: nsresult CreateResponseParsedJSON(JSContext* aCx); michael@0: void CreatePartialBlob(); michael@0: bool CreateDOMFile(nsIRequest *request); michael@0: // Change the state of the object with this. The broadcast argument michael@0: // determines if the onreadystatechange listener should be called. michael@0: nsresult ChangeState(uint32_t aState, bool aBroadcast = true); michael@0: already_AddRefed GetLoadGroup() const; michael@0: nsIURI *GetBaseURI(); michael@0: michael@0: already_AddRefed GetCurrentHttpChannel(); michael@0: already_AddRefed GetCurrentJARChannel(); michael@0: michael@0: bool IsSystemXHR(); michael@0: michael@0: void ChangeStateToDone(); michael@0: michael@0: /** michael@0: * Check if aChannel is ok for a cross-site request by making sure no michael@0: * inappropriate headers are set, and no username/password is set. michael@0: * michael@0: * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit. michael@0: */ michael@0: nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel); michael@0: michael@0: void StartProgressEventTimer(); michael@0: michael@0: friend class AsyncVerifyRedirectCallbackForwarder; michael@0: void OnRedirectVerifyCallback(nsresult result); michael@0: michael@0: nsresult Open(const nsACString& method, const nsACString& url, bool async, michael@0: const mozilla::dom::Optional& user, michael@0: const mozilla::dom::Optional& password); michael@0: michael@0: already_AddRefed EnsureXPCOMifier(); michael@0: michael@0: nsCOMPtr mContext; michael@0: nsCOMPtr mPrincipal; michael@0: nsCOMPtr mChannel; michael@0: nsCOMPtr mResponseXML; michael@0: nsCOMPtr mCORSPreflightChannel; michael@0: nsTArray mCORSUnsafeHeaders; michael@0: michael@0: nsCOMPtr mXMLParserStreamListener; michael@0: michael@0: // used to implement getAllResponseHeaders() michael@0: class nsHeaderVisitor : public nsIHttpHeaderVisitor { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIHTTPHEADERVISITOR michael@0: nsHeaderVisitor(nsXMLHttpRequest* aXMLHttpRequest, nsIHttpChannel* aHttpChannel) michael@0: : mXHR(aXMLHttpRequest), mHttpChannel(aHttpChannel) {} michael@0: virtual ~nsHeaderVisitor() {} michael@0: const nsACString &Headers() { return mHeaders; } michael@0: private: michael@0: nsCString mHeaders; michael@0: nsXMLHttpRequest* mXHR; michael@0: nsCOMPtr mHttpChannel; michael@0: }; michael@0: michael@0: // The bytes of our response body. Only used for DEFAULT, ARRAYBUFFER and michael@0: // BLOB responseTypes michael@0: nsCString mResponseBody; michael@0: michael@0: // The text version of our response body. This is incrementally decoded into michael@0: // as we receive network data. However for the DEFAULT responseType we michael@0: // lazily decode into this from mResponseBody only when .responseText is michael@0: // accessed. michael@0: // Only used for DEFAULT and TEXT responseTypes. michael@0: nsString mResponseText; michael@0: michael@0: // For DEFAULT responseType we use this to keep track of how far we've michael@0: // lazily decoded from mResponseBody to mResponseText michael@0: uint32_t mResponseBodyDecodedPos; michael@0: michael@0: // Decoder used for decoding into mResponseText michael@0: // Only used for DEFAULT, TEXT and JSON responseTypes. michael@0: // In cases where we've only received half a surrogate, the decoder itself michael@0: // carries the state to remember this. Next time we receive more data we michael@0: // simply feed the new data into the decoder which will handle the second michael@0: // part of the surrogate. michael@0: nsCOMPtr mDecoder; michael@0: michael@0: nsCString mResponseCharset; michael@0: michael@0: enum ResponseTypeEnum { michael@0: XML_HTTP_RESPONSE_TYPE_DEFAULT, michael@0: XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER, michael@0: XML_HTTP_RESPONSE_TYPE_BLOB, michael@0: XML_HTTP_RESPONSE_TYPE_DOCUMENT, michael@0: XML_HTTP_RESPONSE_TYPE_JSON, michael@0: XML_HTTP_RESPONSE_TYPE_TEXT, michael@0: XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT, michael@0: XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER, michael@0: XML_HTTP_RESPONSE_TYPE_MOZ_BLOB michael@0: }; michael@0: michael@0: void SetResponseType(nsXMLHttpRequest::ResponseTypeEnum aType, ErrorResult& aRv); michael@0: michael@0: ResponseTypeEnum mResponseType; michael@0: michael@0: // It is either a cached blob-response from the last call to GetResponse, michael@0: // but is also explicitly set in OnStopRequest. michael@0: nsCOMPtr mResponseBlob; michael@0: // Non-null only when we are able to get a os-file representation of the michael@0: // response, i.e. when loading from a file. michael@0: nsRefPtr mDOMFile; michael@0: // We stream data to mBlobSet when response type is "blob" or "moz-blob" michael@0: // and mDOMFile is null. michael@0: nsAutoPtr mBlobSet; michael@0: michael@0: nsString mOverrideMimeType; michael@0: michael@0: /** michael@0: * The notification callbacks the channel had when Send() was michael@0: * called. We want to forward things here as needed. michael@0: */ michael@0: nsCOMPtr mNotificationCallbacks; michael@0: /** michael@0: * Sink interfaces that we implement that mNotificationCallbacks may michael@0: * want to also be notified for. These are inited lazily if we're michael@0: * asked for the relevant interface. michael@0: */ michael@0: nsCOMPtr mChannelEventSink; michael@0: nsCOMPtr mProgressEventSink; michael@0: michael@0: nsIRequestObserver* mRequestObserver; michael@0: michael@0: nsCOMPtr mBaseURI; michael@0: michael@0: uint32_t mState; michael@0: michael@0: nsRefPtr mUpload; michael@0: uint64_t mUploadTransferred; michael@0: uint64_t mUploadTotal; michael@0: bool mUploadLengthComputable; michael@0: bool mUploadComplete; michael@0: bool mProgressSinceLastProgressEvent; michael@0: michael@0: // Timeout support michael@0: PRTime mRequestSentTime; michael@0: uint32_t mTimeoutMilliseconds; michael@0: nsCOMPtr mTimeoutTimer; michael@0: void StartTimeoutTimer(); michael@0: void HandleTimeoutCallback(); michael@0: michael@0: bool mErrorLoad; michael@0: bool mWaitingForOnStopRequest; michael@0: bool mProgressTimerIsActive; michael@0: bool mIsHtml; michael@0: bool mWarnAboutMultipartHtml; michael@0: bool mWarnAboutSyncHtml; michael@0: bool mLoadLengthComputable; michael@0: uint64_t mLoadTotal; // 0 if not known. michael@0: uint64_t mLoadTransferred; michael@0: nsCOMPtr mProgressNotifier; michael@0: void HandleProgressTimerCallback(); michael@0: michael@0: bool mIsSystem; michael@0: bool mIsAnon; michael@0: michael@0: /** michael@0: * Close the XMLHttpRequest's channels and dispatch appropriate progress michael@0: * events. michael@0: * michael@0: * @param aType The progress event type. michael@0: * @param aFlag A XML_HTTP_REQUEST_* state flag defined in michael@0: * nsXMLHttpRequest.cpp. michael@0: */ michael@0: void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag); michael@0: michael@0: bool mFirstStartRequestSeen; michael@0: bool mInLoadProgressEvent; michael@0: michael@0: nsCOMPtr mRedirectCallback; michael@0: nsCOMPtr mNewRedirectChannel; michael@0: michael@0: JS::Heap mResultJSON; michael@0: michael@0: mozilla::ArrayBufferBuilder mArrayBufferBuilder; michael@0: JS::Heap mResultArrayBuffer; michael@0: michael@0: void ResetResponse(); michael@0: michael@0: struct RequestHeader michael@0: { michael@0: nsCString header; michael@0: nsCString value; michael@0: }; michael@0: nsTArray mModifiedRequestHeaders; michael@0: michael@0: nsTHashtable mAlreadySetHeaders; michael@0: michael@0: // Helper object to manage our XPCOM scriptability bits michael@0: nsXMLHttpRequestXPCOMifier* mXPCOMifier; michael@0: michael@0: static bool sDontWarnAboutSyncXHR; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS AutoDontWarnAboutSyncXHR michael@0: { michael@0: public: michael@0: AutoDontWarnAboutSyncXHR() : mOldVal(nsXMLHttpRequest::DontWarnAboutSyncXHR()) michael@0: { michael@0: nsXMLHttpRequest::SetDontWarnAboutSyncXHR(true); michael@0: } michael@0: michael@0: ~AutoDontWarnAboutSyncXHR() michael@0: { michael@0: nsXMLHttpRequest::SetDontWarnAboutSyncXHR(mOldVal); michael@0: } michael@0: michael@0: private: michael@0: bool mOldVal; michael@0: }; michael@0: michael@0: // A shim class designed to expose the non-DOM interfaces of michael@0: // XMLHttpRequest via XPCOM stuff. michael@0: class nsXMLHttpRequestXPCOMifier MOZ_FINAL : public nsIStreamListener, michael@0: public nsIChannelEventSink, michael@0: public nsIProgressEventSink, michael@0: public nsIInterfaceRequestor, michael@0: public nsITimerCallback michael@0: { michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier, michael@0: nsIStreamListener) michael@0: michael@0: nsXMLHttpRequestXPCOMifier(nsXMLHttpRequest* aXHR) : michael@0: mXHR(aXHR) michael@0: { michael@0: } michael@0: michael@0: ~nsXMLHttpRequestXPCOMifier() { michael@0: if (mXHR) { michael@0: mXHR->mXPCOMifier = nullptr; michael@0: } michael@0: } michael@0: michael@0: NS_FORWARD_NSISTREAMLISTENER(mXHR->) michael@0: NS_FORWARD_NSIREQUESTOBSERVER(mXHR->) michael@0: NS_FORWARD_NSICHANNELEVENTSINK(mXHR->) michael@0: NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->) michael@0: NS_FORWARD_NSITIMERCALLBACK(mXHR->) michael@0: michael@0: NS_DECL_NSIINTERFACEREQUESTOR michael@0: michael@0: private: michael@0: nsRefPtr mXHR; michael@0: }; michael@0: michael@0: class nsXHRParseEndListener : public nsIDOMEventListener michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_IMETHOD HandleEvent(nsIDOMEvent *event) MOZ_OVERRIDE michael@0: { michael@0: nsCOMPtr xhr = do_QueryReferent(mXHR); michael@0: if (xhr) { michael@0: static_cast(xhr.get())->ChangeStateToDone(); michael@0: } michael@0: mXHR = nullptr; michael@0: return NS_OK; michael@0: } michael@0: nsXHRParseEndListener(nsIXMLHttpRequest* aXHR) michael@0: : mXHR(do_GetWeakReference(aXHR)) {} michael@0: virtual ~nsXHRParseEndListener() {} michael@0: private: michael@0: nsWeakPtr mXHR; michael@0: }; michael@0: michael@0: #endif