Title: [187593] trunk
Revision
187593
Author
simon.fra...@apple.com
Date
2015-07-30 11:32:45 -0700 (Thu, 30 Jul 2015)

Log Message

Selecting in an iframe can cause main page scrolling
https://bugs.webkit.org/show_bug.cgi?id=147431
rdar://problem/19244589

Reviewed by Zalan Bujtas.

Source/WebCore:

The RenderLayer auatoscroll code walks up the RenderLayer hierarchy, crossing
frame boundaries. However, as it crosses into an ancestor frame it failed to
map the target rect into the coordinate space of the new frame, which caused
us to scroll to an incorrect location in that parent frame.

Test: fast/events/autoscroll-in-iframe.html

* rendering/RenderLayer.cpp:
(WebCore::parentLayerCrossFrame): Make the layer a reference, and pass in
an optional rect. When crossing frame boundaries, map the rect from the
contents of the child frame to the contents of the parent frame.
(WebCore::RenderLayer::enclosingScrollableLayer): Pass optional rect.
(WebCore::RenderLayer::scrollRectToVisible):
(WebCore::RenderLayer::hasScrollableOrRubberbandableAncestor):
* rendering/RenderLayer.h:

LayoutTests:

Test that uses eventSender to select in an iframe after scrolling the
main page.

* fast/events/autoscroll-in-iframe-expected.txt: Added.
* fast/events/autoscroll-in-iframe.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (187592 => 187593)


--- trunk/LayoutTests/ChangeLog	2015-07-30 18:31:01 UTC (rev 187592)
+++ trunk/LayoutTests/ChangeLog	2015-07-30 18:32:45 UTC (rev 187593)
@@ -1,3 +1,17 @@
+2015-07-30  Simon Fraser  <simon.fra...@apple.com>
+
+        Selecting in an iframe can cause main page scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=147431
+        rdar://problem/19244589
+
+        Reviewed by Zalan Bujtas.
+        
+        Test that uses eventSender to select in an iframe after scrolling the
+        main page.
+
+        * fast/events/autoscroll-in-iframe-expected.txt: Added.
+        * fast/events/autoscroll-in-iframe.html: Added.
+
 2015-07-29  Matt Rajca  <mra...@apple.com>
 
         Media Session: test Next/Previous Track media control events delivered to Content media sessions

Added: trunk/LayoutTests/fast/events/autoscroll-in-iframe-expected.txt (0 => 187593)


--- trunk/LayoutTests/fast/events/autoscroll-in-iframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/autoscroll-in-iframe-expected.txt	2015-07-30 18:32:45 UTC (rev 187593)
@@ -0,0 +1,3 @@
+
+PASSED: selecting in the iframe did not scroll the page.
+

Added: trunk/LayoutTests/fast/events/autoscroll-in-iframe.html (0 => 187593)


--- trunk/LayoutTests/fast/events/autoscroll-in-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/autoscroll-in-iframe.html	2015-07-30 18:32:45 UTC (rev 187593)
@@ -0,0 +1,89 @@
+<html>
+    <head>
+        <style>
+            body {
+                height: 2000px;
+            }
+            iframe {
+                position: absolute;
+                top: 800px;
+            }
+        </style>
+        <script src=""
+        <script>
+
+        if (window.testRunner) {
+            testRunner.waitUntilDone();
+            testRunner.dumpAsText();
+        }
+
+        function log(msg)
+        {
+            document.getElementById('console').appendChild(document.createTextNode(msg + '\n'));
+        }
+        
+        var verticalScrollOffset;
+        var iframe;
+        
+        function doTest()
+        {
+            document.scrollingElement.scrollTop = 800;
+            if (document.scrollingElement.scrollTop != 800)
+                log("FAILED: failed to scroll by 800px");
+
+            iframe = document.getElementById('targetFrame');
+
+            var textInIframe = iframe.contentDocument.getElementById('starthere');
+            var x = iframe.clientLeft + textInIframe.offsetLeft + 7;
+            var y = iframe.clientTop + textInIframe.offsetTop + 7;
+            if (window.eventSender) {
+                eventSender.dragMode = false;
+                eventSender.mouseMoveTo(x, y);
+                eventSender.mouseDown();
+                eventSender.mouseUp();
+            }
+            setTimeout(autoscrollTestPart1, 0);
+        }
+
+        function autoscrollTestPart1()
+        {
+            var mainDocumentTop = document.scrollingElement.scrollTop;
+            if (mainDocumentTop != 800)
+                log("FAILED: Clicking in the iframe scrolled the page (window.scrollTops is " + mainDocumentTop + ")");
+
+            if (window.eventSender) {
+                var textInIframe = iframe.contentDocument.getElementById('starthere');
+                var x = iframe.clientLeft + textInIframe.offsetLeft + 7;
+                var y = iframe.clientTop + textInIframe.offsetTop + 7;
+                eventSender.dragMode = false;
+                eventSender.mouseMoveTo(x, y);
+                eventSender.mouseDown();
+                eventSender.mouseMoveTo(x + 10, y);
+            }
+            setTimeout(autoscrollTestPart2, 100);
+        }
+
+        function autoscrollTestPart2()
+        {
+            if (window.eventSender)
+                eventSender.mouseUp();
+
+            var mainDocumentTop = document.scrollingElement.scrollTop;
+            if (mainDocumentTop == 800)
+                log("PASSED: selecting in the iframe did not scroll the page.");
+            else
+                log("FAILED: the page autoscrolled (window.scrollTop is " + mainDocumentTop + ").");
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+        </script>
+    </head>
+<body>
+    <iframe id="targetFrame" srcdoc="<!DOCTYPE html><html><body><div id='starthere' style='height:60px; overflow:auto;'>asdf<br>asdf<br>asdf<br>asdf<br>asdf"></iframe>
+    <div id="console"></div>
+</body>
+</html>
+

Modified: trunk/Source/WebCore/ChangeLog (187592 => 187593)


--- trunk/Source/WebCore/ChangeLog	2015-07-30 18:31:01 UTC (rev 187592)
+++ trunk/Source/WebCore/ChangeLog	2015-07-30 18:32:45 UTC (rev 187593)
@@ -1,5 +1,29 @@
 2015-07-30  Simon Fraser  <simon.fra...@apple.com>
 
+        Selecting in an iframe can cause main page scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=147431
+        rdar://problem/19244589
+
+        Reviewed by Zalan Bujtas.
+        
+        The RenderLayer auatoscroll code walks up the RenderLayer hierarchy, crossing
+        frame boundaries. However, as it crosses into an ancestor frame it failed to
+        map the target rect into the coordinate space of the new frame, which caused
+        us to scroll to an incorrect location in that parent frame.
+
+        Test: fast/events/autoscroll-in-iframe.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::parentLayerCrossFrame): Make the layer a reference, and pass in
+        an optional rect. When crossing frame boundaries, map the rect from the
+        contents of the child frame to the contents of the parent frame.
+        (WebCore::RenderLayer::enclosingScrollableLayer): Pass optional rect.
+        (WebCore::RenderLayer::scrollRectToVisible):
+        (WebCore::RenderLayer::hasScrollableOrRubberbandableAncestor):
+        * rendering/RenderLayer.h:
+
+2015-07-30  Simon Fraser  <simon.fra...@apple.com>
+
         Improve the history logging output
         https://bugs.webkit.org/show_bug.cgi?id=147429
 

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (187592 => 187593)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2015-07-30 18:31:01 UTC (rev 187592)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2015-07-30 18:32:45 UTC (rev 187593)
@@ -1449,13 +1449,12 @@
     return curr;
 }
 
-static RenderLayer* parentLayerCrossFrame(const RenderLayer* layer)
+static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer, LayoutRect* rect = nullptr)
 {
-    ASSERT(layer);
-    if (layer->parent())
-        return layer->parent();
+    if (layer.parent())
+        return layer.parent();
 
-    HTMLFrameOwnerElement* ownerElement = layer->renderer().document().ownerElement();
+    HTMLFrameOwnerElement* ownerElement = layer.renderer().document().ownerElement();
     if (!ownerElement)
         return nullptr;
 
@@ -1463,12 +1462,18 @@
     if (!ownerRenderer)
         return nullptr;
 
+    // Convert the rect into the coordinate space of the parent frame's document.
+    if (rect) {
+        IntRect viewRect = layer.renderer().frame().view()->convertToContainingView(enclosingIntRect(*rect));
+        *rect = ownerRenderer->frame().view()->viewToContents(viewRect);
+    }
+
     return ownerRenderer->enclosingLayer();
 }
 
-RenderLayer* RenderLayer::enclosingScrollableLayer() const
+RenderLayer* RenderLayer::enclosingScrollableLayer(LayoutRect* rect) const
 {
-    for (RenderLayer* nextLayer = parentLayerCrossFrame(this); nextLayer; nextLayer = parentLayerCrossFrame(nextLayer)) {
+    for (RenderLayer* nextLayer = parentLayerCrossFrame(*this, rect); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer, rect)) {
         if (is<RenderBox>(nextLayer->renderer()) && downcast<RenderBox>(nextLayer->renderer()).canBeScrolledAndHasScrollableArea())
             return nextLayer;
     }
@@ -2531,7 +2536,7 @@
     }
     
     if (renderer().frame().eventHandler().autoscrollInProgress())
-        parentLayer = enclosingScrollableLayer();
+        parentLayer = enclosingScrollableLayer(&newRect);
 
     if (parentLayer)
         parentLayer->scrollRectToVisible(newRect, alignX, alignY);
@@ -3182,7 +3187,7 @@
 
 bool RenderLayer::hasScrollableOrRubberbandableAncestor()
 {
-    for (RenderLayer* nextLayer = parentLayerCrossFrame(this); nextLayer; nextLayer = parentLayerCrossFrame(nextLayer)) {
+    for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
         if (nextLayer->isScrollableOrRubberbandable())
             return true;
     }

Modified: trunk/Source/WebCore/rendering/RenderLayer.h (187592 => 187593)


--- trunk/Source/WebCore/rendering/RenderLayer.h	2015-07-30 18:31:01 UTC (rev 187592)
+++ trunk/Source/WebCore/rendering/RenderLayer.h	2015-07-30 18:32:45 UTC (rev 187593)
@@ -404,7 +404,7 @@
     RenderLayer* enclosingAncestorForPosition(EPosition) const;
 
     // Returns the nearest enclosing layer that is scrollable.
-    RenderLayer* enclosingScrollableLayer() const;
+    RenderLayer* enclosingScrollableLayer(LayoutRect* = nullptr) const;
 
     // The layer relative to which clipping rects for this layer are computed.
     RenderLayer* clippingRootForPainting() const;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to