Bug 805638 - Find parent node while the inner scrollable region reaches the end. r=vingtetun
authorShih-Chiang Chien <schien@mozilla.com>
Thu, 01 Nov 2012 15:48:45 +0800
changeset 114447 0e44ca36bba8cb53491d10c8717ab2fb8b008c28
parent 114446 706259475ba65a06160683f7d4003f0bf844cc31
child 114448 18038441ff7a69684dce2ef24575cc613a1ac84c
push id18775
push userryanvm@gmail.com
push dateThu, 29 Nov 2012 03:46:54 +0000
treeherdermozilla-inbound@99233fe0b2b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvingtetun
bugs805638
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 805638 - Find parent node while the inner scrollable region reaches the end. r=vingtetun
dom/browser-element/BrowserElementScrolling.js
--- a/dom/browser-element/BrowserElementScrolling.js
+++ b/dom/browser-element/BrowserElementScrolling.js
@@ -120,18 +120,28 @@ const ContentPanning = {
   },
 
   onKineticEnd: function cp_onKineticEnd() {
     if (!this.dragging)
       this.scrollCallback = null;
   },
 
   getPannable: function cp_getPannable(node) {
-    if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML')
-      return [null, null];
+    let pannableNode = this._findPannable(node);
+    if (pannableNode) {
+      return [pannableNode, this._generateCallback(pannableNode)];
+    }
+
+    return [null, null];
+  },
+
+  _findPannable: function cp_findPannable(node) {
+    if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML') {
+      return null;
+    }
 
     let nodeContent = node.ownerDocument.defaultView;
     while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) {
       let style = nodeContent.getComputedStyle(node, null);
 
       let overflow = [style.getPropertyValue('overflow'),
                       style.getPropertyValue('overflow-x'),
                       style.getPropertyValue('overflow-y')];
@@ -143,49 +153,73 @@ const ContentPanning = {
 
       let isScroll = (overflow.indexOf('scroll') != -1);
 
       let isScrollableTextarea = (node.tagName == 'TEXTAREA' &&
           (node.scrollHeight > node.clientHeight ||
            node.scrollWidth > node.clientWidth ||
            ('scrollLeftMax' in node && node.scrollLeftMax > 0) ||
            ('scrollTopMax' in node && node.scrollTopMax > 0)));
-      if (isScroll || isAuto || isScrollableTextarea)
-        return [node, this._generateCallback(node)];
+      if (isScroll || isAuto || isScrollableTextarea) {
+        return node;
+      }
 
       node = node.parentNode;
     }
 
     if (ContentPanning._asyncPanZoomForViewportFrame &&
-        nodeContent === content)
-      // The parent context is asynchronously panning and zooming our
-      // root scrollable frame, so don't use our synchronous fallback.
-      return [null, null];
+        nodeContent === content) {
+        // The parent context is asynchronously panning and zooming our
+        // root scrollable frame, so don't use our synchronous fallback.
+        return null;
+    }
 
     if (nodeContent.scrollMaxX || nodeContent.scrollMaxY) {
-      return [nodeContent, this._generateCallback(nodeContent)];
+      return nodeContent;
     }
 
-    return [null, null];
+    return null;
   },
 
   _generateCallback: function cp_generateCallback(content) {
+    let firstScroll = true;
+    let target;
+    let isScrolling = false;
+    let oldX, oldY, newX, newY;
+
+    function doScroll(node, delta) {
+      if (node instanceof Ci.nsIDOMHTMLElement) {
+        oldX = node.scrollLeft, oldY = node.scrollTop;
+        node.scrollLeft += delta.x;
+        node.scrollTop += delta.y;
+        newX = node.scrollLeft, newY = node.scrollTop;
+        return (newX != oldX || newY != oldY);
+      } else {
+        oldX = node.scrollX, oldY = node.scrollY;
+        node.scrollBy(delta.x, delta.y);
+        newX = node.scrollX, newY = node.scrollY;
+        return (newX != oldX || newY != oldY);
+      }
+    };
+
     function scroll(delta) {
-      if (content instanceof Ci.nsIDOMHTMLElement) {
-        let oldX = content.scrollLeft, oldY = content.scrollTop;
-        content.scrollLeft += delta.x;
-        content.scrollTop += delta.y;
-        let newX = content.scrollLeft, newY = content.scrollTop;
-        return (newX != oldX) || (newY != oldY);
-      } else {
-        let oldX = content.scrollX, oldY = content.scrollY;
-        content.scrollBy(delta.x, delta.y);
-        let newX = content.scrollX, newY = content.scrollY;
-        return (newX != oldX) || (newY != oldY);
+      for (target = content; target;
+          target = ContentPanning._findPannable(target.parentNode)) {
+        isScrolling = doScroll(target, delta);
+        if (isScrolling || !firstScroll) {
+          break;
+        }
       }
+      if (isScrolling) {
+        if (firstScroll) {
+          content = target; // set scrolling target to the first scrolling region
+        }
+        firstScroll = false; // lockdown the scrolling target after a success scrolling
+      }
+      return isScrolling;
     }
     return scroll;
   },
 
   get _domUtils() {
     delete this._domUtils;
     return this._domUtils = Cc['@mozilla.org/inspector/dom-utils;1']
                               .getService(Ci.inIDOMUtils);