browser/components/customizableui/content/toolbar.xml
author Kris Maglione <maglione.k@gmail.com>
Mon, 29 Jan 2018 15:20:18 -0800
changeset 456545 e6a7b5e11ba856ee3535f76c6bcca17ea29e3d5f
parent 456423 c6e0fe339cb1e7c0c029e68be5332e52f53ab6b7
child 457156 d5a5ad1dbbf2c53a80386e7397ba6b32153b2b8e
permissions -rw-r--r--
Bug 1431533: Part 5a - Auto-rewrite code to use ChromeUtils import methods. r=florian This was done using the following script: https://bitbucket.org/kmaglione/m-c-rewrites/src/37e3803c7a23385bab5ea077ba8692bcfe1608fc/processors/chromeutils-import.jsm MozReview-Commit-ID: 1Nc3XDu0wGl

<?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/. -->

<bindings id="browserToolbarBindings"
          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">

  <binding id="toolbar" role="xul:toolbar">
    <resources>
      <stylesheet src="chrome://global/skin/toolbar.css"/>
    </resources>
    <implementation>
      <field name="overflowedDuringConstruction">null</field>

      <constructor><![CDATA[
          let scope = {};
          ChromeUtils.import("resource:///modules/CustomizableUI.jsm", scope);
          let CustomizableUI = scope.CustomizableUI;
          // Add an early overflow event listener that will mark if the
          // toolbar overflowed during construction.
          if (CustomizableUI.isAreaOverflowable(this.id)) {
            this.addEventListener("overflow", this);
            this.addEventListener("underflow", this);
          }

          // Searching for the toolbox palette in the toolbar binding because
          // toolbars are constructed first.
          let toolbox = this.toolbox;
          if (toolbox && !toolbox.palette) {
            for (let node of toolbox.children) {
              if (node.localName == "toolbarpalette") {
                // Hold on to the palette but remove it from the document.
                toolbox.palette = node;
                toolbox.removeChild(node);
                break;
              }
            }
          }

          // pass the current set of children for comparison with placements:
          let children = Array.from(this.childNodes)
                              .filter(node => node.getAttribute("skipintoolbarset") != "true" && node.id)
                              .map(node => node.id);
          CustomizableUI.registerToolbarNode(this, children);
      ]]></constructor>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (aEvent.type == "overflow" && aEvent.detail > 0) {
            if (this.overflowable && this.overflowable.initialized) {
              this.overflowable.onOverflow(aEvent);
            } else {
              this.overflowedDuringConstruction = aEvent;
            }
          } else if (aEvent.type == "underflow" && aEvent.detail > 0) {
            this.overflowedDuringConstruction = null;
          }
        ]]></body>
      </method>

      <method name="insertItem">
        <parameter name="aId"/>
        <parameter name="aBeforeElt"/>
        <parameter name="aWrapper"/>
        <body><![CDATA[
          if (aWrapper) {
            Cu.reportError("Can't insert " + aId + ": using insertItem " +
                           "no longer supports wrapper elements.");
            return null;
          }

          // Hack, the customizable UI code makes this be the last position
          let pos = null;
          if (aBeforeElt) {
            let beforeInfo = CustomizableUI.getPlacementOfWidget(aBeforeElt.id);
            if (beforeInfo.area != this.id) {
              Cu.reportError("Can't insert " + aId + " before " +
                             aBeforeElt.id + " which isn't in this area (" +
                             this.id + ").");
              return null;
            }
            pos = beforeInfo.position;
          }

          CustomizableUI.addWidgetToArea(aId, this.id, pos);
          return this.ownerDocument.getElementById(aId);
        ]]></body>
      </method>

      <property name="toolbarName"
                onget="return this.getAttribute('toolbarname');"
                onset="this.setAttribute('toolbarname', val); return val;"/>

      <property name="customizationTarget" readonly="true">
        <getter><![CDATA[
          if (this._customizationTarget)
            return this._customizationTarget;

          let id = this.getAttribute("customizationtarget");
          if (id)
            this._customizationTarget = document.getElementById(id);

          if (this._customizationTarget)
            this._customizationTarget.insertItem = this.insertItem.bind(this);
          else
            this._customizationTarget = this;

          return this._customizationTarget;
        ]]></getter>
      </property>

      <property name="toolbox" readonly="true">
        <getter><![CDATA[
          if (this._toolbox)
            return this._toolbox;

          let toolboxId = this.getAttribute("toolboxid");
          if (toolboxId) {
            let toolbox = document.getElementById(toolboxId);
            if (toolbox) {
              this._toolbox = toolbox;
            }
          }

          if (!this._toolbox && this.parentNode &&
              this.parentNode.localName == "toolbox") {
            this._toolbox = this.parentNode;
          }

          return this._toolbox;
        ]]></getter>
      </property>

      <property name="currentSet">
        <getter><![CDATA[
          let currentWidgets = new Set();
          for (let node of this.customizationTarget.children) {
            let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
            if (realNode.getAttribute("skipintoolbarset") != "true") {
              currentWidgets.add(realNode.id);
            }
          }
          if (this.getAttribute("overflowing") == "true") {
            let overflowTarget = this.getAttribute("overflowtarget");
            let overflowList = this.ownerDocument.getElementById(overflowTarget);
            for (let node of overflowList.children) {
              let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
              if (realNode.getAttribute("skipintoolbarset") != "true") {
                currentWidgets.add(realNode.id);
              }
            }
          }
          let orderedPlacements = CustomizableUI.getWidgetIdsInArea(this.id);
          return orderedPlacements.filter(w => currentWidgets.has(w)).join(",");
        ]]></getter>
        <setter><![CDATA[
          // Get list of new and old ids:
          let newVal = (val || "").split(",").filter(x => x);
          let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);

          // Get a list of items only in the new list
          let newIds = newVal.filter(id => oldIds.indexOf(id) == -1);
          CustomizableUI.beginBatchUpdate();
          try {
            for (let newId of newIds) {
              oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
              let nextId = newId;
              let pos;
              do {
                // Get the next item
                nextId = newVal[newVal.indexOf(nextId) + 1];
                // Figure out where it is in the old list
                pos = oldIds.indexOf(nextId);
                // If it's not in the old list, repeat:
              } while (pos == -1 && nextId);
              if (pos == -1) {
                pos = null; // We didn't find anything, insert at the end
              }
              CustomizableUI.addWidgetToArea(newId, this.id, pos);
            }

            let currentIds = this.currentSet.split(",");
            let removedIds = currentIds.filter(id => newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1);
            for (let removedId of removedIds) {
              CustomizableUI.removeWidgetFromArea(removedId);
            }
          } finally {
            CustomizableUI.endBatchUpdate();
          }
        ]]></setter>
      </property>


    </implementation>
  </binding>

  <binding id="toolbar-menubar-stub">
    <implementation>
      <property name="toolbox" readonly="true">
        <getter><![CDATA[
          if (this._toolbox)
            return this._toolbox;

          if (this.parentNode && this.parentNode.localName == "toolbox") {
            this._toolbox = this.parentNode;
          }

          return this._toolbox;
        ]]></getter>
      </property>
      <property name="currentSet" readonly="true">
        <getter><![CDATA[
          return this.getAttribute("defaultset");
        ]]></getter>
      </property>
      <method name="insertItem">
        <body><![CDATA[
          return null;
        ]]></body>
      </method>
    </implementation>
  </binding>

  <!-- The toolbar-menubar-autohide and toolbar-drag bindings are almost
       verbatim copies of their toolkit counterparts - they just inherit from
       the customizableui's toolbar binding instead of toolkit's. We're currently
       OK with the maintainance burden of having two copies of a binding, since
       the long term goal is to move the customization framework into toolkit. -->

  <binding id="toolbar-menubar-autohide"
           extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
    <implementation>
      <constructor>
        this._setInactive();
      </constructor>
      <destructor>
        this._setActive();
      </destructor>

      <field name="_inactiveTimeout">null</field>

      <field name="_contextMenuListener"><![CDATA[({
        toolbar: this,
        contextMenu: null,

        get active() {
          return !!this.contextMenu;
        },

        init(event) {
          let node = event.target;
          while (node != this.toolbar) {
            if (node.localName == "menupopup")
              return;
            node = node.parentNode;
          }

          let contextMenuId = this.toolbar.getAttribute("context");
          if (!contextMenuId)
            return;

          this.contextMenu = document.getElementById(contextMenuId);
          if (!this.contextMenu)
            return;

          this.contextMenu.addEventListener("popupshown", this);
          this.contextMenu.addEventListener("popuphiding", this);
          this.toolbar.addEventListener("mousemove", this);
        },
        handleEvent(event) {
          switch (event.type) {
            case "popupshown":
              this.toolbar.removeEventListener("mousemove", this);
              break;
            case "popuphiding":
            case "mousemove":
              this.toolbar._setInactiveAsync();
              this.toolbar.removeEventListener("mousemove", this);
              this.contextMenu.removeEventListener("popuphiding", this);
              this.contextMenu.removeEventListener("popupshown", this);
              this.contextMenu = null;
              break;
          }
        }
      })]]></field>

      <method name="_setInactive">
        <body><![CDATA[
          this.setAttribute("inactive", "true");
        ]]></body>
      </method>

      <method name="_setInactiveAsync">
        <body><![CDATA[
          this._inactiveTimeout = setTimeout(function(self) {
            if (self.getAttribute("autohide") == "true") {
              self._inactiveTimeout = null;
              self._setInactive();
            }
          }, 0, this);
        ]]></body>
      </method>

      <method name="_setActive">
        <body><![CDATA[
          if (this._inactiveTimeout) {
            clearTimeout(this._inactiveTimeout);
            this._inactiveTimeout = null;
          }
          this.removeAttribute("inactive");
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="DOMMenuBarActive"     action="this._setActive();"/>
      <handler event="popupshowing"         action="this._setActive();"/>
      <handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
      <handler event="DOMMenuBarInactive"><![CDATA[
        if (!this._contextMenuListener.active)
          this._setInactiveAsync();
      ]]></handler>
    </handlers>
  </binding>

  <binding id="toolbar-drag"
           extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
    <implementation>
      <field name="_dragBindingAlive">true</field>
      <constructor><![CDATA[
        if (!this._draggableStarted) {
          this._draggableStarted = true;
          try {
            let tmp = {};
            ChromeUtils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
            let draggableThis = new tmp.WindowDraggingElement(this);
            draggableThis.mouseDownCheck = function(e) {
              return this._dragBindingAlive;
            };
          } catch (e) {}
        }
      ]]></constructor>
    </implementation>
  </binding>
</bindings>