mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1186307 - StructuredCloneHelper in workers.postMessage(), r=smaug
This commit is contained in:
parent
f7df8f3619
commit
6e34f6b5b0
@ -113,8 +113,11 @@ PostMessageEvent::Run()
|
||||
false /*cancelable */, messageData, mCallerOrigin,
|
||||
EmptyString(), mSource);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
TakeTransferredPorts(ports);
|
||||
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
GetTransferredPorts()));
|
||||
ports));
|
||||
|
||||
// We can't simply call dispatchEvent on the window because doing so ends
|
||||
// up flipping the trusted bit on the event, and we don't want that to
|
||||
|
@ -14,11 +14,24 @@
|
||||
#include "mozilla/dom/FileListBinding.h"
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/dom/ImageDataBinding.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/StructuredClone.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "MultipartBlobImpl.h"
|
||||
#include "nsFormData.h"
|
||||
#include "nsIRemoteBlob.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -142,11 +155,7 @@ bool
|
||||
StructuredCloneHelperInternal::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
|
||||
MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown.");
|
||||
|
||||
mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
|
||||
return mBuffer->write(aCx, aValue, &gCallbacks, this);
|
||||
return Write(aCx, aValue, JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -217,24 +226,37 @@ StructuredCloneHelper::StructuredCloneHelper(CloningSupport aSupportsCloning,
|
||||
StructuredCloneHelper::~StructuredCloneHelper()
|
||||
{
|
||||
Shutdown();
|
||||
MOZ_ASSERT(mTransferredPorts.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHelper::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
bool aMaybeToDifferentThread,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
|
||||
Write(aCx, aValue, JS::UndefinedHandleValue, aMaybeToDifferentThread, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHelper::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
bool aMaybeToDifferentThread,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aMaybeToDifferentThread) {
|
||||
for (uint32_t i = 0, len = mBlobImplArray.Length(); i < len; ++i) {
|
||||
if (!mBlobImplArray[i]->MayBeClonedToOtherThreads()) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,6 +346,346 @@ StructuredCloneHelper::FreeBuffer(uint64_t* aBuffer,
|
||||
JS_ClearStructuredClone(aBuffer, aBufferLength, &gCallbacks, this, false);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Recursive!
|
||||
already_AddRefed<BlobImpl>
|
||||
EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
|
||||
PBackgroundChild* aManager = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
if (!aManager) {
|
||||
aManager = BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(aManager);
|
||||
}
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = aBlobImpl;
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>* subBlobImpls =
|
||||
aBlobImpl->GetSubBlobImpls();
|
||||
|
||||
if (!subBlobImpls || !subBlobImpls->Length()) {
|
||||
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
|
||||
// Always make sure we have a blob from an actor we can use on this
|
||||
// thread.
|
||||
BlobChild* blobChild = BlobChild::GetOrCreate(aManager, blobImpl);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
blobImpl = blobChild->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
const uint32_t subBlobCount = subBlobImpls->Length();
|
||||
MOZ_ASSERT(subBlobCount);
|
||||
|
||||
nsTArray<nsRefPtr<BlobImpl>> newSubBlobImpls;
|
||||
newSubBlobImpls.SetLength(subBlobCount);
|
||||
|
||||
bool newBlobImplNeeded = false;
|
||||
|
||||
for (uint32_t index = 0; index < subBlobCount; index++) {
|
||||
const nsRefPtr<BlobImpl>& subBlobImpl = subBlobImpls->ElementAt(index);
|
||||
MOZ_ASSERT(subBlobImpl);
|
||||
|
||||
nsRefPtr<BlobImpl>& newSubBlobImpl = newSubBlobImpls[index];
|
||||
|
||||
newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager);
|
||||
MOZ_ASSERT(newSubBlobImpl);
|
||||
|
||||
if (subBlobImpl != newSubBlobImpl) {
|
||||
newBlobImplNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBlobImplNeeded) {
|
||||
nsString contentType;
|
||||
blobImpl->GetType(contentType);
|
||||
|
||||
if (blobImpl->IsFile()) {
|
||||
nsString name;
|
||||
blobImpl->GetName(name);
|
||||
|
||||
blobImpl = new MultipartBlobImpl(newSubBlobImpls, name, contentType);
|
||||
} else {
|
||||
blobImpl = new MultipartBlobImpl(newSubBlobImpls, contentType);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ReadBlob(JSContext* aCx,
|
||||
uint32_t aIndex,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aHelper);
|
||||
MOZ_ASSERT(aIndex < aHelper->BlobImpls().Length());
|
||||
nsRefPtr<BlobImpl> blobImpl = aHelper->BlobImpls()[aIndex];
|
||||
|
||||
blobImpl = EnsureBlobForBackgroundManager(blobImpl);
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(aHelper->ParentDuringRead(), blobImpl);
|
||||
if (!ToJSValue(aCx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
bool
|
||||
WriteBlob(JSStructuredCloneWriter* aWriter,
|
||||
Blob* aBlob,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aBlob);
|
||||
MOZ_ASSERT(aHelper);
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlob->Impl());
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// We store the position of the blobImpl in the array as index.
|
||||
if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
|
||||
aHelper->BlobImpls().Length())) {
|
||||
aHelper->BlobImpls().AppendElement(blobImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the WriteFileList for the format.
|
||||
JSObject*
|
||||
ReadFileList(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aCount,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aReader);
|
||||
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<FileList> fileList = new FileList(aHelper->ParentDuringRead());
|
||||
|
||||
uint32_t tag, offset;
|
||||
// Offset is the index of the blobImpl from which we can find the blobImpl
|
||||
// for this FileList.
|
||||
if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(tag == 0);
|
||||
|
||||
// |aCount| is the number of BlobImpls to use from the |offset|.
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
uint32_t index = offset + i;
|
||||
MOZ_ASSERT(index < aHelper->BlobImpls().Length());
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = aHelper->BlobImpls()[index];
|
||||
MOZ_ASSERT(blobImpl->IsFile());
|
||||
|
||||
nsRefPtr<File> file = File::Create(aHelper->ParentDuringRead(), blobImpl);
|
||||
if (!fileList->Append(file)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ToJSValue(aCx, fileList, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
// The format of the FileList serialization is:
|
||||
// - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
|
||||
// - pair of ints: 0, The offset of the BlobImpl array
|
||||
bool
|
||||
WriteFileList(JSStructuredCloneWriter* aWriter,
|
||||
FileList* aFileList,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aFileList);
|
||||
MOZ_ASSERT(aHelper);
|
||||
|
||||
// A FileList is serialized writing the X number of elements and the offset
|
||||
// from mBlobImplArray. The Read will take X elements from mBlobImplArray
|
||||
// starting from the offset.
|
||||
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
|
||||
aFileList->Length()) ||
|
||||
!JS_WriteUint32Pair(aWriter, 0,
|
||||
aHelper->BlobImpls().Length())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
|
||||
aHelper->BlobImpls().AppendElement(aFileList->Item(i)->Impl());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the WriteFormData for the format.
|
||||
JSObject*
|
||||
ReadFormData(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aCount,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aReader);
|
||||
MOZ_ASSERT(aHelper);
|
||||
|
||||
// See the serialization of the FormData for the format.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<nsFormData> formData =
|
||||
new nsFormData(aHelper->ParentDuringRead());
|
||||
|
||||
Optional<nsAString> thirdArg;
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
nsAutoString name;
|
||||
if (!ReadString(aReader, name)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t tag, indexOrLengthOfString;
|
||||
if (!JS_ReadUint32Pair(aReader, &tag, &indexOrLengthOfString)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB) {
|
||||
MOZ_ASSERT(indexOrLengthOfString < aHelper->BlobImpls().Length());
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl =
|
||||
aHelper->BlobImpls()[indexOrLengthOfString];
|
||||
MOZ_ASSERT(blobImpl->IsFile());
|
||||
|
||||
nsRefPtr<File> file =
|
||||
File::Create(aHelper->ParentDuringRead(), blobImpl);
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
formData->Append(name, *file, thirdArg);
|
||||
} else {
|
||||
MOZ_ASSERT(tag == 0);
|
||||
|
||||
nsAutoString value;
|
||||
value.SetLength(indexOrLengthOfString);
|
||||
size_t charSize = sizeof(nsString::char_type);
|
||||
if (!JS_ReadBytes(aReader, (void*) value.BeginWriting(),
|
||||
indexOrLengthOfString * charSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
formData->Append(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ToJSValue(aCx, formData, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
// The format of the FormData serialization is:
|
||||
// - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements
|
||||
// - for each Element element:
|
||||
// - name string
|
||||
// - if it's a blob:
|
||||
// - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
|
||||
// mBlobImplArray.
|
||||
// - else:
|
||||
// - pair of ints: 0, string length
|
||||
// - value string
|
||||
bool
|
||||
WriteFormData(JSStructuredCloneWriter* aWriter,
|
||||
nsFormData* aFormData,
|
||||
StructuredCloneHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aFormData);
|
||||
MOZ_ASSERT(aHelper);
|
||||
|
||||
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA,
|
||||
aFormData->Length())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
class MOZ_STACK_CLASS Closure final
|
||||
{
|
||||
JSStructuredCloneWriter* mWriter;
|
||||
StructuredCloneHelper* mHelper;
|
||||
|
||||
public:
|
||||
Closure(JSStructuredCloneWriter* aWriter,
|
||||
StructuredCloneHelper* aHelper)
|
||||
: mWriter(aWriter),
|
||||
mHelper(aHelper)
|
||||
{ }
|
||||
|
||||
static bool
|
||||
Write(const nsString& aName, bool isFile, const nsString& aValue,
|
||||
File* aFile, void* aClosure)
|
||||
{
|
||||
Closure* closure = static_cast<Closure*>(aClosure);
|
||||
if (!WriteString(closure->mWriter, aName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFile) {
|
||||
BlobImpl* blobImpl = aFile->Impl();
|
||||
if (!JS_WriteUint32Pair(closure->mWriter, SCTAG_DOM_BLOB,
|
||||
closure->mHelper->BlobImpls().Length())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mHelper->BlobImpls().AppendElement(blobImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t charSize = sizeof(nsString::char_type);
|
||||
if (!JS_WriteUint32Pair(closure->mWriter, 0, aValue.Length()) ||
|
||||
!JS_WriteBytes(closure->mWriter, aValue.get(),
|
||||
aValue.Length() * charSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Closure closure(aWriter, aHelper);
|
||||
return aFormData->ForEach(Closure::Write, &closure);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
JSObject*
|
||||
StructuredCloneHelper::ReadCallback(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
@ -333,56 +695,19 @@ StructuredCloneHelper::ReadCallback(JSContext* aCx,
|
||||
MOZ_ASSERT(mSupportsCloning);
|
||||
|
||||
if (aTag == SCTAG_DOM_BLOB) {
|
||||
MOZ_ASSERT(aIndex < mBlobImplArray.Length());
|
||||
nsRefPtr<BlobImpl> blobImpl = mBlobImplArray[aIndex];
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
|
||||
if (!ToJSValue(aCx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
return ReadBlob(aCx, aIndex, this);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_FILELIST) {
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<FileList> fileList = new FileList(mParent);
|
||||
return ReadFileList(aCx, aReader, aIndex, this);
|
||||
}
|
||||
|
||||
// |aIndex| is the number of BlobImpls to use from |offset|.
|
||||
uint32_t tag, offset;
|
||||
if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(tag == 0);
|
||||
if (aTag == SCTAG_DOM_IMAGEDATA) {
|
||||
return ReadStructuredCloneImageData(aCx, aReader);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aIndex; ++i) {
|
||||
uint32_t index = offset + i;
|
||||
MOZ_ASSERT(index < mBlobImplArray.Length());
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = mBlobImplArray[index];
|
||||
MOZ_ASSERT(blobImpl->IsFile());
|
||||
|
||||
nsRefPtr<File> file = File::Create(mParent, blobImpl);
|
||||
if (!fileList->Append(file)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ToJSValue(aCx, fileList, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
if (aTag == SCTAG_DOM_FORMDATA) {
|
||||
return ReadFormData(aCx, aReader, aIndex, this);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
|
||||
@ -410,35 +735,31 @@ StructuredCloneHelper::WriteCallback(JSContext* aCx,
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
|
||||
mBlobImplArray.Length())) {
|
||||
mBlobImplArray.AppendElement(blobImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return WriteBlob(aWriter, blob, this);
|
||||
}
|
||||
}
|
||||
|
||||
// See if this is a FileList object.
|
||||
{
|
||||
FileList* fileList = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
|
||||
// A FileList is serialized writing the X number of elements and the offset
|
||||
// from mBlobImplArray. The Read will take X elements from mBlobImplArray
|
||||
// starting from the offset.
|
||||
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
|
||||
fileList->Length()) ||
|
||||
!JS_WriteUint32Pair(aWriter, 0,
|
||||
mBlobImplArray.Length())) {
|
||||
return false;
|
||||
}
|
||||
return WriteFileList(aWriter, fileList, this);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < fileList->Length(); ++i) {
|
||||
mBlobImplArray.AppendElement(fileList->Item(i)->Impl());
|
||||
}
|
||||
// See if this is a ImageData object.
|
||||
{
|
||||
ImageData* imageData = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
|
||||
return WriteStructuredCloneImageData(aCx, aWriter, imageData);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// See if this is a FormData object.
|
||||
{
|
||||
nsFormData* formData = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
|
||||
return WriteFormData(aWriter, formData, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,11 @@ public:
|
||||
bool Read(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue);
|
||||
|
||||
bool HasBeenWritten() const
|
||||
{
|
||||
return !!mBuffer;
|
||||
}
|
||||
|
||||
uint64_t* BufferData() const
|
||||
{
|
||||
MOZ_ASSERT(mBuffer, "Write() has never been called.");
|
||||
@ -132,11 +137,13 @@ public:
|
||||
|
||||
void Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
bool aMaybeToDifferentThread,
|
||||
ErrorResult &aRv);
|
||||
|
||||
void Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
bool aMaybeToDifferentThread,
|
||||
ErrorResult &aRv);
|
||||
|
||||
void Read(nsISupports* aParent,
|
||||
@ -184,10 +191,19 @@ public:
|
||||
return mBlobImplArray;
|
||||
}
|
||||
|
||||
const nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts() const
|
||||
nsISupports* ParentDuringRead() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
// This must be called if the transferring has ports generated by Read().
|
||||
// MessagePorts are not thread-safe and they must be retrieved in the thread
|
||||
// where they are created.
|
||||
void TakeTransferredPorts(nsTArray<nsRefPtr<MessagePortBase>>& aPorts)
|
||||
{
|
||||
MOZ_ASSERT(mSupportsTransferring);
|
||||
return mTransferredPorts;
|
||||
MOZ_ASSERT(aPorts.IsEmpty());
|
||||
aPorts.SwapElements(mTransferredPorts);
|
||||
}
|
||||
|
||||
nsTArray<MessagePortIdentifier>& PortIdentifiers()
|
||||
|
@ -46,6 +46,8 @@ enum StructuredCloneTags {
|
||||
|
||||
SCTAG_DOM_RTC_CERTIFICATE,
|
||||
|
||||
SCTAG_DOM_FORMDATA,
|
||||
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
|
@ -8635,7 +8635,7 @@ nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessa
|
||||
JS::Rooted<JS::Value> message(aCx, aMessage);
|
||||
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
|
||||
|
||||
event->Write(aCx, message, transfer, aError);
|
||||
event->Write(aCx, message, transfer, false, aError);
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
Write(aCx, aData, rv);
|
||||
Write(aCx, aData, true, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
@ -836,4 +836,5 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_window_element_enumeration.html]
|
||||
[test_referrer_redirect.html]
|
||||
[test_postMessages.html]
|
||||
support-files = worker_postMessages.js
|
||||
[test_window_proto.html]
|
||||
|
@ -278,6 +278,51 @@ function test_windowToIframeURL(url) {
|
||||
document.body.appendChild(ifr);
|
||||
}
|
||||
|
||||
// PostMessage for Workers
|
||||
function test_workers() {
|
||||
info("Testing Workers");
|
||||
|
||||
var resolve;
|
||||
|
||||
var w = new Worker('worker_postMessages.js');
|
||||
w.postMessage('workers');
|
||||
w.onmessage = function(e) {
|
||||
is(e.data, 'ok', "Worker ready!");
|
||||
|
||||
w.onmessage = function(e) {
|
||||
if (!resolve) {
|
||||
ok(false, "Unexpected message!");
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp = resolve;
|
||||
resolve = null;
|
||||
tmp({ data: e.data, ports: e.ports });
|
||||
}
|
||||
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
try {
|
||||
w.postMessage(what, ports);
|
||||
} catch(e) {
|
||||
resolve = null;
|
||||
rr();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
finished: function() {
|
||||
onmessage = null;
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// PostMessage for BroadcastChannel
|
||||
function test_broadcastChannel() {
|
||||
info("Testing broadcastChannel");
|
||||
@ -320,6 +365,52 @@ function test_broadcastChannel() {
|
||||
});
|
||||
}
|
||||
|
||||
// PostMessage for BroadcastChannel in workers
|
||||
function test_broadcastChannel_inWorkers() {
|
||||
info("Testing broadcastChannel in Workers");
|
||||
|
||||
var bc = new BroadcastChannel('postMessagesTest_inWorkers');
|
||||
var resolve;
|
||||
|
||||
var w = new Worker('worker_postMessages.js');
|
||||
w.postMessage('broadcastChannel');
|
||||
w.onmessage = function(e) {
|
||||
is(e.data, 'ok', "Worker ready!");
|
||||
|
||||
w.onmessage = function(e) {
|
||||
if (!resolve) {
|
||||
ok(false, "Unexpected message!");
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp = resolve;
|
||||
resolve = null;
|
||||
tmp({ data: e.data, ports: e.ports });
|
||||
}
|
||||
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: false,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
if (ports.length) {
|
||||
rr();
|
||||
return;
|
||||
}
|
||||
|
||||
resolve = r;
|
||||
bc.postMessage(what);
|
||||
});
|
||||
},
|
||||
|
||||
finished: function() {
|
||||
onmessage = null;
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// PostMessage for MessagePort
|
||||
function test_messagePort() {
|
||||
info("Testing messagePort");
|
||||
@ -360,6 +451,52 @@ function test_messagePort() {
|
||||
});
|
||||
}
|
||||
|
||||
// PostMessage for MessagePort in Workers
|
||||
function test_messagePort_inWorkers() {
|
||||
info("Testing messagePort in workers");
|
||||
|
||||
var mc = new MessageChannel();
|
||||
var resolve;
|
||||
|
||||
var w = new Worker('worker_postMessages.js');
|
||||
w.postMessage('messagePort', [ mc.port2 ]);
|
||||
w.onmessage = function(e) {
|
||||
is(e.data, 'ok', "Worker ready!");
|
||||
|
||||
w.onmessage = function(e) {
|
||||
if (!resolve) {
|
||||
ok(false, "Unexpected message!");
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp = resolve;
|
||||
resolve = null;
|
||||
tmp({ data: e.data, ports: e.ports });
|
||||
}
|
||||
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
try {
|
||||
mc.port1.postMessage(what, ports);
|
||||
} catch(e) {
|
||||
resolve = null;
|
||||
rr();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
finished: function() {
|
||||
onmessage = null;
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var tests = [
|
||||
create_fileList,
|
||||
|
||||
@ -367,11 +504,13 @@ var tests = [
|
||||
test_windowToIframe,
|
||||
test_windowToCrossOriginIframe,
|
||||
|
||||
test_workers,
|
||||
|
||||
test_broadcastChannel,
|
||||
// TODO BroadcastChannel in worker
|
||||
test_broadcastChannel_inWorkers,
|
||||
|
||||
test_messagePort,
|
||||
// TODO MessagePort in worker
|
||||
test_messagePort_inWorkers,
|
||||
];
|
||||
|
||||
function next() {
|
||||
|
33
dom/base/test/worker_postMessages.js
Normal file
33
dom/base/test/worker_postMessages.js
Normal file
@ -0,0 +1,33 @@
|
||||
function test_workers() {
|
||||
onmessage = function(e) {
|
||||
postMessage(e.data, e.ports);
|
||||
}
|
||||
}
|
||||
|
||||
function test_broadcastChannel() {
|
||||
var bc = new BroadcastChannel('postMessagesTest_inWorkers');
|
||||
bc.onmessage = function(e) {
|
||||
postMessage(e.data);
|
||||
}
|
||||
}
|
||||
|
||||
function test_messagePort(port) {
|
||||
port.onmessage = function(e) {
|
||||
postMessage(e.data, e.ports);
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = function(e) {
|
||||
if (e.data == 'workers') {
|
||||
test_workers();
|
||||
postMessage('ok');
|
||||
} else if (e.data == 'broadcastChannel') {
|
||||
test_broadcastChannel();
|
||||
postMessage('ok');
|
||||
} else if (e.data == 'messagePort') {
|
||||
test_messagePort(e.ports[0]);
|
||||
postMessage('ok');
|
||||
} else {
|
||||
postMessage('ko');
|
||||
}
|
||||
}
|
@ -454,19 +454,11 @@ BroadcastChannel::PostMessageInternal(JSContext* aCx,
|
||||
{
|
||||
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
|
||||
|
||||
data->Write(aCx, aMessage, aRv);
|
||||
data->Write(aCx, aMessage, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PostMessageData(data);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "imgTools.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "libyuv.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
|
@ -140,9 +140,12 @@ public:
|
||||
event->SetTrusted(true);
|
||||
event->SetSource(mPort);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
mData->TakeTransferredPorts(ports);
|
||||
|
||||
nsRefPtr<MessagePortList> portList =
|
||||
new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
mData->GetTransferredPorts());
|
||||
ports);
|
||||
event->SetPorts(portList);
|
||||
|
||||
bool dummy;
|
||||
|
@ -46,19 +46,11 @@ SharedMessagePortMessage::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aTransfer,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
StructuredCloneHelper::Write(aCx, aValue, aTransfer, aRv);
|
||||
StructuredCloneHelper::Write(aCx, aValue, aTransfer, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = BlobImpls();
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FallibleTArray<uint8_t> cloneData;
|
||||
|
||||
MoveBufferDataToArray(cloneData, aRv);
|
||||
|
@ -233,7 +233,7 @@ public:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// This needs to be structured cloned while it's still on the worker thread.
|
||||
Write(aCx, aObj, mRv);
|
||||
Write(aCx, aObj, true, mRv);
|
||||
NS_WARN_IF(mRv.Failed());
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ public:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// This needs to be structured cloned while it's still on the worker thread.
|
||||
Write(aCx, aObj, mRv);
|
||||
Write(aCx, aObj, true, mRv);
|
||||
NS_WARN_IF(mRv.Failed());
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -73,24 +72,17 @@ ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProt
|
||||
|
||||
namespace {
|
||||
|
||||
class ServiceWorkerClientPostMessageRunnable final : public nsRunnable
|
||||
class ServiceWorkerClientPostMessageRunnable final
|
||||
: public nsRunnable
|
||||
, public StructuredCloneHelper
|
||||
{
|
||||
uint64_t mWindowId;
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
|
||||
public:
|
||||
ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
|
||||
JSAutoStructuredCloneBuffer&& aData,
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
: mWindowId(aWindowId),
|
||||
mBuffer(Move(aData))
|
||||
{
|
||||
mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
mClosure.mClonedImages.SwapElements(aClosure.mClonedImages);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
mClosure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
|
||||
}
|
||||
explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
|
||||
: StructuredCloneHelper(CloningSupported, TransferringSupported)
|
||||
, mWindowId(aWindowId)
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
@ -123,40 +115,40 @@ private:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// Release reference to objects that were AddRef'd for
|
||||
// cloning into worker when array goes out of scope.
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
|
||||
closure.mClonedImages.SwapElements(mClosure.mClonedImages);
|
||||
MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
|
||||
closure.mParentWindow = do_QueryInterface(aTargetContainer->GetParentObject());
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
if (!mBuffer.read(aCx, &messageData,
|
||||
WorkerStructuredCloneCallbacks(), &closure)) {
|
||||
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
ErrorResult rv;
|
||||
Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(aCx, rv.StealNSResult());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMMessageEvent> event = new MessageEvent(aTargetContainer,
|
||||
nullptr, nullptr);
|
||||
nsresult rv =
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* not cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(aCx, rv);
|
||||
nsRefPtr<MessageEvent> event = new MessageEvent(aTargetContainer,
|
||||
nullptr, nullptr);
|
||||
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* not cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(aCx, rv.StealNSResult());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
TakeTransferredPorts(ports);
|
||||
|
||||
nsRefPtr<MessagePortList> portList =
|
||||
new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
ports);
|
||||
event->SetPorts(portList);
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool status = false;
|
||||
aTargetContainer->DispatchEvent(event, &status);
|
||||
aTargetContainer->DispatchEvent(static_cast<dom::Event*>(event.get()),
|
||||
&status);
|
||||
|
||||
if (!status) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -194,22 +186,17 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks();
|
||||
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
|
||||
new ServiceWorkerClientPostMessageRunnable(mWindowId);
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
runnable->Write(aCx, aMessage, transferable, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
|
||||
new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer),
|
||||
closure);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
aRv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,15 +45,10 @@
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/LoadContext.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "mozilla/dom/ErrorEventBinding.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/dom/ImageDataBinding.h"
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "mozilla/dom/MessageEventBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
@ -62,26 +57,18 @@
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseDebugging.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/StructuredClone.h"
|
||||
#include "mozilla/dom/StructuredCloneHelper.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
#include "mozilla/dom/WorkerBinding.h"
|
||||
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/indexedDB/IDBFactory.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/ipc/nsIRemoteBlob.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "MultipartBlobImpl.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsError.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsFormData.h"
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "nsJSUtils.h"
|
||||
@ -113,7 +100,6 @@
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
#include "WorkerThread.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
@ -327,481 +313,6 @@ LogErrorToConsole(const nsAString& aMessage,
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// Recursive!
|
||||
already_AddRefed<BlobImpl>
|
||||
EnsureBlobForBackgroundManager(BlobImpl* aBlobImpl,
|
||||
PBackgroundChild* aManager = nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
if (!aManager) {
|
||||
aManager = BackgroundChild::GetForCurrentThread();
|
||||
MOZ_ASSERT(aManager);
|
||||
}
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = aBlobImpl;
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>* subBlobImpls =
|
||||
aBlobImpl->GetSubBlobImpls();
|
||||
|
||||
if (!subBlobImpls) {
|
||||
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
|
||||
// Always make sure we have a blob from an actor we can use on this
|
||||
// thread.
|
||||
BlobChild* blobChild = BlobChild::GetOrCreate(aManager, blobImpl);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
blobImpl = blobChild->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
const uint32_t subBlobCount = subBlobImpls->Length();
|
||||
MOZ_ASSERT(subBlobCount);
|
||||
|
||||
nsTArray<nsRefPtr<BlobImpl>> newSubBlobImpls;
|
||||
newSubBlobImpls.SetLength(subBlobCount);
|
||||
|
||||
bool newBlobImplNeeded = false;
|
||||
|
||||
for (uint32_t index = 0; index < subBlobCount; index++) {
|
||||
const nsRefPtr<BlobImpl>& subBlobImpl = subBlobImpls->ElementAt(index);
|
||||
MOZ_ASSERT(subBlobImpl);
|
||||
|
||||
nsRefPtr<BlobImpl>& newSubBlobImpl = newSubBlobImpls[index];
|
||||
|
||||
newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager);
|
||||
MOZ_ASSERT(newSubBlobImpl);
|
||||
|
||||
if (subBlobImpl != newSubBlobImpl) {
|
||||
newBlobImplNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newBlobImplNeeded) {
|
||||
nsString contentType;
|
||||
blobImpl->GetType(contentType);
|
||||
|
||||
if (blobImpl->IsFile()) {
|
||||
nsString name;
|
||||
blobImpl->GetName(name);
|
||||
|
||||
blobImpl = new MultipartBlobImpl(newSubBlobImpls, name, contentType);
|
||||
} else {
|
||||
blobImpl = new MultipartBlobImpl(newSubBlobImpls, contentType);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
|
||||
}
|
||||
|
||||
return blobImpl.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Blob>
|
||||
ReadBlobOrFileNoWrap(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aReader);
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl;
|
||||
{
|
||||
BlobImpl* rawBlobImpl;
|
||||
MOZ_ALWAYS_TRUE(JS_ReadBytes(aReader, &rawBlobImpl, sizeof(rawBlobImpl)));
|
||||
|
||||
MOZ_ASSERT(rawBlobImpl);
|
||||
|
||||
blobImpl = rawBlobImpl;
|
||||
}
|
||||
|
||||
blobImpl = EnsureBlobForBackgroundManager(blobImpl);
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
nsCOMPtr<nsISupports> parent;
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
|
||||
nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
parent = do_QueryInterface(scriptGlobal);
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
parent = do_QueryObject(globalScope);
|
||||
}
|
||||
|
||||
nsRefPtr<Blob> blob = Blob::Create(parent, blobImpl);
|
||||
return blob.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ReadBlobOrFile(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
JS::MutableHandle<JSObject*> aBlobOrFile)
|
||||
{
|
||||
nsRefPtr<Blob> blob = ReadBlobOrFileNoWrap(aCx, aReader);
|
||||
aBlobOrFile.set(blob->WrapObject(aCx, nullptr));
|
||||
}
|
||||
|
||||
// See WriteFormData for serialization format.
|
||||
void
|
||||
ReadFormData(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aCount,
|
||||
JS::MutableHandle<JSObject*> aFormData)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aReader);
|
||||
MOZ_ASSERT(!aFormData);
|
||||
|
||||
nsCOMPtr<nsISupports> parent;
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
|
||||
nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
parent = do_QueryInterface(scriptGlobal);
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
parent = do_QueryObject(globalScope);
|
||||
}
|
||||
|
||||
nsRefPtr<nsFormData> formData = new nsFormData(parent);
|
||||
MOZ_ASSERT(formData);
|
||||
|
||||
Optional<nsAString> thirdArg;
|
||||
|
||||
uint32_t isFile;
|
||||
uint32_t dummy;
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
MOZ_ALWAYS_TRUE(JS_ReadUint32Pair(aReader, &isFile, &dummy));
|
||||
|
||||
nsAutoString name;
|
||||
MOZ_ALWAYS_TRUE(ReadString(aReader, name));
|
||||
|
||||
if (isFile) {
|
||||
// Read out the tag since the blob reader isn't expecting it.
|
||||
MOZ_ALWAYS_TRUE(JS_ReadUint32Pair(aReader, &dummy, &dummy));
|
||||
nsRefPtr<Blob> blob = ReadBlobOrFileNoWrap(aCx, aReader);
|
||||
MOZ_ASSERT(blob);
|
||||
formData->Append(name, *blob, thirdArg);
|
||||
} else {
|
||||
nsAutoString value;
|
||||
MOZ_ALWAYS_TRUE(ReadString(aReader, value));
|
||||
formData->Append(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
aFormData.set(formData->WrapObject(aCx, nullptr));
|
||||
}
|
||||
|
||||
bool
|
||||
WriteBlobOrFile(JSStructuredCloneWriter* aWriter,
|
||||
BlobImpl* aBlobImpl,
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
|
||||
if (!aBlobImpl->MayBeClonedToOtherThreads()) {
|
||||
NS_WARNING("Not all the blob implementations can be sent between threads.");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlobImpl);
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
aBlobImpl = blobImpl;
|
||||
|
||||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
|
||||
NS_WARN_IF(!JS_WriteBytes(aWriter, &aBlobImpl, sizeof(aBlobImpl)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aClosure.mClonedObjects.AppendElement(aBlobImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A FormData is serialized as:
|
||||
// - A pair of ints (tag identifying it as a FormData, number of elements in
|
||||
// the FormData)
|
||||
// - for each (key, value) pair:
|
||||
// - pair of ints (is value a file?, 0). If not a file, value is a string.
|
||||
// - string name
|
||||
// - if value is a file:
|
||||
// - write the file/blob
|
||||
// - else:
|
||||
// - string value
|
||||
bool
|
||||
WriteFormData(JSStructuredCloneWriter* aWriter,
|
||||
nsFormData* aFormData,
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aWriter);
|
||||
MOZ_ASSERT(aFormData);
|
||||
|
||||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FORMDATA, aFormData->Length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
class MOZ_STACK_CLASS Closure final
|
||||
{
|
||||
JSStructuredCloneWriter* mWriter;
|
||||
WorkerStructuredCloneClosure& mClones;
|
||||
|
||||
public:
|
||||
Closure(JSStructuredCloneWriter* aWriter,
|
||||
WorkerStructuredCloneClosure& aClones)
|
||||
: mWriter(aWriter), mClones(aClones)
|
||||
{ }
|
||||
|
||||
static bool
|
||||
Write(const nsString& aName, bool isFile, const nsString& aValue,
|
||||
File* aFile, void* aClosure)
|
||||
{
|
||||
Closure* closure = static_cast<Closure*>(aClosure);
|
||||
if (!JS_WriteUint32Pair(closure->mWriter, /* a file? */ (uint32_t) isFile, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WriteString(closure->mWriter, aName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFile) {
|
||||
if (!WriteBlobOrFile(closure->mWriter, aFile->Impl(),
|
||||
closure->mClones)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!WriteString(closure->mWriter, aValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Closure closure(aWriter, aClosure);
|
||||
return aFormData->ForEach(Closure::Write, &closure);
|
||||
}
|
||||
|
||||
struct WorkerStructuredCloneCallbacks
|
||||
{
|
||||
static JSObject*
|
||||
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
|
||||
uint32_t aData, void* aClosure)
|
||||
{
|
||||
// See if object is a nsIDOMBlob pointer.
|
||||
if (aTag == DOMWORKER_SCTAG_BLOB) {
|
||||
MOZ_ASSERT(!aData);
|
||||
|
||||
JS::Rooted<JSObject*> blobOrFile(aCx);
|
||||
ReadBlobOrFile(aCx, aReader, &blobOrFile);
|
||||
|
||||
return blobOrFile;
|
||||
}
|
||||
|
||||
// See if the object is an ImageData.
|
||||
if (aTag == SCTAG_DOM_IMAGEDATA) {
|
||||
MOZ_ASSERT(!aData);
|
||||
return ReadStructuredCloneImageData(aCx, aReader);
|
||||
}
|
||||
|
||||
// See if the object is a FormData.
|
||||
if (aTag == DOMWORKER_SCTAG_FORMDATA) {
|
||||
JS::Rooted<JSObject*> formData(aCx);
|
||||
// aData is the entry count.
|
||||
ReadFormData(aCx, aReader, aData, &formData);
|
||||
return formData;
|
||||
}
|
||||
|
||||
// See if the object is an ImageBitmap.
|
||||
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
|
||||
NS_ASSERTION(aClosure, "Null pointer!");
|
||||
|
||||
// Get the current global object.
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(closure->mParentWindow);
|
||||
// aData is the index of the cloned image.
|
||||
return ImageBitmap::ReadStructuredClone(aCx, aReader, parent,
|
||||
closure->mClonedImages, aData);
|
||||
}
|
||||
|
||||
return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
|
||||
}
|
||||
|
||||
static bool
|
||||
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj, void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(aClosure, "Null pointer!");
|
||||
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
// See if this is a Blob/File object.
|
||||
{
|
||||
nsRefPtr<Blob> blob;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
if (WriteBlobOrFile(aWriter, blobImpl, *closure)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if this is an ImageData object.
|
||||
{
|
||||
ImageData* imageData = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
|
||||
return WriteStructuredCloneImageData(aCx, aWriter, imageData);
|
||||
}
|
||||
}
|
||||
|
||||
// See if this is a FormData object.
|
||||
{
|
||||
nsFormData* formData = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
|
||||
if (WriteFormData(aWriter, formData, *closure)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if this is an ImageBitmap object.
|
||||
{
|
||||
ImageBitmap* imageBitmap = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
|
||||
return ImageBitmap::WriteStructuredClone(aWriter,
|
||||
closure->mClonedImages,
|
||||
imageBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
|
||||
}
|
||||
|
||||
static void
|
||||
Error(JSContext* aCx, uint32_t /* aErrorId */)
|
||||
{
|
||||
Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
|
||||
static bool
|
||||
ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag, void* aContent, uint64_t aExtraData,
|
||||
void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!aContent);
|
||||
MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
|
||||
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePortBase> port =
|
||||
dom::MessagePort::Create(closure->mParentWindow,
|
||||
closure->mMessagePortIdentifiers[aExtraData],
|
||||
rv);
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mMessagePorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Transfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
|
||||
uint32_t* aTag, JS::TransferableOwnership* aOwnership,
|
||||
void** aContent, uint64_t *aExtraData)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
MessagePortBase* port;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (NS_WARN_IF(closure->mTransferredPorts.Contains(port))) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
MessagePortIdentifier identifier;
|
||||
if (!port->CloneAndDisentangle(identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mMessagePortIdentifiers.AppendElement(identifier);
|
||||
closure->mTransferredPorts.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
*aExtraData = closure->mMessagePortIdentifiers.Length() - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
|
||||
void *aContent, uint64_t aExtraData, void* aClosure)
|
||||
{
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(aClosure);
|
||||
MOZ_ASSERT(!aContent);
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
|
||||
dom::MessagePort::ForceClose(closure->mMessagePortIdentifiers[aExtraData]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
|
||||
WorkerStructuredCloneCallbacks::Read,
|
||||
WorkerStructuredCloneCallbacks::Write,
|
||||
WorkerStructuredCloneCallbacks::Error,
|
||||
WorkerStructuredCloneCallbacks::ReadTransfer,
|
||||
WorkerStructuredCloneCallbacks::Transfer,
|
||||
WorkerStructuredCloneCallbacks::FreeTransfer
|
||||
};
|
||||
|
||||
class MainThreadReleaseRunnable final : public nsRunnable
|
||||
{
|
||||
nsTArray<nsCOMPtr<nsISupports>> mDoomed;
|
||||
@ -1081,9 +592,8 @@ private:
|
||||
};
|
||||
|
||||
class MessageEventRunnable final : public WorkerRunnable
|
||||
, public StructuredCloneHelper
|
||||
{
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
uint64_t mMessagePortSerial;
|
||||
bool mToMessagePort;
|
||||
|
||||
@ -1095,24 +605,12 @@ public:
|
||||
TargetAndBusyBehavior aBehavior,
|
||||
bool aToMessagePort, uint64_t aMessagePortSerial)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
, StructuredCloneHelper(CloningSupported, TransferringSupported)
|
||||
, mMessagePortSerial(aMessagePortSerial)
|
||||
, mToMessagePort(aToMessagePort)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransferredValue,
|
||||
const JSStructuredCloneCallbacks *aCallbacks)
|
||||
{
|
||||
bool ok = mBuffer.write(aCx, aValue, aTransferredValue, aCallbacks,
|
||||
&mClosure);
|
||||
// This hashtable has to be empty because it could contain MessagePort
|
||||
// objects that cannot be freed on a different thread.
|
||||
mClosure.mTransferredPorts.Clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
SetMessageSource(ServiceWorkerClientInfo* aSource)
|
||||
{
|
||||
@ -1123,49 +621,44 @@ public:
|
||||
DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
DOMEventTargetHelper* aTarget, bool aIsMainThread)
|
||||
{
|
||||
// Release reference to objects that were AddRef'd for
|
||||
// cloning into worker when array goes out of scope.
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
|
||||
closure.mClonedImages.SwapElements(mClosure.mClonedImages);
|
||||
MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> parent;
|
||||
if (aIsMainThread) {
|
||||
closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
|
||||
parent = do_QueryInterface(aTarget->GetParentObject());
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
if (!mBuffer.read(aCx, &messageData,
|
||||
workers::WorkerStructuredCloneCallbacks(),
|
||||
&closure)) {
|
||||
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
ErrorResult rv;
|
||||
Read(parent, aCx, &messageData, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(aCx, rv.StealNSResult());
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
|
||||
nsresult rv =
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* non-cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
if (mEventSource) {
|
||||
nsRefPtr<ServiceWorkerClient> client =
|
||||
new ServiceWorkerWindowClient(aTarget, *mEventSource);
|
||||
event->SetSource(client);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(aCx, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(aCx, rv.StealNSResult());
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
TakeTransferredPorts(ports);
|
||||
|
||||
event->SetTrusted(true);
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
closure.mMessagePorts));
|
||||
ports));
|
||||
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
|
||||
|
||||
nsEventStatus dummy = nsEventStatus_eIgnore;
|
||||
@ -1190,8 +683,7 @@ private:
|
||||
return
|
||||
aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
|
||||
mMessagePortSerial,
|
||||
Move(mBuffer),
|
||||
mClosure);
|
||||
*this);
|
||||
}
|
||||
|
||||
if (aWorkerPrivate->IsFrozen()) {
|
||||
@ -3307,9 +2799,8 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
|
||||
WorkerRunnable::WorkerThreadModifyBusyCount,
|
||||
aToMessagePort, aMessagePortSerial);
|
||||
|
||||
if (!runnable->Write(aCx, aMessage, transferable,
|
||||
&gWorkerStructuredCloneCallbacks)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
runnable->Write(aCx, aMessage, transferable, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3352,45 +2843,10 @@ template <class Derived>
|
||||
bool
|
||||
WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
JSContext* aCx, uint64_t aMessagePortSerial,
|
||||
JSAutoStructuredCloneBuffer&& aBuffer,
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
StructuredCloneHelper& aHelper)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
|
||||
|
||||
class MOZ_STACK_CLASS AutoCloneBufferCleaner final
|
||||
{
|
||||
public:
|
||||
AutoCloneBufferCleaner(JSAutoStructuredCloneBuffer& aBuffer,
|
||||
const JSStructuredCloneCallbacks* aCallbacks,
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
: mBuffer(aBuffer)
|
||||
, mCallbacks(aCallbacks)
|
||||
, mClosure(aClosure)
|
||||
{}
|
||||
|
||||
~AutoCloneBufferCleaner()
|
||||
{
|
||||
mBuffer.clear(mCallbacks, &mClosure);
|
||||
}
|
||||
|
||||
private:
|
||||
JSAutoStructuredCloneBuffer& mBuffer;
|
||||
const JSStructuredCloneCallbacks* mCallbacks;
|
||||
WorkerStructuredCloneClosure& mClosure;
|
||||
};
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
closure.mClonedImages.SwapElements(aClosure.mClonedImages);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
|
||||
|
||||
AutoCloneBufferCleaner bufferCleaner(buffer,
|
||||
&gWorkerStructuredCloneCallbacks,
|
||||
closure);
|
||||
|
||||
SharedWorker* sharedWorker;
|
||||
if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
|
||||
// SharedWorker has already been unregistered?
|
||||
@ -3404,7 +2860,7 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
return true;
|
||||
}
|
||||
|
||||
closure.mParentWindow = do_QueryInterface(port->GetParentObject());
|
||||
nsCOMPtr<nsISupports> parent = do_QueryInterface(port->GetParentObject());
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
|
||||
@ -3412,24 +2868,27 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JS::Value> data(cx);
|
||||
if (!buffer.read(cx, &data, &gWorkerStructuredCloneCallbacks,
|
||||
&closure)) {
|
||||
aHelper.Read(parent, cx, &data, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(cx, rv.StealNSResult());
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
|
||||
nsresult rv =
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
|
||||
EmptyString(), EmptyString(), nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
|
||||
EmptyString(), EmptyString(), nullptr);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(cx, rv.StealNSResult());
|
||||
return false;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
aHelper.TakeTransferredPorts(ports);
|
||||
|
||||
event->SetPorts(new MessagePortList(port, closure.mMessagePorts));
|
||||
event->SetTrusted(true);
|
||||
event->SetPorts(new MessagePortList(port, ports));
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> domEvent;
|
||||
CallQueryInterface(event.get(), getter_AddRefs(domEvent));
|
||||
@ -3437,8 +2896,8 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
|
||||
bool ignored;
|
||||
rv = port->DispatchEvent(domEvent, &ignored);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(cx, rv.StealNSResult());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -6091,9 +5550,8 @@ WorkerPrivate::PostMessageToParentInternal(
|
||||
WorkerRunnable::ParentThreadUnchangedBusyCount,
|
||||
aToMessagePort, aMessagePortSerial);
|
||||
|
||||
if (!runnable->Write(aCx, aMessage, transferable,
|
||||
&gWorkerStructuredCloneCallbacks)) {
|
||||
aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
runnable->Write(aCx, aMessage, transferable, true, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7244,30 +6702,7 @@ GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
|
||||
return w->GetCrossThreadDispatcher();
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks*
|
||||
WorkerStructuredCloneCallbacks()
|
||||
{
|
||||
return &gWorkerStructuredCloneCallbacks;
|
||||
}
|
||||
|
||||
// Force instantiation.
|
||||
template class WorkerPrivateParent<WorkerPrivate>;
|
||||
|
||||
WorkerStructuredCloneClosure::WorkerStructuredCloneClosure()
|
||||
{}
|
||||
|
||||
WorkerStructuredCloneClosure::~WorkerStructuredCloneClosure()
|
||||
{}
|
||||
|
||||
void
|
||||
WorkerStructuredCloneClosure::Clear()
|
||||
{
|
||||
mParentWindow = nullptr;
|
||||
mClonedObjects.Clear();
|
||||
mClonedImages.Clear();
|
||||
mMessagePorts.Clear();
|
||||
mMessagePortIdentifiers.Clear();
|
||||
mTransferredPorts.Clear();
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
@ -28,12 +28,10 @@
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
#include "Queue.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
||||
class JSAutoStructuredCloneBuffer;
|
||||
class nsIChannel;
|
||||
class nsIDocument;
|
||||
class nsIEventTarget;
|
||||
@ -52,6 +50,7 @@ struct RuntimeStats;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Function;
|
||||
class StructuredCloneHelper;
|
||||
} // namespace dom
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
@ -74,7 +73,6 @@ class WorkerDebuggerGlobalScope;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
class WorkerRunnable;
|
||||
class WorkerStructuredCloneClosure;
|
||||
class WorkerThread;
|
||||
|
||||
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
|
||||
@ -350,8 +348,7 @@ public:
|
||||
DispatchMessageEventToMessagePort(
|
||||
JSContext* aCx,
|
||||
uint64_t aMessagePortSerial,
|
||||
JSAutoStructuredCloneBuffer&& aBuffer,
|
||||
WorkerStructuredCloneClosure& aClosure);
|
||||
StructuredCloneHelper& aHelper);
|
||||
|
||||
void
|
||||
UpdateRuntimeOptions(JSContext* aCx,
|
||||
@ -1509,17 +1506,6 @@ IsCurrentThreadRunningChromeWorker();
|
||||
JSContext*
|
||||
GetCurrentThreadJSContext();
|
||||
|
||||
enum WorkerStructuredDataType
|
||||
{
|
||||
DOMWORKER_SCTAG_BLOB = SCTAG_DOM_MAX,
|
||||
DOMWORKER_SCTAG_FORMDATA = SCTAG_DOM_MAX + 1,
|
||||
|
||||
DOMWORKER_SCTAG_END
|
||||
};
|
||||
|
||||
const JSStructuredCloneCallbacks*
|
||||
WorkerStructuredCloneCallbacks();
|
||||
|
||||
class AutoSyncLoopHolder
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
@ -1,63 +0,0 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* 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_workers_WorkerStructuredClone_h
|
||||
#define mozilla_dom_workers_WorkerStructuredClone_h
|
||||
|
||||
#include "Workers.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
class Image;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
class MessagePortBase;
|
||||
|
||||
namespace workers {
|
||||
|
||||
// This class is implemented in WorkerPrivate.cpp
|
||||
class WorkerStructuredCloneClosure final
|
||||
{
|
||||
private:
|
||||
WorkerStructuredCloneClosure(const WorkerStructuredCloneClosure&) = delete;
|
||||
WorkerStructuredCloneClosure & operator=(const WorkerStructuredCloneClosure&) = delete;
|
||||
|
||||
public:
|
||||
WorkerStructuredCloneClosure();
|
||||
~WorkerStructuredCloneClosure();
|
||||
|
||||
void Clear();
|
||||
|
||||
// This can be null if the MessagePort is created in a worker.
|
||||
nsCOMPtr<nsPIDOMWindow> mParentWindow;
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
|
||||
// This is used for sharing the backend of ImageBitmaps.
|
||||
// The layers::Image object must be thread-safely reference-counted.
|
||||
// The layers::Image object will not be written ever via any ImageBitmap
|
||||
// instance, so no race condition will occur.
|
||||
nsTArray<nsRefPtr<layers::Image>> mClonedImages;
|
||||
|
||||
// The transferred ports.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mMessagePorts;
|
||||
|
||||
// Information for the transferring.
|
||||
nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
|
||||
|
||||
// To avoid duplicates in the transferred ports.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_WorkerStructuredClone_h
|
@ -18,6 +18,7 @@
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/ProgressEvent.h"
|
||||
#include "mozilla/dom/StructuredCloneHelper.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsFormData.h"
|
||||
@ -27,7 +28,6 @@
|
||||
#include "RuntimeService.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
#include "XMLHttpRequestUpload.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -175,6 +175,116 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerThreadProxySyncRunnable : public nsRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<Proxy> mProxy;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
private:
|
||||
class ResponseRunnable final: public MainThreadStopSyncLoopRunnable
|
||||
{
|
||||
nsRefPtr<Proxy> mProxy;
|
||||
nsresult mErrorCode;
|
||||
|
||||
public:
|
||||
ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
nsresult aErrorCode)
|
||||
: MainThreadStopSyncLoopRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
|
||||
NS_SUCCEEDED(aErrorCode)),
|
||||
mProxy(aProxy), mErrorCode(aErrorCode)
|
||||
{
|
||||
MOZ_ASSERT(aProxy);
|
||||
}
|
||||
|
||||
private:
|
||||
~ResponseRunnable()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
MaybeSetException(JSContext* aCx) override
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(mErrorCode));
|
||||
|
||||
Throw(aCx, mErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
||||
: mWorkerPrivate(aWorkerPrivate), mProxy(aProxy)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aProxy);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~WorkerThreadProxySyncRunnable()
|
||||
{ }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadRun() = 0;
|
||||
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class SendRunnable final
|
||||
: public WorkerThreadProxySyncRunnable
|
||||
, public StructuredCloneHelper
|
||||
{
|
||||
nsString mStringBody;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
bool mHasUploadListeners;
|
||||
|
||||
public:
|
||||
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsAString& aStringBody)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, StructuredCloneHelper(CloningSupported, TransferringNotSupported)
|
||||
, mStringBody(aStringBody)
|
||||
, mHasUploadListeners(false)
|
||||
{
|
||||
}
|
||||
|
||||
void SetHaveUploadListeners(bool aHasUploadListeners)
|
||||
{
|
||||
mHasUploadListeners = aHasUploadListeners;
|
||||
}
|
||||
|
||||
void SetSyncLoopTarget(nsIEventTarget* aSyncLoopTarget)
|
||||
{
|
||||
mSyncLoopTarget = aSyncLoopTarget;
|
||||
}
|
||||
|
||||
private:
|
||||
~SendRunnable()
|
||||
{ }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadRun() override;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
@ -410,11 +520,10 @@ private:
|
||||
};
|
||||
|
||||
class EventRunnable final : public MainThreadProxyRunnable
|
||||
, public StructuredCloneHelper
|
||||
{
|
||||
nsString mType;
|
||||
nsString mResponseType;
|
||||
JSAutoStructuredCloneBuffer mResponseBuffer;
|
||||
WorkerStructuredCloneClosure mResponseClosure;
|
||||
JS::Heap<JS::Value> mResponse;
|
||||
nsString mResponseText;
|
||||
nsString mResponseURL;
|
||||
@ -456,17 +565,19 @@ public:
|
||||
|
||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
||||
bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
|
||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||
mResponse(JS::UndefinedValue()), mLoaded(aLoaded), mTotal(aTotal),
|
||||
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
||||
mUploadEvent(aUploadEvent), mProgressEvent(true),
|
||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
|
||||
StructuredCloneHelper(CloningSupported, TransferringNotSupported),
|
||||
mType(aType), mResponse(JS::UndefinedValue()), mLoaded(aLoaded),
|
||||
mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
|
||||
mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(true),
|
||||
mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
|
||||
mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK)
|
||||
{ }
|
||||
|
||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||
mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
|
||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
|
||||
StructuredCloneHelper(CloningSupported, TransferringNotSupported),
|
||||
mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
|
||||
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
||||
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
|
||||
mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
|
||||
@ -484,80 +595,6 @@ private:
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
|
||||
};
|
||||
|
||||
class WorkerThreadProxySyncRunnable : public nsRunnable
|
||||
{
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<Proxy> mProxy;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
private:
|
||||
class ResponseRunnable final: public MainThreadStopSyncLoopRunnable
|
||||
{
|
||||
nsRefPtr<Proxy> mProxy;
|
||||
nsresult mErrorCode;
|
||||
|
||||
public:
|
||||
ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
nsresult aErrorCode)
|
||||
: MainThreadStopSyncLoopRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
|
||||
NS_SUCCEEDED(aErrorCode)),
|
||||
mProxy(aProxy), mErrorCode(aErrorCode)
|
||||
{
|
||||
MOZ_ASSERT(aProxy);
|
||||
}
|
||||
|
||||
private:
|
||||
~ResponseRunnable()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
MaybeSetException(JSContext* aCx) override
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(mErrorCode));
|
||||
|
||||
Throw(aCx, mErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
||||
: mWorkerPrivate(aWorkerPrivate), mProxy(aProxy)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aProxy);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this))) {
|
||||
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return syncLoop.Run();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~WorkerThreadProxySyncRunnable()
|
||||
{ }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadRun() = 0;
|
||||
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
public:
|
||||
@ -791,39 +828,6 @@ private:
|
||||
MainThreadRunInternal();
|
||||
};
|
||||
|
||||
class SendRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
nsString mStringBody;
|
||||
JSAutoStructuredCloneBuffer mBody;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
bool mHasUploadListeners;
|
||||
|
||||
public:
|
||||
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, mStringBody(aStringBody)
|
||||
, mBody(Move(aBody))
|
||||
, mSyncLoopTarget(aSyncLoopTarget)
|
||||
, mHasUploadListeners(aHasUploadListeners)
|
||||
{
|
||||
mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
mClosure.mClonedImages.SwapElements(aClosure.mClonedImages);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(aClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
}
|
||||
|
||||
private:
|
||||
~SendRunnable()
|
||||
{ }
|
||||
|
||||
virtual nsresult
|
||||
MainThreadRun() override;
|
||||
};
|
||||
|
||||
class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
nsCString mHeader;
|
||||
@ -1227,21 +1231,11 @@ EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
}
|
||||
|
||||
if (doClone) {
|
||||
// Anything subject to GC must be cloned.
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
workers::WorkerStructuredCloneCallbacks();
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
if (mResponseBuffer.write(aCx, response, transferable, callbacks,
|
||||
&closure)) {
|
||||
mResponseClosure.mClonedObjects.SwapElements(closure.mClonedObjects);
|
||||
mResponseClosure.mClonedImages.SwapElements(closure.mClonedImages);
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
} else {
|
||||
ErrorResult rv;
|
||||
Write(aCx, response, transferable, false, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
NS_WARNING("Failed to clone response!");
|
||||
mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
mResponseResult = rv.StealNSResult();
|
||||
mProxy->mArrayBufferResponseWasTransferred = false;
|
||||
}
|
||||
}
|
||||
@ -1336,22 +1330,14 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
state->mResponseResult = mResponseResult;
|
||||
|
||||
if (NS_SUCCEEDED(mResponseResult)) {
|
||||
if (mResponseBuffer.data()) {
|
||||
if (HasBeenWritten()) {
|
||||
MOZ_ASSERT(mResponse.isUndefined());
|
||||
|
||||
JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
workers::WorkerStructuredCloneCallbacks();
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mResponseClosure.mClonedObjects);
|
||||
closure.mClonedImages.SwapElements(mResponseClosure.mClonedImages);
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
|
||||
ErrorResult rv;
|
||||
JS::Rooted<JS::Value> response(aCx);
|
||||
if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
|
||||
Read(nullptr, aCx, &response, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1517,32 +1503,25 @@ SendRunnable::MainThreadRun()
|
||||
{
|
||||
nsCOMPtr<nsIVariant> variant;
|
||||
|
||||
if (mBody.data()) {
|
||||
if (HasBeenWritten()) {
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
MOZ_ASSERT(xpc);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
workers::WorkerStructuredCloneCallbacks();
|
||||
ErrorResult rv;
|
||||
|
||||
JS::Rooted<JS::Value> body(cx);
|
||||
if (mBody.read(cx, &body, callbacks, &mClosure)) {
|
||||
if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
|
||||
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
Read(nullptr, cx, &body, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
mBody.clear();
|
||||
mClosure.Clear();
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = xpc->JSValToVariant(cx, body, getter_AddRefs(variant));
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
else {
|
||||
nsCOMPtr<nsIWritableVariant> wvariant =
|
||||
@ -1845,11 +1824,10 @@ XMLHttpRequest::Unpin()
|
||||
}
|
||||
|
||||
void
|
||||
XMLHttpRequest::SendInternal(const nsAString& aStringBody,
|
||||
JSAutoStructuredCloneBuffer&& aBody,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
XMLHttpRequest::SendInternal(SendRunnable* aRunnable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// No send() calls when open is running.
|
||||
@ -1879,10 +1857,10 @@ XMLHttpRequest::SendInternal(const nsAString& aStringBody,
|
||||
|
||||
JSContext* cx = mWorkerPrivate->GetJSContext();
|
||||
|
||||
nsRefPtr<SendRunnable> runnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
|
||||
aClosure, syncLoopTarget, hasUploadListeners);
|
||||
if (!runnable->Dispatch(cx)) {
|
||||
aRunnable->SetSyncLoopTarget(syncLoopTarget);
|
||||
aRunnable->SetHaveUploadListeners(hasUploadListeners);
|
||||
|
||||
if (!aRunnable->Dispatch(cx)) {
|
||||
// Dispatch() may have spun the event loop and we may have already unrooted.
|
||||
// If so we don't want autoUnpin to try again.
|
||||
if (!mRooted) {
|
||||
@ -2109,11 +2087,11 @@ XMLHttpRequest::Send(ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
// Nothing to clone.
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
nsRefPtr<SendRunnable> sendRunnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, NullString());
|
||||
|
||||
SendInternal(NullString(), Move(buffer), closure, aRv);
|
||||
// Nothing to clone.
|
||||
SendInternal(sendRunnable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2131,11 +2109,11 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
// Nothing to clone.
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
nsRefPtr<SendRunnable> sendRunnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, aBody);
|
||||
|
||||
SendInternal(aBody, Move(buffer), closure, aRv);
|
||||
// Nothing to clone.
|
||||
SendInternal(sendRunnable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2171,18 +2149,15 @@ XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
|
||||
valToClone.setString(bodyStr);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
WorkerStructuredCloneCallbacks();
|
||||
nsRefPtr<SendRunnable> sendRunnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(cx, valToClone, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
sendRunnable->Write(cx, valToClone, false, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
SendInternal(sendRunnable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2215,18 +2190,15 @@ XMLHttpRequest::Send(Blob& aBody, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
WorkerStructuredCloneCallbacks();
|
||||
nsRefPtr<SendRunnable> sendRunnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(cx, value, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
sendRunnable->Write(cx, value, false, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
SendInternal(sendRunnable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2251,17 +2223,15 @@ XMLHttpRequest::Send(nsFormData& aBody, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks =
|
||||
WorkerStructuredCloneCallbacks();
|
||||
nsRefPtr<SendRunnable> sendRunnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
if (!buffer.write(cx, value, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
sendRunnable->Write(cx, value, false, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
SendInternal(sendRunnable, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsXMLHttpRequest.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -26,9 +25,9 @@ class Blob;
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class Proxy;
|
||||
class SendRunnable;
|
||||
class XMLHttpRequestUpload;
|
||||
class WorkerPrivate;
|
||||
class WorkerStructuredCloneClosure;
|
||||
|
||||
class XMLHttpRequest final: public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
@ -291,9 +290,7 @@ private:
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SendInternal(const nsAString& aStringBody,
|
||||
JSAutoStructuredCloneBuffer&& aBody,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
SendInternal(SendRunnable* aRunnable,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user