diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index a4fdcc710d77..41684ea9ae0c 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -122,11 +122,11 @@ SOURCES += [ 'StructuredClone.cpp', ] -# Tests for maplike and setlike require bindings to be built, which means they -# must be included in libxul. This breaks the "no test classes are exported" -# rule stated in the test/ directory, but it's the only way this will work. -# Test classes are only built in debug mode, and all tests requiring use of -# them are only run in debug mode. +# Some tests, including those for for maplike and setlike, require bindings +# to be built, which means they must be included in libxul. This breaks the +# "no test classes are exported" rule stated in the test/ directory, but it's +# the only way this will work. Test classes are only built in debug mode, and +# all tests requiring use of them are only run in debug mode. if CONFIG['MOZ_DEBUG'] and CONFIG['ENABLE_TESTS']: EXPORTS.mozilla.dom += [ "test/TestFunctions.h", @@ -136,7 +136,8 @@ if CONFIG['MOZ_DEBUG'] and CONFIG['ENABLE_TESTS']: "test/TestInterfaceMaplike.h", "test/TestInterfaceMaplikeObject.h", "test/TestInterfaceSetlike.h", - "test/TestInterfaceSetlikeNode.h" + "test/TestInterfaceSetlikeNode.h", + "test/WrapperCachedNonISupportsTestInterface.h", ] UNIFIED_SOURCES += [ "test/TestFunctions.cpp", @@ -147,6 +148,7 @@ if CONFIG['MOZ_DEBUG'] and CONFIG['ENABLE_TESTS']: "test/TestInterfaceMaplikeObject.cpp", "test/TestInterfaceSetlike.cpp", "test/TestInterfaceSetlikeNode.cpp", + "test/WrapperCachedNonISupportsTestInterface.cpp", ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/dom/bindings/test/TestFunctions.cpp b/dom/bindings/test/TestFunctions.cpp index ca662ff94b30..c55bdaca5f65 100644 --- a/dom/bindings/test/TestFunctions.cpp +++ b/dom/bindings/test/TestFunctions.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/TestFunctions.h" #include "mozilla/dom/TestFunctionsBinding.h" +#include "mozilla/dom/WrapperCachedNonISupportsTestInterface.h" #include "nsStringBuffer.h" #include "mozITestInterfaceJS.h" #include "nsComponentManagerUtils.h" @@ -140,6 +141,15 @@ TestFunctions::ObjectFromAboutBlank(JSContext* aCx, JSObject* aObj) return doc->GetDocumentURI()->GetSpecOrDefault().EqualsLiteral("about:blank"); } +WrapperCachedNonISupportsTestInterface* +TestFunctions::WrapperCachedNonISupportsObject() +{ + if (!mWrapperCachedNonISupportsTestInterface) { + mWrapperCachedNonISupportsTestInterface = new WrapperCachedNonISupportsTestInterface(); + } + return mWrapperCachedNonISupportsTestInterface; +} + bool TestFunctions::WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aWrapper) diff --git a/dom/bindings/test/TestFunctions.h b/dom/bindings/test/TestFunctions.h index 4772d8841991..9e07797fcccb 100644 --- a/dom/bindings/test/TestFunctions.h +++ b/dom/bindings/test/TestFunctions.h @@ -17,6 +17,7 @@ namespace dom { class Promise; class PromiseReturner; +class WrapperCachedNonISupportsTestInterface; class TestFunctions : public NonRefcountedDOMObject { public: @@ -51,10 +52,13 @@ public: static bool ObjectFromAboutBlank(JSContext* aCx, JSObject* aObj); + WrapperCachedNonISupportsTestInterface* WrapperCachedNonISupportsObject(); + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aWrapper); private: nsString mStringData; + RefPtr mWrapperCachedNonISupportsTestInterface; }; } // namespace dom diff --git a/dom/bindings/test/WrapperCachedNonISupportsTestInterface.cpp b/dom/bindings/test/WrapperCachedNonISupportsTestInterface.cpp new file mode 100644 index 000000000000..a2a959e01758 --- /dev/null +++ b/dom/bindings/test/WrapperCachedNonISupportsTestInterface.cpp @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "mozilla/dom/WrapperCachedNonISupportsTestInterface.h" +#include "mozilla/dom/TestFunctionsBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WrapperCachedNonISupportsTestInterface, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WrapperCachedNonISupportsTestInterface, Release) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WrapperCachedNonISupportsTestInterface) + +JSObject* +WrapperCachedNonISupportsTestInterface::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return WrapperCachedNonISupportsTestInterface_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/bindings/test/WrapperCachedNonISupportsTestInterface.h b/dom/bindings/test/WrapperCachedNonISupportsTestInterface.h new file mode 100644 index 000000000000..9a677a94cf23 --- /dev/null +++ b/dom/bindings/test/WrapperCachedNonISupportsTestInterface.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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_WrapperCachedNonISupportsTestInterface_h +#define mozilla_dom_WrapperCachedNonISupportsTestInterface_h + +#include "js/TypeDecls.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + + +namespace mozilla { +namespace dom { + +class WrapperCachedNonISupportsTestInterface final : public nsWrapperCache +{ +public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WrapperCachedNonISupportsTestInterface) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WrapperCachedNonISupportsTestInterface) + +public: + WrapperCachedNonISupportsTestInterface() {} + +protected: + ~WrapperCachedNonISupportsTestInterface() {} + +public: + nsISupports* GetParentObject() const { + return nullptr; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_WrapperCachedNonISupportsTestInterface_h diff --git a/dom/webidl/TestFunctions.webidl b/dom/webidl/TestFunctions.webidl index 9c72392c3701..c278b2069232 100644 --- a/dom/webidl/TestFunctions.webidl +++ b/dom/webidl/TestFunctions.webidl @@ -8,6 +8,10 @@ callback PromiseReturner = Promise(); +[Pref="dom.expose_test_interfaces"] +interface WrapperCachedNonISupportsTestInterface { +}; + [Pref="dom.expose_test_interfaces", Constructor] interface TestFunctions { @@ -56,4 +60,11 @@ interface TestFunctions { // Testing for how default toJSON behaves. [Default] object toJSON(); + + // This returns a wrappercached non-ISupports object. While this will always + // return the same object, no optimization attributes like [Pure] should be + // used here because the object should not be held alive from JS by the + // bindings. This is needed to test wrapper preservation for weak map keys. + // See bug 1351501. + readonly attribute WrapperCachedNonISupportsTestInterface wrapperCachedNonISupportsObject; }; diff --git a/js/xpconnect/tests/mochitest/mochitest.ini b/js/xpconnect/tests/mochitest/mochitest.ini index ecabd947669f..e38b5695e87d 100644 --- a/js/xpconnect/tests/mochitest/mochitest.ini +++ b/js/xpconnect/tests/mochitest/mochitest.ini @@ -103,6 +103,7 @@ support-files = skip-if = (debug == false) [test_getweakmapkeys.html] [test_paris_weakmap_keys.html] +skip-if = (debug == false) [test_nac.xhtml] [test_nukeContentWindow.html] [test_sameOriginPolicy.html] diff --git a/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html b/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html index 4477b04e9710..9c8bfa5e0ce8 100644 --- a/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html +++ b/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html @@ -39,28 +39,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=777385 make_live_map(); - - // CanvasGradient is a non-nsISupports wrapper cached class using WebIDL - // bindings. If we used it as a key in a weak map, then it should not be - // removed from the weak map as long as it remains alive. - let doc = new DOMParser().parseFromString("", "text/html"); - let canv = doc.createElement("canvas"); - let ctx = canv.getContext("2d"); + let tf = new TestFunctions; let add_non_isupports2 = function () { - let grad = ctx.createLinearGradient(0, 0, 0, 0); - ctx.strokeStyle = grad; + let testKey = tf.wrapperCachedNonISupportsObject; - let gradFail = false; + let testFail = false; try { - live_map.set(grad, 23456); + live_map.set(testKey, 23456); } catch (e) { - gradFail = true; + testFail = true; } - ok(!gradFail, "Using a wrapper cached non-nsISupports class as a weak map key should not produce an exception."); + ok(!testFail, "Using a wrapper cached non-nsISupports class as a weak map key should not produce an exception."); - is(live_map.get(grad), 23456, "Live map should have live DOMPoint with right value before GC."); + is(live_map.get(testKey), 23456, "Live map should have wrapper cached non-nsISupports class right value before GC."); } add_non_isupports2(); @@ -74,20 +67,28 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=777385 SpecialPowers.forceGC(); is(SpecialPowers.nondeterministicGetWeakMapKeys(live_map).length, 2, - "Live nsISupports new DOM bindings wrappercached native weak map key should not be removed."); + "Live WebIDL bindings keys should not be removed from a weak map."); - is(live_map.get(get_div_style()), 12345, "Live map should have live style with right value after GC."); - is(live_map.get(ctx.strokeStyle), 23456, "Live map should have live gradient with right value after GC."); + is(live_map.get(get_div_style()), 12345, "Live weak map should have live style with right value after GC."); + is(live_map.get(tf.wrapperCachedNonISupportsObject), 23456, + "Live weak map should have live wrapper cached non-nsISupports class with right value after GC."); SimpleTest.finish(); }); } + + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, + go); + });
- +