Bug 1044736 - Part 3. Make BrowserElementParent implement nsIBrowserElementAPI and use it. r=bz,fabrice
authorKan-Ru Chen (陳侃如) <kanru@kanru.info>
Wed, 12 Nov 2014 19:27:31 +0800
changeset 215260 8ecfc1f41ddc53bd3b480c819e0089403829782f
parent 215259 c7c9170ab299698df5fa4ea53e98d39795efeaac
child 215261 e4f1f09f150fc204b942337688a7fdb2cf2559bf
push id27812
push userryanvm@gmail.com
push dateWed, 12 Nov 2014 20:49:51 +0000
treeherdermozilla-central@66cdb18f36da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, fabrice
bugs1044736
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 1044736 - Part 3. Make BrowserElementParent implement nsIBrowserElementAPI and use it. r=bz,fabrice
dom/browser-element/BrowserElementParent.js
dom/browser-element/BrowserElementParent.jsm
dom/browser-element/BrowserElementParent.manifest
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -3,127 +3,15 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {utils: Cu, interfaces: Ci} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
-const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
-
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserElementParentBuilder",
-                                  "resource://gre/modules/BrowserElementParent.jsm",
-                                  "BrowserElementParentBuilder");
-
-function debug(msg) {
-  //dump("BrowserElementParent.js - " + msg + "\n");
-}
+Cu.import("resource://gre/modules/BrowserElementParent.jsm");
 
 /**
  * BrowserElementParent implements one half of <iframe mozbrowser>.  (The other
  * half is, unsurprisingly, BrowserElementChild.)
- *
- * BrowserElementParentFactory detects when we create a windows or docshell
- * contained inside a <iframe mozbrowser> and creates a BrowserElementParent
- * object for that window.
- *
- * It creates a BrowserElementParent that injects script to listen for
- * certain event.
  */
-
-function BrowserElementParentFactory() {
-  this._initialized = false;
-}
-
-BrowserElementParentFactory.prototype = {
-  classID: Components.ID("{ddeafdac-cb39-47c4-9cb8-c9027ee36d26}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
-
-  /**
-   * Called on app startup, and also when the browser frames enabled pref is
-   * changed.
-   */
-  _init: function() {
-    if (this._initialized) {
-      return;
-    }
-
-    // If the pref is disabled, do nothing except wait for the pref to change.
-    // (This is important for tests, if nothing else.)
-    if (!this._browserFramesPrefEnabled()) {
-      Services.prefs.addObserver(BROWSER_FRAMES_ENABLED_PREF, this, /* ownsWeak = */ true);
-      return;
-    }
-
-    debug("_init");
-    this._initialized = true;
-
-    // Maps frame elements to BrowserElementParent objects.  We never look up
-    // anything in this map; the purpose is to keep the BrowserElementParent
-    // alive for as long as its frame element lives.
-    this._bepMap = new WeakMap();
-
-    Services.obs.addObserver(this, 'remote-browser-pending', /* ownsWeak = */ true);
-    Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true);
-  },
-
-  _browserFramesPrefEnabled: function() {
-    try {
-      return Services.prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
-    }
-    catch(e) {
-      return false;
-    }
-  },
-
-  _observeInProcessBrowserFrameShown: function(frameLoader) {
-    // Ignore notifications that aren't from a BrowserOrApp
-    if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
-      return;
-    }
-    debug("In-process browser frame shown " + frameLoader);
-    this._createBrowserElementParent(frameLoader,
-                                     /* hasRemoteFrame = */ false,
-                                     /* pending frame */ false);
-  },
-
-  _observeRemoteBrowserFramePending: function(frameLoader) {
-    // Ignore notifications that aren't from a BrowserOrApp
-    if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
-      return;
-    }
-    debug("Remote browser frame shown " + frameLoader);
-    this._createBrowserElementParent(frameLoader,
-                                     /* hasRemoteFrame = */ true,
-                                     /* pending frame */ true);
-  },
-
-  _createBrowserElementParent: function(frameLoader, hasRemoteFrame, isPendingFrame) {
-    let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
-    this._bepMap.set(frameElement, BrowserElementParentBuilder.create(
-      frameLoader, hasRemoteFrame, isPendingFrame));
-  },
-
-  observe: function(subject, topic, data) {
-    switch(topic) {
-    case 'app-startup':
-      this._init();
-      break;
-    case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
-      if (data == BROWSER_FRAMES_ENABLED_PREF) {
-        this._init();
-      }
-      break;
-    case 'remote-browser-pending':
-      this._observeRemoteBrowserFramePending(subject);
-      break;
-    case 'inprocess-browser-shown':
-      this._observeInProcessBrowserFrameShown(subject);
-      break;
-    }
-  },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParent]);
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -9,31 +9,29 @@ let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cr = Components.results;
 
 /* BrowserElementParent injects script to listen for certain events in the
  * child.  We then listen to messages from the child script and take
  * appropriate action here in the parent.
  */
 
-this.EXPORTED_SYMBOLS = ["BrowserElementParentBuilder"];
+this.EXPORTED_SYMBOLS = ["BrowserElementParent"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function () {
   Cu.import("resource://gre/modules/Webapps.jsm");
   return DOMApplicationRegistry;
 });
 
-const TOUCH_EVENTS_ENABLED_PREF = "dom.w3c_touch_events.enabled";
-
 function debug(msg) {
-  //dump("BrowserElementParent.jsm - " + msg + "\n");
+  //dump("BrowserElementParent - " + msg + "\n");
 }
 
 function getIntPref(prefName, def) {
   try {
     return Services.prefs.getIntPref(prefName);
   }
   catch(err) {
     return def;
@@ -54,148 +52,93 @@ function visibilityChangeHandler(e) {
     return;
   }
 
   for (let i = 0; i < beps.length; i++) {
     beps[i]._ownerVisibilityChange();
   }
 }
 
-this.BrowserElementParentBuilder = {
-  create: function create(frameLoader, hasRemoteFrame, isPendingFrame) {
-    return new BrowserElementParent(frameLoader, hasRemoteFrame);
-  }
+function defineNoReturnMethod(fn) {
+  return function method() {
+    if (!this._domRequestReady) {
+      // Remote browser haven't been created, we just queue the API call.
+      let args = Array.slice(arguments);
+      args.unshift(this);
+      this._pendingAPICalls.push(method.bind.apply(fn, args));
+      return;
+    }
+    if (this._isAlive()) {
+      fn.apply(this, arguments);
+    }
+  };
 }
 
-function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
-  debug("Creating new BrowserElementParent object for " + frameLoader);
+function defineDOMRequestMethod(msgName) {
+  return function() {
+    return this._sendDOMRequest(msgName);
+  };
+}
+
+function BrowserElementParent() {
+  debug("Creating new BrowserElementParent object");
   this._domRequestCounter = 0;
   this._domRequestReady = false;
   this._pendingAPICalls = [];
   this._pendingDOMRequests = {};
   this._pendingSetInputMethodActive = [];
-  this._hasRemoteFrame = hasRemoteFrame;
   this._nextPaintListeners = [];
 
-  this._frameLoader = frameLoader;
-  this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
-  let self = this;
-  if (!this._frameElement) {
-    debug("No frame element?");
-    return;
-  }
-
   Services.obs.addObserver(this, 'ask-children-to-exit-fullscreen', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
-
-  let defineMethod = function(name, fn) {
-    XPCNativeWrapper.unwrap(self._frameElement)[name] = Cu.exportFunction(function() {
-      if (self._isAlive()) {
-        return fn.apply(self, arguments);
-      }
-    }, self._frameElement);
-  }
-
-  let defineNoReturnMethod = function(name, fn) {
-    XPCNativeWrapper.unwrap(self._frameElement)[name] = Cu.exportFunction(function method() {
-      if (!self._domRequestReady) {
-        // Remote browser haven't been created, we just queue the API call.
-        let args = Array.slice(arguments);
-        args.unshift(self);
-        self._pendingAPICalls.push(method.bind.apply(fn, args));
-        return;
-      }
-      if (self._isAlive()) {
-        fn.apply(self, arguments);
-      }
-    }, self._frameElement);
-  };
-
-  let defineDOMRequestMethod = function(domName, msgName) {
-    XPCNativeWrapper.unwrap(self._frameElement)[domName] = Cu.exportFunction(function() {
-      return self._sendDOMRequest(msgName);
-    }, self._frameElement);
-  }
-
-  // Define methods on the frame element.
-  defineNoReturnMethod('setVisible', this._setVisible);
-  defineDOMRequestMethod('getVisible', 'get-visible');
-
-  // Not expose security sensitive browser API for widgets
-  if (!this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
-    defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
-
-    // 0 = disabled, 1 = enabled, 2 - auto detect
-    if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
-      defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
-    }
-    defineNoReturnMethod('goBack', this._goBack);
-    defineNoReturnMethod('goForward', this._goForward);
-    defineNoReturnMethod('reload', this._reload);
-    defineNoReturnMethod('stop', this._stop);
-    defineMethod('download', this._download);
-    defineDOMRequestMethod('purgeHistory', 'purge-history');
-    defineMethod('getScreenshot', this._getScreenshot);
-    defineNoReturnMethod('zoom', this._zoom);
-
-    defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
-    defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
-    defineDOMRequestMethod('getContentDimensions', 'get-contentdimensions');
-  }
-
-  defineMethod('addNextPaintListener', this._addNextPaintListener);
-  defineMethod('removeNextPaintListener', this._removeNextPaintListener);
-  defineNoReturnMethod('setActive', this._setActive);
-  defineMethod('getActive', 'this._getActive');
-
-  let principal = this._frameElement.ownerDocument.nodePrincipal;
-  let perm = Services.perms
-             .testExactPermissionFromPrincipal(principal, "input-manage");
-  if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
-    defineMethod('setInputMethodActive', this._setInputMethodActive);
-  }
-
-  // Listen to visibilitychange on the iframe's owner window, and forward
-  // changes down to the child.  We want to do this while registering as few
-  // visibilitychange listeners on _window as possible, because such a listener
-  // may live longer than this BrowserElementParent object.
-  //
-  // To accomplish this, we register just one listener on the window, and have
-  // it reference a WeakMap whose keys are all the BrowserElementParent objects
-  // on the window.  Then when the listener fires, we iterate over the
-  // WeakMap's keys (which we can do, because we're chrome) to notify the
-  // BrowserElementParents.
-  if (!this._window._browserElementParents) {
-    this._window._browserElementParents = new WeakMap();
-    this._window.addEventListener('visibilitychange',
-                                  visibilityChangeHandler,
-                                  /* useCapture = */ false,
-                                  /* wantsUntrusted = */ false);
-  }
-
-  this._window._browserElementParents.set(this, null);
-
-  // Insert ourself into the prompt service.
-  BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
-  if (!isPendingFrame) {
-    this._setupMessageListener();
-    this._registerAppManifest();
-  } else {
-    // if we are a pending frame, we setup message manager after
-    // observing remote-browser-frame-shown
-    Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
-  }
 }
 
 BrowserElementParent.prototype = {
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+  classDescription: "BrowserElementAPI implementation",
+  classID: Components.ID("{9f171ac4-0939-4ef8-b360-3408aedc3060}"),
+  contractID: "@mozilla.org/dom/browser-element-api;1",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserElementAPI,
+                                         Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
+  setFrameLoader: function(frameLoader) {
+    this._frameLoader = frameLoader;
+    this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
+    if (!this._frameElement) {
+      debug("No frame element?");
+      return;
+    }
+    // Listen to visibilitychange on the iframe's owner window, and forward
+    // changes down to the child.  We want to do this while registering as few
+    // visibilitychange listeners on _window as possible, because such a listener
+    // may live longer than this BrowserElementParent object.
+    //
+    // To accomplish this, we register just one listener on the window, and have
+    // it reference a WeakMap whose keys are all the BrowserElementParent objects
+    // on the window.  Then when the listener fires, we iterate over the
+    // WeakMap's keys (which we can do, because we're chrome) to notify the
+    // BrowserElementParents.
+    if (!this._window._browserElementParents) {
+      this._window._browserElementParents = new WeakMap();
+      this._window.addEventListener('visibilitychange',
+                                    visibilityChangeHandler,
+                                    /* useCapture = */ false,
+                                    /* wantsUntrusted = */ false);
+    }
+
+    this._window._browserElementParents.set(this, null);
+
+    // Insert ourself into the prompt service.
+    BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
+    this._setupMessageListener();
+    this._registerAppManifest();
+  },
+
   _runPendingAPICall: function() {
     if (!this._pendingAPICalls) {
       return;
     }
     for (let i = 0; i < this._pendingAPICalls.length; i++) {
       try {
         this._pendingAPICalls[i]();
       } catch (e) {
@@ -587,43 +530,50 @@ BrowserElementParent.prototype = {
     }
     else {
       debug("Got error in gotDOMRequestResult.");
       Services.DOMRequest.fireErrorAsync(req,
         Cu.cloneInto(data.json.errorMsg, this._window));
     }
   },
 
-  _setVisible: function(visible) {
+  setVisible: defineNoReturnMethod(function(visible) {
     this._sendAsyncMsg('set-visible', {visible: visible});
     this._frameLoader.visible = visible;
-  },
+  }),
 
-  _setActive: function(active) {
+  getVisible: defineDOMRequestMethod('get-visible'),
+
+  setActive: defineNoReturnMethod(function(active) {
     this._frameLoader.visible = active;
-  },
+  }),
 
-  _getActive: function() {
+  getActive: function() {
+    if (!this._isAlive()) {
+      throw Components.Exception("Dead content process",
+                                 Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
     return this._frameLoader.visible;
   },
 
-  _sendMouseEvent: function(type, x, y, button, clickCount, modifiers) {
+  sendMouseEvent: defineNoReturnMethod(function(type, x, y, button, clickCount, modifiers) {
     this._sendAsyncMsg("send-mouse-event", {
       "type": type,
       "x": x,
       "y": y,
       "button": button,
       "clickCount": clickCount,
       "modifiers": modifiers
     });
-  },
+  }),
 
-  _sendTouchEvent: function(type, identifiers, touchesX, touchesY,
-                            radiisX, radiisY, rotationAngles, forces,
-                            count, modifiers) {
+  sendTouchEvent: defineNoReturnMethod(function(type, identifiers, touchesX, touchesY,
+                                                radiisX, radiisY, rotationAngles, forces,
+                                                count, modifiers) {
 
     let tabParent = this._frameLoader.tabParent;
     if (tabParent && tabParent.useAsyncPanZoom) {
       tabParent.injectTouchEvent(type,
                                  identifiers,
                                  touchesX,
                                  touchesY,
                                  radiisX,
@@ -641,45 +591,55 @@ BrowserElementParent.prototype = {
         "radiisX": radiisX,
         "radiisY": radiisY,
         "rotationAngles": rotationAngles,
         "forces": forces,
         "count": count,
         "modifiers": modifiers
       });
     }
-  },
+  }),
 
-  _goBack: function() {
+  getCanGoBack: defineDOMRequestMethod('get-can-go-back'),
+  getCanGoForward: defineDOMRequestMethod('get-can-go-forward'),
+  getContentDimensions: defineDOMRequestMethod('get-contentdimensions'),
+
+  goBack: defineNoReturnMethod(function() {
     this._sendAsyncMsg('go-back');
-  },
+  }),
 
-  _goForward: function() {
+  goForward: defineNoReturnMethod(function() {
     this._sendAsyncMsg('go-forward');
-  },
+  }),
 
-  _reload: function(hardReload) {
+  reload: defineNoReturnMethod(function(hardReload) {
     this._sendAsyncMsg('reload', {hardReload: hardReload});
-  },
+  }),
 
-  _stop: function() {
+  stop: defineNoReturnMethod(function() {
     this._sendAsyncMsg('stop');
-  },
+  }),
 
   /*
    * The valid range of zoom scale is defined in preference "zoom.maxPercent" and "zoom.minPercent".
    */
-  _zoom: function(zoom) {
+  zoom: defineNoReturnMethod(function(zoom) {
     zoom *= 100;
     zoom = Math.min(getIntPref("zoom.maxPercent", 300), zoom);
     zoom = Math.max(getIntPref("zoom.minPercent", 50), zoom);
     this._sendAsyncMsg('zoom', {zoom: zoom / 100.0});
-  },
+  }),
+
+  purgeHistory: defineDOMRequestMethod('purge-history'),
+
 
-  _download: function(_url, _options) {
+  download: function(_url, _options) {
+    if (!this._isAlive()) {
+      return null;
+    }
     let ioService =
       Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
     let uri = ioService.newURI(_url, null, null);
     let url = uri.QueryInterface(Ci.nsIURL);
 
     // Ensure we have _options, we always use it to send the filename.
     _options = _options || {};
     if (!_options.filename) {
@@ -789,17 +749,22 @@ BrowserElementParent.prototype = {
     }
 
     // Set-up complete, let's get things started.
     channel.asyncOpen(new DownloadListener(), null);
 
     return req;
   },
 
-  _getScreenshot: function(_width, _height, _mimeType) {
+  getScreenshot: function(_width, _height, _mimeType) {
+    if (!this._isAlive()) {
+      throw Components.Exception("Dead content process",
+                                 Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
     let width = parseInt(_width);
     let height = parseInt(_height);
     let mimeType = (typeof _mimeType === 'string') ?
       _mimeType.trim().toLowerCase() : 'image/jpeg';
     if (isNaN(width) || isNaN(height) || width < 0 || height < 0) {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
@@ -809,42 +774,46 @@ BrowserElementParent.prototype = {
                                  mimeType: mimeType});
   },
 
   _recvNextPaint: function(data) {
     let listeners = this._nextPaintListeners;
     this._nextPaintListeners = [];
     for (let listener of listeners) {
       try {
-        listener();
+        listener.recvNextPaint();
       } catch (e) {
         // If a listener throws we'll continue.
       }
     }
   },
 
-  _addNextPaintListener: function(listener) {
-    if (typeof listener != 'function')
-      throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
+  addNextPaintListener: function(listener) {
+    if (!this._isAlive()) {
+      throw Components.Exception("Dead content process",
+                                 Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
 
     let self = this;
     let run = function() {
       if (self._nextPaintListeners.push(listener) == 1)
         self._sendAsyncMsg('activate-next-paint-listener');
     };
     if (!this._domRequestReady) {
       this._pendingAPICalls.push(run);
     } else {
       run();
     }
   },
 
-  _removeNextPaintListener: function(listener) {
-    if (typeof listener != 'function')
-      throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
+  removeNextPaintListener: function(listener) {
+    if (!this._isAlive()) {
+      throw Components.Exception("Dead content process",
+                                 Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
 
     let self = this;
     let run = function() {
       for (let i = self._nextPaintListeners.length - 1; i >= 0; i--) {
         if (self._nextPaintListeners[i] == listener) {
           self._nextPaintListeners.splice(i, 1);
           break;
         }
@@ -855,17 +824,22 @@ BrowserElementParent.prototype = {
     };
     if (!this._domRequestReady) {
       this._pendingAPICalls.push(run);
     } else {
       run();
     }
   },
 
-  _setInputMethodActive: function(isActive) {
+  setInputMethodActive: function(isActive) {
+    if (!this._isAlive()) {
+      throw Components.Exception("Dead content process",
+                                 Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
     if (typeof isActive !== 'boolean') {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
 
     return this._sendDOMRequest('set-input-method-active',
                                 {isActive: isActive});
   },
@@ -917,28 +891,20 @@ BrowserElementParent.prototype = {
     case 'oop-frameloader-crashed':
       if (this._isAlive() && subject == this._frameLoader) {
         this._fireFatalError();
       }
       break;
     case 'ask-children-to-exit-fullscreen':
       if (this._isAlive() &&
           this._frameElement.ownerDocument == subject &&
-          this._hasRemoteFrame) {
+          this._frameLoader.QueryInterface(Ci.nsIFrameLoader).tabParent) {
         this._sendAsyncMsg('exit-fullscreen');
       }
       break;
-    case 'remote-browser-frame-shown':
-      if (this._frameLoader == subject) {
-        if (!this._mm) {
-          this._setupMessageListener();
-          this._registerAppManifest();
-        }
-        Services.obs.removeObserver(this, 'remote-browser-frame-shown');
-      }
     case 'copypaste-docommand':
       if (this._isAlive() && this._frameElement.isEqualNode(subject.wrappedJSObject)) {
         this._sendAsyncMsg('do-command', { command: data });
       }
       break;
     default:
       debug('Unknown topic: ' + topic);
       break;
--- a/dom/browser-element/BrowserElementParent.manifest
+++ b/dom/browser-element/BrowserElementParent.manifest
@@ -1,3 +1,2 @@
-component {ddeafdac-cb39-47c4-9cb8-c9027ee36d26} BrowserElementParent.js
-contract @mozilla.org/browser-element-parent-factory;1 {ddeafdac-cb39-47c4-9cb8-c9027ee36d26}
-category app-startup BrowserElementParentFactory service,@mozilla.org/browser-element-parent-factory;1
+component {9f171ac4-0939-4ef8-b360-3408aedc3060} BrowserElementParent.js
+contract @mozilla.org/dom/browser-element-api;1 {9f171ac4-0939-4ef8-b360-3408aedc3060}