Bug 1493237 - Use a test-only non-nsISupports wrapper cached WebIDL class in a weak map test r=bzbarsky
authorAndrew McCreight <continuation@gmail.com>
Thu, 11 Oct 2018 23:02:10 +0000
changeset 440805 a19bd92250b6d4c7ca6639c632bca4950d2b911d
parent 440804 0c9456c0ba9afcb7bcd5b9ee3f6cf99e4d745fae
child 440806 31313cac4517c54061fe8207a965492cdde9b564
child 440859 d699af334ae27f961d06def962b91bb9f1063309
child 440860 580c03c8ae389ea12073561e2b445312257d7cf3
push id70830
push useramccreight@mozilla.com
push dateFri, 12 Oct 2018 00:06:27 +0000
treeherderautoland@a19bd92250b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1493237
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1493237 - Use a test-only non-nsISupports wrapper cached WebIDL class in a weak map test r=bzbarsky Differential Revision: https://phabricator.services.mozilla.com/D8134
dom/bindings/moz.build
dom/bindings/test/TestFunctions.cpp
dom/bindings/test/TestFunctions.h
dom/bindings/test/WrapperCachedNonISupportsTestInterface.cpp
dom/bindings/test/WrapperCachedNonISupportsTestInterface.h
dom/webidl/TestFunctions.webidl
js/xpconnect/tests/mochitest/mochitest.ini
js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -117,41 +117,43 @@ UNIFIED_SOURCES += [
     'ToJSValue.cpp',
     'WebIDLGlobalNameHash.cpp',
 ]
 
 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",
         "test/TestInterfaceIterableDouble.h",
         "test/TestInterfaceIterableDoubleUnion.h",
         "test/TestInterfaceIterableSingle.h",
         "test/TestInterfaceMaplike.h",
         "test/TestInterfaceMaplikeObject.h",
         "test/TestInterfaceSetlike.h",
-        "test/TestInterfaceSetlikeNode.h"
+        "test/TestInterfaceSetlikeNode.h",
+        "test/WrapperCachedNonISupportsTestInterface.h",
         ]
     UNIFIED_SOURCES += [
         "test/TestFunctions.cpp",
         "test/TestInterfaceIterableDouble.cpp",
         "test/TestInterfaceIterableDoubleUnion.cpp",
         "test/TestInterfaceIterableSingle.cpp",
         "test/TestInterfaceMaplike.cpp",
         "test/TestInterfaceMaplikeObject.cpp",
         "test/TestInterfaceSetlike.cpp",
         "test/TestInterfaceSetlikeNode.cpp",
+        "test/WrapperCachedNonISupportsTestInterface.cpp",
         ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 SPHINX_TREES['webidl'] = 'docs'
 
--- a/dom/bindings/test/TestFunctions.cpp
+++ b/dom/bindings/test/TestFunctions.cpp
@@ -2,16 +2,17 @@
 /* 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 "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"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ TestFunctions*
@@ -135,16 +136,25 @@ TestFunctions::ObjectFromAboutBlank(JSCo
   nsIDocument* doc = win->GetDoc();
   if (!doc) {
     return false;
   }
 
   return doc->GetDocumentURI()->GetSpecOrDefault().EqualsLiteral("about:blank");
 }
 
+WrapperCachedNonISupportsTestInterface*
+TestFunctions::WrapperCachedNonISupportsObject()
+{
+  if (!mWrapperCachedNonISupportsTestInterface) {
+    mWrapperCachedNonISupportsTestInterface = new WrapperCachedNonISupportsTestInterface();
+  }
+  return mWrapperCachedNonISupportsTestInterface;
+}
+
 bool
 TestFunctions::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
                           JS::MutableHandle<JSObject*> aWrapper)
 {
   return TestFunctions_Binding::Wrap(aCx, this, aGivenProto, aWrapper);
 }
 
 }
--- a/dom/bindings/test/TestFunctions.h
+++ b/dom/bindings/test/TestFunctions.h
@@ -12,16 +12,17 @@
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PromiseReturner;
+class WrapperCachedNonISupportsTestInterface;
 
 class TestFunctions : public NonRefcountedDOMObject {
 public:
   static TestFunctions* Constructor(GlobalObject& aGlobal, ErrorResult& aRv);
 
   static void
   ThrowUncatchableException(GlobalObject& aGlobal, ErrorResult& aRv);
 
@@ -46,18 +47,21 @@ public:
   ThrowToRejectPromise(GlobalObject& aGlobal,
                        ErrorResult& aError);
 
   int32_t One() const;
   int32_t Two() const;
 
   static bool ObjectFromAboutBlank(JSContext* aCx, JSObject* aObj);
 
+  WrapperCachedNonISupportsTestInterface* WrapperCachedNonISupportsObject();
+
   bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
                   JS::MutableHandle<JSObject*> aWrapper);
 private:
   nsString mStringData;
+  RefPtr<WrapperCachedNonISupportsTestInterface> mWrapperCachedNonISupportsTestInterface;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TestFunctions_h
new file mode 100644
--- /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<JSObject*> aGivenProto)
+{
+  return WrapperCachedNonISupportsTestInterface_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /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<JSObject*> aGivenProto) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WrapperCachedNonISupportsTestInterface_h
--- a/dom/webidl/TestFunctions.webidl
+++ b/dom/webidl/TestFunctions.webidl
@@ -3,16 +3,20 @@
  * 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/.
  */
 
 // A dumping ground for random testing functions
 
 callback PromiseReturner = Promise<any>();
 
+[Pref="dom.expose_test_interfaces"]
+interface WrapperCachedNonISupportsTestInterface {
+};
+
 [Pref="dom.expose_test_interfaces",
  Constructor]
 interface TestFunctions {
   [Throws]
   static void throwUncatchableException();
 
   // Simply returns its argument.  Can be used to test Promise
   // argument processing behavior.
@@ -51,9 +55,16 @@ interface TestFunctions {
 
   // Some attributes for the toJSON to work with.
   readonly attribute long one;
   [Func="mozilla::dom::TestFunctions::ObjectFromAboutBlank"]
   readonly attribute long two;
 
   // 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;
 };
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -98,15 +98,16 @@ support-files =
 [test_bug1448048.html]
 [test_crosscompartment_weakmap.html]
 [test_frameWrapping.html]
 # The JS test component we use below is only available in debug builds.
 [test_getWebIDLCaller.html]
 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]
 [test_sandbox_fetch.html]
   support-files =
     ../../../../dom/tests/mochitest/fetch/test_fetch_basic.js
 [test_weakmaps.html]
--- a/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html
+++ b/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html
@@ -34,60 +34,61 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(!div_fail, "Using elem.style as a weak map key should not produce an exception.");
 
     is(live_map.get(get_div_style()), 12345, "Live map should have live style with right value before GC.");
 
   }
 
   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();
 
 
   /* Set up for running precise GC/CC then check the results. */
 
   SpecialPowers.exactGC(function () {
     SpecialPowers.forceCC();
     SpecialPowers.forceGC();
     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);
+  });
   </script>
 </head>
 <div></div>
 <div id="mydivname"></div>
-<body onload="go()";>
+<body>
 <p id="display"></p>
 </body>
 </html>