summaryrefslogtreecommitdiff
path: root/dom
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2023-10-03 17:23:19 +0000
committerMoonchild <moonchild@palemoon.org>2023-10-03 17:23:19 +0000
commit5d76f359905f10fbd5b4c53ac458e26d22ffe2a1 (patch)
tree912d084cee32880dad7b06fcd1fdc1fc71ce5b95 /dom
parent682e5a4727b8bdf33a670c5d08e9cb382f019a57 (diff)
parent054f135a5455967009587e12c14908904ab4105a (diff)
downloaduxp-5d76f359905f10fbd5b4c53ac458e26d22ffe2a1.tar.gz
Merge pull request 'Initial implementation of readable streams' (#2324) from dbsoft/UXP:readablestreams into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/2324
Diffstat (limited to 'dom')
-rw-r--r--dom/base/FormData.cpp4
-rw-r--r--dom/base/Navigator.cpp131
-rw-r--r--dom/base/Navigator.h19
-rw-r--r--dom/base/nsContentUtils.cpp90
-rw-r--r--dom/base/nsContentUtils.h6
-rw-r--r--dom/bindings/Bindings.conf7
-rw-r--r--dom/bindings/Codegen.py164
-rw-r--r--dom/bindings/ReadableStream.h28
-rw-r--r--dom/bindings/SpiderMonkeyInterface.h144
-rw-r--r--dom/bindings/TypedArray.h134
-rw-r--r--dom/bindings/moz.build2
-rw-r--r--dom/bindings/parser/WebIDL.py39
-rw-r--r--dom/cache/AutoUtils.cpp8
-rw-r--r--dom/cache/AutoUtils.h2
-rw-r--r--dom/cache/Cache.cpp40
-rw-r--r--dom/cache/Cache.h16
-rw-r--r--dom/cache/CacheOpChild.cpp12
-rw-r--r--dom/cache/CacheStorage.cpp9
-rw-r--r--dom/cache/CacheStorage.h6
-rw-r--r--dom/cache/CacheWorkerHolder.cpp31
-rw-r--r--dom/cache/CacheWorkerHolder.h8
-rw-r--r--dom/cache/TypeUtils.cpp25
-rw-r--r--dom/cache/TypeUtils.h12
-rw-r--r--dom/canvas/CanvasRenderingContext2D.cpp8
-rw-r--r--dom/canvas/ImageBitmap.cpp2
-rw-r--r--dom/canvas/WebGLTextureUpload.cpp4
-rw-r--r--dom/crypto/WebCryptoTask.cpp4
-rw-r--r--dom/fetch/BodyExtractor.cpp207
-rw-r--r--dom/fetch/BodyExtractor.h48
-rw-r--r--dom/fetch/Fetch.cpp514
-rw-r--r--dom/fetch/Fetch.h519
-rw-r--r--dom/fetch/FetchStream.cpp640
-rw-r--r--dom/fetch/FetchStream.h158
-rw-r--r--dom/fetch/FetchStreamReader.cpp405
-rw-r--r--dom/fetch/FetchStreamReader.h85
-rw-r--r--dom/fetch/InternalResponse.cpp6
-rw-r--r--dom/fetch/InternalResponse.h8
-rw-r--r--dom/fetch/Request.cpp42
-rw-r--r--dom/fetch/Request.h3
-rw-r--r--dom/fetch/Response.cpp190
-rw-r--r--dom/fetch/Response.h9
-rw-r--r--dom/fetch/moz.build5
-rw-r--r--dom/network/TCPSocketParent.cpp2
-rw-r--r--dom/url/URLSearchParams.cpp5
-rw-r--r--dom/webidl/Fetch.webidl14
-rw-r--r--dom/webidl/Navigator.webidl2
-rw-r--r--dom/webidl/Response.webidl10
-rw-r--r--dom/workers/RuntimeService.cpp1
-rw-r--r--dom/workers/ScriptLoader.cpp14
-rw-r--r--dom/workers/ServiceWorkerEvents.cpp130
-rw-r--r--dom/workers/ServiceWorkerScriptCache.cpp10
-rw-r--r--dom/workers/WorkerHolder.cpp9
-rw-r--r--dom/workers/WorkerHolder.h11
-rw-r--r--dom/workers/WorkerPrefs.h1
-rw-r--r--dom/workers/WorkerPrivate.cpp15
-rw-r--r--dom/workers/WorkerPrivate.h1
-rw-r--r--dom/xhr/XMLHttpRequestMainThread.cpp192
-rw-r--r--dom/xhr/XMLHttpRequestMainThread.h52
-rw-r--r--dom/xhr/nsIXMLHttpRequest.idl5
59 files changed, 3206 insertions, 1062 deletions
diff --git a/dom/base/FormData.cpp b/dom/base/FormData.cpp
index de4d71a676..d47a970fc8 100644
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -398,7 +398,7 @@ FormData::Constructor(const GlobalObject& aGlobal,
NS_IMETHODIMP
FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset)
+ nsACString& aContentTypeWithCharset, nsACString& aCharset)
{
FSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
@@ -419,7 +419,7 @@ FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
}
}
- fs.GetContentType(aContentType);
+ fs.GetContentType(aContentTypeWithCharset);
aCharset.Truncate();
*aContentLength = 0;
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 05268a5156..0bf9ccbf45 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -11,7 +11,9 @@
#include "nsPluginArray.h"
#include "nsMimeTypeArray.h"
#include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/DesktopNotification.h"
+#include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/File.h"
#include "nsGeolocation.h"
#include "nsIClassOfService.h"
@@ -38,6 +40,7 @@
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
+#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/workers/RuntimeService.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
@@ -845,9 +848,53 @@ BeaconStreamListener::OnDataAvailable(nsIRequest *aRequest,
bool
Navigator::SendBeacon(const nsAString& aUrl,
- const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
+ const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv)
{
+ if (aData.IsNull()) {
+ return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsArrayBuffer()) {
+ BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsArrayBufferView()) {
+ BodyExtractor<const ArrayBufferView> body(&aData.Value().GetAsArrayBufferView());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsBlob()) {
+ BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsBlob());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsFormData()) {
+ BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsFormData());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsUSVString()) {
+ BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ if (aData.Value().IsURLSearchParams()) {
+ BodyExtractor<nsIXHRSendable> body(&aData.Value().GetAsURLSearchParams());
+ return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
+ }
+
+ MOZ_CRASH("Invalid data type.");
+ return false;
+}
+
+bool
+Navigator::SendBeaconInternal(const nsAString& aUrl,
+ BodyExtractorBase* aBody,
+ BeaconType aType,
+ ErrorResult& aRv)
+{
if (!mWindow) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
@@ -887,9 +934,9 @@ Navigator::SendBeacon(const nsAString& aUrl,
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
// No need to use CORS for sendBeacon unless it's a BLOB
- nsSecurityFlags securityFlags = (!aData.IsNull() && aData.Value().IsBlob())
- ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
- : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+ nsSecurityFlags securityFlags = aType == eBeaconTypeBlob
+ ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
+ : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
nsCOMPtr<nsIChannel> channel;
@@ -915,76 +962,30 @@ Navigator::SendBeacon(const nsAString& aUrl,
}
httpChannel->SetReferrer(documentURI);
- nsCString mimeType;
- if (!aData.IsNull()) {
- nsCOMPtr<nsIInputStream> in;
-
- if (aData.Value().IsString()) {
- nsCString stringData = NS_ConvertUTF16toUTF8(aData.Value().GetAsString());
- nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
- if (NS_FAILED(rv)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
- rv = strStream->SetData(stringData.BeginReading(), stringData.Length());
- if (NS_FAILED(rv)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
- mimeType.AssignLiteral("text/plain;charset=UTF-8");
- in = strStream;
-
- } else if (aData.Value().IsArrayBufferView()) {
-
- nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
- if (NS_FAILED(rv)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
-
- const ArrayBufferView& view = aData.Value().GetAsArrayBufferView();
- view.ComputeLengthAndData();
- rv = strStream->SetData(reinterpret_cast<char*>(view.Data()),
- view.Length());
-
- if (NS_FAILED(rv)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return false;
- }
- mimeType.AssignLiteral("application/octet-stream");
- in = strStream;
-
- } else if (aData.Value().IsBlob()) {
- Blob& blob = aData.Value().GetAsBlob();
- blob.GetInternalStream(getter_AddRefs(in), aRv);
- if (NS_WARN_IF(aRv.Failed())) {
- return false;
- }
+ nsCOMPtr<nsIInputStream> in;
+ nsAutoCString contentTypeWithCharset;
+ nsAutoCString charset;
+ uint64_t length = 0;
- nsAutoString type;
- blob.GetType(type);
- mimeType = NS_ConvertUTF16toUTF8(type);
-
- } else if (aData.Value().IsFormData()) {
- FormData& form = aData.Value().GetAsFormData();
- uint64_t len;
- nsAutoCString charset;
- form.GetSendInfo(getter_AddRefs(in),
- &len,
- mimeType,
- charset);
- } else {
- MOZ_ASSERT(false, "switch statements not in sync");
- aRv.Throw(NS_ERROR_FAILURE);
+ if (aBody) {
+ aRv = aBody->GetAsStream(getter_AddRefs(in), &length,
+ contentTypeWithCharset, charset);
+ if (NS_WARN_IF(aRv.Failed())) {
return false;
}
+ if (aType == eBeaconTypeArrayBuffer) {
+ MOZ_ASSERT(contentTypeWithCharset.IsEmpty());
+ MOZ_ASSERT(charset.IsEmpty());
+ contentTypeWithCharset.Assign("application/octet-stream");
+ }
+
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
if (!uploadChannel) {
aRv.Throw(NS_ERROR_FAILURE);
return false;
}
- uploadChannel->ExplicitSetUploadStream(in, mimeType, -1,
+ uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length,
NS_LITERAL_CSTRING("POST"),
false);
} else {
diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h
index 2915b50692..175940e82d 100644
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -7,6 +7,7 @@
#define mozilla_dom_Navigator_h
#include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/ErrorResult.h"
#include "nsIDOMNavigator.h"
@@ -30,12 +31,13 @@ class nsIURI;
namespace mozilla {
namespace dom {
+class BodyExtractorBase;
class Geolocation;
class systemMessageCallback;
class MediaDevices;
struct MediaStreamConstraints;
class WakeLock;
-class ArrayBufferViewOrBlobOrStringOrFormData;
+class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class ServiceWorkerContainer;
class DOMRequest;
} // namespace dom
@@ -196,7 +198,7 @@ public:
#endif // MOZ_AUDIO_CHANNEL_MANAGER
bool SendBeacon(const nsAString& aUrl,
- const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
+ const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv);
void MozGetUserMedia(const MediaStreamConstraints& aConstraints,
@@ -253,6 +255,19 @@ private:
bool CheckPermission(const char* type);
static bool CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType);
+ // This enum helps SendBeaconInternal to apply different behaviors to body
+ // types.
+ enum BeaconType {
+ eBeaconTypeBlob,
+ eBeaconTypeArrayBuffer,
+ eBeaconTypeOther
+ };
+
+ bool SendBeaconInternal(const nsAString& aUrl,
+ BodyExtractorBase* aBody,
+ BeaconType aType,
+ ErrorResult& aRv);
+
RefPtr<nsMimeTypeArray> mMimeTypes;
RefPtr<nsPluginArray> mPlugins;
RefPtr<Permissions> mPermissions;
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 86e52984af..4c00f358f2 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -39,6 +39,8 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FileSystemSecurity.h"
@@ -8735,6 +8737,25 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
// static
bool
+nsContentUtils::StreamsEnabled(JSContext* aCx, JSObject* aObj)
+{
+ if (NS_IsMainThread()) {
+ return Preferences::GetBool("dom.streams.enabled", false);
+ }
+
+ using namespace workers;
+
+ // Otherwise, check the pref via the WorkerPrivate
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ if (!workerPrivate) {
+ return false;
+ }
+
+ return workerPrivate->StreamsEnabled();
+}
+
+// static
+bool
nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
{
nsLoadFlags loadFlags = 0;
@@ -10060,3 +10081,72 @@ void nsContentUtils::StructuredClone(JSContext* aCx,
nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
Unused << ports;
}
+
+/* static */ void
+nsContentUtils::ExtractErrorValues(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ nsACString& aSourceSpecOut,
+ uint32_t* aLineOut,
+ uint32_t* aColumnOut,
+ nsString& aMessageOut)
+{
+ MOZ_ASSERT(aLineOut);
+ MOZ_ASSERT(aColumnOut);
+
+ if (aValue.isObject()) {
+ JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+ RefPtr<dom::DOMException> domException;
+
+ // Try to process as an Error object. Use the file/line/column values
+ // from the Error as they will be more specific to the root cause of
+ // the problem.
+ JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
+ if (err) {
+ // Use xpc to extract the error message only. We don't actually send
+ // this report anywhere.
+ RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
+ report->Init(err,
+ "<unknown>", // toString result
+ false, // chrome
+ 0); // window ID
+
+ if (!report->mFileName.IsEmpty()) {
+ CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
+ *aLineOut = report->mLineNumber;
+ *aColumnOut = report->mColumn;
+ }
+ aMessageOut.Assign(report->mErrorMsg);
+ }
+
+ // Next, try to unwrap the rejection value as a DOMException.
+ else if(NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException))) {
+
+ nsAutoString filename;
+ domException->GetFilename(aCx, filename);
+ if (!filename.IsEmpty()) {
+ CopyUTF16toUTF8(filename, aSourceSpecOut);
+ *aLineOut = domException->LineNumber(aCx);
+ *aColumnOut = domException->ColumnNumber();
+ }
+
+ domException->GetName(aMessageOut);
+ aMessageOut.AppendLiteral(": ");
+
+ nsAutoString message;
+ domException->GetMessageMoz(message);
+ aMessageOut.Append(message);
+ }
+ }
+
+ // If we could not unwrap a specific error type, then perform default safe
+ // string conversions on primitives. Objects will result in "[Object]"
+ // unfortunately.
+ if (aMessageOut.IsEmpty()) {
+ nsAutoJSString jsString;
+ if (jsString.init(aCx, aValue)) {
+ aMessageOut = jsString;
+ } else {
+ JS_ClearPendingException(aCx);
+ }
+ }
+}
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 58f9f56546..6e9f23054a 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1010,6 +1010,10 @@ public:
static bool PrefetchEnabled(nsIDocShell* aDocShell);
+ static void ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ nsACString& aSourceSpecOut, uint32_t *aLineOut,
+ uint32_t *aColumnOut, nsString& aMessageOut);
+
static nsresult CalculateBufferSizeForImage(const uint32_t& aStride,
const mozilla::gfx::IntSize& aImageSize,
const mozilla::gfx::SurfaceFormat& aFormat,
@@ -2679,6 +2683,8 @@ public:
static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader);
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
+
+ static bool StreamsEnabled(JSContext* aCx, JSObject* aObj);
static bool IsNonSubresourceRequest(nsIChannel* aChannel);
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index a12a294766..17bbed79d5 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -103,11 +103,13 @@ DOMInterfaces = {
},
'Cache': {
- 'implicitJSContext': [ 'add', 'addAll' ],
+ 'implicitJSContext': [ 'add', 'addAll', 'match', 'matchAll', 'put',
+ 'delete', 'keys' ],
'nativeType': 'mozilla::dom::cache::Cache',
},
'CacheStorage': {
+ 'implicitJSContext': [ 'match' ],
'nativeType': 'mozilla::dom::cache::CacheStorage',
},
@@ -705,6 +707,7 @@ DOMInterfaces = {
'headers': 'headers_',
'referrerPolicy': 'referrerPolicy_'
},
+ 'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
},
'ResizeObservation': {
@@ -729,6 +732,8 @@ DOMInterfaces = {
'Response': {
'binaryNames': { 'headers': 'headers_' },
+ 'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text',
+ 'clone', 'cloneUnfiltered' ],
},
'RGBColor': {
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index a70253fc3a..b7caaad7bb 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1156,7 +1156,10 @@ class CGHeaders(CGWrapper):
# just include their header if we need to have functions
# taking references to them declared in that header.
headerSet = declareIncludes
- headerSet.add("mozilla/dom/TypedArray.h")
+ if unrolled.isReadableStream():
+ headerSet.add("mozilla/dom/ReadableStream.h")
+ else:
+ headerSet.add("mozilla/dom/TypedArray.h")
else:
try:
typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
@@ -1371,7 +1374,10 @@ def UnionTypes(unionTypes, config):
elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
- headers.add("mozilla/dom/TypedArray.h")
+ if f.isReadableStream():
+ headers.add("mozilla/dom/ReadableStream.h")
+ else:
+ headers.add("mozilla/dom/TypedArray.h")
else:
try:
typeDesc = config.getDescriptor(f.inner.identifier.name)
@@ -1457,7 +1463,10 @@ def UnionConversions(unionTypes, config):
elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
- headers.add("mozilla/dom/TypedArray.h")
+ if f.isReadableStream():
+ headers.add("mozilla/dom/ReadableStream.h")
+ else:
+ headers.add("mozilla/dom/TypedArray.h")
elif f.inner.isExternal():
try:
typeDesc = config.getDescriptor(f.inner.identifier.name)
@@ -5582,8 +5591,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isSpiderMonkeyInterface():
assert not isEnforceRange and not isClamp
name = type.unroll().name # unroll() because it may be nullable
- arrayType = CGGeneric(name)
- declType = arrayType
+ interfaceType = CGGeneric(name)
+ declType = interfaceType
if type.nullable():
declType = CGTemplatedType("Nullable", declType)
objRef = "${declName}.SetValue()"
@@ -5607,22 +5616,23 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# This is a bit annoying. In a union we don't want to have a
# holder, since unions don't support that. But if we're optional we
# want to have a holder, so that the callee doesn't see
- # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
- # optional and use a RootedTypedArray otherwise.
+ # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
+ # holder if we're optional and use a RootedSpiderMonkeyInterface
+ # otherwise.
if isOptional:
- holderType = CGTemplatedType("TypedArrayRooter", arrayType)
- # If our typed array is nullable, this will set the Nullable to
- # be not-null, but that's ok because we make an explicit
- # SetNull() call on it as needed if our JS value is actually
- # null. XXXbz Because "Maybe" takes const refs for constructor
- # arguments, we can't pass a reference here; have to pass a
- # pointer.
+ holderType = CGTemplatedType("SpiderMonkeyInterfaceRooter", interfaceType)
+ # If our SpiderMonkey interface is nullable, this will set the
+ # Nullable to be not-null, but that's ok because we make an
+ # explicit SetNull() call on it as needed if our JS value is
+ # actually null. XXXbz Because "Maybe" takes const refs for
+ # constructor arguments, we can't pass a reference here; have
+ # to pass a pointer.
holderArgs = "cx, &%s" % objRef
declArgs = None
else:
holderType = None
holderArgs = None
- declType = CGTemplatedType("RootedTypedArray", declType)
+ declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
declArgs = "cx"
else:
holderType = None
@@ -6371,7 +6381,7 @@ def getMaybeWrapValueFuncForType(type):
if type.nullable():
return "MaybeWrapObjectOrNullValue"
return "MaybeWrapObjectValue"
- # Spidermonkey interfaces are never DOM objects. Neither are sequences or
+ # SpiderMonkey interfaces are never DOM objects. Neither are sequences or
# dictionaries, since those are always plain JS objects.
if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
if type.nullable():
@@ -6391,7 +6401,8 @@ recordWrapLevel = 0
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
- returnsNewObject, exceptionCode, typedArraysAreStructs,
+ returnsNewObject, exceptionCode,
+ spiderMonkeyInterfacesAreStructs,
isConstructorRetval=False):
"""
Reflect a C++ value stored in "result", of IDL type "type" into JS. The
@@ -6401,8 +6412,9 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
doing a 'break' if the entire conversion template is inside a block that
the 'break' will exit).
- If typedArraysAreStructs is true, then if the type is a typed array,
- "result" is one of the dom::TypedArray subclasses, not a JSObject*.
+ If spiderMonkeyInterfacesAreStructs is true, then if the type is a
+ SpiderMonkey interface, "result" is one of the
+ dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
The resulting string should be used with string.Template. It
needs the following keys when substituting:
@@ -6497,7 +6509,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
returnsNewObject, exceptionCode,
- typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs)
code = fill(
"""
@@ -6529,7 +6541,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode,
'obj': "returnArray",
- 'typedArraysAreStructs': typedArraysAreStructs
+ 'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
})
sequenceWrapLevel -= 1
code = fill(
@@ -6583,7 +6595,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode,
'obj': "returnObj",
- 'typedArraysAreStructs': typedArraysAreStructs
+ 'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
})
recordWrapLevel -= 1
if type.keyType.isByteString():
@@ -6749,7 +6761,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (head + _setValue(result, wrapAsType=type), False)
if (type.isObject() or (type.isSpiderMonkeyInterface() and
- not typedArraysAreStructs)):
+ not spiderMonkeyInterfacesAreStructs)):
# See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here.
if type.nullable():
@@ -6768,21 +6780,21 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
type.isDate() or
- (type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
+ (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)):
raise TypeError("Need to learn to wrap %s" % type)
if type.nullable():
recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
returnsNewObject, exceptionCode,
- typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs)
return ("if (%s.IsNull()) {\n" % result +
indent(setNull()) +
"}\n" +
recTemplate, recInfal)
if type.isSpiderMonkeyInterface():
- assert typedArraysAreStructs
+ assert spiderMonkeyInterfacesAreStructs
# See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here.
# NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
@@ -6864,7 +6876,7 @@ def wrapForType(type, descriptorProvider, templateValues):
templateValues.get('successCode', None),
templateValues.get('returnsNewObject', False),
templateValues.get('exceptionCode', "return false;\n"),
- templateValues.get('typedArraysAreStructs', False),
+ templateValues.get('spiderMonkeyInterfacesAreStructs', False),
isConstructorRetval=templateValues.get('isConstructorRetval', False))[0]
defaultValues = {'obj': 'obj'}
@@ -8193,10 +8205,10 @@ class CGMethodCall(CGThing):
# "object" arg.
# First grab all the overloads that have a non-callback interface
- # (which includes typed arrays and arraybuffers) at the
- # distinguishing index. We can also include the ones that have an
- # "object" here, since if those are present no other object-typed
- # argument will be.
+ # (which includes SpiderMonkey interfaces) at the distinguishing
+ # index. We can also include the ones that have an "object" here,
+ # since if those are present no other object-typed argument will
+ # be.
objectSigs = [
s for s in possibleSignatures
if (distinguishingType(s).isObject() or
@@ -8227,16 +8239,17 @@ class CGMethodCall(CGThing):
# There might be more than one thing in objectSigs; we need to check
# which ones we unwrap to.
if len(objectSigs) > 0:
- # Here it's enough to guard on our argument being an object. The
- # code for unwrapping non-callback interfaces, typed arrays,
- # sequences, and Dates will just bail out and move on to
- # the next overload if the object fails to unwrap correctly,
- # while "object" accepts any object anyway. We could even not
- # do the isObject() check up front here, but in cases where we
- # have multiple object overloads it makes sense to do it only
- # once instead of for each overload. That will also allow the
- # unwrapping test to skip having to do codegen for the
- # null-or-undefined case, which we already handled above.
+ # Here it's enough to guard on our argument being an object.
+ # The code for unwrapping non-callback interfaces, spiderMonkey
+ # interfaces, sequences, and Dates will just bail out and move
+ # on to the next overload if the object fails to unwrap
+ # correctly, while "object" accepts any object anyway. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
distinguishingArg))
for sig in objectSigs:
@@ -10239,7 +10252,7 @@ class CGUnionStruct(CGThing):
"jsvalHandle": "rval",
"obj": "scopeObj",
"result": val,
- "typedArraysAreStructs": True
+ "spiderMonkeyInterfacesAreStructs": True
})
return CGGeneric(wrapCode)
@@ -13219,7 +13232,7 @@ class CGDictionary(CGThing):
# 'obj' can just be allowed to be the string "obj", since that
# will be our dictionary object, which is presumably itself in
# the right scope.
- 'typedArraysAreStructs': True
+ 'spiderMonkeyInterfacesAreStructs': True
})
conversion = CGGeneric(innerTemplate)
conversion = CGWrapper(conversion,
@@ -13679,7 +13692,7 @@ class ForwardDeclarationBuilder:
except NoSuchDescriptorError:
pass
- # Note: Spidermonkey interfaces are typedefs, so can't be
+ # Note: SpiderMonkey interfaces are typedefs, so can't be
# forward-declared
elif t.isPromise():
self.addInMozillaDom("Promise")
@@ -13988,7 +14001,7 @@ class CGBindingRoot(CGThing):
return {desc.getDescriptor(desc.interface.parent.identifier.name)}
for x in dependencySortObjects(jsImplemented, getParentDescriptor,
lambda d: d.interface.identifier.name):
- cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True))
+ cgthings.append(CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True))
cgthings.append(CGJSImplClass(x))
# And make sure we have the right number of newlines at the end
@@ -14050,14 +14063,13 @@ class CGBindingRoot(CGThing):
class CGNativeMember(ClassMethod):
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
- typedArraysAreStructs=True, variadicIsSequence=False,
- resultNotAddRefed=False,
- virtual=False,
- override=False):
+ spiderMonkeyInterfacesAreStructs=True,
+ variadicIsSequence=False, resultNotAddRefed=False,
+ virtual=False, override=False):
"""
- If typedArraysAreStructs is false, typed arrays will be passed as
- JS::Handle<JSObject*>. If it's true they will be passed as one of the
- dom::TypedArray subclasses.
+ If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
+ will be passed as JS::Handle<JSObject*>. If it's true they will be
+ passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
If passJSBitsAsNeeded is false, we don't automatically pass in a
JSContext* or a JSObject* based on the return and argument types. We
@@ -14068,7 +14080,7 @@ class CGNativeMember(ClassMethod):
self.extendedAttrs = extendedAttrs
self.resultAlreadyAddRefed = not resultNotAddRefed
self.passJSBitsAsNeeded = passJSBitsAsNeeded
- self.typedArraysAreStructs = typedArraysAreStructs
+ self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
self.variadicIsSequence = variadicIsSequence
breakAfterSelf = "\n" if breakAfter else ""
ClassMethod.__init__(self, name,
@@ -14375,7 +14387,7 @@ class CGNativeMember(ClassMethod):
False, False)
if type.isSpiderMonkeyInterface():
- if not self.typedArraysAreStructs:
+ if not self.spiderMonkeyInterfacesAreStructs:
return "JS::Handle<JSObject*>", False, False
# Unroll for the name, in case we're nullable.
@@ -15547,16 +15559,16 @@ class CGFastCallback(CGClass):
class CGCallbackInterface(CGCallback):
- def __init__(self, descriptor, typedArraysAreStructs=False):
+ def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
iface = descriptor.interface
attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
- getters = [CallbackGetter(a, descriptor, typedArraysAreStructs)
+ getters = [CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
for a in attrs]
- setters = [CallbackSetter(a, descriptor, typedArraysAreStructs)
+ setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
for a in attrs if not a.readonly]
methods = [m for m in iface.members
if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
- methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs)
+ methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
for m in methods for sig in m.signatures()]
if iface.isJSImplemented() and iface.ctor():
sigs = descriptor.interface.ctor().signatures()
@@ -15601,7 +15613,8 @@ class CallbackMember(CGNativeMember):
# CallSetup already handled the unmark-gray bits for us. we don't have
# anything better to use for 'obj', really...
def __init__(self, sig, name, descriptorProvider, needThisHandling,
- rethrowContentException=False, typedArraysAreStructs=False,
+ rethrowContentException=False,
+ spiderMonkeyInterfacesAreStructs=False,
wrapScope='CallbackKnownNotGray()'):
"""
needThisHandling is True if we need to be able to accept a specified
@@ -15636,7 +15649,7 @@ class CallbackMember(CGNativeMember):
extendedAttrs={},
passJSBitsAsNeeded=False,
visibility=visibility,
- typedArraysAreStructs=typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
# We have to do all the generation of our body now, because
# the caller relies on us throwing if we can't manage it.
self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
@@ -15753,7 +15766,7 @@ class CallbackMember(CGNativeMember):
'obj': self.wrapScope,
'returnsNewObject': False,
'exceptionCode': self.exceptionCode,
- 'typedArraysAreStructs': self.typedArraysAreStructs
+ 'spiderMonkeyInterfacesAreStructs': self.spiderMonkeyInterfacesAreStructs
})
except MethodNotNewObjectError as err:
raise TypeError("%s being passed as an argument to %s but is not "
@@ -15857,10 +15870,11 @@ class CallbackMember(CGNativeMember):
class CallbackMethod(CallbackMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling,
- rethrowContentException=False, typedArraysAreStructs=False):
+ rethrowContentException=False,
+ spiderMonkeyInterfacesAreStructs=False):
CallbackMember.__init__(self, sig, name, descriptorProvider,
needThisHandling, rethrowContentException,
- typedArraysAreStructs=typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@@ -15919,12 +15933,12 @@ class CallbackOperationBase(CallbackMethod):
"""
def __init__(self, signature, jsName, nativeName, descriptor,
singleOperation, rethrowContentException=False,
- typedArraysAreStructs=False):
+ spiderMonkeyInterfacesAreStructs=False):
self.singleOperation = singleOperation
self.methodName = descriptor.binaryNameFor(jsName)
CallbackMethod.__init__(self, signature, nativeName, descriptor,
singleOperation, rethrowContentException,
- typedArraysAreStructs=typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getThisDecl(self):
if not self.singleOperation:
@@ -15975,7 +15989,8 @@ class CallbackOperation(CallbackOperationBase):
"""
Codegen actual WebIDL operations on callback interfaces.
"""
- def __init__(self, method, signature, descriptor, typedArraysAreStructs):
+ def __init__(self, method, signature, descriptor,
+ spiderMonkeyInterfacesAreStructs):
self.ensureASCIIName(method)
self.method = method
jsName = method.identifier.name
@@ -15984,7 +15999,7 @@ class CallbackOperation(CallbackOperationBase):
MakeNativeName(descriptor.binaryNameFor(jsName)),
descriptor, descriptor.interface.isSingleOperationInterface(),
rethrowContentException=descriptor.interface.isJSImplemented(),
- typedArraysAreStructs=typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@@ -15995,13 +16010,14 @@ class CallbackAccessor(CallbackMember):
"""
Shared superclass for CallbackGetter and CallbackSetter.
"""
- def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs):
+ def __init__(self, attr, sig, name, descriptor,
+ spiderMonkeyInterfacesAreStructs):
self.ensureASCIIName(attr)
self.attrName = attr.identifier.name
CallbackMember.__init__(self, sig, name, descriptor,
needThisHandling=False,
rethrowContentException=descriptor.interface.isJSImplemented(),
- typedArraysAreStructs=typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@@ -16009,12 +16025,12 @@ class CallbackAccessor(CallbackMember):
class CallbackGetter(CallbackAccessor):
- def __init__(self, attr, descriptor, typedArraysAreStructs):
+ def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr,
(attr.type, []),
callbackGetterName(attr, descriptor),
descriptor,
- typedArraysAreStructs)
+ spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@@ -16036,12 +16052,12 @@ class CallbackGetter(CallbackAccessor):
class CallbackSetter(CallbackAccessor):
- def __init__(self, attr, descriptor, typedArraysAreStructs):
+ def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr,
(BuiltinTypes[IDLBuiltinType.Types.void],
[FakeArgument(attr.type, attr)]),
callbackSetterName(attr, descriptor),
- descriptor, typedArraysAreStructs)
+ descriptor, spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
# We don't need an rval
@@ -16076,7 +16092,7 @@ class CGJSImplInitOperation(CallbackOperationBase):
"__init", "__Init", descriptor,
singleOperation=False,
rethrowContentException=True,
- typedArraysAreStructs=True)
+ spiderMonkeyInterfacesAreStructs=True)
def getPrettyName(self):
return "__init"
diff --git a/dom/bindings/ReadableStream.h b/dom/bindings/ReadableStream.h
new file mode 100644
index 0000000000..1ea7ac4d88
--- /dev/null
+++ b/dom/bindings/ReadableStream.h
@@ -0,0 +1,28 @@
+/* -*- 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_ReadableStream_h
+#define mozilla_dom_ReadableStream_h
+
+#include "mozilla/dom/SpiderMonkeyInterface.h"
+
+namespace mozilla {
+namespace dom {
+
+struct ReadableStream : public SpiderMonkeyInterfaceObjectStorage
+{
+ inline bool Init(JSObject* obj)
+ {
+ MOZ_ASSERT(!inited());
+ mImplObj = mWrappedObj = js::UnwrapReadableStream(obj);
+ return inited();
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ReadableStream_h */
diff --git a/dom/bindings/SpiderMonkeyInterface.h b/dom/bindings/SpiderMonkeyInterface.h
new file mode 100644
index 0000000000..f852afddaf
--- /dev/null
+++ b/dom/bindings/SpiderMonkeyInterface.h
@@ -0,0 +1,144 @@
+/* -*- 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_SpiderMonkeyInterface_h
+#define mozilla_dom_SpiderMonkeyInterface_h
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/TracingAPI.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Class that just handles the JSObject storage and tracing for spidermonkey
+ * interfaces
+ */
+struct SpiderMonkeyInterfaceObjectStorage
+{
+protected:
+ JSObject* mImplObj;
+ JSObject* mWrappedObj;
+
+ SpiderMonkeyInterfaceObjectStorage()
+ : mImplObj(nullptr),
+ mWrappedObj(nullptr)
+ {
+ }
+
+ SpiderMonkeyInterfaceObjectStorage(SpiderMonkeyInterfaceObjectStorage&& aOther)
+ : mImplObj(aOther.mImplObj),
+ mWrappedObj(aOther.mWrappedObj)
+ {
+ aOther.mImplObj = nullptr;
+ aOther.mWrappedObj = nullptr;
+ }
+
+public:
+ inline void TraceSelf(JSTracer* trc)
+ {
+ JS::UnsafeTraceRoot(trc, &mImplObj, "SpiderMonkeyInterfaceObjectStorage.mImplObj");
+ JS::UnsafeTraceRoot(trc, &mWrappedObj, "SpiderMonkeyInterfaceObjectStorage.mWrappedObj");
+ }
+
+ inline bool inited() const
+ {
+ return !!mImplObj;
+ }
+
+ inline bool WrapIntoNewCompartment(JSContext* cx)
+ {
+ return JS_WrapObject(cx,
+ JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
+ }
+
+ inline JSObject *Obj() const
+ {
+ MOZ_ASSERT(inited());
+ return mWrappedObj;
+ }
+
+private:
+ SpiderMonkeyInterfaceObjectStorage(const SpiderMonkeyInterfaceObjectStorage&) = delete;
+};
+
+// A class for rooting an existing SpiderMonkey Interface struct
+template<typename InterfaceType>
+class MOZ_RAII SpiderMonkeyInterfaceRooter : private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ SpiderMonkeyInterfaceRooter(const CX& cx,
+ InterfaceType* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mInterface(aInterface)
+ {
+ }
+
+ virtual void trace(JSTracer* trc) override
+ {
+ mInterface->TraceSelf(trc);
+ }
+
+private:
+ SpiderMonkeyInterfaceObjectStorage* const mInterface;
+};
+
+// And a specialization for dealing with nullable SpiderMonkey interfaces
+template<typename Inner> struct Nullable;
+template<typename InterfaceType>
+class MOZ_RAII SpiderMonkeyInterfaceRooter<Nullable<InterfaceType>> :
+ private JS::CustomAutoRooter
+{
+public:
+ template <typename CX>
+ SpiderMonkeyInterfaceRooter(const CX& cx,
+ Nullable<InterfaceType>* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
+ mInterface(aInterface)
+ {
+ }
+
+ virtual void trace(JSTracer* trc) override
+ {
+ if (!mInterface->IsNull()) {
+ mInterface->Value().TraceSelf(trc);
+ }
+ }
+
+private:
+ Nullable<InterfaceType>* const mInterface;
+};
+
+// Class for easily setting up a rooted SpiderMonkey interface object on the
+// stack
+template<typename InterfaceType>
+class MOZ_RAII RootedSpiderMonkeyInterface final : public InterfaceType,
+ private SpiderMonkeyInterfaceRooter<InterfaceType>
+{
+public:
+ template <typename CX>
+ explicit RootedSpiderMonkeyInterface(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : InterfaceType(),
+ SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+
+ template <typename CX>
+ RootedSpiderMonkeyInterface(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : InterfaceType(obj),
+ SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+ {
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_SpiderMonkeyInterface_h */
diff --git a/dom/bindings/TypedArray.h b/dom/bindings/TypedArray.h
index 631707579b..8de0621d46 100644
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -6,52 +6,16 @@
#ifndef mozilla_dom_TypedArray_h
#define mozilla_dom_TypedArray_h
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "js/RootingAPI.h"
-#include "js/TracingAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/SpiderMonkeyInterface.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
/*
- * Class that just handles the JSObject storage and tracing for typed arrays
- */
-struct TypedArrayObjectStorage : AllTypedArraysBase {
-protected:
- JSObject* mTypedObj;
- JSObject* mWrappedObj;
-
- TypedArrayObjectStorage()
- : mTypedObj(nullptr),
- mWrappedObj(nullptr)
- {
- }
-
- TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
- : mTypedObj(aOther.mTypedObj),
- mWrappedObj(aOther.mWrappedObj)
- {
- aOther.mTypedObj = nullptr;
- aOther.mWrappedObj = nullptr;
- }
-
-public:
- inline void TraceSelf(JSTracer* trc)
- {
- JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj");
- JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj");
- }
-
-private:
- TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete;
-};
-
-/*
* Various typed array classes for argument conversion. We have a base class
* that has a way of initializing a TypedArray from an existing typed array, and
* a subclass of the base class that supports creation of a relevant typed array
@@ -60,7 +24,9 @@ private:
template<typename T,
JSObject* UnwrapArray(JSObject*),
void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
-struct TypedArray_base : public TypedArrayObjectStorage {
+struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
+ AllTypedArraysBase
+{
typedef T element_type;
TypedArray_base()
@@ -72,7 +38,7 @@ struct TypedArray_base : public TypedArrayObjectStorage {
}
TypedArray_base(TypedArray_base&& aOther)
- : TypedArrayObjectStorage(Move(aOther)),
+ : SpiderMonkeyInterfaceObjectStorage(Move(aOther)),
mData(aOther.mData),
mLength(aOther.mLength),
mShared(aOther.mShared),
@@ -94,14 +60,10 @@ public:
inline bool Init(JSObject* obj)
{
MOZ_ASSERT(!inited());
- mTypedObj = mWrappedObj = UnwrapArray(obj);
+ mImplObj = mWrappedObj = UnwrapArray(obj);
return inited();
}
- inline bool inited() const {
- return !!mTypedObj;
- }
-
// About shared memory:
//
// Any DOM TypedArray as well as any DOM ArrayBufferView that does
@@ -173,22 +135,11 @@ public:
return mLength;
}
- inline JSObject *Obj() const {
- MOZ_ASSERT(inited());
- return mWrappedObj;
- }
-
- inline bool WrapIntoNewCompartment(JSContext* cx)
- {
- return JS_WrapObject(cx,
- JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
- }
-
inline void ComputeLengthAndData() const
{
MOZ_ASSERT(inited());
MOZ_ASSERT(!mComputed);
- GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData);
+ GetLengthAndDataAndSharedness(mImplObj, &mLength, &mShared, &mData);
mComputed = true;
}
@@ -363,77 +314,6 @@ class TypedArrayCreator
const ArrayType& mArray;
};
-// A class for rooting an existing TypedArray struct
-template<typename ArrayType>
-class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
-{
-public:
- template <typename CX>
- TypedArrayRooter(const CX& cx,
- ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
- JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
- mArray(aArray)
- {
- }
-
- virtual void trace(JSTracer* trc) override
- {
- mArray->TraceSelf(trc);
- }
-
-private:
- TypedArrayObjectStorage* const mArray;
-};
-
-// And a specialization for dealing with nullable typed arrays
-template<typename Inner> struct Nullable;
-template<typename ArrayType>
-class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
- private JS::CustomAutoRooter
-{
-public:
- template <typename CX>
- TypedArrayRooter(const CX& cx,
- Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
- JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
- mArray(aArray)
- {
- }
-
- virtual void trace(JSTracer* trc) override
- {
- if (!mArray->IsNull()) {
- mArray->Value().TraceSelf(trc);
- }
- }
-
-private:
- Nullable<ArrayType>* const mArray;
-};
-
-// Class for easily setting up a rooted typed array object on the stack
-template<typename ArrayType>
-class MOZ_RAII RootedTypedArray final : public ArrayType,
- private TypedArrayRooter<ArrayType>
-{
-public:
- template <typename CX>
- explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
- ArrayType(),
- TypedArrayRooter<ArrayType>(cx, this
- MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
- {
- }
-
- template <typename CX>
- RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
- ArrayType(obj),
- TypedArrayRooter<ArrayType>(cx, this
- MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
- {
- }
-};
-
} // namespace dom
} // namespace mozilla
diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build
index 9afaf4fd45..60309080e4 100644
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -38,9 +38,11 @@ EXPORTS.mozilla.dom += [
'NonRefcountedDOMObject.h',
'Nullable.h',
'PrimitiveConversions.h',
+ 'ReadableStream.h',
'Record.h',
'RootedDictionary.h',
'SimpleGlobalObject.h',
+ 'SpiderMonkeyInterface.h',
'StructuredClone.h',
'ToJSValue.h',
'TypedArray.h',
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
index 59db43f6bd..0aa3afa4e8 100644
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2054,6 +2054,9 @@ class IDLType(IDLObject):
def isRecord(self):
return False
+ def isReadableStream(self):
+ return False
+
def isArrayBuffer(self):
return False
@@ -2081,12 +2084,12 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self):
""" Returns a boolean indicating whether this type is an 'interface'
- type that is implemented in Spidermonkey. At the moment, this
- only returns true for the types from the TypedArray spec. """
+ type that is implemented in SpiderMonkey. """
return self.isInterface() and (self.isArrayBuffer() or
self.isArrayBufferView() or
self.isSharedArrayBuffer() or
- self.isTypedArray())
+ self.isTypedArray() or
+ self.isReadableStream())
def isDictionary(self):
return False
@@ -2275,6 +2278,9 @@ class IDLNullableType(IDLParameterizedType):
def isRecord(self):
return self.inner.isRecord()
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
+
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -2634,6 +2640,9 @@ class IDLTypedefType(IDLType):
def isRecord(self):
return self.inner.isRecord()
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
+
def isDictionary(self):
return self.inner.isDictionary()
@@ -2945,7 +2954,8 @@ class IDLBuiltinType(IDLType):
'Int32Array',
'Uint32Array',
'Float32Array',
- 'Float64Array'
+ 'Float64Array',
+ 'ReadableStream',
)
TagLookup = {
@@ -2980,7 +2990,8 @@ class IDLBuiltinType(IDLType):
Types.Int32Array: IDLType.Tags.interface,
Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: IDLType.Tags.interface,
- Types.Float64Array: IDLType.Tags.interface
+ Types.Float64Array: IDLType.Tags.interface,
+ Types.ReadableStream: IDLType.Tags.interface,
}
def __init__(self, location, name, type):
@@ -3027,6 +3038,9 @@ class IDLBuiltinType(IDLType):
return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
self._typeTag <= IDLBuiltinType.Types.Float64Array)
+ def isReadableStream(self):
+ return self._typeTag == IDLBuiltinType.Types.ReadableStream
+
def isInterface(self):
# TypedArray things are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements
@@ -3034,7 +3048,8 @@ class IDLBuiltinType(IDLType):
return (self.isArrayBuffer() or
self.isArrayBufferView() or
self.isSharedArrayBuffer() or
- self.isTypedArray())
+ self.isTypedArray() or
+ self.isReadableStream())
def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback
@@ -3104,6 +3119,7 @@ class IDLBuiltinType(IDLType):
# that's not an ArrayBuffer or a callback interface
(self.isArrayBuffer() and not other.isArrayBuffer()) or
(self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
+ (self.isReadableStream() and not other.isReadableStream()) or
# ArrayBufferView is distinguishable from everything
# that's not an ArrayBufferView or typed array.
(self.isArrayBufferView() and not other.isArrayBufferView() and
@@ -3213,7 +3229,10 @@ BuiltinTypes = {
IDLBuiltinType.Types.Float32Array),
IDLBuiltinType.Types.Float64Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
- IDLBuiltinType.Types.Float64Array)
+ IDLBuiltinType.Types.Float64Array),
+ IDLBuiltinType.Types.ReadableStream:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream",
+ IDLBuiltinType.Types.ReadableStream),
}
@@ -5232,7 +5251,8 @@ class Tokenizer(object):
"maplike": "MAPLIKE",
"setlike": "SETLIKE",
"iterable": "ITERABLE",
- "namespace": "NAMESPACE"
+ "namespace": "NAMESPACE",
+ "ReadableStream": "READABLESTREAM",
}
tokens.extend(keywords.values())
@@ -6420,6 +6440,7 @@ class Parser(Tokenizer):
NonAnyType : PrimitiveType Null
| ARRAYBUFFER Null
| SHAREDARRAYBUFFER Null
+ | READABLESTREAM Null
| OBJECT Null
"""
if p[1] == "object":
@@ -6428,6 +6449,8 @@ class Parser(Tokenizer):
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
elif p[1] == "SharedArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
+ elif p[1] == "ReadableStream":
+ type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
else:
type = BuiltinTypes[p[1]]
diff --git a/dom/cache/AutoUtils.cpp b/dom/cache/AutoUtils.cpp
index d1f354336e..bfa4cffd23 100644
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -289,9 +289,9 @@ MatchInPutList(InternalRequest* aRequest,
} // namespace
void
-AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
- SchemeAction aSchemeAction, Response& aResponse,
- ErrorResult& aRv)
+AutoChildOpArgs::Add(JSContext* aCx, InternalRequest* aRequest,
+ BodyAction aBodyAction, SchemeAction aSchemeAction,
+ Response& aResponse, ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(!mSent);
@@ -329,7 +329,7 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
aSchemeAction, mStreamCleanupList, aRv);
if (!aRv.Failed()) {
- mTypeUtils->ToCacheResponse(pair.response(), aResponse,
+ mTypeUtils->ToCacheResponse(aCx, pair.response(), aResponse,
mStreamCleanupList, aRv);
}
diff --git a/dom/cache/AutoUtils.h b/dom/cache/AutoUtils.h
index 244639f7c0..188c9d74e3 100644
--- a/dom/cache/AutoUtils.h
+++ b/dom/cache/AutoUtils.h
@@ -55,7 +55,7 @@ public:
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, ErrorResult& aRv);
- void Add(InternalRequest* aRequest, BodyAction aBodyAction,
+ void Add(JSContext* aCx, InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv);
const CacheOpArgs& SendAsOpArgs();
diff --git a/dom/cache/Cache.cpp b/dom/cache/Cache.cpp
index 60e4f76b92..dd142881ec 100644
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -184,7 +184,10 @@ public:
// Now store the unwrapped Response list in the Cache.
ErrorResult result;
- RefPtr<Promise> put = mCache->PutAll(mRequestList, responseList, result);
+ // TODO: Here we use the JSContext as received by the ResolvedCallback, and
+ // its state could be the wrong one. The spec doesn't say anything
+ // about it, yet (bug 1384006)
+ RefPtr<Promise> put = mCache->PutAll(aCx, mRequestList, responseList, result);
if (NS_WARN_IF(result.Failed())) {
// TODO: abort the fetch requests we have running (bug 1157434)
mPromise->MaybeReject(result);
@@ -245,7 +248,7 @@ Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
}
already_AddRefed<Promise>
-Cache::Match(const RequestOrUSVString& aRequest,
+Cache::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@@ -255,7 +258,8 @@ Cache::Match(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor);
- RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
+ RefPtr<InternalRequest> ir =
+ ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@@ -274,7 +278,7 @@ Cache::Match(const RequestOrUSVString& aRequest,
}
already_AddRefed<Promise>
-Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
+Cache::MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@@ -290,8 +294,8 @@ Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1);
if (aRequest.WasPassed()) {
- RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
- IgnoreBody, aRv);
+ RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest.Value(),
+ IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
@@ -390,8 +394,8 @@ Cache::AddAll(JSContext* aContext,
}
already_AddRefed<Promise>
-Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
- ErrorResult& aRv)
+Cache::Put(JSContext* aCx, const RequestOrUSVString& aRequest,
+ Response& aResponse, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -404,14 +408,14 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
return nullptr;
}
- RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
+ RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest, ReadBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
AutoChildOpArgs args(this, CachePutAllArgs(), 1);
- args.Add(ir, ReadBody, TypeErrorOnInvalidScheme,
+ args.Add(aCx, ir, ReadBody, TypeErrorOnInvalidScheme,
aResponse, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@@ -421,7 +425,7 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
}
already_AddRefed<Promise>
-Cache::Delete(const RequestOrUSVString& aRequest,
+Cache::Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@@ -431,7 +435,8 @@ Cache::Delete(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor);
- RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
+ RefPtr<InternalRequest> ir =
+ ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@@ -450,7 +455,7 @@ Cache::Delete(const RequestOrUSVString& aRequest,
}
already_AddRefed<Promise>
-Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
+Cache::Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@@ -466,8 +471,8 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1);
if (aRequest.WasPassed()) {
- RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
- IgnoreBody, aRv);
+ RefPtr<InternalRequest> ir =
+ ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@@ -627,7 +632,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
}
already_AddRefed<Promise>
-Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
+Cache::PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv)
{
@@ -644,7 +649,8 @@ Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
RefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest();
- args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv);
+ args.Add(aCx, ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i],
+ aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
diff --git a/dom/cache/Cache.h b/dom/cache/Cache.h
index 04f891dca5..6fea3d36a0 100644
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -43,10 +43,10 @@ public:
// webidl interface methods
already_AddRefed<Promise>
- Match(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
- ErrorResult& aRv);
+ Match(JSContext* aCx, const RequestOrUSVString& aRequest,
+ const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
- MatchAll(const Optional<RequestOrUSVString>& aRequest,
+ MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
Add(JSContext* aContext, const RequestOrUSVString& aRequest,
@@ -55,13 +55,13 @@ public:
AddAll(JSContext* aContext,
const Sequence<OwningRequestOrUSVString>& aRequests, ErrorResult& aRv);
already_AddRefed<Promise>
- Put(const RequestOrUSVString& aRequest, Response& aResponse,
+ Put(JSContext* aCx, const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv);
already_AddRefed<Promise>
- Delete(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
- ErrorResult& aRv);
+ Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
+ const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
- Keys(const Optional<RequestOrUSVString>& aRequest,
+ Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aParams, ErrorResult& aRv);
// binding methods
@@ -100,7 +100,7 @@ private:
ErrorResult& aRv);
already_AddRefed<Promise>
- PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
+ PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv);
diff --git a/dom/cache/CacheOpChild.cpp b/dom/cache/CacheOpChild.cpp
index 492c205ffc..9dc8997c26 100644
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -74,7 +74,11 @@ CacheOpChild::CacheOpChild(CacheWorkerHolder* aWorkerHolder,
MOZ_DIAGNOSTIC_ASSERT(mPromise);
MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
- SetWorkerHolder(aWorkerHolder);
+ RefPtr<CacheWorkerHolder> workerHolder =
+ CacheWorkerHolder::PreferBehavior(aWorkerHolder,
+ CacheWorkerHolder::PreventIdleShutdownStart);
+
+ SetWorkerHolder(workerHolder);
}
CacheOpChild::~CacheOpChild()
@@ -165,7 +169,11 @@ CacheOpChild::Recv__delete__(const ErrorResult& aRv,
break;
}
- actor->SetWorkerHolder(GetWorkerHolder());
+ RefPtr<CacheWorkerHolder> workerHolder =
+ CacheWorkerHolder::PreferBehavior(GetWorkerHolder(),
+ CacheWorkerHolder::AllowIdleShutdownStart);
+
+ actor->SetWorkerHolder(workerHolder);
RefPtr<Cache> cache = new Cache(mGlobal, actor);
mPromise->MaybeResolve(cache);
break;
diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp
index 2dae0b157d..05df8ddc69 100644
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -204,7 +204,8 @@ CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
}
RefPtr<CacheWorkerHolder> workerHolder =
- CacheWorkerHolder::Create(aWorkerPrivate);
+ CacheWorkerHolder::Create(aWorkerPrivate,
+ CacheWorkerHolder::AllowIdleShutdownStart);
if (!workerHolder) {
NS_WARNING("Worker thread is shutting down.");
aRv.Throw(NS_ERROR_FAILURE);
@@ -315,7 +316,7 @@ CacheStorage::CacheStorage(nsresult aFailureResult)
}
already_AddRefed<Promise>
-CacheStorage::Match(const RequestOrUSVString& aRequest,
+CacheStorage::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
@@ -325,8 +326,8 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
return nullptr;
}
- RefPtr<InternalRequest> request = ToInternalRequest(aRequest, IgnoreBody,
- aRv);
+ RefPtr<InternalRequest> request =
+ ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
diff --git a/dom/cache/CacheStorage.h b/dom/cache/CacheStorage.h
index 04a2fa0dd9..c1cef4b428 100644
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -59,9 +59,9 @@ public:
DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
// webidl interface methods
- already_AddRefed<Promise> Match(const RequestOrUSVString& aRequest,
- const CacheQueryOptions& aOptions,
- ErrorResult& aRv);
+ already_AddRefed<Promise>
+ Match(JSContext* aCx, const RequestOrUSVString& aRequest,
+ const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> Has(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Open(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Delete(const nsAString& aKey, ErrorResult& aRv);
diff --git a/dom/cache/CacheWorkerHolder.cpp b/dom/cache/CacheWorkerHolder.cpp
index 4ac97cbcab..1fd78553ee 100644
--- a/dom/cache/CacheWorkerHolder.cpp
+++ b/dom/cache/CacheWorkerHolder.cpp
@@ -18,11 +18,11 @@ using mozilla::dom::workers::WorkerPrivate;
// static
already_AddRefed<CacheWorkerHolder>
-CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate)
+CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate, Behavior aBehavior)
{
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
- RefPtr<CacheWorkerHolder> workerHolder = new CacheWorkerHolder();
+ RefPtr<CacheWorkerHolder> workerHolder = new CacheWorkerHolder(aBehavior);
if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate, Terminating))) {
return nullptr;
}
@@ -30,6 +30,28 @@ CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate)
return workerHolder.forget();
}
+// static
+already_AddRefed<CacheWorkerHolder>
+CacheWorkerHolder::PreferBehavior(CacheWorkerHolder* aCurrentHolder,
+ Behavior aBehavior)
+{
+ if (!aCurrentHolder) {
+ return nullptr;
+ }
+
+ RefPtr<CacheWorkerHolder> orig = aCurrentHolder;
+ if (orig->GetBehavior() == aBehavior) {
+ return orig.forget();
+ }
+
+ RefPtr<CacheWorkerHolder> replace = Create(orig->mWorkerPrivate, aBehavior);
+ if (!replace) {
+ return orig.forget();
+ }
+
+ return replace.forget();
+}
+
void
CacheWorkerHolder::AddActor(ActorChild* aActor)
{
@@ -88,8 +110,9 @@ CacheWorkerHolder::Notify(Status aStatus)
return true;
}
-CacheWorkerHolder::CacheWorkerHolder()
- : mNotified(false)
+CacheWorkerHolder::CacheWorkerHolder(Behavior aBehavior)
+ : WorkerHolder(aBehavior)
+ , mNotified(false)
{
}
diff --git a/dom/cache/CacheWorkerHolder.h b/dom/cache/CacheWorkerHolder.h
index 9eed7e2b6a..7e4c55f839 100644
--- a/dom/cache/CacheWorkerHolder.h
+++ b/dom/cache/CacheWorkerHolder.h
@@ -25,7 +25,11 @@ class CacheWorkerHolder final : public workers::WorkerHolder
{
public:
static already_AddRefed<CacheWorkerHolder>
- Create(workers::WorkerPrivate* aWorkerPrivate);
+ Create(workers::WorkerPrivate* aWorkerPrivate,
+ Behavior aBehavior);
+
+ static already_AddRefed<CacheWorkerHolder>
+ PreferBehavior(CacheWorkerHolder* aCurrentHolder, Behavior aBehavior);
void AddActor(ActorChild* aActor);
void RemoveActor(ActorChild* aActor);
@@ -36,7 +40,7 @@ public:
virtual bool Notify(workers::Status aStatus) override;
private:
- CacheWorkerHolder();
+ explicit CacheWorkerHolder(Behavior aBehavior);
~CacheWorkerHolder();
nsTArray<ActorChild*> mActorList;
diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp
index f849f18874..1af5ee9458 100644
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -79,7 +79,7 @@ ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
} // namespace
already_AddRefed<InternalRequest>
-TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
+TypeUtils::ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv)
{
if (aIn.IsRequest()) {
@@ -87,7 +87,7 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
- CheckAndSetBodyUsed(&request, aBodyAction, aRv);
+ CheckAndSetBodyUsed(aCx, &request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request.GetInternalRequest();
@@ -97,7 +97,8 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
}
already_AddRefed<InternalRequest>
-TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
+TypeUtils::ToInternalRequest(JSContext* aCx,
+ const OwningRequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv)
{
@@ -106,7 +107,7 @@ TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
- CheckAndSetBodyUsed(request, aBodyAction, aRv);
+ CheckAndSetBodyUsed(aCx, request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request->GetInternalRequest();
@@ -204,7 +205,7 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
}
void
-TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
+TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv)
{
@@ -222,7 +223,10 @@ TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
nsCOMPtr<nsIInputStream> stream;
ir->GetUnfilteredBody(getter_AddRefs(stream));
if (stream) {
- aIn.SetBodyUsed();
+ aIn.SetBodyUsed(aCx, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
}
SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
@@ -426,8 +430,8 @@ TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
}
void
-TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
- ErrorResult& aRv)
+TypeUtils::CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest,
+ BodyAction aBodyAction, ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(aRequest);
@@ -443,7 +447,10 @@ TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
nsCOMPtr<nsIInputStream> stream;
aRequest->GetBody(getter_AddRefs(stream));
if (stream) {
- aRequest->SetBodyUsed();
+ aRequest->SetBodyUsed(aCx, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
}
}
diff --git a/dom/cache/TypeUtils.h b/dom/cache/TypeUtils.h
index 274586e3f7..ff5816db3e 100644
--- a/dom/cache/TypeUtils.h
+++ b/dom/cache/TypeUtils.h
@@ -73,12 +73,12 @@ public:
GetIPCManager() = 0;
already_AddRefed<InternalRequest>
- ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction,
- ErrorResult& aRv);
+ ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
+ BodyAction aBodyAction, ErrorResult& aRv);
already_AddRefed<InternalRequest>
- ToInternalRequest(const OwningRequestOrUSVString& aIn, BodyAction aBodyAction,
- ErrorResult& aRv);
+ ToInternalRequest(JSContext* aCx, const OwningRequestOrUSVString& aIn,
+ BodyAction aBodyAction, ErrorResult& aRv);
void
ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
@@ -91,7 +91,7 @@ public:
ErrorResult& aRv);
void
- ToCacheResponse(CacheResponse& aOut, Response& aIn,
+ ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv);
@@ -133,7 +133,7 @@ public:
private:
void
- CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
+ CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv);
already_AddRefed<InternalRequest>
diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp
index ed263f75d2..1e7eab82b2 100644
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2974,8 +2974,8 @@ ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZe
// The values of canvas API input are in double precision, but Moz2D APIs are
// using float precision. Bypass canvas API calls when the input is out of
// float precision to avoid precision problem
- if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
- !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
+ if (!std::isfinite((float)aX) || !std::isfinite((float)aY) ||
+ !std::isfinite((float)aWidth) || !std::isfinite((float)aHeight)) {
return false;
}
@@ -5959,7 +5959,7 @@ void
CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDy, ErrorResult& aError)
{
- RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
+ RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
@@ -5975,7 +5975,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDirtyHeight,
ErrorResult& aError)
{
- RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
+ RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp
index 042c0fa11a..7aaf4e74d8 100644
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -929,7 +929,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
{
// Copy data into SourceSurface.
- RootedTypedArray<Uint8ClampedArray> array(RootingCx());
+ RootedSpiderMonkeyInterface<Uint8ClampedArray> array(RootingCx());
DebugOnly<bool> inited = array.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp
index ed199cfb43..661fb91ce2 100644
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -475,7 +475,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
GLsizei depth, GLint border, const webgl::PackingInfo& pi,
const TexImageSource& src)
{
- dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
+ dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr);
if (!blob)
@@ -491,7 +491,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
const webgl::PackingInfo& pi, const TexImageSource& src)
{
const GLint border = 0;
- dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
+ dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr);
if (!blob)
diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp
index ed47325f8d..34a4c877d7 100644
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -1366,7 +1366,7 @@ public:
mDataIsJwk = false;
// Try ArrayBuffer
- RootedTypedArray<ArrayBuffer> ab(aCx);
+ RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
if (ab.Init(aKeyData)) {
if (!mKeyData.Assign(ab)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
@@ -1375,7 +1375,7 @@ public:
}
// Try ArrayBufferView
- RootedTypedArray<ArrayBufferView> abv(aCx);
+ RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
if (abv.Init(aKeyData)) {
if (!mKeyData.Assign(abv)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
diff --git a/dom/fetch/BodyExtractor.cpp b/dom/fetch/BodyExtractor.cpp
new file mode 100644
index 0000000000..b840641214
--- /dev/null
+++ b/dom/fetch/BodyExtractor.cpp
@@ -0,0 +1,207 @@
+/* -*- 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/. */
+
+#include "BodyExtractor.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FormData.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/dom/XMLHttpRequest.h"
+#include "nsContentUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMSerializer.h"
+#include "nsIGlobalObject.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIStorageStream.h"
+#include "nsStringStream.h"
+#include "nsIUnicodeEncoder.h"
+
+namespace mozilla {
+namespace dom {
+
+static nsresult
+GetBufferDataAsStream(const uint8_t* aData, uint32_t aDataLength,
+ nsIInputStream** aResult, uint64_t* aContentLength,
+ nsACString& aContentType, nsACString& aCharset)
+{
+ aContentType.SetIsVoid(true);
+ aCharset.Truncate();
+
+ *aContentLength = aDataLength;
+ const char* data = reinterpret_cast<const char*>(aData);
+
+ nsCOMPtr<nsIInputStream> stream;
+ nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
+ NS_ASSIGNMENT_COPY);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ stream.forget(aResult);
+
+ return NS_OK;
+}
+
+template<> nsresult
+BodyExtractor<const ArrayBuffer>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ mBody->ComputeLengthAndData();
+ return GetBufferDataAsStream(mBody->Data(), mBody->Length(),
+ aResult, aContentLength, aContentTypeWithCharset,
+ aCharset);
+}
+
+template<> nsresult
+BodyExtractor<const ArrayBufferView>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ mBody->ComputeLengthAndData();
+ return GetBufferDataAsStream(mBody->Data(), mBody->Length(),
+ aResult, aContentLength, aContentTypeWithCharset,
+ aCharset);
+}
+
+template<> nsresult
+BodyExtractor<nsIDocument>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(mBody));
+ NS_ENSURE_STATE(domdoc);
+ aCharset.AssignLiteral("UTF-8");
+
+ nsresult rv;
+ nsCOMPtr<nsIStorageStream> storStream;
+ rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> output;
+ rv = storStream->GetOutputStream(0, getter_AddRefs(output));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mBody->IsHTMLDocument()) {
+ aContentTypeWithCharset.AssignLiteral("text/html;charset=UTF-8");
+
+ nsString serialized;
+ if (!nsContentUtils::SerializeNodeToMarkup(mBody, true, serialized)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsAutoCString utf8Serialized;
+ if (!AppendUTF16toUTF8(serialized, utf8Serialized, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t written;
+ rv = output->Write(utf8Serialized.get(), utf8Serialized.Length(), &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MOZ_ASSERT(written == utf8Serialized.Length());
+ } else {
+ aContentTypeWithCharset.AssignLiteral("application/xml;charset=UTF-8");
+
+ nsCOMPtr<nsIDOMSerializer> serializer =
+ do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make sure to use the encoding we'll send
+ rv = serializer->SerializeToStream(domdoc, output, aCharset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ output->Close();
+
+ uint32_t length;
+ rv = storStream->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aContentLength = length;
+
+ rv = storStream->NewInputStream(0, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+template<> nsresult
+BodyExtractor<const nsAString>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ nsCOMPtr<nsIUnicodeEncoder> encoder =
+ EncodingUtils::EncoderForEncoding("UTF-8");
+ if (!encoder) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t destBufferLen;
+ nsresult rv = encoder->GetMaxLength(mBody->BeginReading(), mBody->Length(),
+ &destBufferLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString encoded;
+ if (!encoded.SetCapacity(destBufferLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char* destBuffer = encoded.BeginWriting();
+ int32_t srcLen = (int32_t) mBody->Length();
+ int32_t outLen = destBufferLen;
+ rv = encoder->Convert(mBody->BeginReading(), &srcLen, destBuffer, &outLen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(outLen <= destBufferLen);
+ encoded.SetLength(outLen);
+
+ rv = NS_NewCStringInputStream(aResult, encoded);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ *aContentLength = outLen;
+ aContentTypeWithCharset.AssignLiteral("text/plain;charset=UTF-8");
+ aCharset.AssignLiteral("UTF-8");
+ return NS_OK;
+}
+
+template<> nsresult
+BodyExtractor<nsIInputStream>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ aContentTypeWithCharset.AssignLiteral("text/plain");
+ aCharset.Truncate();
+
+ nsresult rv = mBody->Available(aContentLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> stream(mBody);
+ stream.forget(aResult);
+ return NS_OK;
+}
+
+template<> nsresult
+BodyExtractor<nsIXHRSendable>::GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const
+{
+ return mBody->GetSendInfo(aResult, aContentLength, aContentTypeWithCharset,
+ aCharset);
+}
+
+} // dom namespace
+} // mozilla namespace
diff --git a/dom/fetch/BodyExtractor.h b/dom/fetch/BodyExtractor.h
new file mode 100644
index 0000000000..73dfef6aff
--- /dev/null
+++ b/dom/fetch/BodyExtractor.h
@@ -0,0 +1,48 @@
+/* -*- 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_BodyExtractor_h
+#define mozilla_dom_BodyExtractor_h
+
+#include "jsapi.h"
+#include "nsString.h"
+
+class nsIInputStream;
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class BodyExtractorBase
+{
+public:
+ virtual nsresult GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const = 0;
+};
+
+// The implementation versions of this template are:
+// ArrayBuffer, ArrayBufferView, nsIXHRSendable (Blob, FormData,
+// URLSearchParams), nsAString, nsIDocument, nsIInputStream.
+template<typename Type>
+class BodyExtractor final : public BodyExtractorBase
+{
+ Type* mBody;
+public:
+ explicit BodyExtractor(Type* aBody) : mBody(aBody)
+ {}
+
+ nsresult GetAsStream(nsIInputStream** aResult,
+ uint64_t* aContentLength,
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset) const override;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_BodyExtractor_h
diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp
index 6a6b4faaf8..a9d8514765 100644
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -5,13 +5,13 @@
#include "Fetch.h"
#include "FetchConsumer.h"
+#include "FetchStream.h"
#include "nsIDocument.h"
#include "nsIGlobalObject.h"
#include "nsIStreamLoader.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
#include "nsDOMString.h"
#include "nsNetUtil.h"
@@ -22,7 +22,7 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BodyUtil.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FetchDriver.h"
#include "mozilla/dom/File.h"
@@ -35,8 +35,10 @@
#include "mozilla/dom/Response.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "BodyExtractor.h"
#include "FetchObserver.h"
#include "InternalRequest.h"
#include "InternalResponse.h"
@@ -722,154 +724,95 @@ WorkerFetchResolver::FlushConsoleReport()
mReporter->FlushConsoleReports(worker->GetDocument());
}
-namespace {
-
-nsresult
-ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
- nsIInputStream** aStream,
- uint64_t& aContentLength)
-{
- aBuffer.ComputeLengthAndData();
- aContentLength = aBuffer.Length();
- //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
- return NS_NewByteInputStream(aStream,
- reinterpret_cast<char*>(aBuffer.Data()),
- aBuffer.Length(), NS_ASSIGNMENT_COPY);
-}
-
nsresult
-ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
- nsIInputStream** aStream,
- uint64_t& aContentLength)
+ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentTypeWithCharset,
+ uint64_t& aContentLength)
{
- aBuffer.ComputeLengthAndData();
- aContentLength = aBuffer.Length();
- //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
- return NS_NewByteInputStream(aStream,
- reinterpret_cast<char*>(aBuffer.Data()),
- aBuffer.Length(), NS_ASSIGNMENT_COPY);
-}
+ MOZ_ASSERT(aStream);
+ nsAutoCString charset;
+ aContentTypeWithCharset.SetIsVoid(true);
-nsresult
-ExtractFromBlob(const Blob& aBlob,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength)
-{
- RefPtr<BlobImpl> impl = aBlob.Impl();
- ErrorResult rv;
- aContentLength = impl->GetSize(rv);
- if (NS_WARN_IF(rv.Failed())) {
- return rv.StealNSResult();
+ if (aBodyInit.IsArrayBuffer()) {
+ BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
-
- impl->GetInternalStream(aStream, rv);
- if (NS_WARN_IF(rv.Failed())) {
- return rv.StealNSResult();
+ if (aBodyInit.IsArrayBufferView()) {
+ BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
-
- nsAutoString type;
- impl->GetType(type);
- aContentType = NS_ConvertUTF16toUTF8(type);
- return NS_OK;
-}
-
-nsresult
-ExtractFromFormData(FormData& aFormData,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength)
-{
- nsAutoCString unusedCharset;
- return aFormData.GetSendInfo(aStream, &aContentLength,
- aContentType, unusedCharset);
-}
-
-nsresult
-ExtractFromUSVString(const nsString& aStr,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength)
-{
- nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
- if (!encoder) {
- return NS_ERROR_OUT_OF_MEMORY;
+ if (aBodyInit.IsBlob()) {
+ Blob& blob = aBodyInit.GetAsBlob();
+ BodyExtractor<nsIXHRSendable> body(&blob);
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
-
- int32_t destBufferLen;
- nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ if (aBodyInit.IsFormData()) {
+ FormData& formData = aBodyInit.GetAsFormData();
+ BodyExtractor<nsIXHRSendable> body(&formData);
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
-
- nsCString encoded;
- if (!encoded.SetCapacity(destBufferLen, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
+ if (aBodyInit.IsUSVString()) {
+ BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
-
- char* destBuffer = encoded.BeginWriting();
- int32_t srcLen = (int32_t) aStr.Length();
- int32_t outLen = destBufferLen;
- rv = encoder->Convert(aStr.get(), &srcLen, destBuffer, &outLen);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ if (aBodyInit.IsURLSearchParams()) {
+ URLSearchParams& usp = aBodyInit.GetAsURLSearchParams();
+ BodyExtractor<nsIXHRSendable> body(&usp);
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
- MOZ_ASSERT(outLen <= destBufferLen);
- encoded.SetLength(outLen);
-
- aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
- aContentLength = outLen;
-
- return NS_NewCStringInputStream(aStream, encoded);
-}
-
-nsresult
-ExtractFromURLSearchParams(const URLSearchParams& aParams,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength)
-{
- nsAutoString serialized;
- aParams.Stringify(serialized);
- aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
- aContentLength = serialized.Length();
- return NS_NewCStringInputStream(aStream, NS_ConvertUTF16toUTF8(serialized));
+ NS_NOTREACHED("Should never reach here");
+ return NS_ERROR_FAILURE;
}
-} // namespace
nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
nsIInputStream** aStream,
- nsCString& aContentType,
+ nsCString& aContentTypeWithCharset,
uint64_t& aContentLength)
{
MOZ_ASSERT(aStream);
+ MOZ_ASSERT(!*aStream);
+
+ nsAutoCString charset;
+ aContentTypeWithCharset.SetIsVoid(true);
if (aBodyInit.IsArrayBuffer()) {
- const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
- return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+ BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
if (aBodyInit.IsArrayBufferView()) {
- const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
- return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+ BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
if (aBodyInit.IsBlob()) {
- const Blob& blob = aBodyInit.GetAsBlob();
- return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsBlob());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
if (aBodyInit.IsFormData()) {
- FormData& form = aBodyInit.GetAsFormData();
- return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsFormData());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
if (aBodyInit.IsUSVString()) {
- nsAutoString str;
- str.Assign(aBodyInit.GetAsUSVString());
- return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+ BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
if (aBodyInit.IsURLSearchParams()) {
- URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
- return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsURLSearchParams());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
NS_NOTREACHED("Should never reach here");
@@ -877,69 +820,181 @@ ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDa
}
nsresult
-ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
nsIInputStream** aStream,
- nsCString& aContentType,
+ nsCString& aContentTypeWithCharset,
uint64_t& aContentLength)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(!*aStream);
+ // ReadableStreams should be handled by
+ // BodyExtractorReadableStream::GetAsStream.
+ MOZ_ASSERT(!aBodyInit.IsReadableStream());
+
+ nsAutoCString charset;
+ aContentTypeWithCharset.SetIsVoid(true);
+
if (aBodyInit.IsArrayBuffer()) {
- const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
- return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+ BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
+
if (aBodyInit.IsArrayBufferView()) {
- const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
- return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+ BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
+
if (aBodyInit.IsBlob()) {
- const Blob& blob = aBodyInit.GetAsBlob();
- return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsBlob());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
+
if (aBodyInit.IsFormData()) {
- FormData& form = aBodyInit.GetAsFormData();
- return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsFormData());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
+
if (aBodyInit.IsUSVString()) {
- nsAutoString str;
- str.Assign(aBodyInit.GetAsUSVString());
- return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+ BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
+
if (aBodyInit.IsURLSearchParams()) {
- URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
- return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
+ BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsURLSearchParams());
+ return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
+ charset);
}
NS_NOTREACHED("Should never reach here");
return NS_ERROR_FAILURE;
}
+
template <class Derived>
-FetchBody<Derived>::FetchBody()
+FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
: mWorkerPrivate(nullptr)
+ , mOwner(aOwner)
+ , mReadableStreamBody(nullptr)
+ , mReadableStreamReader(nullptr)
, mBodyUsed(false)
{
+ MOZ_ASSERT(aOwner);
+
if (!NS_IsMainThread()) {
mWorkerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(mWorkerPrivate);
+ } else {
+ mWorkerPrivate = nullptr;
}
}
template
-FetchBody<Request>::FetchBody();
+FetchBody<Request>::FetchBody(nsIGlobalObject* aOwner);
template
-FetchBody<Response>::FetchBody();
+FetchBody<Response>::FetchBody(nsIGlobalObject* aOwner);
template <class Derived>
FetchBody<Derived>::~FetchBody()
{
}
+template
+FetchBody<Request>::~FetchBody();
+
+template
+FetchBody<Response>::~FetchBody();
+
+template <class Derived>
+bool
+FetchBody<Derived>::BodyUsed() const
+{
+ if (mBodyUsed) {
+ return true;
+ }
+
+ // If this object is disturbed or locked, return false.
+ if (mReadableStreamBody) {
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(mOwner)) {
+ return true;
+ }
+
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
+ if (JS::ReadableStreamIsDisturbed(body) ||
+ JS::ReadableStreamIsLocked(body) ||
+ !JS::ReadableStreamIsReadable(body)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template
+bool
+FetchBody<Request>::BodyUsed() const;
+
+template
+bool
+FetchBody<Response>::BodyUsed() const;
+
+template <class Derived>
+void
+FetchBody<Derived>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv)
+{
+ MOZ_ASSERT(aCx);
+
+ if (mBodyUsed) {
+ return;
+ }
+
+ mBodyUsed = true;
+
+ // If we already have a ReadableStreamBody and it has been created by DOM, we
+ // have to lock it now because it can have been shared with other objects.
+ if (mReadableStreamBody) {
+ JS::Rooted<JSObject*> readableStreamObj(aCx, mReadableStreamBody);
+ if (JS::ReadableStreamGetMode(readableStreamObj) ==
+ JS::ReadableStreamMode::ExternalSource) {
+ LockStream(aCx, readableStreamObj, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+ } else {
+ // If this is not a native ReadableStream, let's activate the
+ // FetchStreamReader.
+ MOZ_ASSERT(mFetchStreamReader);
+ JS::Rooted<JSObject*> reader(aCx);
+ mFetchStreamReader->StartConsuming(aCx, readableStreamObj, &reader, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ mReadableStreamReader = reader;
+ }
+ }
+}
+
+template
+void
+FetchBody<Request>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
+
template <class Derived>
already_AddRefed<Promise>
-FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
+FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv)
{
RefPtr<AbortSignal> signal = DerivedClass()->GetSignal();
if (signal && signal->Aborted()) {
@@ -952,11 +1007,15 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
return nullptr;
}
- SetBodyUsed();
+ SetBodyUsed(aCx, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
RefPtr<Promise> promise =
- FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(),
- this, signal, aType, aRv);
+ FetchBodyConsumer<Derived>::Create(global, this, signal, aType, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@@ -966,11 +1025,11 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
template
already_AddRefed<Promise>
-FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
+FetchBody<Request>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template
already_AddRefed<Promise>
-FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
+FetchBody<Response>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template <class Derived>
void
@@ -1000,5 +1059,170 @@ template
void
FetchBody<Response>::SetMimeType();
+template <class Derived>
+void
+FetchBody<Derived>::SetReadableStreamBody(JSObject* aBody)
+{
+ MOZ_ASSERT(!mReadableStreamBody);
+ MOZ_ASSERT(aBody);
+ mReadableStreamBody = aBody;
+}
+
+template
+void
+FetchBody<Request>::SetReadableStreamBody(JSObject* aBody);
+
+template
+void
+FetchBody<Response>::SetReadableStreamBody(JSObject* aBody);
+
+template <class Derived>
+void
+FetchBody<Derived>::GetBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aBodyOut,
+ ErrorResult& aRv)
+{
+ if (mReadableStreamBody) {
+ aBodyOut.set(mReadableStreamBody);
+ return;
+ }
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ DerivedClass()->GetBody(getter_AddRefs(inputStream));
+
+ if (!inputStream) {
+ aBodyOut.set(nullptr);
+ return;
+ }
+
+ JS::Rooted<JSObject*> body(aCx);
+ FetchStream::Create(aCx, this, DerivedClass()->GetParentObject(),
+ inputStream, &body, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(body);
+
+ // If the body has been already consumed, we lock the stream.
+ if (BodyUsed()) {
+ LockStream(aCx, body, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+ }
+
+ mReadableStreamBody = body;
+ aBodyOut.set(mReadableStreamBody);
+}
+
+template
+void
+FetchBody<Request>::GetBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aMessage,
+ ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::GetBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aMessage,
+ ErrorResult& aRv);
+
+template <class Derived>
+void
+FetchBody<Derived>::LockStream(JSContext* aCx,
+ JS::HandleObject aStream,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(JS::ReadableStreamGetMode(aStream) ==
+ JS::ReadableStreamMode::ExternalSource);
+
+ // This is native stream, creating a reader will not execute any JS code.
+ JS::Rooted<JSObject*> reader(aCx,
+ JS::ReadableStreamGetReader(aCx, aStream,
+ JS::ReadableStreamReaderMode::Default));
+ if (!reader) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ mReadableStreamReader = reader;
+}
+
+template
+void
+FetchBody<Request>::LockStream(JSContext* aCx,
+ JS::HandleObject aStream,
+ ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::LockStream(JSContext* aCx,
+ JS::HandleObject aStream,
+ ErrorResult& aRv);
+
+template <class Derived>
+void
+FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aBodyOut,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
+ ErrorResult& aRv)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aStreamReader);
+ MOZ_DIAGNOSTIC_ASSERT(aInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
+
+ aBodyOut.set(nullptr);
+ *aStreamReader = nullptr;
+ *aInputStream = nullptr;
+
+ if (!mReadableStreamBody) {
+ return;
+ }
+
+ JS::Rooted<JSObject*> stream(aCx, mReadableStreamBody);
+
+ // If this is a ReadableStream with an external source, this has been
+ // generated by a Fetch. In this case, Fetch will be able to recreate it
+ // again when GetBody() is called.
+ if (JS::ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource) {
+ aBodyOut.set(nullptr);
+ return;
+ }
+
+ JS::Rooted<JSObject*> branch1(aCx);
+ JS::Rooted<JSObject*> branch2(aCx);
+
+ if (!JS::ReadableStreamTee(aCx, stream, &branch1, &branch2)) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ mReadableStreamBody = branch1;
+ aBodyOut.set(branch2);
+
+ aRv = FetchStreamReader::Create(aCx, mOwner, aStreamReader, aInputStream);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+}
+
+template
+void
+FetchBody<Request>::MaybeTeeReadableStreamBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aMessage,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
+ ErrorResult& aRv);
+
+template
+void
+FetchBody<Response>::MaybeTeeReadableStreamBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aMessage,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
+ ErrorResult& aRv);
+
} // namespace dom
} // namespace mozilla
diff --git a/dom/fetch/Fetch.h b/dom/fetch/Fetch.h
index 73d5c15199..19b579cb6c 100644
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -1,207 +1,312 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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_Fetch_h
-#define mozilla_dom_Fetch_h
-
-#include "nsAutoPtr.h"
-#include "nsIStreamLoader.h"
-
-#include "nsCOMPtr.h"
-#include "nsError.h"
-#include "nsProxyRelease.h"
-#include "nsString.h"
-
-#include "mozilla/DebugOnly.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/Promise.h"
-#include "mozilla/dom/RequestBinding.h"
-
-class nsIGlobalObject;
-
-namespace mozilla {
-namespace dom {
-
-class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
-class BlobImpl;
-class InternalRequest;
-class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
-class RequestOrUSVString;
-
-namespace workers {
-class WorkerPrivate;
-} // namespace workers
-
-already_AddRefed<Promise>
-FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
- const RequestInit& aInit, ErrorResult& aRv);
-
-nsresult
-UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
-
-/*
- * Creates an nsIInputStream based on the fetch specifications 'extract a byte
- * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
- * Stores content type in out param aContentType.
- */
-nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength);
-
-/*
- * Non-owning version.
- */
-nsresult
-ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
- nsIInputStream** aStream,
- nsCString& aContentType,
- uint64_t& aContentLength);
-
-template <class Derived> class FetchBodyConsumer;
-
-enum FetchConsumeType
-{
- CONSUME_ARRAYBUFFER,
- CONSUME_BLOB,
- CONSUME_FORMDATA,
- CONSUME_JSON,
- CONSUME_TEXT,
-};
-
-/*
- * FetchBody's body consumption uses nsIInputStreamPump to read from the
- * underlying stream to a block of memory, which is then adopted by
- * ContinueConsumeBody() and converted to the right type based on the JS
- * function called.
- *
- * Use of the nsIInputStreamPump complicates things on the worker thread.
- * The solution used here is similar to WebSockets.
- * The difference is that we are only interested in completion and not data
- * events, and nsIInputStreamPump can only deliver completion on the main thread.
- *
- * Before starting the pump on the main thread, we addref the FetchBody to keep
- * it alive. Then we add a feature, to track the status of the worker.
- *
- * ContinueConsumeBody() is the function that cleans things up in both success
- * and error conditions and so all callers call it with the appropriate status.
- *
- * Once the read is initiated on the main thread there are two possibilities.
- *
- * 1) Pump finishes before worker has finished Running.
- * In this case we adopt the data and dispatch a runnable to the worker,
- * which derefs FetchBody and removes the feature and resolves the Promise.
- *
- * 2) Pump still working while worker has stopped Running.
- * The feature is Notify()ed and ContinueConsumeBody() is called with
- * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
- * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
- * held by it) until pump->Cancel() is called. OnStreamComplete() will not
- * do anything if the error code is NS_BINDING_ABORTED, so we don't have to
- * worry about keeping anything alive.
- *
- * The pump is always released on the main thread.
- */
-template <class Derived>
-class FetchBody
-{
-public:
- friend class FetchBodyConsumer<Derived>;
-
- NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
- NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
-
- bool
- BodyUsed() const { return mBodyUsed; }
-
- already_AddRefed<Promise>
- ArrayBuffer(ErrorResult& aRv)
- {
- return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
- }
-
- already_AddRefed<Promise>
- Blob(ErrorResult& aRv)
- {
- return ConsumeBody(CONSUME_BLOB, aRv);
- }
-
- already_AddRefed<Promise>
- FormData(ErrorResult& aRv)
- {
- return ConsumeBody(CONSUME_FORMDATA, aRv);
- }
-
- already_AddRefed<Promise>
- Json(ErrorResult& aRv)
- {
- return ConsumeBody(CONSUME_JSON, aRv);
- }
-
- already_AddRefed<Promise>
- Text(ErrorResult& aRv)
- {
- return ConsumeBody(CONSUME_TEXT, aRv);
- }
-
- // Utility public methods accessed by various runnables.
-
- void
- SetBodyUsed()
- {
- mBodyUsed = true;
- }
-
- const nsCString&
- MimeType() const
- {
- return mMimeType;
- }
-
- virtual AbortSignal*
- GetSignal() const = 0;
-
-protected:
- FetchBody();
-
- // Always set whenever the FetchBody is created on the worker thread.
- workers::WorkerPrivate* mWorkerPrivate;
-
- virtual ~FetchBody();
-
- void
- SetMimeType();
-private:
- Derived*
- DerivedClass() const
- {
- return static_cast<Derived*>(const_cast<FetchBody*>(this));
- }
-
- already_AddRefed<Promise>
- ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
-
- bool
- IsOnTargetThread()
- {
- return NS_IsMainThread() == !mWorkerPrivate;
- }
-
- void
- AssertIsOnTargetThread()
- {
- MOZ_ASSERT(IsOnTargetThread());
- }
-
- // Only ever set once, always on target thread.
- bool mBodyUsed;
- nsCString mMimeType;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_Fetch_h
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_Fetch_h
+#define mozilla_dom_Fetch_h
+
+#include "nsAutoPtr.h"
+#include "nsIStreamLoader.h"
+
+#include "nsCOMPtr.h"
+#include "nsError.h"
+#include "nsProxyRelease.h"
+#include "nsString.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/FetchStreamReader.h"
+// Fix X11 header brain damage that conflicts with HeadersGuardEnum::None
+#undef None
+#include "mozilla/dom/RequestBinding.h"
+
+class nsIGlobalObject;
+class nsIEventTarget;
+
+namespace mozilla {
+namespace dom {
+
+class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
+class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString;
+class BlobImpl;
+class InternalRequest;
+class OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
+struct ReadableStream;
+class RequestOrUSVString;
+
+namespace workers {
+class WorkerPrivate;
+} // namespace workers
+
+already_AddRefed<Promise>
+FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
+ const RequestInit& aInit, ErrorResult& aRv);
+
+nsresult
+UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
+
+/* Deal with unwieldy long webIDL-generated type names */
+namespace fetch {
+ typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString BodyInit;
+ typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString ResponseBodyInit;
+ typedef OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString OwningBodyInit;
+};
+
+/*
+ * Creates an nsIInputStream based on the fetch specifications 'extract a byte
+ * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
+ * Stores content type in out param aContentType.
+ */
+nsresult
+ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength);
+
+/*
+ * Non-owning version.
+ */
+nsresult
+ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength);
+
+/*
+ * Non-owning version. This method should go away when BodyInit will contain
+ * ReadableStream.
+ */
+nsresult
+ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
+ nsIInputStream** aStream,
+ nsCString& aContentType,
+ uint64_t& aContentLength);
+
+template <class Derived> class FetchBodyConsumer;
+
+enum FetchConsumeType
+{
+ CONSUME_ARRAYBUFFER,
+ CONSUME_BLOB,
+ CONSUME_FORMDATA,
+ CONSUME_JSON,
+ CONSUME_TEXT,
+};
+
+class FetchStreamHolder
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ virtual void NullifyStream() = 0;
+
+ virtual void MarkAsRead() = 0;
+
+ virtual JSObject* ReadableStreamBody() = 0;
+};
+
+/*
+ * FetchBody's body consumption uses nsIInputStreamPump to read from the
+ * underlying stream to a block of memory, which is then adopted by
+ * ContinueConsumeBody() and converted to the right type based on the JS
+ * function called.
+ *
+ * Use of the nsIInputStreamPump complicates things on the worker thread.
+ * The solution used here is similar to WebSockets.
+ * The difference is that we are only interested in completion and not data
+ * events, and nsIInputStreamPump can only deliver completion on the main thread.
+ *
+ * Before starting the pump on the main thread, we addref the FetchBody to keep
+ * it alive. Then we add a feature, to track the status of the worker.
+ *
+ * ContinueConsumeBody() is the function that cleans things up in both success
+ * and error conditions and so all callers call it with the appropriate status.
+ *
+ * Once the read is initiated on the main thread there are two possibilities.
+ *
+ * 1) Pump finishes before worker has finished Running.
+ * In this case we adopt the data and dispatch a runnable to the worker,
+ * which derefs FetchBody and removes the feature and resolves the Promise.
+ *
+ * 2) Pump still working while worker has stopped Running.
+ * The feature is Notify()ed and ContinueConsumeBody() is called with
+ * NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
+ * ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
+ * held by it) until pump->Cancel() is called. OnStreamComplete() will not
+ * do anything if the error code is NS_BINDING_ABORTED, so we don't have to
+ * worry about keeping anything alive.
+ *
+ * The pump is always released on the main thread.
+ */
+template <class Derived>
+class FetchBody : public FetchStreamHolder
+{
+public:
+ friend class FetchBodyConsumer<Derived>;
+
+ bool
+ BodyUsed() const;
+
+ already_AddRefed<Promise>
+ ArrayBuffer(JSContext* aCx, ErrorResult& aRv)
+ {
+ return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Blob(JSContext* aCx, ErrorResult& aRv)
+ {
+ return ConsumeBody(aCx, CONSUME_BLOB, aRv);
+ }
+
+ already_AddRefed<Promise>
+ FormData(JSContext* aCx, ErrorResult& aRv)
+ {
+ return ConsumeBody(aCx, CONSUME_FORMDATA, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Json(JSContext* aCx, ErrorResult& aRv)
+ {
+ return ConsumeBody(aCx, CONSUME_JSON, aRv);
+ }
+
+ already_AddRefed<Promise>
+ Text(JSContext* aCx, ErrorResult& aRv)
+ {
+ return ConsumeBody(aCx, CONSUME_TEXT, aRv);
+ }
+
+ void
+ GetBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aBodyOut,
+ ErrorResult& aRv);
+
+ // If the body contains a ReadableStream body object, this method produces a
+ // tee() of it.
+ void
+ MaybeTeeReadableStreamBody(JSContext* aCx,
+ JS::MutableHandle<JSObject*> aBodyOut,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream,
+ ErrorResult& aRv);
+
+ // Utility public methods accessed by various runnables.
+
+ // This method _must_ be called in order to set the body as used. If the body
+ // is a ReadableStream, this method will start reading the stream.
+ // More in details, this method does:
+ // 1) It uses an internal flag to track if the body is used. This is tracked
+ // separately from the ReadableStream disturbed state due to purely native
+ // streams.
+ // 2) If there is a ReadableStream reflector for the native stream it is
+ // Locked.
+ // 3) If there is a JS ReadableStream then we begin pumping it into the native
+ // body stream. This effectively locks and disturbs the stream.
+ //
+ // Note that JSContext is used only if there is a ReadableStream (this can
+ // happen because the body is a ReadableStream or because attribute body has
+ // already been used by content). If something goes wrong using
+ // ReadableStream, errors will be reported via ErrorResult and not as JS
+ // exceptions in JSContext. This is done in order to have a centralized error
+ // reporting way.
+ //
+ // Exceptions generated when reading from the ReadableStream are directly sent
+ // to the Console.
+ void
+ SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
+
+ const nsCString&
+ MimeType() const
+ {
+ return mMimeType;
+ }
+
+ // FetchStreamHolder
+ void
+ NullifyStream() override
+ {
+ mReadableStreamBody = nullptr;
+ mReadableStreamReader = nullptr;
+ mFetchStreamReader = nullptr;
+ }
+
+ JSObject*
+ ReadableStreamBody() override
+ {
+ MOZ_ASSERT(mReadableStreamBody);
+ return mReadableStreamBody;
+ }
+
+ void
+ MarkAsRead() override
+ {
+ mBodyUsed = true;
+ }
+
+ virtual AbortSignal*
+ GetSignal() const = 0;
+
+protected:
+ nsCOMPtr<nsIGlobalObject> mOwner;
+
+ explicit FetchBody(nsIGlobalObject* aOwner);
+
+ // Always set whenever the FetchBody is created on the worker thread.
+ workers::WorkerPrivate* mWorkerPrivate;
+
+ // This is the ReadableStream exposed to content. Its underlying source is a FetchStream object.
+ JS::Heap<JSObject*> mReadableStreamBody;
+
+ // This is the Reader used to retrieve data from the body.
+ JS::Heap<JSObject*> mReadableStreamReader;
+ RefPtr<FetchStreamReader> mFetchStreamReader;
+
+ virtual ~FetchBody();
+
+ void
+ SetMimeType();
+
+ void
+ SetReadableStreamBody(JSObject* aBody);
+
+private:
+ Derived*
+ DerivedClass() const
+ {
+ return static_cast<Derived*>(const_cast<FetchBody*>(this));
+ }
+
+ already_AddRefed<Promise>
+ ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
+
+ void
+ LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv);
+
+ bool
+ IsOnTargetThread()
+ {
+ return NS_IsMainThread() == !mWorkerPrivate;
+ }
+
+ void
+ AssertIsOnTargetThread()
+ {
+ MOZ_ASSERT(IsOnTargetThread());
+ }
+
+ // Only ever set once, always on target thread.
+ bool mBodyUsed;
+ nsCString mMimeType;
+
+ // The main-thread event target for runnable dispatching.
+ nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_Fetch_h
diff --git a/dom/fetch/FetchStream.cpp b/dom/fetch/FetchStream.cpp
new file mode 100644
index 0000000000..1c22a71405
--- /dev/null
+++ b/dom/fetch/FetchStream.cpp
@@ -0,0 +1,640 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "FetchStream.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/Maybe.h"
+#include "nsNetCID.h"
+#include "nsITransport.h"
+#include "nsIStreamTransportService.h"
+#include "nsProxyRelease.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "Workers.h"
+
+#include "mozilla/dom/DOMError.h"
+
+#define FETCH_STREAM_FLAG 0
+
+static NS_DEFINE_CID(kStreamTransportServiceCID,
+ NS_STREAMTRANSPORTSERVICE_CID);
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+namespace {
+
+class FetchStreamWorkerHolder final : public WorkerHolder
+{
+public:
+ explicit FetchStreamWorkerHolder(FetchStream* aStream)
+ : WorkerHolder()
+ , mStream(aStream)
+ , mWasNotified(false)
+ {}
+
+ bool Notify(Status aStatus) override
+ {
+ if (!mWasNotified) {
+ mWasNotified = true;
+ mStream->Close();
+ }
+
+ return true;
+ }
+
+ WorkerPrivate* GetWorkerPrivate() const
+ {
+ return mWorkerPrivate;
+ }
+
+private:
+ RefPtr<FetchStream> mStream;
+ bool mWasNotified;
+};
+
+class FetchStreamWorkerHolderShutdown final : public WorkerControlRunnable
+{
+public:
+ FetchStreamWorkerHolderShutdown(WorkerPrivate* aWorkerPrivate,
+ UniquePtr<WorkerHolder>&& aHolder,
+ nsCOMPtr<nsIGlobalObject>&& aGlobal,
+ RefPtr<FetchStreamHolder>&& aStreamHolder)
+ : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+ , mHolder(Move(aHolder))
+ , mGlobal(Move(aGlobal))
+ , mStreamHolder(Move(aStreamHolder))
+ {}
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ mHolder = nullptr;
+ mGlobal = nullptr;
+
+ mStreamHolder->NullifyStream();
+ mStreamHolder = nullptr;
+
+ return true;
+ }
+
+ // This runnable starts from a JS Thread. We need to disable a couple of
+ // assertions by overriding the following methods.
+
+ bool
+ PreDispatch(WorkerPrivate* aWorkerPrivate) override
+ {
+ return true;
+ }
+
+ void
+ PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
+ {}
+
+private:
+ UniquePtr<WorkerHolder> mHolder;
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ RefPtr<FetchStreamHolder> mStreamHolder;
+};
+
+} // anonymous
+
+NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver,
+ nsISupportsWeakReference)
+
+/* static */ void
+FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
+ nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
+ JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aCx);
+ MOZ_DIAGNOSTIC_ASSERT(aInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
+
+ RefPtr<FetchStream> stream = new FetchStream(aGlobal, aStreamHolder, aInputStream);
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (NS_WARN_IF(!os)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ aRv = os->AddObserver(stream, DOM_WINDOW_DESTROYED_TOPIC, true);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ } else {
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ MOZ_ASSERT(workerPrivate);
+
+ UniquePtr<FetchStreamWorkerHolder> holder(
+ new FetchStreamWorkerHolder(stream));
+ if (NS_WARN_IF(!holder->HoldWorker(workerPrivate, Closing))) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ // Note, this will create a ref-cycle between the holder and the stream.
+ // The cycle is broken when the stream is closed or the worker begins
+ // shutting down.
+ stream->mWorkerHolder = Move(holder);
+ }
+
+ if (!JS::HasReadableStreamCallbacks(aCx)) {
+ JS::SetReadableStreamCallbacks(aCx,
+ &FetchStream::RequestDataCallback,
+ &FetchStream::WriteIntoReadRequestCallback,
+ &FetchStream::CancelCallback,
+ &FetchStream::ClosedCallback,
+ &FetchStream::ErroredCallback,
+ &FetchStream::FinalizeCallback);
+ }
+
+ JS::Rooted<JSObject*> body(aCx,
+ JS::NewReadableExternalSourceStreamObject(aCx, stream, FETCH_STREAM_FLAG));
+ if (!body) {
+ aRv.StealExceptionFromJSContext(aCx);
+ return;
+ }
+
+ // This will be released in FetchStream::FinalizeCallback(). We are
+ // guaranteed the jsapi will call FinalizeCallback when ReadableStream
+ // js object is finalized.
+ NS_ADDREF(stream.get());
+
+ aStream.set(body);
+}
+
+/* static */ void
+FetchStream::RequestDataCallback(JSContext* aCx,
+ JS::HandleObject aStream,
+ void* aUnderlyingSource,
+ uint8_t aFlags,
+ size_t aDesiredSize)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+ MOZ_DIAGNOSTIC_ASSERT(JS::ReadableStreamIsDisturbed(aStream));
+
+ RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
+ stream->AssertIsOnOwningThread();
+
+ MutexAutoLock lock(stream->mMutex);
+
+ MOZ_DIAGNOSTIC_ASSERT(stream->mState == eInitializing ||
+ stream->mState == eWaiting ||
+ stream->mState == eChecking ||
+ stream->mState == eReading);
+
+ if (stream->mState == eReading) {
+ // We are already reading data.
+ return;
+ }
+
+ if (stream->mState == eChecking) {
+ // If we are looking for more data, there is nothing else we should do:
+ // let's move this checking operation in a reading.
+ MOZ_ASSERT(stream->mInputStream);
+ stream->mState = eReading;
+ return;
+ }
+
+ if (stream->mState == eInitializing) {
+ // The stream has been used for the first time.
+ stream->mStreamHolder->MarkAsRead();
+ }
+
+ stream->mState = eReading;
+
+ if (!stream->mInputStream) {
+ // This is the first use of the stream. Let's convert the
+ // mOriginalInputStream into an nsIAsyncInputStream.
+ MOZ_ASSERT(stream->mOriginalInputStream);
+
+ bool nonBlocking = false;
+ nsresult rv = stream->mOriginalInputStream->IsNonBlocking(&nonBlocking);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ nsCOMPtr<nsIAsyncInputStream> asyncStream =
+ do_QueryInterface(stream->mOriginalInputStream);
+ if (!nonBlocking || !asyncStream) {
+ nsCOMPtr<nsIStreamTransportService> sts =
+ do_GetService(kStreamTransportServiceCID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ nsCOMPtr<nsITransport> transport;
+ rv = sts->CreateInputTransport(stream->mOriginalInputStream,
+ /* aStartOffset */ 0,
+ /* aReadLimit */ -1,
+ /* aCloseWhenDone */ true,
+ getter_AddRefs(transport));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ nsCOMPtr<nsIInputStream> wrapper;
+ rv = transport->OpenInputStream(/* aFlags */ 0,
+ /* aSegmentSize */ 0,
+ /* aSegmentCount */ 0,
+ getter_AddRefs(wrapper));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ asyncStream = do_QueryInterface(wrapper);
+ }
+
+ stream->mInputStream = asyncStream;
+ stream->mOriginalInputStream = nullptr;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream);
+
+ nsresult rv =
+ stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ // All good.
+}
+
+/* static */ void
+FetchStream::WriteIntoReadRequestCallback(JSContext* aCx,
+ JS::HandleObject aStream,
+ void* aUnderlyingSource,
+ uint8_t aFlags, void* aBuffer,
+ size_t aLength, size_t* aByteWritten)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+ MOZ_DIAGNOSTIC_ASSERT(aBuffer);
+ MOZ_DIAGNOSTIC_ASSERT(aByteWritten);
+
+ RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
+ stream->AssertIsOnOwningThread();
+
+ MutexAutoLock lock(stream->mMutex);
+
+ MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWriting);
+ stream->mState = eChecking;
+
+ uint32_t written;
+ nsresult rv =
+ stream->mInputStream->Read(static_cast<char*>(aBuffer), aLength, &written);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ *aByteWritten = written;
+
+ if (written == 0) {
+ stream->CloseAndReleaseObjects(aCx, lock, aStream);
+ return;
+ }
+
+ rv = stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ stream->ErrorPropagation(aCx, lock, aStream, rv);
+ return;
+ }
+
+ // All good.
+}
+
+/* static */ JS::Value
+FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ JS::HandleValue aReason)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+
+ // This is safe because we created an extra reference in FetchStream::Create()
+ // that won't be released until FetchStream::FinalizeCallback() is called.
+ // We are guaranteed that won't happen until the js ReadableStream object
+ // is finalized.
+ FetchStream* stream = static_cast<FetchStream*>(aUnderlyingSource);
+ stream->AssertIsOnOwningThread();
+
+ if (stream->mInputStream) {
+ stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
+ }
+
+ stream->ReleaseObjects();
+
+ return JS::UndefinedValue();
+}
+
+/* static */ void
+FetchStream::ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+}
+
+/* static */ void
+FetchStream::ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ JS::HandleValue aReason)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+
+ // This is safe because we created an extra reference in FetchStream::Create()
+ // that won't be released until FetchStream::FinalizeCallback() is called.
+ // We are guaranteed that won't happen until the js ReadableStream object
+ // is finalized.
+ FetchStream* stream = static_cast<FetchStream*>(aUnderlyingSource);
+ stream->AssertIsOnOwningThread();
+
+ if (stream->mState == eInitializing) {
+ // The stream has been used for the first time.
+ stream->mStreamHolder->MarkAsRead();
+ }
+
+ if (stream->mInputStream) {
+ stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
+ }
+
+ stream->ReleaseObjects();
+}
+
+void
+FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags)
+{
+ MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
+ MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
+
+ // This can be called in any thread.
+
+ // This takes ownership of the ref created in FetchStream::Create().
+ RefPtr<FetchStream> stream =
+ dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource));
+
+ stream->ReleaseObjects();
+}
+
+FetchStream::FetchStream(nsIGlobalObject* aGlobal,
+ FetchStreamHolder* aStreamHolder,
+ nsIInputStream* aInputStream)
+ : mMutex("FetchStream::mMutex")
+ , mState(eInitializing)
+ , mGlobal(aGlobal)
+ , mStreamHolder(aStreamHolder)
+ , mOriginalInputStream(aInputStream)
+ // TODO: Replace with mGlobal->EventTargetFor(TaskCategory::Other)
+ // When we have the Dispatcher API in the tree, see Issue #1442
+ , mOwningEventTarget(NS_GetCurrentThread())
+{
+ MOZ_DIAGNOSTIC_ASSERT(aInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
+}
+
+FetchStream::~FetchStream()
+{
+}
+
+void
+FetchStream::ErrorPropagation(JSContext* aCx,
+ const MutexAutoLock& aProofOfLock,
+ JS::HandleObject aStream,
+ nsresult aError)
+{
+ AssertIsOnOwningThread();
+
+ // Nothing to do.
+ if (mState == eClosed) {
+ return;
+ }
+
+ // Let's close the stream.
+ if (aError == NS_BASE_STREAM_CLOSED) {
+ CloseAndReleaseObjects(aCx, aProofOfLock, aStream);
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
+
+ // Let's use a generic error.
+ RefPtr<DOMError> error = new DOMError(window, NS_ERROR_DOM_TYPE_ERR);
+
+ JS::Rooted<JS::Value> errorValue(aCx);
+ if (ToJSValue(aCx, error, &errorValue)) {
+ MutexAutoUnlock unlock(mMutex);
+ JS::ReadableStreamError(aCx, aStream, errorValue);
+ }
+
+ ReleaseObjects(aProofOfLock);
+}
+
+NS_IMETHODIMP
+FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
+{
+ AssertIsOnOwningThread();
+ MOZ_DIAGNOSTIC_ASSERT(aStream);
+
+ Maybe<MutexAutoLock> lock;
+ lock.emplace(mMutex);
+
+ // Already closed. We have nothing else to do here.
+ if (mState == eClosed) {
+ return NS_OK;
+ }
+
+ nsAutoMicroTask mt;
+ AutoEntryScript aes(mGlobal, "fetch body data available");
+
+ MOZ_DIAGNOSTIC_ASSERT(mInputStream);
+ MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking);
+
+ JSContext* cx = aes.cx();
+ JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody());
+
+ uint64_t size = 0;
+ nsresult rv = mInputStream->Available(&size);
+ if (NS_SUCCEEDED(rv) && size == 0) {
+ // In theory this should not happen. If size is 0, the stream should be
+ // considered closed.
+ rv = NS_BASE_STREAM_CLOSED;
+ }
+
+ // No warning for stream closed.
+ if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
+ ErrorPropagation(cx, *lock, stream, rv);
+ return NS_OK;
+ }
+
+ // This extra checking is completed. Let's wait for the next read request.
+ if (mState == eChecking) {
+ mState = eWaiting;
+ return NS_OK;
+ }
+
+ mState = eWriting;
+
+ lock.reset();
+
+ JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size);
+
+ return NS_OK;
+}
+
+/* static */ nsresult
+FetchStream::RetrieveInputStream(void* aUnderlyingReadableStreamSource,
+ nsIInputStream** aInputStream)
+{
+ MOZ_ASSERT(aUnderlyingReadableStreamSource);
+ MOZ_ASSERT(aInputStream);
+
+ RefPtr<FetchStream> stream =
+ static_cast<FetchStream*>(aUnderlyingReadableStreamSource);
+ stream->AssertIsOnOwningThread();
+
+ // if mOriginalInputStream is null, the reading already started. We don't want
+ // to expose the internal inputStream.
+ if (NS_WARN_IF(!stream->mOriginalInputStream)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ nsCOMPtr<nsIInputStream> inputStream = stream->mOriginalInputStream;
+ inputStream.forget(aInputStream);
+ return NS_OK;
+}
+
+void
+FetchStream::Close()
+{
+ AssertIsOnOwningThread();
+
+ MutexAutoLock lock(mMutex);
+
+ if (mState == eClosed) {
+ return;
+ }
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
+ ReleaseObjects(lock);
+ return;
+ }
+
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody());
+
+ CloseAndReleaseObjects(cx, lock, stream);
+}
+
+void
+FetchStream::CloseAndReleaseObjects(JSContext* aCx,
+ const MutexAutoLock& aProofOfLock,
+ JS::HandleObject aStream)
+{
+ AssertIsOnOwningThread();
+ MOZ_DIAGNOSTIC_ASSERT(mState != eClosed);
+
+ ReleaseObjects(aProofOfLock);
+
+ MutexAutoUnlock unlock(mMutex);
+
+ if (JS::ReadableStreamIsReadable(aStream)) {
+ JS::ReadableStreamClose(aCx, aStream);
+ }
+}
+
+void
+FetchStream::ReleaseObjects()
+{
+ MutexAutoLock lock(mMutex);
+ ReleaseObjects(lock);
+}
+
+void
+FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock)
+{
+ // This method can be called on 2 possible threads: the owning one and a JS
+ // thread used to release resources. If we are on the JS thread, we need to
+ // dispatch a runnable to go back to the owning thread in order to release
+ // resources correctly.
+
+ if (mState == eClosed) {
+ // Already gone. Nothing to do.
+ return;
+ }
+
+ mState = eClosed;
+
+ if (mWorkerHolder) {
+ RefPtr<FetchStreamWorkerHolderShutdown> r =
+ new FetchStreamWorkerHolderShutdown(
+ static_cast<FetchStreamWorkerHolder*>(mWorkerHolder.get())->GetWorkerPrivate(),
+ Move(mWorkerHolder), Move(mGlobal), Move(mStreamHolder));
+ r->Dispatch();
+ } else {
+ RefPtr<FetchStream> self = this;
+ RefPtr<Runnable> r = NS_NewRunnableFunction(
+ [self] () {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(self, DOM_WINDOW_DESTROYED_TOPIC);
+ }
+ self->mGlobal = nullptr;
+
+ self->mStreamHolder->NullifyStream();
+ self->mStreamHolder = nullptr;
+ });
+
+ NS_DispatchToMainThread(r);
+ }
+}
+
+#ifdef DEBUG
+void
+FetchStream::AssertIsOnOwningThread()
+{
+ NS_ASSERT_OWNINGTHREAD(FetchStream);
+}
+#endif
+
+// nsIObserver
+// -----------
+
+NS_IMETHODIMP
+FetchStream::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ AssertIsOnMainThread();
+ AssertIsOnOwningThread();
+
+ MOZ_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0);
+
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
+ if (SameCOMIdentity(aSubject, window)) {
+ Close();
+ }
+
+ return NS_OK;
+}
+
+} // dom namespace
+} // mozilla namespace
diff --git a/dom/fetch/FetchStream.h b/dom/fetch/FetchStream.h
new file mode 100644
index 0000000000..66e1de5642
--- /dev/null
+++ b/dom/fetch/FetchStream.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_FetchStream_h
+#define mozilla_dom_FetchStream_h
+
+#include "Fetch.h"
+#include "jsapi.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIObserver.h"
+#include "nsISupportsImpl.h"
+#include "nsWeakReference.h"
+
+class nsIGlobalObject;
+
+class nsIInputStream;
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerHolder;
+}
+
+class FetchStreamHolder;
+
+class FetchStream final : public nsIInputStreamCallback
+ , public nsIObserver
+ , public nsSupportsWeakReference
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+ NS_DECL_NSIOBSERVER
+
+ static void
+ Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
+ nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
+ JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv);
+
+ void
+ Close();
+
+ static nsresult
+ RetrieveInputStream(void* aUnderlyingReadableStreamSource,
+ nsIInputStream** aInputStream);
+private:
+ FetchStream(nsIGlobalObject* aGlobal,
+ FetchStreamHolder* aStreamHolder,
+ nsIInputStream* aInputStream);
+ ~FetchStream();
+
+#ifdef DEBUG
+ void
+ AssertIsOnOwningThread();
+#else
+ void
+ AssertIsOnOwningThread() {}
+#endif
+
+ static void
+ RequestDataCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ size_t aDesiredSize);
+
+ static void
+ WriteIntoReadRequestCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ void* aBuffer, size_t aLength,
+ size_t* aByteWritten);
+
+ static JS::Value
+ CancelCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ JS::HandleValue aReason);
+
+ static void
+ ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags);
+
+ static void
+ ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
+ void* aUnderlyingSource, uint8_t aFlags,
+ JS::HandleValue reason);
+
+ static void
+ FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags);
+
+ void
+ ErrorPropagation(JSContext* aCx,
+ const MutexAutoLock& aProofOfLock,
+ JS::HandleObject aStream, nsresult aRv);
+
+ void
+ CloseAndReleaseObjects(JSContext* aCx,
+ const MutexAutoLock& aProofOfLock,
+ JS::HandleObject aSteam);
+
+ class WorkerShutdown;
+
+ void
+ ReleaseObjects(const MutexAutoLock& aProofOfLock);
+
+ void
+ ReleaseObjects();
+
+ // Common methods
+
+ enum State {
+ // This is the beginning state before any reading operation.
+ eInitializing,
+
+ // RequestDataCallback has not been called yet. We haven't started to read
+ // data from the stream yet.
+ eWaiting,
+
+ // We are reading data in a separate I/O thread.
+ eReading,
+
+ // We are ready to write something in the JS Buffer.
+ eWriting,
+
+ // After a writing, we want to check if the stream is closed. After the
+ // check, we go back to eWaiting. If a reading request happens in the
+ // meantime, we move to eReading state.
+ eChecking,
+
+ // Operation completed.
+ eClosed,
+ };
+
+ // We need a mutex because JS engine can release FetchStream on a non-owning
+ // thread. We must be sure that the releasing of resources doesn't trigger
+ // race conditions.
+ Mutex mMutex;
+
+ // Protected by mutex.
+ State mState;
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ RefPtr<FetchStreamHolder> mStreamHolder;
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+
+ // This is the original inputStream received during the CTOR. It will be
+ // converted into an nsIAsyncInputStream and stored into mInputStream at the
+ // first use.
+ nsCOMPtr<nsIInputStream> mOriginalInputStream;
+ nsCOMPtr<nsIAsyncInputStream> mInputStream;
+
+ UniquePtr<workers::WorkerHolder> mWorkerHolder;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FetchStream_h
diff --git a/dom/fetch/FetchStreamReader.cpp b/dom/fetch/FetchStreamReader.cpp
new file mode 100644
index 0000000000..c6f5ca4d1d
--- /dev/null
+++ b/dom/fetch/FetchStreamReader.cpp
@@ -0,0 +1,405 @@
+/* -*- 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/. */
+
+#include "FetchStreamReader.h"
+#include "InternalResponse.h"
+#include "mozilla/dom/PromiseBinding.h"
+#include "mozilla/dom/DOMException.h"
+#include "nsContentUtils.h"
+#include "nsIScriptError.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+namespace {
+
+class FetchStreamReaderWorkerHolder final : public WorkerHolder
+{
+public:
+ explicit FetchStreamReaderWorkerHolder(FetchStreamReader* aReader)
+ : WorkerHolder(WorkerHolder::Behavior::AllowIdleShutdownStart)
+ , mReader(aReader)
+ , mWasNotified(false)
+ {}
+
+ bool Notify(Status aStatus) override
+ {
+ if (!mWasNotified) {
+ mWasNotified = true;
+ // The WorkerPrivate does have a context available, and we could pass it
+ // here to trigger cancellation of the reader, but the author of this
+ // comment chickened out.
+ mReader->CloseAndRelease(nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
+ }
+
+ return true;
+ }
+
+private:
+ RefPtr<FetchStreamReader> mReader;
+ bool mWasNotified;
+};
+
+} // anonymous
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(FetchStreamReader)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(FetchStreamReader)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(FetchStreamReader)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FetchStreamReader)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FetchStreamReader)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FetchStreamReader)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReader)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchStreamReader)
+ NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStreamCallback)
+NS_INTERFACE_MAP_END
+
+/* static */ nsresult
+FetchStreamReader::Create(JSContext* aCx, nsIGlobalObject* aGlobal,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream)
+{
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(aGlobal);
+ MOZ_ASSERT(aStreamReader);
+ MOZ_ASSERT(aInputStream);
+
+ RefPtr<FetchStreamReader> streamReader = new FetchStreamReader(aGlobal);
+
+ nsCOMPtr<nsIAsyncInputStream> pipeIn;
+
+ nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn),
+ getter_AddRefs(streamReader->mPipeOut),
+ true, true, 0, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ MOZ_ASSERT(workerPrivate);
+
+ // We need to know when the worker goes away.
+ UniquePtr<FetchStreamReaderWorkerHolder> holder(
+ new FetchStreamReaderWorkerHolder(streamReader));
+ if (NS_WARN_IF(!holder->HoldWorker(workerPrivate, Closing))) {
+ streamReader->mPipeOut->CloseWithStatus(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ // These 2 objects create a ref-cycle here that is broken when the stream is
+ // closed or the worker shutsdown.
+ streamReader->mWorkerHolder = Move(holder);
+ }
+
+ pipeIn.forget(aInputStream);
+ streamReader.forget(aStreamReader);
+ return NS_OK;
+}
+
+FetchStreamReader::FetchStreamReader(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal)
+ // TODO: Replace with mGlobal->EventTargetFor(TaskCategory::Other)
+ // When we have the Dispatcher API in the tree, see Issue #1442
+ , mOwningEventTarget(NS_GetCurrentThread())
+ , mBufferRemaining(0)
+ , mBufferOffset(0)
+ , mStreamClosed(false)
+{
+ MOZ_ASSERT(aGlobal);
+}
+
+FetchStreamReader::~FetchStreamReader()
+{
+ CloseAndRelease(nullptr, NS_BASE_STREAM_CLOSED);
+}
+
+// If a context is provided, an attempt will be made to cancel the reader. The
+// only situation where we don't expect to have a context is when closure is
+// being triggered from the destructor or the WorkerHolder is notifying. If
+// we're at the destructor, it's far too late to cancel anything. And if the
+// WorkerHolder is being notified, the global is going away, so there's also
+// no need to do further JS work.
+void
+FetchStreamReader::CloseAndRelease(JSContext* aCx, nsresult aStatus)
+{
+ NS_ASSERT_OWNINGTHREAD(FetchStreamReader);
+
+ if (mStreamClosed) {
+ // Already closed.
+ return;
+ }
+
+ RefPtr<FetchStreamReader> kungFuDeathGrip = this;
+
+ if (aCx) {
+ MOZ_ASSERT(mReader);
+
+ RefPtr<DOMException> error = DOMException::Create(aStatus);
+
+ JS::Rooted<JS::Value> errorValue(aCx);
+ if (ToJSValue(aCx, error, &errorValue)) {
+ JS::Rooted<JSObject*> reader(aCx, mReader);
+ // It's currently safe to cancel an already closed reader because, per the
+ // comments in ReadableStream::cancel() conveying the spec, step 2 of
+ // 3.4.3 that specified ReadableStreamCancel is: If stream.[[state]] is
+ // "closed", return a new promise resolved with undefined.
+ JS::ReadableStreamReaderCancel(aCx, reader, errorValue);
+ }
+ }
+ mStreamClosed = true;
+
+ mGlobal = nullptr;
+
+ mPipeOut->CloseWithStatus(aStatus);
+ mPipeOut = nullptr;
+
+ mWorkerHolder = nullptr;
+
+ mReader = nullptr;
+ mBuffer = nullptr;
+}
+
+void
+FetchStreamReader::StartConsuming(JSContext* aCx,
+ JS::HandleObject aStream,
+ JS::MutableHandle<JSObject*> aReader,
+ ErrorResult& aRv)
+{
+ MOZ_DIAGNOSTIC_ASSERT(!mReader);
+ MOZ_DIAGNOSTIC_ASSERT(aStream);
+
+ JS::Rooted<JSObject*> reader(aCx,
+ JS::ReadableStreamGetReader(aCx, aStream,
+ JS::ReadableStreamReaderMode::Default));
+ if (!reader) {
+ aRv.StealExceptionFromJSContext(aCx);
+ CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ mReader = reader;
+ aReader.set(reader);
+
+ aRv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+}
+
+// nsIOutputStreamCallback interface
+
+NS_IMETHODIMP
+FetchStreamReader::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
+{
+ NS_ASSERT_OWNINGTHREAD(FetchStreamReader);
+ MOZ_ASSERT(aStream == mPipeOut);
+ MOZ_ASSERT(mReader);
+
+ if (mStreamClosed) {
+ return NS_OK;
+ }
+
+ if (mBuffer) {
+ return WriteBuffer();
+ }
+
+ // TODO: We need to verify this is the correct global per the spec.
+ // See bug 1385890.
+ AutoEntryScript aes(mGlobal, "ReadableStreamReader.read", !mWorkerHolder);
+
+ JS::Rooted<JSObject*> reader(aes.cx(), mReader);
+ JS::Rooted<JSObject*> promise(aes.cx(),
+ JS::ReadableStreamDefaultReaderRead(aes.cx(),
+ reader));
+ if (NS_WARN_IF(!promise)) {
+ // Let's close the stream.
+ CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<Promise> domPromise = Promise::CreateFromExisting(mGlobal, promise);
+ if (NS_WARN_IF(!domPromise)) {
+ // Let's close the stream.
+ CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Let's wait.
+ domPromise->AppendNativeHandler(this);
+ return NS_OK;
+}
+
+void
+FetchStreamReader::ResolvedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue)
+{
+ if (mStreamClosed) {
+ return;
+ }
+
+ // This promise should be resolved with { done: boolean, value: something },
+ // "value" is interesting only if done is false.
+
+ // We don't want to play with JS api, let's WebIDL bindings doing it for us.
+ // FetchReadableStreamReadDataDone is a dictionary with just a boolean, if the
+ // parsing succeeded, we can proceed with the parsing of the "value", which it
+ // must be a Uint8Array.
+ FetchReadableStreamReadDataDone valueDone;
+ if (!valueDone.Init(aCx, aValue)) {
+ JS_ClearPendingException(aCx);
+ CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ if (valueDone.mDone) {
+ // Stream is completed.
+ CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ UniquePtr<FetchReadableStreamReadDataArray> value(
+ new FetchReadableStreamReadDataArray);
+ if (!value->Init(aCx, aValue) || !value->mValue.WasPassed()) {
+ JS_ClearPendingException(aCx);
+ CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ Uint8Array& array = value->mValue.Value();
+ array.ComputeLengthAndData();
+ uint32_t len = array.Length();
+
+ if (len == 0) {
+ // If there is nothing to read, let's do another reading.
+ OnOutputStreamReady(mPipeOut);
+ return;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(!mBuffer);
+ mBuffer = Move(value);
+
+ mBufferOffset = 0;
+ mBufferRemaining = len;
+
+ nsresult rv = WriteBuffer();
+ if (NS_FAILED(rv)) {
+ // DOMException only understands errors from domerr.msg, so we normalize to
+ // identifying an abort if the write fails.
+ CloseAndRelease(aCx, NS_ERROR_DOM_ABORT_ERR);
+ }
+}
+
+nsresult
+FetchStreamReader::WriteBuffer()
+{
+ MOZ_ASSERT(mBuffer);
+ MOZ_ASSERT(mBuffer->mValue.WasPassed());
+
+ Uint8Array& array = mBuffer->mValue.Value();
+ char* data = reinterpret_cast<char*>(array.Data());
+
+ while (1) {
+ uint32_t written = 0;
+ nsresult rv =
+ mPipeOut->Write(data + mBufferOffset, mBufferRemaining, &written);
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ break;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(written <= mBufferRemaining);
+ mBufferRemaining -= written;
+ mBufferOffset += written;
+
+ if (mBufferRemaining == 0) {
+ mBuffer = nullptr;
+ break;
+ }
+ }
+
+ nsresult rv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+FetchStreamReader::RejectedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue)
+{
+ ReportErrorToConsole(aCx, aValue);
+ CloseAndRelease(aCx, NS_ERROR_FAILURE);
+}
+
+void
+FetchStreamReader::ReportErrorToConsole(JSContext* aCx,
+ JS::Handle<JS::Value> aValue)
+{
+ nsCString sourceSpec;
+ uint32_t line = 0;
+ uint32_t column = 0;
+ nsString valueString;
+
+ nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line,
+ &column, valueString);
+
+ nsTArray<nsString> params;
+ params.AppendElement(valueString);
+
+ RefPtr<ConsoleReportCollector> reporter = new ConsoleReportCollector();
+ reporter->AddConsoleReport(nsIScriptError::errorFlag,
+ NS_LITERAL_CSTRING("ReadableStreamReader.read"),
+ nsContentUtils::eDOM_PROPERTIES,
+ sourceSpec, line, column,
+ NS_LITERAL_CSTRING("ReadableStreamReadingFailed"),
+ params);
+
+ uint64_t innerWindowId = 0;
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
+ if (window) {
+ innerWindowId = window->WindowID();
+ }
+ reporter->FlushReportsByWindowId(innerWindowId);
+ return;
+ }
+
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+ if (workerPrivate) {
+ innerWindowId = workerPrivate->WindowID();
+ }
+
+ RefPtr<Runnable> r = NS_NewRunnableFunction(
+ [reporter, innerWindowId] () {
+ reporter->FlushReportsByWindowId(innerWindowId);
+ });
+
+ workerPrivate->DispatchToMainThread(r.forget());
+}
+
+} // dom namespace
+} // mozilla namespace
diff --git a/dom/fetch/FetchStreamReader.h b/dom/fetch/FetchStreamReader.h
new file mode 100644
index 0000000000..c25685046c
--- /dev/null
+++ b/dom/fetch/FetchStreamReader.h
@@ -0,0 +1,85 @@
+/* -*- 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_FetchStreamReader_h
+#define mozilla_dom_FetchStreamReader_h
+
+#include "jsapi.h"
+#include "mozilla/dom/FetchBinding.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "nsIAsyncOutputStream.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerHolder;
+}
+
+class FetchStreamReader final : public nsIOutputStreamCallback
+ , public PromiseNativeHandler
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(FetchStreamReader, nsIOutputStreamCallback)
+ NS_DECL_NSIOUTPUTSTREAMCALLBACK
+
+ // This creates a nsIInputStream able to retrieve data from the ReadableStream
+ // object. The reading starts when StartConsuming() is called.
+ static nsresult
+ Create(JSContext* aCx, nsIGlobalObject* aGlobal,
+ FetchStreamReader** aStreamReader,
+ nsIInputStream** aInputStream);
+
+ void
+ ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ void
+ RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
+ // Idempotently close the output stream and null out all state. If aCx is
+ // provided, the reader will also be canceled. aStatus must be a DOM error
+ // as understood by DOMException because it will be provided as the
+ // cancellation reason.
+ void
+ CloseAndRelease(JSContext* aCx, nsresult aStatus);
+
+ void
+ StartConsuming(JSContext* aCx,
+ JS::HandleObject aStream,
+ JS::MutableHandle<JSObject*> aReader,
+ ErrorResult& aRv);
+
+private:
+ explicit FetchStreamReader(nsIGlobalObject* aGlobal);
+ ~FetchStreamReader();
+
+ nsresult
+ WriteBuffer();
+
+ void
+ ReportErrorToConsole(JSContext* aCx, JS::Handle<JS::Value> aValue);
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+
+ nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
+
+ UniquePtr<workers::WorkerHolder> mWorkerHolder;
+
+ JS::Heap<JSObject*> mReader;
+
+ UniquePtr<FetchReadableStreamReadDataArray> mBuffer;
+ uint32_t mBufferRemaining;
+ uint32_t mBufferOffset;
+
+ bool mStreamClosed;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FetchStreamReader_h
diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp
index ec32c4e216..f4fd1912f9 100644
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -139,18 +139,18 @@ InternalResponse::ToIPC(IPCInternalResponse* aIPCResponse,
}
already_AddRefed<InternalResponse>
-InternalResponse::Clone()
+InternalResponse::Clone(CloneType aCloneType)
{
RefPtr<InternalResponse> clone = CreateIncompleteCopy();
clone->mHeaders = new InternalHeaders(*mHeaders);
if (mWrappedResponse) {
- clone->mWrappedResponse = mWrappedResponse->Clone();
+ clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
MOZ_ASSERT(!mBody);
return clone.forget();
}
- if (!mBody) {
+ if (!mBody || aCloneType == eDontCloneInputStream) {
return clone.forget();
}
diff --git a/dom/fetch/InternalResponse.h b/dom/fetch/InternalResponse.h
index e4b4a0ab62..d5c759d577 100644
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -46,7 +46,13 @@ public:
M* aManager,
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
- already_AddRefed<InternalResponse> Clone();
+ enum CloneType
+ {
+ eCloneInputStream,
+ eDontCloneInputStream,
+ };
+
+ already_AddRefed<InternalResponse> Clone(CloneType eCloneType);
static already_AddRefed<InternalResponse>
NetworkError()
diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp
index ab87a3215a..b79b539772 100644
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -29,7 +29,25 @@ using namespace workers;
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Request)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Request)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Request)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Request)
+ MOZ_DIAGNOSTIC_ASSERT(!tmp->mReadableStreamReader);
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamReader)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -37,8 +55,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_INTERFACE_MAP_END
Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest, AbortSignal* aSignal)
- : FetchBody<Request>()
- , mOwner(aOwner)
+ : FetchBody<Request>(aOwner)
, mRequest(aRequest)
, mSignal(aSignal)
{
@@ -557,17 +574,15 @@ Request::Constructor(const GlobalObject& aGlobal,
}
if (aInit.mBody.WasPassed()) {
- const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
- aInit.mBody.Value();
+ const Nullable<fetch::OwningBodyInit>& bodyInitNullable = aInit.mBody.Value();
if (!bodyInitNullable.IsNull()) {
- const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
- bodyInitNullable.Value();
+ const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
nsCOMPtr<nsIInputStream> stream;
- nsAutoCString contentType;
+ nsAutoCString contentTypeWithCharset;
uint64_t contentLengthUnused;
aRv = ExtractByteStreamFromBody(bodyInit,
getter_AddRefs(stream),
- contentType,
+ contentTypeWithCharset,
contentLengthUnused);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@@ -575,10 +590,10 @@ Request::Constructor(const GlobalObject& aGlobal,
temporaryBody = stream;
- if (!contentType.IsVoid() &&
+ if (!contentTypeWithCharset.IsVoid() &&
!requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
- contentType, aRv);
+ contentTypeWithCharset, aRv);
}
if (NS_WARN_IF(aRv.Failed())) {
@@ -599,7 +614,10 @@ Request::Constructor(const GlobalObject& aGlobal,
inputReq->GetBody(getter_AddRefs(body));
if (body) {
inputReq->SetBody(nullptr);
- inputReq->SetBodyUsed();
+ inputReq->SetBodyUsed(aGlobal.Context(), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
}
}
return domRequest.forget();
diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h
index 8c01cbd799..d5fc09fe25 100644
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -128,6 +128,8 @@ public:
void
GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
+ using FetchBody::GetBody;
+
void
SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
@@ -162,7 +164,6 @@ public:
private:
~Request();
- nsCOMPtr<nsIGlobalObject> mOwner;
RefPtr<InternalRequest> mRequest;
// Lazily created.
diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp
index 42b25ae1d9..3ee5e93d4a 100644
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -12,12 +12,16 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/FetchBinding.h"
+#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/URL.h"
#include "nsDOMString.h"
+#include "BodyExtractor.h"
+#include "FetchStream.h"
+#include "FetchStreamReader.h"
#include "InternalResponse.h"
#include "WorkerPrivate.h"
@@ -26,7 +30,31 @@ namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Response)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Response)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader)
+
+ tmp->mReadableStreamBody = nullptr;
+ tmp->mReadableStreamReader = nullptr;
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Response)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchStreamReader)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Response)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamReader)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -34,18 +62,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_INTERFACE_MAP_END
Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse, AbortSignal* aSignal)
- : FetchBody<Response>()
- , mOwner(aGlobal)
+ : FetchBody<Response>(aGlobal)
, mInternalResponse(aInternalResponse)
, mSignal(aSignal)
{
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
SetMimeType();
+
+ // Keep a firm grip!
+ mozilla::HoldJSObjects(this);
}
Response::~Response()
{
+ mozilla::DropJSObjects(this);
}
/* static */ already_AddRefed<Response>
@@ -106,7 +137,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
return nullptr;
}
- Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>> body;
+ Optional<fetch::ResponseBodyInit> body;
ResponseInit init;
init.mStatus = aStatus;
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
@@ -127,7 +158,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
/*static*/ already_AddRefed<Response>
Response::Constructor(const GlobalObject& aGlobal,
- const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
+ const Optional<fetch::ResponseBodyInit>& aBody,
const ResponseInit& aInit, ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
@@ -193,29 +224,86 @@ Response::Constructor(const GlobalObject& aGlobal,
}
}
- if (aBody.WasPassed() && !aBody.Value().IsNull()) {
+ if (aBody.WasPassed()) {
if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
return nullptr;
}
+ nsCString contentTypeWithCharset;
nsCOMPtr<nsIInputStream> bodyStream;
- nsCString contentType;
- uint64_t bodySize = 0;
- aRv = ExtractByteStreamFromBody(aBody.Value().Value(),
- getter_AddRefs(bodyStream),
- contentType,
- bodySize);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
+ int64_t bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
+
+ if (aBody.Value().IsReadableStream()) {
+ const ReadableStream& readableStream =
+ aBody.Value().GetAsReadableStream();
+
+ JS::Rooted<JSObject*> readableStreamObj(aGlobal.Context(),
+ readableStream.Obj());
+
+ if (JS::ReadableStreamIsDisturbed(readableStreamObj) ||
+ JS::ReadableStreamIsLocked(readableStreamObj) ||
+ !JS::ReadableStreamIsReadable(readableStreamObj)) {
+ aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
+ return nullptr;
+ }
+
+ r->SetReadableStreamBody(readableStreamObj);
+
+ if (JS::ReadableStreamGetMode(readableStreamObj) ==
+ JS::ReadableStreamMode::ExternalSource) {
+ // If this is a DOM generated ReadableStream, we can extract the
+ // inputStream directly.
+ void* underlyingSource = nullptr;
+ if (!JS::ReadableStreamGetExternalUnderlyingSource(aGlobal.Context(),
+ readableStreamObj,
+ &underlyingSource)) {
+ aRv.StealExceptionFromJSContext(aGlobal.Context());
+ return nullptr;
+ }
+
+ MOZ_ASSERT(underlyingSource);
+
+ aRv = FetchStream::RetrieveInputStream(underlyingSource,
+ getter_AddRefs(bodyStream));
+
+ // The releasing of the external source is needed in order to avoid an
+ // extra stream lock.
+ JS::ReadableStreamReleaseExternalUnderlyingSource(readableStreamObj);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ } else {
+ // If this is a JS-created ReadableStream, let's create a
+ // FetchStreamReader.
+ aRv = FetchStreamReader::Create(aGlobal.Context(), global,
+ getter_AddRefs(r->mFetchStreamReader),
+ getter_AddRefs(bodyStream));
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+ } else {
+ uint64_t size = 0;
+ aRv = ExtractByteStreamFromBody(aBody.Value(),
+ getter_AddRefs(bodyStream),
+ contentTypeWithCharset,
+ size);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ bodySize = size;
}
+
internalResponse->SetBody(bodyStream, bodySize);
- if (!contentType.IsVoid() &&
+ if (!contentTypeWithCharset.IsVoid() &&
!internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
// Ignore Append() failing here.
ErrorResult error;
- internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
+ internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
+ contentTypeWithCharset, error);
error.SuppressException();
}
@@ -229,29 +317,87 @@ Response::Constructor(const GlobalObject& aGlobal,
}
already_AddRefed<Response>
-Response::Clone(ErrorResult& aRv) const
+Response::Clone(JSContext* aCx, ErrorResult& aRv)
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr;
}
- RefPtr<InternalResponse> ir = mInternalResponse->Clone();
- RefPtr<Response> response = new Response(mOwner, ir, mSignal);
+ RefPtr<FetchStreamReader> streamReader;
+ nsCOMPtr<nsIInputStream> inputStream;
+
+ JS::Rooted<JSObject*> body(aCx);
+ MaybeTeeReadableStreamBody(aCx, &body,
+ getter_AddRefs(streamReader),
+ getter_AddRefs(inputStream), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT_IF(body, streamReader);
+ MOZ_ASSERT_IF(body, inputStream);
+
+ RefPtr<InternalResponse> ir =
+ mInternalResponse->Clone(body
+ ? InternalResponse::eDontCloneInputStream
+ : InternalResponse::eCloneInputStream);
+
+ RefPtr<Response> response = new Response(mOwner, ir, nullptr);
+
+ if (body) {
+ // Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
+ // if this body is a native stream. In this case, the InternalResponse will
+ // have a clone of the native body and the ReadableStream will be created
+ // lazily if needed.
+ response->SetReadableStreamBody(body);
+ response->mFetchStreamReader = streamReader;
+ ir->SetBody(inputStream, InternalResponse::UNKNOWN_BODY_SIZE);
+ }
+
return response.forget();
}
already_AddRefed<Response>
-Response::CloneUnfiltered(ErrorResult& aRv) const
+Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv)
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr;
}
- RefPtr<InternalResponse> clone = mInternalResponse->Clone();
+ RefPtr<FetchStreamReader> streamReader;
+ nsCOMPtr<nsIInputStream> inputStream;
+
+ JS::Rooted<JSObject*> body(aCx);
+ MaybeTeeReadableStreamBody(aCx, &body,
+ getter_AddRefs(streamReader),
+ getter_AddRefs(inputStream), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT_IF(body, streamReader);
+ MOZ_ASSERT_IF(body, inputStream);
+
+ RefPtr<InternalResponse> clone =
+ mInternalResponse->Clone(body
+ ? InternalResponse::eDontCloneInputStream
+ : InternalResponse::eCloneInputStream);
+
RefPtr<InternalResponse> ir = clone->Unfiltered();
- RefPtr<Response> ref = new Response(mOwner, ir, mSignal);
+ RefPtr<Response> ref = new Response(mOwner, ir, nullptr);
+
+ if (body) {
+ // Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
+ // if this body is a native stream. In this case, the InternalResponse will
+ // have a clone of the native body and the ReadableStream will be created
+ // lazily if needed.
+ ref->SetReadableStreamBody(body);
+ ref->mFetchStreamReader = streamReader;
+ ir->SetBody(inputStream, InternalResponse::UNKNOWN_BODY_SIZE);
+ }
+
return ref.forget();
}
diff --git a/dom/fetch/Response.h b/dom/fetch/Response.h
index fa2ced87d8..d2f885fbc7 100644
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -105,6 +105,8 @@ public:
void
GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
+ using FetchBody::GetBody;
+
static already_AddRefed<Response>
Error(const GlobalObject& aGlobal);
@@ -113,7 +115,7 @@ public:
static already_AddRefed<Response>
Constructor(const GlobalObject& aGlobal,
- const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
+ const Optional<fetch::ResponseBodyInit>& aBody,
const ResponseInit& aInit, ErrorResult& rv);
nsIGlobalObject* GetParentObject() const
@@ -122,10 +124,10 @@ public:
}
already_AddRefed<Response>
- Clone(ErrorResult& aRv) const;
+ Clone(JSContext* aCx, ErrorResult& aRv);
already_AddRefed<Response>
- CloneUnfiltered(ErrorResult& aRv) const;
+ CloneUnfiltered(JSContext* aCx, ErrorResult& aRv);
void
SetBody(nsIInputStream* aBody, int64_t aBodySize);
@@ -142,7 +144,6 @@ public:
private:
~Response();
- nsCOMPtr<nsIGlobalObject> mOwner;
RefPtr<InternalResponse> mInternalResponse;
// Lazily created
diff --git a/dom/fetch/moz.build b/dom/fetch/moz.build
index d1a8a49323..f01254e042 100644
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -4,11 +4,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
+ 'BodyExtractor.h',
'ChannelInfo.h',
'Fetch.h',
'FetchDriver.h',
'FetchIPCTypes.h',
'FetchObserver.h',
+ 'FetchStreamReader.h',
'FetchUtil.h',
'Headers.h',
'InternalHeaders.h',
@@ -19,11 +21,14 @@ EXPORTS.mozilla.dom += [
]
UNIFIED_SOURCES += [
+ 'BodyExtractor.cpp',
'ChannelInfo.cpp',
'Fetch.cpp',
'FetchConsumer.cpp',
'FetchDriver.cpp',
'FetchObserver.cpp',
+ 'FetchStream.cpp',
+ 'FetchStreamReader.cpp',
'FetchUtil.cpp',
'Headers.cpp',
'InternalHeaders.cpp',
diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp
index 96eab44510..0e7f0e17ae 100644
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -310,7 +310,7 @@ TCPSocketParent::RecvData(const SendableData& aData,
const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
NS_ENSURE_TRUE(ok, true);
- RootedTypedArray<ArrayBuffer> data(autoCx);
+ RootedSpiderMonkeyInterface<ArrayBuffer> data(autoCx);
data.Init(&val.toObject());
Optional<uint32_t> byteLength(buffer.Length());
mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
diff --git a/dom/url/URLSearchParams.cpp b/dom/url/URLSearchParams.cpp
index 3a0311aabd..19cc3cda6b 100644
--- a/dom/url/URLSearchParams.cpp
+++ b/dom/url/URLSearchParams.cpp
@@ -575,9 +575,10 @@ URLSearchParams::ReadStructuredClone(JSStructuredCloneReader* aReader)
NS_IMETHODIMP
URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset)
+ nsACString& aContentTypeWithCharset,
+ nsACString& aCharset)
{
- aContentType.AssignLiteral("application/x-www-form-urlencoded");
+ aContentTypeWithCharset.AssignLiteral("application/x-www-form-urlencoded;charset=UTF-8");
aCharset.AssignLiteral("UTF-8");
nsAutoString serialized;
diff --git a/dom/webidl/Fetch.webidl b/dom/webidl/Fetch.webidl
index 4b2a0af7d0..bbb1faf7f7 100644
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -8,7 +8,7 @@
*/
typedef object JSON;
-typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or USVString or URLSearchParams) BodyInit;
+typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) BodyInit;
[NoInterfaceObject, Exposed=(Window,Worker)]
interface Body {
@@ -24,3 +24,15 @@ interface Body {
[Throws]
Promise<USVString> text();
};
+
+// These are helper dictionaries for the parsing of a
+// getReader().read().then(data) parsing.
+// See more about how these 2 helpers are used in
+// dom/fetch/FetchStreamReader.cpp
+dictionary FetchReadableStreamReadDataDone {
+ boolean done = false;
+};
+
+dictionary FetchReadableStreamReadDataArray {
+ Uint8Array value;
+};
diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl
index 516b5415e3..5f2ac63e1e 100644
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -296,7 +296,7 @@ partial interface Navigator {
partial interface Navigator {
[Throws, Pref="beacon.enabled"]
boolean sendBeacon(DOMString url,
- optional (ArrayBufferView or Blob or DOMString or FormData)? data = null);
+ optional BodyInit? data = null);
};
partial interface Navigator {
diff --git a/dom/webidl/Response.webidl b/dom/webidl/Response.webidl
index 08f31fe29a..0f4c99dbac 100644
--- a/dom/webidl/Response.webidl
+++ b/dom/webidl/Response.webidl
@@ -7,7 +7,9 @@
* https://fetch.spec.whatwg.org/#response-class
*/
-[Constructor(optional BodyInit? body, optional ResponseInit init),
+// This should be Constructor(optional BodyInit... but BodyInit doesn't include
+// ReadableStream yet because we don't want to expose the Streams API to Request.
+[Constructor(optional (Blob or BufferSource or FormData or URLSearchParams or ReadableStream or USVString) body, optional ResponseInit init),
Exposed=(Window,Worker)]
interface Response {
[NewObject] static Response error();
@@ -30,6 +32,12 @@ interface Response {
};
Response implements Body;
+// This should be part of Body but we don't want to expose body to request yet.
+partial interface Response {
+ [GetterThrows, Func="nsContentUtils::StreamsEnabled"]
+ readonly attribute ReadableStream? body;
+};
+
dictionary ResponseInit {
unsigned short status = 200;
ByteString statusText = "OK";
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
index 239207efc9..6138a3c6de 100644
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -299,6 +299,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
.setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
+ .setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
.setArrayProtoValues(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("array_prototype_values")));
diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp
index 5aaac7cfae..bedf966ef1 100644
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -700,9 +700,14 @@ private:
request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
loadInfo.mFullURL.Length());
+ // This JSContext will not end up executing JS code because here there are
+ // no ReadableStreams involved.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+
ErrorResult error;
RefPtr<Promise> cachePromise =
- mCacheCreator->Cache_()->Put(request, *response, error);
+ mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
if (NS_WARN_IF(error.Failed())) {
nsresult rv = error.StealNSResult();
channel->Cancel(rv);
@@ -1667,8 +1672,13 @@ CacheScriptLoader::Load(Cache* aCache)
mozilla::dom::CacheQueryOptions params;
+ // This JSContext will not end up executing JS code because here there are
+ // no ReadableStreams involved.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+
ErrorResult error;
- RefPtr<Promise> promise = aCache->Match(request, params, error);
+ RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
if (NS_WARN_IF(error.Failed())) {
Fail(error.StealNSResult());
return;
diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp
index ce2e5e7aae..569422da25 100644
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -6,6 +6,7 @@
#include "ServiceWorkerEvents.h"
#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
#include "nsIConsoleReportCollector.h"
#include "nsIHttpChannelInternal.h"
#include "nsINetworkInterceptController.h"
@@ -30,8 +31,6 @@
#include "mozilla/Move.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/BodyUtil.h"
-#include "mozilla/dom/DOMException.h"
-#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/FetchEventBinding.h"
#include "mozilla/dom/MessagePort.h"
@@ -404,76 +403,6 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
}
-namespace {
-
-void
-ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
- nsACString& aSourceSpecOut, uint32_t *aLineOut,
- uint32_t *aColumnOut, nsString& aMessageOut)
-{
- MOZ_ASSERT(aLineOut);
- MOZ_ASSERT(aColumnOut);
-
- if (aValue.isObject()) {
- JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
- RefPtr<DOMException> domException;
-
- // Try to process as an Error object. Use the file/line/column values
- // from the Error as they will be more specific to the root cause of
- // the problem.
- JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
- if (err) {
- // Use xpc to extract the error message only. We don't actually send
- // this report anywhere.
- RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
- report->Init(err,
- "<unknown>", // toString result
- false, // chrome
- 0); // window ID
-
- if (!report->mFileName.IsEmpty()) {
- CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
- *aLineOut = report->mLineNumber;
- *aColumnOut = report->mColumn;
- }
- aMessageOut.Assign(report->mErrorMsg);
- }
-
- // Next, try to unwrap the rejection value as a DOMException.
- else if(NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, obj, domException))) {
-
- nsAutoString filename;
- domException->GetFilename(aCx, filename);
- if (!filename.IsEmpty()) {
- CopyUTF16toUTF8(filename, aSourceSpecOut);
- *aLineOut = domException->LineNumber(aCx);
- *aColumnOut = domException->ColumnNumber();
- }
-
- domException->GetName(aMessageOut);
- aMessageOut.AppendLiteral(": ");
-
- nsAutoString message;
- domException->GetMessageMoz(message);
- aMessageOut.Append(message);
- }
- }
-
- // If we could not unwrap a specific error type, then perform default safe
- // string conversions on primitives. Objects will result in "[Object]"
- // unfortunately.
- if (aMessageOut.IsEmpty()) {
- nsAutoJSString jsString;
- if (jsString.init(aCx, aValue)) {
- aMessageOut = jsString;
- } else {
- JS_ClearPendingException(aCx);
- }
- }
-}
-
-} // anonymous namespace
-
class MOZ_STACK_CLASS AutoCancel
{
RefPtr<RespondWithHandler> mOwner;
@@ -505,6 +434,44 @@ public:
}
}
+ // This function steals the error message from a ErrorResult.
+ void
+ SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
+ MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
+
+ // Storing the error as exception in the JSContext.
+ if (!aRv.MaybeSetPendingException(aCx)) {
+ return;
+ }
+
+ MOZ_ASSERT(!aRv.Failed());
+
+ // Let's take the pending exception.
+ JS::Rooted<JS::Value> exn(aCx);
+ if (!JS_GetPendingException(aCx, &exn)) {
+ return;
+ }
+
+ JS_ClearPendingException(aCx);
+
+ // Converting the exception in a js::ErrorReport.
+ js::ErrorReport report(aCx);
+ if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
+ JS_ClearPendingException(aCx);
+ return;
+ }
+
+ MOZ_ASSERT(mOwner);
+ MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
+ MOZ_ASSERT(mParams.Length() == 1);
+
+ // Let's store the error message here.
+ mMessageName.Assign(report.toStringResult().c_str());
+ mParams.Clear();
+ }
+
template<typename... Params>
void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams)
{
@@ -557,7 +524,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
uint32_t line = 0;
uint32_t column = 0;
nsString valueString;
- ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
+ nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
+ valueString);
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
@@ -572,7 +540,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
uint32_t line = 0;
uint32_t column = 0;
nsString valueString;
- ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
+ nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
+ valueString);
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
@@ -659,7 +628,12 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
ir->GetUnfilteredBody(getter_AddRefs(body));
// Errors and redirects may not have a body.
if (body) {
- response->SetBodyUsed();
+ IgnoredErrorResult error;
+ response->SetBodyUsed(aCx, error);
+ if (NS_WARN_IF(error.Failed())) {
+ autoCancel.SetCancelErrorResult(aCx, error);
+ return;
+ }
nsCOMPtr<nsIOutputStream> responseBody;
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
@@ -715,7 +689,8 @@ RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
uint32_t column = mRespondWithColumnNumber;
nsString valueString;
- ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString);
+ nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
+ valueString);
::AsyncLog(mInterceptedChannel, sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
@@ -853,7 +828,8 @@ public:
nsCString spec;
uint32_t line = 0;
uint32_t column = 0;
- ExtractErrorValues(aCx, aValue, spec, &line, &column, mRejectValue);
+ nsContentUtils::ExtractErrorValues(aCx, aValue, spec, &line, &column,
+ mRejectValue);
// only use the extracted location if we found one
if (!spec.IsEmpty()) {
diff --git a/dom/workers/ServiceWorkerScriptCache.cpp b/dom/workers/ServiceWorkerScriptCache.cpp
index 05be250da8..f343c35586 100644
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -395,7 +395,7 @@ public:
return;
}
- WriteToCache(cache);
+ WriteToCache(aCx, cache);
return;
}
@@ -528,7 +528,7 @@ private:
}
void
- WriteToCache(Cache* aCache)
+ WriteToCache(JSContext* aCx, Cache* aCache)
{
AssertIsOnMainThread();
MOZ_ASSERT(aCache);
@@ -561,8 +561,10 @@ private:
// For now we have to wait until the Put Promise is fulfilled before we can
// continue since Cache does not yet support starting a read that is being
// written to.
- RefPtr<Promise> cachePromise = aCache->Put(request, *response, result);
+ RefPtr<Promise> cachePromise = aCache->Put(aCx, request, *response, result);
if (NS_WARN_IF(result.Failed())) {
+ // No exception here because there are no ReadableStreams involved here.
+ MOZ_ASSERT(!result.IsJSException());
MOZ_ASSERT(!result.IsErrorWithMessage());
Fail(result.StealNSResult());
return;
@@ -903,7 +905,7 @@ CompareCache::ManageCacheResult(JSContext* aCx, JS::Handle<JS::Value> aValue)
request.SetAsUSVString().Rebind(mURL.Data(), mURL.Length());
ErrorResult error;
CacheQueryOptions params;
- RefPtr<Promise> promise = cache->Match(request, params, error);
+ RefPtr<Promise> promise = cache->Match(aCx, request, params, error);
if (NS_WARN_IF(error.Failed())) {
mManager->CacheFinished(error.StealNSResult(), false);
return;
diff --git a/dom/workers/WorkerHolder.cpp b/dom/workers/WorkerHolder.cpp
index 5a8c5c4d8f..dcdcd29fa3 100644
--- a/dom/workers/WorkerHolder.cpp
+++ b/dom/workers/WorkerHolder.cpp
@@ -8,8 +8,9 @@
BEGIN_WORKERS_NAMESPACE
-WorkerHolder::WorkerHolder()
+WorkerHolder::WorkerHolder(Behavior aBehavior)
: mWorkerPrivate(nullptr)
+ , mBehavior(aBehavior)
{
}
@@ -44,6 +45,12 @@ WorkerHolder::ReleaseWorker()
ReleaseWorkerInternal();
}
+WorkerHolder::Behavior
+WorkerHolder::GetBehavior() const
+{
+ return mBehavior;
+}
+
void
WorkerHolder::ReleaseWorkerInternal()
{
diff --git a/dom/workers/WorkerHolder.h b/dom/workers/WorkerHolder.h
index 050c6f8e20..d8f3927af7 100644
--- a/dom/workers/WorkerHolder.h
+++ b/dom/workers/WorkerHolder.h
@@ -73,7 +73,12 @@ class WorkerHolder
public:
NS_DECL_OWNINGTHREAD
- WorkerHolder();
+ enum Behavior {
+ AllowIdleShutdownStart,
+ PreventIdleShutdownStart,
+ };
+
+ explicit WorkerHolder(Behavior aBehavior = PreventIdleShutdownStart);
virtual ~WorkerHolder();
bool HoldWorker(WorkerPrivate* aWorkerPrivate, Status aFailStatus);
@@ -81,6 +86,8 @@ public:
virtual bool Notify(Status aStatus) = 0;
+ Behavior GetBehavior() const;
+
protected:
void ReleaseWorkerInternal();
@@ -88,6 +95,8 @@ protected:
private:
void AssertIsOwningThread() const;
+
+ const Behavior mBehavior;
};
END_WORKERS_NAMESPACE
diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h
index 215b375ddb..415435cf06 100644
--- a/dom/workers/WorkerPrefs.h
+++ b/dom/workers/WorkerPrefs.h
@@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEn
WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
+WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index 622a882a65..3b3de7e3b5 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4154,6 +4154,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
, mDebugger(nullptr)
, mJSContext(nullptr)
, mPRThread(nullptr)
+ , mNumHoldersPreventingShutdownStart(0)
, mDebuggerEventLoopLevel(0)
, mMainThreadEventTarget(do_GetMainThread())
, mErrorHandlerRecursionCount(0)
@@ -5382,8 +5383,11 @@ WorkerPrivate::AddHolder(WorkerHolder* aHolder, Status aFailStatus)
MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
- if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(true)) {
- return false;
+ if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
+ if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
+ return false;
+ }
+ mNumHoldersPreventingShutdownStart += 1;
}
mHolders.AppendElement(aHolder);
@@ -5398,8 +5402,11 @@ WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
mHolders.RemoveElement(aHolder);
- if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
- NS_WARNING("Failed to modify busy count!");
+ if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
+ mNumHoldersPreventingShutdownStart -= 1;
+ if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
+ NS_WARNING("Failed to modify busy count!");
+ }
}
}
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index 9effdccc9a..26afecb69b 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -980,6 +980,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
nsTArray<ParentType*> mChildWorkers;
nsTObserverArray<WorkerHolder*> mHolders;
+ uint32_t mNumHoldersPreventingShutdownStart;
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
uint32_t mDebuggerEventLoopLevel;
RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;
diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp
index d97476ef61..a89af3c370 100644
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2298,175 +2298,6 @@ XMLHttpRequestMainThread::ChangeStateToDone()
}
}
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<nsIDocument>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(mBody));
- NS_ENSURE_STATE(domdoc);
- aCharset.AssignLiteral("UTF-8");
-
- nsresult rv;
- nsCOMPtr<nsIStorageStream> storStream;
- rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIOutputStream> output;
- rv = storStream->GetOutputStream(0, getter_AddRefs(output));
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (mBody->IsHTMLDocument()) {
- aContentType.AssignLiteral("text/html");
-
- nsString serialized;
- if (!nsContentUtils::SerializeNodeToMarkup(mBody, true, serialized)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- nsAutoCString utf8Serialized;
- if (!AppendUTF16toUTF8(serialized, utf8Serialized, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- uint32_t written;
- rv = output->Write(utf8Serialized.get(), utf8Serialized.Length(), &written);
- NS_ENSURE_SUCCESS(rv, rv);
-
- MOZ_ASSERT(written == utf8Serialized.Length());
- } else {
- aContentType.AssignLiteral("application/xml");
-
- nsCOMPtr<nsIDOMSerializer> serializer =
- do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Make sure to use the encoding we'll send
- rv = serializer->SerializeToStream(domdoc, output, aCharset);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- output->Close();
-
- uint32_t length;
- rv = storStream->GetLength(&length);
- NS_ENSURE_SUCCESS(rv, rv);
- *aContentLength = length;
-
- rv = storStream->NewInputStream(0, aResult);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<const nsAString>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- aContentType.AssignLiteral("text/plain");
- aCharset.AssignLiteral("UTF-8");
-
- nsAutoCString converted;
- if (!AppendUTF16toUTF8(*mBody, converted, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- *aContentLength = converted.Length();
- nsresult rv = NS_NewCStringInputStream(aResult, converted);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<nsIInputStream>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- aContentType.AssignLiteral("text/plain");
- aCharset.Truncate();
-
- nsresult rv = mBody->Available(aContentLength);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIInputStream> stream(mBody);
- stream.forget(aResult);
- return NS_OK;
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<Blob>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- return mBody->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<FormData>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- return mBody->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<URLSearchParams>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- return mBody->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<nsIXHRSendable>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- return mBody->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
-}
-
-static nsresult
-GetBufferDataAsStream(const uint8_t* aData, uint32_t aDataLength,
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset)
-{
- aContentType.SetIsVoid(true);
- aCharset.Truncate();
-
- *aContentLength = aDataLength;
- const char* data = reinterpret_cast<const char*>(aData);
-
- nsCOMPtr<nsIInputStream> stream;
- nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
- NS_ASSIGNMENT_COPY);
- NS_ENSURE_SUCCESS(rv, rv);
-
- stream.forget(aResult);
-
- return NS_OK;
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<const ArrayBuffer>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- mBody->ComputeLengthAndData();
- return GetBufferDataAsStream(mBody->Data(), mBody->Length(),
- aResult, aContentLength, aContentType, aCharset);
-}
-
-template<> nsresult
-XMLHttpRequestMainThread::RequestBody<const ArrayBufferView>::GetAsStream(
- nsIInputStream** aResult, uint64_t* aContentLength,
- nsACString& aContentType, nsACString& aCharset) const
-{
- mBody->ComputeLengthAndData();
- return GetBufferDataAsStream(mBody->Data(), mBody->Length(),
- aResult, aContentLength, aContentType, aCharset);
-}
-
-
nsresult
XMLHttpRequestMainThread::CreateChannel()
{
@@ -2792,7 +2623,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
// document?
nsCOMPtr<nsIDocument> doc = do_QueryInterface(supports);
if (doc) {
- RequestBody<nsIDocument> body(doc);
+ BodyExtractor<nsIDocument> body(doc);
return SendInternal(&body);
}
@@ -2801,21 +2632,21 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
if (wstr) {
nsAutoString string;
wstr->GetData(string);
- RequestBody<const nsAString> body(&string);
+ BodyExtractor<const nsAString> body(&string);
return SendInternal(&body);
}
// nsIInputStream?
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
if (stream) {
- RequestBody<nsIInputStream> body(stream);
+ BodyExtractor<nsIInputStream> body(stream);
return SendInternal(&body);
}
// nsIXHRSendable?
nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
if (sendable) {
- RequestBody<nsIXHRSendable> body(sendable);
+ BodyExtractor<nsIXHRSendable> body(sendable);
return SendInternal(&body);
}
@@ -2826,9 +2657,9 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
nsresult rv = aVariant->GetAsJSVal(&realVal);
if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
- RootedTypedArray<ArrayBuffer> buf(rootingCx);
+ RootedSpiderMonkeyInterface<ArrayBuffer> buf(rootingCx);
if (buf.Init(obj)) {
- RequestBody<const ArrayBuffer> body(&buf);
+ BodyExtractor<const ArrayBuffer> body(&buf);
return SendInternal(&body);
}
}
@@ -2845,12 +2676,12 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
nsString string;
string.Adopt(data, len);
- RequestBody<const nsAString> body(&string);
+ BodyExtractor<const nsAString> body(&string);
return SendInternal(&body);
}
nsresult
-XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
+XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
{
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
@@ -2916,13 +2747,6 @@ XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
mAuthorRequestHeaders.Get("content-type", uploadContentType);
if (uploadContentType.IsVoid()) {
uploadContentType = defaultContentType;
-
- if (!charset.IsEmpty()) {
- // If we are providing the default content type, then we also need to
- // provide a charset declaration.
- uploadContentType.Append(NS_LITERAL_CSTRING(";charset="));
- uploadContentType.Append(charset);
- }
}
// We don't want to set a charset for streams.
diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h
index 4a6eeec2ef..f2145605ef 100644
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -34,7 +34,11 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/NotNull.h"
#include "mozilla/dom/MutableBlobStorage.h"
+#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/TypedArray.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FormData.h"
+#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/XMLHttpRequest.h"
#include "mozilla/dom/XMLHttpRequestBinding.h"
#include "mozilla/dom/XMLHttpRequestEventTarget.h"
@@ -54,11 +58,8 @@ class nsIJSID;
namespace mozilla {
namespace dom {
-class Blob;
class BlobSet;
class DOMString;
-class FormData;
-class URLSearchParams;
class XMLHttpRequestUpload;
struct OriginAttributesDictionary;
@@ -288,34 +289,7 @@ public:
private:
virtual ~XMLHttpRequestMainThread();
- class RequestBodyBase
- {
- public:
- virtual nsresult GetAsStream(nsIInputStream** aResult,
- uint64_t* aContentLength,
- nsACString& aContentType,
- nsACString& aCharset) const
- {
- NS_ASSERTION(false, "RequestBodyBase should not be used directly.");
- return NS_ERROR_FAILURE;
- }
- };
-
- template<typename Type>
- class RequestBody final : public RequestBodyBase
- {
- Type* mBody;
- public:
- explicit RequestBody(Type* aBody) : mBody(aBody)
- {
- }
- nsresult GetAsStream(nsIInputStream** aResult,
- uint64_t* aContentLength,
- nsACString& aContentType,
- nsACString& aCharset) const override;
- };
-
- nsresult SendInternal(const RequestBodyBase* aBody);
+ nsresult SendInternal(const BodyExtractorBase* aBody);
bool IsCrossSiteCORSRequest() const;
bool IsDeniedCrossSiteCORSRequest();
@@ -336,7 +310,7 @@ public:
Send(JSContext* /*aCx*/, const ArrayBuffer& aArrayBuffer,
ErrorResult& aRv) override
{
- RequestBody<const ArrayBuffer> body(&aArrayBuffer);
+ BodyExtractor<const ArrayBuffer> body(&aArrayBuffer);
aRv = SendInternal(&body);
}
@@ -344,28 +318,28 @@ public:
Send(JSContext* /*aCx*/, const ArrayBufferView& aArrayBufferView,
ErrorResult& aRv) override
{
- RequestBody<const ArrayBufferView> body(&aArrayBufferView);
+ BodyExtractor<const ArrayBufferView> body(&aArrayBufferView);
aRv = SendInternal(&body);
}
virtual void
Send(JSContext* /*aCx*/, Blob& aBlob, ErrorResult& aRv) override
{
- RequestBody<Blob> body(&aBlob);
+ BodyExtractor<nsIXHRSendable> body(&aBlob);
aRv = SendInternal(&body);
}
virtual void Send(JSContext* /*aCx*/, URLSearchParams& aURLSearchParams,
ErrorResult& aRv) override
{
- RequestBody<URLSearchParams> body(&aURLSearchParams);
+ BodyExtractor<nsIXHRSendable> body(&aURLSearchParams);
aRv = SendInternal(&body);
}
virtual void
Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) override
{
- RequestBody<nsIDocument> body(&aDoc);
+ BodyExtractor<nsIDocument> body(&aDoc);
aRv = SendInternal(&body);
}
@@ -375,7 +349,7 @@ public:
if (DOMStringIsNull(aString)) {
Send(aCx, aRv);
} else {
- RequestBody<const nsAString> body(&aString);
+ BodyExtractor<const nsAString> body(&aString);
aRv = SendInternal(&body);
}
}
@@ -383,7 +357,7 @@ public:
virtual void
Send(JSContext* /*aCx*/, FormData& aFormData, ErrorResult& aRv) override
{
- RequestBody<FormData> body(&aFormData);
+ BodyExtractor<nsIXHRSendable> body(&aFormData);
aRv = SendInternal(&body);
}
@@ -391,7 +365,7 @@ public:
Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) override
{
NS_ASSERTION(aStream, "Null should go to string version");
- RequestBody<nsIInputStream> body(aStream);
+ BodyExtractor<nsIInputStream> body(aStream);
aRv = SendInternal(&body);
}
diff --git a/dom/xhr/nsIXMLHttpRequest.idl b/dom/xhr/nsIXMLHttpRequest.idl
index 53e80bab70..5505bd47ee 100644
--- a/dom/xhr/nsIXMLHttpRequest.idl
+++ b/dom/xhr/nsIXMLHttpRequest.idl
@@ -326,9 +326,12 @@ interface nsIXMLHttpRequest : nsISupports
[uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
interface nsIXHRSendable : nsISupports {
+ // contentTypeWithCharset can be set to the contentType or
+ // contentType+charset based on what the spec says.
+ // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
void getSendInfo(out nsIInputStream body,
out uint64_t contentLength,
- out ACString contentType,
+ out ACString contentTypeWithCharset,
out ACString charset);
};