diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index ded37bd4c8..50cc781efc 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" @@ -10023,3 +10024,41 @@ nsContentUtils::CreateJSValueFromSequenceOfObject(JSContext* aCx, aValue.setObject(*array); return NS_OK; } + +/* static */ +void nsContentUtils::StructuredClone(JSContext* aCx, + nsIGlobalObject* aGlobal, + JS::Handle aValue, + const StructuredSerializeOptions& aOptions, + JS::MutableHandle aRv, + ErrorResult& aError) { + JS::Rooted 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> ports = holder.TakeTransferredPorts(); + Unused << ports; +} diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 62f8451555..c0e44b3be4 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 { @@ -2817,6 +2818,18 @@ public: const mozilla::dom::Sequence& aTransfer, JS::MutableHandle 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 aValue, + const mozilla::dom::StructuredSerializeOptions& aOptions, + JS::MutableHandle aRv, + mozilla::ErrorResult& aError); + private: static bool InitializeEventTable(); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 1000f89a70..b53ad29769 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -192,7 +192,6 @@ #include "mozilla/dom/IDBFactory.h" #include "mozilla/dom/MessageChannel.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/MessagePort.h" #ifdef MOZ_GAMEPAD #include "mozilla/dom/Gamepad.h" @@ -14650,40 +14649,10 @@ void nsGlobalWindow::StructuredClone(JSContext* aCx, JS::Handle aValue, const StructuredSerializeOptions& aOptions, - JS::MutableHandle aRetval, + JS::MutableHandle aRv, ErrorResult& aError) { - JS::Rooted transferArray(aCx, JS::UndefinedValue()); - aError = nsContentUtils::CreateJSValueFromSequenceOfObject( - aCx, aOptions.mTransfer, &transferArray); - if (NS_WARN_IF(aError.Failed())) { - return; - } - - // FIXME: Uncomment once bug 1609990 and bug 1611855 lands. - //JS::CloneDataPolicy clonePolicy; - //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: Stop casting to nsISupports once bug 1585284 lands. - nsISupports* windowAsSupports = static_cast(this); - // TODO: Pass clonePolicy. - //holder.Read(this, aCx, aRetval, clonePolicy, aError); - holder.Read(windowAsSupports, aCx, aRetval, aError); - if (NS_WARN_IF(aError.Failed())) { - return; - } - - nsTArray> ports = holder.TakeTransferredPorts(); - Unused << ports; + nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError); } // Helper called by methods that move/resize the window, diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 141a14e727..bc989ea2e4 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1185,7 +1185,7 @@ public: void StructuredClone(JSContext* aCx, JS::Handle aValue, const mozilla::dom::StructuredSerializeOptions& aOptions, - JS::MutableHandle aRetval, + JS::MutableHandle aRv, mozilla::ErrorResult& aError); // ChromeWindow bits. Do NOT call these unless your window is in diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 1c49e38f18..637e441732 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -497,39 +497,9 @@ WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage, void WorkerGlobalScope::StructuredClone(JSContext* aCx, JS::Handle aValue, const StructuredSerializeOptions& aOptions, - JS::MutableHandle aRetval, + JS::MutableHandle aRv, ErrorResult& aError) { - JS::Rooted transferArray(aCx, JS::UndefinedValue()); - aError = nsContentUtils::CreateJSValueFromSequenceOfObject( - aCx, aOptions.mTransfer, &transferArray); - if (NS_WARN_IF(aError.Failed())) { - return; - } - - // FIXME: Uncomment once bug 1609990 and bug 1611855 lands. - //JS::CloneDataPolicy clonePolicy; - //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: Stop casting to nsISupports once bug 1585284 lands. - nsISupports* workerAsSupports = static_cast(this); - // TODO: Pass clonePolicy. - //holder.Read(this, aCx, aRetval, clonePolicy, aError); - holder.Read(workerAsSupports, aCx, aRetval, aError); - if (NS_WARN_IF(aError.Failed())) { - return; - } - - nsTArray> ports = holder.TakeTransferredPorts(); - Unused << ports; + nsContentUtils::StructuredClone(aCx, this, aValue, aOptions, aRv, aError); } DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate) diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 31b4d6d850..01bae5cc11 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -185,7 +185,7 @@ public: void StructuredClone(JSContext* aCx, JS::Handle aValue, const StructuredSerializeOptions& aOptions, - JS::MutableHandle aRetval, + JS::MutableHandle aRv, ErrorResult& aError); bool diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index b8a4859a46..c3b740e7e5 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -326,6 +326,45 @@ 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 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 result(cx); + ErrorResult rv; + nsContentUtils::StructuredClone(cx, global, args[0], options, &result, rv); + if (rv.MaybeSetPendingException(cx)) { + return false; + } + + MOZ_ASSERT_IF(result.isGCThing(), + !JS::GCThingIsMarkedGray(result.toGCCellPtr())); + 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) { @@ -931,6 +970,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")) { @@ -1006,6 +1047,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]