- Revision
- 202147
- Author
- [email protected]
- Date
- 2016-06-16 16:34:17 -0700 (Thu, 16 Jun 2016)
Log Message
Source/WebCore:
[iOS] Focus event dispatched in iframe causes parent document to scroll incorrectly
https://bugs.webkit.org/show_bug.cgi?id=158629
rdar://problem/26521616
Reviewed by Enrica Casucci.
When focussing elements in iframes, the page could scroll to an incorrect location.
This happened because code in Element::focus() tried to disable scrolling on focus,
but did so only for the current frame, so ancestor frames got programmatically scrolled.
On iOS we handle the scrolling in the UI process, so never want the web process to
do programmatic scrolling.
Fix by changing the focus and cache restore code to use SelectionRevealMode::DoNotReveal,
rather than manually prohibiting frame scrolling.
Tests: fast/forms/ios/focus-input-in-iframe.html
fast/forms/ios/programmatic-focus-input-in-iframe.html
* dom/Element.cpp:
(WebCore::Element::focus):
* history/CachedPage.cpp:
(WebCore::CachedPage::restore):
LayoutTests:
Focus event dispatched in iframe causes parent document to scroll incorrectly
https://bugs.webkit.org/show_bug.cgi?id=158629
rdar://problem/26521616
Reviewed by Enrica Casucci.
Tests for user-initiated and programmatic focus in frames.
* fast/forms/ios/focus-input-in-iframe-expected.txt: Added.
* fast/forms/ios/focus-input-in-iframe.html: Added.
* fast/forms/ios/programmatic-focus-input-in-iframe.html: Added.
* fast/forms/ios/programmatic-focus-input-in-iframe-expected.txt: Added.
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (202146 => 202147)
--- trunk/LayoutTests/ChangeLog 2016-06-16 23:27:46 UTC (rev 202146)
+++ trunk/LayoutTests/ChangeLog 2016-06-16 23:34:17 UTC (rev 202147)
@@ -1,3 +1,18 @@
+2016-06-16 Simon Fraser <[email protected]>
+
+ Focus event dispatched in iframe causes parent document to scroll incorrectly
+ https://bugs.webkit.org/show_bug.cgi?id=158629
+ rdar://problem/26521616
+
+ Reviewed by Enrica Casucci.
+
+ Tests for user-initiated and programmatic focus in frames.
+
+ * fast/forms/ios/focus-input-in-iframe-expected.txt: Added.
+ * fast/forms/ios/focus-input-in-iframe.html: Added.
+ * fast/forms/ios/programmatic-focus-input-in-iframe.html: Added.
+ * fast/forms/ios/programmatic-focus-input-in-iframe-expected.txt: Added.
+
2016-06-16 Zalan Bujtas <[email protected]>
[New Block-Inside-Inline Model] Do not attempt to re-run margin collapsing on the block sequence.
Added: trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe-expected.txt (0 => 202147)
--- trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe-expected.txt 2016-06-16 23:34:17 UTC (rev 202147)
@@ -0,0 +1,7 @@
+Tests zooming into a text input on tap.
+
+Click to focus input
+
+tap location { x: 20.000, y: 62.000 }
+scale 1.455
+visibleRect { left: 0.000, top: 1201.976, width: 219.979, height: 329.968 }
Added: trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe.html (0 => 202147)
--- trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe.html (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/focus-input-in-iframe.html 2016-06-16 23:34:17 UTC (rev 202147)
@@ -0,0 +1,48 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=0.5">
+ <style>
+ button {
+ display: block;
+ }
+ iframe {
+ margin-top: 800px;
+ }
+ </style>
+
+ <script src=""
+ <script>
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+ }
+
+ if (window.internals)
+ internals.settings.setFrameFlatteningEnabled(true);
+
+ function buttonClicked()
+ {
+ document.getElementById('frame').contentDocument.getElementById('input').focus();
+ }
+
+ function doTest()
+ {
+ testZoomAfterTap(document.getElementById('target'), 10, 10);
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body>
+
+<p>Tests zooming into a text input on tap.</p>
+<button id="target" _onclick_="buttonClicked()">Click to focus input</button>
+
+<iframe id="frame" srcdoc="<style>input { margin: 400px 20px; }</style>
+<input id='input' type='text'>
+"></iframe>
+
+</body>
+</html>
Added: trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe-expected.txt (0 => 202147)
--- trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe-expected.txt 2016-06-16 23:34:17 UTC (rev 202147)
@@ -0,0 +1,4 @@
+Tests that a programmatic focus should not scroll into view
+
+PASS: page did not scroll.
+
Added: trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe.html (0 => 202147)
--- trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe.html (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/programmatic-focus-input-in-iframe.html 2016-06-16 23:34:17 UTC (rev 202147)
@@ -0,0 +1,57 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+<head>
+ <meta name="viewport" content="initial-scale=0.5">
+ <style>
+ button {
+ display: block;
+ }
+ iframe {
+ margin-top: 800px;
+ }
+ </style>
+
+ <script>
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+ }
+
+ if (window.internals)
+ internals.settings.setFrameFlatteningEnabled(true);
+
+ function pageDidScroll()
+ {
+ document.getElementById('result').textContent = 'FAIL: page scrolled to ' + document.scrollingElement.scrollTop;
+ }
+
+ function doTest()
+ {
+ window.setTimeout(function() {
+ document.getElementById('frame').contentDocument.getElementById('input').focus();
+ }, 0);
+
+ // Wait for any scroll to happen.
+ window.setTimeout(function() {
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }, 100);
+ }
+
+ window.addEventListener('load', doTest, false);
+ </script>
+</head>
+<body _onscroll_="pageDidScroll()">
+
+<p>Tests that a programmatic focus should not scroll into view</p>
+<div id="result">
+ PASS: page did not scroll.
+</div>
+
+<iframe id="frame" srcdoc="<style>input { margin: 400px 20px; }</style>
+<input id='input' type='text'>
+"></iframe>
+
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (202146 => 202147)
--- trunk/Source/WebCore/ChangeLog 2016-06-16 23:27:46 UTC (rev 202146)
+++ trunk/Source/WebCore/ChangeLog 2016-06-16 23:34:17 UTC (rev 202147)
@@ -1,3 +1,28 @@
+2016-06-16 Simon Fraser <[email protected]>
+
+ [iOS] Focus event dispatched in iframe causes parent document to scroll incorrectly
+ https://bugs.webkit.org/show_bug.cgi?id=158629
+ rdar://problem/26521616
+
+ Reviewed by Enrica Casucci.
+
+ When focussing elements in iframes, the page could scroll to an incorrect location.
+ This happened because code in Element::focus() tried to disable scrolling on focus,
+ but did so only for the current frame, so ancestor frames got programmatically scrolled.
+ On iOS we handle the scrolling in the UI process, so never want the web process to
+ do programmatic scrolling.
+
+ Fix by changing the focus and cache restore code to use SelectionRevealMode::DoNotReveal,
+ rather than manually prohibiting frame scrolling.
+
+ Tests: fast/forms/ios/focus-input-in-iframe.html
+ fast/forms/ios/programmatic-focus-input-in-iframe.html
+
+ * dom/Element.cpp:
+ (WebCore::Element::focus):
+ * history/CachedPage.cpp:
+ (WebCore::CachedPage::restore):
+
2016-06-16 Zalan Bujtas <[email protected]>
[New Block-Inside-Inline Model] Do not attempt to re-run margin collapsing on the block sequence.
Modified: trunk/Source/WebCore/dom/Element.cpp (202146 => 202147)
--- trunk/Source/WebCore/dom/Element.cpp 2016-06-16 23:27:46 UTC (rev 202146)
+++ trunk/Source/WebCore/dom/Element.cpp 2016-06-16 23:34:17 UTC (rev 202147)
@@ -2245,6 +2245,8 @@
}
cancelFocusAppearanceUpdate();
+
+ SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
#if PLATFORM(IOS)
// Focusing a form element triggers animation in UIKit to scroll to the right position.
// Calling updateFocusAppearance() would generate an unnecessary call to ScrollView::setScrollPosition(),
@@ -2252,13 +2254,9 @@
FrameView* view = document().view();
bool isFormControl = view && is<HTMLFormControlElement>(*this);
if (isFormControl)
- view->setProhibitsScrolling(true);
+ revealMode = SelectionRevealMode::DoNotReveal;
#endif
- updateFocusAppearance(restorePreviousSelection ? SelectionRestorationMode::Restore : SelectionRestorationMode::SetDefault);
-#if PLATFORM(IOS)
- if (isFormControl)
- view->setProhibitsScrolling(false);
-#endif
+ updateFocusAppearance(restorePreviousSelection ? SelectionRestorationMode::Restore : SelectionRestorationMode::SetDefault, revealMode);
}
void Element::updateFocusAppearanceAfterAttachIfNeeded()
Modified: trunk/Source/WebCore/history/CachedPage.cpp (202146 => 202147)
--- trunk/Source/WebCore/history/CachedPage.cpp 2016-06-16 23:27:46 UTC (rev 202146)
+++ trunk/Source/WebCore/history/CachedPage.cpp 2016-06-16 23:34:17 UTC (rev 202147)
@@ -77,25 +77,18 @@
m_cachedMainFrame->open();
// Restore the focus appearance for the focused element.
- // FIXME: Right now we don't support pages w/ frames in the b/f cache. This may need to be tweaked when we add support for that.
+ // FIXME: Right now we don't support pages with frames in the b/f cache. This may need to be tweaked when we add support for that.
Document* focusedDocument = page.focusController().focusedOrMainFrame().document();
if (Element* element = focusedDocument->focusedElement()) {
+ SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
#if PLATFORM(IOS)
// We don't want focused nodes changing scroll position when restoring from the cache
// as it can cause ugly jumps before we manage to restore the cached position.
page.mainFrame().selection().suppressScrolling();
-
- bool hadProhibitsScrolling = false;
- FrameView* frameView = page.mainFrame().view();
- if (frameView) {
- hadProhibitsScrolling = frameView->prohibitsScrolling();
- frameView->setProhibitsScrolling(true);
- }
+ revealMode = SelectionRevealMode::DoNotReveal;
#endif
- element->updateFocusAppearance(SelectionRestorationMode::Restore);
+ element->updateFocusAppearance(SelectionRestorationMode::Restore, revealMode);
#if PLATFORM(IOS)
- if (frameView)
- frameView->setProhibitsScrolling(hadProhibitsScrolling);
page.mainFrame().selection().restoreScrolling();
#endif
}