Bug 1455433 - migrate xul:checkbox to CE, r=bgrins
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 28 Feb 2019 20:32:50 +0000
changeset 519716 623c6ca6ed6cf8128cebf9944524f3be13e0d4d2
parent 519715 02c49a3e368dd98c05ff61978f80d98a20464f71
child 519717 a456a1594cb1960c465dc7a582134710f286fcf9
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1455433
milestone67.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 1455433 - migrate xul:checkbox to CE, r=bgrins Differential Revision: https://phabricator.services.mozilla.com/D21457
browser/components/preferences/in-content/tests/browser_extension_controlled.js
toolkit/components/prompts/test/chromeScript.js
toolkit/content/customElements.js
toolkit/content/jar.mn
toolkit/content/widgets/checkbox.js
toolkit/content/widgets/checkbox.xml
toolkit/content/xul.css
toolkit/themes/linux/global/in-content/common.css
toolkit/themes/shared/in-content/common.inc.css
toolkit/themes/windows/global/in-content/common.css
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -751,17 +751,17 @@ add_task(async function testExtensionCon
           },
         }, "The user is notified that an extension is controlling proxy settings.");
       }
       function getProxyControls() {
         let controlGroup = doc.getElementById("networkProxyType");
         let manualControlContainer = controlGroup.querySelector("grid");
         return {
           manualControls: [
-            ...manualControlContainer.querySelectorAll("label:not([control=networkProxyNone])"),
+            ...manualControlContainer.querySelectorAll("label[data-l10n-id]:not([control=networkProxyNone])"),
             ...manualControlContainer.querySelectorAll("textbox:not(#networkProxyNone)"),
             ...manualControlContainer.querySelectorAll("checkbox"),
             ...doc.querySelectorAll("#networkProxySOCKSVersion > radio")],
           pacControls: [doc.getElementById("networkProxyAutoconfigURL")],
           otherControls: [
             doc.querySelector("label[control=networkProxyNone]"),
             doc.getElementById("networkProxyNone"),
             ...controlGroup.querySelectorAll(":scope > radio"),
--- a/toolkit/components/prompts/test/chromeScript.js
+++ b/toolkit/components/prompts/test/chromeScript.js
@@ -160,17 +160,17 @@ function dismissSelect(ui, action) {
   } else if (action.buttonClick == "cancel") {
       dialog.cancelDialog();
   }
 }
 
 function dismissPrompt(ui, action) {
   if (action.setCheckbox) {
     // Annoyingly, the prompt code is driven by oncommand.
-    ui.checkbox.setChecked(true);
+    ui.checkbox.checked = true;
     ui.checkbox.doCommand();
   }
 
   if ("textField" in action) {
     ui.loginTextbox.setAttribute("value", action.textField);
   }
 
   if ("passField" in action) {
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -495,16 +495,17 @@ customElements.setElementCreationCallbac
 });
 
 // For now, don't load any elements in the extension dummy document.
 // We will want to load <browser> when that's migrated (bug 1441935).
 const isDummyDocument = document.documentURI == "chrome://extensions/content/dummy.xul";
 if (!isDummyDocument) {
   for (let script of [
     "chrome://global/content/elements/general.js",
+    "chrome://global/content/elements/checkbox.js",
     "chrome://global/content/elements/menu.js",
     "chrome://global/content/elements/notificationbox.js",
     "chrome://global/content/elements/popupnotification.js",
     "chrome://global/content/elements/radio.js",
     "chrome://global/content/elements/richlistbox.js",
     "chrome://global/content/elements/autocomplete-richlistitem.js",
     "chrome://global/content/elements/textbox.js",
     "chrome://global/content/elements/tabbox.js",
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -59,17 +59,16 @@ toolkit.jar:
    content/global/treeUtils.js
 #ifndef MOZ_FENNEC
    content/global/viewZoomOverlay.js
 #endif
    content/global/widgets.css
    content/global/bindings/autocomplete.xml    (widgets/autocomplete.xml)
    content/global/bindings/button.xml          (widgets/button.xml)
    content/global/bindings/calendar.js         (widgets/calendar.js)
-   content/global/bindings/checkbox.xml        (widgets/checkbox.xml)
    content/global/bindings/datekeeper.js       (widgets/datekeeper.js)
    content/global/bindings/datepicker.js       (widgets/datepicker.js)
    content/global/bindings/datetimebox.css     (widgets/datetimebox.css)
 *  content/global/bindings/dialog.xml          (widgets/dialog.xml)
    content/global/bindings/general.xml         (widgets/general.xml)
    content/global/bindings/menu.xml            (widgets/menu.xml)
    content/global/bindings/popup.xml           (widgets/popup.xml)
    content/global/bindings/radio.xml           (widgets/radio.xml)
@@ -81,16 +80,17 @@ toolkit.jar:
    content/global/elements/text.js             (widgets/text.js)
 *  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/toolbarbutton.xml   (widgets/toolbarbutton.xml)
 *  content/global/bindings/wizard.xml          (widgets/wizard.xml)
    content/global/elements/autocomplete-richlistitem.js       (widgets/autocomplete-richlistitem.js)
    content/global/elements/browser-custom-element.js          (widgets/browser-custom-element.js)
+   content/global/elements/checkbox.js         (widgets/checkbox.js)
    content/global/elements/datetimebox.js      (widgets/datetimebox.js)
    content/global/elements/findbar.js          (widgets/findbar.js)
    content/global/elements/editor.js           (widgets/editor.js)
    content/global/elements/general.js          (widgets/general.js)
    content/global/elements/menu.js             (widgets/menu.js)
    content/global/elements/notificationbox.js  (widgets/notificationbox.js)
    content/global/elements/pluginProblem.js    (widgets/pluginProblem.js)
    content/global/elements/radio.js            (widgets/radio.js)
rename from toolkit/content/widgets/checkbox.xml
rename to toolkit/content/widgets/checkbox.js
--- a/toolkit/content/widgets/checkbox.xml
+++ b/toolkit/content/widgets/checkbox.js
@@ -1,62 +1,86 @@
-<?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";
 
+// This is loaded into all XUL windows. Wrap in a block to prevent
+// leaking to window scope.
+{
+class MozCheckbox extends MozElements.BaseText {
+  constructor() {
+    super();
 
-<bindings id="checkboxBindings"
-   xmlns="http://www.mozilla.org/xbl"
-   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-   xmlns:xbl="http://www.mozilla.org/xbl">
+    // While it would seem we could do this by handling oncommand, we need can't
+    // because any external oncommand handlers might get called before ours, and
+    // then they would see the incorrect value of checked.
+    this.addEventListener("click", (event) => {
+      if (event.button === 0 && !this.disabled) {
+        this.checked = !this.checked;
+      }
+    });
+    this.addEventListener("keypress", (event) => {
+      if (event.key == " ") {
+        this.checked = !this.checked;
+        // Prevent page from scrolling on the space key.
+        event.preventDefault();
+      }
+    });
+  }
 
-  <binding id="checkbox"
-    extends="chrome://global/content/bindings/general.xml#basetext">
-    <content>
-      <xul:image class="checkbox-check" xbl:inherits="checked,disabled"/>
-      <xul:hbox class="checkbox-label-box" flex="1">
-        <xul:image class="checkbox-icon" xbl:inherits="src"/>
-        <xul:label class="checkbox-label" xbl:inherits="xbl:text=label,accesskey,crop" flex="1"/>
-      </xul:hbox>
-    </content>
+  static get inheritedAttributes() {
+    return {
+      ".checkbox-label": "accesskey,text=label",
+      ".checkbox-icon": "src",
+    };
+  }
+
+  connectedCallback() {
+    if (this.delayConnectedCallback()) {
+      return;
+    }
 
-    <implementation>
-      <method name="setChecked">
-        <parameter name="aValue"/>
-        <body>
-        <![CDATA[
-          var change = (aValue != (this.getAttribute("checked") == "true"));
-          if (aValue)
-            this.setAttribute("checked", "true");
-          else
-            this.removeAttribute("checked");
-          if (change) {
-            var event = document.createEvent("Events");
-            event.initEvent("CheckboxStateChange", true, true);
-            this.dispatchEvent(event);
-          }
-          return aValue;
-        ]]>
-        </body>
-      </method>
+    if (!MozCheckbox.contentFragment) {
+      let content = `
+        <image class="checkbox-check"/>
+        <hbox class="checkbox-label-box" flex="1">
+          <image class="checkbox-icon"/>
+          <label class="checkbox-label" flex="1"/>
+        </hbox>
+      `;
+      MozCheckbox.contentFragment = MozXULElement.parseXULToFragment(content);
+    }
+
+    this.textContent = "";
+    let fragment =
+      this.ownerDocument.importNode(MozCheckbox.contentFragment, true);
+    this.appendChild(fragment);
+
+    this.initializeAttributeInheritance();
+  }
 
-      <!-- public implementation -->
-      <property name="checked"    onset="return this.setChecked(val);"
-                                  onget="return this.getAttribute('checked') == 'true';"/>
-    </implementation>
+  set checked(val) {
+    let change = (val != (this.getAttribute("checked") == "true"));
+    if (val) {
+      this.setAttribute("checked", "true");
+    } else {
+      this.removeAttribute("checked");
+    }
 
-    <handlers>
-      <!-- While it would seem we could do this by handling oncommand, we need can't
-           because any external oncommand handlers might get called before ours, and
-           then they would see the incorrect value of checked. -->
-      <handler event="click" button="0" action="if (!this.disabled) this.checked = !this.checked;"/>
-      <handler event="keypress" key=" ">
-        <![CDATA[
-          this.checked = !this.checked;
-          // Prevent page from scrolling on the space key.
-          event.preventDefault();
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
+    if (change) {
+      let event = document.createEvent("Events");
+      event.initEvent("CheckboxStateChange", true, true);
+      this.dispatchEvent(event);
+    }
+    return val;
+  }
 
-</bindings>
+  get checked() {
+    return this.getAttribute("checked") == "true";
+  }
+}
+
+MozCheckbox.contentFragment = null;
+
+customElements.define("checkbox", MozCheckbox);
+}
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -176,22 +176,16 @@ iframe {
 popupnotification {
   -moz-box-orient: vertical;
 }
 
 .popup-notification-menubutton:not([label]) {
   display: none;
 }
 
-/********** checkbox **********/
-
-checkbox {
-  -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
-}
-
 /********** radio **********/
 
 radiogroup {
   -moz-box-orient: vertical;
 }
 
 radio {
   -moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
--- a/toolkit/themes/linux/global/in-content/common.css
+++ b/toolkit/themes/linux/global/in-content/common.css
@@ -28,17 +28,17 @@ xul|menulist {
 }
 
 xul|*.menulist-dropmarker {
   display: -moz-box;
   margin-top: 6px;
   margin-bottom: 6px;
 }
 
-xul|*.checkbox-check[checked],
+xul|checkbox[checked] > xul|*.checkbox-check,
 xul|*.radio-check[selected] {
   background-color: -moz-field;
   fill: -moz-fieldText;
 }
 
 xul|*.radio-label-box {
   -moz-appearance: none;
 }
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -490,17 +490,17 @@ html|input[type="checkbox"] {
   background-repeat: no-repeat;
 }
 
 xul|checkbox:not([disabled="true"]):hover > xul|*.checkbox-check,
 html|input[type="checkbox"]:not(:disabled):hover {
   border-color: var(--in-content-border-focus);
 }
 
-xul|*.checkbox-check[checked] {
+xul|checkbox[checked] > xul|*.checkbox-check {
   list-style-image: url("chrome://global/skin/icons/check.svg");
   -moz-context-properties: fill;
   fill: #2292d0;
 }
 
 html|input[type="checkbox"]:checked {
   background-image: url("chrome://global/skin/icons/check.svg");
   -moz-context-properties: fill;
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -10,17 +10,17 @@ xul|*.menulist-dropmarker {
 }
 
 xul|checkbox,
 xul|radio {
   padding-inline-start: 0;
 }
 
 @media (-moz-windows-default-theme: 0) {
-  xul|*.checkbox-check[checked],
+  xul|checkbox[checked] > xul|*.checkbox-check,
   xul|*.radio-check[selected] {
     fill: -moz-fieldText;
     background-color: -moz-field;
   }
 }
 
 xul|menulist:-moz-focusring > xul|*.menulist-label-box {
   outline: none !important;