Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-10-04 10:06:04 +08:00
commit a55674f653
89 changed files with 10466 additions and 916 deletions

View file

@ -398,7 +398,7 @@ FormData::Constructor(const GlobalObject& aGlobal,
NS_IMETHODIMP NS_IMETHODIMP
FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
nsACString& aContentType, nsACString& aCharset) nsACString& aContentTypeWithCharset, nsACString& aCharset)
{ {
FSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr); 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(); aCharset.Truncate();
*aContentLength = 0; *aContentLength = 0;
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength)); NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));

View file

@ -11,7 +11,9 @@
#include "nsPluginArray.h" #include "nsPluginArray.h"
#include "nsMimeTypeArray.h" #include "nsMimeTypeArray.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/DesktopNotification.h" #include "mozilla/dom/DesktopNotification.h"
#include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/File.h" #include "mozilla/dom/File.h"
#include "nsGeolocation.h" #include "nsGeolocation.h"
#include "nsIClassOfService.h" #include "nsIClassOfService.h"
@ -38,6 +40,7 @@
#include "mozilla/dom/ServiceWorkerContainer.h" #include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h" #include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h" #include "mozilla/dom/TCPSocket.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/workers/RuntimeService.h" #include "mozilla/dom/workers/RuntimeService.h"
#include "mozilla/Hal.h" #include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h" #include "nsISiteSpecificUserAgent.h"
@ -857,8 +860,52 @@ BeaconStreamListener::OnDataAvailable(nsIRequest *aRequest,
bool bool
Navigator::SendBeacon(const nsAString& aUrl, Navigator::SendBeacon(const nsAString& aUrl,
const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData, const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv) 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) { if (!mWindow) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
@ -900,9 +947,9 @@ Navigator::SendBeacon(const nsAString& aUrl,
nsIChannel::LOAD_CLASSIFY_URI; nsIChannel::LOAD_CLASSIFY_URI;
// No need to use CORS for sendBeacon unless it's a BLOB // No need to use CORS for sendBeacon unless it's a BLOB
nsSecurityFlags securityFlags = (!aData.IsNull() && aData.Value().IsBlob()) nsSecurityFlags securityFlags = aType == eBeaconTypeBlob
? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
nsCOMPtr<nsIChannel> channel; nsCOMPtr<nsIChannel> channel;
@ -928,76 +975,30 @@ Navigator::SendBeacon(const nsAString& aUrl,
} }
httpChannel->SetReferrer(documentURI); httpChannel->SetReferrer(documentURI);
nsCString mimeType; nsCOMPtr<nsIInputStream> in;
if (!aData.IsNull()) { nsAutoCString contentTypeWithCharset;
nsCOMPtr<nsIInputStream> in; nsAutoCString charset;
uint64_t length = 0;
if (aData.Value().IsString()) { if (aBody) {
nsCString stringData = NS_ConvertUTF16toUTF8(aData.Value().GetAsString()); aRv = aBody->GetAsStream(getter_AddRefs(in), &length,
nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv); contentTypeWithCharset, charset);
if (NS_FAILED(rv)) { if (NS_WARN_IF(aRv.Failed())) {
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;
}
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);
return false; return false;
} }
if (aType == eBeaconTypeArrayBuffer) {
MOZ_ASSERT(contentTypeWithCharset.IsEmpty());
MOZ_ASSERT(charset.IsEmpty());
contentTypeWithCharset.Assign("application/octet-stream");
}
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel); nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
if (!uploadChannel) { if (!uploadChannel) {
aRv.Throw(NS_ERROR_FAILURE); aRv.Throw(NS_ERROR_FAILURE);
return false; return false;
} }
uploadChannel->ExplicitSetUploadStream(in, mimeType, -1, uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length,
NS_LITERAL_CSTRING("POST"), NS_LITERAL_CSTRING("POST"),
false); false);
} else { } else {

View file

@ -7,6 +7,7 @@
#define mozilla_dom_Navigator_h #define mozilla_dom_Navigator_h
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/Nullable.h" #include "mozilla/dom/Nullable.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "nsIDOMNavigator.h" #include "nsIDOMNavigator.h"
@ -30,12 +31,13 @@ class nsIURI;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
class BodyExtractorBase;
class Geolocation; class Geolocation;
class systemMessageCallback; class systemMessageCallback;
class MediaDevices; class MediaDevices;
struct MediaStreamConstraints; struct MediaStreamConstraints;
class WakeLock; class WakeLock;
class ArrayBufferViewOrBlobOrStringOrFormData; class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class ServiceWorkerContainer; class ServiceWorkerContainer;
class DOMRequest; class DOMRequest;
} // namespace dom } // namespace dom
@ -196,7 +198,7 @@ public:
#endif // MOZ_AUDIO_CHANNEL_MANAGER #endif // MOZ_AUDIO_CHANNEL_MANAGER
bool SendBeacon(const nsAString& aUrl, bool SendBeacon(const nsAString& aUrl,
const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData, const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv); ErrorResult& aRv);
void MozGetUserMedia(const MediaStreamConstraints& aConstraints, void MozGetUserMedia(const MediaStreamConstraints& aConstraints,
@ -255,6 +257,19 @@ private:
bool CheckPermission(const char* type); bool CheckPermission(const char* type);
static bool CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType); 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<nsMimeTypeArray> mMimeTypes;
RefPtr<nsPluginArray> mPlugins; RefPtr<nsPluginArray> mPlugins;
RefPtr<Permissions> mPermissions; RefPtr<Permissions> mPermissions;

View file

@ -39,6 +39,8 @@
#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/CustomElementRegistry.h" #include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DocumentFragment.h" #include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/dom/FileSystemSecurity.h" #include "mozilla/dom/FileSystemSecurity.h"
@ -8735,6 +8737,25 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
return workerPrivate->PushEnabled(); return workerPrivate->PushEnabled();
} }
// 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 // static
bool bool
nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
@ -10062,3 +10083,72 @@ void nsContentUtils::StructuredClone(JSContext* aCx,
nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts(); nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
Unused << ports; 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);
}
}
}

View file

@ -1010,6 +1010,10 @@ public:
static bool PrefetchEnabled(nsIDocShell* aDocShell); 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, static nsresult CalculateBufferSizeForImage(const uint32_t& aStride,
const mozilla::gfx::IntSize& aImageSize, const mozilla::gfx::IntSize& aImageSize,
const mozilla::gfx::SurfaceFormat& aFormat, const mozilla::gfx::SurfaceFormat& aFormat,
@ -2679,6 +2683,8 @@ public:
static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader); static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader);
static bool PushEnabled(JSContext* aCx, JSObject* aObj); static bool PushEnabled(JSContext* aCx, JSObject* aObj);
static bool StreamsEnabled(JSContext* aCx, JSObject* aObj);
static bool IsNonSubresourceRequest(nsIChannel* aChannel); static bool IsNonSubresourceRequest(nsIChannel* aChannel);

View file

@ -116,11 +116,13 @@ DOMInterfaces = {
}, },
'Cache': { 'Cache': {
'implicitJSContext': [ 'add', 'addAll' ], 'implicitJSContext': [ 'add', 'addAll', 'match', 'matchAll', 'put',
'delete', 'keys' ],
'nativeType': 'mozilla::dom::cache::Cache', 'nativeType': 'mozilla::dom::cache::Cache',
}, },
'CacheStorage': { 'CacheStorage': {
'implicitJSContext': [ 'match' ],
'nativeType': 'mozilla::dom::cache::CacheStorage', 'nativeType': 'mozilla::dom::cache::CacheStorage',
}, },
@ -722,6 +724,7 @@ DOMInterfaces = {
'headers': 'headers_', 'headers': 'headers_',
'referrerPolicy': 'referrerPolicy_' 'referrerPolicy': 'referrerPolicy_'
}, },
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
}, },
'ResizeObservation': { 'ResizeObservation': {
@ -746,6 +749,8 @@ DOMInterfaces = {
'Response': { 'Response': {
'binaryNames': { 'headers': 'headers_' }, 'binaryNames': { 'headers': 'headers_' },
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text',
'clone', 'cloneUnfiltered' ],
}, },
'RGBColor': { 'RGBColor': {

View file

@ -1156,7 +1156,10 @@ class CGHeaders(CGWrapper):
# just include their header if we need to have functions # just include their header if we need to have functions
# taking references to them declared in that header. # taking references to them declared in that header.
headerSet = declareIncludes 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: else:
try: try:
typeDesc = config.getDescriptor(unrolled.inner.identifier.name) typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
@ -1371,7 +1374,10 @@ def UnionTypes(unionTypes, config):
elif f.isInterface(): elif f.isInterface():
if f.isSpiderMonkeyInterface(): if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h") 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: else:
try: try:
typeDesc = config.getDescriptor(f.inner.identifier.name) typeDesc = config.getDescriptor(f.inner.identifier.name)
@ -1457,7 +1463,10 @@ def UnionConversions(unionTypes, config):
elif f.isInterface(): elif f.isInterface():
if f.isSpiderMonkeyInterface(): if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h") 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(): elif f.inner.isExternal():
try: try:
typeDesc = config.getDescriptor(f.inner.identifier.name) typeDesc = config.getDescriptor(f.inner.identifier.name)
@ -5582,8 +5591,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isSpiderMonkeyInterface(): if type.isSpiderMonkeyInterface():
assert not isEnforceRange and not isClamp assert not isEnforceRange and not isClamp
name = type.unroll().name # unroll() because it may be nullable name = type.unroll().name # unroll() because it may be nullable
arrayType = CGGeneric(name) interfaceType = CGGeneric(name)
declType = arrayType declType = interfaceType
if type.nullable(): if type.nullable():
declType = CGTemplatedType("Nullable", declType) declType = CGTemplatedType("Nullable", declType)
objRef = "${declName}.SetValue()" 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 # 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 # 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 # want to have a holder, so that the callee doesn't see
# Optional<RootedTypedArray<ArrayType> >. So do a holder if we're # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
# optional and use a RootedTypedArray otherwise. # holder if we're optional and use a RootedSpiderMonkeyInterface
# otherwise.
if isOptional: if isOptional:
holderType = CGTemplatedType("TypedArrayRooter", arrayType) holderType = CGTemplatedType("SpiderMonkeyInterfaceRooter", interfaceType)
# If our typed array is nullable, this will set the Nullable to # If our SpiderMonkey interface is nullable, this will set the
# be not-null, but that's ok because we make an explicit # Nullable to be not-null, but that's ok because we make an
# SetNull() call on it as needed if our JS value is actually # explicit SetNull() call on it as needed if our JS value is
# null. XXXbz Because "Maybe" takes const refs for constructor # actually null. XXXbz Because "Maybe" takes const refs for
# arguments, we can't pass a reference here; have to pass a # constructor arguments, we can't pass a reference here; have
# pointer. # to pass a pointer.
holderArgs = "cx, &%s" % objRef holderArgs = "cx, &%s" % objRef
declArgs = None declArgs = None
else: else:
holderType = None holderType = None
holderArgs = None holderArgs = None
declType = CGTemplatedType("RootedTypedArray", declType) declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
declArgs = "cx" declArgs = "cx"
else: else:
holderType = None holderType = None
@ -6371,7 +6381,7 @@ def getMaybeWrapValueFuncForType(type):
if type.nullable(): if type.nullable():
return "MaybeWrapObjectOrNullValue" return "MaybeWrapObjectOrNullValue"
return "MaybeWrapObjectValue" 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. # dictionaries, since those are always plain JS objects.
if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence(): if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
if type.nullable(): if type.nullable():
@ -6391,7 +6401,8 @@ recordWrapLevel = 0
def getWrapTemplateForType(type, descriptorProvider, result, successCode, def getWrapTemplateForType(type, descriptorProvider, result, successCode,
returnsNewObject, exceptionCode, typedArraysAreStructs, returnsNewObject, exceptionCode,
spiderMonkeyInterfacesAreStructs,
isConstructorRetval=False): isConstructorRetval=False):
""" """
Reflect a C++ value stored in "result", of IDL type "type" into JS. The 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 doing a 'break' if the entire conversion template is inside a block that
the 'break' will exit). the 'break' will exit).
If typedArraysAreStructs is true, then if the type is a typed array, If spiderMonkeyInterfacesAreStructs is true, then if the type is a
"result" is one of the dom::TypedArray subclasses, not a JSObject*. SpiderMonkey interface, "result" is one of the
dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
The resulting string should be used with string.Template. It The resulting string should be used with string.Template. It
needs the following keys when substituting: needs the following keys when substituting:
@ -6497,7 +6509,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider, recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode, "%s.Value()" % result, successCode,
returnsNewObject, exceptionCode, returnsNewObject, exceptionCode,
typedArraysAreStructs) spiderMonkeyInterfacesAreStructs)
code = fill( code = fill(
""" """
@ -6529,7 +6541,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject, 'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode, 'exceptionCode': exceptionCode,
'obj': "returnArray", 'obj': "returnArray",
'typedArraysAreStructs': typedArraysAreStructs 'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
}) })
sequenceWrapLevel -= 1 sequenceWrapLevel -= 1
code = fill( code = fill(
@ -6583,7 +6595,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject, 'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode, 'exceptionCode': exceptionCode,
'obj': "returnObj", 'obj': "returnObj",
'typedArraysAreStructs': typedArraysAreStructs 'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
}) })
recordWrapLevel -= 1 recordWrapLevel -= 1
if type.keyType.isByteString(): if type.keyType.isByteString():
@ -6749,7 +6761,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (head + _setValue(result, wrapAsType=type), False) return (head + _setValue(result, wrapAsType=type), False)
if (type.isObject() or (type.isSpiderMonkeyInterface() and if (type.isObject() or (type.isSpiderMonkeyInterface() and
not typedArraysAreStructs)): not spiderMonkeyInterfacesAreStructs)):
# See comments in GetOrCreateDOMReflector explaining why we need # See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here. # to wrap here.
if type.nullable(): if type.nullable():
@ -6768,21 +6780,21 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
type.isDate() or type.isDate() or
(type.isSpiderMonkeyInterface() and typedArraysAreStructs)): (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)):
raise TypeError("Need to learn to wrap %s" % type) raise TypeError("Need to learn to wrap %s" % type)
if type.nullable(): if type.nullable():
recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider, recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode, "%s.Value()" % result, successCode,
returnsNewObject, exceptionCode, returnsNewObject, exceptionCode,
typedArraysAreStructs) spiderMonkeyInterfacesAreStructs)
return ("if (%s.IsNull()) {\n" % result + return ("if (%s.IsNull()) {\n" % result +
indent(setNull()) + indent(setNull()) +
"}\n" + "}\n" +
recTemplate, recInfal) recTemplate, recInfal)
if type.isSpiderMonkeyInterface(): if type.isSpiderMonkeyInterface():
assert typedArraysAreStructs assert spiderMonkeyInterfacesAreStructs
# See comments in GetOrCreateDOMReflector explaining why we need # See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here. # to wrap here.
# NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible # 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('successCode', None),
templateValues.get('returnsNewObject', False), templateValues.get('returnsNewObject', False),
templateValues.get('exceptionCode', "return false;\n"), templateValues.get('exceptionCode', "return false;\n"),
templateValues.get('typedArraysAreStructs', False), templateValues.get('spiderMonkeyInterfacesAreStructs', False),
isConstructorRetval=templateValues.get('isConstructorRetval', False))[0] isConstructorRetval=templateValues.get('isConstructorRetval', False))[0]
defaultValues = {'obj': 'obj'} defaultValues = {'obj': 'obj'}
@ -8193,10 +8205,10 @@ class CGMethodCall(CGThing):
# "object" arg. # "object" arg.
# First grab all the overloads that have a non-callback interface # First grab all the overloads that have a non-callback interface
# (which includes typed arrays and arraybuffers) at the # (which includes SpiderMonkey interfaces) at the distinguishing
# distinguishing index. We can also include the ones that have an # index. We can also include the ones that have an "object" here,
# "object" here, since if those are present no other object-typed # since if those are present no other object-typed argument will
# argument will be. # be.
objectSigs = [ objectSigs = [
s for s in possibleSignatures s for s in possibleSignatures
if (distinguishingType(s).isObject() or 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 # There might be more than one thing in objectSigs; we need to check
# which ones we unwrap to. # which ones we unwrap to.
if len(objectSigs) > 0: if len(objectSigs) > 0:
# Here it's enough to guard on our argument being an object. The # Here it's enough to guard on our argument being an object.
# code for unwrapping non-callback interfaces, typed arrays, # The code for unwrapping non-callback interfaces, spiderMonkey
# sequences, and Dates will just bail out and move on to # interfaces, sequences, and Dates will just bail out and move
# the next overload if the object fails to unwrap correctly, # on to the next overload if the object fails to unwrap
# while "object" accepts any object anyway. We could even not # correctly, while "object" accepts any object anyway. We
# do the isObject() check up front here, but in cases where we # could even not do the isObject() check up front here, but in
# have multiple object overloads it makes sense to do it only # cases where we have multiple object overloads it makes sense
# once instead of for each overload. That will also allow the # to do it only once instead of for each overload. That will
# unwrapping test to skip having to do codegen for the # also allow the unwrapping test to skip having to do codegen
# null-or-undefined case, which we already handled above. # for the null-or-undefined case, which we already handled
# above.
caseBody.append(CGGeneric("if (%s.isObject()) {\n" % caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
distinguishingArg)) distinguishingArg))
for sig in objectSigs: for sig in objectSigs:
@ -10239,7 +10252,7 @@ class CGUnionStruct(CGThing):
"jsvalHandle": "rval", "jsvalHandle": "rval",
"obj": "scopeObj", "obj": "scopeObj",
"result": val, "result": val,
"typedArraysAreStructs": True "spiderMonkeyInterfacesAreStructs": True
}) })
return CGGeneric(wrapCode) return CGGeneric(wrapCode)
@ -13219,7 +13232,7 @@ class CGDictionary(CGThing):
# 'obj' can just be allowed to be the string "obj", since that # 'obj' can just be allowed to be the string "obj", since that
# will be our dictionary object, which is presumably itself in # will be our dictionary object, which is presumably itself in
# the right scope. # the right scope.
'typedArraysAreStructs': True 'spiderMonkeyInterfacesAreStructs': True
}) })
conversion = CGGeneric(innerTemplate) conversion = CGGeneric(innerTemplate)
conversion = CGWrapper(conversion, conversion = CGWrapper(conversion,
@ -13679,7 +13692,7 @@ class ForwardDeclarationBuilder:
except NoSuchDescriptorError: except NoSuchDescriptorError:
pass pass
# Note: Spidermonkey interfaces are typedefs, so can't be # Note: SpiderMonkey interfaces are typedefs, so can't be
# forward-declared # forward-declared
elif t.isPromise(): elif t.isPromise():
self.addInMozillaDom("Promise") self.addInMozillaDom("Promise")
@ -13988,7 +14001,7 @@ class CGBindingRoot(CGThing):
return {desc.getDescriptor(desc.interface.parent.identifier.name)} return {desc.getDescriptor(desc.interface.parent.identifier.name)}
for x in dependencySortObjects(jsImplemented, getParentDescriptor, for x in dependencySortObjects(jsImplemented, getParentDescriptor,
lambda d: d.interface.identifier.name): lambda d: d.interface.identifier.name):
cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True)) cgthings.append(CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True))
cgthings.append(CGJSImplClass(x)) cgthings.append(CGJSImplClass(x))
# And make sure we have the right number of newlines at the end # And make sure we have the right number of newlines at the end
@ -14050,14 +14063,13 @@ class CGBindingRoot(CGThing):
class CGNativeMember(ClassMethod): class CGNativeMember(ClassMethod):
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
breakAfter=True, passJSBitsAsNeeded=True, visibility="public", breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
typedArraysAreStructs=True, variadicIsSequence=False, spiderMonkeyInterfacesAreStructs=True,
resultNotAddRefed=False, variadicIsSequence=False, resultNotAddRefed=False,
virtual=False, virtual=False, override=False):
override=False):
""" """
If typedArraysAreStructs is false, typed arrays will be passed as If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
JS::Handle<JSObject*>. If it's true they will be passed as one of the will be passed as JS::Handle<JSObject*>. If it's true they will be
dom::TypedArray subclasses. passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
If passJSBitsAsNeeded is false, we don't automatically pass in a If passJSBitsAsNeeded is false, we don't automatically pass in a
JSContext* or a JSObject* based on the return and argument types. We JSContext* or a JSObject* based on the return and argument types. We
@ -14068,7 +14080,7 @@ class CGNativeMember(ClassMethod):
self.extendedAttrs = extendedAttrs self.extendedAttrs = extendedAttrs
self.resultAlreadyAddRefed = not resultNotAddRefed self.resultAlreadyAddRefed = not resultNotAddRefed
self.passJSBitsAsNeeded = passJSBitsAsNeeded self.passJSBitsAsNeeded = passJSBitsAsNeeded
self.typedArraysAreStructs = typedArraysAreStructs self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
self.variadicIsSequence = variadicIsSequence self.variadicIsSequence = variadicIsSequence
breakAfterSelf = "\n" if breakAfter else "" breakAfterSelf = "\n" if breakAfter else ""
ClassMethod.__init__(self, name, ClassMethod.__init__(self, name,
@ -14375,7 +14387,7 @@ class CGNativeMember(ClassMethod):
False, False) False, False)
if type.isSpiderMonkeyInterface(): if type.isSpiderMonkeyInterface():
if not self.typedArraysAreStructs: if not self.spiderMonkeyInterfacesAreStructs:
return "JS::Handle<JSObject*>", False, False return "JS::Handle<JSObject*>", False, False
# Unroll for the name, in case we're nullable. # Unroll for the name, in case we're nullable.
@ -15547,16 +15559,16 @@ class CGFastCallback(CGClass):
class CGCallbackInterface(CGCallback): class CGCallbackInterface(CGCallback):
def __init__(self, descriptor, typedArraysAreStructs=False): def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
iface = descriptor.interface iface = descriptor.interface
attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] 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] for a in attrs]
setters = [CallbackSetter(a, descriptor, typedArraysAreStructs) setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
for a in attrs if not a.readonly] for a in attrs if not a.readonly]
methods = [m for m in iface.members methods = [m for m in iface.members
if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] 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()] for m in methods for sig in m.signatures()]
if iface.isJSImplemented() and iface.ctor(): if iface.isJSImplemented() and iface.ctor():
sigs = descriptor.interface.ctor().signatures() 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 # CallSetup already handled the unmark-gray bits for us. we don't have
# anything better to use for 'obj', really... # anything better to use for 'obj', really...
def __init__(self, sig, name, descriptorProvider, needThisHandling, def __init__(self, sig, name, descriptorProvider, needThisHandling,
rethrowContentException=False, typedArraysAreStructs=False, rethrowContentException=False,
spiderMonkeyInterfacesAreStructs=False,
wrapScope='CallbackKnownNotGray()'): wrapScope='CallbackKnownNotGray()'):
""" """
needThisHandling is True if we need to be able to accept a specified needThisHandling is True if we need to be able to accept a specified
@ -15636,7 +15649,7 @@ class CallbackMember(CGNativeMember):
extendedAttrs={}, extendedAttrs={},
passJSBitsAsNeeded=False, passJSBitsAsNeeded=False,
visibility=visibility, visibility=visibility,
typedArraysAreStructs=typedArraysAreStructs) spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
# We have to do all the generation of our body now, because # We have to do all the generation of our body now, because
# the caller relies on us throwing if we can't manage it. # the caller relies on us throwing if we can't manage it.
self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n" self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
@ -15753,7 +15766,7 @@ class CallbackMember(CGNativeMember):
'obj': self.wrapScope, 'obj': self.wrapScope,
'returnsNewObject': False, 'returnsNewObject': False,
'exceptionCode': self.exceptionCode, 'exceptionCode': self.exceptionCode,
'typedArraysAreStructs': self.typedArraysAreStructs 'spiderMonkeyInterfacesAreStructs': self.spiderMonkeyInterfacesAreStructs
}) })
except MethodNotNewObjectError as err: except MethodNotNewObjectError as err:
raise TypeError("%s being passed as an argument to %s but is not " raise TypeError("%s being passed as an argument to %s but is not "
@ -15857,10 +15870,11 @@ class CallbackMember(CGNativeMember):
class CallbackMethod(CallbackMember): class CallbackMethod(CallbackMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling, def __init__(self, sig, name, descriptorProvider, needThisHandling,
rethrowContentException=False, typedArraysAreStructs=False): rethrowContentException=False,
spiderMonkeyInterfacesAreStructs=False):
CallbackMember.__init__(self, sig, name, descriptorProvider, CallbackMember.__init__(self, sig, name, descriptorProvider,
needThisHandling, rethrowContentException, needThisHandling, rethrowContentException,
typedArraysAreStructs=typedArraysAreStructs) spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self): def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@ -15919,12 +15933,12 @@ class CallbackOperationBase(CallbackMethod):
""" """
def __init__(self, signature, jsName, nativeName, descriptor, def __init__(self, signature, jsName, nativeName, descriptor,
singleOperation, rethrowContentException=False, singleOperation, rethrowContentException=False,
typedArraysAreStructs=False): spiderMonkeyInterfacesAreStructs=False):
self.singleOperation = singleOperation self.singleOperation = singleOperation
self.methodName = descriptor.binaryNameFor(jsName) self.methodName = descriptor.binaryNameFor(jsName)
CallbackMethod.__init__(self, signature, nativeName, descriptor, CallbackMethod.__init__(self, signature, nativeName, descriptor,
singleOperation, rethrowContentException, singleOperation, rethrowContentException,
typedArraysAreStructs=typedArraysAreStructs) spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getThisDecl(self): def getThisDecl(self):
if not self.singleOperation: if not self.singleOperation:
@ -15975,7 +15989,8 @@ class CallbackOperation(CallbackOperationBase):
""" """
Codegen actual WebIDL operations on callback interfaces. 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.ensureASCIIName(method)
self.method = method self.method = method
jsName = method.identifier.name jsName = method.identifier.name
@ -15984,7 +15999,7 @@ class CallbackOperation(CallbackOperationBase):
MakeNativeName(descriptor.binaryNameFor(jsName)), MakeNativeName(descriptor.binaryNameFor(jsName)),
descriptor, descriptor.interface.isSingleOperationInterface(), descriptor, descriptor.interface.isSingleOperationInterface(),
rethrowContentException=descriptor.interface.isJSImplemented(), rethrowContentException=descriptor.interface.isJSImplemented(),
typedArraysAreStructs=typedArraysAreStructs) spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self): def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name, return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@ -15995,13 +16010,14 @@ class CallbackAccessor(CallbackMember):
""" """
Shared superclass for CallbackGetter and CallbackSetter. 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.ensureASCIIName(attr)
self.attrName = attr.identifier.name self.attrName = attr.identifier.name
CallbackMember.__init__(self, sig, name, descriptor, CallbackMember.__init__(self, sig, name, descriptor,
needThisHandling=False, needThisHandling=False,
rethrowContentException=descriptor.interface.isJSImplemented(), rethrowContentException=descriptor.interface.isJSImplemented(),
typedArraysAreStructs=typedArraysAreStructs) spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self): def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name, return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@ -16009,12 +16025,12 @@ class CallbackAccessor(CallbackMember):
class CallbackGetter(CallbackAccessor): class CallbackGetter(CallbackAccessor):
def __init__(self, attr, descriptor, typedArraysAreStructs): def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr, CallbackAccessor.__init__(self, attr,
(attr.type, []), (attr.type, []),
callbackGetterName(attr, descriptor), callbackGetterName(attr, descriptor),
descriptor, descriptor,
typedArraysAreStructs) spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self): def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@ -16036,12 +16052,12 @@ class CallbackGetter(CallbackAccessor):
class CallbackSetter(CallbackAccessor): class CallbackSetter(CallbackAccessor):
def __init__(self, attr, descriptor, typedArraysAreStructs): def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr, CallbackAccessor.__init__(self, attr,
(BuiltinTypes[IDLBuiltinType.Types.void], (BuiltinTypes[IDLBuiltinType.Types.void],
[FakeArgument(attr.type, attr)]), [FakeArgument(attr.type, attr)]),
callbackSetterName(attr, descriptor), callbackSetterName(attr, descriptor),
descriptor, typedArraysAreStructs) descriptor, spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self): def getRvalDecl(self):
# We don't need an rval # We don't need an rval
@ -16076,7 +16092,7 @@ class CGJSImplInitOperation(CallbackOperationBase):
"__init", "__Init", descriptor, "__init", "__Init", descriptor,
singleOperation=False, singleOperation=False,
rethrowContentException=True, rethrowContentException=True,
typedArraysAreStructs=True) spiderMonkeyInterfacesAreStructs=True)
def getPrettyName(self): def getPrettyName(self):
return "__init" return "__init"

View file

@ -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 */

View file

@ -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 */

View file

@ -6,51 +6,15 @@
#ifndef mozilla_dom_TypedArray_h #ifndef mozilla_dom_TypedArray_h
#define 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/Attributes.h"
#include "mozilla/Move.h" #include "mozilla/Move.h"
#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/SpiderMonkeyInterface.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
namespace mozilla { namespace mozilla {
namespace dom { 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 * 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 * that has a way of initializing a TypedArray from an existing typed array, and
@ -60,7 +24,9 @@ private:
template<typename T, template<typename T,
JSObject* UnwrapArray(JSObject*), JSObject* UnwrapArray(JSObject*),
void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)> void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
struct TypedArray_base : public TypedArrayObjectStorage { struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
AllTypedArraysBase
{
typedef T element_type; typedef T element_type;
TypedArray_base() TypedArray_base()
@ -72,7 +38,7 @@ struct TypedArray_base : public TypedArrayObjectStorage {
} }
TypedArray_base(TypedArray_base&& aOther) TypedArray_base(TypedArray_base&& aOther)
: TypedArrayObjectStorage(Move(aOther)), : SpiderMonkeyInterfaceObjectStorage(Move(aOther)),
mData(aOther.mData), mData(aOther.mData),
mLength(aOther.mLength), mLength(aOther.mLength),
mShared(aOther.mShared), mShared(aOther.mShared),
@ -94,14 +60,10 @@ public:
inline bool Init(JSObject* obj) inline bool Init(JSObject* obj)
{ {
MOZ_ASSERT(!inited()); MOZ_ASSERT(!inited());
mTypedObj = mWrappedObj = UnwrapArray(obj); mImplObj = mWrappedObj = UnwrapArray(obj);
return inited(); return inited();
} }
inline bool inited() const {
return !!mTypedObj;
}
// About shared memory: // About shared memory:
// //
// Any DOM TypedArray as well as any DOM ArrayBufferView that does // Any DOM TypedArray as well as any DOM ArrayBufferView that does
@ -173,22 +135,11 @@ public:
return mLength; 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 inline void ComputeLengthAndData() const
{ {
MOZ_ASSERT(inited()); MOZ_ASSERT(inited());
MOZ_ASSERT(!mComputed); MOZ_ASSERT(!mComputed);
GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData); GetLengthAndDataAndSharedness(mImplObj, &mLength, &mShared, &mData);
mComputed = true; mComputed = true;
} }
@ -363,77 +314,6 @@ class TypedArrayCreator
const ArrayType& mArray; 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 dom
} // namespace mozilla } // namespace mozilla

View file

@ -38,9 +38,11 @@ EXPORTS.mozilla.dom += [
'NonRefcountedDOMObject.h', 'NonRefcountedDOMObject.h',
'Nullable.h', 'Nullable.h',
'PrimitiveConversions.h', 'PrimitiveConversions.h',
'ReadableStream.h',
'Record.h', 'Record.h',
'RootedDictionary.h', 'RootedDictionary.h',
'SimpleGlobalObject.h', 'SimpleGlobalObject.h',
'SpiderMonkeyInterface.h',
'StructuredClone.h', 'StructuredClone.h',
'ToJSValue.h', 'ToJSValue.h',
'TypedArray.h', 'TypedArray.h',

View file

@ -2054,6 +2054,9 @@ class IDLType(IDLObject):
def isRecord(self): def isRecord(self):
return False return False
def isReadableStream(self):
return False
def isArrayBuffer(self): def isArrayBuffer(self):
return False return False
@ -2081,12 +2084,12 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self): def isSpiderMonkeyInterface(self):
""" Returns a boolean indicating whether this type is an 'interface' """ Returns a boolean indicating whether this type is an 'interface'
type that is implemented in Spidermonkey. At the moment, this type that is implemented in SpiderMonkey. """
only returns true for the types from the TypedArray spec. """
return self.isInterface() and (self.isArrayBuffer() or return self.isInterface() and (self.isArrayBuffer() or
self.isArrayBufferView() or self.isArrayBufferView() or
self.isSharedArrayBuffer() or self.isSharedArrayBuffer() or
self.isTypedArray()) self.isTypedArray() or
self.isReadableStream())
def isDictionary(self): def isDictionary(self):
return False return False
@ -2275,6 +2278,9 @@ class IDLNullableType(IDLParameterizedType):
def isRecord(self): def isRecord(self):
return self.inner.isRecord() return self.inner.isRecord()
def isReadableStream(self):
return self.inner.isReadableStream()
def isArrayBuffer(self): def isArrayBuffer(self):
return self.inner.isArrayBuffer() return self.inner.isArrayBuffer()
@ -2634,6 +2640,9 @@ class IDLTypedefType(IDLType):
def isRecord(self): def isRecord(self):
return self.inner.isRecord() return self.inner.isRecord()
def isReadableStream(self):
return self.inner.isReadableStream()
def isDictionary(self): def isDictionary(self):
return self.inner.isDictionary() return self.inner.isDictionary()
@ -2945,7 +2954,8 @@ class IDLBuiltinType(IDLType):
'Int32Array', 'Int32Array',
'Uint32Array', 'Uint32Array',
'Float32Array', 'Float32Array',
'Float64Array' 'Float64Array',
'ReadableStream',
) )
TagLookup = { TagLookup = {
@ -2980,7 +2990,8 @@ class IDLBuiltinType(IDLType):
Types.Int32Array: IDLType.Tags.interface, Types.Int32Array: IDLType.Tags.interface,
Types.Uint32Array: IDLType.Tags.interface, Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: 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): def __init__(self, location, name, type):
@ -3027,6 +3038,9 @@ class IDLBuiltinType(IDLType):
return (self._typeTag >= IDLBuiltinType.Types.Int8Array and return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
self._typeTag <= IDLBuiltinType.Types.Float64Array) self._typeTag <= IDLBuiltinType.Types.Float64Array)
def isReadableStream(self):
return self._typeTag == IDLBuiltinType.Types.ReadableStream
def isInterface(self): def isInterface(self):
# TypedArray things are interface types per the TypedArray spec, # TypedArray things are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements # but we handle them as builtins because SpiderMonkey implements
@ -3034,7 +3048,8 @@ class IDLBuiltinType(IDLType):
return (self.isArrayBuffer() or return (self.isArrayBuffer() or
self.isArrayBufferView() or self.isArrayBufferView() or
self.isSharedArrayBuffer() or self.isSharedArrayBuffer() or
self.isTypedArray()) self.isTypedArray() or
self.isReadableStream())
def isNonCallbackInterface(self): def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback # 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 # that's not an ArrayBuffer or a callback interface
(self.isArrayBuffer() and not other.isArrayBuffer()) or (self.isArrayBuffer() and not other.isArrayBuffer()) or
(self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
(self.isReadableStream() and not other.isReadableStream()) or
# ArrayBufferView is distinguishable from everything # ArrayBufferView is distinguishable from everything
# that's not an ArrayBufferView or typed array. # that's not an ArrayBufferView or typed array.
(self.isArrayBufferView() and not other.isArrayBufferView() and (self.isArrayBufferView() and not other.isArrayBufferView() and
@ -3213,7 +3229,10 @@ BuiltinTypes = {
IDLBuiltinType.Types.Float32Array), IDLBuiltinType.Types.Float32Array),
IDLBuiltinType.Types.Float64Array: IDLBuiltinType.Types.Float64Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "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", "maplike": "MAPLIKE",
"setlike": "SETLIKE", "setlike": "SETLIKE",
"iterable": "ITERABLE", "iterable": "ITERABLE",
"namespace": "NAMESPACE" "namespace": "NAMESPACE",
"ReadableStream": "READABLESTREAM",
} }
tokens.extend(keywords.values()) tokens.extend(keywords.values())
@ -6420,6 +6440,7 @@ class Parser(Tokenizer):
NonAnyType : PrimitiveType Null NonAnyType : PrimitiveType Null
| ARRAYBUFFER Null | ARRAYBUFFER Null
| SHAREDARRAYBUFFER Null | SHAREDARRAYBUFFER Null
| READABLESTREAM Null
| OBJECT Null | OBJECT Null
""" """
if p[1] == "object": if p[1] == "object":
@ -6428,6 +6449,8 @@ class Parser(Tokenizer):
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
elif p[1] == "SharedArrayBuffer": elif p[1] == "SharedArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer] type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
elif p[1] == "ReadableStream":
type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
else: else:
type = BuiltinTypes[p[1]] type = BuiltinTypes[p[1]]

View file

@ -289,9 +289,9 @@ MatchInPutList(InternalRequest* aRequest,
} // namespace } // namespace
void void
AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, AutoChildOpArgs::Add(JSContext* aCx, InternalRequest* aRequest,
SchemeAction aSchemeAction, Response& aResponse, BodyAction aBodyAction, SchemeAction aSchemeAction,
ErrorResult& aRv) Response& aResponse, ErrorResult& aRv)
{ {
MOZ_DIAGNOSTIC_ASSERT(!mSent); MOZ_DIAGNOSTIC_ASSERT(!mSent);
@ -329,7 +329,7 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction, mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
aSchemeAction, mStreamCleanupList, aRv); aSchemeAction, mStreamCleanupList, aRv);
if (!aRv.Failed()) { if (!aRv.Failed()) {
mTypeUtils->ToCacheResponse(pair.response(), aResponse, mTypeUtils->ToCacheResponse(aCx, pair.response(), aResponse,
mStreamCleanupList, aRv); mStreamCleanupList, aRv);
} }

View file

@ -55,7 +55,7 @@ public:
void Add(InternalRequest* aRequest, BodyAction aBodyAction, void Add(InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, ErrorResult& aRv); SchemeAction aSchemeAction, ErrorResult& aRv);
void Add(InternalRequest* aRequest, BodyAction aBodyAction, void Add(JSContext* aCx, InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv); SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv);
const CacheOpArgs& SendAsOpArgs(); const CacheOpArgs& SendAsOpArgs();

40
dom/cache/Cache.cpp vendored
View file

@ -184,7 +184,10 @@ public:
// Now store the unwrapped Response list in the Cache. // Now store the unwrapped Response list in the Cache.
ErrorResult result; 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())) { if (NS_WARN_IF(result.Failed())) {
// TODO: abort the fetch requests we have running (bug 1157434) // TODO: abort the fetch requests we have running (bug 1157434)
mPromise->MaybeReject(result); mPromise->MaybeReject(result);
@ -245,7 +248,7 @@ Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::Match(const RequestOrUSVString& aRequest, Cache::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) { if (NS_WARN_IF(!mActor)) {
@ -255,7 +258,8 @@ Cache::Match(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor); 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())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -274,7 +278,7 @@ Cache::Match(const RequestOrUSVString& aRequest,
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest, Cache::MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) { if (NS_WARN_IF(!mActor)) {
@ -290,8 +294,8 @@ Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1); AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1);
if (aRequest.WasPassed()) { if (aRequest.WasPassed()) {
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(), RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest.Value(),
IgnoreBody, aRv); IgnoreBody, aRv);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }
@ -390,8 +394,8 @@ Cache::AddAll(JSContext* aContext,
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse, Cache::Put(JSContext* aCx, const RequestOrUSVString& aRequest,
ErrorResult& aRv) Response& aResponse, ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) { if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
@ -404,14 +408,14 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
return nullptr; return nullptr;
} }
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv); RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest, ReadBody, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
AutoChildOpArgs args(this, CachePutAllArgs(), 1); AutoChildOpArgs args(this, CachePutAllArgs(), 1);
args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, args.Add(aCx, ir, ReadBody, TypeErrorOnInvalidScheme,
aResponse, aRv); aResponse, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
@ -421,7 +425,7 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::Delete(const RequestOrUSVString& aRequest, Cache::Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) { if (NS_WARN_IF(!mActor)) {
@ -431,7 +435,8 @@ Cache::Delete(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor); 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())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -450,7 +455,7 @@ Cache::Delete(const RequestOrUSVString& aRequest,
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::Keys(const Optional<RequestOrUSVString>& aRequest, Cache::Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
if (NS_WARN_IF(!mActor)) { if (NS_WARN_IF(!mActor)) {
@ -466,8 +471,8 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1); AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1);
if (aRequest.WasPassed()) { if (aRequest.WasPassed()) {
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(), RefPtr<InternalRequest> ir =
IgnoreBody, aRv); ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -627,7 +632,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList, Cache::PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList, const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv) ErrorResult& aRv)
{ {
@ -644,7 +649,8 @@ Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
for (uint32_t i = 0; i < aRequestList.Length(); ++i) { for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
RefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest(); 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())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }

16
dom/cache/Cache.h vendored
View file

@ -43,10 +43,10 @@ public:
// webidl interface methods // webidl interface methods
already_AddRefed<Promise> already_AddRefed<Promise>
Match(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions, Match(JSContext* aCx, const RequestOrUSVString& aRequest,
ErrorResult& aRv); const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
MatchAll(const Optional<RequestOrUSVString>& aRequest, MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv); const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
Add(JSContext* aContext, const RequestOrUSVString& aRequest, Add(JSContext* aContext, const RequestOrUSVString& aRequest,
@ -55,13 +55,13 @@ public:
AddAll(JSContext* aContext, AddAll(JSContext* aContext,
const Sequence<OwningRequestOrUSVString>& aRequests, ErrorResult& aRv); const Sequence<OwningRequestOrUSVString>& aRequests, ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
Put(const RequestOrUSVString& aRequest, Response& aResponse, Put(JSContext* aCx, const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv); ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
Delete(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions, Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
ErrorResult& aRv); const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
Keys(const Optional<RequestOrUSVString>& aRequest, Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aParams, ErrorResult& aRv); const CacheQueryOptions& aParams, ErrorResult& aRv);
// binding methods // binding methods
@ -100,7 +100,7 @@ private:
ErrorResult& aRv); ErrorResult& aRv);
already_AddRefed<Promise> already_AddRefed<Promise>
PutAll(const nsTArray<RefPtr<Request>>& aRequestList, PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList, const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv); ErrorResult& aRv);

View file

@ -74,7 +74,11 @@ CacheOpChild::CacheOpChild(CacheWorkerHolder* aWorkerHolder,
MOZ_DIAGNOSTIC_ASSERT(mPromise); MOZ_DIAGNOSTIC_ASSERT(mPromise);
MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder); MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
SetWorkerHolder(aWorkerHolder); RefPtr<CacheWorkerHolder> workerHolder =
CacheWorkerHolder::PreferBehavior(aWorkerHolder,
CacheWorkerHolder::PreventIdleShutdownStart);
SetWorkerHolder(workerHolder);
} }
CacheOpChild::~CacheOpChild() CacheOpChild::~CacheOpChild()
@ -165,7 +169,11 @@ CacheOpChild::Recv__delete__(const ErrorResult& aRv,
break; break;
} }
actor->SetWorkerHolder(GetWorkerHolder()); RefPtr<CacheWorkerHolder> workerHolder =
CacheWorkerHolder::PreferBehavior(GetWorkerHolder(),
CacheWorkerHolder::AllowIdleShutdownStart);
actor->SetWorkerHolder(workerHolder);
RefPtr<Cache> cache = new Cache(mGlobal, actor); RefPtr<Cache> cache = new Cache(mGlobal, actor);
mPromise->MaybeResolve(cache); mPromise->MaybeResolve(cache);
break; break;

View file

@ -204,7 +204,8 @@ CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
} }
RefPtr<CacheWorkerHolder> workerHolder = RefPtr<CacheWorkerHolder> workerHolder =
CacheWorkerHolder::Create(aWorkerPrivate); CacheWorkerHolder::Create(aWorkerPrivate,
CacheWorkerHolder::AllowIdleShutdownStart);
if (!workerHolder) { if (!workerHolder) {
NS_WARNING("Worker thread is shutting down."); NS_WARNING("Worker thread is shutting down.");
aRv.Throw(NS_ERROR_FAILURE); aRv.Throw(NS_ERROR_FAILURE);
@ -315,7 +316,7 @@ CacheStorage::CacheStorage(nsresult aFailureResult)
} }
already_AddRefed<Promise> already_AddRefed<Promise>
CacheStorage::Match(const RequestOrUSVString& aRequest, CacheStorage::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv) const CacheQueryOptions& aOptions, ErrorResult& aRv)
{ {
NS_ASSERT_OWNINGTHREAD(CacheStorage); NS_ASSERT_OWNINGTHREAD(CacheStorage);
@ -325,8 +326,8 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
return nullptr; return nullptr;
} }
RefPtr<InternalRequest> request = ToInternalRequest(aRequest, IgnoreBody, RefPtr<InternalRequest> request =
aRv); ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }

View file

@ -59,9 +59,9 @@ public:
DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal); DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
// webidl interface methods // webidl interface methods
already_AddRefed<Promise> Match(const RequestOrUSVString& aRequest, already_AddRefed<Promise>
const CacheQueryOptions& aOptions, Match(JSContext* aCx, const RequestOrUSVString& aRequest,
ErrorResult& aRv); const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> Has(const nsAString& aKey, ErrorResult& aRv); already_AddRefed<Promise> Has(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Open(const nsAString& aKey, ErrorResult& aRv); already_AddRefed<Promise> Open(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Delete(const nsAString& aKey, ErrorResult& aRv); already_AddRefed<Promise> Delete(const nsAString& aKey, ErrorResult& aRv);

View file

@ -18,11 +18,11 @@ using mozilla::dom::workers::WorkerPrivate;
// static // static
already_AddRefed<CacheWorkerHolder> already_AddRefed<CacheWorkerHolder>
CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate) CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate, Behavior aBehavior)
{ {
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate); MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
RefPtr<CacheWorkerHolder> workerHolder = new CacheWorkerHolder(); RefPtr<CacheWorkerHolder> workerHolder = new CacheWorkerHolder(aBehavior);
if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate, Terminating))) { if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate, Terminating))) {
return nullptr; return nullptr;
} }
@ -30,6 +30,28 @@ CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate)
return workerHolder.forget(); 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 void
CacheWorkerHolder::AddActor(ActorChild* aActor) CacheWorkerHolder::AddActor(ActorChild* aActor)
{ {
@ -88,8 +110,9 @@ CacheWorkerHolder::Notify(Status aStatus)
return true; return true;
} }
CacheWorkerHolder::CacheWorkerHolder() CacheWorkerHolder::CacheWorkerHolder(Behavior aBehavior)
: mNotified(false) : WorkerHolder(aBehavior)
, mNotified(false)
{ {
} }

View file

@ -25,7 +25,11 @@ class CacheWorkerHolder final : public workers::WorkerHolder
{ {
public: public:
static already_AddRefed<CacheWorkerHolder> 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 AddActor(ActorChild* aActor);
void RemoveActor(ActorChild* aActor); void RemoveActor(ActorChild* aActor);
@ -36,7 +40,7 @@ public:
virtual bool Notify(workers::Status aStatus) override; virtual bool Notify(workers::Status aStatus) override;
private: private:
CacheWorkerHolder(); explicit CacheWorkerHolder(Behavior aBehavior);
~CacheWorkerHolder(); ~CacheWorkerHolder();
nsTArray<ActorChild*> mActorList; nsTArray<ActorChild*> mActorList;

View file

@ -79,7 +79,7 @@ ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
} // namespace } // namespace
already_AddRefed<InternalRequest> already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn, TypeUtils::ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv) BodyAction aBodyAction, ErrorResult& aRv)
{ {
if (aIn.IsRequest()) { if (aIn.IsRequest()) {
@ -87,7 +87,7 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request // Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest. // instead of InternalRequest.
CheckAndSetBodyUsed(&request, aBodyAction, aRv); CheckAndSetBodyUsed(aCx, &request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; } if (aRv.Failed()) { return nullptr; }
return request.GetInternalRequest(); return request.GetInternalRequest();
@ -97,7 +97,8 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
} }
already_AddRefed<InternalRequest> already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn, TypeUtils::ToInternalRequest(JSContext* aCx,
const OwningRequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv) BodyAction aBodyAction, ErrorResult& aRv)
{ {
@ -106,7 +107,7 @@ TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request // Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest. // instead of InternalRequest.
CheckAndSetBodyUsed(request, aBodyAction, aRv); CheckAndSetBodyUsed(aCx, request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; } if (aRv.Failed()) { return nullptr; }
return request->GetInternalRequest(); return request->GetInternalRequest();
@ -204,7 +205,7 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
} }
void void
TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn, TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList, nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv) ErrorResult& aRv)
{ {
@ -222,7 +223,10 @@ TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
nsCOMPtr<nsIInputStream> stream; nsCOMPtr<nsIInputStream> stream;
ir->GetUnfilteredBody(getter_AddRefs(stream)); ir->GetUnfilteredBody(getter_AddRefs(stream));
if (stream) { if (stream) {
aIn.SetBodyUsed(); aIn.SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
} }
SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv); SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
@ -426,8 +430,8 @@ TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
} }
void void
TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction, TypeUtils::CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest,
ErrorResult& aRv) BodyAction aBodyAction, ErrorResult& aRv)
{ {
MOZ_DIAGNOSTIC_ASSERT(aRequest); MOZ_DIAGNOSTIC_ASSERT(aRequest);
@ -443,7 +447,10 @@ TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
nsCOMPtr<nsIInputStream> stream; nsCOMPtr<nsIInputStream> stream;
aRequest->GetBody(getter_AddRefs(stream)); aRequest->GetBody(getter_AddRefs(stream));
if (stream) { if (stream) {
aRequest->SetBodyUsed(); aRequest->SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
} }
} }

12
dom/cache/TypeUtils.h vendored
View file

@ -73,12 +73,12 @@ public:
GetIPCManager() = 0; GetIPCManager() = 0;
already_AddRefed<InternalRequest> already_AddRefed<InternalRequest>
ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction, ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
ErrorResult& aRv); BodyAction aBodyAction, ErrorResult& aRv);
already_AddRefed<InternalRequest> already_AddRefed<InternalRequest>
ToInternalRequest(const OwningRequestOrUSVString& aIn, BodyAction aBodyAction, ToInternalRequest(JSContext* aCx, const OwningRequestOrUSVString& aIn,
ErrorResult& aRv); BodyAction aBodyAction, ErrorResult& aRv);
void void
ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn, ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
@ -91,7 +91,7 @@ public:
ErrorResult& aRv); ErrorResult& aRv);
void void
ToCacheResponse(CacheResponse& aOut, Response& aIn, ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList, nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv); ErrorResult& aRv);
@ -133,7 +133,7 @@ public:
private: private:
void void
CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction, CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv); ErrorResult& aRv);
already_AddRefed<InternalRequest> already_AddRefed<InternalRequest>

View file

@ -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 // 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 // using float precision. Bypass canvas API calls when the input is out of
// float precision to avoid precision problem // float precision to avoid precision problem
if (!std::isfinite((float)aX) | !std::isfinite((float)aY) | if (!std::isfinite((float)aX) || !std::isfinite((float)aY) ||
!std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) { !std::isfinite((float)aWidth) || !std::isfinite((float)aHeight)) {
return false; return false;
} }
@ -5973,7 +5973,7 @@ void
CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx, CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDy, ErrorResult& aError) double aDy, ErrorResult& aError)
{ {
RootedTypedArray<Uint8ClampedArray> arr(RootingCx()); RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject()); DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited); MOZ_ASSERT(inited);
@ -5989,7 +5989,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDirtyHeight, double aDirtyHeight,
ErrorResult& aError) ErrorResult& aError)
{ {
RootedTypedArray<Uint8ClampedArray> arr(RootingCx()); RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject()); DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited); MOZ_ASSERT(inited);

View file

@ -929,7 +929,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
{ {
// Copy data into SourceSurface. // Copy data into SourceSurface.
RootedTypedArray<Uint8ClampedArray> array(RootingCx()); RootedSpiderMonkeyInterface<Uint8ClampedArray> array(RootingCx());
DebugOnly<bool> inited = array.Init(aImageData.GetDataObject()); DebugOnly<bool> inited = array.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited); MOZ_ASSERT(inited);

View file

@ -475,7 +475,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
GLsizei depth, GLint border, const webgl::PackingInfo& pi, GLsizei depth, GLint border, const webgl::PackingInfo& pi,
const TexImageSource& src) 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, const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr); depth, border, pi, src, &scopedArr);
if (!blob) if (!blob)
@ -491,7 +491,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
const webgl::PackingInfo& pi, const TexImageSource& src) const webgl::PackingInfo& pi, const TexImageSource& src)
{ {
const GLint border = 0; 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, const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr); depth, border, pi, src, &scopedArr);
if (!blob) if (!blob)

View file

@ -1365,7 +1365,7 @@ public:
mDataIsJwk = false; mDataIsJwk = false;
// Try ArrayBuffer // Try ArrayBuffer
RootedTypedArray<ArrayBuffer> ab(aCx); RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
if (ab.Init(aKeyData)) { if (ab.Init(aKeyData)) {
if (!mKeyData.Assign(ab)) { if (!mKeyData.Assign(ab)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR; mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
@ -1374,7 +1374,7 @@ public:
} }
// Try ArrayBufferView // Try ArrayBufferView
RootedTypedArray<ArrayBufferView> abv(aCx); RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
if (abv.Init(aKeyData)) { if (abv.Init(aKeyData)) {
if (!mKeyData.Assign(abv)) { if (!mKeyData.Assign(abv)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR; mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;

207
dom/fetch/BodyExtractor.cpp Normal file
View file

@ -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

48
dom/fetch/BodyExtractor.h Normal file
View file

@ -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

View file

@ -5,13 +5,13 @@
#include "Fetch.h" #include "Fetch.h"
#include "FetchConsumer.h" #include "FetchConsumer.h"
#include "FetchStream.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsIGlobalObject.h" #include "nsIGlobalObject.h"
#include "nsIStreamLoader.h" #include "nsIStreamLoader.h"
#include "nsIThreadRetargetableRequest.h" #include "nsIThreadRetargetableRequest.h"
#include "nsIUnicodeDecoder.h" #include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h"
#include "nsDOMString.h" #include "nsDOMString.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
@ -22,7 +22,7 @@
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/BodyUtil.h"
#include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/DOMError.h"
#include "mozilla/dom/Exceptions.h" #include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FetchDriver.h" #include "mozilla/dom/FetchDriver.h"
#include "mozilla/dom/File.h" #include "mozilla/dom/File.h"
@ -35,8 +35,10 @@
#include "mozilla/dom/Response.h" #include "mozilla/dom/Response.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/URLSearchParams.h" #include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h" #include "mozilla/dom/workers/ServiceWorkerManager.h"
#include "BodyExtractor.h"
#include "FetchObserver.h" #include "FetchObserver.h"
#include "InternalRequest.h" #include "InternalRequest.h"
#include "InternalResponse.h" #include "InternalResponse.h"
@ -722,154 +724,48 @@ WorkerFetchResolver::FlushConsoleReport()
mReporter->FlushConsoleReports(worker->GetDocument()); mReporter->FlushConsoleReports(worker->GetDocument());
} }
namespace {
nsresult nsresult
ExtractFromArrayBuffer(const ArrayBuffer& aBuffer, ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
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)
{
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
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();
}
impl->GetInternalStream(aStream, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
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;
}
int32_t destBufferLen;
nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.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) aStr.Length();
int32_t outLen = destBufferLen;
rv = encoder->Convert(aStr.get(), &srcLen, destBuffer, &outLen);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
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));
}
} // namespace
nsresult
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream, nsIInputStream** aStream,
nsCString& aContentType, nsCString& aContentTypeWithCharset,
uint64_t& aContentLength) uint64_t& aContentLength)
{ {
MOZ_ASSERT(aStream); MOZ_ASSERT(aStream);
nsAutoCString charset;
aContentTypeWithCharset.SetIsVoid(true);
if (aBodyInit.IsArrayBuffer()) { if (aBodyInit.IsArrayBuffer()) {
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer(); BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
return ExtractFromArrayBuffer(buf, aStream, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsArrayBufferView()) { if (aBodyInit.IsArrayBufferView()) {
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView(); BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
return ExtractFromArrayBufferView(buf, aStream, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsBlob()) { if (aBodyInit.IsBlob()) {
const Blob& blob = aBodyInit.GetAsBlob(); Blob& blob = aBodyInit.GetAsBlob();
return ExtractFromBlob(blob, aStream, aContentType, aContentLength); BodyExtractor<nsIXHRSendable> body(&blob);
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsFormData()) { if (aBodyInit.IsFormData()) {
FormData& form = aBodyInit.GetAsFormData(); FormData& formData = aBodyInit.GetAsFormData();
return ExtractFromFormData(form, aStream, aContentType, aContentLength); BodyExtractor<nsIXHRSendable> body(&formData);
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsUSVString()) { if (aBodyInit.IsUSVString()) {
nsAutoString str; BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
str.Assign(aBodyInit.GetAsUSVString()); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
return ExtractFromUSVString(str, aStream, aContentType, aContentLength); charset);
} }
if (aBodyInit.IsURLSearchParams()) { if (aBodyInit.IsURLSearchParams()) {
URLSearchParams& params = aBodyInit.GetAsURLSearchParams(); URLSearchParams& usp = aBodyInit.GetAsURLSearchParams();
return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength); BodyExtractor<nsIXHRSendable> body(&usp);
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
NS_NOTREACHED("Should never reach here"); NS_NOTREACHED("Should never reach here");
@ -877,69 +773,228 @@ ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDa
} }
nsresult nsresult
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit, ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
nsIInputStream** aStream, nsIInputStream** aStream,
nsCString& aContentType, nsCString& aContentTypeWithCharset,
uint64_t& aContentLength) uint64_t& aContentLength)
{ {
MOZ_ASSERT(aStream); MOZ_ASSERT(aStream);
MOZ_ASSERT(!*aStream); MOZ_ASSERT(!*aStream);
nsAutoCString charset;
aContentTypeWithCharset.SetIsVoid(true);
if (aBodyInit.IsArrayBuffer()) { if (aBodyInit.IsArrayBuffer()) {
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer(); BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
return ExtractFromArrayBuffer(buf, aStream, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsArrayBufferView()) { if (aBodyInit.IsArrayBufferView()) {
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView(); BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
return ExtractFromArrayBufferView(buf, aStream, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsBlob()) { if (aBodyInit.IsBlob()) {
const Blob& blob = aBodyInit.GetAsBlob(); BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsBlob());
return ExtractFromBlob(blob, aStream, aContentType, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsFormData()) { if (aBodyInit.IsFormData()) {
FormData& form = aBodyInit.GetAsFormData(); BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsFormData());
return ExtractFromFormData(form, aStream, aContentType, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
if (aBodyInit.IsUSVString()) { if (aBodyInit.IsUSVString()) {
nsAutoString str; BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
str.Assign(aBodyInit.GetAsUSVString()); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
return ExtractFromUSVString(str, aStream, aContentType, aContentLength); charset);
} }
if (aBodyInit.IsURLSearchParams()) { if (aBodyInit.IsURLSearchParams()) {
URLSearchParams& params = aBodyInit.GetAsURLSearchParams(); BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsURLSearchParams());
return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength); return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
} }
NS_NOTREACHED("Should never reach here"); NS_NOTREACHED("Should never reach here");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsresult
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
nsIInputStream** aStream,
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()) {
BodyExtractor<const ArrayBuffer> body(&aBodyInit.GetAsArrayBuffer());
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
}
if (aBodyInit.IsArrayBufferView()) {
BodyExtractor<const ArrayBufferView> body(&aBodyInit.GetAsArrayBufferView());
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
}
if (aBodyInit.IsBlob()) {
BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsBlob());
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
}
if (aBodyInit.IsFormData()) {
BodyExtractor<nsIXHRSendable> body(&aBodyInit.GetAsFormData());
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
}
if (aBodyInit.IsUSVString()) {
BodyExtractor<const nsAString> body(&aBodyInit.GetAsUSVString());
return body.GetAsStream(aStream, &aContentLength, aContentTypeWithCharset,
charset);
}
if (aBodyInit.IsURLSearchParams()) {
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> template <class Derived>
FetchBody<Derived>::FetchBody() FetchBody<Derived>::FetchBody(nsIGlobalObject* aOwner)
: mWorkerPrivate(nullptr) : mWorkerPrivate(nullptr)
, mOwner(aOwner)
, mReadableStreamBody(nullptr)
, mReadableStreamReader(nullptr)
, mBodyUsed(false) , mBodyUsed(false)
{ {
MOZ_ASSERT(aOwner);
if (!NS_IsMainThread()) { if (!NS_IsMainThread()) {
mWorkerPrivate = GetCurrentThreadWorkerPrivate(); mWorkerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(mWorkerPrivate); MOZ_ASSERT(mWorkerPrivate);
} else {
mWorkerPrivate = nullptr;
} }
} }
template template
FetchBody<Request>::FetchBody(); FetchBody<Request>::FetchBody(nsIGlobalObject* aOwner);
template template
FetchBody<Response>::FetchBody(); FetchBody<Response>::FetchBody(nsIGlobalObject* aOwner);
template <class Derived> template <class Derived>
FetchBody<Derived>::~FetchBody() 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> template <class Derived>
already_AddRefed<Promise> already_AddRefed<Promise>
FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv) FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv)
{ {
RefPtr<AbortSignal> signal = DerivedClass()->GetSignal(); RefPtr<AbortSignal> signal = DerivedClass()->GetSignal();
if (signal && signal->Aborted()) { if (signal && signal->Aborted()) {
@ -952,11 +1007,15 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
return nullptr; return nullptr;
} }
SetBodyUsed(); SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
RefPtr<Promise> promise = RefPtr<Promise> promise =
FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(), FetchBodyConsumer<Derived>::Create(global, this, signal, aType, aRv);
this, signal, aType, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@ -966,11 +1025,11 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
template template
already_AddRefed<Promise> already_AddRefed<Promise>
FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); FetchBody<Request>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template template
already_AddRefed<Promise> already_AddRefed<Promise>
FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); FetchBody<Response>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template <class Derived> template <class Derived>
void void
@ -1000,5 +1059,170 @@ template
void void
FetchBody<Response>::SetMimeType(); 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 dom
} // namespace mozilla } // namespace mozilla

View file

@ -17,17 +17,23 @@
#include "mozilla/DebugOnly.h" #include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.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" #include "mozilla/dom/RequestBinding.h"
class nsIGlobalObject; class nsIGlobalObject;
class nsIEventTarget;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams; class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString;
class BlobImpl; class BlobImpl;
class InternalRequest; class InternalRequest;
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams; class OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
struct ReadableStream;
class RequestOrUSVString; class RequestOrUSVString;
namespace workers { namespace workers {
@ -41,13 +47,20 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
nsresult nsresult
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest); 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 * Creates an nsIInputStream based on the fetch specifications 'extract a byte
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract. * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
* Stores content type in out param aContentType. * Stores content type in out param aContentType.
*/ */
nsresult nsresult
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit, ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
nsIInputStream** aStream, nsIInputStream** aStream,
nsCString& aContentType, nsCString& aContentType,
uint64_t& aContentLength); uint64_t& aContentLength);
@ -56,7 +69,17 @@ ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDa
* Non-owning version. * Non-owning version.
*/ */
nsresult nsresult
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit, 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, nsIInputStream** aStream,
nsCString& aContentType, nsCString& aContentType,
uint64_t& aContentLength); uint64_t& aContentLength);
@ -72,6 +95,19 @@ enum FetchConsumeType
CONSUME_TEXT, 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 * FetchBody's body consumption uses nsIInputStreamPump to read from the
* underlying stream to a block of memory, which is then adopted by * underlying stream to a block of memory, which is then adopted by
@ -106,54 +142,82 @@ enum FetchConsumeType
* The pump is always released on the main thread. * The pump is always released on the main thread.
*/ */
template <class Derived> template <class Derived>
class FetchBody class FetchBody : public FetchStreamHolder
{ {
public: public:
friend class FetchBodyConsumer<Derived>; friend class FetchBodyConsumer<Derived>;
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
bool bool
BodyUsed() const { return mBodyUsed; } BodyUsed() const;
already_AddRefed<Promise> already_AddRefed<Promise>
ArrayBuffer(ErrorResult& aRv) ArrayBuffer(JSContext* aCx, ErrorResult& aRv)
{ {
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv); return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv);
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Blob(ErrorResult& aRv) Blob(JSContext* aCx, ErrorResult& aRv)
{ {
return ConsumeBody(CONSUME_BLOB, aRv); return ConsumeBody(aCx, CONSUME_BLOB, aRv);
} }
already_AddRefed<Promise> already_AddRefed<Promise>
FormData(ErrorResult& aRv) FormData(JSContext* aCx, ErrorResult& aRv)
{ {
return ConsumeBody(CONSUME_FORMDATA, aRv); return ConsumeBody(aCx, CONSUME_FORMDATA, aRv);
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Json(ErrorResult& aRv) Json(JSContext* aCx, ErrorResult& aRv)
{ {
return ConsumeBody(CONSUME_JSON, aRv); return ConsumeBody(aCx, CONSUME_JSON, aRv);
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Text(ErrorResult& aRv) Text(JSContext* aCx, ErrorResult& aRv)
{ {
return ConsumeBody(CONSUME_TEXT, 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. // 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 void
SetBodyUsed() SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
{
mBodyUsed = true;
}
const nsCString& const nsCString&
MimeType() const MimeType() const
@ -161,19 +225,54 @@ public:
return mMimeType; 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* virtual AbortSignal*
GetSignal() const = 0; GetSignal() const = 0;
protected: protected:
FetchBody(); nsCOMPtr<nsIGlobalObject> mOwner;
explicit FetchBody(nsIGlobalObject* aOwner);
// Always set whenever the FetchBody is created on the worker thread. // Always set whenever the FetchBody is created on the worker thread.
workers::WorkerPrivate* mWorkerPrivate; 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(); virtual ~FetchBody();
void void
SetMimeType(); SetMimeType();
void
SetReadableStreamBody(JSObject* aBody);
private: private:
Derived* Derived*
DerivedClass() const DerivedClass() const
@ -182,7 +281,10 @@ private:
} }
already_AddRefed<Promise> already_AddRefed<Promise>
ConsumeBody(FetchConsumeType aType, ErrorResult& aRv); ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
void
LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv);
bool bool
IsOnTargetThread() IsOnTargetThread()
@ -199,6 +301,9 @@ private:
// Only ever set once, always on target thread. // Only ever set once, always on target thread.
bool mBodyUsed; bool mBodyUsed;
nsCString mMimeType; nsCString mMimeType;
// The main-thread event target for runnable dispatching.
nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
}; };
} // namespace dom } // namespace dom

640
dom/fetch/FetchStream.cpp Normal file
View file

@ -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

158
dom/fetch/FetchStream.h Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -139,18 +139,18 @@ InternalResponse::ToIPC(IPCInternalResponse* aIPCResponse,
} }
already_AddRefed<InternalResponse> already_AddRefed<InternalResponse>
InternalResponse::Clone() InternalResponse::Clone(CloneType aCloneType)
{ {
RefPtr<InternalResponse> clone = CreateIncompleteCopy(); RefPtr<InternalResponse> clone = CreateIncompleteCopy();
clone->mHeaders = new InternalHeaders(*mHeaders); clone->mHeaders = new InternalHeaders(*mHeaders);
if (mWrappedResponse) { if (mWrappedResponse) {
clone->mWrappedResponse = mWrappedResponse->Clone(); clone->mWrappedResponse = mWrappedResponse->Clone(aCloneType);
MOZ_ASSERT(!mBody); MOZ_ASSERT(!mBody);
return clone.forget(); return clone.forget();
} }
if (!mBody) { if (!mBody || aCloneType == eDontCloneInputStream) {
return clone.forget(); return clone.forget();
} }

View file

@ -46,7 +46,13 @@ public:
M* aManager, M* aManager,
UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream); UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
already_AddRefed<InternalResponse> Clone(); enum CloneType
{
eCloneInputStream,
eDontCloneInputStream,
};
already_AddRefed<InternalResponse> Clone(CloneType eCloneType);
static already_AddRefed<InternalResponse> static already_AddRefed<InternalResponse>
NetworkError() NetworkError()

View file

@ -29,7 +29,25 @@ using namespace workers;
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request) NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
NS_IMPL_CYCLE_COLLECTING_RELEASE(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_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -37,8 +55,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest, AbortSignal* aSignal) Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest, AbortSignal* aSignal)
: FetchBody<Request>() : FetchBody<Request>(aOwner)
, mOwner(aOwner)
, mRequest(aRequest) , mRequest(aRequest)
, mSignal(aSignal) , mSignal(aSignal)
{ {
@ -557,17 +574,15 @@ Request::Constructor(const GlobalObject& aGlobal,
} }
if (aInit.mBody.WasPassed()) { if (aInit.mBody.WasPassed()) {
const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable = const Nullable<fetch::OwningBodyInit>& bodyInitNullable = aInit.mBody.Value();
aInit.mBody.Value();
if (!bodyInitNullable.IsNull()) { if (!bodyInitNullable.IsNull()) {
const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
bodyInitNullable.Value();
nsCOMPtr<nsIInputStream> stream; nsCOMPtr<nsIInputStream> stream;
nsAutoCString contentType; nsAutoCString contentTypeWithCharset;
uint64_t contentLengthUnused; uint64_t contentLengthUnused;
aRv = ExtractByteStreamFromBody(bodyInit, aRv = ExtractByteStreamFromBody(bodyInit,
getter_AddRefs(stream), getter_AddRefs(stream),
contentType, contentTypeWithCharset,
contentLengthUnused); contentLengthUnused);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
@ -575,10 +590,10 @@ Request::Constructor(const GlobalObject& aGlobal,
temporaryBody = stream; temporaryBody = stream;
if (!contentType.IsVoid() && if (!contentTypeWithCharset.IsVoid() &&
!requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"), requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
contentType, aRv); contentTypeWithCharset, aRv);
} }
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
@ -599,7 +614,10 @@ Request::Constructor(const GlobalObject& aGlobal,
inputReq->GetBody(getter_AddRefs(body)); inputReq->GetBody(getter_AddRefs(body));
if (body) { if (body) {
inputReq->SetBody(nullptr); inputReq->SetBody(nullptr);
inputReq->SetBodyUsed(); inputReq->SetBodyUsed(aGlobal.Context(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} }
} }
return domRequest.forget(); return domRequest.forget();

View file

@ -128,6 +128,8 @@ public:
void void
GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); } GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
using FetchBody::GetBody;
void void
SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); } SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
@ -162,7 +164,6 @@ public:
private: private:
~Request(); ~Request();
nsCOMPtr<nsIGlobalObject> mOwner;
RefPtr<InternalRequest> mRequest; RefPtr<InternalRequest> mRequest;
// Lazily created. // Lazily created.

View file

@ -12,12 +12,16 @@
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/dom/FetchBinding.h" #include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/Headers.h" #include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/URL.h" #include "mozilla/dom/URL.h"
#include "nsDOMString.h" #include "nsDOMString.h"
#include "BodyExtractor.h"
#include "FetchStream.h"
#include "FetchStreamReader.h"
#include "InternalResponse.h" #include "InternalResponse.h"
#include "WorkerPrivate.h" #include "WorkerPrivate.h"
@ -26,7 +30,31 @@ namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response) NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
NS_IMPL_CYCLE_COLLECTING_RELEASE(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_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -34,18 +62,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse, AbortSignal* aSignal) Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse, AbortSignal* aSignal)
: FetchBody<Response>() : FetchBody<Response>(aGlobal)
, mOwner(aGlobal)
, mInternalResponse(aInternalResponse) , mInternalResponse(aInternalResponse)
, mSignal(aSignal) , mSignal(aSignal)
{ {
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable || MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response); aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
SetMimeType(); SetMimeType();
// Keep a firm grip!
mozilla::HoldJSObjects(this);
} }
Response::~Response() Response::~Response()
{ {
mozilla::DropJSObjects(this);
} }
/* static */ already_AddRefed<Response> /* static */ already_AddRefed<Response>
@ -106,7 +137,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
return nullptr; return nullptr;
} }
Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>> body; Optional<fetch::ResponseBodyInit> body;
ResponseInit init; ResponseInit init;
init.mStatus = aStatus; init.mStatus = aStatus;
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv); 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> /*static*/ already_AddRefed<Response>
Response::Constructor(const GlobalObject& aGlobal, Response::Constructor(const GlobalObject& aGlobal,
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody, const Optional<fetch::ResponseBodyInit>& aBody,
const ResponseInit& aInit, ErrorResult& aRv) const ResponseInit& aInit, ErrorResult& aRv)
{ {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 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) { if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>(); aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
return nullptr; return nullptr;
} }
nsCString contentTypeWithCharset;
nsCOMPtr<nsIInputStream> bodyStream; nsCOMPtr<nsIInputStream> bodyStream;
nsCString contentType; int64_t bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
uint64_t bodySize = 0;
aRv = ExtractByteStreamFromBody(aBody.Value().Value(), if (aBody.Value().IsReadableStream()) {
getter_AddRefs(bodyStream), const ReadableStream& readableStream =
contentType, aBody.Value().GetAsReadableStream();
bodySize);
if (NS_WARN_IF(aRv.Failed())) { JS::Rooted<JSObject*> readableStreamObj(aGlobal.Context(),
return nullptr; 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); internalResponse->SetBody(bodyStream, bodySize);
if (!contentType.IsVoid() && if (!contentTypeWithCharset.IsVoid() &&
!internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
// Ignore Append() failing here. // Ignore Append() failing here.
ErrorResult error; ErrorResult error;
internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error); internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
contentTypeWithCharset, error);
error.SuppressException(); error.SuppressException();
} }
@ -229,29 +317,87 @@ Response::Constructor(const GlobalObject& aGlobal,
} }
already_AddRefed<Response> already_AddRefed<Response>
Response::Clone(ErrorResult& aRv) const Response::Clone(JSContext* aCx, ErrorResult& aRv)
{ {
if (BodyUsed()) { if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr; return nullptr;
} }
RefPtr<InternalResponse> ir = mInternalResponse->Clone(); RefPtr<FetchStreamReader> streamReader;
RefPtr<Response> response = new Response(mOwner, ir, mSignal); 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(); return response.forget();
} }
already_AddRefed<Response> already_AddRefed<Response>
Response::CloneUnfiltered(ErrorResult& aRv) const Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv)
{ {
if (BodyUsed()) { if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr; 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<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(); return ref.forget();
} }

View file

@ -105,6 +105,8 @@ public:
void void
GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); } GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
using FetchBody::GetBody;
static already_AddRefed<Response> static already_AddRefed<Response>
Error(const GlobalObject& aGlobal); Error(const GlobalObject& aGlobal);
@ -113,7 +115,7 @@ public:
static already_AddRefed<Response> static already_AddRefed<Response>
Constructor(const GlobalObject& aGlobal, Constructor(const GlobalObject& aGlobal,
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody, const Optional<fetch::ResponseBodyInit>& aBody,
const ResponseInit& aInit, ErrorResult& rv); const ResponseInit& aInit, ErrorResult& rv);
nsIGlobalObject* GetParentObject() const nsIGlobalObject* GetParentObject() const
@ -122,10 +124,10 @@ public:
} }
already_AddRefed<Response> already_AddRefed<Response>
Clone(ErrorResult& aRv) const; Clone(JSContext* aCx, ErrorResult& aRv);
already_AddRefed<Response> already_AddRefed<Response>
CloneUnfiltered(ErrorResult& aRv) const; CloneUnfiltered(JSContext* aCx, ErrorResult& aRv);
void void
SetBody(nsIInputStream* aBody, int64_t aBodySize); SetBody(nsIInputStream* aBody, int64_t aBodySize);
@ -142,7 +144,6 @@ public:
private: private:
~Response(); ~Response();
nsCOMPtr<nsIGlobalObject> mOwner;
RefPtr<InternalResponse> mInternalResponse; RefPtr<InternalResponse> mInternalResponse;
// Lazily created // Lazily created

View file

@ -4,11 +4,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom += [
'BodyExtractor.h',
'ChannelInfo.h', 'ChannelInfo.h',
'Fetch.h', 'Fetch.h',
'FetchDriver.h', 'FetchDriver.h',
'FetchIPCTypes.h', 'FetchIPCTypes.h',
'FetchObserver.h', 'FetchObserver.h',
'FetchStreamReader.h',
'FetchUtil.h', 'FetchUtil.h',
'Headers.h', 'Headers.h',
'InternalHeaders.h', 'InternalHeaders.h',
@ -19,11 +21,14 @@ EXPORTS.mozilla.dom += [
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'BodyExtractor.cpp',
'ChannelInfo.cpp', 'ChannelInfo.cpp',
'Fetch.cpp', 'Fetch.cpp',
'FetchConsumer.cpp', 'FetchConsumer.cpp',
'FetchDriver.cpp', 'FetchDriver.cpp',
'FetchObserver.cpp', 'FetchObserver.cpp',
'FetchStream.cpp',
'FetchStreamReader.cpp',
'FetchUtil.cpp', 'FetchUtil.cpp',
'Headers.cpp', 'Headers.cpp',
'InternalHeaders.cpp', 'InternalHeaders.cpp',

View file

@ -310,7 +310,7 @@ TCPSocketParent::RecvData(const SendableData& aData,
const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t(); const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val); bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
NS_ENSURE_TRUE(ok, true); NS_ENSURE_TRUE(ok, true);
RootedTypedArray<ArrayBuffer> data(autoCx); RootedSpiderMonkeyInterface<ArrayBuffer> data(autoCx);
data.Init(&val.toObject()); data.Init(&val.toObject());
Optional<uint32_t> byteLength(buffer.Length()); Optional<uint32_t> byteLength(buffer.Length());
mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv); mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);

View file

@ -575,9 +575,10 @@ URLSearchParams::ReadStructuredClone(JSStructuredCloneReader* aReader)
NS_IMETHODIMP NS_IMETHODIMP
URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, 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"); aCharset.AssignLiteral("UTF-8");
nsAutoString serialized; nsAutoString serialized;

View file

@ -8,7 +8,7 @@
*/ */
typedef object JSON; 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)] [NoInterfaceObject, Exposed=(Window,Worker)]
interface Body { interface Body {
@ -24,3 +24,15 @@ interface Body {
[Throws] [Throws]
Promise<USVString> text(); 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;
};

View file

@ -294,7 +294,7 @@ partial interface Navigator {
partial interface Navigator { partial interface Navigator {
[Throws, Pref="beacon.enabled"] [Throws, Pref="beacon.enabled"]
boolean sendBeacon(DOMString url, boolean sendBeacon(DOMString url,
optional (ArrayBufferView or Blob or DOMString or FormData)? data = null); optional BodyInit? data = null);
}; };
partial interface Navigator { partial interface Navigator {

View file

@ -7,7 +7,9 @@
* https://fetch.spec.whatwg.org/#response-class * 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)] Exposed=(Window,Worker)]
interface Response { interface Response {
[NewObject] static Response error(); [NewObject] static Response error();
@ -30,6 +32,12 @@ interface Response {
}; };
Response implements Body; 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 { dictionary ResponseInit {
unsigned short status = 200; unsigned short status = 200;
ByteString statusText = "OK"; ByteString statusText = "OK";

View file

@ -299,6 +299,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
.setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp"))) .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack"))) .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
.setArrayProtoValues(GetWorkerPref<bool>( .setArrayProtoValues(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("array_prototype_values"))); NS_LITERAL_CSTRING("array_prototype_values")));

View file

@ -701,9 +701,14 @@ private:
request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(), request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
loadInfo.mFullURL.Length()); 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; ErrorResult error;
RefPtr<Promise> cachePromise = RefPtr<Promise> cachePromise =
mCacheCreator->Cache_()->Put(request, *response, error); mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
if (NS_WARN_IF(error.Failed())) { if (NS_WARN_IF(error.Failed())) {
nsresult rv = error.StealNSResult(); nsresult rv = error.StealNSResult();
channel->Cancel(rv); channel->Cancel(rv);
@ -1668,8 +1673,13 @@ CacheScriptLoader::Load(Cache* aCache)
mozilla::dom::CacheQueryOptions params; 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; 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())) { if (NS_WARN_IF(error.Failed())) {
Fail(error.StealNSResult()); Fail(error.StealNSResult());
return; return;

View file

@ -6,6 +6,7 @@
#include "ServiceWorkerEvents.h" #include "ServiceWorkerEvents.h"
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsIConsoleReportCollector.h" #include "nsIConsoleReportCollector.h"
#include "nsIHttpChannelInternal.h" #include "nsIHttpChannelInternal.h"
#include "nsINetworkInterceptController.h" #include "nsINetworkInterceptController.h"
@ -30,8 +31,6 @@
#include "mozilla/Move.h" #include "mozilla/Move.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/BodyUtil.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/FetchEventBinding.h" #include "mozilla/dom/FetchEventBinding.h"
#include "mozilla/dom/MessagePort.h" #include "mozilla/dom/MessagePort.h"
@ -404,76 +403,6 @@ void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event)); 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 class MOZ_STACK_CLASS AutoCancel
{ {
RefPtr<RespondWithHandler> mOwner; 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> template<typename... Params>
void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) 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 line = 0;
uint32_t column = 0; uint32_t column = 0;
nsString valueString; nsString valueString;
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
valueString);
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
@ -572,7 +540,8 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
uint32_t line = 0; uint32_t line = 0;
uint32_t column = 0; uint32_t column = 0;
nsString valueString; nsString valueString;
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
valueString);
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
@ -659,7 +628,12 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
ir->GetUnfilteredBody(getter_AddRefs(body)); ir->GetUnfilteredBody(getter_AddRefs(body));
// Errors and redirects may not have a body. // Errors and redirects may not have a body.
if (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; nsCOMPtr<nsIOutputStream> responseBody;
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody)); rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
@ -715,7 +689,8 @@ RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
uint32_t column = mRespondWithColumnNumber; uint32_t column = mRespondWithColumnNumber;
nsString valueString; nsString valueString;
ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
valueString);
::AsyncLog(mInterceptedChannel, sourceSpec, line, column, ::AsyncLog(mInterceptedChannel, sourceSpec, line, column,
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"), NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
@ -853,7 +828,8 @@ public:
nsCString spec; nsCString spec;
uint32_t line = 0; uint32_t line = 0;
uint32_t column = 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 // only use the extracted location if we found one
if (!spec.IsEmpty()) { if (!spec.IsEmpty()) {

View file

@ -395,7 +395,7 @@ public:
return; return;
} }
WriteToCache(cache); WriteToCache(aCx, cache);
return; return;
} }
@ -528,7 +528,7 @@ private:
} }
void void
WriteToCache(Cache* aCache) WriteToCache(JSContext* aCx, Cache* aCache)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
MOZ_ASSERT(aCache); MOZ_ASSERT(aCache);
@ -561,8 +561,10 @@ private:
// For now we have to wait until the Put Promise is fulfilled before we can // 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 // continue since Cache does not yet support starting a read that is being
// written to. // 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())) { if (NS_WARN_IF(result.Failed())) {
// No exception here because there are no ReadableStreams involved here.
MOZ_ASSERT(!result.IsJSException());
MOZ_ASSERT(!result.IsErrorWithMessage()); MOZ_ASSERT(!result.IsErrorWithMessage());
Fail(result.StealNSResult()); Fail(result.StealNSResult());
return; return;
@ -903,7 +905,7 @@ CompareCache::ManageCacheResult(JSContext* aCx, JS::Handle<JS::Value> aValue)
request.SetAsUSVString().Rebind(mURL.Data(), mURL.Length()); request.SetAsUSVString().Rebind(mURL.Data(), mURL.Length());
ErrorResult error; ErrorResult error;
CacheQueryOptions params; 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())) { if (NS_WARN_IF(error.Failed())) {
mManager->CacheFinished(error.StealNSResult(), false); mManager->CacheFinished(error.StealNSResult(), false);
return; return;

View file

@ -8,8 +8,9 @@
BEGIN_WORKERS_NAMESPACE BEGIN_WORKERS_NAMESPACE
WorkerHolder::WorkerHolder() WorkerHolder::WorkerHolder(Behavior aBehavior)
: mWorkerPrivate(nullptr) : mWorkerPrivate(nullptr)
, mBehavior(aBehavior)
{ {
} }
@ -44,6 +45,12 @@ WorkerHolder::ReleaseWorker()
ReleaseWorkerInternal(); ReleaseWorkerInternal();
} }
WorkerHolder::Behavior
WorkerHolder::GetBehavior() const
{
return mBehavior;
}
void void
WorkerHolder::ReleaseWorkerInternal() WorkerHolder::ReleaseWorkerInternal()
{ {

View file

@ -73,7 +73,12 @@ class WorkerHolder
public: public:
NS_DECL_OWNINGTHREAD NS_DECL_OWNINGTHREAD
WorkerHolder(); enum Behavior {
AllowIdleShutdownStart,
PreventIdleShutdownStart,
};
explicit WorkerHolder(Behavior aBehavior = PreventIdleShutdownStart);
virtual ~WorkerHolder(); virtual ~WorkerHolder();
bool HoldWorker(WorkerPrivate* aWorkerPrivate, Status aFailStatus); bool HoldWorker(WorkerPrivate* aWorkerPrivate, Status aFailStatus);
@ -81,6 +86,8 @@ public:
virtual bool Notify(Status aStatus) = 0; virtual bool Notify(Status aStatus) = 0;
Behavior GetBehavior() const;
protected: protected:
void ReleaseWorkerInternal(); void ReleaseWorkerInternal();
@ -88,6 +95,8 @@ protected:
private: private:
void AssertIsOwningThread() const; void AssertIsOwningThread() const;
const Behavior mBehavior;
}; };
END_WORKERS_NAMESPACE END_WORKERS_NAMESPACE

View file

@ -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.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED) WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_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("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)

View file

@ -4154,6 +4154,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
, mDebugger(nullptr) , mDebugger(nullptr)
, mJSContext(nullptr) , mJSContext(nullptr)
, mPRThread(nullptr) , mPRThread(nullptr)
, mNumHoldersPreventingShutdownStart(0)
, mDebuggerEventLoopLevel(0) , mDebuggerEventLoopLevel(0)
, mMainThreadEventTarget(do_GetMainThread()) , mMainThreadEventTarget(do_GetMainThread())
, mErrorHandlerRecursionCount(0) , mErrorHandlerRecursionCount(0)
@ -5382,8 +5383,11 @@ WorkerPrivate::AddHolder(WorkerHolder* aHolder, Status aFailStatus)
MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!"); MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(true)) { if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
return false; if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
return false;
}
mNumHoldersPreventingShutdownStart += 1;
} }
mHolders.AppendElement(aHolder); mHolders.AppendElement(aHolder);
@ -5398,8 +5402,11 @@ WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!"); MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
mHolders.RemoveElement(aHolder); mHolders.RemoveElement(aHolder);
if (mHolders.IsEmpty() && !ModifyBusyCountFromWorker(false)) { if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
NS_WARNING("Failed to modify busy count!"); mNumHoldersPreventingShutdownStart -= 1;
if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
NS_WARNING("Failed to modify busy count!");
}
} }
} }

View file

@ -980,6 +980,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope; RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
nsTArray<ParentType*> mChildWorkers; nsTArray<ParentType*> mChildWorkers;
nsTObserverArray<WorkerHolder*> mHolders; nsTObserverArray<WorkerHolder*> mHolders;
uint32_t mNumHoldersPreventingShutdownStart;
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts; nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
uint32_t mDebuggerEventLoopLevel; uint32_t mDebuggerEventLoopLevel;
RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue; RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;

View file

@ -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 nsresult
XMLHttpRequestMainThread::CreateChannel() XMLHttpRequestMainThread::CreateChannel()
{ {
@ -2793,7 +2624,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
// document? // document?
nsCOMPtr<nsIDocument> doc = do_QueryInterface(supports); nsCOMPtr<nsIDocument> doc = do_QueryInterface(supports);
if (doc) { if (doc) {
RequestBody<nsIDocument> body(doc); BodyExtractor<nsIDocument> body(doc);
return SendInternal(&body); return SendInternal(&body);
} }
@ -2802,21 +2633,21 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
if (wstr) { if (wstr) {
nsAutoString string; nsAutoString string;
wstr->GetData(string); wstr->GetData(string);
RequestBody<const nsAString> body(&string); BodyExtractor<const nsAString> body(&string);
return SendInternal(&body); return SendInternal(&body);
} }
// nsIInputStream? // nsIInputStream?
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports); nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
if (stream) { if (stream) {
RequestBody<nsIInputStream> body(stream); BodyExtractor<nsIInputStream> body(stream);
return SendInternal(&body); return SendInternal(&body);
} }
// nsIXHRSendable? // nsIXHRSendable?
nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports); nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
if (sendable) { if (sendable) {
RequestBody<nsIXHRSendable> body(sendable); BodyExtractor<nsIXHRSendable> body(sendable);
return SendInternal(&body); return SendInternal(&body);
} }
@ -2827,9 +2658,9 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
nsresult rv = aVariant->GetAsJSVal(&realVal); nsresult rv = aVariant->GetAsJSVal(&realVal);
if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) { if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull()); JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
RootedTypedArray<ArrayBuffer> buf(rootingCx); RootedSpiderMonkeyInterface<ArrayBuffer> buf(rootingCx);
if (buf.Init(obj)) { if (buf.Init(obj)) {
RequestBody<const ArrayBuffer> body(&buf); BodyExtractor<const ArrayBuffer> body(&buf);
return SendInternal(&body); return SendInternal(&body);
} }
} }
@ -2846,12 +2677,12 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
nsString string; nsString string;
string.Adopt(data, len); string.Adopt(data, len);
RequestBody<const nsAString> body(&string); BodyExtractor<const nsAString> body(&string);
return SendInternal(&body); return SendInternal(&body);
} }
nsresult nsresult
XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody) XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
{ {
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
@ -2917,13 +2748,6 @@ XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
mAuthorRequestHeaders.Get("content-type", uploadContentType); mAuthorRequestHeaders.Get("content-type", uploadContentType);
if (uploadContentType.IsVoid()) { if (uploadContentType.IsVoid()) {
uploadContentType = defaultContentType; 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. // We don't want to set a charset for streams.

View file

@ -34,7 +34,11 @@
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/NotNull.h" #include "mozilla/NotNull.h"
#include "mozilla/dom/MutableBlobStorage.h" #include "mozilla/dom/MutableBlobStorage.h"
#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/TypedArray.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/XMLHttpRequest.h"
#include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/XMLHttpRequestBinding.h"
#include "mozilla/dom/XMLHttpRequestEventTarget.h" #include "mozilla/dom/XMLHttpRequestEventTarget.h"
@ -54,11 +58,8 @@ class nsIJSID;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
class Blob;
class BlobSet; class BlobSet;
class DOMString; class DOMString;
class FormData;
class URLSearchParams;
class XMLHttpRequestUpload; class XMLHttpRequestUpload;
struct OriginAttributesDictionary; struct OriginAttributesDictionary;
@ -288,34 +289,7 @@ public:
private: private:
virtual ~XMLHttpRequestMainThread(); virtual ~XMLHttpRequestMainThread();
class RequestBodyBase nsresult SendInternal(const BodyExtractorBase* aBody);
{
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);
bool IsCrossSiteCORSRequest() const; bool IsCrossSiteCORSRequest() const;
bool IsDeniedCrossSiteCORSRequest(); bool IsDeniedCrossSiteCORSRequest();
@ -336,7 +310,7 @@ public:
Send(JSContext* /*aCx*/, const ArrayBuffer& aArrayBuffer, Send(JSContext* /*aCx*/, const ArrayBuffer& aArrayBuffer,
ErrorResult& aRv) override ErrorResult& aRv) override
{ {
RequestBody<const ArrayBuffer> body(&aArrayBuffer); BodyExtractor<const ArrayBuffer> body(&aArrayBuffer);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
@ -344,28 +318,28 @@ public:
Send(JSContext* /*aCx*/, const ArrayBufferView& aArrayBufferView, Send(JSContext* /*aCx*/, const ArrayBufferView& aArrayBufferView,
ErrorResult& aRv) override ErrorResult& aRv) override
{ {
RequestBody<const ArrayBufferView> body(&aArrayBufferView); BodyExtractor<const ArrayBufferView> body(&aArrayBufferView);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
virtual void virtual void
Send(JSContext* /*aCx*/, Blob& aBlob, ErrorResult& aRv) override Send(JSContext* /*aCx*/, Blob& aBlob, ErrorResult& aRv) override
{ {
RequestBody<Blob> body(&aBlob); BodyExtractor<nsIXHRSendable> body(&aBlob);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
virtual void Send(JSContext* /*aCx*/, URLSearchParams& aURLSearchParams, virtual void Send(JSContext* /*aCx*/, URLSearchParams& aURLSearchParams,
ErrorResult& aRv) override ErrorResult& aRv) override
{ {
RequestBody<URLSearchParams> body(&aURLSearchParams); BodyExtractor<nsIXHRSendable> body(&aURLSearchParams);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
virtual void virtual void
Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) override Send(JSContext* /*aCx*/, nsIDocument& aDoc, ErrorResult& aRv) override
{ {
RequestBody<nsIDocument> body(&aDoc); BodyExtractor<nsIDocument> body(&aDoc);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
@ -375,7 +349,7 @@ public:
if (DOMStringIsNull(aString)) { if (DOMStringIsNull(aString)) {
Send(aCx, aRv); Send(aCx, aRv);
} else { } else {
RequestBody<const nsAString> body(&aString); BodyExtractor<const nsAString> body(&aString);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
} }
@ -383,7 +357,7 @@ public:
virtual void virtual void
Send(JSContext* /*aCx*/, FormData& aFormData, ErrorResult& aRv) override Send(JSContext* /*aCx*/, FormData& aFormData, ErrorResult& aRv) override
{ {
RequestBody<FormData> body(&aFormData); BodyExtractor<nsIXHRSendable> body(&aFormData);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }
@ -391,7 +365,7 @@ public:
Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) override Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) override
{ {
NS_ASSERTION(aStream, "Null should go to string version"); NS_ASSERTION(aStream, "Null should go to string version");
RequestBody<nsIInputStream> body(aStream); BodyExtractor<nsIInputStream> body(aStream);
aRv = SendInternal(&body); aRv = SendInternal(&body);
} }

View file

@ -326,9 +326,12 @@ interface nsIXMLHttpRequest : nsISupports
[uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)] [uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
interface nsIXHRSendable : nsISupports { 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, void getSendInfo(out nsIInputStream body,
out uint64_t contentLength, out uint64_t contentLength,
out ACString contentType, out ACString contentTypeWithCharset,
out ACString charset); out ACString charset);
}; };

510
js/public/Stream.h Normal file
View file

@ -0,0 +1,510 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
/*
* JSAPI functions and callbacks related to WHATWG Stream objects.
*
* Much of the API here mirrors the JS API of ReadableStream and associated
* classes, e.g. ReadableStreamDefaultReader, ReadableStreamBYOBReader,
* ReadableStreamDefaultController, ReadableByteStreamController, and
* ReadableStreamBYOBRequest.
*
* There are some crucial differences, though: Functionality that's exposed
* as methods/accessors on controllers in JS is exposed as functions taking
* ReadableStream instances instead. This is because an analysis of how
* the API would be used showed that all functions that'd take controllers
* would do so by first getting the controller from the stream instance it's
* associated with and then call the function taking it. I.e., it would purely
* add boilerplate without any gains in ease of use of the API.
*
* Some functions exposed here deal with ReadableStream instances that have an
* embedding-provided underlying source. These instances are largely similar
* to byte streams as created using |new ReadableStream({type: "bytes"})|:
* They enable users to acquire ReadableStreamBYOBReaders and only vend chunks
* that're typed array instances.
*
* When creating an "external readable stream" using
* JS::NewReadableExternalSourceStreamObject, an underlying source and a set
* of flags can be passed to be stored on the stream. The underlying source is
* treated as an opaque void* pointer by the JS engine: it's purely meant as
* a reference to be used by the embedding to identify whatever actual source
* it uses to supply data for the stream. Similarly, the flags aren't
* interpreted by the JS engine, but are passed to some of the callbacks below
* and can be retrieved using JS::ReadableStreamGetEmbeddingFlags.
*
* External readable streams are optimized to allow the embedding to interact
* with them with a minimum of overhead: chunks aren't enqueued as individual
* typed array instances; instead, the embedding only updates the amount of
* data available using ReadableStreamUpdateDataAvailableFromSource.
* When content requests data by reading from a reader,
* WriteIntoReadRequestBufferCallback is invoked, asking the embedding to
* write data directly into the buffer we're about to hand to content.
*
* Additionally, ReadableStreamGetExternalUnderlyingSource can be used to
* get the void* pointer to the underlying source. This is equivalent to
* acquiring a reader for the stream in that it locks the stream until it
* is released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
*
* Embeddings are expected to detect situations where an API exposed to JS
* takes a ReadableStream to read from that has an external underlying source.
* In those situations, it might be preferable to directly perform data
* transfers from the stream's underlying source to whatever sink the
* embedding uses, assuming that such direct transfers can be performed
* more efficiently.
*/
#ifndef js_Stream_h
#define js_Stream_h
#include "jstypes.h"
#include "js/TypeDecls.h"
namespace JS {
/**
* Invoked whenever a reader desires more data from a ReadableStream's
* embedding-provided underlying source.
*
* The given |desiredSize| is the absolute size, not a delta from the previous
* desired size.
*/
typedef void
(* RequestReadableStreamDataCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, size_t desiredSize);
/**
* Invoked to cause the embedding to fill the given |buffer| with data from
* the given embedding-provided underlying source.
*
* This can only happen after the embedding has updated the amount of data
* available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
* least one read request is pending when
* JS::ReadableStreamUpdateDataAvailableFromSource is called,
* the WriteIntoReadRequestBufferCallback is invoked immediately from under
* the call to JS::WriteIntoReadRequestBufferCallback. If not, it is invoked
* if and when a new read request is made.
*
* Note: This callback *must not cause GC*, because that could potentially
* invalidate the |buffer| pointer.
*/
typedef void
(* WriteIntoReadRequestBufferCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, void* buffer,
size_t length, size_t* bytesWritten);
/**
* Invoked in reaction to the ReadableStream being canceled to allow the
* embedding to free the underlying source.
*
* This is equivalent to calling |cancel| on non-external underlying sources
* provided to the ReadableStream constructor in JavaScript.
*
* The given |reason| is the JS::Value that was passed as an argument to
* ReadableStream#cancel().
*
* The returned JS::Value will be used to resolve the Promise returned by
* ReadableStream#cancel().
*/
typedef Value
(* CancelReadableStreamCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, HandleValue reason);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being closed.
*/
typedef void
(* ReadableStreamClosedCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
uint8_t flags);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being errored with the
* given reason.
*/
typedef void
(* ReadableStreamErroredCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
uint8_t flags, HandleValue reason);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being finalized. Only the underlying source is passed
* as an argument, while the ReadableStream itself is not to prevent the
* embedding from operating on a JSObject that might not be in a valid state
* anymore.
*
* Note: the ReadableStream might be finalized on a background thread. That
* means this callback might be invoked from an arbitrary thread, which the
* embedding must be able to handle.
*/
typedef void
(* ReadableStreamFinalizeCallback)(void* underlyingSource, uint8_t flags);
/**
* Sets runtime-wide callbacks to use for interacting with embedding-provided
* hooks for operating on ReadableStream instances.
*
* See the documentation for the individual callback types for details.
*/
extern JS_PUBLIC_API(void)
SetReadableStreamCallbacks(JSContext* cx,
RequestReadableStreamDataCallback dataRequestCallback,
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
CancelReadableStreamCallback cancelCallback,
ReadableStreamClosedCallback closedCallback,
ReadableStreamErroredCallback erroredCallback,
ReadableStreamFinalizeCallback finalizeCallback);
extern JS_PUBLIC_API(bool)
HasReadableStreamCallbacks(JSContext* cx);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, configured as a default stream.
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of |ReadableStream.prototype|.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableDefaultStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
HandleFunction size = nullptr, double highWaterMark = 1,
HandleObject proto = nullptr);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, configured as a byte stream.
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of |ReadableStream.prototype|.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableByteStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
double highWaterMark = 0, HandleObject proto = nullptr);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, with the right slot layout. If a |proto| is passed, that gets
* set as the instance's [[Prototype]] instead of the original value of
* |ReadableStream.prototype|.
*
* The instance is optimized for operating as a byte stream backed by an
* embedding-provided underlying source, using the callbacks set via
* |JS::SetReadableStreamCallbacks|.
*
* The given |flags| will be passed to all applicable callbacks and can be
* used to disambiguate between different types of stream sources the
* embedding might support.
*
* Note: the embedding is responsible for ensuring that the pointer to the
* underlying source stays valid as long as the stream can be read from.
* The underlying source can be freed if the tree is canceled or errored.
* It can also be freed if the stream is destroyed. The embedding is notified
* of that using ReadableStreamFinalizeCallback.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
uint8_t flags = 0, HandleObject proto = nullptr);
/**
* Returns the flags that were passed to NewReadableExternalSourceStreamObject
* when creating the given stream.
*
* Asserts that the given stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(uint8_t)
ReadableStreamGetEmbeddingFlags(const JSObject* stream);
/**
* Returns the embedding-provided underlying source of the given |stream|.
*
* Can be used to optimize operations if both the underlying source and the
* intended sink are embedding-provided. In that case it might be
* preferrable to pipe data directly from source to sink without interacting
* with the stream at all.
*
* Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is
* called.
*
* Throws an exception if the stream is locked, i.e. if a reader has been
* acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource
* has been used previously without releasing the external source again.
*
* Throws an exception if the stream isn't readable, i.e if it is errored or
* closed. This is different from ReadableStreamGetReader because we don't
* have a Promise to resolve/reject, which a reader provides.
*
* Asserts that the stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream, void** source);
/**
* Releases the embedding-provided underlying source of the given |stream|,
* returning the stream into an unlocked state.
*
* Asserts that the stream was locked through
* ReadableStreamGetExternalUnderlyingSource.
*
* Asserts that the stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(void)
ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream);
/**
* Update the amount of data available at the underlying source of the given
* |stream|.
*
* Can only be used for streams with an embedding-provided underlying source.
* The JS engine will use the given value to satisfy read requests for the
* stream by invoking the JS::WriteIntoReadRequestBuffer callback.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, HandleObject stream,
uint32_t availableData);
/**
* Returns true if the given object is an unwrapped ReadableStream object,
* false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStream(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamDefaultReader or ReadableStreamBYOBReader object,
* false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamReader(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamDefaultReader object, false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamDefaultReader(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamBYOBReader object, false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamBYOBReader(const JSObject* obj);
enum class ReadableStreamMode {
Default,
Byte,
ExternalSource
};
/**
* Returns the stream's ReadableStreamMode. If the mode is |Byte| or
* |ExternalSource|, it's possible to acquire a BYOB reader for more optimized
* operations.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(ReadableStreamMode)
ReadableStreamGetMode(const JSObject* stream);
enum class ReadableStreamReaderMode {
Default,
BYOB
};
/**
* Returns true if the given ReadableStream is readable, false if not.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsReadable(const JSObject* stream);
/**
* Returns true if the given ReadableStream is locked, false if not.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsLocked(const JSObject* stream);
/**
* Returns true if the given ReadableStream is disturbed, false if not.
*
* Asserts that |stream| is an ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsDisturbed(const JSObject* stream);
/**
* Cancels the given ReadableStream with the given reason and returns a
* Promise resolved according to the result.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamCancel(JSContext* cx, HandleObject stream, HandleValue reason);
/**
* Creates a reader of the type specified by the mode option and locks the
* stream to the new reader.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamGetReader(JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode);
/**
* Tees the given ReadableStream and stores the two resulting streams in
* outparams. Returns false if the operation fails, e.g. because the stream is
* locked.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamTee(JSContext* cx, HandleObject stream,
MutableHandleObject branch1Stream, MutableHandleObject branch2Stream);
/**
* Retrieves the desired combined size of additional chunks to fill the given
* ReadableStream's queue. Stores the result in |value| and sets |hasValue| to
* true on success, returns false on failure.
*
* If the stream is errored, the call will succeed but no value will be stored
* in |value| and |hasValue| will be set to false.
*
* Note: This is semantically equivalent to the |desiredSize| getter on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(void)
ReadableStreamGetDesiredSize(JSObject* stream, bool* hasValue, double* value);
/**
* Closes the given ReadableStream.
*
* Throws a TypeError and returns false if the closing operation fails.
*
* Note: This is semantically equivalent to the |close| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamClose(JSContext* cx, HandleObject stream);
/**
* Returns true if the given ReadableStream reader is locked, false otherwise.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderIsClosed(const JSObject* reader);
/**
* Enqueues the given chunk in the given ReadableStream.
*
* Throws a TypeError and returns false if the enqueing operation fails.
*
* Note: This is semantically equivalent to the |enqueue| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* If the ReadableStream has an underlying byte source, the given chunk must
* be a typed array or a DataView. Consider using
* ReadableByteStreamEnqueueBuffer.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamEnqueue(JSContext* cx, HandleObject stream, HandleValue chunk);
/**
* Enqueues the given buffer as a chunk in the given ReadableStream.
*
* Throws a TypeError and returns false if the enqueing operation fails.
*
* Note: This is semantically equivalent to the |enqueue| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity. Additionally, the JS version only
* takes typed arrays and ArrayBufferView instances as arguments, whereas
* this takes an ArrayBuffer, obviating the need to wrap it into a typed
* array.
*
* Asserts that |stream| is an unwrapped ReadableStream instance and |buffer|
* an unwrapped ArrayBuffer instance.
*/
extern JS_PUBLIC_API(bool)
ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject stream, HandleObject buffer);
/**
* Errors the given ReadableStream, causing all future interactions to fail
* with the given error value.
*
* Throws a TypeError and returns false if the erroring operation fails.
*
* Note: This is semantically equivalent to the |error| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
/**
* Cancels the given ReadableStream reader's associated stream.
*
* Throws a TypeError and returns false if the given reader isn't active.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
/**
* Cancels the given ReadableStream reader's associated stream.
*
* Throws a TypeError and returns false if the given reader has pending
* read or readInto (for default or byob readers, respectively) requests.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
/**
* Requests a read from the reader's associated ReadableStream and returns the
* resulting PromiseObject.
*
* Returns a Promise that's resolved with the read result once available or
* rejected immediately if the stream is errored or the operation failed.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
/**
* Requests a read from the reader's associated ReadableStream into the given
* ArrayBufferView and returns the resulting PromiseObject.
*
* Returns a Promise that's resolved with the read result once available or
* rejected immediately if the stream is errored or the operation failed.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader and
* |view| an unwrapped typed array or DataView instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject reader, HandleObject view);
} // namespace JS
#endif // js_Realm_h

5488
js/src/builtin/Stream.cpp Normal file

File diff suppressed because it is too large Load diff

170
js/src/builtin/Stream.h Normal file
View file

@ -0,0 +1,170 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 builtin_Stream_h
#define builtin_Stream_h
#include "builtin/Promise.h"
#include "vm/NativeObject.h"
namespace js {
class AutoSetNewObjectMetadata;
class ReadableStream : public NativeObject
{
public:
static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource,
HandleValue size, HandleValue highWaterMark,
HandleObject proto = nullptr);
static ReadableStream* createByteStream(JSContext* cx, HandleValue underlyingSource,
HandleValue highWaterMark,
HandleObject proto = nullptr);
static ReadableStream* createExternalSourceStream(JSContext* cx, void* underlyingSource,
uint8_t flags, HandleObject proto = nullptr);
bool readable() const;
bool closed() const;
bool errored() const;
bool disturbed() const;
bool locked() const;
void desiredSize(bool* hasSize, double* size) const;
JS::ReadableStreamMode mode() const;
static MOZ_MUST_USE bool close(JSContext* cx, Handle<ReadableStream*> stream);
static MOZ_MUST_USE JSObject* cancel(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue reason);
static MOZ_MUST_USE bool error(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue error);
static MOZ_MUST_USE NativeObject* getReader(JSContext* cx, Handle<ReadableStream*> stream,
JS::ReadableStreamReaderMode mode);
static MOZ_MUST_USE bool tee(JSContext* cx,
Handle<ReadableStream*> stream, bool cloneForBranch2,
MutableHandle<ReadableStream*> branch1Stream,
MutableHandle<ReadableStream*> branch2Stream);
static MOZ_MUST_USE bool enqueue(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue chunk);
static MOZ_MUST_USE bool enqueueBuffer(JSContext* cx, Handle<ReadableStream*> stream,
Handle<ArrayBufferObject*> chunk);
static MOZ_MUST_USE bool getExternalSource(JSContext* cx, Handle<ReadableStream*> stream,
void** source);
void releaseExternalSource();
uint8_t embeddingFlags() const;
static MOZ_MUST_USE bool updateDataAvailableFromSource(JSContext* cx,
Handle<ReadableStream*> stream,
uint32_t availableData);
enum State {
Readable = 1 << 0,
Closed = 1 << 1,
Errored = 1 << 2,
Disturbed = 1 << 3
};
private:
static MOZ_MUST_USE ReadableStream* createStream(JSContext* cx, HandleObject proto = nullptr);
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamDefaultReader : public NativeObject
{
public:
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamDefaultReader*> reader);
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBReader : public NativeObject
{
public:
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamBYOBReader*> reader,
Handle<ArrayBufferViewObject*> view);
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
bool ReadableStreamReaderIsClosed(const JSObject* reader);
MOZ_MUST_USE bool ReadableStreamReaderCancel(JSContext* cx, HandleObject reader,
HandleValue reason);
MOZ_MUST_USE bool ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
class ReadableStreamDefaultController : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableByteStreamController : public NativeObject
{
public:
bool hasExternalSource();
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBRequest : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ByteLengthQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class CountQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
} // namespace js
#endif /* builtin_Stream_h */

View file

@ -1441,6 +1441,14 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp)
return result; return result;
} }
static bool
StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(cx->options().streams());
return true;
}
static unsigned finalizeCount = 0; static unsigned finalizeCount = 0;
static void static void
@ -4178,6 +4186,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
"rejectPromise(promise, reason)", "rejectPromise(promise, reason)",
" Reject a Promise by calling the JSAPI function JS::RejectPromise."), " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
"streamsAreEnabled()",
" Returns a boolean indicating whether WHATWG Streams are enabled for the current compartment."),
JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
"makeFinalizeObserver()", "makeFinalizeObserver()",
" Get a special object whose finalization increases the counter returned\n" " Get a special object whose finalization increases the counter returned\n"

View file

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TypedObjectConstants.h"
function ViewedArrayBufferIfReified(tarray) { function ViewedArrayBufferIfReified(tarray) {
assert(IsTypedArray(tarray), "non-typed array asked for its buffer"); assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
@ -1781,7 +1779,7 @@ function ArrayBufferSlice(start, end) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED); ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Steps 19-21. // Steps 19-21.
ArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped); ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
// Step 22. // Step 22.
return new_; return new_;
@ -1863,7 +1861,7 @@ function SharedArrayBufferSlice(start, end) {
ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen); ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
// Steps 16-18. // Steps 16-18.
SharedArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped); SharedArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
// Step 19. // Step 19.
return new_; return new_;

View file

@ -23,6 +23,7 @@
*/ */
#include "SelfHostingDefines.h" #include "SelfHostingDefines.h"
#include "TypedObjectConstants.h"
// Assertions and debug printing, defined here instead of in the header above // Assertions and debug printing, defined here instead of in the header above
// to make `assert` invisible to C++. // to make `assert` invisible to C++.

View file

@ -613,11 +613,39 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method") MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
// Async Iteration // Async Iteration
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'") MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator") MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator") MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value") MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
// ReadableStream
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_RANGEERR,"'mode' must be \"byob\" or undefined.")
MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "'{0}' may only be called on a locked stream.")
MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
// Other Stream-related
MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
// BigInt // BigInt
MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number") MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt") MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt")

View file

@ -126,6 +126,11 @@ if CONFIG['ENABLE_ION']:
'testJitRValueAlloc.cpp', 'testJitRValueAlloc.cpp',
] ]
if CONFIG['ENABLE_STREAMS']:
UNIFIED_SOURCES += [
'testReadableStream.cpp',
]
DEFINES['EXPORT_JS_API'] = True DEFINES['EXPORT_JS_API'] = True
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [

View file

@ -29,6 +29,7 @@
#include "js/RequiredDefines.h" #include "js/RequiredDefines.h"
#include "js/RootingAPI.h" #include "js/RootingAPI.h"
#include "js/SliceBudget.h" #include "js/SliceBudget.h"
#include "js/Stream.h"
#include "js/StructuredClone.h" #include "js/StructuredClone.h"
#include "js/TracingAPI.h" #include "js/TracingAPI.h"
#include "js/TrackedOptimizationInfo.h" #include "js/TrackedOptimizationInfo.h"

View file

@ -0,0 +1,676 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "jsapi.h"
#include "jsapi-tests/tests.h"
using namespace JS;
char test_buffer_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static JSObject*
NewDefaultStream(JSContext* cx, HandleObject source = nullptr, HandleFunction size = nullptr,
double highWaterMark = 1, HandleObject proto = nullptr)
{
RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, highWaterMark,
proto));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
static JSObject*
NewByteStream(JSContext* cx, double highWaterMark = 0, HandleObject proto = nullptr)
{
RootedObject source(cx, JS_NewPlainObject(cx));
MOZ_ASSERT(source);
RootedObject stream(cx, NewReadableByteStreamObject(cx, source, highWaterMark, proto));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
static bool dataRequestCBCalled = false;
static void
DataRequestCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
size_t desiredSize)
{
MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup");
dataRequestCBCalled = true;
}
static bool writeIntoRequestBufferCBCalled = false;
static void
WriteIntoRequestBufferCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
void* buffer, size_t length, size_t* bytesWritten)
{
MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
MOZ_ASSERT(length <= sizeof(test_buffer_data));
memcpy(buffer, test_buffer_data, length);
writeIntoRequestBufferCBCalled = true;
*bytesWritten = length;
}
static bool cancelStreamCBCalled = false;
static Value cancelStreamReason;
static Value
CancelStreamCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
HandleValue reason)
{
MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
cancelStreamCBCalled = true;
cancelStreamReason = reason;
return reason;
}
static bool streamClosedCBCalled = false;
static Value streamClosedReason;
static void
StreamClosedCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags)
{
MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup");
streamClosedCBCalled = true;
}
static bool streamErroredCBCalled = false;
static Value streamErroredReason;
static void
StreamErroredCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
HandleValue reason)
{
MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup");
streamErroredCBCalled = true;
streamErroredReason = reason;
}
static bool finalizeStreamCBCalled = false;
static void* finalizedStreamUnderlyingSource;
static void
FinalizeStreamCB(void* underlyingSource, uint8_t flags)
{
MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
finalizeStreamCBCalled = true;
finalizedStreamUnderlyingSource = underlyingSource;
}
static void
ResetCallbacks()
{
dataRequestCBCalled = false;
writeIntoRequestBufferCBCalled = false;
cancelStreamReason = UndefinedValue();
cancelStreamCBCalled = false;
streamClosedCBCalled = false;
streamErroredCBCalled = false;
finalizeStreamCBCalled = false;
}
static bool
GetIterResult(JSContext* cx, HandleObject promise, MutableHandleValue value, bool* done)
{
RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
bool found;
if (!JS_HasProperty(cx, iterResult, "value", &found))
return false;
MOZ_ASSERT(found);
if (!JS_HasProperty(cx, iterResult, "done", &found))
return false;
MOZ_ASSERT(found);
RootedValue doneVal(cx);
if (!JS_GetProperty(cx, iterResult, "value", value))
return false;
if (!JS_GetProperty(cx, iterResult, "done", &doneVal))
return false;
*done = doneVal.toBoolean();
MOZ_ASSERT_IF(*done, value.isUndefined());
return true;
}
static JSObject*
GetReadChunk(JSContext* cx, HandleObject readRequest)
{
MOZ_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled);
RootedValue resultVal(cx, GetPromiseResult(readRequest));
MOZ_ASSERT(resultVal.isObject());
RootedObject result(cx, &resultVal.toObject());
RootedValue chunkVal(cx);
JS_GetProperty(cx, result, "value", &chunkVal);
return &chunkVal.toObject();
}
BEGIN_TEST(testReadableStream_NewReadableStream)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Default);
return true;
}
END_TEST(testReadableStream_NewReadableStream)
BEGIN_TEST(testReadableStream_NewReadableByteStream)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Byte);
return true;
}
END_TEST(testReadableStream_NewReadableByteStream)
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderDefault)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
CHECK(IsReadableStreamDefaultReader(reader));
CHECK(ReadableStreamIsLocked(stream));
CHECK(!ReadableStreamReaderIsClosed(reader));
return true;
}
END_TEST(testReadableStream_ReadableStreamGetReaderDefault)
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
CHECK(reader);
CHECK(IsReadableStreamBYOBReader(reader));
CHECK(ReadableStreamIsLocked(stream));
CHECK(!ReadableStreamReaderIsClosed(reader));
return true;
}
END_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
BEGIN_TEST(testReadableStream_ReadableStreamTee)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject leftStream(cx);
RootedObject rightStream(cx);
CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream));
CHECK(ReadableStreamIsLocked(stream));
CHECK(leftStream);
CHECK(IsReadableStream(leftStream));
CHECK(rightStream);
CHECK(IsReadableStream(rightStream));
return true;
}
END_TEST(testReadableStream_ReadableStreamTee)
BEGIN_TEST(testReadableStream_ReadableStreamEnqueue)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject chunk(cx, JS_NewPlainObject(cx));
CHECK(chunk);
RootedValue chunkVal(cx, ObjectValue(*chunk));
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
return true;
}
END_TEST(testReadableStream_ReadableStreamEnqueue)
BEGIN_TEST(testReadableStream_ReadableByteStreamEnqueue)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject chunk(cx, JS_NewUint8Array(cx, 42));
CHECK(chunk);
CHECK(!ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
CHECK(JS_IsExceptionPending(cx));
return true;
}
END_TEST(testReadableStream_ReadableByteStreamEnqueue)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
RootedObject chunk(cx, JS_NewPlainObject(cx));
CHECK(chunk);
RootedValue chunkVal(cx, ObjectValue(*chunk));
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
CHECK(GetReadChunk(cx, request) == chunk);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
BEGIN_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
size_t length = sizeof(test_buffer_data);
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
CHECK(buffer);
RootedObject chunk(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, length));
CHECK(chunk);
bool isShared;
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
CHECK(JS_IsDetachedArrayBufferObject(buffer));
RootedObject readChunk(cx, GetReadChunk(cx, request));
CHECK(JS_IsUint8Array(readChunk));
void* readBufferData;
{
JS::AutoCheckCannotGC autoNoGC(cx);
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
}
CHECK(readBufferData);
CHECK(!memcmp(test_buffer_data, readBufferData, length));
return true;
}
END_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
BEGIN_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
CHECK(reader);
size_t length = sizeof(test_buffer_data);
RootedObject targetArray(cx, JS_NewUint8Array(cx, length));
bool isShared;
RootedObject request(cx, ReadableStreamBYOBReaderRead(cx, reader, targetArray));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(JS_IsDetachedArrayBufferObject(JS_GetArrayBufferViewBuffer(cx, targetArray, &isShared)));
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
CHECK(buffer);
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, buffer));
CHECK(JS_IsDetachedArrayBufferObject(buffer));
RootedObject readChunk(cx, GetReadChunk(cx, request));
CHECK(JS_IsUint8Array(readChunk));
void* readBufferData;
{
JS::AutoCheckCannotGC autoNoGC(cx);
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
}
CHECK(readBufferData);
CHECK(!memcmp(test_buffer_data, readBufferData, length));
// TODO: eliminate the memcpy that happens here.
// CHECK(readBufferData == test_buffer_data);
return true;
}
END_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
{
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB);
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(ReadableStreamClose(cx, stream));
bool done;
RootedValue value(cx);
CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
CHECK(GetIterResult(cx, request, &value, &done));
CHECK(value.isUndefined());
CHECK(done);
// The callbacks are only invoked for external streams.
CHECK(!streamClosedCBCalled);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderError)
{
ResetCallbacks();
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB);
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(ReadableStreamIsLocked(stream));
CHECK(ReadableStreamIsReadable(stream));
RootedValue error(cx, Int32Value(42));
CHECK(ReadableStreamError(cx, stream, error));
CHECK(GetPromiseState(request) == PromiseState::Rejected);
RootedValue reason(cx, GetPromiseResult(request));
CHECK(reason.isInt32());
CHECK(reason.toInt32() == 42);
// The callbacks are only invoked for external streams.
CHECK(!streamErroredCBCalled);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderError)
static JSObject*
NewExternalSourceStream(JSContext* cx, void* underlyingSource,
RequestReadableStreamDataCallback dataRequestCallback,
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
CancelReadableStreamCallback cancelCallback,
ReadableStreamClosedCallback closedCallback,
ReadableStreamErroredCallback erroredCallback,
ReadableStreamFinalizeCallback finalizeCallback)
{
SetReadableStreamCallbacks(cx, dataRequestCallback, writeIntoReadRequestCallback,
cancelCallback, closedCallback, erroredCallback,
finalizeCallback);
RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, underlyingSource));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
BEGIN_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
return true;
}
END_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
BEGIN_TEST(testReadableStream_ExternalSourceCancel)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
RootedValue reason(cx, Int32Value(42));
CHECK(ReadableStreamCancel(cx, stream, reason));
CHECK(cancelStreamCBCalled);
CHECK(cancelStreamReason == reason);
return true;
}
END_TEST(testReadableStream_ExternalSourceCancel)
BEGIN_TEST(testReadableStream_ExternalSourceGetReader)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL("stream.getReader()", &rval);
CHECK(rval.isObject());
RootedObject reader(cx, &rval.toObject());
CHECK(IsReadableStreamDefaultReader(reader));
return true;
}
END_TEST(testReadableStream_ExternalSourceGetReader)
BEGIN_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1024);
return true;
}
END_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
struct ReadFromExternalSourceFixture : public JSAPITest
{
virtual ~ReadFromExternalSourceFixture() {}
bool readWithoutDataAvailable(const char* evalSrc, const char* evalSrc2,
uint32_t writtenLength)
{
ResetCallbacks();
definePrint();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB,
&CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
js::RunJobs(cx);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
CHECK(dataRequestCBCalled);
CHECK(!writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject promise(cx, &rval.toObject());
CHECK(IsPromiseObject(promise));
CHECK(GetPromiseState(promise) == PromiseState::Pending);
size_t length = sizeof(test_buffer_data);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
CHECK(writeIntoRequestBufferCBCalled);
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
RootedValue iterVal(cx);
bool done;
if (!GetIterResult(cx, promise, &iterVal, &done))
return false;
CHECK(!done);
RootedObject chunk(cx, &iterVal.toObject());
CHECK(JS_IsUint8Array(chunk));
{
JS::AutoCheckCannotGC noGC(cx);
bool dummy;
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
}
dataRequestCBCalled = false;
writeIntoRequestBufferCBCalled = false;
EVAL(evalSrc2, &rval);
CHECK(dataRequestCBCalled);
CHECK(!writeIntoRequestBufferCBCalled);
return true;
}
bool readWithDataAvailable(const char* evalSrc, uint32_t writtenLength) {
ResetCallbacks();
definePrint();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
size_t length = sizeof(test_buffer_data);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
CHECK(writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject promise(cx, &rval.toObject());
CHECK(IsPromiseObject(promise));
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
RootedValue iterVal(cx);
bool done;
if (!GetIterResult(cx, promise, &iterVal, &done))
return false;
CHECK(!done);
RootedObject chunk(cx, &iterVal.toObject());
CHECK(JS_IsUint8Array(chunk));
{
JS::AutoCheckCannotGC noGC(cx);
bool dummy;
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
}
return true;
}
};
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
{
return readWithoutDataAvailable("r = stream.getReader(); r.read()", "r.read()", sizeof(test_buffer_data));
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceCloseWithPendingRead)
{
CHECK(readWithoutDataAvailable("r = stream.getReader(); request0 = r.read(); "
"request1 = r.read(); request0", "r.read()",
sizeof(test_buffer_data)));
RootedValue val(cx);
CHECK(JS_GetProperty(cx, global, "request1", &val));
CHECK(val.isObject());
RootedObject request(cx, &val.toObject());
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(JS_GetProperty(cx, global, "stream", &val));
RootedObject stream(cx, &val.toObject());
ReadableStreamClose(cx, stream);
val = GetPromiseResult(request);
MOZ_ASSERT(val.isObject());
RootedObject result(cx, &val.toObject());
JS_GetProperty(cx, result, "done", &val);
CHECK(val.isBoolean());
CHECK(val.toBoolean() == true);
JS_GetProperty(cx, result, "value", &val);
CHECK(val.isUndefined());
return true;
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceCloseWithPendingRead)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
{
return readWithDataAvailable("r = stream.getReader(); r.read()", sizeof(test_buffer_data));
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
{
return readWithoutDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(63))", "r.read(new Uint8Array(10))", 10);
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)
{
return readWithDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(10))", 10);
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)

View file

@ -81,6 +81,10 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals)
/* Create the global object. */ /* Create the global object. */
JS::RootedObject newGlobal(cx); JS::RootedObject newGlobal(cx);
JS::CompartmentOptions options; JS::CompartmentOptions options;
#ifdef ENABLE_STREAMS
options.creationOptions().setStreamsEnabled(true);
#endif
printf("enabled\n");
options.behaviors().setVersion(JSVERSION_LATEST); options.behaviors().setVersion(JSVERSION_LATEST);
newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook, newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
options); options);

View file

@ -46,6 +46,7 @@
#include "builtin/MapObject.h" #include "builtin/MapObject.h"
#include "builtin/Promise.h" #include "builtin/Promise.h"
#include "builtin/RegExp.h" #include "builtin/RegExp.h"
#include "builtin/Stream.h"
#include "builtin/SymbolObject.h" #include "builtin/SymbolObject.h"
#ifdef ENABLE_BINARYDATA #ifdef ENABLE_BINARYDATA
# include "builtin/TypedObject.h" # include "builtin/TypedObject.h"
@ -5095,7 +5096,6 @@ CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj,
return false; return false;
} }
return true; return true;
} }
JS_PUBLIC_API(JSObject*) JS_PUBLIC_API(JSObject*)
@ -5126,7 +5126,7 @@ JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promiseObj,
* Unforgeable version of Promise.all for internal use. * Unforgeable version of Promise.all for internal use.
* *
* Takes a dense array of Promise objects and returns a promise that's * Takes a dense array of Promise objects and returns a promise that's
* resolved with an array of resolution values when all those promises ahve * resolved with an array of resolution values when all those promises have
* been resolved, or rejected with the rejection value of the first rejected * been resolved, or rejected with the rejection value of the first rejected
* promise. * promise.
* *
@ -5141,6 +5141,368 @@ JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
return js::GetWaitForAllPromise(cx, promises); return js::GetWaitForAllPromise(cx, promises);
} }
JS_PUBLIC_API(JSObject*)
JS::NewReadableDefaultStreamObject(JSContext* cx,
JS::HandleObject underlyingSource /* = nullptr */,
JS::HandleFunction size /* = nullptr */,
double highWaterMark /* = 1 */,
JS::HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedObject source(cx, underlyingSource);
if (!source) {
source = NewBuiltinClassInstance<PlainObject>(cx);
if (!source)
return nullptr;
}
RootedValue sourceVal(cx, ObjectValue(*source));
RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
return ReadableStream::createDefaultStream(cx, sourceVal, sizeVal, highWaterMarkVal, proto);
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableByteStreamObject(JSContext* cx,
JS::HandleObject underlyingSource /* = nullptr */,
double highWaterMark /* = 1 */,
JS::HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedObject source(cx, underlyingSource);
if (!source) {
source = NewBuiltinClassInstance<PlainObject>(cx);
if (!source)
return nullptr;
}
RootedValue sourceVal(cx, ObjectValue(*source));
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
return ReadableStream::createByteStream(cx, sourceVal, highWaterMarkVal, proto);
}
extern JS_PUBLIC_API(void)
JS::SetReadableStreamCallbacks(JSContext* cx,
JS::RequestReadableStreamDataCallback dataRequestCallback,
JS::WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
JS::CancelReadableStreamCallback cancelCallback,
JS::ReadableStreamClosedCallback closedCallback,
JS::ReadableStreamErroredCallback erroredCallback,
JS::ReadableStreamFinalizeCallback finalizeCallback)
{
MOZ_ASSERT(dataRequestCallback);
MOZ_ASSERT(writeIntoReadRequestCallback);
MOZ_ASSERT(cancelCallback);
MOZ_ASSERT(closedCallback);
MOZ_ASSERT(erroredCallback);
MOZ_ASSERT(finalizeCallback);
JSRuntime* rt = cx->runtime();
MOZ_ASSERT(!rt->readableStreamDataRequestCallback);
MOZ_ASSERT(!rt->readableStreamWriteIntoReadRequestCallback);
MOZ_ASSERT(!rt->readableStreamCancelCallback);
MOZ_ASSERT(!rt->readableStreamClosedCallback);
MOZ_ASSERT(!rt->readableStreamErroredCallback);
MOZ_ASSERT(!rt->readableStreamFinalizeCallback);
rt->readableStreamDataRequestCallback = dataRequestCallback;
rt->readableStreamWriteIntoReadRequestCallback = writeIntoReadRequestCallback;
rt->readableStreamCancelCallback = cancelCallback;
rt->readableStreamClosedCallback = closedCallback;
rt->readableStreamErroredCallback = erroredCallback;
rt->readableStreamFinalizeCallback = finalizeCallback;
}
JS_PUBLIC_API(bool)
JS::HasReadableStreamCallbacks(JSContext* cx)
{
return cx->runtime()->readableStreamDataRequestCallback;
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
uint8_t flags /* = 0 */,
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
#ifdef DEBUG
JSRuntime* rt = cx->runtime();
MOZ_ASSERT(rt->readableStreamDataRequestCallback);
MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
MOZ_ASSERT(rt->readableStreamCancelCallback);
MOZ_ASSERT(rt->readableStreamClosedCallback);
MOZ_ASSERT(rt->readableStreamErroredCallback);
MOZ_ASSERT(rt->readableStreamFinalizeCallback);
#endif // DEBUG
return ReadableStream::createExternalSourceStream(cx, underlyingSource, flags, proto);
}
JS_PUBLIC_API(uint8_t)
JS::ReadableStreamGetEmbeddingFlags(const JSObject* stream)
{
return stream->as<ReadableStream>().embeddingFlags();
}
JS_PUBLIC_API(bool)
JS::IsReadableStream(const JSObject* obj)
{
return obj->is<ReadableStream>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamReader(const JSObject* obj)
{
return obj->is<ReadableStreamDefaultReader>() || obj->is<ReadableStreamBYOBReader>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamDefaultReader(const JSObject* obj)
{
return obj->is<ReadableStreamDefaultReader>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamBYOBReader(const JSObject* obj)
{
return obj->is<ReadableStreamBYOBReader>();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsReadable(const JSObject* stream)
{
return stream->as<ReadableStream>().readable();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsLocked(const JSObject* stream)
{
return stream->as<ReadableStream>().locked();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsDisturbed(const JSObject* stream)
{
return stream->as<ReadableStream>().disturbed();
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamCancel(JSContext* cx, HandleObject streamObj, HandleValue reason)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, reason);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::cancel(cx, stream, reason);
}
JS_PUBLIC_API(JS::ReadableStreamMode)
JS::ReadableStreamGetMode(const JSObject* stream)
{
return stream->as<ReadableStream>().mode();
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamGetReader(JSContext* cx, HandleObject streamObj, ReadableStreamReaderMode mode)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::getReader(cx, stream, mode);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::getExternalSource(cx, stream, source);
}
JS_PUBLIC_API(void)
JS::ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream)
{
stream->as<ReadableStream>().releaseExternalSource();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, JS::HandleObject streamObj,
uint32_t availableData)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::updateDataAvailableFromSource(cx, stream, availableData);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamTee(JSContext* cx, HandleObject streamObj,
MutableHandleObject branch1Obj, MutableHandleObject branch2Obj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
Rooted<ReadableStream*> branch1Stream(cx);
Rooted<ReadableStream*> branch2Stream(cx);
if (!ReadableStream::tee(cx, stream, false, &branch1Stream, &branch2Stream))
return false;
branch1Obj.set(branch1Stream);
branch2Obj.set(branch2Stream);
return true;
}
JS_PUBLIC_API(void)
JS::ReadableStreamGetDesiredSize(JSObject* streamObj, bool* hasValue, double* value)
{
streamObj->as<ReadableStream>().desiredSize(hasValue, value);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamClose(JSContext* cx, HandleObject streamObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::close(cx, stream);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamEnqueue(JSContext* cx, HandleObject streamObj, HandleValue chunk)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, chunk);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
if (stream->mode() != JS::ReadableStreamMode::Default) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
"JS::ReadableStreamEnqueue");
return false;
}
return ReadableStream::enqueue(cx, stream, chunk);
}
JS_PUBLIC_API(bool)
JS::ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject streamObj, HandleObject chunkObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, chunkObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
if (stream->mode() != JS::ReadableStreamMode::Byte) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER,
"JS::ReadableByteStreamEnqueueBuffer");
return false;
}
Rooted<ArrayBufferObject*> buffer(cx);
if (chunkObj->is<ArrayBufferViewObject>()) {
bool dummy;
buffer = &JS_GetArrayBufferViewBuffer(cx, chunkObj, &dummy)->as<ArrayBufferObject>();
} else if (chunkObj->is<ArrayBufferObject>()) {
buffer = &chunkObj->as<ArrayBufferObject>();
} else {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK,
"JS::ReadableByteStreamEnqueueBuffer");
return false;
}
return ReadableStream::enqueueBuffer(cx, stream, buffer);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamError(JSContext* cx, HandleObject streamObj, HandleValue error)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, error);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return js::ReadableStream::error(cx, stream, error);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderIsClosed(const JSObject* reader)
{
return js::ReadableStreamReaderIsClosed(reader);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, reader);
assertSameCompartment(cx, reason);
return js::ReadableStreamReaderCancel(cx, reader, reason);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, reader);
return js::ReadableStreamReaderReleaseLock(cx, reader);
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, readerObj);
Rooted<ReadableStreamDefaultReader*> reader(cx, &readerObj->as<ReadableStreamDefaultReader>());
return js::ReadableStreamDefaultReader::read(cx, reader);
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject readerObj, HandleObject viewObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, readerObj);
assertSameCompartment(cx, viewObj);
Rooted<ReadableStreamBYOBReader*> reader(cx, &readerObj->as<ReadableStreamBYOBReader>());
Rooted<ArrayBufferViewObject*> view(cx, &viewObj->as<ArrayBufferViewObject>());
return js::ReadableStreamBYOBReader::read(cx, reader, view);
}
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start, JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start,
JS::FinishAsyncTaskCallback finish) JS::FinishAsyncTaskCallback finish)

View file

@ -35,6 +35,7 @@
#include "js/Principals.h" #include "js/Principals.h"
#include "js/Realm.h" #include "js/Realm.h"
#include "js/RootingAPI.h" #include "js/RootingAPI.h"
#include "js/Stream.h"
#include "js/TracingAPI.h" #include "js/TracingAPI.h"
#include "js/UniquePtr.h" #include "js/UniquePtr.h"
#include "js/Utility.h" #include "js/Utility.h"
@ -1161,6 +1162,16 @@ class JS_PUBLIC_API(ContextOptions) {
return *this; return *this;
} }
bool streams() const { return streams_; }
ContextOptions& setStreams(bool flag) {
streams_ = flag;
return *this;
}
ContextOptions& toggleStreams() {
streams_ = !streams_;
return *this;
}
bool nativeRegExp() const { return nativeRegExp_; } bool nativeRegExp() const { return nativeRegExp_; }
ContextOptions& setNativeRegExp(bool flag) { ContextOptions& setNativeRegExp(bool flag) {
nativeRegExp_ = flag; nativeRegExp_ = flag;
@ -1243,6 +1254,7 @@ class JS_PUBLIC_API(ContextOptions) {
bool strictMode_ : 1; bool strictMode_ : 1;
bool extraWarnings_ : 1; bool extraWarnings_ : 1;
bool arrayProtoValues_ : 1; bool arrayProtoValues_ : 1;
bool streams_ : 1;
}; };
JS_PUBLIC_API(ContextOptions&) JS_PUBLIC_API(ContextOptions&)

View file

@ -1787,6 +1787,9 @@ UnwrapArrayBufferView(JSObject* obj);
extern JS_FRIEND_API(JSObject*) extern JS_FRIEND_API(JSObject*)
UnwrapSharedArrayBuffer(JSObject* obj); UnwrapSharedArrayBuffer(JSObject* obj);
extern JS_FRIEND_API(JSObject*)
UnwrapReadableStream(JSObject* obj);
namespace detail { namespace detail {
@ -2005,6 +2008,12 @@ JS_IsArrayBufferViewObject(JSObject* obj);
extern JS_FRIEND_API(uint32_t) extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteLength(JSObject* obj); JS_GetArrayBufferViewByteLength(JSObject* obj);
/**
* More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
*/
extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteOffset(JSObject* obj);
/* /*
* Return a pointer to the start of the data referenced by a typed array. The * Return a pointer to the start of the data referenced by a typed array. The
* data is still owned by the typed array, and should not be modified on * data is still owned by the typed array, and should not be modified on

View file

@ -1003,6 +1003,13 @@ static const JSFunctionSpec number_methods[] = {
JS_FS_END JS_FS_END
}; };
bool
js::IsInteger(const Value& val)
{
return val.isInt32() ||
(mozilla::IsFinite(val.toDouble()) && JS::ToInteger(val.toDouble()) == val.toDouble());
}
// ES6 draft ES6 15.7.3.12 // ES6 draft ES6 15.7.3.12
static bool static bool
Number_isInteger(JSContext* cx, unsigned argc, Value* vp) Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
@ -1013,9 +1020,7 @@ Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
return true; return true;
} }
Value val = args[0]; Value val = args[0];
args.rval().setBoolean(val.isInt32() || args.rval().setBoolean(js::IsInteger(val));
(mozilla::IsFinite(val.toDouble()) &&
JS::ToInteger(val.toDouble()) == val.toDouble()));
return true; return true;
} }

View file

@ -72,6 +72,10 @@ Int32ToString(ExclusiveContext* cx, int32_t i);
extern JSAtom* extern JSAtom*
Int32ToAtom(ExclusiveContext* cx, int32_t si); Int32ToAtom(ExclusiveContext* cx, int32_t si);
// ES6 15.7.3.12
extern bool
IsInteger(const Value& val);
/* /*
* Convert an integer or double (contained in the given value) to a string and * Convert an integer or double (contained in the given value) to a string and
* append to the given buffer. * append to the given buffer.

View file

@ -108,6 +108,17 @@ IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
imaginary(WasmMemory, dummy, dummy) \ imaginary(WasmMemory, dummy, dummy) \
imaginary(WasmTable, dummy, dummy) \ imaginary(WasmTable, dummy, dummy) \
real(Promise, InitViaClassSpec, OCLASP(Promise)) \ real(Promise, InitViaClassSpec, OCLASP(Promise)) \
real(ReadableStream, InitViaClassSpec, &js::ReadableStream::class_) \
real(ReadableStreamDefaultReader, InitViaClassSpec, &js::ReadableStreamDefaultReader::class_) \
real(ReadableStreamBYOBReader, InitViaClassSpec, &js::ReadableStreamBYOBReader::class_) \
real(ReadableStreamDefaultController, InitViaClassSpec, &js::ReadableStreamDefaultController::class_) \
real(ReadableByteStreamController, InitViaClassSpec, &js::ReadableByteStreamController::class_) \
real(ReadableStreamBYOBRequest, InitViaClassSpec, &js::ReadableStreamBYOBRequest::class_) \
imaginary(WritableStream, dummy, dummy) \
imaginary(WritableStreamDefaultWriter, dummy, dummy) \
imaginary(WritableStreamDefaultController,dummy, dummy) \
real(ByteLengthQueuingStrategy, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
real(CountQueuingStrategy, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

View file

@ -92,6 +92,7 @@ EXPORTS.js += [
'../public/Result.h', '../public/Result.h',
'../public/RootingAPI.h', '../public/RootingAPI.h',
'../public/SliceBudget.h', '../public/SliceBudget.h',
'../public/Stream.h',
'../public/StructuredClone.h', '../public/StructuredClone.h',
'../public/SweepingAPI.h', '../public/SweepingAPI.h',
'../public/TraceKind.h', '../public/TraceKind.h',
@ -384,6 +385,7 @@ main_deunified_sources = [
SOURCES += [ SOURCES += [
'builtin/BigInt.cpp', 'builtin/BigInt.cpp',
'builtin/RegExp.cpp', 'builtin/RegExp.cpp',
'builtin/Stream.cpp',
'frontend/Parser.cpp', 'frontend/Parser.cpp',
'gc/StoreBuffer.cpp', 'gc/StoreBuffer.cpp',
'jsarray.cpp', 'jsarray.cpp',

View file

@ -318,6 +318,7 @@ static bool enableUnboxedArrays = false;
static bool enableSharedMemory = SHARED_MEMORY_DEFAULT; static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
static bool enableWasmAlwaysBaseline = false; static bool enableWasmAlwaysBaseline = false;
static bool enableArrayProtoValues = true; static bool enableArrayProtoValues = true;
static bool enableStreams = false;
static bool printTiming = false; static bool printTiming = false;
static const char* jsCacheDir = nullptr; static const char* jsCacheDir = nullptr;
static const char* jsCacheAsmJSPath = nullptr; static const char* jsCacheAsmJSPath = nullptr;
@ -7565,6 +7566,7 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); enableUnboxedArrays = op.getBoolOption("unboxed-arrays");
enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline"); enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline");
enableArrayProtoValues = !op.getBoolOption("no-array-proto-values"); enableArrayProtoValues = !op.getBoolOption("no-array-proto-values");
enableStreams = op.getBoolOption("enable-streams");
JS::ContextOptionsRef(cx).setBaseline(enableBaseline) JS::ContextOptionsRef(cx).setBaseline(enableBaseline)
.setIon(enableIon) .setIon(enableIon)
@ -7573,7 +7575,8 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp) .setNativeRegExp(enableNativeRegExp)
.setUnboxedArrays(enableUnboxedArrays) .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues); .setArrayProtoValues(enableArrayProtoValues)
.setStreams(enableStreams);
if (op.getBoolOption("wasm-check-bce")) if (op.getBoolOption("wasm-check-bce"))
jit::JitOptions.wasmAlwaysCheckBounds = true; jit::JitOptions.wasmAlwaysCheckBounds = true;
@ -7845,7 +7848,8 @@ SetWorkerContextOptions(JSContext* cx)
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
.setNativeRegExp(enableNativeRegExp) .setNativeRegExp(enableNativeRegExp)
.setUnboxedArrays(enableUnboxedArrays) .setUnboxedArrays(enableUnboxedArrays)
.setArrayProtoValues(enableArrayProtoValues); .setArrayProtoValues(enableArrayProtoValues)
.setStreams(enableStreams);
cx->setOffthreadIonCompilationEnabled(offthreadCompilation); cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps; cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
@ -8019,6 +8023,7 @@ main(int argc, char** argv, char** envp)
|| !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible") || !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
|| !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.") || !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
|| !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values") || !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values")
|| !op.addBoolOption('\0', "enable-streams", "Enable WHATWG Streams")
#ifdef ENABLE_SHARED_ARRAY_BUFFER #ifdef ENABLE_SHARED_ARRAY_BUFFER
|| !op.addStringOption('\0', "shared-memory", "on/off", || !op.addStringOption('\0', "shared-memory", "on/off",
"SharedArrayBuffer and Atomics " "SharedArrayBuffer and Atomics "

View file

@ -1255,15 +1255,16 @@ ArrayBufferObject::finalize(FreeOp* fop, JSObject* obj)
} }
/* static */ void /* static */ void
ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
Handle<ArrayBufferObject*> fromBuffer, Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
uint32_t fromIndex, uint32_t count) uint32_t count)
{ {
MOZ_ASSERT(toBuffer->byteLength() >= count); MOZ_ASSERT(toBuffer->byteLength() >= count);
MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
memcpy(toBuffer->dataPointer(), fromBuffer->dataPointer() + fromIndex, count); memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
} }
/* static */ void /* static */ void
@ -1890,6 +1891,17 @@ JS_GetArrayBufferViewByteLength(JSObject* obj)
: obj->as<TypedArrayObject>().byteLength(); : obj->as<TypedArrayObject>().byteLength();
} }
JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteOffset(JSObject* obj)
{
obj = CheckedUnwrap(obj);
if (!obj)
return 0;
return obj->is<DataViewObject>()
? obj->as<DataViewObject>().byteOffset()
: obj->as<TypedArrayObject>().byteOffset();
}
JS_FRIEND_API(JSObject*) JS_FRIEND_API(JSObject*)
JS_GetObjectAsArrayBufferView(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data) JS_GetObjectAsArrayBufferView(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
{ {

View file

@ -261,9 +261,9 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
template<typename T> template<typename T>
static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp); static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);
static void copyData(Handle<ArrayBufferObject*> toBuffer, static void copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
Handle<ArrayBufferObject*> fromBuffer, Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
uint32_t fromIndex, uint32_t count); uint32_t count);
static void trace(JSTracer* trc, JSObject* obj); static void trace(JSTracer* trc, JSObject* obj);
static void objectMoved(JSObject* obj, const JSObject* old); static void objectMoved(JSObject* obj, const JSObject* old);

View file

@ -18,6 +18,8 @@
macro(apply, apply, "apply") \ macro(apply, apply, "apply") \
macro(args, args, "args") \ macro(args, args, "args") \
macro(arguments, arguments, "arguments") \ macro(arguments, arguments, "arguments") \
macro(AcquireReadableStreamBYOBReader, AcquireReadableStreamBYOBReader, "AcquireReadableStreamBYOBReader") \
macro(AcquireReadableStreamDefaultReader, AcquireReadableStreamDefaultReader, "AcquireReadableStreamDefaultReader") \
macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \ macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \
macro(ArrayIterator, ArrayIterator, "Array Iterator") \ macro(ArrayIterator, ArrayIterator, "Array Iterator") \
macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \ macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
@ -35,6 +37,7 @@
macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \ macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \ macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \ macro(async, async, "async") \
macro(autoAllocateChunkSize, autoAllocateChunkSize, "autoAllocateChunkSize") \
macro(await, await, "await") \ macro(await, await, "await") \
macro(bigint64, bigint64, "bigint64") \ macro(bigint64, bigint64, "bigint64") \
macro(biguint64, biguint64, "biguint64") \ macro(biguint64, biguint64, "biguint64") \
@ -48,6 +51,7 @@
macro(buffer, buffer, "buffer") \ macro(buffer, buffer, "buffer") \
macro(builder, builder, "builder") \ macro(builder, builder, "builder") \
macro(by, by, "by") \ macro(by, by, "by") \
macro(byob, byob, "byob") \
macro(byteAlignment, byteAlignment, "byteAlignment") \ macro(byteAlignment, byteAlignment, "byteAlignment") \
macro(byteLength, byteLength, "byteLength") \ macro(byteLength, byteLength, "byteLength") \
macro(byteOffset, byteOffset, "byteOffset") \ macro(byteOffset, byteOffset, "byteOffset") \
@ -59,6 +63,7 @@
macro(callee, callee, "callee") \ macro(callee, callee, "callee") \
macro(caller, caller, "caller") \ macro(caller, caller, "caller") \
macro(callFunction, callFunction, "callFunction") \ macro(callFunction, callFunction, "callFunction") \
macro(cancel, cancel, "cancel") \
macro(case, case_, "case") \ macro(case, case_, "case") \
macro(caseFirst, caseFirst, "caseFirst") \ macro(caseFirst, caseFirst, "caseFirst") \
macro(catch, catch_, "catch") \ macro(catch, catch_, "catch") \
@ -180,6 +185,7 @@
macro(has, has, "has") \ macro(has, has, "has") \
macro(hasOwn, hasOwn, "hasOwn") \ macro(hasOwn, hasOwn, "hasOwn") \
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \ macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
macro(highWaterMark, highWaterMark, "highWaterMark") \
macro(hour, hour, "hour") \ macro(hour, hour, "hour") \
macro(hourCycle, hourCycle, "hourCycle") \ macro(hourCycle, hourCycle, "hourCycle") \
macro(if, if_, "if") \ macro(if, if_, "if") \
@ -250,6 +256,7 @@
macro(minusSign, minusSign, "minusSign") \ macro(minusSign, minusSign, "minusSign") \
macro(minute, minute, "minute") \ macro(minute, minute, "minute") \
macro(missingArguments, missingArguments, "missingArguments") \ macro(missingArguments, missingArguments, "missingArguments") \
macro(mode, mode, "mode") \
macro(module, module, "module") \ macro(module, module, "module") \
macro(Module, Module, "Module") \ macro(Module, Module, "Module") \
macro(ModuleInstantiate, ModuleInstantiate, "ModuleInstantiate") \ macro(ModuleInstantiate, ModuleInstantiate, "ModuleInstantiate") \
@ -312,7 +319,52 @@
macro(prototype, prototype, "prototype") \ macro(prototype, prototype, "prototype") \
macro(proxy, proxy, "proxy") \ macro(proxy, proxy, "proxy") \
macro(public, public_, "public") \ macro(public, public_, "public") \
macro(pull, pull, "pull") \
macro(raw, raw, "raw") \ macro(raw, raw, "raw") \
macro(ReadableByteStreamControllerGetDesiredSize, \
ReadableByteStreamControllerGetDesiredSize, \
"ReadableByteStreamControllerGetDesiredSize") \
macro(ReadableByteStreamController_close, \
ReadableByteStreamController_close, \
"ReadableByteStreamController_close") \
macro(ReadableByteStreamController_enqueue, \
ReadableByteStreamController_enqueue, \
"ReadableByteStreamController_enqueue") \
macro(ReadableByteStreamController_error, \
ReadableByteStreamController_error, \
"ReadableByteStreamController_error") \
macro(ReadableStreamBYOBReader_cancel, \
ReadableStreamBYOBReader_cancel, \
"ReadableStreamBYOBReader_cancel") \
macro(ReadableStreamBYOBReader_read, \
ReadableStreamBYOBReader_read, \
"ReadableStreamBYOBReader_read") \
macro(ReadableStreamBYOBReader_releaseLock, \
ReadableStreamBYOBReader_releaseLock, \
"ReadableStreamBYOBReader_releaseLock") \
macro(ReadableStream_cancel, ReadableStream_cancel, "ReadableStream_cancel") \
macro(ReadableStreamDefaultControllerGetDesiredSize, \
ReadableStreamDefaultControllerGetDesiredSize, \
"ReadableStreamDefaultControllerGetDesiredSize") \
macro(ReadableStreamDefaultController_close, \
ReadableStreamDefaultController_close, \
"ReadableStreamDefaultController_close") \
macro(ReadableStreamDefaultController_enqueue, \
ReadableStreamDefaultController_enqueue, \
"ReadableStreamDefaultController_enqueue") \
macro(ReadableStreamDefaultController_error, \
ReadableStreamDefaultController_error, \
"ReadableStreamDefaultController_error") \
macro(ReadableStreamDefaultReader_cancel, \
ReadableStreamDefaultReader_cancel, \
"ReadableStreamDefaultReader_cancel") \
macro(ReadableStreamDefaultReader_read, \
ReadableStreamDefaultReader_read, \
"ReadableStreamDefaultReader_read") \
macro(ReadableStreamDefaultReader_releaseLock, \
ReadableStreamDefaultReader_releaseLock, \
"ReadableStreamDefaultReader_releaseLock") \
macro(ReadableStreamTee, ReadableStreamTee, "ReadableStreamTee") \
macro(reason, reason, "reason") \ macro(reason, reason, "reason") \
macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \ macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \
macro(RegExpStringIterator, RegExpStringIterator, "RegExp String Iterator") \ macro(RegExpStringIterator, RegExpStringIterator, "RegExp String Iterator") \
@ -347,6 +399,7 @@
macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \ macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \ macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \ macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
macro(start, start, "start") \
macro(startTimestamp, startTimestamp, "startTimestamp") \ macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(state, state, "state") \ macro(state, state, "state") \
macro(static, static_, "static") \ macro(static, static_, "static") \

View file

@ -23,6 +23,7 @@
#include "builtin/Promise.h" #include "builtin/Promise.h"
#include "builtin/RegExp.h" #include "builtin/RegExp.h"
#include "builtin/SelfHostingDefines.h" #include "builtin/SelfHostingDefines.h"
#include "builtin/Stream.h"
#include "builtin/SymbolObject.h" #include "builtin/SymbolObject.h"
#include "builtin/TypedObject.h" #include "builtin/TypedObject.h"
#include "builtin/WeakMapObject.h" #include "builtin/WeakMapObject.h"
@ -98,6 +99,16 @@ GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
case JSProto_WebAssembly: case JSProto_WebAssembly:
return !wasm::HasSupport(cx); return !wasm::HasSupport(cx);
case JSProto_ReadableStream:
case JSProto_ReadableStreamDefaultReader:
case JSProto_ReadableStreamBYOBReader:
case JSProto_ReadableStreamDefaultController:
case JSProto_ReadableByteStreamController:
case JSProto_ReadableStreamBYOBRequest:
case JSProto_ByteLengthQueuingStrategy:
case JSProto_CountQueuingStrategy:
return !cx->options().streams();
#ifdef ENABLE_SHARED_ARRAY_BUFFER #ifdef ENABLE_SHARED_ARRAY_BUFFER
case JSProto_Atomics: case JSProto_Atomics:
case JSProto_SharedArrayBuffer: case JSProto_SharedArrayBuffer:

View file

@ -156,6 +156,12 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
startAsyncTaskCallback(nullptr), startAsyncTaskCallback(nullptr),
finishAsyncTaskCallback(nullptr), finishAsyncTaskCallback(nullptr),
promiseTasksToDestroy(mutexid::PromiseTaskPtrVector), promiseTasksToDestroy(mutexid::PromiseTaskPtrVector),
readableStreamDataRequestCallback(nullptr),
readableStreamWriteIntoReadRequestCallback(nullptr),
readableStreamCancelCallback(nullptr),
readableStreamClosedCallback(nullptr),
readableStreamErroredCallback(nullptr),
readableStreamFinalizeCallback(nullptr),
exclusiveAccessLock(mutexid::RuntimeExclusiveAccess), exclusiveAccessLock(mutexid::RuntimeExclusiveAccess),
#ifdef DEBUG #ifdef DEBUG
mainThreadHasExclusiveAccess(false), mainThreadHasExclusiveAccess(false),

View file

@ -655,6 +655,13 @@ struct JSRuntime : public JS::shadow::Runtime,
JS::FinishAsyncTaskCallback finishAsyncTaskCallback; JS::FinishAsyncTaskCallback finishAsyncTaskCallback;
js::ExclusiveData<js::PromiseTaskPtrVector> promiseTasksToDestroy; js::ExclusiveData<js::PromiseTaskPtrVector> promiseTasksToDestroy;
JS::RequestReadableStreamDataCallback readableStreamDataRequestCallback;
JS::WriteIntoReadRequestBufferCallback readableStreamWriteIntoReadRequestCallback;
JS::CancelReadableStreamCallback readableStreamCancelCallback;
JS::ReadableStreamClosedCallback readableStreamClosedCallback;
JS::ReadableStreamErroredCallback readableStreamErroredCallback;
JS::ReadableStreamFinalizeCallback readableStreamFinalizeCallback;
private: private:
/* /*
* Lock taken when using per-runtime or per-zone data that could otherwise * Lock taken when using per-runtime or per-zone data that could otherwise

View file

@ -36,6 +36,7 @@
#include "builtin/Promise.h" #include "builtin/Promise.h"
#include "builtin/Reflect.h" #include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h" #include "builtin/SelfHostingDefines.h"
#include "builtin/Stream.h"
#include "builtin/TypedObject.h" #include "builtin/TypedObject.h"
#include "builtin/WeakSetObject.h" #include "builtin/WeakSetObject.h"
#include "gc/Marking.h" #include "gc/Marking.h"
@ -71,7 +72,6 @@ using JS::AutoCheckCannotGC;
using mozilla::IsInRange; using mozilla::IsInRange;
using mozilla::Maybe; using mozilla::Maybe;
using mozilla::PodMove; using mozilla::PodMove;
using mozilla::Maybe;
static void static void
selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report)
@ -1098,9 +1098,9 @@ static bool
intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp) intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 5); MOZ_ASSERT(args.length() == 6);
bool isWrapped = args[4].toBoolean(); bool isWrapped = args[5].toBoolean();
Rooted<T*> toBuffer(cx); Rooted<T*> toBuffer(cx);
if (!isWrapped) { if (!isWrapped) {
toBuffer = &args[0].toObject().as<T>(); toBuffer = &args[0].toObject().as<T>();
@ -1114,11 +1114,12 @@ intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp)
} }
toBuffer = toBufferObj.as<T>(); toBuffer = toBufferObj.as<T>();
} }
Rooted<T*> fromBuffer(cx, &args[1].toObject().as<T>()); uint32_t toIndex = uint32_t(args[1].toInt32());
uint32_t fromIndex = uint32_t(args[2].toInt32()); Rooted<T*> fromBuffer(cx, &args[2].toObject().as<T>());
uint32_t count = uint32_t(args[3].toInt32()); uint32_t fromIndex = uint32_t(args[3].toInt32());
uint32_t count = uint32_t(args[4].toInt32());
T::copyData(toBuffer, fromBuffer, fromIndex, count); T::copyData(toBuffer, toIndex, fromBuffer, fromIndex, count);
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
@ -2426,14 +2427,14 @@ static const JSFunctionSpec intrinsic_functions[] = {
intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1,0, intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1,0,
IntrinsicPossiblyWrappedArrayBufferByteLength), IntrinsicPossiblyWrappedArrayBufferByteLength),
JS_FN("ArrayBufferCopyData", JS_FN("ArrayBufferCopyData",
intrinsic_ArrayBufferCopyData<ArrayBufferObject>, 5,0), intrinsic_ArrayBufferCopyData<ArrayBufferObject>, 6,0),
JS_FN("SharedArrayBufferByteLength", JS_FN("SharedArrayBufferByteLength",
intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>, 1,0), intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>, 1,0),
JS_FN("PossiblyWrappedSharedArrayBufferByteLength", JS_FN("PossiblyWrappedSharedArrayBufferByteLength",
intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, 1,0), intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, 1,0),
JS_FN("SharedArrayBufferCopyData", JS_FN("SharedArrayBufferCopyData",
intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>, 5,0), intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>, 6,0),
JS_FN("IsUint8TypedArray", intrinsic_IsUint8TypedArray, 1,0), JS_FN("IsUint8TypedArray", intrinsic_IsUint8TypedArray, 1,0),
JS_FN("IsInt8TypedArray", intrinsic_IsInt8TypedArray, 1,0), JS_FN("IsInt8TypedArray", intrinsic_IsInt8TypedArray, 1,0),
@ -2489,6 +2490,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallWeakSetMethodIfWrapped", JS_FN("CallWeakSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0), CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
JS_FN("IsReadableStreamBYOBRequest",
intrinsic_IsInstanceOfBuiltin<ReadableStreamBYOBRequest>, 1, 0),
// See builtin/TypedObject.h for descriptors of the typedobj functions. // See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0), JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0), JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),

View file

@ -331,15 +331,16 @@ SharedArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSi
} }
/* static */ void /* static */ void
SharedArrayBufferObject::copyData(Handle<SharedArrayBufferObject*> toBuffer, SharedArrayBufferObject::copyData(Handle<SharedArrayBufferObject*> toBuffer, uint32_t toIndex,
Handle<SharedArrayBufferObject*> fromBuffer, Handle<SharedArrayBufferObject*> fromBuffer, uint32_t fromIndex,
uint32_t fromIndex, uint32_t count) uint32_t count)
{ {
MOZ_ASSERT(toBuffer->byteLength() >= count); MOZ_ASSERT(toBuffer->byteLength() >= count);
MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count); MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared(), jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared() + toIndex,
fromBuffer->dataPointerShared() + fromIndex, fromBuffer->dataPointerShared() + fromIndex,
count); count);
} }

View file

@ -146,9 +146,9 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info); JS::ClassInfo* info);
static void copyData(Handle<SharedArrayBufferObject*> toBuffer, static void copyData(Handle<SharedArrayBufferObject*> toBuffer, uint32_t toIndex,
Handle<SharedArrayBufferObject*> fromBuffer, Handle<SharedArrayBufferObject*> fromBuffer, uint32_t fromIndex,
uint32_t fromIndex, uint32_t count); uint32_t count);
SharedArrayRawBuffer* rawBufferObject() const; SharedArrayRawBuffer* rawBufferObject() const;

View file

@ -1438,6 +1438,8 @@ ReloadPrefsCallback(const char* pref, void* data)
bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror"); bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict"); bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects"); bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
@ -1463,7 +1465,8 @@ ReloadPrefsCallback(const char* pref, void* data)
.setDumpStackOnDebuggeeWouldRun(dumpStackOnDebuggeeWouldRun) .setDumpStackOnDebuggeeWouldRun(dumpStackOnDebuggeeWouldRun)
.setWerror(werror) .setWerror(werror)
.setExtraWarnings(extraWarnings) .setExtraWarnings(extraWarnings)
.setArrayProtoValues(arrayProtoValues); .setArrayProtoValues(arrayProtoValues)
.setStreams(streams);
JS_SetParallelParsingEnabled(cx, parallelParsing); JS_SetParallelParsingEnabled(cx, parallelParsing);
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation); JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);

View file

@ -1331,6 +1331,9 @@ pref("javascript.options.main_thread_stack_quota_cap", 2097152);
// Dynamic module import. // Dynamic module import.
pref("javascript.options.dynamicImport", true); pref("javascript.options.dynamicImport", true);
// Streams API
pref("javascript.options.streams", true);
// advanced prefs // advanced prefs
pref("advanced.mailftp", false); pref("advanced.mailftp", false);
pref("image.animation_mode", "normal"); pref("image.animation_mode", "normal");
@ -4589,6 +4592,9 @@ pref("layout.animated-image-layers.enabled", false);
// Abort API // Abort API
pref("dom.abortController.enabled", true); pref("dom.abortController.enabled", true);
// Streams API
pref("dom.streams.enabled", true);
// Push // Push
pref("dom.push.enabled", false); pref("dom.push.enabled", false);
pref("dom.push.loglevel", "error"); pref("dom.push.loglevel", "error");