toolkit/content/widgets/richlistbox.xml

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 <?xml version="1.0"?>
     3 <!-- This Source Code Form is subject to the terms of the Mozilla Public
     4    - License, v. 2.0. If a copy of the MPL was not distributed with this
     5    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     7 <bindings id="richlistboxBindings"
     8           xmlns="http://www.mozilla.org/xbl"
     9           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    10           xmlns:xbl="http://www.mozilla.org/xbl">
    12   <binding id="richlistbox"
    13            extends="chrome://global/content/bindings/listbox.xml#listbox-base">
    14     <resources>
    15       <stylesheet src="chrome://global/skin/richlistbox.css"/>
    16     </resources>
    18     <content>
    19       <children includes="listheader"/>
    20       <xul:scrollbox allowevents="true" orient="vertical" anonid="main-box"
    21                      flex="1" style="overflow: auto;" xbl:inherits="dir,pack">
    22         <children/>
    23       </xul:scrollbox>
    24     </content>
    26     <implementation>
    27       <field name="_scrollbox">
    28         document.getAnonymousElementByAttribute(this, "anonid", "main-box");
    29       </field>
    30       <field name="scrollBoxObject">
    31         this._scrollbox.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
    32       </field>
    33       <constructor>
    34         <![CDATA[
    35           // add a template build listener
    36           if (this.builder)
    37             this.builder.addListener(this._builderListener);
    38           else
    39             this._refreshSelection();
    40         ]]>
    41       </constructor>
    43       <destructor>
    44         <![CDATA[
    45           // remove the template build listener
    46           if (this.builder)
    47             this.builder.removeListener(this._builderListener);
    48         ]]>
    49       </destructor>
    51     <!-- Overriding baselistbox -->
    52       <method name="_fireOnSelect">
    53         <body>
    54           <![CDATA[
    55             // make sure not to modify last-selected when suppressing select events
    56             // (otherwise we'll lose the selection when a template gets rebuilt)
    57             if (this._suppressOnSelect || this.suppressOnSelect)
    58               return;
    60             // remember the current item and all selected items with IDs
    61             var state = this.currentItem ? this.currentItem.id : "";
    62             if (this.selType == "multiple" && this.selectedCount) {
    63               let getId = function getId(aItem) { return aItem.id; }
    64               state += " " + this.selectedItems.filter(getId).map(getId).join(" ");
    65             }
    66             if (state)
    67               this.setAttribute("last-selected", state);
    68             else
    69               this.removeAttribute("last-selected");
    71             // preserve the index just in case no IDs are available
    72             if (this.currentIndex > -1)
    73               this._currentIndex = this.currentIndex + 1;
    75             var event = document.createEvent("Events");
    76             event.initEvent("select", true, true);
    77             this.dispatchEvent(event);
    79             // always call this (allows a commandupdater without controller)
    80             document.commandDispatcher.updateCommands("richlistbox-select");
    81           ]]>
    82         </body>
    83       </method>
    85       <!-- We override base-listbox here because those methods don't take dir
    86            into account on listbox (which doesn't support dir yet) -->
    87       <method name="getNextItem">
    88         <parameter name="aStartItem"/>
    89         <parameter name="aDelta"/>
    90         <body>
    91         <![CDATA[
    92           var prop = this.dir == "reverse" && this._mayReverse ?
    93                                                 "previousSibling" :
    94                                                 "nextSibling";
    95           while (aStartItem) {
    96             aStartItem = aStartItem[prop];
    97             if (aStartItem && aStartItem instanceof
    98                 Components.interfaces.nsIDOMXULSelectControlItemElement &&
    99                 (!this._userSelecting || this._canUserSelect(aStartItem))) {
   100               --aDelta;
   101               if (aDelta == 0)
   102                 return aStartItem;
   103             }
   104           }
   105           return null;
   106         ]]></body>
   107       </method>
   109       <method name="getPreviousItem">
   110         <parameter name="aStartItem"/>
   111         <parameter name="aDelta"/>
   112         <body>
   113         <![CDATA[
   114           var prop = this.dir == "reverse" && this._mayReverse ?
   115                                                 "nextSibling" :
   116                                                 "previousSibling";
   117           while (aStartItem) {
   118             aStartItem = aStartItem[prop];
   119             if (aStartItem && aStartItem instanceof
   120                 Components.interfaces.nsIDOMXULSelectControlItemElement &&
   121                 (!this._userSelecting || this._canUserSelect(aStartItem))) {
   122               --aDelta;
   123               if (aDelta == 0)
   124                 return aStartItem;
   125             }
   126           }
   127           return null;
   128         ]]>
   129         </body>
   130       </method>
   132       <method name="appendItem">
   133         <parameter name="aLabel"/>
   134         <parameter name="aValue"/>
   135         <body>
   136           return this.insertItemAt(-1, aLabel, aValue);
   137         </body>
   138       </method>
   140       <method name="insertItemAt">
   141         <parameter name="aIndex"/>
   142         <parameter name="aLabel"/>
   143         <parameter name="aValue"/>
   144         <body>
   145           const XULNS =
   146             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   148           var item =
   149             this.ownerDocument.createElementNS(XULNS, "richlistitem");
   150           item.setAttribute("value", aValue);
   152           var label = this.ownerDocument.createElementNS(XULNS, "label");
   153           label.setAttribute("value", aLabel);
   154           label.setAttribute("flex", "1");
   155           label.setAttribute("crop", "end");
   156           item.appendChild(label);
   158           var before = this.getItemAtIndex(aIndex);
   159           if (!before)
   160             this.appendChild(item);
   161           else
   162             this.insertBefore(item, before);
   164           return item;
   165         </body>
   166       </method>
   168       <property name="itemCount" readonly="true"
   169                 onget="return this.children.length"/>
   171       <method name="getIndexOfItem">
   172         <parameter name="aItem"/>
   173         <body>
   174           <![CDATA[
   175             // don't search the children, if we're looking for none of them
   176             if (aItem == null)
   177               return -1;
   179             return this.children.indexOf(aItem);
   180           ]]>
   181         </body>
   182       </method>
   184       <method name="getItemAtIndex">
   185         <parameter name="aIndex"/>
   186         <body>
   187           return this.children[aIndex] || null;
   188         </body>
   189       </method>
   191       <method name="ensureIndexIsVisible">
   192         <parameter name="aIndex"/>
   193         <body>
   194           <![CDATA[
   195             // work around missing implementation in scrollBoxObject
   196             return this.ensureElementIsVisible(this.getItemAtIndex(aIndex));
   197           ]]>
   198         </body>
   199       </method>
   201       <method name="ensureElementIsVisible">
   202         <parameter name="aElement"/>
   203         <body>
   204           <![CDATA[
   205             if (!aElement)
   206               return;
   207             var targetRect = aElement.getBoundingClientRect();
   208             var scrollRect = this._scrollbox.getBoundingClientRect();
   209             var offset = targetRect.top - scrollRect.top;
   210             if (offset >= 0) {
   211               // scrollRect.bottom wouldn't take a horizontal scroll bar into account
   212               let scrollRectBottom = scrollRect.top + this._scrollbox.clientHeight;
   213               offset = targetRect.bottom - scrollRectBottom;
   214               if (offset <= 0)
   215                 return;
   216             }
   217             this._scrollbox.scrollTop += offset;
   218           ]]>
   219         </body>
   220       </method>
   222       <method name="scrollToIndex">
   223         <parameter name="aIndex"/>
   224         <body>
   225           <![CDATA[
   226             var item = this.getItemAtIndex(aIndex);
   227             if (item)
   228               this.scrollBoxObject.scrollToElement(item);
   229           ]]>
   230         </body>
   231       </method>
   233       <method name="getNumberOfVisibleRows">
   234         <!-- returns the number of currently visible rows                -->
   235         <!-- don't rely on this function, if the items' height can vary! -->
   236         <body>
   237           <![CDATA[
   238             var children = this.children;
   240             for (var top = 0; top < children.length && !this._isItemVisible(children[top]); top++);
   241             for (var ix = top; ix < children.length && this._isItemVisible(children[ix]); ix++);
   243             return ix - top;
   244           ]]>
   245         </body>
   246       </method>
   248       <method name="getIndexOfFirstVisibleRow">
   249         <body>
   250           <![CDATA[
   251             var children = this.children;
   253             for (var ix = 0; ix < children.length; ix++)
   254               if (this._isItemVisible(children[ix]))
   255                 return ix;
   257             return -1;
   258           ]]>
   259         </body>
   260       </method>
   262       <method name="getRowCount">
   263         <body>
   264           <![CDATA[
   265             return this.children.length;
   266           ]]>
   267         </body>
   268       </method>
   270       <method name="scrollOnePage">
   271         <parameter name="aDirection"/> <!-- Must be -1 or 1 -->
   272         <body>
   273           <![CDATA[
   274             var children = this.children;
   276             if (children.length == 0)
   277               return 0;
   279             // If nothing is selected, we just select the first element
   280             // at the extreme we're moving away from
   281             if (!this.currentItem)
   282               return aDirection == -1 ? children.length : 0;
   284             // If the current item is visible, scroll by one page so that
   285             // the new current item is at approximately the same position as
   286             // the existing current item.
   287             if (this._isItemVisible(this.currentItem))
   288               this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
   290             // Figure out, how many items fully fit into the view port
   291             // (including the currently selected one), and determine
   292             // the index of the first one lying (partially) outside
   293             var height = this.scrollBoxObject.height;
   294             var startBorder = this.currentItem.boxObject.y;
   295             if (aDirection == -1)
   296               startBorder += this.currentItem.boxObject.height;
   298             var index = this.currentIndex;
   299             for (var ix = index; 0 <= ix && ix < children.length; ix += aDirection) {
   300               var boxObject = children[ix].boxObject;
   301               if (boxObject.height == 0)
   302                 continue; // hidden children have a y of 0
   303               var endBorder = boxObject.y + (aDirection == -1 ? boxObject.height : 0);
   304               if ((endBorder - startBorder) * aDirection > height)
   305                 break; // we've reached the desired distance
   306               index = ix;
   307             }
   309             return index != this.currentIndex ? index - this.currentIndex : aDirection;
   310           ]]>
   311         </body>
   312       </method>
   314     <!-- richlistbox specific -->
   315       <property name="children" readonly="true">
   316         <getter>
   317           <![CDATA[
   318             var childNodes = [];
   319             var isReverse = this.dir == "reverse" && this._mayReverse;
   320             var child = isReverse ? this.lastChild : this.firstChild;
   321             var prop = isReverse ? "previousSibling" : "nextSibling";
   322             while (child) {
   323               if (child instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
   324                 childNodes.push(child);
   325               child = child[prop];
   326             }
   327             return childNodes;
   328           ]]>
   329         </getter>
   330       </property>
   332       <field name="_builderListener" readonly="true">
   333         <![CDATA[
   334           ({
   335             mOuter: this,
   336             item: null,
   337             willRebuild: function(builder) { },
   338             didRebuild: function(builder) {
   339               this.mOuter._refreshSelection();
   340             }
   341           });
   342         ]]>
   343       </field>
   345       <method name="_refreshSelection">
   346         <body>
   347           <![CDATA[
   348             // when this method is called, we know that either the currentItem
   349             // and selectedItems we have are null (ctor) or a reference to an
   350             // element no longer in the DOM (template).
   352             // first look for the last-selected attribute
   353             var state = this.getAttribute("last-selected");
   354             if (state) {
   355               var ids = state.split(" ");
   357               var suppressSelect = this._suppressOnSelect;
   358               this._suppressOnSelect = true;
   359               this.clearSelection();
   360               for (var i = 1; i < ids.length; i++) {
   361                 var selectedItem = document.getElementById(ids[i]);
   362                 if (selectedItem)
   363                   this.addItemToSelection(selectedItem);
   364               }
   366               var currentItem = document.getElementById(ids[0]);
   367               if (!currentItem && this._currentIndex)
   368                 currentItem = this.getItemAtIndex(Math.min(
   369                   this._currentIndex - 1, this.getRowCount()));
   370               if (currentItem) {
   371                 this.currentItem = currentItem;
   372                 if (this.selType != "multiple" && this.selectedCount == 0)
   373                   this.selectedItem = currentItem;
   375                 if (this.scrollBoxObject.height) {
   376                   this.ensureElementIsVisible(currentItem);
   377                 }
   378                 else {
   379                   // XXX hack around a bug in ensureElementIsVisible as it will
   380                   // scroll beyond the last element, bug 493645.
   381                   var previousElement = this.dir == "reverse" ? currentItem.nextSibling :
   382                                                                 currentItem.previousSibling;
   383                   this.ensureElementIsVisible(previousElement);
   384                 }
   385               }
   386               this._suppressOnSelect = suppressSelect;
   387               // XXX actually it's just a refresh, but at least
   388               // the Extensions manager expects this:
   389               this._fireOnSelect();
   390               return;
   391             }
   393             // try to restore the selected items according to their IDs
   394             // (applies after a template rebuild, if last-selected was not set)
   395             if (this.selectedItems) {
   396               for (i = this.selectedCount - 1; i >= 0; i--) {
   397                 if (this.selectedItems[i] && this.selectedItems[i].id)
   398                   this.selectedItems[i] = document.getElementById(this.selectedItems[i].id);
   399                 else
   400                   this.selectedItems[i] = null;
   401                 if (!this.selectedItems[i])
   402                   this.selectedItems.splice(i, 1);
   403               }
   404             }
   405             if (this.currentItem && this.currentItem.id)
   406               this.currentItem = document.getElementById(this.currentItem.id);
   407             else
   408               this.currentItem = null;
   410             // if we have no previously current item or if the above check fails to
   411             // find the previous nodes (which causes it to clear selection)
   412             if (!this.currentItem && this.selectedCount == 0) {
   413               this.currentIndex = this._currentIndex ? this._currentIndex - 1 : 0;
   415               // cf. listbox constructor:
   416               // select items according to their attributes
   417               var children = this.children;
   418               for (var i = 0; i < children.length; ++i) {
   419                 if (children[i].getAttribute("selected") == "true")
   420                   this.selectedItems.push(children[i]);
   421               }
   422             }
   424             if (this.selType != "multiple" && this.selectedCount == 0)
   425               this.selectedItem = this.currentItem;
   426           ]]>
   427         </body>
   428       </method>
   430       <method name="_isItemVisible">
   431         <parameter name="aItem"/>
   432         <body>
   433           <![CDATA[
   434             if (!aItem)
   435               return false;
   437             var y = {};
   438             this.scrollBoxObject.getPosition({}, y);
   439             y.value += this.scrollBoxObject.y;
   441             // Partially visible items are also considered visible
   442             return (aItem.boxObject.y + aItem.boxObject.height > y.value) &&
   443                    (aItem.boxObject.y < y.value + this.scrollBoxObject.height);
   444           ]]>
   445         </body>
   446       </method>
   448       <field name="_currentIndex">null</field>
   450       <!-- For backwards-compatibility and for convenience.
   451         Use getIndexOfItem instead. -->
   452       <method name="getIndexOf">
   453         <parameter name="aElement"/>
   454         <body>
   455           <![CDATA[
   456             return this.getIndexOfItem(aElement);
   457           ]]>
   458         </body>
   459       </method>
   461       <!-- For backwards-compatibility and for convenience.
   462         Use ensureElementIsVisible instead -->
   463       <method name="ensureSelectedElementIsVisible">
   464         <body>
   465           <![CDATA[
   466             return this.ensureElementIsVisible(this.selectedItem);
   467           ]]>
   468         </body>
   469       </method>
   471       <!-- For backwards-compatibility and for convenience.
   472         Use moveByOffset instead. -->
   473       <method name="goUp">
   474         <body>
   475           <![CDATA[
   476             var index = this.currentIndex;
   477             this.moveByOffset(-1, true, false);
   478             return index != this.currentIndex;
   479           ]]>
   480         </body>
   481       </method>
   482       <method name="goDown">
   483         <body>
   484           <![CDATA[
   485             var index = this.currentIndex;
   486             this.moveByOffset(1, true, false);
   487             return index != this.currentIndex;
   488           ]]>
   489         </body>
   490       </method>
   492       <!-- deprecated (is implied by currentItem and selectItem) -->
   493       <method name="fireActiveItemEvent"><body/></method>
   494     </implementation>
   496     <handlers>
   497       <handler event="click">
   498         <![CDATA[
   499           // clicking into nothing should unselect
   500           if (event.originalTarget == this._scrollbox) {
   501             this.clearSelection();
   502             this.currentItem = null;
   503           }
   504         ]]>
   505       </handler>
   507       <handler event="MozSwipeGesture">
   508         <![CDATA[
   509           // Only handle swipe gestures up and down
   510           switch (event.direction) {
   511             case event.DIRECTION_DOWN:
   512               this._scrollbox.scrollTop = this._scrollbox.scrollHeight;
   513               break;
   514             case event.DIRECTION_UP:
   515               this._scrollbox.scrollTop = 0;
   516               break;
   517           }
   518         ]]>
   519       </handler>
   520     </handlers>
   521   </binding>
   523   <binding id="richlistitem"
   524            extends="chrome://global/content/bindings/listbox.xml#listitem">
   525     <content>
   526       <children/>
   527     </content>
   529     <resources>
   530       <stylesheet src="chrome://global/skin/richlistbox.css"/>
   531     </resources>
   533     <implementation>
   534       <destructor>
   535         <![CDATA[
   536           var control = this.control;
   537           if (!control)
   538             return;
   539           // When we are destructed and we are current or selected, unselect ourselves
   540           // so that richlistbox's selection doesn't point to something not in the DOM.
   541           // We don't want to reset last-selected, so we set _suppressOnSelect.
   542           if (this.selected) {
   543             var suppressSelect = control._suppressOnSelect;
   544             control._suppressOnSelect = true;
   545             control.removeItemFromSelection(this);
   546             control._suppressOnSelect = suppressSelect;
   547           }
   548           if (this.current)
   549             control.currentItem = null;
   550         ]]>
   551       </destructor>
   553       <property name="label" readonly="true">
   554         <!-- Setter purposely not implemented; the getter returns a
   555              concatentation of label text to expose via accessibility APIs -->
   556         <getter>
   557           <![CDATA[
   558             const XULNS =
   559               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   560             return Array.map(this.getElementsByTagNameNS(XULNS, "label"),
   561                              function (label) label.value)
   562                         .join(" ");
   563           ]]>
   564         </getter>
   565       </property>
   567       <property name="searchLabel">
   568         <getter>
   569           <![CDATA[
   570             return this.hasAttribute("searchlabel") ?
   571                    this.getAttribute("searchlabel") : this.label;
   572           ]]>
   573         </getter>
   574         <setter>
   575           <![CDATA[
   576             if (val !== null)
   577               this.setAttribute("searchlabel", val);
   578             else
   579               // fall back to the label property (default value)
   580               this.removeAttribute("searchlabel");
   581             return val;
   582           ]]>
   583         </setter>
   584       </property>
   585     </implementation>
   586   </binding>
   587 </bindings>

mercurial