Bug 996785 - Fix CPOW wrapping and compartment ownership (r=mrbkap)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 16 May 2014 16:40:37 -0700
changeset 202874 a141144b6c99a87d255fe9e7753611ec3755d280
parent 202873 9055e91c6154d4372c1fb498cbc51da20cca505b
child 202875 4f0a7452db012f72fca1738cfa773162e145196b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs996785
milestone32.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 996785 - Fix CPOW wrapping and compartment ownership (r=mrbkap)
js/ipc/JavaScriptShared.cpp
js/ipc/JavaScriptShared.h
js/ipc/WrapperOwner.cpp
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=4 sw=4 et 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 "JavaScriptShared.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/TabChild.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
@@ -351,20 +352,51 @@ JavaScriptShared::ConvertID(const JSIID 
     to->m3[2] = from.m3_2();
     to->m3[3] = from.m3_3();
     to->m3[4] = from.m3_4();
     to->m3[5] = from.m3_5();
     to->m3[6] = from.m3_6();
     to->m3[7] = from.m3_7();
 }
 
-void
-JavaScriptShared::ReportNonexistentObject(JSContext *cx)
+JSObject *
+JavaScriptShared::findObjectById(JSContext *cx, uint32_t objId)
 {
-    JS_ReportError(cx, "operation not possible on dead CPOW");
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj) {
+        JS_ReportError(cx, "operation not possible on dead CPOW");
+        return nullptr;
+    }
+
+    // Objects are stored in objects_ unwrapped. We want to wrap the object
+    // before returning it so that all operations happen on Xray wrappers. If
+    // the object is a DOM element, we try to obtain the corresponding
+    // TabChildGlobal and wrap in that.
+    RootedObject global(cx, GetGlobalForObjectCrossCompartment(obj));
+    nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::GetNativeForGlobal(global);
+    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(nativeGlobal);
+    if (window) {
+        dom::TabChild *tabChild = dom::TabChild::GetFrom(window);
+        if (tabChild) {
+            nsCOMPtr<nsIContentFrameMessageManager> mm;
+            tabChild->GetMessageManager(getter_AddRefs(mm));
+            nsCOMPtr<nsIGlobalObject> tabChildNativeGlobal = do_QueryInterface(mm);
+            RootedObject tabChildGlobal(cx, tabChildNativeGlobal->GetGlobalJSObject());
+            JSAutoCompartment ac(cx, tabChildGlobal);
+            if (!JS_WrapObject(cx, &obj))
+                return nullptr;
+            return obj;
+        }
+    }
+
+    // If there's no TabChildGlobal, we use the junk scope.
+    JSAutoCompartment ac(cx, xpc::GetJunkScope());
+    if (!JS_WrapObject(cx, &obj))
+        return nullptr;
+    return obj;
 }
 
 static const uint64_t DefaultPropertyOp = 1;
 static const uint64_t GetterOnlyPropertyStub = 2;
 static const uint64_t UnknownPropertyOp = 3;
 
 bool
 JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> desc,
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -111,30 +111,23 @@ class JavaScriptShared
     bool convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId id);
 
     virtual bool toObjectVariant(JSContext *cx, JSObject *obj, ObjectVariant *objVarp) = 0;
     virtual JSObject *fromObjectVariant(JSContext *cx, ObjectVariant objVar) = 0;
 
     static void ConvertID(const nsID &from, JSIID *to);
     static void ConvertID(const JSIID &from, nsID *to);
 
-    void ReportNonexistentObject(JSContext *cx);
-
     JSObject *findCPOWById(uint32_t objId) {
         return cpows_.find(objId);
     }
     JSObject *findObjectById(uint32_t objId) {
         return objects_.find(objId);
     }
-    JSObject *findObjectById(JSContext *cx, uint32_t objId) {
-        if (JSObject *result = objects_.find(objId))
-            return result;
-        ReportNonexistentObject(cx);
-        return nullptr;
-    }
+    JSObject *findObjectById(JSContext *cx, uint32_t objId);
 
   protected:
     JSRuntime *rt_;
     uintptr_t refcount_;
 
     IdToObjectMap objects_;
     IdToObjectMap cpows_;
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -626,31 +626,42 @@ WrapperOwner::ok(JSContext *cx, const Re
     if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
         return false;
 
     JS_SetPendingException(cx, exn);
     return false;
 }
 
 bool
-WrapperOwner::toObjectVariant(JSContext *cx, JSObject *obj, ObjectVariant *objVarp)
+WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *objVarp)
 {
+    RootedObject obj(cx, objArg);
     JS_ASSERT(obj);
-    JSObject *unwrapped = js::CheckedUnwrap(obj, false);
-    if (unwrapped && IsCPOW(unwrapped) && OwnerOf(unwrapped) == this) {
-        *objVarp = LocalObject(idOf(unwrapped));
+
+    // We always save objects unwrapped in the CPOW table. If we stored
+    // wrappers, then the wrapper might be GCed while the target remained alive.
+    // Whenever operating on an object that comes from the table, we wrap it
+    // in findObjectById.
+    obj = js::CheckedUnwrap(obj, false);
+    if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
+        *objVarp = LocalObject(idOf(obj));
         return true;
     }
 
     ObjectId id = objectIds_.find(obj);
     if (id) {
         *objVarp = RemoteObject(id);
         return true;
     }
 
+    // Need to call PreserveWrapper on |obj| in case it's a reflector.
+    // FIXME: What if it's an XPCWrappedNative?
+    if (mozilla::dom::IsDOMObject(obj))
+        mozilla::dom::TryPreserveWrapper(obj);
+
     id = ++lastId_;
     if (id > MAX_CPOW_IDS) {
         JS_ReportError(cx, "CPOW id limit reached");
         return false;
     }
 
     id <<= OBJECT_EXTRA_BITS;
     if (JS_ObjectIsCallable(cx, obj))