Source code

Revision control

Other Tools

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/. -->
5
6
7
<bindings id="arrowscrollboxBindings"
10
xmlns:xbl="http://www.mozilla.org/xbl">
11
12
<binding id="arrowscrollbox" extends="chrome://global/content/bindings/general.xml#basecontrol">
13
<content>
14
<xul:toolbarbutton class="scrollbutton-up"
15
anonid="scrollbutton-up"
16
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtostart"
17
onclick="_onButtonClick(event);"
18
onmousedown="_onButtonMouseDown(event, -1);"
19
onmouseup="_onButtonMouseUp(event);"
20
onmouseover="_onButtonMouseOver(-1);"
21
onmouseout="_onButtonMouseOut();"/>
22
<xul:spacer class="arrowscrollbox-overflow-start-indicator"
23
xbl:inherits="collapsed=scrolledtostart"/>
24
<xul:scrollbox class="arrowscrollbox-scrollbox"
25
anonid="scrollbox"
26
flex="1"
27
xbl:inherits="orient,align,pack,dir,smoothscroll">
28
<children/>
29
</xul:scrollbox>
30
<xul:spacer class="arrowscrollbox-overflow-end-indicator"
31
xbl:inherits="collapsed=scrolledtoend"/>
32
<xul:toolbarbutton class="scrollbutton-down"
33
anonid="scrollbutton-down"
34
xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
35
onclick="_onButtonClick(event);"
36
onmousedown="_onButtonMouseDown(event, 1);"
37
onmouseup="_onButtonMouseUp(event);"
38
onmouseover="_onButtonMouseOver(1);"
39
onmouseout="_onButtonMouseOut();"/>
40
</content>
41
42
<implementation>
43
<constructor><![CDATA[
44
if (!this.hasAttribute("smoothscroll")) {
45
this.smoothScroll = this._prefBranch
46
.getBoolPref("toolkit.scrollbox.smoothScroll", true);
47
}
48
49
this.setAttribute("notoverflowing", "true");
50
this._updateScrollButtonsDisabledState();
51
]]></constructor>
52
53
<destructor><![CDATA[
54
// Release timer to avoid reference cycles.
55
if (this._scrollTimer) {
56
this._scrollTimer.cancel();
57
this._scrollTimer = null;
58
}
59
]]></destructor>
60
61
<field name="scrollbox">
62
document.getAnonymousElementByAttribute(this, "anonid", "scrollbox");
63
</field>
64
<field name="_scrollButtonUp">
65
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-up");
66
</field>
67
<field name="_scrollButtonDown">
68
document.getAnonymousElementByAttribute(this, "anonid", "scrollbutton-down");
69
</field>
70
71
<field name="_scrollIndex">0</field>
72
73
<field name="_arrowScrollAnim"><![CDATA[({
74
scrollbox: this,
75
requestHandle: 0, /* 0 indicates there is no pending request */
76
start: function arrowSmoothScroll_start() {
77
this.lastFrameTime = window.performance.now();
78
if (!this.requestHandle)
79
this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
80
},
81
stop: function arrowSmoothScroll_stop() {
82
window.cancelAnimationFrame(this.requestHandle);
83
this.requestHandle = 0;
84
},
85
sample: function arrowSmoothScroll_handleEvent(timeStamp) {
86
const scrollIndex = this.scrollbox._scrollIndex;
87
const timePassed = timeStamp - this.lastFrameTime;
88
this.lastFrameTime = timeStamp;
89
90
const scrollDelta = 0.5 * timePassed * scrollIndex;
91
this.scrollbox.scrollByPixels(scrollDelta, true);
92
this.requestHandle = window.requestAnimationFrame(this.sample.bind(this));
93
},
94
})]]></field>
95
96
<property name="_clickToScroll" readonly="true">
97
<getter><![CDATA[
98
return this.hasAttribute("clicktoscroll");
99
]]></getter>
100
</property>
101
102
<property name="_scrollDelay" readonly="true">
103
<getter><![CDATA[
104
if (this._clickToScroll) {
105
return this._prefBranch.getIntPref(
106
"toolkit.scrollbox.clickToScroll.scrollDelay", 150);
107
}
108
109
// Use the same REPEAT_DELAY as "nsRepeatService.h".
110
return /Mac/.test(navigator.platform) ? 25 : 50;
111
]]></getter>
112
</property>
113
114
<field name="__prefBranch">null</field>
115
<property name="_prefBranch" readonly="true">
116
<getter><![CDATA[
117
if (this.__prefBranch === null) {
118
this.__prefBranch = Cc["@mozilla.org/preferences-service;1"]
119
.getService(Ci.nsIPrefBranch);
120
}
121
return this.__prefBranch;
122
]]></getter>
123
</property>
124
125
<field name="_scrollIncrement">null</field>
126
<property name="scrollIncrement" readonly="true">
127
<getter><![CDATA[
128
if (this._scrollIncrement === null) {
129
this._scrollIncrement = this._prefBranch
130
.getIntPref("toolkit.scrollbox.scrollIncrement", 20);
131
}
132
return this._scrollIncrement;
133
]]></getter>
134
</property>
135
136
<property name="smoothScroll">
137
<getter><![CDATA[
138
return this.getAttribute("smoothscroll") == "true";
139
]]></getter>
140
<setter><![CDATA[
141
this.setAttribute("smoothscroll", !!val);
142
return val;
143
]]></setter>
144
</property>
145
146
<property name="scrollClientRect" readonly="true">
147
<getter><![CDATA[
148
return this.scrollbox.getBoundingClientRect();
149
]]></getter>
150
</property>
151
152
<property name="scrollClientSize" readonly="true">
153
<getter><![CDATA[
154
return this.orient == "vertical" ?
155
this.scrollbox.clientHeight :
156
this.scrollbox.clientWidth;
157
]]></getter>
158
</property>
159
160
<property name="scrollSize" readonly="true">
161
<getter><![CDATA[
162
return this.orient == "vertical" ?
163
this.scrollbox.scrollHeight :
164
this.scrollbox.scrollWidth;
165
]]></getter>
166
</property>
167
168
<property name="lineScrollAmount" readonly="true">
169
<getter><![CDATA[
170
// line scroll amout should be the width (at horizontal scrollbox) or
171
// the height (at vertical scrollbox) of the scrolled elements.
172
// However, the elements may have different width or height. So,
173
// for consistent speed, let's use avalage with of the elements.
174
var elements = this._getScrollableElements();
175
return elements.length && (this.scrollSize / elements.length);
176
]]></getter>
177
</property>
178
179
<property name="scrollPosition" readonly="true">
180
<getter><![CDATA[
181
return this.orient == "vertical" ?
182
this.scrollbox.scrollTop :
183
this.scrollbox.scrollLeft;
184
]]></getter>
185
</property>
186
187
<field name="_startEndProps"><![CDATA[
188
this.orient == "vertical" ? ["top", "bottom"] : ["left", "right"];
189
]]></field>
190
191
<field name="_isRTLScrollbox"><![CDATA[
192
this.orient != "vertical" &&
193
document.defaultView.getComputedStyle(this.scrollbox).direction == "rtl";
194
]]></field>
195
196
<method name="_onButtonClick">
197
<parameter name="event"/>
198
<body><![CDATA[
199
if (this._clickToScroll) {
200
this._distanceScroll(event);
201
}
202
]]></body>
203
</method>
204
205
<method name="_onButtonMouseDown">
206
<parameter name="event"/>
207
<parameter name="index"/>
208
<body><![CDATA[
209
if (this._clickToScroll && event.button == 0) {
210
this._startScroll(index);
211
}
212
]]></body>
213
</method>
214
215
<method name="_onButtonMouseUp">
216
<parameter name="event"/>
217
<body><![CDATA[
218
if (this._clickToScroll && event.button == 0) {
219
this._stopScroll();
220
}
221
]]></body>
222
</method>
223
224
<method name="_onButtonMouseOver">
225
<parameter name="index"/>
226
<body><![CDATA[
227
if (this._clickToScroll) {
228
this._continueScroll(index);
229
} else {
230
this._startScroll(index);
231
}
232
]]></body>
233
</method>
234
235
<method name="_onButtonMouseOut">
236
<parameter name="index"/>
237
<body><![CDATA[
238
if (this._clickToScroll) {
239
this._pauseScroll();
240
} else {
241
this._stopScroll();
242
}
243
]]></body>
244
</method>
245
246
<method name="_boundsWithoutFlushing">
247
<parameter name="element"/>
248
<body><![CDATA[
249
if (!("_DOMWindowUtils" in this)) {
250
this._DOMWindowUtils = window.windowUtils;
251
}
252
253
return this._DOMWindowUtils ?
254
this._DOMWindowUtils.getBoundsWithoutFlushing(element) :
255
element.getBoundingClientRect();
256
]]></body>
257
</method>
258
259
<method name="_canScrollToElement">
260
<parameter name="element"/>
261
<body><![CDATA[
262
if (element.hidden) {
263
return false;
264
}
265
266
// See if the element is hidden via CSS without the hidden attribute.
267
// If we get only zeros for the client rect, this means the element
268
// is hidden. As a performance optimization, we don't flush layout
269
// here which means that on the fly changes aren't fully supported.
270
let rect = this._boundsWithoutFlushing(element);
271
return !!(rect.top || rect.left || rect.width || rect.height);
272
]]></body>
273
</method>
274
275
<field name="_ensureElementIsVisibleAnimationFrame">0</field>
276
<method name="ensureElementIsVisible">
277
<parameter name="element"/>
278
<parameter name="aInstant"/>
279
<body><![CDATA[
280
if (!this._canScrollToElement(element))
281
return;
282
283
if (this._ensureElementIsVisibleAnimationFrame) {
284
window.cancelAnimationFrame(this._ensureElementIsVisibleAnimationFrame);
285
}
286
this._ensureElementIsVisibleAnimationFrame = window.requestAnimationFrame(() => {
287
element.scrollIntoView({ block: "nearest",
288
behavior: aInstant ? "instant" : "auto" });
289
this._ensureElementIsVisibleAnimationFrame = 0;
290
});
291
]]></body>
292
</method>
293
294
<method name="scrollByIndex">
295
<parameter name="index"/>
296
<parameter name="aInstant"/>
297
<body><![CDATA[
298
if (index == 0)
299
return;
300
301
var rect = this.scrollClientRect;
302
var [start, end] = this._startEndProps;
303
var x = index > 0 ? rect[end] + 1 : rect[start] - 1;
304
var nextElement = this._elementFromPoint(x, index);
305
if (!nextElement)
306
return;
307
308
var targetElement;
309
if (this._isRTLScrollbox)
310
index *= -1;
311
while (index < 0 && nextElement) {
312
if (this._canScrollToElement(nextElement))
313
targetElement = nextElement;
314
nextElement = nextElement.previousElementSibling;
315
index++;
316
}
317
while (index > 0 && nextElement) {
318
if (this._canScrollToElement(nextElement))
319
targetElement = nextElement;
320
nextElement = nextElement.nextElementSibling;
321
index--;
322
}
323
if (!targetElement)
324
return;
325
326
this.ensureElementIsVisible(targetElement, aInstant);
327
]]></body>
328
</method>
329
330
<method name="_getScrollableElements">
331
<body><![CDATA[
332
var nodes = this.children;
333
if (nodes.length == 1 &&
334
nodes[0].localName == "children" &&
335
nodes[0].namespaceURI == "http://www.mozilla.org/xbl") {
336
nodes = document.getBindingParent(this).children;
337
}
338
339
return Array.prototype.filter.call(nodes, this._canScrollToElement, this);
340
]]></body>
341
</method>
342
343
<method name="_elementFromPoint">
344
<parameter name="aX"/>
345
<parameter name="aPhysicalScrollDir"/>
346
<body><![CDATA[
347
var elements = this._getScrollableElements();
348
if (!elements.length)
349
return null;
350
351
if (this._isRTLScrollbox)
352
elements.reverse();
353
354
var [start, end] = this._startEndProps;
355
var low = 0;
356
var high = elements.length - 1;
357
358
if (aX < elements[low].getBoundingClientRect()[start] ||
359
aX > elements[high].getBoundingClientRect()[end])
360
return null;
361
362
var mid, rect;
363
while (low <= high) {
364
mid = Math.floor((low + high) / 2);
365
rect = elements[mid].getBoundingClientRect();
366
if (rect[start] > aX)
367
high = mid - 1;
368
else if (rect[end] < aX)
369
low = mid + 1;
370
else
371
return elements[mid];
372
}
373
374
// There's no element at the requested coordinate, but the algorithm
375
// from above yields an element next to it, in a random direction.
376
// The desired scrolling direction leads to the correct element.
377
378
if (!aPhysicalScrollDir)
379
return null;
380
381
if (aPhysicalScrollDir < 0 && rect[start] > aX)
382
mid = Math.max(mid - 1, 0);
383
else if (aPhysicalScrollDir > 0 && rect[end] < aX)
384
mid = Math.min(mid + 1, elements.length - 1);
385
386
return elements[mid];
387
]]></body>
388
</method>
389
390
<method name="_startScroll">
391
<parameter name="index"/>
392
<body><![CDATA[
393
if (this._isRTLScrollbox) {
394
index *= -1;
395
}
396
397
if (this._clickToScroll) {
398
this._scrollIndex = index;
399
this._mousedown = true;
400
401
if (this.smoothScroll) {
402
this._arrowScrollAnim.start();
403
return;
404
}
405
}
406
407
if (!this._scrollTimer) {
408
this._scrollTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
409
} else {
410
this._scrollTimer.cancel();
411
}
412
413
let callback;
414
if (this._clickToScroll) {
415
callback = () => {
416
if (!document && this._scrollTimer) {
417
this._scrollTimer.cancel();
418
}
419
this.scrollByIndex(this._scrollIndex);
420
};
421
} else {
422
callback = () => this.scrollByPixels(this.scrollIncrement * index);
423
}
424
425
this._scrollTimer.initWithCallback(callback, this._scrollDelay,
426
Ci.nsITimer.TYPE_REPEATING_SLACK);
427
428
callback();
429
]]>
430
</body>
431
</method>
432
433
<method name="_stopScroll">
434
<body><![CDATA[
435
if (this._scrollTimer)
436
this._scrollTimer.cancel();
437
438
if (this._clickToScroll) {
439
this._mousedown = false;
440
if (!this._scrollIndex || !this.smoothScroll)
441
return;
442
443
this.scrollByIndex(this._scrollIndex);
444
this._scrollIndex = 0;
445
446
this._arrowScrollAnim.stop();
447
}
448
]]></body>
449
</method>
450
451
<method name="_pauseScroll">
452
<body><![CDATA[
453
if (this._mousedown) {
454
this._stopScroll();
455
this._mousedown = true;
456
document.addEventListener("mouseup", this);
457
document.addEventListener("blur", this, true);
458
}
459
]]></body>
460
</method>
461
462
<method name="_continueScroll">
463
<parameter name="index"/>
464
<body><![CDATA[
465
if (this._mousedown)
466
this._startScroll(index);
467
]]></body>
468
</method>
469
470
<method name="_distanceScroll">
471
<parameter name="aEvent"/>
472
<body><![CDATA[
473
if (aEvent.detail < 2 || aEvent.detail > 3)
474
return;
475
476
var scrollBack = (aEvent.originalTarget == this._scrollButtonUp);
477
var scrollLeftOrUp = this._isRTLScrollbox ? !scrollBack : scrollBack;
478
var targetElement;
479
480
if (aEvent.detail == 2) {
481
// scroll by the size of the scrollbox
482
let [start, end] = this._startEndProps;
483
let x;
484
if (scrollLeftOrUp)
485
x = this.scrollClientRect[start] - this.scrollClientSize;
486
else
487
x = this.scrollClientRect[end] + this.scrollClientSize;
488
targetElement = this._elementFromPoint(x, scrollLeftOrUp ? -1 : 1);
489
490
// the next partly-hidden element will become fully visible,
491
// so don't scroll too far
492
if (targetElement)
493
targetElement = scrollBack ?
494
targetElement.nextElementSibling :
495
targetElement.previousElementSibling;
496
}
497
498
if (!targetElement) {
499
// scroll to the first resp. last element
500
let elements = this._getScrollableElements();
501
targetElement = scrollBack ?
502
elements[0] :
503
elements[elements.length - 1];
504
}
505
506
this.ensureElementIsVisible(targetElement);
507
]]></body>
508
</method>
509
510
<method name="handleEvent">
511
<parameter name="aEvent"/>
512
<body><![CDATA[
513
if (aEvent.type == "mouseup" ||
514
aEvent.type == "blur" && aEvent.target == document) {
515
this._mousedown = false;
516
document.removeEventListener("mouseup", this);
517
document.removeEventListener("blur", this, true);
518
}
519
]]></body>
520
</method>
521
522
<method name="scrollByPixels">
523
<parameter name="aPixels"/>
524
<parameter name="aInstant"/>
525
<body><![CDATA[
526
let scrollOptions = { behavior: aInstant ? "instant" : "auto" };
527
scrollOptions[this._startEndProps[0]] = aPixels;
528
this.scrollbox.scrollBy(scrollOptions);
529
]]></body>
530
</method>
531
532
<field name="_prevMouseScrolls">[null, null]</field>
533
534
<field name="_touchStart">-1</field>
535
536
<field name="_scrollButtonUpdatePending">false</field>
537
<method name="_updateScrollButtonsDisabledState">
538
<body><![CDATA[
539
if (this.hasAttribute("notoverflowing")) {
540
this.setAttribute("scrolledtoend", "true");
541
this.setAttribute("scrolledtostart", "true");
542
return;
543
}
544
545
if (this._scrollButtonUpdatePending) {
546
return;
547
}
548
this._scrollButtonUpdatePending = true;
549
550
// Wait until after the next paint to get current layout data from
551
// getBoundsWithoutFlushing.
552
window.requestAnimationFrame(() => {
553
setTimeout(() => {
554
if (!this._startEndProps) {
555
// We've been destroyed in the meantime.
556
return;
557
}
558
559
this._scrollButtonUpdatePending = false;
560
561
let scrolledToStart = false;
562
let scrolledToEnd = false;
563
564
if (this.hasAttribute("notoverflowing")) {
565
scrolledToStart = true;
566
scrolledToEnd = true;
567
} else {
568
let [leftOrTop, rightOrBottom] = this._startEndProps;
569
let leftOrTopEdge = ele => Math.round(this._boundsWithoutFlushing(ele)[leftOrTop]);
570
let rightOrBottomEdge = ele => Math.round(this._boundsWithoutFlushing(ele)[rightOrBottom]);
571
572
let elements = this._getScrollableElements();
573
let [leftOrTopElement, rightOrBottomElement] = [elements[0], elements[elements.length - 1]];
574
if (this._isRTLScrollbox) {
575
[leftOrTopElement, rightOrBottomElement] = [rightOrBottomElement, leftOrTopElement];
576
}
577
578
if (leftOrTopElement &&
579
leftOrTopEdge(leftOrTopElement) >= leftOrTopEdge(this.scrollbox)) {
580
scrolledToStart = !this._isRTLScrollbox;
581
scrolledToEnd = this._isRTLScrollbox;
582
} else if (rightOrBottomElement &&
583
rightOrBottomEdge(rightOrBottomElement) <= rightOrBottomEdge(this.scrollbox)) {
584
scrolledToStart = this._isRTLScrollbox;
585
scrolledToEnd = !this._isRTLScrollbox;
586
}
587
}
588
589
if (scrolledToEnd) {
590
this.setAttribute("scrolledtoend", "true");
591
} else {
592
this.removeAttribute("scrolledtoend");
593
}
594
595
if (scrolledToStart) {
596
this.setAttribute("scrolledtostart", "true");
597
} else {
598
this.removeAttribute("scrolledtostart");
599
}
600
}, 0);
601
});
602
]]></body>
603
</method>
604
605
<field name="_isScrolling">false</field>
606
<field name="_destination">0</field>
607
<field name="_direction">0</field>
608
</implementation>
609
610
<handlers>
611
<handler event="wheel"><![CDATA[
612
// Don't consume the event if we can't scroll.
613
if (this.hasAttribute("notoverflowing")) {
614
return;
615
}
616
617
let doScroll = false;
618
let instant;
619
let scrollAmount = 0;
620
if (this.orient == "vertical") {
621
doScroll = true;
622
if (event.deltaMode == event.DOM_DELTA_PIXEL)
623
scrollAmount = event.deltaY;
624
else if (event.deltaMode == event.DOM_DELTA_PAGE)
625
scrollAmount = event.deltaY * this.scrollClientSize;
626
else
627
scrollAmount = event.deltaY * this.lineScrollAmount;
628
} else {
629
// We allow vertical scrolling to scroll a horizontal scrollbox
630
// because many users have a vertical scroll wheel but no
631
// horizontal support.
632
// Because of this, we need to avoid scrolling chaos on trackpads
633
// and mouse wheels that support simultaneous scrolling in both axes.
634
// We do this by scrolling only when the last two scroll events were
635
// on the same axis as the current scroll event.
636
// For diagonal scroll events we only respect the dominant axis.
637
let isVertical = Math.abs(event.deltaY) > Math.abs(event.deltaX);
638
let delta = isVertical ? event.deltaY : event.deltaX;
639
let scrollByDelta = isVertical && this._isRTLScrollbox ? -delta : delta;
640
641
if (this._prevMouseScrolls.every(prev => prev == isVertical)) {
642
doScroll = true;
643
if (event.deltaMode == event.DOM_DELTA_PIXEL) {
644
scrollAmount = scrollByDelta;
645
instant = true;
646
} else if (event.deltaMode == event.DOM_DELTA_PAGE) {
647
scrollAmount = scrollByDelta * this.scrollClientSize;
648
} else {
649
scrollAmount = scrollByDelta * this.lineScrollAmount;
650
}
651
}
652
653
if (this._prevMouseScrolls.length > 1)
654
this._prevMouseScrolls.shift();
655
this._prevMouseScrolls.push(isVertical);
656
}
657
658
if (doScroll) {
659
let direction = scrollAmount < 0 ? -1 : 1;
660
let startPos = this.scrollPosition;
661
662
if (!this._isScrolling || this._direction != direction) {
663
this._destination = startPos + scrollAmount;
664
this._direction = direction;
665
} else {
666
// We were already in the process of scrolling in this direction
667
this._destination = this._destination + scrollAmount;
668
scrollAmount = this._destination - startPos;
669
}
670
this.scrollByPixels(scrollAmount, instant);
671
}
672
673
event.stopPropagation();
674
event.preventDefault();
675
]]></handler>
676
677
<handler event="touchstart"><![CDATA[
678
if (event.touches.length > 1) {
679
// Multiple touch points detected, abort. In particular this aborts
680
// the panning gesture when the user puts a second finger down after
681
// already panning with one finger. Aborting at this point prevents
682
// the pan gesture from being resumed until all fingers are lifted
683
// (as opposed to when the user is back down to one finger).
684
this._touchStart = -1;
685
} else {
686
this._touchStart = (this.orient == "vertical"
687
? event.touches[0].screenY
688
: event.touches[0].screenX);
689
}
690
]]></handler>
691
692
<handler event="touchmove"><![CDATA[
693
if (event.touches.length == 1 &&
694
this._touchStart >= 0) {
695
var touchPoint = (this.orient == "vertical"
696
? event.touches[0].screenY
697
: event.touches[0].screenX);
698
var delta = this._touchStart - touchPoint;
699
if (Math.abs(delta) > 0) {
700
this.scrollByPixels(delta, true);
701
this._touchStart = touchPoint;
702
}
703
event.preventDefault();
704
}
705
]]></handler>
706
707
<handler event="touchend"><![CDATA[
708
this._touchStart = -1;
709
]]></handler>
710
711
<handler event="underflow" phase="capturing"><![CDATA[
712
// Ignore underflow events:
713
// - from nested scrollable elements
714
// - corresponding to an overflow event that we ignored
715
if (event.target != this ||
716
this.hasAttribute("notoverflowing")) {
717
return;
718
}
719
720
// Ignore events that doesn't match our orientation.
721
// Scrollport event orientation:
722
// 0: vertical
723
// 1: horizontal
724
// 2: both
725
if (this.orient == "vertical") {
726
if (event.detail == 1)
727
return;
728
} else if (event.detail == 0) {
729
// horizontal scrollbox
730
return;
731
}
732
733
this.setAttribute("notoverflowing", "true");
734
this._updateScrollButtonsDisabledState();
735
]]></handler>
736
737
<handler event="overflow" phase="capturing"><![CDATA[
738
// Ignore overflow events:
739
// - from nested scrollable elements
740
if (event.target != this) {
741
return;
742
}
743
744
// Ignore events that doesn't match our orientation.
745
// Scrollport event orientation:
746
// 0: vertical
747
// 1: horizontal
748
// 2: both
749
if (this.orient == "vertical") {
750
if (event.detail == 1)
751
return;
752
} else if (event.detail == 0) {
753
// horizontal scrollbox
754
return;
755
}
756
757
this.removeAttribute("notoverflowing");
758
this._updateScrollButtonsDisabledState();
759
]]></handler>
760
761
<handler event="scroll"><![CDATA[
762
this._isScrolling = true;
763
this._updateScrollButtonsDisabledState();
764
]]></handler>
765
766
<handler event="scrollend"><![CDATA[
767
this._isScrolling = false;
768
this._destination = 0;
769
this._direction = 0;
770
]]></handler>
771
</handlers>
772
</binding>
773
</bindings>