summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2023-04-09 13:44:46 +0000
committerMoonchild <moonchild@palemoon.org>2023-04-09 13:44:46 +0000
commitbfeb62ceece166cc90de95f394c0313f35ac0fd1 (patch)
treec61991a79bfcee2ee57a5168ce87a8451adc45c6
parentb62bf0c4b7e70f3bfcc1915526e158712aef1211 (diff)
parentae19a0513ee353dbdee92de16fc7c1e83516a40f (diff)
downloaduxp-bfeb62ceece166cc90de95f394c0313f35ac0fd1.tar.gz
Merge pull request 'Implement `self.structuredClone()`' (#2206) from FranklinDM/UXP-contrib:work_js-structuredclone into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/2206
-rw-r--r--dom/base/nsContentUtils.cpp69
-rw-r--r--dom/base/nsContentUtils.h18
-rw-r--r--dom/base/nsGlobalWindow.cpp48
-rw-r--r--dom/base/nsGlobalWindow.h12
-rw-r--r--dom/messagechannel/MessagePort.cpp65
-rw-r--r--dom/messagechannel/MessagePort.h8
-rw-r--r--dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml7
-rw-r--r--dom/webidl/Client.webidl4
-rw-r--r--dom/webidl/DedicatedWorkerGlobalScope.webidl4
-rw-r--r--dom/webidl/MessagePort.webidl9
-rw-r--r--dom/webidl/ServiceWorker.webidl5
-rw-r--r--dom/webidl/Window.webidl9
-rw-r--r--dom/webidl/WindowOrWorkerGlobalScope.webidl4
-rw-r--r--dom/webidl/Worker.webidl4
-rw-r--r--dom/workers/ServiceWorker.cpp12
-rw-r--r--dom/workers/ServiceWorker.h10
-rw-r--r--dom/workers/ServiceWorkerClient.cpp30
-rw-r--r--dom/workers/ServiceWorkerClient.h8
-rw-r--r--dom/workers/ServiceWorkerPrivate.cpp2
-rw-r--r--dom/workers/ServiceWorkerPrivate.h2
-rw-r--r--dom/workers/SharedWorker.cpp12
-rw-r--r--dom/workers/SharedWorker.h11
-rw-r--r--dom/workers/WorkerPrivate.cpp77
-rw-r--r--dom/workers/WorkerPrivate.h26
-rw-r--r--dom/workers/WorkerScope.cpp21
-rw-r--r--dom/workers/WorkerScope.h14
-rw-r--r--js/xpconnect/src/Sandbox.cpp42
-rw-r--r--js/xpconnect/src/xpcprivate.h1
-rw-r--r--js/xpconnect/tests/unit/test_structuredClone.js25
-rw-r--r--js/xpconnect/tests/unit/xpcshell.ini1
30 files changed, 413 insertions, 147 deletions
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 990e1bc589..86e52984af 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -47,6 +47,7 @@
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TabParent.h"
@@ -9991,3 +9992,71 @@ nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement)
}
return e;
}
+
+/* static */ nsresult
+nsContentUtils::CreateJSValueFromSequenceOfObject(JSContext* aCx,
+ const Sequence<JSObject*>& aTransfer,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ if (aTransfer.IsEmpty()) {
+ return NS_OK;
+ }
+
+ JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, aTransfer.Length()));
+ if (!array) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
+ JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
+ if (!object) {
+ continue;
+ }
+
+ if (NS_WARN_IF(!JS_DefineElement(aCx, array, i, object,
+ JSPROP_ENUMERATE))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ aValue.setObject(*array);
+ return NS_OK;
+}
+
+/* static */
+void nsContentUtils::StructuredClone(JSContext* aCx,
+ nsIGlobalObject* aGlobal,
+ JS::Handle<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ ErrorResult& aError) {
+ JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
+ aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
+ aCx, aOptions.mTransfer, &transferArray);
+ if (NS_WARN_IF(aError.Failed())) {
+ return;
+ }
+
+ JS::CloneDataPolicy clonePolicy;
+ // FIXME: Uncomment once bug 1609990 and bug 1611855 lands.
+ //clonePolicy.allowIntraClusterClonableSharedObjects();
+ //clonePolicy.allowSharedMemoryObjects();
+
+ StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
+ StructuredCloneHolder::TransferringSupported,
+ JS::StructuredCloneScope::SameProcessDifferentThread);
+ holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
+ if (NS_WARN_IF(aError.Failed())) {
+ return;
+ }
+
+ // TODO: Pass clonePolicy.
+ //holder.Read(this, aCx, aRv, clonePolicy, aError);
+ holder.Read(aGlobal, aCx, aRv, aError);
+ if (NS_WARN_IF(aError.Failed())) {
+ return;
+ }
+
+ nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
+ Unused << ports;
+}
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index a0c602f61a..58f9f56546 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -132,6 +132,7 @@ class nsIContentParent;
class TabChild;
class Selection;
class TabParent;
+struct StructuredSerializeOptions;
} // namespace dom
namespace ipc {
@@ -2812,6 +2813,23 @@ public:
static bool
IsCustomElementsEnabled() { return sIsCustomElementsEnabled; }
+ static nsresult
+ CreateJSValueFromSequenceOfObject(JSContext* aCx,
+ const mozilla::dom::Sequence<JSObject*>& aTransfer,
+ JS::MutableHandle<JS::Value> aValue);
+
+ /**
+ * This implements the structured cloning algorithm as described by
+ * https://html.spec.whatwg.org/#structured-cloning.
+ */
+ static void
+ StructuredClone(JSContext* aCx,
+ nsIGlobalObject* aGlobal,
+ JS::Handle<JS::Value> aValue,
+ const mozilla::dom::StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ mozilla::ErrorResult& aError);
+
private:
static bool InitializeEventTable();
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 01023f3a2d..a816efc7cd 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8891,30 +8891,33 @@ nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
void
-nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+nsGlobalWindow::PostMessageMoz(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
- const Optional<Sequence<JS::Value>>& aTransfer,
+ const Sequence<JSObject*>& aTransfer,
nsIPrincipal& aSubjectPrincipal,
- ErrorResult& aError)
+ ErrorResult& aRv)
{
JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
- if (aTransfer.WasPassed()) {
- const Sequence<JS::Value >& values = aTransfer.Value();
-
- // The input sequence only comes from the generated bindings code, which
- // ensures it is rooted.
- JS::HandleValueArray elements =
- JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements());
-
- transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
- if (transferArray.isNull()) {
- aError.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
+ &transferArray);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray,
- aSubjectPrincipal, aError);
+ aSubjectPrincipal, aRv);
+}
+
+void
+nsGlobalWindow::PostMessageMoz(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const WindowPostMessageOptions& aOptions,
+ nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv)
+{
+ PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
+ aSubjectPrincipal, aRv);
}
class nsCloseEvent : public Runnable {
@@ -14638,6 +14641,17 @@ nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
}
}
+// https://html.spec.whatwg.org/#structured-cloning
+void
+nsGlobalWindow::StructuredClone(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ ErrorResult& aError)
+{
+ nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError);
+}
+
// Helper called by methods that move/resize the window,
// to ensure the presContext (if any) is aware of resolution
// change that may happen in multi-monitor configuration.
diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
index e593890af0..63bb574dd4 100644
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -135,6 +135,7 @@ class TabGroup;
class Timeout;
class U2F;
class WakeLock;
+struct WindowPostMessageOptions;
class Worklet;
namespace cache {
class CacheStorage;
@@ -934,7 +935,12 @@ public:
void Print(mozilla::ErrorResult& aError);
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
- const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
+ const mozilla::dom::Sequence<JSObject*>& aTransfer,
+ nsIPrincipal& aSubjectPrincipal,
+ mozilla::ErrorResult& aRv);
+ void PostMessageMoz(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const mozilla::dom::WindowPostMessageOptions& aOptions,
nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aError);
int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
@@ -1178,6 +1184,10 @@ public:
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
mozilla::ErrorResult& aRv);
+ void StructuredClone(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ const mozilla::dom::StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ mozilla::ErrorResult& aError);
// ChromeWindow bits. Do NOT call these unless your window is in
// fact an nsGlobalChromeWindow.
diff --git a/dom/messagechannel/MessagePort.cpp b/dom/messagechannel/MessagePort.cpp
index 6f7a1abc19..5fbd1e9bb9 100644
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -394,52 +394,39 @@ MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
}
void
-MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+MessagePort::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
// We *must* clone the data here, or the JS::Value could be modified
// by script
- JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
- if (aTransferable.WasPassed()) {
- const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-
- // Here we want to check if the transerable object list contains
- // this port. No other checks are done.
- for (const JS::Value& value : realTransferable) {
- if (!value.isObject()) {
- continue;
- }
-
- JS::Rooted<JSObject*> object(aCx, &value.toObject());
-
- MessagePort* port = nullptr;
- nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
- if (NS_FAILED(rv)) {
- continue;
- }
-
- if (port == this) {
- aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
- return;
- }
+ // Here we want to check if the transferable object list contains
+ // this port.
+ for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
+ JSObject* rawObject = aTransferable[i];
+ if (!rawObject) {
+ continue;
}
- // The input sequence only comes from the generated bindings code, which
- // ensures it is rooted.
- JS::HandleValueArray elements =
- JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
- realTransferable.Elements());
+ JS::Rooted<JSObject*> object(aCx, rawObject);
- JSObject* array =
- JS_NewArrayObject(aCx, elements);
- if (!array) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ MessagePort* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
+ if (NS_SUCCEEDED(rv) && port == this) {
+ aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
+ }
- transferable.setObject(*array);
+ JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
+
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
+ aTransferable,
+ &transferable);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
@@ -509,6 +496,14 @@ MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
void
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
+void
MessagePort::Start()
{
if (mMessageQueueEnabled) {
diff --git a/dom/messagechannel/MessagePort.h b/dom/messagechannel/MessagePort.h
index 0ed2af11c1..b832872e1f 100644
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -26,6 +26,7 @@ class MessagePortIdentifier;
class MessagePortMessage;
class PostMessageRunnable;
class SharedMessagePortMessage;
+struct StructuredSerializeOptions;
namespace workers {
class WorkerHolder;
@@ -62,7 +63,12 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
void Start();
diff --git a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
index f6a9198968..e48bee136d 100644
--- a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
@@ -383,13 +383,6 @@ var tests =
hasWrongReturnOriginBug: true
},
- // 55
- {
- args: ["NOT-RECEIVED", undefined],
- source: "sameDomain",
- name: "SyntaxError",
- code: DOMException.SYNTAX_ERR
- },
];
function allTests(callback)
diff --git a/dom/webidl/Client.webidl b/dom/webidl/Client.webidl
index 3e79c6a635..c961c2b31f 100644
--- a/dom/webidl/Client.webidl
+++ b/dom/webidl/Client.webidl
@@ -15,7 +15,9 @@ interface Client {
readonly attribute DOMString id;
[Throws]
- void postMessage(any message, optional sequence<Transferable> transfer);
+ void postMessage(any message, sequence<object> transferable);
+ [Throws]
+ void postMessage(any message, optional StructuredSerializeOptions options);
};
[Exposed=ServiceWorker]
diff --git a/dom/webidl/DedicatedWorkerGlobalScope.webidl b/dom/webidl/DedicatedWorkerGlobalScope.webidl
index 26dca58dab..80c7c8decb 100644
--- a/dom/webidl/DedicatedWorkerGlobalScope.webidl
+++ b/dom/webidl/DedicatedWorkerGlobalScope.webidl
@@ -16,7 +16,9 @@
Exposed=DedicatedWorker]
interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
[Throws]
- void postMessage(any message, optional sequence<any> transfer);
+ void postMessage(any message, sequence<object> transfer);
+ [Throws]
+ void postMessage(any message, optional StructuredSerializeOptions options);
attribute EventHandler onmessage;
};
diff --git a/dom/webidl/MessagePort.webidl b/dom/webidl/MessagePort.webidl
index 59c61a7149..0b36c47d94 100644
--- a/dom/webidl/MessagePort.webidl
+++ b/dom/webidl/MessagePort.webidl
@@ -10,7 +10,9 @@
[Exposed=(Window,Worker,System)]
interface MessagePort : EventTarget {
[Throws]
- void postMessage(any message, optional sequence<Transferable> transferable);
+ void postMessage(any message, sequence<object> transferable);
+ [Throws]
+ void postMessage(any message, optional StructuredSerializeOptions options);
void start();
void close();
@@ -19,3 +21,8 @@ interface MessagePort : EventTarget {
attribute EventHandler onmessage;
};
// MessagePort implements Transferable;
+
+// Used to declare which objects should be transferred.
+dictionary StructuredSerializeOptions {
+ sequence<object> transfer = [];
+}; \ No newline at end of file
diff --git a/dom/webidl/ServiceWorker.webidl b/dom/webidl/ServiceWorker.webidl
index 8c3749e940..ff80fafc21 100644
--- a/dom/webidl/ServiceWorker.webidl
+++ b/dom/webidl/ServiceWorker.webidl
@@ -19,9 +19,10 @@ interface ServiceWorker : EventTarget {
attribute EventHandler onstatechange;
- // FIXME(catalinb): Should inherit this from Worker.
[Throws]
- void postMessage(any message, optional sequence<Transferable> transferable);
+ void postMessage(any message, sequence<object> transferable);
+ [Throws]
+ void postMessage(any message, optional StructuredSerializeOptions options);
};
ServiceWorker implements AbstractWorker;
diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
index 6020f71dce..2ba9f4f124 100644
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -22,7 +22,6 @@ interface IID;
interface nsIBrowserDOMWindow;
interface nsIMessageBroadcaster;
interface nsIDOMCrypto;
-typedef any Transferable;
// http://www.whatwg.org/specs/web-apps/current-work/
[PrimaryGlobal, LegacyUnenumerableNamedProperties, NeedResolve]
@@ -81,7 +80,9 @@ typedef any Transferable;
[Throws, UnsafeInPrerendering] void print();
[Throws, CrossOriginCallable, NeedsSubjectPrincipal]
- void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
+ void postMessage(any message, DOMString targetOrigin, optional sequence<object> transfer = []);
+ [Throws, CrossOriginCallable, NeedsSubjectPrincipal]
+ void postMessage(any message, optional WindowPostMessageOptions options);
// also has obsolete members
};
@@ -487,3 +488,7 @@ callback IdleRequestCallback = void (IdleDeadline deadline);
partial interface Window {
[ChromeOnly] readonly attribute boolean isSecureContextIfOpenerIgnored;
};
+
+dictionary WindowPostMessageOptions : StructuredSerializeOptions {
+ USVString targetOrigin = "/";
+};
diff --git a/dom/webidl/WindowOrWorkerGlobalScope.webidl b/dom/webidl/WindowOrWorkerGlobalScope.webidl
index 652a46ffcb..9e639db5f4 100644
--- a/dom/webidl/WindowOrWorkerGlobalScope.webidl
+++ b/dom/webidl/WindowOrWorkerGlobalScope.webidl
@@ -44,6 +44,10 @@ interface WindowOrWorkerGlobalScope {
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
[Throws]
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
+
+ // structured cloning
+ [Throws]
+ any structuredClone(any value, optional StructuredSerializeOptions options);
};
// https://fetch.spec.whatwg.org/#fetch-method
diff --git a/dom/webidl/Worker.webidl b/dom/webidl/Worker.webidl
index 158a502d60..c13357cd7e 100644
--- a/dom/webidl/Worker.webidl
+++ b/dom/webidl/Worker.webidl
@@ -19,7 +19,9 @@ interface Worker : EventTarget {
void terminate();
[Throws]
- void postMessage(any message, optional sequence<any> transfer);
+ void postMessage(any message, sequence<object> transfer);
+ [Throws]
+ void postMessage(any message, optional StructuredSerializeOptions options);
attribute EventHandler onmessage;
};
diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp
index 16dd28b8ad..f2a39ea9c2 100644
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -14,6 +14,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
#ifdef XP_WIN
@@ -77,7 +78,7 @@ ServiceWorker::GetScriptURL(nsString& aURL) const
void
ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
if (State() == ServiceWorkerState::Redundant) {
@@ -97,6 +98,15 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
}
+void
+ServiceWorker::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
} // namespace workers
} // namespace dom
} // namespace mozilla
diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h
index 3a3cce8260..eb14fc4293 100644
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -15,6 +15,8 @@ class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
+struct StructuredSerializeOptions;
+
namespace workers {
class ServiceWorkerInfo;
@@ -63,7 +65,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
private:
diff --git a/dom/workers/ServiceWorkerClient.cpp b/dom/workers/ServiceWorkerClient.cpp
index 1f845a947b..e8b8341e21 100644
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -11,6 +11,7 @@
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/ServiceWorkerMessageEvent.h"
#include "mozilla/dom/ServiceWorkerMessageEventBinding.h"
+#include "mozilla/dom/MessagePortBinding.h"
#include "nsGlobalWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDocument.h"
@@ -190,7 +191,7 @@ private:
void
ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -198,20 +199,11 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
workerPrivate->AssertIsOnWorkerThread();
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
- if (aTransferable.WasPassed()) {
- const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-
- JS::HandleValueArray elements =
- JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
- realTransferable.Elements());
-
- JSObject* array = JS_NewArrayObject(aCx, elements);
- if (!array) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
-
- transferable.setObject(*array);
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
+ aTransferable,
+ &transferable);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
@@ -229,3 +221,11 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
}
}
+void
+ServiceWorkerClient::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
diff --git a/dom/workers/ServiceWorkerClient.h b/dom/workers/ServiceWorkerClient.h
index 46bcdbc4c4..7f89ee611a 100644
--- a/dom/workers/ServiceWorkerClient.h
+++ b/dom/workers/ServiceWorkerClient.h
@@ -91,7 +91,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp
index 2f5b8f7500..fe6ec138b7 100644
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -141,7 +141,7 @@ NS_IMPL_ISUPPORTS0(MessageWaitUntilHandler)
nsresult
ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
{
ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));
diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h
index 8ce021c8cf..c2d5abfd6c 100644
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -74,7 +74,7 @@ public:
nsresult
SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
// This is used to validate the worker script and continue the installation
diff --git a/dom/workers/SharedWorker.cpp b/dom/workers/SharedWorker.cpp
index 97df0f57fd..2fae19f291 100644
--- a/dom/workers/SharedWorker.cpp
+++ b/dom/workers/SharedWorker.cpp
@@ -22,6 +22,7 @@
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
using mozilla::dom::MessagePort;
+using mozilla::dom::StructuredSerializeOptions;
using namespace mozilla;
USING_WORKERS_NAMESPACE
@@ -142,7 +143,7 @@ SharedWorker::Close()
void
SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnMainThread();
@@ -152,6 +153,15 @@ SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
}
+void
+SharedWorker::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper)
diff --git a/dom/workers/SharedWorker.h b/dom/workers/SharedWorker.h
index 4820b4e7c1..e85fc2609a 100644
--- a/dom/workers/SharedWorker.h
+++ b/dom/workers/SharedWorker.h
@@ -94,8 +94,15 @@ private:
// Only called by MessagePort.
void
- PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
};
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index cf270703e0..a7a4929f3b 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3006,13 +3006,12 @@ WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
template <class Derived>
void
-WorkerPrivateParent<Derived>::PostMessageInternal(
- JSContext* aCx,
- JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
- PromiseNativeHandler* aHandler,
- ErrorResult& aRv)
+WorkerPrivateParent<Derived>::PostMessageInternal(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+ PromiseNativeHandler* aHandler,
+ ErrorResult& aRv)
{
AssertIsOnParentThread();
@@ -3024,22 +3023,11 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
}
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
- if (aTransferable.WasPassed()) {
- const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-
- // The input sequence only comes from the generated bindings code, which
- // ensures it is rooted.
- JS::HandleValueArray elements =
- JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
- realTransferable.Elements());
-
- JSObject* array =
- JS_NewArrayObject(aCx, elements);
- if (!array) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
- transferable.setObject(*array);
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
+ aTransferable,
+ &transferable);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
RefPtr<MessageEventRunnable> runnable =
@@ -3083,8 +3071,9 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
template <class Derived>
void
WorkerPrivateParent<Derived>::PostMessage(
- JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
PostMessageInternal(aCx, aMessage, aTransferable, nullptr, nullptr, aRv);
@@ -3092,9 +3081,21 @@ WorkerPrivateParent<Derived>::PostMessage(
template <class Derived>
void
+WorkerPrivateParent<Derived>::PostMessage(
+ JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessageInternal(aCx, aMessage, aOptions.mTransfer, nullptr, nullptr, aRv);
+}
+
+template <class Derived>
+void
WorkerPrivateParent<Derived>::PostMessageToServiceWorker(
- JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv)
@@ -5677,27 +5678,17 @@ void
WorkerPrivate::PostMessageToParentInternal(
JSContext* aCx,
JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
AssertIsOnWorkerThread();
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
- if (aTransferable.WasPassed()) {
- const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-
- // The input sequence only comes from the generated bindings code, which
- // ensures it is rooted.
- JS::HandleValueArray elements =
- JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
- realTransferable.Elements());
-
- JSObject* array = JS_NewArrayObject(aCx, elements);
- if (!array) {
- aRv = NS_ERROR_OUT_OF_MEMORY;
- return;
- }
- transferable.setObject(*array);
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx,
+ aTransferable,
+ &transferable);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
}
RefPtr<MessageEventRunnable> runnable =
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
index b638c1ef11..0a71420047 100644
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -68,6 +68,7 @@ class PromiseNativeHandler;
class StructuredCloneHolder;
class WorkerDebuggerGlobalScope;
class WorkerGlobalScope;
+struct StructuredSerializeOptions;
} // namespace dom
namespace ipc {
class PrincipalInfo;
@@ -287,7 +288,7 @@ private:
void
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv);
@@ -400,12 +401,18 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
void
PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
PromiseNativeHandler* aHandler,
ErrorResult& aRv);
@@ -1161,18 +1168,17 @@ public:
void
PostMessageToParent(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
}
void
- PostMessageToParentMessagePort(
- JSContext* aCx,
- JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv);
+ PostMessageToParentMessagePort(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
void
EnterDebuggerEventLoop();
@@ -1471,7 +1477,7 @@ private:
void
PostMessageToParentInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv);
void
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
index ec42364b52..637e441732 100644
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -7,6 +7,7 @@
#include "jsapi.h"
#include "mozilla/EventListenerManager.h"
+#include "mozilla/Unused.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
@@ -492,6 +493,15 @@ WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
}
}
+// https://html.spec.whatwg.org/#structured-cloning
+void WorkerGlobalScope::StructuredClone(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ ErrorResult& aError) {
+ nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError);
+}
+
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
: WorkerGlobalScope(aWorkerPrivate)
{
@@ -535,13 +545,22 @@ DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
void
DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv);
}
+void
+DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
+ ErrorResult& aRv)
+{
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
const nsCString& aName)
: WorkerGlobalScope(aWorkerPrivate), mName(aName)
diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h
index 8d06152da2..01bae5cc11 100644
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -24,6 +24,7 @@ class Function;
class IDBFactory;
enum class ImageBitmapFormat : uint32_t;
class Performance;
+struct StructuredSerializeOptions;
class Promise;
class RequestOrUSVString;
class ServiceWorkerRegistration;
@@ -182,6 +183,11 @@ public:
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
mozilla::ErrorResult& aRv);
+ void StructuredClone(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ const StructuredSerializeOptions& aOptions,
+ JS::MutableHandle<JS::Value> aRv,
+ ErrorResult& aError);
+
bool
WindowInteractionAllowed() const
{
@@ -215,7 +221,13 @@ public:
void
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv);
+
+ void
+ PostMessage(JSContext* aCx,
+ JS::Handle<JS::Value> aMessage,
+ const StructuredSerializeOptions& aOptions,
ErrorResult& aRv);
IMPL_EVENT_HANDLER(message)
diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp
index a34870269a..a1d0292e76 100644
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -322,6 +322,43 @@ SandboxCreateFetch(JSContext* cx, HandleObject obj)
dom::HeadersBinding::GetConstructorObject(cx);
}
+static bool SandboxStructuredClone(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (!args.requireAtLeast(cx, "structuredClone", 1)) {
+ return false;
+ }
+
+ RootedDictionary<dom::StructuredSerializeOptions> options(cx);
+ if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
+ "Argument 2", false)) {
+ return false;
+ }
+
+ nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
+ if (!global) {
+ JS_ReportErrorASCII(cx, "structuredClone: Missing global");
+ return false;
+ }
+
+ JS::Rooted<JS::Value> result(cx);
+ ErrorResult rv;
+ nsContentUtils::StructuredClone(cx, global, args[0], options, &result, rv);
+ if (rv.MaybeSetPendingException(cx)) {
+ return false;
+ }
+
+ args.rval().set(result);
+ return true;
+}
+
+static bool SandboxCreateStructuredClone(JSContext* cx, HandleObject obj) {
+ MOZ_ASSERT(JS_IsGlobalObject(obj));
+
+ return JS_DefineFunction(cx, obj, "structuredClone", SandboxStructuredClone,
+ 1, 0);
+}
+
static bool
SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp)
{
@@ -927,6 +964,8 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
#endif
} else if (!strcmp(name.ptr(), "fetch")) {
fetch = true;
+ } else if (!strcmp(name.ptr(), "structuredClone")) {
+ structuredClone = true;
} else if (!strcmp(name.ptr(), "caches")) {
caches = true;
} else if (!strcmp(name.ptr(), "FileReader")) {
@@ -1002,6 +1041,9 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
if (fetch && !SandboxCreateFetch(cx, obj))
return false;
+ if (structuredClone && !SandboxCreateStructuredClone(cx, obj))
+ return false;
+
if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj))
return false;
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
index 724c8db3a9..6c2711210a 100644
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2879,6 +2879,7 @@ struct GlobalProperties {
bool crypto : 1;
bool rtcIdentityProvider : 1;
bool fetch : 1;
+ bool structuredClone : 1;
bool caches : 1;
bool fileReader: 1;
private:
diff --git a/js/xpconnect/tests/unit/test_structuredClone.js b/js/xpconnect/tests/unit/test_structuredClone.js
new file mode 100644
index 0000000000..6b9b2d776a
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_structuredClone.js
@@ -0,0 +1,25 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["structuredClone"] });
+
+ sb.equal = equal;
+
+ sb.testing = Components.utils.cloneInto({xyz: 123}, sb);
+ Cu.evalInSandbox(`
+ equal(structuredClone("abc"), "abc");
+
+ var obj = {a: 1};
+ obj.self = obj;
+ var clone = structuredClone(obj);
+ equal(clone.a, 1);
+ equal(clone.self, clone);
+
+ var ab = new ArrayBuffer(1);
+ clone = structuredClone(ab, {transfer: [ab]});
+ equal(clone.byteLength, 1);
+ equal(ab.byteLength, 0);
+
+ clone = structuredClone(testing);
+ equal(clone.xyz, 123);
+ `, sb);
+}
diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini
index 12648d3ecc..b13d8d455f 100644
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -114,6 +114,7 @@ skip-if = os == "android" # native test components aren't available on Android
[test_css.js]
[test_rtcIdentityProvider.js]
[test_sandbox_atob.js]
+[test_structuredClone.js]
[test_isProxy.js]
[test_getObjectPrincipal.js]
[test_sandbox_name.js]