mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-26 09:07:30 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
a55674f653
89 changed files with 10466 additions and 916 deletions
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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': {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
28
dom/bindings/ReadableStream.h
Normal file
28
dom/bindings/ReadableStream.h
Normal 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 */
|
||||||
144
dom/bindings/SpiderMonkeyInterface.h
Normal file
144
dom/bindings/SpiderMonkeyInterface.h
Normal 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 */
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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]]
|
||||||
|
|
||||||
|
|
|
||||||
8
dom/cache/AutoUtils.cpp
vendored
8
dom/cache/AutoUtils.cpp
vendored
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
dom/cache/AutoUtils.h
vendored
2
dom/cache/AutoUtils.h
vendored
|
|
@ -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
40
dom/cache/Cache.cpp
vendored
|
|
@ -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
16
dom/cache/Cache.h
vendored
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
12
dom/cache/CacheOpChild.cpp
vendored
12
dom/cache/CacheOpChild.cpp
vendored
|
|
@ -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;
|
||||||
|
|
|
||||||
9
dom/cache/CacheStorage.cpp
vendored
9
dom/cache/CacheStorage.cpp
vendored
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
dom/cache/CacheStorage.h
vendored
6
dom/cache/CacheStorage.h
vendored
|
|
@ -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);
|
||||||
|
|
|
||||||
31
dom/cache/CacheWorkerHolder.cpp
vendored
31
dom/cache/CacheWorkerHolder.cpp
vendored
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
8
dom/cache/CacheWorkerHolder.h
vendored
8
dom/cache/CacheWorkerHolder.h
vendored
|
|
@ -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;
|
||||||
|
|
|
||||||
25
dom/cache/TypeUtils.cpp
vendored
25
dom/cache/TypeUtils.cpp
vendored
|
|
@ -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
12
dom/cache/TypeUtils.h
vendored
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
207
dom/fetch/BodyExtractor.cpp
Normal 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
48
dom/fetch/BodyExtractor.h
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
640
dom/fetch/FetchStream.cpp
Normal 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
158
dom/fetch/FetchStream.h
Normal 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
|
||||||
405
dom/fetch/FetchStreamReader.cpp
Normal file
405
dom/fetch/FetchStreamReader.cpp
Normal 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
|
||||||
85
dom/fetch/FetchStreamReader.h
Normal file
85
dom/fetch/FetchStreamReader.h
Normal 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
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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")));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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()) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
510
js/public/Stream.h
Normal 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
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
170
js/src/builtin/Stream.h
Normal 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 */
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
|
|
@ -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++.
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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 += [
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
676
js/src/jsapi-tests/testReadableStream.cpp
Normal file
676
js/src/jsapi-tests/testReadableStream.cpp
Normal 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)
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
366
js/src/jsapi.cpp
366
js/src/jsapi.cpp
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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&)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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 "
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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") \
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue