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"?>
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/. -->
7 <bindings id="arrowscrollboxBindings"
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="scrollbox-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
13 <resources>
14 <stylesheet src="chrome://global/skin/scrollbox.css"/>
15 </resources>
16 </binding>
18 <binding id="scrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
19 <content>
20 <xul:box class="box-inherit scrollbox-innerbox" xbl:inherits="orient,align,pack,dir" flex="1">
21 <children/>
22 </xul:box>
23 </content>
24 </binding>
26 <binding id="arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
27 <content>
28 <xul:autorepeatbutton class="autorepeatbutton-up"
29 anonid="scrollbutton-up"
30 collapsed="true"
31 xbl:inherits="orient"
32 oncommand="_autorepeatbuttonScroll(event);"/>
33 <xul:scrollbox class="arrowscrollbox-scrollbox"
34 anonid="scrollbox"
35 flex="1"
36 xbl:inherits="orient,align,pack,dir">
37 <children/>
38 </xul:scrollbox>
39 <xul:autorepeatbutton class="autorepeatbutton-down"
40 anonid="scrollbutton-down"
41 collapsed="true"
42 xbl:inherits="orient"
43 oncommand="_autorepeatbuttonScroll(event);"/>
44 </content>
46 <implementation>
47 <destructor><![CDATA[
48 this._stopSmoothScroll();
49 ]]></destructor>
51 <field name="_scrollbox">
52 document.getAnonymousElementByAttribute(this, "anonid", "scrollbox");
53 </field>
54 <field name="_scrollButtonUp">
55 document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-up");
56 </field>
57 <field name="_scrollButtonDown">
58 document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-down");
59 </field>
61 <field name="__prefBranch">null</field>
62 <property name="_prefBranch" readonly="true">
63 <getter><![CDATA[
64 if (this.__prefBranch === null) {
65 this.__prefBranch = Components.classes['@mozilla.org/preferences-service;1']
66 .getService(Components.interfaces.nsIPrefBranch);
67 }
68 return this.__prefBranch;
69 ]]></getter>
70 </property>
72 <field name="_scrollIncrement">null</field>
73 <property name="scrollIncrement" readonly="true">
74 <getter><![CDATA[
75 if (this._scrollIncrement === null) {
76 try {
77 this._scrollIncrement = this._prefBranch
78 .getIntPref("toolkit.scrollbox.scrollIncrement");
79 }
80 catch (ex) {
81 this._scrollIncrement = 20;
82 }
83 }
84 return this._scrollIncrement;
85 ]]></getter>
86 </property>
88 <field name="_smoothScroll">null</field>
89 <property name="smoothScroll">
90 <getter><![CDATA[
91 if (this._smoothScroll === null) {
92 if (this.hasAttribute("smoothscroll")) {
93 this._smoothScroll = (this.getAttribute("smoothscroll") == "true");
94 } else {
95 try {
96 this._smoothScroll = this._prefBranch
97 .getBoolPref("toolkit.scrollbox.smoothScroll");
98 }
99 catch (ex) {
100 this._smoothScroll = true;
101 }
102 }
103 }
104 return this._smoothScroll;
105 ]]></getter>
106 <setter><![CDATA[
107 this._smoothScroll = val;
108 return val;
109 ]]></setter>
110 </property>
112 <field name="_scrollBoxObject">null</field>
113 <property name="scrollBoxObject" readonly="true">
114 <getter><![CDATA[
115 if (!this._scrollBoxObject) {
116 this._scrollBoxObject =
117 this._scrollbox.boxObject
118 .QueryInterface(Components.interfaces.nsIScrollBoxObject);
119 }
120 return this._scrollBoxObject;
121 ]]></getter>
122 </property>
124 <property name="scrollClientRect" readonly="true">
125 <getter><![CDATA[
126 return this._scrollbox.getBoundingClientRect();
127 ]]></getter>
128 </property>
130 <property name="scrollClientSize" readonly="true">
131 <getter><![CDATA[
132 return this.orient == "vertical" ?
133 this._scrollbox.clientHeight :
134 this._scrollbox.clientWidth;
135 ]]></getter>
136 </property>
138 <property name="scrollSize" readonly="true">
139 <getter><![CDATA[
140 return this.orient == "vertical" ?
141 this._scrollbox.scrollHeight :
142 this._scrollbox.scrollWidth;
143 ]]></getter>
144 </property>
145 <property name="scrollPaddingRect" readonly="true">
146 <getter><![CDATA[
147 // This assumes that this._scrollbox doesn't have any border.
148 var outerRect = this.scrollClientRect;
149 var innerRect = {};
150 innerRect.left = outerRect.left - this._scrollbox.scrollLeft;
151 innerRect.top = outerRect.top - this._scrollbox.scrollTop;
152 innerRect.right = innerRect.left + this._scrollbox.scrollWidth;
153 innerRect.bottom = innerRect.top + this._scrollbox.scrollHeight;
154 return innerRect;
155 ]]></getter>
156 </property>
157 <property name="scrollboxPaddingStart" readonly="true">
158 <getter><![CDATA[
159 var ltr = (window.getComputedStyle(this, null).direction == "ltr");
160 var paddingStartName = ltr ? "padding-left" : "padding-right";
161 var scrollboxStyle = window.getComputedStyle(this._scrollbox, null);
162 return parseFloat(scrollboxStyle.getPropertyValue(paddingStartName));
163 ]]></getter>
164 </property>
165 <property name="scrollPosition">
166 <getter><![CDATA[
167 return this.orient == "vertical" ?
168 this._scrollbox.scrollTop :
169 this._scrollbox.scrollLeft;
170 ]]></getter>
171 <setter><![CDATA[
172 if (this.orient == "vertical")
173 this._scrollbox.scrollTop = val;
174 else
175 this._scrollbox.scrollLeft = val;
176 return val;
177 ]]></setter>
178 </property>
180 <property name="_startEndProps" readonly="true">
181 <getter><![CDATA[
182 return this.orient == "vertical" ?
183 ["top", "bottom"] : ["left", "right"];
184 ]]></getter>
185 </property>
187 <field name="_isRTLScrollbox"><![CDATA[
188 this.orient != "vertical" &&
189 document.defaultView.getComputedStyle(this._scrollbox, "").direction == "rtl";
190 ]]></field>
192 <field name="_scrollTarget">null</field>
194 <method name="_canScrollToElement">
195 <parameter name="element"/>
196 <body><![CDATA[
197 return window.getComputedStyle(element).display != "none";
198 ]]></body>
199 </method>
201 <method name="ensureElementIsVisible">
202 <parameter name="element"/>
203 <parameter name="aSmoothScroll"/>
204 <body><![CDATA[
205 if (!this._canScrollToElement(element))
206 return;
208 var vertical = this.orient == "vertical";
209 var rect = this.scrollClientRect;
210 var containerStart = vertical ? rect.top : rect.left;
211 var containerEnd = vertical ? rect.bottom : rect.right;
212 rect = element.getBoundingClientRect();
213 var elementStart = vertical ? rect.top : rect.left;
214 var elementEnd = vertical ? rect.bottom : rect.right;
216 var scrollPaddingRect = this.scrollPaddingRect;
217 let style = window.getComputedStyle(this._scrollbox, null);
218 var scrollContentRect = {
219 left: scrollPaddingRect.left + parseFloat(style.paddingLeft),
220 top: scrollPaddingRect.top + parseFloat(style.paddingTop),
221 right: scrollPaddingRect.right - parseFloat(style.paddingRight),
222 bottom: scrollPaddingRect.bottom - parseFloat(style.paddingBottom)
223 };
225 // Provide an entry point for derived bindings to adjust these values.
226 if (this._adjustElementStartAndEnd) {
227 [elementStart, elementEnd] =
228 this._adjustElementStartAndEnd(element, elementStart, elementEnd);
229 }
231 if (elementStart <= (vertical ? scrollContentRect.top : scrollContentRect.left)) {
232 elementStart = vertical ? scrollPaddingRect.top : scrollPaddingRect.left;
233 }
234 if (elementEnd >= (vertical ? scrollContentRect.bottom : scrollContentRect.right)) {
235 elementEnd = vertical ? scrollPaddingRect.bottom : scrollPaddingRect.right;
236 }
238 var amountToScroll;
240 if (elementStart < containerStart) {
241 amountToScroll = elementStart - containerStart;
242 } else if (containerEnd < elementEnd) {
243 amountToScroll = elementEnd - containerEnd;
244 } else if (this._isScrolling) {
245 // decelerate if a currently-visible element is selected during the scroll
246 const STOP_DISTANCE = 15;
247 if (this._isScrolling == -1 && elementStart - STOP_DISTANCE < containerStart)
248 amountToScroll = elementStart - containerStart;
249 else if (this._isScrolling == 1 && containerEnd - STOP_DISTANCE < elementEnd)
250 amountToScroll = elementEnd - containerEnd;
251 else
252 amountToScroll = this._isScrolling * STOP_DISTANCE;
253 } else {
254 return;
255 }
257 this._stopSmoothScroll();
259 if (aSmoothScroll != false && this.smoothScroll) {
260 this._smoothScrollByPixels(amountToScroll, element);
261 } else {
262 this.scrollByPixels(amountToScroll);
263 }
264 ]]></body>
265 </method>
267 <method name="_smoothScrollByPixels">
268 <parameter name="amountToScroll"/>
269 <parameter name="element"/><!-- optional -->
270 <body><![CDATA[
271 this._stopSmoothScroll();
272 if (amountToScroll == 0)
273 return;
275 this._scrollTarget = element;
276 // Positive amountToScroll makes us scroll right (elements fly left), negative scrolls left.
277 this._isScrolling = amountToScroll < 0 ? -1 : 1;
279 this._scrollAnim.start(amountToScroll);
280 ]]></body>
281 </method>
283 <field name="_scrollAnim"><![CDATA[({
284 scrollbox: this,
285 requestHandle: 0, /* 0 indicates there is no pending request */
286 start: function scrollAnim_start(distance) {
287 this.distance = distance;
288 this.startPos = this.scrollbox.scrollPosition;
289 this.duration = Math.min(1000, Math.round(50 * Math.sqrt(Math.abs(distance))));
290 this.startTime = window.mozAnimationStartTime;
292 if (!this.requestHandle)
293 this.requestHandle = window.mozRequestAnimationFrame(this);
294 },
295 stop: function scrollAnim_stop() {
296 window.mozCancelAnimationFrame(this.requestHandle);
297 this.requestHandle = 0;
298 },
299 sample: function scrollAnim_handleEvent(timeStamp) {
300 const timePassed = timeStamp - this.startTime;
301 const pos = timePassed >= this.duration ? 1 :
302 1 - Math.pow(1 - timePassed / this.duration, 4);
304 this.scrollbox.scrollPosition = this.startPos + (this.distance * pos);
306 if (pos == 1)
307 this.scrollbox._stopSmoothScroll();
308 else
309 this.requestHandle = window.mozRequestAnimationFrame(this);
310 }
311 })]]></field>
313 <method name="scrollByIndex">
314 <parameter name="index"/>
315 <parameter name="aSmoothScroll"/>
316 <body><![CDATA[
317 if (index == 0)
318 return;
320 // Each scrollByIndex call is expected to scroll the given number of
321 // items. If a previous call is still in progress because of smooth
322 // scrolling, we need to complete it before starting a new one.
323 if (this._scrollTarget) {
324 let elements = this._getScrollableElements();
325 if (this._scrollTarget != elements[0] &&
326 this._scrollTarget != elements[elements.length - 1])
327 this.ensureElementIsVisible(this._scrollTarget, false);
328 }
330 var rect = this.scrollClientRect;
331 var [start, end] = this._startEndProps;
332 var x = index > 0 ? rect[end] + 1 : rect[start] - 1;
333 var nextElement = this._elementFromPoint(x, index);
334 if (!nextElement)
335 return;
337 var targetElement;
338 if (this._isRTLScrollbox)
339 index *= -1;
340 while (index < 0 && nextElement) {
341 if (this._canScrollToElement(nextElement))
342 targetElement = nextElement;
343 nextElement = nextElement.previousSibling;
344 index++;
345 }
346 while (index > 0 && nextElement) {
347 if (this._canScrollToElement(nextElement))
348 targetElement = nextElement;
349 nextElement = nextElement.nextSibling;
350 index--;
351 }
352 if (!targetElement)
353 return;
355 this.ensureElementIsVisible(targetElement, aSmoothScroll);
356 ]]></body>
357 </method>
359 <method name="_getScrollableElements">
360 <body><![CDATA[
361 var nodes = this.childNodes;
362 if (nodes.length == 1 &&
363 nodes[0].localName == "children" &&
364 nodes[0].namespaceURI == "http://www.mozilla.org/xbl") {
365 nodes = document.getBindingParent(this).childNodes;
366 }
368 return Array.filter(nodes, this._canScrollToElement, this);
369 ]]></body>
370 </method>
372 <method name="_elementFromPoint">
373 <parameter name="aX"/>
374 <parameter name="aPhysicalScrollDir"/>
375 <body><![CDATA[
376 var elements = this._getScrollableElements();
377 if (!elements.length)
378 return null;
380 if (this._isRTLScrollbox)
381 elements.reverse();
383 var [start, end] = this._startEndProps;
384 var low = 0;
385 var high = elements.length - 1;
387 if (aX < elements[low].getBoundingClientRect()[start] ||
388 aX > elements[high].getBoundingClientRect()[end])
389 return null;
391 var mid, rect;
392 while (low <= high) {
393 mid = Math.floor((low + high) / 2);
394 rect = elements[mid].getBoundingClientRect();
395 if (rect[start] > aX)
396 high = mid - 1;
397 else if (rect[end] < aX)
398 low = mid + 1;
399 else
400 return elements[mid];
401 }
403 // There's no element at the requested coordinate, but the algorithm
404 // from above yields an element next to it, in a random direction.
405 // The desired scrolling direction leads to the correct element.
407 if (!aPhysicalScrollDir)
408 return null;
410 if (aPhysicalScrollDir < 0 && rect[start] > aX)
411 mid = Math.max(mid - 1, 0);
412 else if (aPhysicalScrollDir > 0 && rect[end] < aX)
413 mid = Math.min(mid + 1, elements.length - 1);
415 return elements[mid];
416 ]]></body>
417 </method>
419 <method name="_autorepeatbuttonScroll">
420 <parameter name="event"/>
421 <body><![CDATA[
422 var dir = event.originalTarget == this._scrollButtonUp ? -1 : 1;
423 if (this._isRTLScrollbox)
424 dir *= -1;
426 this.scrollByPixels(this.scrollIncrement * dir);
428 event.stopPropagation();
429 ]]></body>
430 </method>
432 <method name="scrollByPixels">
433 <parameter name="px"/>
434 <body><![CDATA[
435 this.scrollPosition += px;
436 ]]></body>
437 </method>
439 <!-- 0: idle
440 1: scrolling right
441 -1: scrolling left -->
442 <field name="_isScrolling">0</field>
443 <field name="_prevMouseScrolls">[null, null]</field>
445 <method name="_stopSmoothScroll">
446 <body><![CDATA[
447 if (this._isScrolling) {
448 this._scrollAnim.stop();
449 this._isScrolling = 0;
450 this._scrollTarget = null;
451 }
452 ]]></body>
453 </method>
455 <method name="_updateScrollButtonsDisabledState">
456 <body><![CDATA[
457 var disableUpButton = false;
458 var disableDownButton = false;
460 if (this.scrollPosition == 0) {
461 // In the RTL case, this means the _last_ element in the
462 // scrollbox is visible
463 if (this._isRTLScrollbox)
464 disableDownButton = true;
465 else
466 disableUpButton = true;
467 }
468 else if (this.scrollClientSize + this.scrollPosition == this.scrollSize) {
469 // In the RTL case, this means the _first_ element in the
470 // scrollbox is visible
471 if (this._isRTLScrollbox)
472 disableUpButton = true;
473 else
474 disableDownButton = true;
475 }
477 this._scrollButtonUp.disabled = disableUpButton;
478 this._scrollButtonDown.disabled = disableDownButton;
479 ]]></body>
480 </method>
481 </implementation>
483 <handlers>
484 <handler event="DOMMouseScroll"><![CDATA[
485 if (this.orient == "vertical") {
486 // prevent horizontal scrolling from scrolling a vertical scrollbox
487 if (event.axis == event.HORIZONTAL_AXIS)
488 return;
489 this.scrollByIndex(event.detail);
490 }
491 // We allow vertical scrolling to scroll a horizontal scrollbox
492 // because many users have a vertical scroll wheel but no
493 // horizontal support.
494 // Because of this, we need to avoid scrolling chaos on trackpads
495 // and mouse wheels that support simultaneous scrolling in both axes.
496 // We do this by scrolling only when the last two scroll events were
497 // on the same axis as the current scroll event.
498 else {
499 let isVertical = event.axis == event.VERTICAL_AXIS;
501 if (this._prevMouseScrolls.every(function(prev) prev == isVertical))
502 this.scrollByIndex(isVertical && this._isRTLScrollbox ? -event.detail :
503 event.detail);
505 if (this._prevMouseScrolls.length > 1)
506 this._prevMouseScrolls.shift();
507 this._prevMouseScrolls.push(isVertical);
508 }
510 event.stopPropagation();
511 event.preventDefault();
512 ]]></handler>
514 <handler event="MozMousePixelScroll"><![CDATA[
515 event.stopPropagation();
516 event.preventDefault();
517 ]]></handler>
519 <handler event="underflow" phase="capturing"><![CDATA[
520 // filter underflow events which were dispatched on nested scrollboxes
521 if (event.target != this)
522 return;
524 // Ignore events that doesn't match our orientation.
525 // Scrollport event orientation:
526 // 0: vertical
527 // 1: horizontal
528 // 2: both
529 if (this.orient == "vertical") {
530 if (event.detail == 1)
531 return;
532 }
533 else { // horizontal scrollbox
534 if (event.detail == 0)
535 return;
536 }
538 this._scrollButtonUp.collapsed = true;
539 this._scrollButtonDown.collapsed = true;
540 try {
541 // See bug 341047 and comments in overflow handler as to why
542 // try..catch is needed here
543 let childNodes = this._getScrollableElements();
544 if (childNodes && childNodes.length)
545 this.ensureElementIsVisible(childNodes[0], false);
546 }
547 catch(e) {
548 this._scrollButtonUp.collapsed = false;
549 this._scrollButtonDown.collapsed = false;
550 }
551 ]]></handler>
553 <handler event="overflow" phase="capturing"><![CDATA[
554 // filter underflow events which were dispatched on nested scrollboxes
555 if (event.target != this)
556 return;
558 // Ignore events that doesn't match our orientation.
559 // Scrollport event orientation:
560 // 0: vertical
561 // 1: horizontal
562 // 2: both
563 if (this.orient == "vertical") {
564 if (event.detail == 1)
565 return;
566 }
567 else { // horizontal scrollbox
568 if (event.detail == 0)
569 return;
570 }
572 this._scrollButtonUp.collapsed = false;
573 this._scrollButtonDown.collapsed = false;
574 try {
575 // See bug 341047, the overflow event is dispatched when the
576 // scrollbox already is mostly destroyed. This causes some code in
577 // _updateScrollButtonsDisabledState() to throw an error. It also
578 // means that the scrollbarbuttons were uncollapsed when that should
579 // not be happening, because the whole overflow event should not be
580 // happening in that case.
581 this._updateScrollButtonsDisabledState();
582 }
583 catch(e) {
584 this._scrollButtonUp.collapsed = true;
585 this._scrollButtonDown.collapsed = true;
586 }
587 ]]></handler>
589 <handler event="scroll" action="this._updateScrollButtonsDisabledState()"/>
590 </handlers>
591 </binding>
593 <binding id="autorepeatbutton" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
594 <content repeat="hover">
595 <xul:image class="autorepeatbutton-icon"/>
596 </content>
597 </binding>
599 <binding id="arrowscrollbox-clicktoscroll" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox">
600 <content>
601 <xul:toolbarbutton class="scrollbutton-up" collapsed="true"
602 xbl:inherits="orient"
603 anonid="scrollbutton-up"
604 onclick="_distanceScroll(event);"
605 onmousedown="if (event.button == 0) _startScroll(-1);"
606 onmouseup="if (event.button == 0) _stopScroll();"
607 onmouseover="_continueScroll(-1);"
608 onmouseout="_pauseScroll();"/>
609 <xul:scrollbox class="arrowscrollbox-scrollbox"
610 anonid="scrollbox"
611 flex="1"
612 xbl:inherits="orient,align,pack,dir">
613 <children/>
614 </xul:scrollbox>
615 <xul:toolbarbutton class="scrollbutton-down" collapsed="true"
616 xbl:inherits="orient"
617 anonid="scrollbutton-down"
618 onclick="_distanceScroll(event);"
619 onmousedown="if (event.button == 0) _startScroll(1);"
620 onmouseup="if (event.button == 0) _stopScroll();"
621 onmouseover="_continueScroll(1);"
622 onmouseout="_pauseScroll();"/>
623 </content>
624 <implementation implements="nsITimerCallback, nsIDOMEventListener">
625 <constructor><![CDATA[
626 try {
627 this._scrollDelay = this._prefBranch
628 .getIntPref("toolkit.scrollbox.clickToScroll.scrollDelay");
629 }
630 catch (ex) {
631 }
632 ]]></constructor>
634 <destructor><![CDATA[
635 // Release timer to avoid reference cycles.
636 if (this._scrollTimer) {
637 this._scrollTimer.cancel();
638 this._scrollTimer = null;
639 }
640 ]]></destructor>
642 <field name="_scrollIndex">0</field>
643 <field name="_scrollDelay">150</field>
645 <method name="notify">
646 <parameter name="aTimer"/>
647 <body>
648 <![CDATA[
649 if (!document)
650 aTimer.cancel();
652 this.scrollByIndex(this._scrollIndex);
653 ]]>
654 </body>
655 </method>
657 <field name="_arrowScrollAnim"><![CDATA[({
658 scrollbox: this,
659 requestHandle: 0, /* 0 indicates there is no pending request */
660 start: function arrowSmoothScroll_start() {
661 this.lastFrameTime = window.mozAnimationStartTime;
662 if (!this.requestHandle)
663 this.requestHandle = window.mozRequestAnimationFrame(this);
664 },
665 stop: function arrowSmoothScroll_stop() {
666 window.mozCancelAnimationFrame(this.requestHandle);
667 this.requestHandle = 0;
668 },
669 sample: function arrowSmoothScroll_handleEvent(timeStamp) {
670 const scrollIndex = this.scrollbox._scrollIndex;
671 const timePassed = timeStamp - this.lastFrameTime;
672 this.lastFrameTime = timeStamp;
674 const scrollDelta = 0.5 * timePassed * scrollIndex;
675 this.scrollbox.scrollPosition += scrollDelta;
677 this.requestHandle = window.mozRequestAnimationFrame(this);
678 }
679 })]]></field>
681 <method name="_startScroll">
682 <parameter name="index"/>
683 <body><![CDATA[
684 if (this._isRTLScrollbox)
685 index *= -1;
686 this._scrollIndex = index;
687 this._mousedown = true;
688 if (this.smoothScroll) {
689 this._arrowScrollAnim.start();
690 return;
691 }
693 if (!this._scrollTimer)
694 this._scrollTimer =
695 Components.classes["@mozilla.org/timer;1"]
696 .createInstance(Components.interfaces.nsITimer);
697 else
698 this._scrollTimer.cancel();
700 this._scrollTimer.initWithCallback(this, this._scrollDelay,
701 this._scrollTimer.TYPE_REPEATING_SLACK);
702 this.notify(this._scrollTimer);
703 ]]>
704 </body>
705 </method>
707 <method name="_stopScroll">
708 <body><![CDATA[
709 if (this._scrollTimer)
710 this._scrollTimer.cancel();
711 this._mousedown = false;
712 if (!this._scrollIndex || !this.smoothScroll)
713 return;
715 this.scrollByIndex(this._scrollIndex);
716 this._scrollIndex = 0;
717 this._arrowScrollAnim.stop();
718 ]]></body>
719 </method>
721 <method name="_pauseScroll">
722 <body><![CDATA[
723 if (this._mousedown) {
724 this._stopScroll();
725 this._mousedown = true;
726 document.addEventListener("mouseup", this, false);
727 document.addEventListener("blur", this, true);
728 }
729 ]]></body>
730 </method>
732 <method name="_continueScroll">
733 <parameter name="index"/>
734 <body><![CDATA[
735 if (this._mousedown)
736 this._startScroll(index);
737 ]]></body>
738 </method>
740 <method name="handleEvent">
741 <parameter name="aEvent"/>
742 <body><![CDATA[
743 if (aEvent.type == "mouseup" ||
744 aEvent.type == "blur" && aEvent.target == document) {
745 this._mousedown = false;
746 document.removeEventListener("mouseup", this, false);
747 document.removeEventListener("blur", this, true);
748 }
749 ]]></body>
750 </method>
752 <method name="_distanceScroll">
753 <parameter name="aEvent"/>
754 <body><![CDATA[
755 if (aEvent.detail < 2 || aEvent.detail > 3)
756 return;
758 var scrollBack = (aEvent.originalTarget == this._scrollButtonUp);
759 var scrollLeftOrUp = this._isRTLScrollbox ? !scrollBack : scrollBack;
760 var targetElement;
762 if (aEvent.detail == 2) {
763 // scroll by the size of the scrollbox
764 let [start, end] = this._startEndProps;
765 let x;
766 if (scrollLeftOrUp)
767 x = this.scrollClientRect[start] - this.scrollClientSize;
768 else
769 x = this.scrollClientRect[end] + this.scrollClientSize;
770 targetElement = this._elementFromPoint(x, scrollLeftOrUp ? -1 : 1);
772 // the next partly-hidden element will become fully visible,
773 // so don't scroll too far
774 if (targetElement)
775 targetElement = scrollBack ?
776 targetElement.nextSibling :
777 targetElement.previousSibling;
778 }
780 if (!targetElement) {
781 // scroll to the first resp. last element
782 let elements = this._getScrollableElements();
783 targetElement = scrollBack ?
784 elements[0] :
785 elements[elements.length - 1];
786 }
788 this.ensureElementIsVisible(targetElement);
789 ]]></body>
790 </method>
792 </implementation>
793 </binding>
794 </bindings>