Sat, 03 Jan 2015 20:18:00 +0100
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>