diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 82a6e1f43b91..8f27f843d379 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -30,6 +30,7 @@ #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/SubtleCryptoBinding.h" #include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/URLSearchParams.h" #include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/gfx/2D.h" #include "mozilla/ipc/BackgroundChild.h" @@ -395,7 +396,7 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, return ReadStructuredCloneImageData(aCx, aReader); } - if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) { + if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) { nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); if (!global) { return nullptr; @@ -404,11 +405,20 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, // Prevent the return value from being trashed by a GC during ~nsRefPtr. JS::Rooted result(aCx); { - RefPtr key = new CryptoKey(global); - if (!key->ReadStructuredClone(aReader)) { - result = nullptr; - } else { - result = key->WrapObject(aCx, nullptr); + if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) { + RefPtr key = new CryptoKey(global); + if (!key->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = key->WrapObject(aCx, nullptr); + } + } else if (aTag == SCTAG_DOM_URLSEARCHPARAMS) { + RefPtr usp = new URLSearchParams(global); + if (!usp->ReadStructuredClone(aReader)) { + result = nullptr; + } else { + result = usp->WrapObject(aCx, nullptr); + } } } return result; @@ -504,6 +514,15 @@ StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx, } } + // Handle URLSearchParams cloning + { + URLSearchParams* usp = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(URLSearchParams, aObj, usp))) { + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_URLSEARCHPARAMS, 0) && + usp->WriteStructuredClone(aWriter); + } + } + // Handle Key cloning { CryptoKey* key = nullptr; diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index abba5567e162..683a09228621 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -54,6 +54,9 @@ enum StructuredCloneTags { SCTAG_DOM_DIRECTORY, + // This tag is used by both main thread and workers. + SCTAG_DOM_URLSEARCHPARAMS, + SCTAG_DOM_MAX }; diff --git a/dom/url/URLSearchParams.cpp b/dom/url/URLSearchParams.cpp index 0eb207b68090..d9492f81c638 100644 --- a/dom/url/URLSearchParams.cpp +++ b/dom/url/URLSearchParams.cpp @@ -8,6 +8,8 @@ #include "mozilla/dom/URLSearchParamsBinding.h" #include "mozilla/dom/EncodingUtils.h" #include "nsDOMString.h" +#include "nsIInputStream.h" +#include "nsStringStream.h" namespace mozilla { namespace dom { @@ -300,6 +302,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END @@ -444,5 +447,103 @@ URLSearchParams::GetValueAtIndex(uint32_t aIndex) const return mParams->GetValueAtIndex(aIndex); } +// Helper functions for structured cloning +inline bool +ReadString(JSStructuredCloneReader* aReader, nsString& aString) +{ + MOZ_ASSERT(aReader); + + bool read; + uint32_t nameLength, zero; + read = JS_ReadUint32Pair(aReader, &nameLength, &zero); + if (!read) { + return false; + } + MOZ_ASSERT(zero == 0); + aString.SetLength(nameLength); + size_t charSize = sizeof(nsString::char_type); + read = JS_ReadBytes(aReader, (void*) aString.BeginWriting(), + nameLength * charSize); + if (!read) { + return false; + } + + return true; +} + +inline bool +WriteString(JSStructuredCloneWriter* aWriter, const nsString& aString) +{ + MOZ_ASSERT(aWriter); + + size_t charSize = sizeof(nsString::char_type); + return JS_WriteUint32Pair(aWriter, aString.Length(), 0) && + JS_WriteBytes(aWriter, aString.get(), aString.Length() * charSize); +} + +bool +URLParams::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + const uint32_t& nParams = mParams.Length(); + if (!JS_WriteUint32Pair(aWriter, nParams, 0)) { + return false; + } + for (uint32_t i = 0; i < nParams; ++i) { + if (!WriteString(aWriter, mParams[i].mKey) || + !WriteString(aWriter, mParams[i].mValue)) { + return false; + } + } + return true; +} + +bool +URLParams::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + MOZ_ASSERT(aReader); + + DeleteAll(); + + uint32_t nParams, zero; + nsAutoString key, value; + if (!JS_ReadUint32Pair(aReader, &nParams, &zero)) { + return false; + } + MOZ_ASSERT(zero == 0); + for (uint32_t i = 0; i < nParams; ++i) { + if (!ReadString(aReader, key) || !ReadString(aReader, value)) { + return false; + } + Append(key, value); + } + return true; +} + +bool +URLSearchParams::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return mParams->WriteStructuredClone(aWriter); +} + +bool +URLSearchParams::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + return mParams->ReadStructuredClone(aReader); +} + +NS_IMETHODIMP +URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, + nsACString& aContentType, nsACString& aCharset) +{ + aContentType.AssignLiteral("application/x-www-form-urlencoded"); + aCharset.AssignLiteral("UTF-8"); + + nsAutoString serialized; + Serialize(serialized); + NS_ConvertUTF16toUTF8 converted(serialized); + *aContentLength = converted.Length(); + return NS_NewCStringInputStream(aBody, converted); +} + } // namespace dom } // namespace mozilla diff --git a/dom/url/URLSearchParams.h b/dom/url/URLSearchParams.h index 84485b8d875a..1b81637e08fc 100644 --- a/dom/url/URLSearchParams.h +++ b/dom/url/URLSearchParams.h @@ -7,12 +7,14 @@ #ifndef mozilla_dom_URLSearchParams_h #define mozilla_dom_URLSearchParams_h +#include "js/StructuredClone.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/ErrorResult.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" #include "nsISupports.h" #include "nsIUnicodeDecoder.h" +#include "nsIXMLHttpRequest.h" namespace mozilla { namespace dom { @@ -108,6 +110,12 @@ public: return mParams[aIndex].mValue; } + bool + ReadStructuredClone(JSStructuredCloneReader* aReader); + + bool + WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + private: void DecodeString(const nsACString& aInput, nsAString& aOutput); void ConvertString(const nsACString& aInput, nsAString& aOutput); @@ -122,17 +130,19 @@ private: nsCOMPtr mDecoder; }; -class URLSearchParams final : public nsISupports, +class URLSearchParams final : public nsIXHRSendable, public nsWrapperCache { ~URLSearchParams(); public: + NS_DECL_NSIXHRSENDABLE + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams) URLSearchParams(nsISupports* aParent, - URLSearchParamsObserver* aObserver); + URLSearchParamsObserver* aObserver=nullptr); URLSearchParams(nsISupports* aParent, const URLSearchParams& aOther); @@ -189,6 +199,12 @@ public: return true; } + bool + ReadStructuredClone(JSStructuredCloneReader* aReader); + + bool + WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + private: void AppendInternal(const nsAString& aName, const nsAString& aValue); diff --git a/dom/webidl/XMLHttpRequest.webidl b/dom/webidl/XMLHttpRequest.webidl index 3dcd004d47e7..39f527cd6ae7 100644 --- a/dom/webidl/XMLHttpRequest.webidl +++ b/dom/webidl/XMLHttpRequest.webidl @@ -102,6 +102,8 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget { void send(FormData data); [Throws] void send(InputStream data); + [Throws] + void send(URLSearchParams data); [Throws] void abort(); diff --git a/dom/xhr/XMLHttpRequest.h b/dom/xhr/XMLHttpRequest.h index bc5e0785d524..1d4d507cb520 100644 --- a/dom/xhr/XMLHttpRequest.h +++ b/dom/xhr/XMLHttpRequest.h @@ -19,6 +19,7 @@ namespace dom { class Blob; class FormData; +class URLSearchParams; class XMLHttpRequestUpload; class XMLHttpRequest : public XMLHttpRequestEventTarget @@ -88,6 +89,9 @@ public: virtual void Send(JSContext* aCx, Blob& aBlob, ErrorResult& aRv) = 0; + virtual void + Send(JSContext* aCx, URLSearchParams& aURLSearchParams, ErrorResult& aRv) = 0; + virtual void Send(JSContext* aCx, nsIDocument& aDoc, ErrorResult& aRv) = 0; diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 5a1d8c74e96f..2b67b5222fa8 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -15,6 +15,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/FormData.h" +#include "mozilla/dom/URLSearchParams.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" #include "mozilla/LoadInfo.h" @@ -2217,6 +2218,15 @@ GetRequestBodyInternal(nsIInputStream* aStream, nsIInputStream** aResult, return NS_OK; } +static nsresult +GetRequestBodyInternal(URLSearchParams* aURLSearchParams, + nsIInputStream** aResult, uint64_t* aContentLength, + nsACString& aContentType, nsACString& aCharset) +{ + return aURLSearchParams->GetSendInfo(aResult, aContentLength, + aContentType, aCharset); +} + static nsresult GetRequestBodyInternal(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength, nsACString& aContentType, @@ -2397,6 +2407,12 @@ XMLHttpRequestMainThread::GetRequestBody(nsIVariant* aVariant, return GetRequestBodyInternal(value.mFormData, aResult, aContentLength, aContentType, aCharset); } + case XMLHttpRequestMainThread::RequestBody::eURLSearchParams: + { + MOZ_ASSERT(value.mURLSearchParams); + return GetRequestBodyInternal(value.mURLSearchParams, aResult, + aContentLength, aContentType, aCharset); + } case XMLHttpRequestMainThread::RequestBody::eInputStream: { return GetRequestBodyInternal(value.mStream, aResult, aContentLength, diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 90f7b1315c65..724725470edf 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -56,6 +56,7 @@ namespace dom { class Blob; class BlobSet; class FormData; +class URLSearchParams; class XMLHttpRequestUpload; // A helper for building up an ArrayBuffer object's data @@ -263,6 +264,11 @@ private: { mValue.mBlob = &aBlob; } + explicit RequestBody(mozilla::dom::URLSearchParams& aURLSearchParams) : + mType(eURLSearchParams) + { + mValue.mURLSearchParams = &aURLSearchParams; + } explicit RequestBody(nsIDocument* aDocument) : mType(eDocument) { mValue.mDocument = aDocument; @@ -288,7 +294,8 @@ private: eDocument, eDOMString, eFormData, - eInputStream + eInputStream, + eURLSearchParams }; union Value { const ArrayBuffer* mArrayBuffer; @@ -298,6 +305,7 @@ private: const nsAString* mString; FormData* mFormData; nsIInputStream* mStream; + URLSearchParams* mURLSearchParams; }; Type GetType() const @@ -368,6 +376,12 @@ public: aRv = Send(RequestBody(aBlob)); } + virtual void Send(JSContext* /*aCx*/, URLSearchParams& aURLSearchParams, + ErrorResult& aRv) override + { + aRv = Send(RequestBody(aURLSearchParams)); + } + virtual void Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) override { diff --git a/dom/xhr/XMLHttpRequestWorker.cpp b/dom/xhr/XMLHttpRequestWorker.cpp index 093290a67cea..b09aa14e9d00 100644 --- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -21,6 +21,7 @@ #include "mozilla/dom/FormData.h" #include "mozilla/dom/ProgressEvent.h" #include "mozilla/dom/StructuredCloneHolder.h" +#include "mozilla/dom/URLSearchParams.h" #include "mozilla/Telemetry.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" @@ -2166,6 +2167,39 @@ XMLHttpRequestWorker::Send(JSContext* aCx, FormData& aBody, ErrorResult& aRv) SendInternal(sendRunnable, aRv); } +void +XMLHttpRequestWorker::Send(JSContext* aCx, URLSearchParams& aBody, + ErrorResult& aRv) +{ + mWorkerPrivate->AssertIsOnWorkerThread(); + + if (mCanceled) { + aRv.ThrowUncatchableException(); + return; + } + + if (!mProxy) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + JS::Rooted value(aCx); + if (!GetOrCreateDOMReflector(aCx, &aBody, &value)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + RefPtr sendRunnable = + new SendRunnable(mWorkerPrivate, mProxy, EmptyString()); + + sendRunnable->Write(aCx, value, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + SendInternal(sendRunnable, aRv); +} + void XMLHttpRequestWorker::Send(JSContext* aCx, const ArrayBuffer& aBody, ErrorResult& aRv) diff --git a/dom/xhr/XMLHttpRequestWorker.h b/dom/xhr/XMLHttpRequestWorker.h index 6edbc084de6c..f924554733b6 100644 --- a/dom/xhr/XMLHttpRequestWorker.h +++ b/dom/xhr/XMLHttpRequestWorker.h @@ -175,6 +175,9 @@ public: virtual void Send(JSContext* aCx, const ArrayBufferView& aBody, ErrorResult& aRv) override; + virtual void + Send(JSContext* aCx, URLSearchParams& aBody, ErrorResult& aRv) override; + virtual void Send(JSContext* aCx, nsIDocument& aDoc, ErrorResult& aRv) override { diff --git a/testing/web-platform/meta/XMLHttpRequest/setrequestheader-content-type.htm.ini b/testing/web-platform/meta/XMLHttpRequest/setrequestheader-content-type.htm.ini new file mode 100644 index 000000000000..6e9ca775ee21 --- /dev/null +++ b/testing/web-platform/meta/XMLHttpRequest/setrequestheader-content-type.htm.ini @@ -0,0 +1,10 @@ +[setrequestheader-content-type.htm] + type: testharness + [ReadableStream request respects setRequestHeader("")] + expected: FAIL + + [ReadableStream request with under type sends no Content-Type without setRequestHeader() call] + expected: FAIL + + [ReadableStream request keeps setRequestHeader() Content-Type and charset] + expected: FAIL diff --git a/testing/web-platform/tests/XMLHttpRequest/setrequestheader-content-type.htm b/testing/web-platform/tests/XMLHttpRequest/setrequestheader-content-type.htm index a648efa64ca6..64a72927a6dd 100644 --- a/testing/web-platform/tests/XMLHttpRequest/setrequestheader-content-type.htm +++ b/testing/web-platform/tests/XMLHttpRequest/setrequestheader-content-type.htm @@ -9,19 +9,229 @@
diff --git a/toolkit/components/places/tests/cpp/mock_Link.h b/toolkit/components/places/tests/cpp/mock_Link.h index 7d2ec50a39d9..8e1cb91c9344 100644 --- a/toolkit/components/places/tests/cpp/mock_Link.h +++ b/toolkit/components/places/tests/cpp/mock_Link.h @@ -215,6 +215,14 @@ URLSearchParams::NotifyObserver() NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver"); } +NS_IMETHODIMP +URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, + nsACString& aContentType, nsACString& aCharset) +{ + NS_NOTREACHED("Unexpected call to URLSearchParams::GetSendInfo"); + return NS_OK; +} + } // namespace dom } // namespace mozilla