- Correct implementation of document.getScrollTop/left.

For old browsers (<= ie10), we keep the old logic and relies on a
quirk/strict mode check to find the document scroll element.
For other browsers, we check first of document.scrollingElement exists
and use it computing scrollTop/Left. If not we use document.body for
webkit browser or rely on the quirk/strict mode check for other
browsers.
We default to document.documentElement if all the logic above return
null (SVG document)

- Fix getAbsoluteLeft/Top computation for webkit browsers to rely on the
correct implementation of document.getScrollLeft/Top.

Bug: #9542
Bug-Link: https://github.com/gwtproject/gwt/issues/9542
Change-Id: I02fe4de00a3f646687f0550e603fc7bb4aca1b80
diff --git a/user/src/com/google/gwt/dom/client/DOMImpl.java b/user/src/com/google/gwt/dom/client/DOMImpl.java
index 24f17e2..b6df165 100644
--- a/user/src/com/google/gwt/dom/client/DOMImpl.java
+++ b/user/src/com/google/gwt/dom/client/DOMImpl.java
@@ -266,7 +266,7 @@
   }-*/;
 
   public int getScrollLeft(Document doc) {
-    return doc.getViewportElement().getScrollLeft();
+    return ensureDocumentScrollingElement(doc).getScrollLeft();
   }
 
   public int getScrollLeft(Element elem) {
@@ -274,7 +274,7 @@
   }
 
   public int getScrollTop(Document doc) {
-    return doc.getViewportElement().getScrollTop();
+    return ensureDocumentScrollingElement(doc).getScrollTop();
   }
 
   public native String getStyleProperty(Style style, String name) /*-{
@@ -376,7 +376,7 @@
   }-*/;
 
   public void setScrollLeft(Document doc, int left) {
-    doc.getViewportElement().setScrollLeft(left);
+    ensureDocumentScrollingElement(doc).setScrollLeft(left);
   }
 
   public native void setScrollLeft(Element elem, int left) /*-{
@@ -384,13 +384,24 @@
   }-*/;
 
   public void setScrollTop(Document doc, int top) {
-    doc.getViewportElement().setScrollTop(top);
+    ensureDocumentScrollingElement(doc).setScrollTop(top);
   }
 
   public native String toString(Element elem) /*-{
     return elem.outerHTML;
   }-*/;
 
+  private Element ensureDocumentScrollingElement(Document document) {
+    // In some case (e.g SVG document and old Webkit browsers), getDocumentScrollingElement can
+    // return null. In this case, default to documentElement.
+    Element scrollingElement = getDocumentScrollingElement(document);
+    return scrollingElement != null ? scrollingElement : document.getDocumentElement();
+  }
+
+  Element getDocumentScrollingElement(Document doc)  {
+    return doc.getViewportElement();
+  }
+
   public int touchGetClientX(Touch touch) {
     return toInt32(touchGetSubPixelClientX(touch));
   }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE9.java b/user/src/com/google/gwt/dom/client/DOMImplIE9.java
index 63626e9..12e1564 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplIE9.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE9.java
@@ -91,11 +91,6 @@
     setScrollLeftImpl(elem, left);
   }
 
-  @Override
-  public void setScrollLeft(Document doc, int left) {
-    setScrollLeft(doc.getDocumentElement(), left);
-  }
-
   private native double getBoundingClientRectLeft(Element elem) /*-{
     // getBoundingClientRect() throws a JS exception if the elem is not attached
     // to the document, so we wrap it in a try/catch block
diff --git a/user/src/com/google/gwt/dom/client/DOMImplStandard.java b/user/src/com/google/gwt/dom/client/DOMImplStandard.java
index 9811b27..ca0b7f0 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplStandard.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplStandard.java
@@ -132,4 +132,24 @@
   public native void setInnerText(Element elem, String text) /*-{
     elem.textContent = text || '';
   }-*/;
+
+  @Override
+  Element getDocumentScrollingElement(Document doc) {
+    // Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrolling element to
+    // avoid trying to guess about browser behavior.
+    if (getNativeDocumentScrollingElement(doc) != null) {
+      return getNativeDocumentScrollingElement(doc);
+    }
+
+    return getLegacyDocumentScrollingElement(doc);
+  }
+
+  Element getLegacyDocumentScrollingElement(Document doc) {
+    return doc.getViewportElement();
+  }
+
+  final native Element getNativeDocumentScrollingElement(Document doc) /*-{
+    return doc.scrollingElement;
+  }-*/;
+
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java b/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java
index 34e3671..7e9f530 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java
@@ -210,7 +210,7 @@
   public int getAbsoluteLeft(Element elem) {
     ClientRect rect = getBoundingClientRect(elem);
     double left = rect != null ? rect.getSubPixelLeft()
-        + elem.getOwnerDocument().getBody().getScrollLeft()
+        + getScrollLeft(elem.getOwnerDocument())
         : getAbsoluteLeftUsingOffsets(elem);
     return toInt32(left);
   }
@@ -219,19 +219,12 @@
   public int getAbsoluteTop(Element elem) {
     ClientRect rect = getBoundingClientRect(elem);
     double top = rect != null ? rect.getSubPixelTop()
-        + elem.getOwnerDocument().getBody().getScrollTop()
+        + getScrollTop(elem.getOwnerDocument())
         : getAbsoluteTopUsingOffsets(elem);
     return toInt32(top);
   }
 
   @Override
-  public native int getScrollLeft(Document doc) /*-{
-    // Safari always applies document scrolling to the body element, even in strict mode.
-    // The behavior of Chrome depends of the doctype mode.
-    return doc.documentElement.scrollLeft || doc.body.scrollLeft;
-  }-*/;
-
-  @Override
   public int getScrollLeft(Element elem) {
     if (!elem.hasTagName(BodyElement.TAG) && isRTL(elem)) {
       return super.getScrollLeft(elem)
@@ -241,13 +234,6 @@
   }
 
   @Override
-  public native int getScrollTop(Document doc) /*-{
-    // Safari always applies document scrolling to the body element, even in strict mode.
-    // The behavior of Chrome depends of the doctype mode.
-    return doc.documentElement.scrollTop || doc.body.scrollTop;
-  }-*/;
-
-  @Override
   public native int getTabIndex(Element elem) /*-{ 
     // tabIndex is undefined for divs and other non-focusable elements prior to
     // Safari 4.
@@ -255,16 +241,6 @@
   }-*/;
 
   @Override
-  public native void setScrollLeft(Document doc, int left) /*-{
-    // Safari always applies document scrolling to the body element, even in strict mode. The
-    // behavior of Chrome depends of the doctype mode.
-    // This instruction will be ignored by safari and chrome in quirks mode.
-    doc.documentElement.scrollLeft = left;
-    // Will be ignored by chrome in strict mode.
-    doc.body.scrollLeft = left;
-  }-*/;
-
-  @Override
   public void setScrollLeft(Element elem, int left) {
     if (!elem.hasTagName(BodyElement.TAG) && isRTL(elem)) {
       left += elem.getScrollWidth() - elem.getClientWidth();
@@ -272,16 +248,6 @@
     super.setScrollLeft(elem, left);
   }
 
-  @Override
-  public native void setScrollTop(Document doc, int top) /*-{
-    // Safari always applies document scrolling to the body element, even in strict mode. The
-    // behavior of Chrome depends of the doctype mode.
-    // This instruction will be ignored by safari and by chrome in quirks mode.
-    doc.documentElement.scrollTop = top;
-    // Will be ignored by chrome in strict mode.
-    doc.body.scrollTop = top;
-  }-*/;
-
   protected native boolean isRTL(Element elem) /*-{
     return elem.ownerDocument.defaultView.getComputedStyle(elem, '').direction == 'rtl';
   }-*/;
diff --git a/user/src/com/google/gwt/dom/client/DOMImplWebkit.java b/user/src/com/google/gwt/dom/client/DOMImplWebkit.java
index cc80997..8433a5b 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplWebkit.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplWebkit.java
@@ -62,5 +62,11 @@
       elem.getStyle().clearProperty("webkitUserDrag");
     }
   }
+
+  @Override
+  Element getLegacyDocumentScrollingElement(Document doc) {
+    // Old WebKit needs body.scrollLeft in both quirks mode and strict mode.
+    return doc.getBody();
+  }
 }
 
diff --git a/user/src/com/google/gwt/dom/client/Document.java b/user/src/com/google/gwt/dom/client/Document.java
index dbcfca7..6eb119c 100644
--- a/user/src/com/google/gwt/dom/client/Document.java
+++ b/user/src/com/google/gwt/dom/client/Document.java
@@ -1413,6 +1413,7 @@
    * @return the height of the document's scrollable area
    */
   public final int getScrollHeight() {
+    // TODO(dramaix): Use document.scrollingElement when its available. See getScrollLeft().
     return getViewportElement().getScrollHeight();
   }
 
@@ -1445,6 +1446,7 @@
    * @return the width of the document's scrollable area
    */
   public final int getScrollWidth() {
+    // TODO(dramaix): Use document.scrollingElement when its available. See getScrollLeft().
     return getViewportElement().getScrollWidth();
   }