AX: VoiceOver iframe scrolling focus jumping bug
authorn_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 May 2018 02:07:34 +0000 (02:07 +0000)
committern_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 May 2018 02:07:34 +0000 (02:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176615
<rdar://problem/34333067>

Reviewed by Chris Fleizach.

Source/WebCore:

Scrolling to make elements visible is not working correctly for elements inside an
offscreen iframe. Fixed it by using RenderLayer::scrollRectToVisible() to handle
scrolling more properly.

Test: accessibility/scroll-to-make-visible-iframe-offscreen.html

* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::scrollToMakeVisible const):

LayoutTests:

* accessibility/resources/iframe.html: Added.
* accessibility/scroll-to-make-visible-iframe-offscreen-expected.txt: Added.
* accessibility/scroll-to-make-visible-iframe-offscreen.html: Added.
* platform/win/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@231628 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/accessibility/resources/iframe.html [new file with mode: 0644]
LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen.html [new file with mode: 0644]
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AccessibilityObject.cpp

index 3767101..af112bd 100644 (file)
@@ -1,3 +1,16 @@
+2018-05-09  Nan Wang  <n_wang@apple.com>
+
+        AX: VoiceOver iframe scrolling focus jumping bug
+        https://bugs.webkit.org/show_bug.cgi?id=176615
+        <rdar://problem/34333067>
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/resources/iframe.html: Added.
+        * accessibility/scroll-to-make-visible-iframe-offscreen-expected.txt: Added.
+        * accessibility/scroll-to-make-visible-iframe-offscreen.html: Added.
+        * platform/win/TestExpectations:
+
 2018-05-09  Joanmarie Diggs  <jdiggs@igalia.com>
 
         AX: accessibleNameForNode should simplify whitespace when using innerText
diff --git a/LayoutTests/accessibility/resources/iframe.html b/LayoutTests/accessibility/resources/iframe.html
new file mode 100644 (file)
index 0000000..92d1180
--- /dev/null
@@ -0,0 +1,6 @@
+<html>
+<body>
+<div id="box" style='border: 1px solid #000; height: 1000px;'>1000-pixel box</div>
+<iframe id="iframe" src="data:text/html,<body><div style='border: 1px solid #000; height: 5000px;'>5000-pixel box</div><button id='button'>Test Button</button></button></body>"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen-expected.txt b/LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen-expected.txt
new file mode 100644 (file)
index 0000000..f22675a
--- /dev/null
@@ -0,0 +1,39 @@
+This tests that scrolling to make an element visible works properly when there's an iframe off screen.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+5000-pixel box
+
+Test scrolling an offscreen iframe.
+PASS window.pageYOffset is 0
+
+Scroll lower target to visible.
+The iframe should be scrolled into view
+PASS scrolledYOffset > 0 is true
+Test the lower target should be scrolled into view.
+PASS scrolledIntoView is true
+
+Scroll upper target to visible.
+The main window shouldn't scroll.
+PASS window.pageYOffset == scrolledYOffset is true
+Test the upper target should be scrolled into view.
+PASS scrolledIntoView is true
+
+Reset scrolling. Test scrolling in nested iframes.
+PASS window.pageYOffset is 0
+
+Scroll inner button to visible.
+Test the button inside the inner frame should be scrolled into view
+PASS scrolledIntoView is true
+The inner iframe should be scrolled into view
+PASS outterFrameWindow.pageYOffset > 0 is true
+
+Scroll outter text element to visible.
+The Y offset of the outter iframe should be reset.
+PASS outterFrameWindow.pageYOffset is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen.html b/LayoutTests/accessibility/scroll-to-make-visible-iframe-offscreen.html
new file mode 100644 (file)
index 0000000..d25f247
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<head>
+<script src="../resources/js-test.js"></script>
+</head>
+<body>
+
+<p id="description"></p>
+
+<div style='border: 1px solid #000; height: 5000px;'>5000-pixel box</div>
+
+<iframe id="frame" src="data:text/html,<body><button id='upper_target'>Upper Target</button><div style='border: 1px solid #000; height: 5000px;'>5000-pixel box</div><button id='lower_target'>Lower Target</button></body>"></iframe>
+
+<br>
+<iframe id="frame2" src="./resources/iframe.html"></iframe>
+
+<div id="console"></div>
+
+<script>
+description("This tests that scrolling to make an element visible works properly when there's an iframe off screen.");
+
+window.jsTestIsAsync = true;
+
+function runTest() {
+
+    window.frame = document.getElementById("frame");
+    window.frameWindow = frame.contentWindow;
+
+    var upperTarget = frameWindow.document.getElementById("upper_target");
+    var lowerTarget = frameWindow.document.getElementById("lower_target");
+
+    var lowerTargetAccessibleObject;
+    var upperTargetAccessibleObject;
+    if (frameWindow.accessibilityController) {
+        lowerTargetAccessibleObject = frameWindow.accessibilityController.accessibleElementById("lower_target");
+        upperTargetAccessibleObject = frameWindow.accessibilityController.accessibleElementById("upper_target");
+    }
+
+    // Initial state
+    debug("Test scrolling an offscreen iframe.");
+    window.scrollTo(0, 0);
+    shouldBeZero("window.pageYOffset");
+
+    // Scroll to make lower target visible and check.
+    debug("\nScroll lower target to visible.");
+    if (frameWindow.accessibilityController)
+        lowerTargetAccessibleObject.scrollToMakeVisible();
+    // The iframe should be scrolled into view.
+    debug("The iframe should be scrolled into view");
+    window.scrolledYOffset = window.pageYOffset;
+    shouldBeTrue("scrolledYOffset > 0");
+    // The content inside the iframe should be scrolled into view too
+    testScrolledIntoView(lowerTarget, frameWindow, "Test the lower target should be scrolled into view.")
+
+    // Scroll to make upper target visible and check.
+    debug("\nScroll upper target to visible.");
+    if (frameWindow.accessibilityController)
+        upperTargetAccessibleObject.scrollToMakeVisible();
+    // The iframe should be visible already
+    debug("The main window shouldn't scroll.");
+    shouldBeTrue("window.pageYOffset == scrolledYOffset");
+    // The content inside the iframe should be scrolled into view too
+    testScrolledIntoView(upperTarget, frameWindow, "Test the upper target should be scrolled into view.")
+
+    // Reset and test iframe inside iframe
+    debug("\nReset scrolling. Test scrolling in nested iframes.");
+    window.scrollTo(0, 0);
+    shouldBeZero("window.pageYOffset");
+
+    window.innerFrame = document.getElementById("frame2").contentWindow.document.getElementById("iframe");
+    window.innerFrameWindow = innerFrame.contentWindow;
+    var button = innerFrameWindow.document.getElementById("button");
+    var buttonAccessibleObject;
+    debug("\nScroll inner button to visible.");
+    if (innerFrameWindow.accessibilityController) {
+        buttonAccessibleObject = innerFrameWindow.accessibilityController.accessibleElementById("button");
+        buttonAccessibleObject.scrollToMakeVisible();
+    }
+    // The content inside the inner iframe should be scrolled into view too
+    testScrolledIntoView(button, innerFrameWindow, "Test the button inside the inner frame should be scrolled into view");
+    // Use outter frame to determine the inner frame is scrolled into view.
+    window.outterFrame = document.getElementById("frame2");
+    window.outterFrameWindow = outterFrame.contentWindow;
+    debug("The inner iframe should be scrolled into view");
+    shouldBeTrue("outterFrameWindow.pageYOffset > 0");
+
+    // Now make sure we can scroll back to the outter text above the inner iframe
+    var text = outterFrameWindow.document.getElementById("box").firstChild;
+    var textAccessibleObject;
+    debug("\nScroll outter text element to visible.");
+    if (outterFrameWindow.accessibilityController) {
+        textAccessibleObject = outterFrameWindow.accessibilityController.accessibleElementById("box").childAtIndex(0);
+        textAccessibleObject.scrollToMakeVisible();
+    }
+    debug("The Y offset of the outter iframe should be reset.")
+    shouldBeZero("outterFrameWindow.pageYOffset");
+
+    finishJSTest();
+}
+
+function testScrolledIntoView(object, testWindow, description) {
+    debug(description);
+    window.frameMinYOffset = object.offsetTop + object.offsetHeight - testWindow.innerHeight;
+    window.frameMaxYOffset = object.offsetTop;
+    window.scrolledIntoView = testWindow.pageYOffset >= frameMinYOffset && testWindow.pageYOffset <= frameMaxYOffset;
+    shouldBeTrue("scrolledIntoView");
+}
+
+window.addEventListener("load", function() {
+    setTimeout(runTest, 0);
+}, false);
+
+</script>
+
+</body>
+</html>
index 2883a13..d83d77d 100644 (file)
@@ -1574,6 +1574,7 @@ accessibility/scroll-to-make-visible-iframe.html [ Skip ]
 accessibility/scroll-to-make-visible-nested-2.html [ Skip ]
 accessibility/scroll-to-make-visible-nested.html [ Skip ]
 accessibility/scroll-to-make-visible-with-subfocus.html [ Skip ]
+accessibility/scroll-to-make-visible-iframe-offscreen.html [ Skip ]
 
 # Apparently missing support for roleDescription on Windows.
 accessibility/aria-roledescription.html [ Failure ]
index 19c8492..cd52eb8 100644 (file)
@@ -1,3 +1,20 @@
+2018-05-09  Nan Wang  <n_wang@apple.com>
+
+        AX: VoiceOver iframe scrolling focus jumping bug
+        https://bugs.webkit.org/show_bug.cgi?id=176615
+        <rdar://problem/34333067>
+
+        Reviewed by Chris Fleizach.
+
+        Scrolling to make elements visible is not working correctly for elements inside an
+        offscreen iframe. Fixed it by using RenderLayer::scrollRectToVisible() to handle
+        scrolling more properly.
+
+        Test: accessibility/scroll-to-make-visible-iframe-offscreen.html
+
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::scrollToMakeVisible const):
+
 2018-05-09  Joanmarie Diggs  <jdiggs@igalia.com>
 
         AX: accessibleNameForNode should simplify whitespace when using innerText
index 1928172..990c4c7 100644 (file)
@@ -2981,9 +2981,12 @@ void AccessibilityObject::scrollToMakeVisible() const
 {
     if (dispatchAccessibilityEventWithType(AccessibilityEventType::ScrollIntoView))
         return;
-    IntRect objectRect = snappedIntRect(boundingBoxRect());
-    objectRect.setLocation(IntPoint());
-    scrollToMakeVisibleWithSubFocus(objectRect);
+    
+    if (isScrollView() && parentObject())
+        parentObject()->scrollToMakeVisible();
+
+    if (auto* renderer = this->renderer())
+        renderer->scrollRectToVisible(SelectionRevealMode::Reveal, boundingBoxRect(), false, ScrollAlignment::alignCenterIfNotVisible, ScrollAlignment::alignCenterIfNotVisible);
 }
 
 void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const