browser/components/customizableui/content/toolbar.xml

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

     1 <?xml version="1.0"?>
     2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
     3    - License, v. 2.0. If a copy of the MPL was not distributed with this
     4    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     6 <bindings id="browserToolbarBindings"
     7           xmlns="http://www.mozilla.org/xbl"
     8           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
     9           xmlns:xbl="http://www.mozilla.org/xbl">
    11   <binding id="toolbar" role="xul:toolbar">
    12     <resources>
    13       <stylesheet src="chrome://global/skin/toolbar.css"/>
    14     </resources>
    15     <implementation>
    16       <field name="overflowedDuringConstruction">null</field>
    18       <constructor><![CDATA[
    19           let scope = {};
    20           Cu.import("resource:///modules/CustomizableUI.jsm", scope);
    21           // Add an early overflow event listener that will mark if the
    22           // toolbar overflowed during construction.
    23           if (scope.CustomizableUI.isAreaOverflowable(this.id)) {
    24             this.addEventListener("overflow", this);
    25             this.addEventListener("underflow", this);
    26           }
    28           if (document.readyState == "complete") {
    29             this._init();
    30           } else {
    31             // Need to wait until XUL overlays are loaded. See bug 554279.
    32             let self = this;
    33             document.addEventListener("readystatechange", function onReadyStateChange() {
    34               if (document.readyState != "complete")
    35                 return;
    36               document.removeEventListener("readystatechange", onReadyStateChange, false);
    37               self._init();
    38             }, false);
    39           }
    40       ]]></constructor>
    42       <method name="_init">
    43         <body><![CDATA[
    44           let scope = {};
    45           Cu.import("resource:///modules/CustomizableUI.jsm", scope);
    46           let CustomizableUI = scope.CustomizableUI;
    48           // Bug 989289: Forcibly set the now unsupported "mode" and "iconsize"
    49           // attributes, just in case they accidentally get restored from
    50           // persistence from a user that's been upgrading and downgrading.
    51           if (CustomizableUI.isBuiltinToolbar(this.id)) {
    52             const kAttributes = new Map([["mode", "icons"], ["iconsize", "small"]]);
    53             for (let [attribute, value] of kAttributes) {
    54               if (this.getAttribute(attribute) != value) {
    55                 this.setAttribute(attribute, value);
    56                 document.persist(this.id, attribute);
    57               }
    58               if (this.toolbox) {
    59                 if (this.toolbox.getAttribute(attribute) != value) {
    60                   this.toolbox.setAttribute(attribute, value);
    61                   document.persist(this.toolbox.id, attribute);
    62                 }
    63               }
    64             }
    65           }
    67           // Searching for the toolbox palette in the toolbar binding because
    68           // toolbars are constructed first.
    69           let toolbox = this.toolbox;
    70           if (toolbox && !toolbox.palette) {
    71             for (let node of toolbox.children) {
    72               if (node.localName == "toolbarpalette") {
    73                 // Hold on to the palette but remove it from the document.
    74                 toolbox.palette = node;
    75                 toolbox.removeChild(node);
    76                 break;
    77               }
    78             }
    79           }
    81           // pass the current set of children for comparison with placements:
    82           let children = [node.id for (node of this.childNodes)
    83                           if (node.getAttribute("skipintoolbarset") != "true" && node.id)];
    84           CustomizableUI.registerToolbarNode(this, children);
    85         ]]></body>
    86       </method>
    88       <method name="handleEvent">
    89         <parameter name="aEvent"/>
    90         <body><![CDATA[
    91           if (aEvent.type == "overflow" && aEvent.detail > 0) {
    92             if (this.overflowable && this.overflowable.initialized) {
    93               this.overflowable.onOverflow(aEvent);
    94             } else {
    95               this.overflowedDuringConstruction = aEvent;
    96             }
    97           } else if (aEvent.type == "underflow" && aEvent.detail > 0) {
    98             this.overflowedDuringConstruction = null;
    99           }
   100         ]]></body>
   101       </method>
   103       <method name="insertItem">
   104         <parameter name="aId"/>
   105         <parameter name="aBeforeElt"/>
   106         <parameter name="aWrapper"/>
   107         <body><![CDATA[
   108           if (aWrapper) {
   109             Cu.reportError("Can't insert " + aId + ": using insertItem " +
   110                            "no longer supports wrapper elements.");
   111             return null;
   112           }
   114           // Hack, the customizable UI code makes this be the last position
   115           let pos = null;
   116           if (aBeforeElt) {
   117             let beforeInfo = CustomizableUI.getPlacementOfWidget(aBeforeElt.id);
   118             if (beforeInfo.area != this.id) {
   119               Cu.reportError("Can't insert " + aId + " before " +
   120                              aBeforeElt.id + " which isn't in this area (" +
   121                              this.id + ").");
   122               return null;
   123             }
   124             pos = beforeInfo.position;
   125           }
   127           CustomizableUI.addWidgetToArea(aId, this.id, pos);
   128           return this.ownerDocument.getElementById(aId);
   129         ]]></body>
   130       </method>
   132       <property name="toolbarName"
   133                 onget="return this.getAttribute('toolbarname');"
   134                 onset="this.setAttribute('toolbarname', val); return val;"/>
   136       <property name="customizationTarget" readonly="true">
   137         <getter><![CDATA[
   138           if (this._customizationTarget)
   139             return this._customizationTarget;
   141           let id = this.getAttribute("customizationtarget");
   142           if (id)
   143             this._customizationTarget = document.getElementById(id);
   145           if (this._customizationTarget)
   146             this._customizationTarget.insertItem = this.insertItem.bind(this);
   147           else
   148             this._customizationTarget = this;
   150           return this._customizationTarget;
   151         ]]></getter>
   152       </property>
   154       <property name="toolbox" readonly="true">
   155         <getter><![CDATA[
   156           if (this._toolbox)
   157             return this._toolbox;
   159           let toolboxId = this.getAttribute("toolboxid");
   160           if (toolboxId) {
   161             let toolbox = document.getElementById(toolboxId);
   162             if (toolbox) {
   163               if (toolbox.externalToolbars.indexOf(this) == -1)
   164                 toolbox.externalToolbars.push(this);
   166               this._toolbox = toolbox;
   167             }
   168           }
   170           if (!this._toolbox && this.parentNode &&
   171               this.parentNode.localName == "toolbox") {
   172             this._toolbox = this.parentNode;
   173           }
   175           return this._toolbox;
   176         ]]></getter>
   177       </property>
   179       <property name="currentSet">
   180         <getter><![CDATA[
   181           let currentWidgets = new Set();
   182           for (let node of this.customizationTarget.children) {
   183             let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
   184             if (realNode.getAttribute("skipintoolbarset") != "true") {
   185               currentWidgets.add(realNode.id);
   186             }
   187           }
   188           if (this.getAttribute("overflowing") == "true") {
   189             let overflowTarget = this.getAttribute("overflowtarget");
   190             let overflowList = this.ownerDocument.getElementById(overflowTarget);
   191             for (let node of overflowList.children) {
   192               let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
   193               if (realNode.getAttribute("skipintoolbarset") != "true") {
   194                 currentWidgets.add(realNode.id);
   195               }
   196             }
   197           }
   198           let orderedPlacements = CustomizableUI.getWidgetIdsInArea(this.id);
   199           return orderedPlacements.filter((x) => currentWidgets.has(x)).join(',');
   200         ]]></getter>
   201         <setter><![CDATA[
   202           // Get list of new and old ids:
   203           let newVal = (val || '').split(',').filter(x => x);
   204           let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
   206           // Get a list of items only in the new list
   207           let newIds = [id for (id of newVal) if (oldIds.indexOf(id) == -1)];
   208           CustomizableUI.beginBatchUpdate();
   209           try {
   210             for (let newId of newIds) {
   211               oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
   212               let nextId = newId;
   213               let pos;
   214               do {
   215                 // Get the next item
   216                 nextId = newVal[newVal.indexOf(nextId) + 1];
   217                 // Figure out where it is in the old list
   218                 pos = oldIds.indexOf(nextId);
   219                 // If it's not in the old list, repeat:
   220               } while (pos == -1 && nextId);
   221               if (pos == -1) {
   222                 pos = null; // We didn't find anything, insert at the end
   223               }
   224               CustomizableUI.addWidgetToArea(newId, this.id, pos);
   225             }
   227             let currentIds = this.currentSet.split(',');
   228             let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
   229             for (let removedId of removedIds) {
   230               CustomizableUI.removeWidgetFromArea(removedId);
   231             }
   232           } finally {
   233             CustomizableUI.endBatchUpdate();
   234           }
   235         ]]></setter>
   236       </property>
   239     </implementation>
   240   </binding>
   242   <binding id="toolbar-menubar-stub">
   243     <implementation>
   244       <property name="toolbox" readonly="true">
   245         <getter><![CDATA[
   246           if (this._toolbox)
   247             return this._toolbox;
   249           if (this.parentNode && this.parentNode.localName == "toolbox") {
   250             this._toolbox = this.parentNode;
   251           }
   253           return this._toolbox;
   254         ]]></getter>
   255       </property>
   256       <property name="currentSet" readonly="true">
   257         <getter><![CDATA[
   258           return this.getAttribute("defaultset");
   259         ]]></getter>
   260       </property>
   261       <method name="insertItem">
   262         <body><![CDATA[
   263           return null;
   264         ]]></body>
   265       </method>
   266     </implementation>
   267   </binding>
   269   <!-- The toolbar-menubar-autohide and toolbar-drag bindings are almost
   270        verbatim copies of their toolkit counterparts - they just inherit from
   271        the customizableui's toolbar binding instead of toolkit's. We're currently
   272        OK with the maintainance burden of having two copies of a binding, since
   273        the long term goal is to move the customization framework into toolkit. -->
   275   <binding id="toolbar-menubar-autohide"
   276            extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
   277     <implementation>
   278       <constructor>
   279         this._setInactive();
   280       </constructor>
   281       <destructor>
   282         this._setActive();
   283       </destructor>
   285       <field name="_inactiveTimeout">null</field>
   287       <field name="_contextMenuListener"><![CDATA[({
   288         toolbar: this,
   289         contextMenu: null,
   291         get active () !!this.contextMenu,
   293         init: function (event) {
   294           let node = event.target;
   295           while (node != this.toolbar) {
   296             if (node.localName == "menupopup")
   297               return;
   298             node = node.parentNode;
   299           }
   301           let contextMenuId = this.toolbar.getAttribute("context");
   302           if (!contextMenuId)
   303             return;
   305           this.contextMenu = document.getElementById(contextMenuId);
   306           if (!this.contextMenu)
   307             return;
   309           this.contextMenu.addEventListener("popupshown", this, false);
   310           this.contextMenu.addEventListener("popuphiding", this, false);
   311           this.toolbar.addEventListener("mousemove", this, false);
   312         },
   313         handleEvent: function (event) {
   314           switch (event.type) {
   315             case "popupshown":
   316               this.toolbar.removeEventListener("mousemove", this, false);
   317               break;
   318             case "popuphiding":
   319             case "mousemove":
   320               this.toolbar._setInactiveAsync();
   321               this.toolbar.removeEventListener("mousemove", this, false);
   322               this.contextMenu.removeEventListener("popuphiding", this, false);
   323               this.contextMenu.removeEventListener("popupshown", this, false);
   324               this.contextMenu = null;
   325               break;
   326           }
   327         }
   328       })]]></field>
   330       <method name="_setInactive">
   331         <body><![CDATA[
   332           this.setAttribute("inactive", "true");
   333         ]]></body>
   334       </method>
   336       <method name="_setInactiveAsync">
   337         <body><![CDATA[
   338           this._inactiveTimeout = setTimeout(function (self) {
   339             if (self.getAttribute("autohide") == "true") {
   340               self._inactiveTimeout = null;
   341               self._setInactive();
   342             }
   343           }, 0, this);
   344         ]]></body>
   345       </method>
   347       <method name="_setActive">
   348         <body><![CDATA[
   349           if (this._inactiveTimeout) {
   350             clearTimeout(this._inactiveTimeout);
   351             this._inactiveTimeout = null;
   352           }
   353           this.removeAttribute("inactive");
   354         ]]></body>
   355       </method>
   356     </implementation>
   358     <handlers>
   359       <handler event="DOMMenuBarActive"     action="this._setActive();"/>
   360       <handler event="popupshowing"         action="this._setActive();"/>
   361       <handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
   362       <handler event="DOMMenuBarInactive"><![CDATA[
   363         if (!this._contextMenuListener.active)
   364           this._setInactiveAsync();
   365       ]]></handler>
   366     </handlers>
   367   </binding>
   369   <binding id="toolbar-drag"
   370            extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
   371     <implementation>
   372       <field name="_dragBindingAlive">true</field>
   373       <constructor><![CDATA[
   374         if (!this._draggableStarted) {
   375           this._draggableStarted = true;
   376           try {
   377             let tmp = {};
   378             Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
   379             let draggableThis = new tmp.WindowDraggingElement(this);
   380             draggableThis.mouseDownCheck = function(e) {
   381               return this._dragBindingAlive;
   382             };
   383           } catch (e) {}
   384         }
   385       ]]></constructor>
   386     </implementation>
   387   </binding>
   390 <!-- This is a peculiar binding. It is here to deal with overlayed/inserted add-on content,
   391       and immediately direct such content elsewhere. -->
   392   <binding id="addonbar-delegating">
   393     <implementation>
   394       <constructor><![CDATA[
   395           // Reading these immediately so nobody messes with them anymore:
   396           this._delegatingToolbar = this.getAttribute("toolbar-delegate");
   397           this._wasCollapsed = this.getAttribute("collapsed") == "true";
   398           // Leaving those in here to unbreak some code:
   399           if (document.readyState == "complete") {
   400             this._init();
   401           } else {
   402             // Need to wait until XUL overlays are loaded. See bug 554279.
   403             let self = this;
   404             document.addEventListener("readystatechange", function onReadyStateChange() {
   405               if (document.readyState != "complete")
   406                 return;
   407               document.removeEventListener("readystatechange", onReadyStateChange, false);
   408               self._init();
   409             }, false);
   410           }
   411       ]]></constructor>
   413       <method name="_init">
   414         <body><![CDATA[
   415           // Searching for the toolbox palette in the toolbar binding because
   416           // toolbars are constructed first.
   417           let toolbox = this.toolbox;
   418           if (toolbox && !toolbox.palette) {
   419             for (let node of toolbox.children) {
   420               if (node.localName == "toolbarpalette") {
   421                 // Hold on to the palette but remove it from the document.
   422                 toolbox.palette = node;
   423                 toolbox.removeChild(node);
   424               }
   425             }
   426           }
   428           // pass the current set of children for comparison with placements:
   429           let children = [];
   430           for (let node of this.childNodes) {
   431             if (node.getAttribute("skipintoolbarset") != "true" && node.id) {
   432               // Force everything to be removable so that buildArea can chuck stuff
   433               // out if the user has customized things / we've been here before:
   434               if (!this._whiteListed.has(node.id)) {
   435                 node.setAttribute("removable", "true");
   436               }
   437               children.push(node);
   438             }
   439           }
   440           CustomizableUI.registerToolbarNode(this, children);
   441           let existingMigratedItems = (this.getAttribute("migratedset") || "").split(',');
   442           for (let migratedItem of existingMigratedItems.filter((x) => !!x)) {
   443             this._currentSetMigrated.add(migratedItem);
   444           }
   445           this.evictNodes();
   446           // We can't easily use |this| or strong bindings for the observer fn here
   447           // because that creates leaky circular references when the node goes away,
   448           // and XBL destructors are unreliable.
   449           let mutationObserver = new MutationObserver(function(mutations) {
   450             if (!mutations.length) {
   451               return;
   452             }
   453             let toolbar = mutations[0].target;
   454             // Can't use our own attribute because we might not have one if we're set to
   455             // collapsed
   456             let areCustomizing = toolbar.ownerDocument.documentElement.getAttribute("customizing");
   457             if (!toolbar._isModifying && !areCustomizing) {
   458               toolbar.evictNodes();
   459             }
   460           });
   461           mutationObserver.observe(this, {childList: true});
   462         ]]></body>
   463       </method>
   464       <method name="evictNodes">
   465         <body><![CDATA[
   466           this._isModifying = true;
   467           let i = this.childNodes.length;
   468           while (i--) {
   469             let node = this.childNodes[i];
   470             if (this.childNodes[i].id) {
   471               this.evictNode(this.childNodes[i]);
   472             } else {
   473               node.remove();
   474             }
   475           }
   476           this._isModifying = false;
   477           this._updateMigratedSet();
   478         ]]></body>
   479       </method>
   480       <method name="evictNode">
   481         <parameter name="aNode"/>
   482         <body>
   483         <![CDATA[
   484           if (this._whiteListed.has(aNode.id) || CustomizableUI.isSpecialWidget(aNode.id)) {
   485             return;
   486           }
   487           const kItemMaxWidth = 100;
   488           let oldParent = aNode.parentNode;
   489           aNode.setAttribute("removable", "true");
   490           this._currentSetMigrated.add(aNode.id);
   492           let movedOut = false;
   493           if (!this._wasCollapsed) {
   494             try {
   495               let nodeWidth = aNode.getBoundingClientRect().width;
   496               if (nodeWidth == 0 || nodeWidth > kItemMaxWidth) {
   497                 throw new Error(aNode.id + " is too big (" + nodeWidth +
   498                                 "px wide), moving to the palette");
   499               }
   500               CustomizableUI.addWidgetToArea(aNode.id, this._delegatingToolbar);
   501               movedOut = true;
   502             } catch (ex) {
   503               // This will throw if the node is too big, or can't be moved there for
   504               // some reason. Report this:
   505               Cu.reportError(ex);
   506             }
   507           }
   509           /* We won't have moved the widget if either the add-on bar was collapsed,
   510            * or if it was too wide to be inserted into the navbar. */
   511           if (!movedOut) {
   512             try {
   513               CustomizableUI.removeWidgetFromArea(aNode.id);
   514             } catch (ex) {
   515               Cu.reportError(ex);
   516               aNode.remove();
   517             }
   518           }
   520           // Surprise: addWidgetToArea(palette) will get you nothing if the palette
   521           // is not constructed yet. Fix:
   522           if (aNode.parentNode == oldParent) {
   523             let palette = this.toolbox.palette;
   524             if (palette && oldParent != palette) {
   525               palette.appendChild(aNode);
   526             }
   527           }
   528         ]]></body>
   529       </method>
   530       <method name="insertItem">
   531         <parameter name="aId"/>
   532         <parameter name="aBeforeElt"/>
   533         <parameter name="aWrapper"/>
   534         <body><![CDATA[
   535           if (aWrapper) {
   536             Cu.reportError("Can't insert " + aId + ": using insertItem " +
   537                            "no longer supports wrapper elements.");
   538             return null;
   539           }
   541           let widget = CustomizableUI.getWidget(aId);
   542           widget = widget && widget.forWindow(window);
   543           let node = widget && widget.node;
   544           if (!node) {
   545             return null;
   546           }
   548           this._isModifying = true;
   549           // Temporarily add it here so it can have a width, then ditch it:
   550           this.appendChild(node);
   551           this.evictNode(node);
   552           this._isModifying = false;
   553           this._updateMigratedSet();
   554           // We will now have moved stuff around; kick off some events
   555           // so add-ons know we've just moved their stuff:
   556           // XXXgijs: only in this window. It's hard to know for sure what's the right
   557           // thing to do here - typically insertItem is used on each window, so
   558           // this seems to make the most sense, even if some of the effects of
   559           // evictNode might affect multiple windows.
   560           CustomizableUI.dispatchToolboxEvent("customizationchange", {}, window);
   561           CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
   562           return node;
   563         ]]></body>
   564       </method>
   565       <method name="getMigratedItems">
   566         <body><![CDATA[
   567           return [... this._currentSetMigrated];
   568         ]]></body>
   569       </method>
   570       <method name="_updateMigratedSet">
   571         <body><![CDATA[
   572           let newMigratedItems = this.getMigratedItems().join(',');
   573           if (this.getAttribute("migratedset") != newMigratedItems) {
   574             this.setAttribute("migratedset", newMigratedItems);
   575             this.ownerDocument.persist(this.id, "migratedset");
   576           }
   577         ]]></body>
   578       </method>
   579       <property name="customizationTarget" readonly="true">
   580         <getter><![CDATA[
   581           return this;
   582         ]]></getter>
   583       </property>
   584       <property name="currentSet">
   585         <getter><![CDATA[
   586           return [node.id for (node of this.children)].join(',');
   587         ]]></getter>
   588         <setter><![CDATA[
   589           let v = val.split(',');
   590           let newButtons = v.filter(x => x && (!this._whiteListed.has(x) &&
   591                                                !CustomizableUI.isSpecialWidget(x) &&
   592                                                !this._currentSetMigrated.has(x)));
   593           for (let newButton of newButtons) {
   594             this._currentSetMigrated.add(newButton);
   595             this.insertItem(newButton);
   596           }
   597           this._updateMigratedSet();
   598         ]]></setter>
   599       </property>
   600       <property name="toolbox" readonly="true">
   601         <getter><![CDATA[
   602           if (!this._toolbox && this.parentNode &&
   603               this.parentNode.localName == "toolbox") {
   604             this._toolbox = this.parentNode;
   605           }
   607           return this._toolbox;
   608         ]]></getter>
   609       </property>
   610       <field name="_whiteListed" readonly="true">new Set(["addonbar-closebutton", "status-bar"])</field>
   611       <field name="_isModifying">false</field>
   612       <field name="_currentSetMigrated">new Set()</field>
   613     </implementation>
   614   </binding>
   615 </bindings>

mercurial