Bug 1461742, shared method to indicate that a custom element implements one or more interfaces, r=bgrins
☠☠ backed out by 17dd12ac3d4e ☠ ☠
authorNeil Deakin <neil@mozilla.com>
Thu, 19 Jul 2018 06:15:29 -0400
changeset 427266 ca5415dbc51a
parent 427265 30bd77de7bf5
child 427267 7eb107c36789
push id105427
push userneil@mozilla.com
push dateThu, 19 Jul 2018 10:16:55 +0000
treeherdermozilla-inbound@7eb107c36789 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1461742
milestone63.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 1461742, shared method to indicate that a custom element implements one or more interfaces, r=bgrins
toolkit/content/customElements.js
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -1,12 +1,14 @@
 /* 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/. */
 
+/* globals MozQueryInterface */
+
 "use strict";
 
 // This is loaded into all XUL windows. Wrap in a block to prevent
 // leaking to window scope.
 {
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
@@ -54,16 +56,64 @@ class MozXULElement extends XULElement {
       currentNode = nodeIterator.nextNode();
     }
     // We use a range here so that we don't access the inner DOM elements from
     // JavaScript before they are imported and inserted into a document.
     let range = doc.createRange();
     range.selectNodeContents(doc.querySelector("box"));
     return range.extractContents();
   }
+
+  /**
+   * Indicate that a class defining an element implements one or more
+   * XPCOM interfaces. The custom element getCustomInterface is added
+   * as well as an implementation of QueryInterface.
+   *
+   * The supplied class should implement the properties and methods of
+   * all of the interfaces that are specified.
+   *
+   * @param cls
+   *        The class that implements the interface.
+   * @param names
+   *        Array of interface names
+   */
+  static implementCustomInterface(cls, ifaces) {
+    cls.prototype.QueryInterface = ChromeUtils.generateQI(ifaces);
+    cls.prototype.getCustomInterfaceCallback = function getCustomInterfaceCallback(iface) {
+      if (ifaces.includes(Ci[Components.interfacesByID[iface.number]])) {
+        return getInterfaceProxy(this);
+      }
+      return null;
+    };
+  }
+}
+
+/**
+ * Given an object, add a proxy that reflects interface implementations
+ * onto the object itself.
+ */
+function getInterfaceProxy(obj) {
+  if (!obj._customInterfaceProxy) {
+    obj._customInterfaceProxy = new Proxy(obj, {
+      get(target, prop, receiver) {
+        let propOrMethod = target[prop];
+        if (typeof propOrMethod == "function") {
+          if (propOrMethod instanceof MozQueryInterface) {
+            return Reflect.get(target, prop, receiver);
+          }
+          return function(...args) {
+            return propOrMethod.apply(target, args);
+          };
+        }
+        return propOrMethod;
+      }
+    });
+  }
+
+  return obj._customInterfaceProxy;
 }
 
 // Attach the base class to the window so other scripts can use it:
 window.MozXULElement = MozXULElement;
 
 for (let script of [
   "chrome://global/content/elements/stringbundle.js",
   "chrome://global/content/elements/general.js",