Bug 1459245 - Migrate <stringbundle> from a XBL binding to a Custom Element;r=mossop draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Wed, 25 Oct 2017 13:51:13 -0700
changeset 791613 5bf94038556c9b956de3678cd4bc817f5d837f45
parent 791511 d07a4da682a2f8a2df811d8f0734a5a632213c9b
push id108850
push userbgrinstead@mozilla.com
push dateFri, 04 May 2018 18:04:41 +0000
reviewersmossop
bugs1459245
milestone61.0a1
Bug 1459245 - Migrate <stringbundle> from a XBL binding to a Custom Element;r=mossop This loads a JS file to register the Custom Element whenever XUL documents get created. This is a pattern we plan to follow for future elements as well - stringbundle is just an easy target to prove it out with. MozReview-Commit-ID: 7PlisCknrKW
toolkit/components/processsingleton/MainProcessSingleton.js
toolkit/content/jar.mn
toolkit/content/widgets/stringbundle.js
toolkit/content/widgets/stringbundle.xml
toolkit/content/xul.css
--- a/toolkit/components/processsingleton/MainProcessSingleton.js
+++ b/toolkit/components/processsingleton/MainProcessSingleton.js
@@ -60,26 +60,44 @@ MainProcessSingleton.prototype = {
       Services.search.addEngine(engineURL.spec, null, iconURL ? iconURL.spec : null, true);
     });
   },
 
   observe(subject, topic, data) {
     switch (topic) {
     case "app-startup": {
       Services.obs.addObserver(this, "xpcom-shutdown");
+      Services.obs.addObserver(this, "document-element-inserted");
 
       // Load this script early so that console.* is initialized
       // before other frame scripts.
       Services.mm.loadFrameScript("chrome://global/content/browser-content.js", true);
       Services.ppmm.loadProcessScript("chrome://global/content/process-content.js", true);
       Services.mm.addMessageListener("Search:AddEngine", this.addSearchEngine);
       Services.ppmm.loadProcessScript("resource:///modules/ContentObservers.js", true);
       break;
     }
 
+    case "document-element-inserted":
+      // Set up Custom Elements for XUL windows before anything else happens
+      // in the document. Anything loaded here should be considered part of
+      // core XUL functionality. Any window-specific elements can be registered
+      // via <script> tags at the top of individual documents.
+      const doc = subject;
+      if (doc.nodePrincipal.isSystemPrincipal &&
+          doc.contentType == "application/vnd.mozilla.xul+xml") {
+        for (let script of [
+          "chrome://global/content/elements/stringbundle.js",
+        ]) {
+          Services.scriptloader.loadSubScript(script, doc.ownerGlobal);
+        }
+      }
+      break;
+
     case "xpcom-shutdown":
       Services.mm.removeMessageListener("Search:AddEngine", this.addSearchEngine);
+      Services.obs.removeObserver(this, "document-element-inserted");
       break;
     }
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MainProcessSingleton]);
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -85,24 +85,24 @@ toolkit.jar:
    content/global/bindings/popup.xml           (widgets/popup.xml)
    content/global/bindings/progressmeter.xml   (widgets/progressmeter.xml)
    content/global/bindings/radio.xml           (widgets/radio.xml)
    content/global/bindings/remote-browser.xml  (widgets/remote-browser.xml)
    content/global/bindings/richlistbox.xml     (widgets/richlistbox.xml)
    content/global/bindings/scale.xml           (widgets/scale.xml)
    content/global/bindings/scrollbox.xml       (widgets/scrollbox.xml)
    content/global/bindings/spinner.js          (widgets/spinner.js)
-   content/global/bindings/stringbundle.xml    (widgets/stringbundle.xml)
 *  content/global/bindings/tabbox.xml          (widgets/tabbox.xml)
    content/global/bindings/text.xml            (widgets/text.xml)
 *  content/global/bindings/textbox.xml         (widgets/textbox.xml)
    content/global/bindings/timekeeper.js       (widgets/timekeeper.js)
    content/global/bindings/timepicker.js       (widgets/timepicker.js)
    content/global/bindings/toolbar.xml         (widgets/toolbar.xml)
    content/global/bindings/toolbarbutton.xml   (widgets/toolbarbutton.xml)
 *  content/global/bindings/tree.xml            (widgets/tree.xml)
    content/global/bindings/videocontrols.xml   (widgets/videocontrols.xml)
 *  content/global/bindings/wizard.xml          (widgets/wizard.xml)
+   content/global/elements/stringbundle.js     (widgets/stringbundle.js)
 #ifdef XP_MACOSX
    content/global/macWindowMenu.js
 #endif
    content/global/gmp-sources/openh264.json    (gmp-sources/openh264.json)
    content/global/gmp-sources/widevinecdm.json (gmp-sources/widevinecdm.json)
rename from toolkit/content/widgets/stringbundle.xml
rename to toolkit/content/widgets/stringbundle.js
--- a/toolkit/content/widgets/stringbundle.xml
+++ b/toolkit/content/widgets/stringbundle.js
@@ -1,91 +1,63 @@
-<?xml version="1.0"?>
-<!-- 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/. -->
+/* 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";
 
-<bindings id="stringBundleBindings"
-          xmlns="http://www.mozilla.org/xbl"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+// This is loaded into all XUL windows. Wrap in a block to prevent
+// leaking to window scope.
+{
 
-  <binding id="stringbundle" extends="xul:spacer">
-    <implementation name="XStringBundle">
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-      <method name="getString">
-        <parameter name="aStringKey"/>
-        <body>
-          <![CDATA[
-            try {
-              return this.stringBundle.GetStringFromName(aStringKey);
-            } catch (e) {
-              dump("*** Failed to get string " + aStringKey + " in bundle: " + this.src + "\n");
-              throw e;
-            }
-          ]]>
-        </body>
-      </method>
+class MozStringbundle extends XULElement {
+  get stringBundle() {
+    if (!this._bundle) {
+      try {
+        this._bundle = Services.strings.createBundle(this.src);
+      } catch (e) {
+        dump("Failed to get stringbundle:\n");
+        dump(e + "\n");
+      }
+    }
+    return this._bundle;
+  }
 
-      <method name="getFormattedString">
-        <parameter name="aStringKey"/>
-        <parameter name="aStringsArray"/>
-        <body>
-          <![CDATA[
-            try {
-              return this.stringBundle.formatStringFromName(aStringKey, aStringsArray, aStringsArray.length);
-            } catch (e) {
-              dump("*** Failed to format string " + aStringKey + " in bundle: " + this.src + "\n");
-              throw e;
-            }
-          ]]>
-        </body>
-      </method>
+  set src(val) {
+    this._bundle = null;
+    this.setAttribute("src", val);
+    return val;
+  }
 
-      <property name="stringBundle" readonly="true">
-        <getter>
-          <![CDATA[
-            if (!this._bundle) {
-              try {
-                this._bundle = Cc["@mozilla.org/intl/stringbundle;1"]
-                                 .getService(Ci.nsIStringBundleService)
-                                 .createBundle(this.src);
-              } catch (e) {
-                dump("Failed to get stringbundle:\n");
-                dump(e + "\n");
-              }
-            }
-            return this._bundle;
-          ]]>
-        </getter>
-      </property>
+  get src() {
+    return this.getAttribute("src");
+  }
+
+  get strings() {
+    // Note: this is a sucky method name! Should be:
+    //       readonly attribute nsISimpleEnumerator strings;
+    return this.stringBundle.getSimpleEnumeration();
+  }
 
-      <property name="src">
-        <getter>
-          <![CDATA[
-            return this.getAttribute("src");
-          ]]>
-        </getter>
-        <setter>
-          <![CDATA[
-            this._bundle = null;
-            this.setAttribute("src", val);
-            return val;
-          ]]>
-        </setter>
-      </property>
+  getString(aStringKey) {
+    try {
+      return this.stringBundle.GetStringFromName(aStringKey);
+    } catch (e) {
+      dump("*** Failed to get string " + aStringKey + " in bundle: " + this.src + "\n");
+      throw e;
+    }
+  }
 
-      <property name="strings">
-        <getter>
-          <![CDATA[
-            // Note: this is a sucky method name! Should be:
-            //       readonly attribute nsISimpleEnumerator strings;
-            return this.stringBundle.getSimpleEnumeration();
-          ]]>
-        </getter>
-      </property>
+  getFormattedString(aStringKey, aStringsArray) {
+    try {
+      return this.stringBundle.formatStringFromName(aStringKey, aStringsArray, aStringsArray.length);
+    } catch (e) {
+      dump("*** Failed to format string " + aStringKey + " in bundle: " + this.src + "\n");
+      throw e;
+    }
+  }
+}
 
-      <field name="_bundle">null</field>
+customElements.define("stringbundle", MozStringbundle);
 
-    </implementation>
-  </binding>
-
-</bindings>
+}
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -883,23 +883,19 @@ arrowscrollbox[clicktoscroll="true"] {
 }
 
 autorepeatbutton {
   -moz-binding: url("chrome://global/content/bindings/scrollbox.xml#autorepeatbutton");
 }
 
 /********** stringbundle **********/
 
+stringbundle,
 stringbundleset {
-  visibility: collapse;
-}
-
-stringbundle {
-  -moz-binding: url("chrome://global/content/bindings/stringbundle.xml#stringbundle");
-  visibility: collapse;
+  display: none;
 }
 
 /********** dialog **********/
 
 dialog,
 dialog:root /* override :root from above */ {
   -moz-binding: url("chrome://global/content/bindings/dialog.xml#dialog");
   -moz-box-orient: vertical;