Bug 1531282 - MozTree event handlers should be attached in connectedCallback() instead of constructor. r=bgrins
authorMagnus Melin <mkmelin+mozilla@iki.fi>
Thu, 28 Feb 2019 21:58:48 +0200
changeset 519749 b9f41b8dd43ce371409b8d1c2d0b1c133c7e45a7
parent 519748 798297c168c5aa3ef85e228268cc98c9fd088dab
child 519750 08157bb630a6626773395db07862b7de3d43a3db
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
bugs1531282
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 1531282 - MozTree event handlers should be attached in connectedCallback() instead of constructor. r=bgrins This patch moves adding the event handlers into MozTree.connectedCallback. This allows <tree onkeydown="...."> to function properly. The patch also folds all the 10 handlers for keydown into one, and handling the different cases in a switch.
toolkit/content/widgets/tree.js
--- a/toolkit/content/widgets/tree.js
+++ b/toolkit/content/widgets/tree.js
@@ -500,16 +500,100 @@
   }
 
   customElements.define("treecols", MozTreecols);
 
   class MozTree extends BaseControlMixin(MozElementMixin(XULTreeElement)) {
     constructor() {
       super();
 
+      this.attachShadow({ mode: "open" });
+      this.shadowRoot.appendChild(MozXULElement.parseXULToFragment(`
+        <html:link rel="stylesheet" href="chrome://global/skin/tree.css" />
+        <html:slot name="treecols"></html:slot>
+        <stack class="tree-stack" flex="1">
+          <hbox class="tree-rows" flex="1">
+            <hbox flex="1" class="tree-bodybox">
+              <html:slot name="treechildren"></html:slot>
+            </hbox>
+            <scrollbar height="0" minwidth="0" minheight="0" orient="vertical" inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollbar>
+          </hbox>
+          <textbox class="tree-input" left="0" top="0" hidden="true"></textbox>
+        </stack>
+        <hbox inherits="collapsed=hidehscroll">
+          <scrollbar orient="horizontal" flex="1" increment="16" style="position:relative; z-index:2147483647;" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollbar>
+          <scrollcorner inherits="collapsed=hidevscroll" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollcorner>
+        </hbox>
+      `));
+    }
+
+    static get observedAttributes() {
+      return [
+        "hidehscroll",
+        "hidevscroll",
+      ];
+    }
+
+    attributeChangedCallback(name, oldValue, newValue) {
+      if (this.isConnectedAndReady && oldValue != newValue) {
+        this._updateAttributes();
+      }
+    }
+
+    _updateAttributes() {
+      for (let [ el, attrs ] of this._inheritedAttributeMap.entries()) {
+        for (let attr of attrs) {
+          this.inheritAttribute(el, attr);
+        }
+      }
+    }
+
+    get _inheritedAttributeMap() {
+      if (!this.__inheritedAttributeMap) {
+        this.__inheritedAttributeMap = new Map();
+        for (let el of this.shadowRoot.querySelectorAll("[inherits]")) {
+          this.__inheritedAttributeMap.set(el, el.getAttribute("inherits").split(","));
+        }
+      }
+      return this.__inheritedAttributeMap;
+    }
+
+    connectedCallback() {
+      if (this.delayConnectedCallback()) {
+        return;
+      }
+      if (!this._eventListenersSetup) {
+        this._eventListenersSetup = true;
+        this.setupEventListeners();
+      }
+
+      this.setAttribute("hidevscroll", "true");
+      this.setAttribute("hidehscroll", "true");
+      this.setAttribute("clickthrough", "never");
+
+      this._updateAttributes();
+
+      this.pageUpOrDownMovesSelection = !/Mac/.test(navigator.platform);
+
+      this._inputField = null;
+
+      this._editingRow = -1;
+
+      this._editingColumn = null;
+
+      this._columnsDirty = true;
+
+      this._lastKeyTime = 0;
+
+      this._incrementalString = "";
+
+      this._touchY = -1;
+    }
+
+    setupEventListeners() {
       this.addEventListener("underflow", (event) => {
         // Scrollport event orientation
         // 0: vertical
         // 1: horizontal
         // 2: both (not used)
         if (event.target.tagName != "treechildren")
           return;
         if (event.detail == 1)
@@ -602,192 +686,162 @@
             targetRow = this.view.rowCount - 1;
             // Fall through for actual action
           case event.DIRECTION_UP:
             this.ensureRowIsVisible(targetRow);
             break;
         }
       });
 
-      this.addEventListener("select", (event) => { if (event.originalTarget == this) this.stopEditing(true); });
+      this.addEventListener("select", (event) => {
+        if (event.originalTarget == this) this.stopEditing(true);
+      });
 
       this.addEventListener("focus", (event) => {
         this.focused = true;
         if (this.currentIndex == -1 && this.view.rowCount > 0) {
           this.currentIndex = this.getFirstVisibleRow();
         }
       });
 
-      this.addEventListener("blur", (event) => { this.focused = false; });
-
-      this.addEventListener("blur", (event) => { if (event.originalTarget == this.inputField.inputField) this.stopEditing(true); }, true);
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_RETURN) {
-          return;
-        }
-
-        if (this._handleEnter(event)) {
-          event.stopPropagation();
-          event.preventDefault();
-        }
-      });
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_ESCAPE) {
-          return;
-        }
-
-        if (this._editingColumn) {
-          this.stopEditing(false);
-          this.focus();
-          event.stopPropagation();
-          event.preventDefault();
-        }
-      });
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_LEFT) {
-          return;
+      this.addEventListener("blur", (event) => {
+        this.focused = false;
+        if (event.originalTarget == this.inputField.inputField) {
+          this.stopEditing(true);
         }
-
-        if (this._editingColumn)
-          return;
-
-        var row = this.currentIndex;
-        if (row < 0)
-          return;
-
-        if (this.changeOpenState(this.currentIndex, false)) {
-          event.preventDefault();
-          return;
-        }
-        var parentIndex = this.view.getParentIndex(this.currentIndex);
-        if (parentIndex >= 0) {
-          this.view.selection.select(parentIndex);
-          this.ensureRowIsVisible(parentIndex);
-          event.preventDefault();
-        }
-      });
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_RIGHT) {
-          return;
-        }
-
-        if (this._editingColumn)
-          return;
-
-        var row = this.currentIndex;
-        if (row < 0)
-          return;
-
-        if (this.changeOpenState(row, true)) {
-          event.preventDefault();
-          return;
-        }
-        var c = row + 1;
-        var view = this.view;
-        if (c < view.rowCount &&
-          view.getParentIndex(c) == row) {
-          // If already opened, select the first child.
-          // The getParentIndex test above ensures that the children
-          // are already populated and ready.
-          this.view.selection.timedSelect(c, this._selectDelay);
-          this.ensureRowIsVisible(c);
-          event.preventDefault();
-        }
-      });
+      }, true);
 
       this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_UP) {
-          return;
-        }
-
-        if (this._editingColumn)
-          return;
-
-        if (event.getModifierState("Shift")) {
-          this._moveByOffsetShift(-1, 0, event);
-        } else {
-          this._moveByOffset(-1, 0, event);
-        }
-      });
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_DOWN) {
-          return;
-        }
+        switch (event.keyCode) {
+          case KeyEvent.DOM_VK_RETURN: {
+            if (this._handleEnter(event)) {
+              event.stopPropagation();
+              event.preventDefault();
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_ESCAPE: {
+            if (this._editingColumn) {
+              this.stopEditing(false);
+              this.focus();
+              event.stopPropagation();
+              event.preventDefault();
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_LEFT: {
+            if (this._editingColumn)
+              return;
 
-        if (this._editingColumn)
-          return;
-        if (event.getModifierState("Shift")) {
-          this._moveByOffsetShift(1, this.view.rowCount - 1, event);
-        } else {
-          this._moveByOffset(1, this.view.rowCount - 1, event);
-        }
-      });
+            let row = this.currentIndex;
+            if (row < 0)
+              return;
 
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_PAGE_UP) {
-          return;
-        }
+            if (this.changeOpenState(this.currentIndex, false)) {
+              event.preventDefault();
+              return;
+            }
+            let parentIndex = this.view.getParentIndex(this.currentIndex);
+            if (parentIndex >= 0) {
+              this.view.selection.select(parentIndex);
+              this.ensureRowIsVisible(parentIndex);
+              event.preventDefault();
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_RIGHT: {
+            if (this._editingColumn)
+              return;
 
-        if (this._editingColumn)
-          return;
-
-        if (event.getModifierState("Shift")) {
-          this._moveByPageShift(-1, 0, event);
-        } else {
-          this._moveByPage(-1, 0, event);
-        }
-      });
+            let row = this.currentIndex;
+            if (row < 0)
+              return;
 
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_PAGE_DOWN) {
-          return;
-        }
-
-        if (this._editingColumn)
-          return;
-
-        if (event.getModifierState("Shift")) {
-          this._moveByPageShift(1, this.view.rowCount - 1, event);
-        } else {
-          this._moveByPage(1, this.view.rowCount - 1, event);
-        }
-      });
-
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_HOME) {
-          return;
-        }
+            if (this.changeOpenState(row, true)) {
+              event.preventDefault();
+              return;
+            }
+            let c = row + 1;
+            let view = this.view;
+            if (c < view.rowCount &&
+              view.getParentIndex(c) == row) {
+              // If already opened, select the first child.
+              // The getParentIndex test above ensures that the children
+              // are already populated and ready.
+              this.view.selection.timedSelect(c, this._selectDelay);
+              this.ensureRowIsVisible(c);
+              event.preventDefault();
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_UP: {
+            if (this._editingColumn)
+              return;
 
-        if (this._editingColumn)
-          return;
-
-        if (event.getModifierState("Shift")) {
-          this._moveToEdgeShift(0, event);
-        } else {
-          this._moveToEdge(0, event);
-        }
-      });
+            if (event.getModifierState("Shift")) {
+              this._moveByOffsetShift(-1, 0, event);
+            } else {
+              this._moveByOffset(-1, 0, event);
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_DOWN: {
+            if (this._editingColumn)
+              return;
+            if (event.getModifierState("Shift")) {
+              this._moveByOffsetShift(1, this.view.rowCount - 1, event);
+            } else {
+              this._moveByOffset(1, this.view.rowCount - 1, event);
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_PAGE_UP: {
+            if (this._editingColumn)
+              return;
 
-      this.addEventListener("keydown", (event) => {
-        if (event.keyCode != KeyEvent.DOM_VK_END) {
-          return;
-        }
+            if (event.getModifierState("Shift")) {
+              this._moveByPageShift(-1, 0, event);
+            } else {
+              this._moveByPage(-1, 0, event);
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_PAGE_DOWN: {
+            if (this._editingColumn)
+              return;
 
-        if (this._editingColumn)
-          return;
+            if (event.getModifierState("Shift")) {
+              this._moveByPageShift(1, this.view.rowCount - 1, event);
+            } else {
+              this._moveByPage(1, this.view.rowCount - 1, event);
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_HOME: {
+            if (this._editingColumn)
+              return;
 
-        if (event.getModifierState("Shift")) {
-          this._moveToEdgeShift(this.view.rowCount - 1, event);
-        } else {
-          this._moveToEdge(this.view.rowCount - 1, event);
+            if (event.getModifierState("Shift")) {
+              this._moveToEdgeShift(0, event);
+            } else {
+              this._moveToEdge(0, event);
+            }
+            break;
+          }
+          case KeyEvent.DOM_VK_END: {
+            if (this._editingColumn)
+              return;
+
+            if (event.getModifierState("Shift")) {
+              this._moveToEdgeShift(this.view.rowCount - 1, event);
+            } else {
+              this._moveToEdge(this.view.rowCount - 1, event);
+            }
+            break;
+          }
         }
       });
 
       this.addEventListener("keypress", (event) => {
         if (this._editingColumn)
           return;
 
         if (event.charCode == " ".charCodeAt(0)) {
@@ -803,94 +857,16 @@
           var l = this._keyNavigate(event);
           if (l >= 0) {
             this.view.selection.timedSelect(l, this._selectDelay);
             this.ensureRowIsVisible(l);
           }
           event.preventDefault();
         }
       });
-
-      this.attachShadow({ mode: "open" });
-      this.shadowRoot.appendChild(MozXULElement.parseXULToFragment(`
-        <html:link rel="stylesheet" href="chrome://global/skin/tree.css" />
-        <html:slot name="treecols"></html:slot>
-        <stack class="tree-stack" flex="1">
-          <hbox class="tree-rows" flex="1">
-            <hbox flex="1" class="tree-bodybox">
-              <html:slot name="treechildren"></html:slot>
-            </hbox>
-            <scrollbar height="0" minwidth="0" minheight="0" orient="vertical" inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollbar>
-          </hbox>
-          <textbox class="tree-input" left="0" top="0" hidden="true"></textbox>
-        </stack>
-        <hbox inherits="collapsed=hidehscroll">
-          <scrollbar orient="horizontal" flex="1" increment="16" style="position:relative; z-index:2147483647;" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollbar>
-          <scrollcorner inherits="collapsed=hidevscroll" oncontextmenu="event.stopPropagation(); event.preventDefault();" onclick="event.stopPropagation(); event.preventDefault();" ondblclick="event.stopPropagation();" oncommand="event.stopPropagation();"></scrollcorner>
-        </hbox>
-      `));
-    }
-
-    static get observedAttributes() {
-      return [
-        "hidehscroll",
-        "hidevscroll",
-      ];
-    }
-
-    attributeChangedCallback(name, oldValue, newValue) {
-      if (this.isConnectedAndReady && oldValue != newValue) {
-        this._updateAttributes();
-      }
-    }
-
-    _updateAttributes() {
-      for (let [ el, attrs ] of this._inheritedAttributeMap.entries()) {
-        for (let attr of attrs) {
-          this.inheritAttribute(el, attr);
-        }
-      }
-    }
-
-    get _inheritedAttributeMap() {
-      if (!this.__inheritedAttributeMap) {
-        this.__inheritedAttributeMap = new Map();
-        for (let el of this.shadowRoot.querySelectorAll("[inherits]")) {
-          this.__inheritedAttributeMap.set(el, el.getAttribute("inherits").split(","));
-        }
-      }
-      return this.__inheritedAttributeMap;
-    }
-
-    connectedCallback() {
-      if (this.delayConnectedCallback()) {
-        return;
-      }
-
-      this.setAttribute("hidevscroll", "true");
-      this.setAttribute("hidehscroll", "true");
-      this.setAttribute("clickthrough", "never");
-
-      this._updateAttributes();
-
-      this.pageUpOrDownMovesSelection = !/Mac/.test(navigator.platform);
-
-      this._inputField = null;
-
-      this._editingRow = -1;
-
-      this._editingColumn = null;
-
-      this._columnsDirty = true;
-
-      this._lastKeyTime = 0;
-
-      this._incrementalString = "";
-
-      this._touchY = -1;
     }
 
     get body() {
       return this.treeBody;
     }
 
     set editable(val) {
       if (val) this.setAttribute("editable", "true");