toolkit/content/widgets/scrollbox.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.

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

mercurial