Backed out a19daae11647 (Bug 1186307) for W8 and W4 bustage on CLOSED TREE

This commit is contained in:
Nigel Babu 2015-08-27 15:04:25 +05:30
parent c3abc500d2
commit d5f46aaf6c
20 changed files with 1042 additions and 856 deletions

View File

@ -113,11 +113,8 @@ PostMessageEvent::Run()
false /*cancelable */, messageData, mCallerOrigin,
EmptyString(), mSource);
nsTArray<nsRefPtr<MessagePortBase>> ports;
TakeTransferredPorts(ports);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
ports));
GetTransferredPorts()));
// 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

View File

@ -14,24 +14,11 @@
#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 {
@ -155,7 +142,11 @@ bool
StructuredCloneHelperInternal::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
return Write(aCx, aValue, JS::UndefinedHandleValue);
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);
}
bool
@ -226,37 +217,24 @@ 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, aMaybeToDifferentThread, aRv);
Write(aCx, aValue, JS::UndefinedHandleValue, 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;
}
}
}
}
@ -346,346 +324,6 @@ 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,
@ -695,19 +333,56 @@ StructuredCloneHelper::ReadCallback(JSContext* aCx,
MOZ_ASSERT(mSupportsCloning);
if (aTag == SCTAG_DOM_BLOB) {
return ReadBlob(aCx, aIndex, this);
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();
}
if (aTag == SCTAG_DOM_FILELIST) {
return ReadFileList(aCx, aReader, aIndex, this);
}
JS::Rooted<JS::Value> val(aCx);
{
nsRefPtr<FileList> fileList = new FileList(mParent);
if (aTag == SCTAG_DOM_IMAGEDATA) {
return ReadStructuredCloneImageData(aCx, aReader);
}
// |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_FORMDATA) {
return ReadFormData(aCx, aReader, aIndex, this);
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_IMAGEBITMAP) {
@ -735,31 +410,35 @@ StructuredCloneHelper::WriteCallback(JSContext* aCx,
{
Blob* blob = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
return WriteBlob(aWriter, blob, this);
BlobImpl* blobImpl = blob->Impl();
if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
mBlobImplArray.Length())) {
mBlobImplArray.AppendElement(blobImpl);
return true;
}
return false;
}
}
// See if this is a FileList object.
{
FileList* fileList = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
return WriteFileList(aWriter, fileList, this);
}
}
// 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;
}
// See if this is a ImageData object.
{
ImageData* imageData = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
return WriteStructuredCloneImageData(aCx, aWriter, imageData);
}
}
for (uint32_t i = 0; i < fileList->Length(); ++i) {
mBlobImplArray.AppendElement(fileList->Item(i)->Impl());
}
// See if this is a FormData object.
{
nsFormData* formData = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
return WriteFormData(aWriter, formData, this);
return true;
}
}

View File

@ -81,11 +81,6 @@ 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.");
@ -137,13 +132,11 @@ 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,
@ -191,19 +184,10 @@ public:
return mBlobImplArray;
}
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)
const nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts() const
{
MOZ_ASSERT(mSupportsTransferring);
MOZ_ASSERT(aPorts.IsEmpty());
aPorts.SwapElements(mTransferredPorts);
return mTransferredPorts;
}
nsTArray<MessagePortIdentifier>& PortIdentifiers()

View File

@ -46,8 +46,6 @@ enum StructuredCloneTags {
SCTAG_DOM_RTC_CERTIFICATE,
SCTAG_DOM_FORMDATA,
SCTAG_DOM_MAX
};

View File

@ -8619,7 +8619,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, false, aError);
event->Write(aCx, message, transfer, aError);
if (NS_WARN_IF(aError.Failed())) {
return;
}

View File

@ -52,7 +52,7 @@ nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
}
ErrorResult rv;
Write(aCx, aData, true, rv);
Write(aCx, aData, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}

View File

@ -836,5 +836,4 @@ 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]

View File

@ -278,51 +278,6 @@ 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");
@ -365,52 +320,6 @@ 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");
@ -451,52 +360,6 @@ 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,
@ -504,13 +367,11 @@ var tests = [
test_windowToIframe,
test_windowToCrossOriginIframe,
test_workers,
test_broadcastChannel,
test_broadcastChannel_inWorkers,
// TODO BroadcastChannel in worker
test_messagePort,
test_messagePort_inWorkers,
// TODO MessagePort in worker
];
function next() {

View File

@ -1,33 +0,0 @@
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');
}
}

View File

@ -454,11 +454,19 @@ BroadcastChannel::PostMessageInternal(JSContext* aCx,
{
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
data->Write(aCx, aMessage, true, aRv);
data->Write(aCx, aMessage, 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);
}

View File

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

View File

@ -140,12 +140,9 @@ 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()),
ports);
mData->GetTransferredPorts());
event->SetPorts(portList);
bool dummy;

View File

@ -46,11 +46,19 @@ SharedMessagePortMessage::Write(JSContext* aCx,
JS::Handle<JS::Value> aTransfer,
ErrorResult& aRv)
{
StructuredCloneHelper::Write(aCx, aValue, aTransfer, true, aRv);
StructuredCloneHelper::Write(aCx, aValue, aTransfer, 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);

View File

@ -233,7 +233,7 @@ public:
aWorkerPrivate->AssertIsOnWorkerThread();
// This needs to be structured cloned while it's still on the worker thread.
Write(aCx, aObj, true, mRv);
Write(aCx, aObj, 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, true, mRv);
Write(aCx, aObj, mRv);
NS_WARN_IF(mRv.Failed());
}

View File

@ -13,6 +13,7 @@
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "WorkerPrivate.h"
#include "WorkerStructuredClone.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -72,17 +73,24 @@ ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProt
namespace {
class ServiceWorkerClientPostMessageRunnable final
: public nsRunnable
, public StructuredCloneHelper
class ServiceWorkerClientPostMessageRunnable final : public nsRunnable
{
uint64_t mWindowId;
JSAutoStructuredCloneBuffer mBuffer;
WorkerStructuredCloneClosure mClosure;
public:
explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
: StructuredCloneHelper(CloningSupported, TransferringSupported)
, mWindowId(aWindowId)
{}
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);
}
NS_IMETHOD
Run()
@ -115,40 +123,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);
ErrorResult rv;
Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(aCx, rv.StealNSResult());
if (!mBuffer.read(aCx, &messageData,
WorkerStructuredCloneCallbacks(), &closure)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return NS_ERROR_FAILURE;
}
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());
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);
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(static_cast<dom::Event*>(event.get()),
&status);
aTargetContainer->DispatchEvent(event, &status);
if (!status) {
return NS_ERROR_FAILURE;
@ -186,17 +194,22 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
transferable.setObject(*array);
}
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
new ServiceWorkerClientPostMessageRunnable(mWindowId);
const JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks();
runnable->Write(aCx, aMessage, transferable, true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(aCx, aMessage, transferable, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
aRv = NS_DispatchToMainThread(runnable);
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);
}
}

View File

@ -45,10 +45,15 @@
#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"
@ -57,18 +62,26 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredCloneHelper.h"
#include "mozilla/dom/StructuredClone.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"
@ -100,6 +113,7 @@
#include "WorkerFeature.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
#include "WorkerStructuredClone.h"
#include "WorkerThread.h"
#ifdef XP_WIN
@ -313,6 +327,481 @@ 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;
@ -592,8 +1081,9 @@ private:
};
class MessageEventRunnable final : public WorkerRunnable
, public StructuredCloneHelper
{
JSAutoStructuredCloneBuffer mBuffer;
WorkerStructuredCloneClosure mClosure;
uint64_t mMessagePortSerial;
bool mToMessagePort;
@ -605,12 +1095,24 @@ 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)
{
@ -621,44 +1123,49 @@ public:
DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
DOMEventTargetHelper* aTarget, bool aIsMainThread)
{
nsCOMPtr<nsPIDOMWindow> parent;
// 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);
if (aIsMainThread) {
parent = do_QueryInterface(aTarget->GetParentObject());
closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
}
JS::Rooted<JS::Value> messageData(aCx);
ErrorResult rv;
Read(parent, aCx, &messageData, rv);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(aCx, rv.StealNSResult());
if (!mBuffer.read(aCx, &messageData,
workers::WorkerStructuredCloneCallbacks(),
&closure)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
true /* cancelable */,
messageData,
EmptyString(),
EmptyString(),
nullptr);
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
false /* non-cancelable */,
messageData,
EmptyString(),
EmptyString(),
nullptr);
if (mEventSource) {
nsRefPtr<ServiceWorkerClient> client =
new ServiceWorkerWindowClient(aTarget, *mEventSource);
event->SetSource(client);
}
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(aCx, rv.StealNSResult());
if (NS_FAILED(rv)) {
xpc::Throw(aCx, rv);
return false;
}
nsTArray<nsRefPtr<MessagePortBase>> ports;
TakeTransferredPorts(ports);
event->SetTrusted(true);
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
ports));
closure.mMessagePorts));
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
nsEventStatus dummy = nsEventStatus_eIgnore;
@ -683,7 +1190,8 @@ private:
return
aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
mMessagePortSerial,
*this);
Move(mBuffer),
mClosure);
}
if (aWorkerPrivate->IsFrozen()) {
@ -2799,8 +3307,9 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
WorkerRunnable::WorkerThreadModifyBusyCount,
aToMessagePort, aMessagePortSerial);
runnable->Write(aCx, aMessage, transferable, true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
if (!runnable->Write(aCx, aMessage, transferable,
&gWorkerStructuredCloneCallbacks)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
@ -2843,10 +3352,45 @@ template <class Derived>
bool
WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
JSContext* aCx, uint64_t aMessagePortSerial,
StructuredCloneHelper& aHelper)
JSAutoStructuredCloneBuffer&& aBuffer,
WorkerStructuredCloneClosure& aClosure)
{
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?
@ -2860,7 +3404,7 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
return true;
}
nsCOMPtr<nsISupports> parent = do_QueryInterface(port->GetParentObject());
closure.mParentWindow = do_QueryInterface(port->GetParentObject());
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
@ -2868,27 +3412,24 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
}
JSContext* cx = jsapi.cx();
ErrorResult rv;
JS::Rooted<JS::Value> data(cx);
aHelper.Read(parent, cx, &data, rv);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
if (!buffer.read(cx, &data, &gWorkerStructuredCloneCallbacks,
&closure)) {
return false;
}
nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
EmptyString(), EmptyString(), nullptr);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
EmptyString(), EmptyString(), nullptr);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
nsTArray<nsRefPtr<MessagePortBase>> ports;
aHelper.TakeTransferredPorts(ports);
event->SetTrusted(true);
event->SetPorts(new MessagePortList(port, ports));
event->SetPorts(new MessagePortList(port, closure.mMessagePorts));
nsCOMPtr<nsIDOMEvent> domEvent;
CallQueryInterface(event.get(), getter_AddRefs(domEvent));
@ -2896,8 +3437,8 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
bool ignored;
rv = port->DispatchEvent(domEvent, &ignored);
if (NS_WARN_IF(rv.Failed())) {
xpc::Throw(cx, rv.StealNSResult());
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
@ -5550,8 +6091,9 @@ WorkerPrivate::PostMessageToParentInternal(
WorkerRunnable::ParentThreadUnchangedBusyCount,
aToMessagePort, aMessagePortSerial);
runnable->Write(aCx, aMessage, transferable, true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
if (!runnable->Write(aCx, aMessage, transferable,
&gWorkerStructuredCloneCallbacks)) {
aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
return;
}
@ -6702,7 +7244,30 @@ 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

View File

@ -28,10 +28,12 @@
#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;
@ -50,7 +52,6 @@ struct RuntimeStats;
namespace mozilla {
namespace dom {
class Function;
class StructuredCloneHelper;
} // namespace dom
namespace ipc {
class PrincipalInfo;
@ -73,6 +74,7 @@ class WorkerDebuggerGlobalScope;
class WorkerGlobalScope;
class WorkerPrivate;
class WorkerRunnable;
class WorkerStructuredCloneClosure;
class WorkerThread;
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
@ -348,7 +350,8 @@ public:
DispatchMessageEventToMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
StructuredCloneHelper& aHelper);
JSAutoStructuredCloneBuffer&& aBuffer,
WorkerStructuredCloneClosure& aClosure);
void
UpdateRuntimeOptions(JSContext* aCx,
@ -1506,6 +1509,17 @@ 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;

View File

@ -0,0 +1,63 @@
/* -*- 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

View File

@ -18,7 +18,6 @@
#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"
@ -28,6 +27,7 @@
#include "RuntimeService.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerStructuredClone.h"
#include "XMLHttpRequestUpload.h"
using namespace mozilla;
@ -175,116 +175,6 @@ 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 {
@ -520,10 +410,11 @@ 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;
@ -565,19 +456,17 @@ public:
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
: 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),
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), 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),
StructuredCloneHelper(CloningSupported, TransferringNotSupported),
mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), 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),
@ -595,6 +484,80 @@ 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:
@ -828,6 +791,39 @@ 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;
@ -1231,11 +1227,21 @@ EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
}
if (doClone) {
ErrorResult rv;
Write(aCx, response, transferable, false, rv);
if (NS_WARN_IF(rv.Failed())) {
// 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 {
NS_WARNING("Failed to clone response!");
mResponseResult = rv.StealNSResult();
mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
mProxy->mArrayBufferResponseWasTransferred = false;
}
}
@ -1330,14 +1336,22 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
state->mResponseResult = mResponseResult;
if (NS_SUCCEEDED(mResponseResult)) {
if (HasBeenWritten()) {
if (mResponseBuffer.data()) {
MOZ_ASSERT(mResponse.isUndefined());
ErrorResult rv;
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());
JS::Rooted<JS::Value> response(aCx);
Read(nullptr, aCx, &response, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
return false;
}
@ -1503,25 +1517,32 @@ SendRunnable::MainThreadRun()
{
nsCOMPtr<nsIVariant> variant;
if (HasBeenWritten()) {
if (mBody.data()) {
AutoSafeJSContext cx;
JSAutoRequest ar(cx);
nsIXPConnect* xpc = nsContentUtils::XPConnect();
MOZ_ASSERT(xpc);
ErrorResult rv;
nsresult rv = NS_OK;
const JSStructuredCloneCallbacks* callbacks =
workers::WorkerStructuredCloneCallbacks();
JS::Rooted<JS::Value> body(cx);
Read(nullptr, cx, &body, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
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;
}
rv = xpc->JSValToVariant(cx, body, getter_AddRefs(variant));
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
mBody.clear();
mClosure.Clear();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
nsCOMPtr<nsIWritableVariant> wvariant =
@ -1824,10 +1845,11 @@ XMLHttpRequest::Unpin()
}
void
XMLHttpRequest::SendInternal(SendRunnable* aRunnable,
XMLHttpRequest::SendInternal(const nsAString& aStringBody,
JSAutoStructuredCloneBuffer&& aBody,
WorkerStructuredCloneClosure& aClosure,
ErrorResult& aRv)
{
MOZ_ASSERT(aRunnable);
mWorkerPrivate->AssertIsOnWorkerThread();
// No send() calls when open is running.
@ -1857,10 +1879,10 @@ XMLHttpRequest::SendInternal(SendRunnable* aRunnable,
JSContext* cx = mWorkerPrivate->GetJSContext();
aRunnable->SetSyncLoopTarget(syncLoopTarget);
aRunnable->SetHaveUploadListeners(hasUploadListeners);
if (!aRunnable->Dispatch(cx)) {
nsRefPtr<SendRunnable> runnable =
new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
aClosure, syncLoopTarget, hasUploadListeners);
if (!runnable->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) {
@ -2087,11 +2109,11 @@ XMLHttpRequest::Send(ErrorResult& aRv)
return;
}
nsRefPtr<SendRunnable> sendRunnable =
new SendRunnable(mWorkerPrivate, mProxy, NullString());
// Nothing to clone.
SendInternal(sendRunnable, aRv);
JSAutoStructuredCloneBuffer buffer;
WorkerStructuredCloneClosure closure;
SendInternal(NullString(), Move(buffer), closure, aRv);
}
void
@ -2109,11 +2131,11 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
return;
}
nsRefPtr<SendRunnable> sendRunnable =
new SendRunnable(mWorkerPrivate, mProxy, aBody);
// Nothing to clone.
SendInternal(sendRunnable, aRv);
JSAutoStructuredCloneBuffer buffer;
WorkerStructuredCloneClosure closure;
SendInternal(aBody, Move(buffer), closure, aRv);
}
void
@ -2149,15 +2171,18 @@ XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
valToClone.setString(bodyStr);
}
nsRefPtr<SendRunnable> sendRunnable =
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
const JSStructuredCloneCallbacks* callbacks =
WorkerStructuredCloneCallbacks();
sendRunnable->Write(cx, valToClone, false, aRv);
if (NS_WARN_IF(aRv.Failed())) {
WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(cx, valToClone, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
SendInternal(sendRunnable, aRv);
SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void
@ -2190,15 +2215,18 @@ XMLHttpRequest::Send(Blob& aBody, ErrorResult& aRv)
return;
}
nsRefPtr<SendRunnable> sendRunnable =
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
const JSStructuredCloneCallbacks* callbacks =
WorkerStructuredCloneCallbacks();
sendRunnable->Write(cx, value, false, aRv);
if (NS_WARN_IF(aRv.Failed())) {
WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(cx, value, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
SendInternal(sendRunnable, aRv);
SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void
@ -2223,15 +2251,17 @@ XMLHttpRequest::Send(nsFormData& aBody, ErrorResult& aRv)
return;
}
nsRefPtr<SendRunnable> sendRunnable =
new SendRunnable(mWorkerPrivate, mProxy, EmptyString());
const JSStructuredCloneCallbacks* callbacks =
WorkerStructuredCloneCallbacks();
sendRunnable->Write(cx, value, false, aRv);
if (NS_WARN_IF(aRv.Failed())) {
JSAutoStructuredCloneBuffer buffer;
WorkerStructuredCloneClosure closure;
if (!buffer.write(cx, value, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
SendInternal(sendRunnable, aRv);
SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void

View File

@ -14,6 +14,7 @@
#include "mozilla/dom/TypedArray.h"
#include "js/StructuredClone.h"
#include "nsXMLHttpRequest.h"
namespace mozilla {
@ -25,9 +26,9 @@ class Blob;
BEGIN_WORKERS_NAMESPACE
class Proxy;
class SendRunnable;
class XMLHttpRequestUpload;
class WorkerPrivate;
class WorkerStructuredCloneClosure;
class XMLHttpRequest final: public nsXHREventTarget,
public WorkerFeature
@ -290,7 +291,9 @@ private:
ErrorResult& aRv);
void
SendInternal(SendRunnable* aRunnable,
SendInternal(const nsAString& aStringBody,
JSAutoStructuredCloneBuffer&& aBody,
WorkerStructuredCloneClosure& aClosure,
ErrorResult& aRv);
};