diff --git a/dom/base/DOMPrefsInternal.h b/dom/base/DOMPrefsInternal.h index a81e02041c3b..ef3d52812753 100644 --- a/dom/base/DOMPrefsInternal.h +++ b/dom/base/DOMPrefsInternal.h @@ -20,6 +20,7 @@ DOM_WEBIDL_PREF(dom_webnotifications_serviceworker_enabled) DOM_WEBIDL_PREF(dom_webnotifications_requireinteraction_enabled) DOM_WEBIDL_PREF(dom_serviceWorkers_enabled) DOM_WEBIDL_PREF(dom_storageManager_enabled) +DOM_WEBIDL_PREF(dom_testing_structuredclonetester_enabled) DOM_WEBIDL_PREF(dom_promise_rejection_events_enabled) DOM_WEBIDL_PREF(dom_push_enabled) DOM_WEBIDL_PREF(gfx_offscreencanvas_enabled) diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 0fbf5975399c..db2561a3152f 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/StructuredCloneBlob.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/DirectoryBinding.h" +#include "mozilla/dom/DOMPrefs.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" @@ -28,6 +29,8 @@ #include "mozilla/dom/OffscreenCanvasBinding.h" #include "mozilla/dom/PMessagePort.h" #include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/StructuredCloneTester.h" +#include "mozilla/dom/StructuredCloneTesterBinding.h" #include "mozilla/dom/SubtleCryptoBinding.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/URLSearchParams.h" @@ -455,6 +458,10 @@ StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx, } #endif + if (aTag == SCTAG_DOM_STRUCTURED_CLONE_TESTER) { + return StructuredCloneTester::ReadStructuredClone(aCx, aReader); + } + // Don't know what this is. Bail. xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); return nullptr; @@ -505,6 +512,22 @@ StructuredCloneHolder::WriteFullySerializableObjects(JSContext* aCx, } #endif + // StructuredCloneTester - testing only + { + StructuredCloneTester* sct = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneTester, &obj, sct))) { + MOZ_ASSERT(StaticPrefs::dom_testing_structuredclonetester_enabled()); + + // "Fail" serialization + if (!sct->Serializable()) { + xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); + return false; + } + + return sct->WriteStructuredClone(aWriter); + } + } + if (NS_IsMainThread() && xpc::IsReflector(obj)) { nsCOMPtr base = xpc::UnwrapReflectorToISupports(obj); nsCOMPtr principal = do_QueryInterface(base); diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index d205301b64ab..79cc046a9e92 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -70,7 +70,9 @@ enum StructuredCloneTags { // Adding to the end of the list would make removing of other tags harder in // future. - SCTAG_DOM_MAX + SCTAG_DOM_MAX, + + SCTAG_DOM_STRUCTURED_CLONE_TESTER }; } // namespace dom diff --git a/dom/base/StructuredCloneTester.cpp b/dom/base/StructuredCloneTester.cpp new file mode 100644 index 000000000000..197542466634 --- /dev/null +++ b/dom/base/StructuredCloneTester.cpp @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "StructuredCloneTester.h" + +#include "js/StructuredClone.h" +#include "mozilla/RefPtr.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/StructuredCloneTesterBinding.h" +#include "xpcpublic.h" + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(StructuredCloneTester) +NS_IMPL_CYCLE_COLLECTING_ADDREF(StructuredCloneTester) +NS_IMPL_CYCLE_COLLECTING_RELEASE(StructuredCloneTester) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StructuredCloneTester) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +StructuredCloneTester::StructuredCloneTester(nsISupports* aParent, + const bool aSerializable, + const bool aDeserializable) + : mParent(aParent), + mSerializable(aSerializable), + mDeserializable(aDeserializable) +{ +} + +/* static */ already_AddRefed +StructuredCloneTester::Constructor(const GlobalObject& aGlobal, + const bool aSerializable, + const bool aDeserializable, + ErrorResult& aRv) +{ + RefPtr sct = new StructuredCloneTester(aGlobal.GetAsSupports(), + aSerializable, + aDeserializable); + return sct.forget(); +} + +bool +StructuredCloneTester::Serializable() const +{ + return mSerializable; +} + +bool +StructuredCloneTester::Deserializable() const +{ + return mDeserializable; +} + +/* static */ JSObject* +StructuredCloneTester::ReadStructuredClone(JSContext* aCx, + JSStructuredCloneReader* aReader) +{ + uint32_t serializable = 0; + uint32_t deserializable = 0; + + if (!JS_ReadUint32Pair(aReader, &serializable, &deserializable)) { + return nullptr; + } + + nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); + + if (NS_WARN_IF(!global)) { + return nullptr; + } + + // Prevent the return value from being trashed by a GC during ~nsRefPtr + JS::RootedObject result(aCx); + + RefPtr sct = new StructuredCloneTester( + global, + static_cast(serializable), + static_cast(deserializable) + ); + + // "Fail" deserialization + if (!sct->Deserializable()) { + xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); + return nullptr; + } + + result = sct->WrapObject(aCx, nullptr); + + return result; +} + +bool +StructuredCloneTester::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_TESTER, 0) && + JS_WriteUint32Pair(aWriter, static_cast(Serializable()), + static_cast(Deserializable())); +} + +nsISupports* +StructuredCloneTester::GetParentObject() const +{ + return mParent; +} + +JSObject* +StructuredCloneTester::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) +{ + return StructuredCloneTester_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/StructuredCloneTester.h b/dom/base/StructuredCloneTester.h new file mode 100644 index 000000000000..95cbe4dc01f5 --- /dev/null +++ b/dom/base/StructuredCloneTester.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_StructuredCloneTester_h +#define mozilla_dom_StructuredCloneTester_h + +#include "mozilla/AlreadyAddRefed.h" +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" + +struct JSStructuredCloneReader; +struct JSStructuredCloneWriter; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; + +class StructuredCloneTester final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StructuredCloneTester) + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const bool aSerializable, + const bool aDeserializable, ErrorResult& aRv); + + bool + Serializable() const; + + bool + Deserializable() const; + + static JSObject* + ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader); + + bool + WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + + nsISupports* + GetParentObject() const; + + // nsWrapperCache interface + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + +private: + StructuredCloneTester(nsISupports* aParent, + const bool aSerializable, + const bool aDeserializable); + ~StructuredCloneTester() = default; + + nsCOMPtr mParent; + bool mSerializable; + bool mDeserializable; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_StructuredCloneTester_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 0100a4d17e77..f36328c4da70 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -220,6 +220,7 @@ EXPORTS.mozilla.dom += [ 'StructuredCloneBlob.h', 'StructuredCloneHolder.h', 'StructuredCloneTags.h', + 'StructuredCloneTester.h', 'StyleSheetList.h', 'SubtleCrypto.h', 'SyncMessageSender.h', @@ -375,6 +376,7 @@ UNIFIED_SOURCES += [ 'ShadowRoot.cpp', 'StructuredCloneBlob.cpp', 'StructuredCloneHolder.cpp', + 'StructuredCloneTester.cpp', 'StyleSheetList.cpp', 'SubtleCrypto.cpp', 'TabGroup.cpp', diff --git a/dom/webidl/StructuredCloneTester.webidl b/dom/webidl/StructuredCloneTester.webidl new file mode 100644 index 000000000000..cade36322441 --- /dev/null +++ b/dom/webidl/StructuredCloneTester.webidl @@ -0,0 +1,15 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +// The WebIDL compiler does not accept a Pref-ed interface exposed to any scopes +// other than *only* `Window`, so the Func is Pref-ed instead. +[Constructor(boolean serializable, boolean deserializable), + Exposed=(Window,Worker), + Func="mozilla::dom::DOMPrefs::dom_testing_structuredclonetester_enabled"] +interface StructuredCloneTester { + readonly attribute boolean serializable; + readonly attribute boolean deserializable; +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index d5e83c019bcb..1500eb412219 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -777,6 +777,7 @@ WEBIDL_FILES = [ 'StorageType.webidl', 'StreamFilter.webidl', 'StreamFilterDataEvent.webidl', + 'StructuredCloneTester.webidl', 'StyleSheet.webidl', 'StyleSheetList.webidl', 'SubtleCrypto.webidl', diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h index 0e686dae1697..c098e435f9e5 100644 --- a/modules/libpref/init/StaticPrefList.h +++ b/modules/libpref/init/StaticPrefList.h @@ -286,6 +286,12 @@ VARCACHE_PREF( RelaxedAtomicBool, false ) +VARCACHE_PREF( + "dom.testing.structuredclonetester.enabled", + dom_testing_structuredclonetester_enabled, + RelaxedAtomicBool, false +) + // Enable Storage API for all platforms except Android. #if !defined(MOZ_WIDGET_ANDROID) # define PREF_VALUE true