- Revision
- 188920
- Author
- [email protected]
- Date
- 2015-08-25 11:03:00 -0700 (Tue, 25 Aug 2015)
Log Message
Wheel events stop propagating when target element is removed from DOM
https://bugs.webkit.org/show_bug.cgi?id=148384
<rdar://problem/19732211>
Reviewed by David Hyatt.
Source/WebCore:
Tested by tiled-drawing/scrolling/latched-to-deleted-node.html
We need to reset our latching state if the targeted node is removed from the DOM.
Add a check in 'platformPrepareForWheelEvents' that checks if the expected latching
target node was already removed from the DOM. If it was, we should not send events
to it, and should reset latching state so we can attach to the next relevant node.
* dom/Element.cpp:
(WebCore::Element::removedFrom): Remove any latched wheel event state objects that
match the current element.
* page/MainFrame.cpp:
(WebCore::MainFrame::removeLatchingStateForTarget): Remove any latched wheel event
state structures that match the passed wheel event target.
* page/MainFrame.h:
LayoutTests:
* tiled-drawing/scrolling/latched-to-deleted-node-expected.txt: Added.
* tiled-drawing/scrolling/latched-to-deleted-node.html: Added.
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (188919 => 188920)
--- trunk/LayoutTests/ChangeLog 2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/LayoutTests/ChangeLog 2015-08-25 18:03:00 UTC (rev 188920)
@@ -1,3 +1,14 @@
+2015-08-24 Brent Fulgham <[email protected]>
+
+ Wheel events stop propagating when target element is removed from DOM
+ https://bugs.webkit.org/show_bug.cgi?id=148384
+ <rdar://problem/19732211>
+
+ Reviewed by David Hyatt.
+
+ * tiled-drawing/scrolling/latched-to-deleted-node-expected.txt: Added.
+ * tiled-drawing/scrolling/latched-to-deleted-node.html: Added.
+
2015-08-25 Chris Dumez <[email protected]>
compareDocumentPosition() should report PRECEDING or FOLLOWING information even if nodes are disconnected
Added: trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt (0 => 188920)
--- trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt 2015-08-25 18:03:00 UTC (rev 188920)
@@ -0,0 +1,18 @@
+Container
+
+After moving 40px up or down we remove the initial target element of wheel event from
+the dom. In safari this causes the wheel event to stop firing on the current frame, but it
+then starts firing on the parent frame.
+
+Tests that iframe doesn't consume wheel events when scrolling a select in an iframe.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Removed the target element
+PASS Page did not scroll
+PASS div continued scrolling after target element was deleted.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html (0 => 188920)
--- trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html 2015-08-25 18:03:00 UTC (rev 188920)
@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ #pane {
+ display:inline-block;
+ width:200px;
+ height:500px;
+ }
+ #boxup {
+ position: absolute;
+ top:0px;
+ width:200px;
+ height:230px;
+ background-color: #000;
+ opacity:.4;
+ }
+ #boxdown {
+ position: absolute;
+ top:270px;
+ width:200px;
+ height:230px;
+ background-color: #000;
+ opacity:.4;
+ }
+ #objectBack {
+ position: absolute;
+ top:200px;
+ width:200px;
+ height:100px;
+ background-color: #6c6;
+ }
+ #targetElement {
+ position: absolute;
+ top:30px;
+ width:200px;
+ height:40px;
+ background-color: #c66;
+ }
+ </style>
+<script src=""
+</head>
+<body _onload_="setupTopLevel();">
+<script>
+window.jsTestIsAsync = true;
+
+var pageScrollPositionBeforeGesture;
+var divElement;
+var divScrollPositionBeforeGesture;
+
+var deleteThreshhold = 40;
+
+function locationInWindowCoordinates(element)
+{
+ var position = {};
+ position.x = element.offsetLeft;
+ position.y = element.offsetTop;
+
+ while (element.offsetParent) {
+ position.x = position.x + element.offsetParent.offsetLeft;
+ position.y = position.y + element.offsetParent.offsetTop;
+ if (element == document.getElementsByTagName("body")[0])
+ break;
+
+ element = element.offsetParent;
+ }
+
+ return position;
+}
+
+document.addEventListener("DOMContentLoaded", function() {
+ var objectBack = document.getElementById("objectBack");
+ var targetElement = document.getElementById("targetElement");
+ var inIFrame = window.parent !== window;
+ var pos = 0;
+
+
+ document.addEventListener("wheel", function(event) {
+ pos = pos - event.deltaY ;
+ move(objectBack, pos);
+ if (Math.abs(pos) > deleteThreshhold) {
+ // don't remove it twice
+ if (targetElement.parentNode) {
+ var parent = targetElement.parentNode;
+ parent.removeChild(targetElement);
+ testPassed("Removed the target element");
+ }
+ }
+ event.preventDefault();
+ });
+
+ // Setup
+ targetElement.innerHTML = "Put cursor here. Scroll up/down";
+
+ function move(element, pos) {
+ element.style.transform = "translateY(" + pos + "px)";
+ }
+});
+
+function checkForScroll()
+{
+ var pageScrollPositionAfterGesture = document.body.scrollTop;
+
+ // The page should not have scrolled
+ if (pageScrollPositionBeforeGesture != pageScrollPositionAfterGesture)
+ testFailed("Page received scroll events, when it should not have. Expected " + pageScrollPositionBeforeGesture + ", but got " + pageScrollPositionAfterGesture);
+ else
+ testPassed("Page did not scroll");
+
+ var divScrollPositionAfterGesture = divElement.getBoundingClientRect().top;
+
+ // The div should have continued moving after the element was deleted
+ if (divScrollPositionAfterGesture > (divScrollPositionBeforeGesture - deleteThreshhold - 10))
+ testFailed("div did not scroll to proper location. Expected " + divScrollPositionBeforeGesture + ", but got " + divScrollPositionAfterGesture);
+ else
+ testPassed("div continued scrolling after target element was deleted.");
+
+ finishJSTest();
+}
+
+function scrollTest()
+{
+ pageScrollPositionBeforeGesture = document.body.scrollTop;
+
+ divElement = document.getElementById('objectBack');
+
+ divScrollPositionBeforeGesture = divElement.getBoundingClientRect().top;
+
+ var windowPosition = locationInWindowCoordinates(divElement);
+
+ var startPosX = windowPosition.x + 0.5 * divElement.clientWidth;
+ var startPosY = windowPosition.y + 0.5 * divElement.clientHeight;
+
+ eventSender.mouseMoveTo(startPosX, startPosY);
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'began', 'none');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, 'changed', 'none');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, 'changed', 'none');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'begin');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue');
+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
+ eventSender.callAfterScrollingCompletes(checkForScroll);
+}
+
+function setupTopLevel()
+{
+ if (window.eventSender) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+
+ eventSender.monitorWheelEvents();
+ setTimeout(scrollTest, 0);
+ } else {
+ var messageLocation = document.getElementById('parent');
+ var message = document.createElement('div');
+ message.innerHTML = "<p>This test is better run under DumpRenderTree. To manually test it, place the mouse pointer<br/>"
+ + "at the top of the page, and then use the mouse wheel or a two-finger swipe to scroll the<br/>"
+ + "down past the iframe.<br/><br/>"
+ + "The iframe should not scroll.</p>";
+ messageLocation.appendChild(message);
+ }
+}
+</script>
+<div id="parent" style="height: 2000px;">
+ <div id="pane">
+ <h3>Container</h3>
+ <div id="objectBack"><div id="targetElement"></div></div>
+ <div id="boxup"></div>
+ <div id="boxdown"></div>
+ </div>
+ <p class="description">After moving 40px up or down we remove the initial target element of wheel event from<br/>
+ the dom. In safari this causes the wheel event to stop firing on the current frame, but it<br/>
+ then starts firing on the parent frame.</p>
+</div>
+<div id="console"></div>
+<script>
+description("Tests that iframe doesn't consume wheel events when scrolling a select in an iframe.");
+</script>
+<script src=""
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (188919 => 188920)
--- trunk/Source/WebCore/ChangeLog 2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/ChangeLog 2015-08-25 18:03:00 UTC (rev 188920)
@@ -1,3 +1,26 @@
+2015-08-24 Brent Fulgham <[email protected]>
+
+ Wheel events stop propagating when target element is removed from DOM
+ https://bugs.webkit.org/show_bug.cgi?id=148384
+ <rdar://problem/19732211>
+
+ Reviewed by David Hyatt.
+
+ Tested by tiled-drawing/scrolling/latched-to-deleted-node.html
+
+ We need to reset our latching state if the targeted node is removed from the DOM.
+ Add a check in 'platformPrepareForWheelEvents' that checks if the expected latching
+ target node was already removed from the DOM. If it was, we should not send events
+ to it, and should reset latching state so we can attach to the next relevant node.
+
+ * dom/Element.cpp:
+ (WebCore::Element::removedFrom): Remove any latched wheel event state objects that
+ match the current element.
+ * page/MainFrame.cpp:
+ (WebCore::MainFrame::removeLatchingStateForTarget): Remove any latched wheel event
+ state structures that match the passed wheel event target.
+ * page/MainFrame.h:
+
2015-08-25 Wenson Hsieh <[email protected]>
Fix crash due to search field disappearing when showing results menu
Modified: trunk/Source/WebCore/dom/Element.cpp (188919 => 188920)
--- trunk/Source/WebCore/dom/Element.cpp 2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/dom/Element.cpp 2015-08-25 18:03:00 UTC (rev 188920)
@@ -56,6 +56,7 @@
#include "IdTargetObserverRegistry.h"
#include "InsertionPoint.h"
#include "KeyboardEvent.h"
+#include "MainFrame.h"
#include "MutationObserverInterestGroup.h"
#include "MutationRecord.h"
#include "NodeRenderStyle.h"
@@ -71,6 +72,7 @@
#include "SVGDocumentExtensions.h"
#include "SVGElement.h"
#include "SVGNames.h"
+#include "ScrollLatchingState.h"
#include "SelectorQuery.h"
#include "Settings.h"
#include "StyleProperties.h"
@@ -1591,6 +1593,12 @@
if (hasPendingResources())
document().accessSVGExtensions().removeElementFromPendingResources(this);
+
+
+#if PLATFORM(MAC)
+ if (Frame* frame = document().frame())
+ frame->mainFrame().removeLatchingStateForTarget(*this);
+#endif
}
void Element::unregisterNamedFlowContentElement()
Modified: trunk/Source/WebCore/page/MainFrame.cpp (188919 => 188920)
--- trunk/Source/WebCore/page/MainFrame.cpp 2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/page/MainFrame.cpp 2015-08-25 18:03:00 UTC (rev 188920)
@@ -124,6 +124,20 @@
{
m_latchingState.removeLast();
}
+
+void MainFrame::removeLatchingStateForTarget(Element& targetNode)
+{
+ if (m_latchingState.isEmpty())
+ return;
+
+ m_latchingState.removeAllMatching([&targetNode] (ScrollLatchingState& state) {
+ auto* wheelElement = state.wheelEventElement();
+ if (!wheelElement)
+ return false;
+
+ return targetNode.isEqualNode(wheelElement);
+ });
+}
#endif
}
Modified: trunk/Source/WebCore/page/MainFrame.h (188919 => 188920)
--- trunk/Source/WebCore/page/MainFrame.h 2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/page/MainFrame.h 2015-08-25 18:03:00 UTC (rev 188920)
@@ -59,6 +59,7 @@
void pushNewLatchingState();
void popLatchingState();
void resetLatchingState();
+ void removeLatchingStateForTarget(Element&);
#endif // PLATFORM(MAC)
WEBCORE_EXPORT DiagnosticLoggingClient& diagnosticLoggingClient() const;