Bug 1396466 - Remove Xray expando chains from the weakmap when transplanting nodes. r=bz, a=ritu
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 10 Oct 2017 10:42:18 -0500
changeset 432453 4d00f19c020d85b88841c557a8ca8baea72735a7
parent 432452 c8d3b27528f758e9291946fa16b1269b6ec71acb
child 432454 355a965b180c95d3e75f859c0d8605b661270dd5
push id7956
push userryanvm@gmail.com
push dateThu, 12 Oct 2017 19:12:45 +0000
treeherdermozilla-beta@bfa99221e3af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, ritu
bugs1396466
milestone57.0
Bug 1396466 - Remove Xray expando chains from the weakmap when transplanting nodes. r=bz, a=ritu
dom/base/crashtests/1396466.html
dom/base/crashtests/crashtests.list
js/public/WeakMapPtr.h
js/src/vm/WeakMapPtr.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1396466.html
@@ -0,0 +1,20 @@
+<script>
+function addNew(i,t){
+  let n=document.createElement(t);
+  n.setAttribute('id', i);
+  document.getElementById('a').appendChild(n);
+}
+document.addEventListener("DOMContentLoaded", function(){
+  addNew('b','multicol');
+  addNew('c','marquee');
+  addNew('d','spacer');
+  document.getElementById('a').appendChild(document.createElement('iframe'));
+  document.getElementById('b').focus();
+  window.frames[0].document.body.appendChild(document.getElementById('c'));
+  let o=window.frames[0].document.body.childNodes[0];
+  document.getElementById('d').appendChild(o.parentNode.removeChild(o));
+  try{window.find('x')}catch(e){}
+  window.frames[0].document.body.appendChild(document.getElementById('c'));
+});
+</script>
+<body id='a'></body>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -220,8 +220,9 @@ load 1377826.html
 skip-if(stylo&&isDebugBuild&&winWidget) load structured_clone_container_throws.html # Bug 1383845
 HTTP(..) load xhr_abortinprogress.html
 load xhr_empty_datauri.html
 load xhr_html_nullresponse.html
 load 1383478.html
 load 1383780.html
 pref(clipboard.autocopy,true) load 1385272-1.html
 load 1393806.html
+load 1396466.html
--- a/js/public/WeakMapPtr.h
+++ b/js/public/WeakMapPtr.h
@@ -27,16 +27,17 @@ class JS_PUBLIC_API(WeakMapPtr)
     bool init(JSContext* cx);
     bool initialized() { return ptr != nullptr; }
     void destroy();
     virtual ~WeakMapPtr() { MOZ_ASSERT(!initialized()); }
     void trace(JSTracer* tracer);
 
     V lookup(const K& key);
     bool put(JSContext* cx, const K& key, const V& value);
+    V removeValue(const K& key);
 
   private:
     void* ptr;
 
     // WeakMapPtr is neither copyable nor assignable.
     WeakMapPtr(const WeakMapPtr& wmp) = delete;
     WeakMapPtr& operator=(const WeakMapPtr& wmp) = delete;
 };
--- a/js/src/vm/WeakMapPtr.cpp
+++ b/js/src/vm/WeakMapPtr.cpp
@@ -93,16 +93,34 @@ JS::WeakMapPtr<K, V>::lookup(const K& ke
 template <typename K, typename V>
 bool
 JS::WeakMapPtr<K, V>::put(JSContext* cx, const K& key, const V& value)
 {
     MOZ_ASSERT(initialized());
     return details::Utils<K, V>::cast(ptr)->put(key, value);
 }
 
+template <typename K, typename V>
+V
+JS::WeakMapPtr<K, V>::removeValue(const K& key)
+{
+    typedef typename details::Utils<K, V>::Type Map;
+    typedef typename Map::Ptr Ptr;
+
+    MOZ_ASSERT(initialized());
+
+    Map* map = details::Utils<K, V>::cast(ptr);
+    if (Ptr result = map->lookup(key)) {
+        V value = result->value();
+        map->remove(result);
+        return value;
+    }
+    return details::DataType<V>::NullValue();
+}
+
 //
 // Supported specializations of JS::WeakMap:
 //
 
 template class JS_PUBLIC_API(JS::WeakMapPtr)<JSObject*, JSObject*>;
 
 #ifdef DEBUG
 // Nobody's using this at the moment, but we want to make sure it compiles.
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -708,16 +708,25 @@ JSObject*
 XPCWrappedNativeScope::GetExpandoChain(HandleObject target)
 {
     MOZ_ASSERT(ObjectScope(target) == this);
     if (!mXrayExpandos.initialized())
         return nullptr;
     return mXrayExpandos.lookup(target);
 }
 
+JSObject*
+XPCWrappedNativeScope::DetachExpandoChain(HandleObject target)
+{
+    MOZ_ASSERT(ObjectScope(target) == this);
+    if (!mXrayExpandos.initialized())
+        return nullptr;
+    return mXrayExpandos.removeValue(target);
+}
+
 bool
 XPCWrappedNativeScope::SetExpandoChain(JSContext* cx, HandleObject target,
                                        HandleObject chain)
 {
     MOZ_ASSERT(ObjectScope(target) == this);
     MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
     MOZ_ASSERT_IF(chain, ObjectScope(chain) == this);
     if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -920,16 +920,19 @@ public:
     GetPrincipal() const {
         JSCompartment* c = js::GetObjectCompartment(mGlobalJSObject);
         return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     }
 
     JSObject*
     GetExpandoChain(JS::HandleObject target);
 
+    JSObject*
+    DetachExpandoChain(JS::HandleObject target);
+
     bool
     SetExpandoChain(JSContext* cx, JS::HandleObject target, JS::HandleObject chain);
 
     static void
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc);
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -684,17 +684,17 @@ TransplantObject(JSContext* cx, JS::Hand
 
 JSObject*
 TransplantObjectRetainingXrayExpandos(JSContext* cx, JS::HandleObject origobj,
                                       JS::HandleObject target)
 {
     // Save the chain of objects that carry origobj's Xray expando properties
     // (from all compartments). TransplantObject will blow this away; we'll
     // restore it manually afterwards.
-    RootedObject expandoChain(cx, GetXrayTraits(origobj)->getExpandoChain(origobj));
+    RootedObject expandoChain(cx, GetXrayTraits(origobj)->detachExpandoChain(origobj));
 
     RootedObject newIdentity(cx, TransplantObject(cx, origobj, target));
 
     // Copy Xray expando properties to the new wrapper.
     if (!GetXrayTraits(newIdentity)->cloneExpandoChain(cx, newIdentity, expandoChain)) {
         // Failure here means some expandos were not copied over. The object graph
         // and the Xray machinery are left in a consistent state, but mysteriously
         // losing these expandos is too weird to allow.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -182,16 +182,22 @@ XrayAwareCalleeGlobalForSpecializedGette
 }
 
 JSObject*
 XrayTraits::getExpandoChain(HandleObject obj)
 {
     return ObjectScope(obj)->GetExpandoChain(obj);
 }
 
+JSObject*
+XrayTraits::detachExpandoChain(HandleObject obj)
+{
+    return ObjectScope(obj)->DetachExpandoChain(obj);
+}
+
 bool
 XrayTraits::setExpandoChain(JSContext* cx, HandleObject obj, HandleObject chain)
 {
     return ObjectScope(obj)->SetExpandoChain(cx, obj, chain);
 }
 
 // static
 XPCWrappedNative*
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -107,16 +107,17 @@ public:
         HOLDER_SHARED_SLOT_COUNT
     };
 
     JSObject* getHolder(JSObject* wrapper);
     JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
     virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
 
     JSObject* getExpandoChain(JS::HandleObject obj);
+    JSObject* detachExpandoChain(JS::HandleObject obj);
     bool setExpandoChain(JSContext* cx, JS::HandleObject obj, JS::HandleObject chain);
     bool cloneExpandoChain(JSContext* cx, JS::HandleObject dst, JS::HandleObject srcChain);
 
 protected:
     static const JSClass HolderClass;
 
     // Get the JSClass we should use for our expando object.
     virtual const JSClass* getExpandoClass(JSContext* cx,