Bug 1091970 - [e10s] Cache object tag in add-on shims for remote objects (r=mrbkap)
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 04 Nov 2014 17:40:08 -0800
changeset 214224 c97553c13fb65447505f83ccd950190cbe830c63
parent 214223 054f17d341c1cc190480ef74438a3863f4d82d57
child 214225 9fa06a2e1a98776febaf8df8ef50e1fb6694c702
push id51437
push userwmccloskey@mozilla.com
push dateThu, 06 Nov 2014 00:10:46 +0000
treeherdermozilla-inbound@9fa06a2e1a98 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs1091970
milestone36.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 1091970 - [e10s] Cache object tag in add-on shims for remote objects (r=mrbkap)
browser/installer/package-manifest.in
js/ipc/JavaScriptTypes.ipdlh
js/ipc/WrapperOwner.cpp
js/ipc/WrapperOwner.h
js/xpconnect/idl/moz.build
js/xpconnect/idl/nsIRemoteTagService.idl
js/xpconnect/idl/xpccomponents.idl
js/xpconnect/src/XPCComponents.cpp
toolkit/components/addoncompat/addoncompat.manifest
toolkit/components/addoncompat/moz.build
toolkit/components/addoncompat/multiprocessShims.js
toolkit/components/addoncompat/remoteTagService.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -436,16 +436,17 @@
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/addoncompat.manifest
 @BINPATH@/components/multiprocessShims.js
+@BINPATH@/components/remoteTagService.js
 @BINPATH@/components/pluginGlue.manifest
 @BINPATH@/components/ProcessSingleton.manifest
 @BINPATH@/components/MainProcessSingleton.js
 @BINPATH@/components/ContentProcessSingleton.js
 @BINPATH@/browser/components/nsSessionStore.manifest
 @BINPATH@/browser/components/nsSessionStartup.js
 @BINPATH@/browser/components/nsSessionStore.js
 @BINPATH@/components/nsURLFormatter.manifest
--- a/js/ipc/JavaScriptTypes.ipdlh
+++ b/js/ipc/JavaScriptTypes.ipdlh
@@ -32,16 +32,17 @@ struct LocalObject
     uint64_t serializedId;
 };
 
 struct RemoteObject
 {
     uint64_t serializedId;
     bool isCallable;
     bool isConstructor;
+    nsCString objectTag;
 };
 
 union ObjectVariant
 {
     LocalObject;
     RemoteObject;
 };
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -8,31 +8,38 @@
 #include "WrapperOwner.h"
 #include "JavaScriptLogging.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"
 #include "WrapperFactory.h"
 
+#include "nsIRemoteTagService.h"
+
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 struct AuxCPOWData
 {
     ObjectId id;
     bool isCallable;
     bool isConstructor;
 
-    AuxCPOWData(ObjectId id, bool isCallable, bool isConstructor)
+    // The object tag is just some auxilliary information that clients can use
+    // however they see fit.
+    nsCString objectTag;
+
+    AuxCPOWData(ObjectId id, bool isCallable, bool isConstructor, const nsACString &objectTag)
       : id(id),
         isCallable(isCallable),
-        isConstructor(isConstructor)
+        isConstructor(isConstructor),
+        objectTag(objectTag)
     {}
 };
 
 WrapperOwner::WrapperOwner(JSRuntime *rt)
   : JavaScriptShared(rt),
     inactive_(false)
 {
 }
@@ -798,16 +805,27 @@ bool
 IsWrappedCPOW(JSObject *obj)
 {
     JSObject *unwrapped = js::UncheckedUnwrap(obj, true);
     if (!unwrapped)
         return false;
     return IsCPOW(unwrapped);
 }
 
+void
+GetWrappedCPOWTag(JSObject *obj, nsACString &out)
+{
+    JSObject *unwrapped = js::UncheckedUnwrap(obj, true);
+    MOZ_ASSERT(IsCPOW(unwrapped));
+
+    AuxCPOWData *aux = AuxCPOWDataOf(unwrapped);
+    if (aux)
+        out = aux->objectTag;
+}
+
 nsresult
 InstanceOf(JSObject *proxy, const nsID *id, bool *bp)
 {
     WrapperOwner *parent = OwnerOf(proxy);
     if (!parent->active())
         return NS_ERROR_UNEXPECTED;
     return parent->instanceOf(proxy, id, bp);
 }
@@ -879,19 +897,31 @@ WrapperOwner::ok(JSContext *cx, const Re
     if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
         return false;
 
     JS_SetPendingException(cx, exn);
     return false;
 }
 
 static RemoteObject
-MakeRemoteObject(ObjectId id, JSObject *obj)
+MakeRemoteObject(JSContext *cx, ObjectId id, HandleObject obj)
 {
-    return RemoteObject(id.serialize(), JS::IsCallable(obj), JS::IsConstructor(obj));
+    nsCString objectTag;
+
+    nsCOMPtr<nsIRemoteTagService> service =
+        do_GetService("@mozilla.org/addons/remote-tag-service;1");
+    if (service) {
+        RootedValue objVal(cx, ObjectValue(*obj));
+        service->GetRemoteObjectTag(objVal, objectTag);
+    }
+
+    return RemoteObject(id.serialize(),
+                        JS::IsCallable(obj),
+                        JS::IsConstructor(obj),
+                        objectTag);
 }
 
 bool
 WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *objVarp)
 {
     RootedObject obj(cx, objArg);
     MOZ_ASSERT(obj);
 
@@ -905,32 +935,32 @@ WrapperOwner::toObjectVariant(JSContext 
         *objVarp = LocalObject(idOf(obj).serialize());
         return true;
     }
     bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
 
     ObjectId id = objectIdMap(waiveXray).find(obj);
     if (!id.isNull()) {
         MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
-        *objVarp = MakeRemoteObject(id, obj);
+        *objVarp = MakeRemoteObject(cx, id, obj);
         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 = ObjectId(nextSerialNumber_++, waiveXray);
     if (!objects_.add(id, obj))
         return false;
     if (!objectIdMap(waiveXray).add(cx, obj, id))
         return false;
 
-    *objVarp = MakeRemoteObject(id, obj);
+    *objVarp = MakeRemoteObject(cx, id, obj);
     return true;
 }
 
 JSObject *
 WrapperOwner::fromObjectVariant(JSContext *cx, ObjectVariant objVar)
 {
     if (objVar.type() == ObjectVariant::TRemoteObject) {
         return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
@@ -959,17 +989,20 @@ WrapperOwner::fromRemoteObjectVariant(JS
             return nullptr;
 
         if (!cpows_.add(objId, obj))
             return nullptr;
 
         // Incref once we know the decref will be called.
         incref();
 
-        AuxCPOWData *aux = new AuxCPOWData(objId, objVar.isCallable(), objVar.isConstructor());
+        AuxCPOWData *aux = new AuxCPOWData(objId,
+                                           objVar.isCallable(),
+                                           objVar.isConstructor(),
+                                           objVar.objectTag());
 
         SetProxyExtra(obj, 0, PrivateValue(this));
         SetProxyExtra(obj, 1, PrivateValue(aux));
     }
 
     if (!JS_WrapObject(cx, &obj))
         return nullptr;
     return obj;
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -158,12 +158,15 @@ bool
 IsWrappedCPOW(JSObject *obj);
 
 nsresult
 InstanceOf(JSObject *obj, const nsID *id, bool *bp);
 
 bool
 DOMInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp);
 
+void
+GetWrappedCPOWTag(JSObject *obj, nsACString &out);
+
 } // jsipc
 } // mozilla
 
 #endif // mozilla_jsipc_WrapperOwner_h__
--- a/js/xpconnect/idl/moz.build
+++ b/js/xpconnect/idl/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'mozIJSSubScriptLoader.idl',
     'nsIAddonInterposition.idl',
     'nsIJSRuntimeService.idl',
+    'nsIRemoteTagService.idl',
     'nsIScriptError.idl',
     'nsIXPConnect.idl',
     'nsIXPCScriptable.idl',
     'xpccomponents.idl',
     'xpcexception.idl',
     'xpcIJSGetFactory.idl',
     'xpcIJSModuleLoader.idl',
     'xpcIJSWeakReference.idl',
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/idl/nsIRemoteTagService.idl
@@ -0,0 +1,13 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsISupports.idl"
+
+[scriptable,uuid(59dbe3d0-6084-11e4-9803-0800200c9a66)]
+interface nsIRemoteTagService : nsISupports
+{
+    ACString getRemoteObjectTag(in jsval target);
+};
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -117,17 +117,17 @@ interface nsIXPCComponents_utils_Sandbox
 interface ScheduledGCCallback : nsISupports
 {
     void callback();
 };
 
 /**
 * interface of Components.utils
 */
-[scriptable, uuid(ad41689c-ed0e-4a92-8f19-a53c2dba5711)]
+[scriptable, uuid(2617a800-63c1-11e4-9803-0800200c9a66)]
 interface nsIXPCComponents_Utils : nsISupports
 {
 
     /* reportError is designed to be called from JavaScript only.
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
@@ -430,16 +430,25 @@ interface nsIXPCComponents_Utils : nsISu
      */
     bool isDeadWrapper(in jsval obj);
 
     /**
      * Determines whether this object is a cross-process wrapper.
      */
     bool isCrossProcessWrapper(in jsval obj);
 
+    /**
+     * CPOWs can have user data attached to them. This data originates
+     * in the local process via the
+     * nsIRemoteTagService.getRemoteObjectTag method. It's sent along
+     * with the CPOW to the remote process, where it can be fetched
+     * with this function, getCrossProcessWrapperTag.
+     */
+    ACString getCrossProcessWrapperTag(in jsval obj);
+
     /*
      * To be called from JS only. This is for Gecko internal use only, and may
      * disappear at any moment.
      *
      * Forces a recomputation of all wrappers in and out of the compartment
      * containing |vobj|. If |vobj| is not an object, all wrappers system-wide
      * are recomputed.
      */
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3101,16 +3101,26 @@ nsXPCComponents_Utils::IsCrossProcessWra
     *out = false;
     if (obj.isPrimitive())
         return NS_ERROR_INVALID_ARG;
 
     *out = jsipc::IsWrappedCPOW(&obj.toObject());
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetCrossProcessWrapperTag(HandleValue obj, nsACString &out)
+{
+    if (obj.isPrimitive() || !jsipc::IsWrappedCPOW(&obj.toObject()))
+        return NS_ERROR_INVALID_ARG;
+
+    jsipc::GetWrappedCPOWTag(&obj.toObject(), out);
+    return NS_OK;
+}
+
 /* void recomputerWrappers(jsval vobj); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::RecomputeWrappers(HandleValue vobj, JSContext *cx)
 {
     // Determine the compartment of the given object, if any.
     JSCompartment *c = vobj.isObject()
                        ? js::GetObjectCompartment(js::UncheckedUnwrap(&vobj.toObject()))
                        : nullptr;
--- a/toolkit/components/addoncompat/addoncompat.manifest
+++ b/toolkit/components/addoncompat/addoncompat.manifest
@@ -1,2 +1,4 @@
 component {1363d5f0-d95e-11e3-9c1a-0800200c9a66} multiprocessShims.js
 contract @mozilla.org/addons/multiprocess-shims;1 {1363d5f0-d95e-11e3-9c1a-0800200c9a66}
+component {dfd07380-6083-11e4-9803-0800200c9a66} remoteTagService.js
+contract @mozilla.org/addons/remote-tag-service;1 {dfd07380-6083-11e4-9803-0800200c9a66}
--- a/toolkit/components/addoncompat/moz.build
+++ b/toolkit/components/addoncompat/moz.build
@@ -4,14 +4,15 @@
 # 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/.
 
 TEST_DIRS += ['tests']
 
 EXTRA_COMPONENTS += [
     'addoncompat.manifest',
     'multiprocessShims.js',
+    'remoteTagService.js',
 ]
 
 EXTRA_JS_MODULES += [
     'RemoteAddonsChild.jsm',
     'RemoteAddonsParent.jsm',
 ]
--- a/toolkit/components/addoncompat/multiprocessShims.js
+++ b/toolkit/components/addoncompat/multiprocessShims.js
@@ -72,23 +72,17 @@ function AddonInterpositionService()
 AddonInterpositionService.prototype = {
   classID: Components.ID("{1363d5f0-d95e-11e3-9c1a-0800200c9a66}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]),
 
   // When the interface is not known for a method call, this code
   // determines the type of the target object.
   getObjectTag: function(target) {
     if (Cu.isCrossProcessWrapper(target)) {
-      if (target instanceof Ci.nsIDocShellTreeItem) {
-        return "ContentDocShellTreeItem";
-      }
-
-      if (target instanceof Ci.nsIDOMDocument) {
-        return "ContentDocument";
-      }
+      return Cu.getCrossProcessWrapperTag(target);
     }
 
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (target instanceof Ci.nsIDOMXULElement) {
       if (target.localName == "browser" && target.isRemoteBrowser) {
         return "RemoteBrowserElement";
       }
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/remoteTagService.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+"use strict";
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function RemoteTagServiceService()
+{
+}
+
+RemoteTagServiceService.prototype = {
+  classID: Components.ID("{dfd07380-6083-11e4-9803-0800200c9a66}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRemoteTagService, Ci.nsISupportsWeakReference]),
+
+  /**
+   * CPOWs can have user data attached to them. This data originates
+   * in the local process from this function, getRemoteObjectTag. It's
+   * sent along with the CPOW to the remote process, where it can be
+   * fetched with Components.utils.getCrossProcessWrapperTag.
+   */
+  getRemoteObjectTag: function(target) {
+    if (target instanceof Ci.nsIDocShellTreeItem) {
+      return "ContentDocShellTreeItem";
+    }
+
+    if (target instanceof Ci.nsIDOMDocument) {
+      return "ContentDocument";
+    }
+
+    return "generic";
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RemoteTagServiceService]);