Bug 1461742, shared method to indicate that a custom element implements one or more interfaces, r=bgrins
authorNeil Deakin <neil@mozilla.com>
Thu, 19 Jul 2018 08:38:51 -0400
changeset 427272 19cb7b9936ee
parent 427271 68da215bdf87
child 427273 ca12371f08ac
child 427326 d94abfaa5992
push id105431
push userneil@mozilla.com
push dateThu, 19 Jul 2018 12:43:53 +0000
treeherdermozilla-inbound@19cb7b9936ee [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",