Bug 1371246: Handle serializing Blobs in StructuredCloneHolder instances. r?billm
MozReview-Commit-ID: 2n15NCnLC48
--- a/dom/base/StructuredCloneBlob.cpp
+++ b/dom/base/StructuredCloneBlob.cpp
@@ -94,61 +94,76 @@ StructuredCloneBlob::Deserialize(JSConte
if (!JS_WrapValue(aCx, aResult)) {
aResult.set(JS::UndefinedValue());
aRv.NoteJSContextException(aCx);
}
}
/* static */ JSObject*
-StructuredCloneBlob::ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader)
+StructuredCloneBlob::ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder)
{
JS::RootedObject obj(aCx);
{
RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
- if (!holder->ReadStructuredCloneInternal(aCx, aReader) ||
+ if (!holder->ReadStructuredCloneInternal(aCx, aReader, aHolder) ||
!holder->WrapObject(aCx, nullptr, &obj)) {
return nullptr;
}
}
return obj.get();
}
bool
-StructuredCloneBlob::ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader)
+StructuredCloneBlob::ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder)
{
uint32_t length;
uint32_t version;
if (!JS_ReadUint32Pair(aReader, &length, &version)) {
return false;
}
+ uint32_t blobOffset;
+ uint32_t blobCount;
+ if (!JS_ReadUint32Pair(aReader, &blobOffset, &blobCount)) {
+ return false;
+ }
+ if (blobCount) {
+ BlobImpls().AppendElements(&aHolder->BlobImpls()[blobOffset], blobCount);
+ }
+
JSStructuredCloneData data(length, length, 4096);
if (!JS_ReadBytes(aReader, data.Start(), length)) {
return false;
}
mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope,
&StructuredCloneHolder::sCallbacks,
this);
mBuffer->adopt(Move(data), version, &StructuredCloneHolder::sCallbacks);
return true;
}
bool
-StructuredCloneBlob::WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter)
+StructuredCloneBlob::WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ StructuredCloneHolder* aHolder)
{
auto& data = mBuffer->data();
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_HOLDER, 0) ||
- !JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION)) {
+ !JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION) ||
+ !JS_WriteUint32Pair(aWriter, aHolder->BlobImpls().Length(), BlobImpls().Length())) {
return false;
}
+ aHolder->BlobImpls().AppendElements(BlobImpls());
+
auto iter = data.Iter();
while (!iter.Done()) {
if (!JS_WriteBytes(aWriter, iter.Data(), iter.RemainingInSegment())) {
return false;
}
iter.Advance(data, iter.RemainingInSegment());
}
--- a/dom/base/StructuredCloneBlob.h
+++ b/dom/base/StructuredCloneBlob.h
@@ -21,18 +21,20 @@ namespace dom {
class StructuredCloneBlob : public StructuredCloneHolder
, public RefCounted<StructuredCloneBlob>
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(StructuredCloneBlob)
explicit StructuredCloneBlob();
- static JSObject* ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader);
- bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter);
+ static JSObject* ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder);
+ bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ StructuredCloneHolder* aHolder);
static already_AddRefed<StructuredCloneBlob>
Constructor(GlobalObject& aGlobal, JS::HandleValue aValue, JS::HandleObject aTargetGlobal, ErrorResult& aRv);
void Deserialize(JSContext* aCx, JS::HandleObject aTargetScope,
JS::MutableHandleValue aResult, ErrorResult& aRv);
nsISupports* GetParentObject() const { return nullptr; }
@@ -42,16 +44,17 @@ public:
protected:
template <typename T, detail::RefCountAtomicity>
friend class detail::RefCounted;
~StructuredCloneBlob() = default;
private:
- bool ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader);
+ bool ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_StructuredCloneBlob_h
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -353,20 +353,16 @@ StructuredCloneHolder::ReadFromBuffer(ns
StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
JSStructuredCloneReader* aReader,
uint32_t aTag)
{
if (aTag == SCTAG_DOM_IMAGEDATA) {
return ReadStructuredCloneImageData(aCx, aReader);
}
- if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
- return StructuredCloneBlob::ReadStructuredClone(aCx, aReader);
- }
-
if (aTag == SCTAG_DOM_WEBCRYPTO_KEY || aTag == SCTAG_DOM_URLSEARCHPARAMS) {
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
if (!global) {
return nullptr;
}
// Prevent the return value from being trashed by a GC during ~nsRefPtr.
JS::Rooted<JSObject*> result(aCx);
@@ -453,24 +449,16 @@ StructuredCloneHolder::WriteFullySeriali
// See if this is a ImageData object.
{
ImageData* imageData = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
return WriteStructuredCloneImageData(aCx, aWriter, imageData);
}
}
- // See if this is a StructuredCloneBlob object.
- {
- StructuredCloneBlob* holder = nullptr;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, aObj, holder))) {
- return holder->WriteStructuredClone(aCx, aWriter);
- }
- }
-
// Handle URLSearchParams cloning
{
URLSearchParams* usp = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(URLSearchParams, aObj, usp))) {
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_URLSEARCHPARAMS, 0) &&
usp->WriteStructuredClone(aWriter);
}
}
@@ -994,16 +982,20 @@ StructuredCloneHolder::CustomReadHandler
// Get the current global object.
// This can be null.
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
// aIndex is the index of the cloned image.
return ImageBitmap::ReadStructuredClone(aCx, aReader,
parent, GetSurfaces(), aIndex);
}
+ if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
+ return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
+ }
+
if (aTag == SCTAG_DOM_WASM) {
return ReadWasmModule(aCx, aIndex, this);
}
if (aTag == SCTAG_DOM_INPUTSTREAM) {
return ReadInputStream(aCx, aIndex, this);
}
@@ -1057,16 +1049,24 @@ StructuredCloneHolder::CustomWriteHandle
ImageBitmap* imageBitmap = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
return ImageBitmap::WriteStructuredClone(aWriter,
GetSurfaces(),
imageBitmap);
}
}
+ // See if this is a StructuredCloneBlob object.
+ {
+ StructuredCloneBlob* holder = nullptr;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, aObj, holder))) {
+ return holder->WriteStructuredClone(aCx, aWriter, this);
+ }
+ }
+
// See if this is a WasmModule.
if ((mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) &&
JS::IsWasmModuleObject(aObj)) {
RefPtr<JS::WasmModule> module = JS::GetWasmModule(aObj);
MOZ_ASSERT(module);
return WriteWasmModule(aWriter, module, this);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
@@ -72,8 +72,40 @@ add_task(async function tabsSendMessageR
"extensionpage.html": `<!DOCTYPE html><meta charset="utf-8"><script src="senderScript.js"></script>`,
},
});
await extension.startup();
await extension.awaitFinish("sendMessage");
await extension.unload();
});
+
+add_task(async function tabsSendMessageBlob() {
+ function background() {
+ browser.runtime.onMessage.addListener(msg => {
+ browser.test.assertTrue(msg.blob instanceof Blob, "Message is a blob");
+ return Promise.resolve(msg);
+ });
+
+ let childFrame = document.createElement("iframe");
+ childFrame.src = "extensionpage.html";
+ document.body.appendChild(childFrame);
+ }
+
+ function senderScript() {
+ browser.runtime.sendMessage({blob: new Blob(["hello"])}).then(response => {
+ browser.test.assertTrue(response.blob instanceof Blob, "Response is a blob");
+ browser.test.notifyPass("sendBlob");
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ background,
+ files: {
+ "senderScript.js": senderScript,
+ "extensionpage.html": `<!DOCTYPE html><meta charset="utf-8"><script src="senderScript.js"></script>`,
+ },
+ });
+
+ await extension.startup();
+ await extension.awaitFinish("sendBlob");
+ await extension.unload();
+});