Bug 1031609 - Docshell rootTreeItem shim (r=mconley)
authorBill McCloskey <wmccloskey@mozilla.com>
Mon, 14 Jul 2014 22:10:06 -0700
changeset 215899 1e14af130411bc004e94f2208ba129e38c33946f
parent 215898 8723969ebaec6f57c1ff0d74effbcb40c0b2111d
child 215900 5bbb653cac5750830939cbd0d4fecab012d19f97
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1031609
milestone33.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 1031609 - Docshell rootTreeItem shim (r=mconley)
toolkit/components/addoncompat/RemoteAddonsChild.jsm
toolkit/components/addoncompat/RemoteAddonsParent.jsm
--- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm
@@ -202,12 +202,14 @@ EventTargetChild.prototype = {
     this._childGlobal.sendRpcMessage("Addons:Event:Run",
                                      {type: event.type, isTrusted: event.isTrusted},
                                      {event: event});
   }
 };
 
 let RemoteAddonsChild = {
   init: function(global) {
+    global.sendAsyncMessage("Addons:RegisterGlobal", {}, {global: global});
+
     // Return this so it gets rooted in the content script.
     return [new EventTargetChild(global)];
   },
 };
--- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm
@@ -399,18 +399,53 @@ EventTargetInterposition.methods.addEven
   };
 
 EventTargetInterposition.methods.removeEventListener =
   function(addon, target, type, listener, useCapture) {
     EventTargetParent.removeEventListener(target, type, listener, useCapture);
     target.removeEventListener(type, listener, useCapture);
   };
 
+// This interposition intercepts accesses to |rootTreeItem| on a child
+// process docshell. In the child, each docshell is its own
+// root. However, add-ons expect the root to be the chrome docshell,
+// so we make that happen here.
+let ContentDocShellTreeItemInterposition = new Interposition();
+
+ContentDocShellTreeItemInterposition.getters.rootTreeItem =
+  function(addon, target) {
+    // The chrome global in the child.
+    let chromeGlobal = target.rootTreeItem
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIContentFrameMessageManager);
+
+    // Map it to a <browser> element and window.
+    let browser = RemoteAddonsParent.globalToBrowser.get(chromeGlobal);
+    if (!browser) {
+      // Somehow we have a CPOW from the child, but it hasn't sent us
+      // its global yet. That shouldn't happen, but return null just
+      // in case.
+      return null;
+    }
+
+    let chromeWin = browser.ownerDocument.defaultView;
+
+    // Return that window's docshell.
+    return chromeWin.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIWebNavigation)
+      .QueryInterface(Ci.nsIDocShellTreeItem);
+  };
+
 let RemoteAddonsParent = {
   init: function() {
+    let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+    mm.addMessageListener("Addons:RegisterGlobal", this);
+
+    this.globalToBrowser = new WeakMap();
+    this.browserToGlobal = new WeakMap();
   },
 
   getInterfaceInterpositions: function() {
     let result = {};
 
     function register(intf, interp) {
       result[intf.number] = interp;
     }
@@ -424,12 +459,22 @@ let RemoteAddonsParent = {
   getTaggedInterpositions: function() {
     let result = {};
 
     function register(tag, interp) {
       result[tag] = interp;
     }
 
     register("EventTarget", EventTargetInterposition);
+    register("ContentDocShellTreeItem", ContentDocShellTreeItemInterposition);
 
     return result;
   },
+
+  receiveMessage: function(msg) {
+    switch (msg.name) {
+    case "Addons:RegisterGlobal":
+      this.browserToGlobal.set(msg.target, msg.objects.global);
+      this.globalToBrowser.set(msg.objects.global, msg.target);
+      break;
+    }
+  }
 };