author | Stone Shih <sshih@mozilla.com> |
Thu, 30 Nov 2017 16:10:03 +0800 | |
changeset 448727 | 2155af8d88c3bd116305378d7e22b947db339c55 |
parent 448726 | 49491891f6b1119387bad90bb9a10fd9468d382c |
child 448728 | d0538cb619eb80b400dc439a59579e8cf9e895b4 |
push id | 8527 |
push user | Callek@gmail.com |
push date | Thu, 11 Jan 2018 21:05:50 +0000 |
treeherder | mozilla-beta@95342d212a7a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1420589 |
milestone | 59.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/events/PointerEventHandler.cpp +++ b/dom/events/PointerEventHandler.cpp @@ -326,42 +326,32 @@ PointerEventHandler::GetPointerCapturing { PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId); if (pointerCaptureInfo) { return pointerCaptureInfo->mOverrideContent; } return nullptr; } -/* static */ nsIFrame* -PointerEventHandler::GetPointerCapturingFrame(WidgetGUIEvent* aEvent) +/* static */ nsIContent* +PointerEventHandler::GetPointerCapturingContent(WidgetGUIEvent* aEvent) { if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass && aEvent->mClass != eMouseEventClass) || aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) { // Pointer capture should only be applied to all pointer events and mouse // events except ePointerDown and eMouseDown; return nullptr; } WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (!mouseEvent) { return nullptr; } - - // Find the content which captures the pointer. - nsIContent* capturingContent = - GetPointerCapturingContent(mouseEvent->pointerId); - - if (!capturingContent) { - return nullptr; - } - // Return the content's primary frame as the target frame. - nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame(); - return capturingFrame ? capturingFrame : nullptr; + return GetPointerCapturingContent(mouseEvent->pointerId); } /* static */ void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) { // We should check that aChild does not contain pointer capturing elements. // If it does we should release the pointer capture for the elements. for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
--- a/dom/events/PointerEventHandler.h +++ b/dom/events/PointerEventHandler.h @@ -84,27 +84,26 @@ public: static void ProcessPointerCaptureForTouch(WidgetTouchEvent* aEvent); static void CheckPointerCaptureState(WidgetPointerEvent* aEvent); // Implicitly get and release capture of current pointer for touch. static void ImplicitlyCapturePointer(nsIFrame* aFrame, WidgetEvent* aEvent); static void ImplicitlyReleasePointerCapture(WidgetEvent* aEvent); /** - * GetPointerCapturingFrame returns a target frame of aEvent. If the event is - * a mouse or pointer event (except mousedown and pointerdown), the pointer - * may be captured by a content. This method returns the capturing content's - * primary frame. Otherwise, nullptr. + * GetPointerCapturingContent returns a target content which captures the + * pointer. It's applied to mouse or pointer event (except mousedown and + * pointerdown). When capturing, return the content. Otherwise, nullptr. * * @param aEvent A mouse event or pointer event which may be * captured. * - * @return Target frame for aEvent. + * @return Target content for aEvent. */ - static nsIFrame* GetPointerCapturingFrame(WidgetGUIEvent* aEvent); + static nsIContent* GetPointerCapturingContent(WidgetGUIEvent* aEvent); static nsIContent* GetPointerCapturingContent(uint32_t aPointerId); // Release pointer capture if captured by the specified content or it's // descendant. This is called to handle the case that the pointer capturing // content or it's parent is removed from the document. static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
--- a/dom/events/test/pointerevents/mochitest.ini +++ b/dom/events/test/pointerevents/mochitest.ini @@ -140,12 +140,13 @@ support-files = pointerevent_touch-action-pan-down-css_touch-manual.html pointerevent_touch-action-pan-left-css_touch-manual.html pointerevent_touch-action-pan-right-css_touch-manual.html pointerevent_touch-action-pan-up-css_touch-manual.html [test_trigger_fullscreen_by_pointer_events.html] support-files = file_test_trigger_fullscreen.html [test_trigger_popup_by_pointer_events.html] +[test_remove_frame_when_got_pointer_capture.html] [test_getCoalescedEvents.html] skip-if = !e10s support-files = ../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
new file mode 100644 --- /dev/null +++ b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Test for triggering popup by pointer events</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<div id="div1" style="width: 50px; height: 50px; background: green"></div> +<div id="div2" style="width: 50px; height: 50px; background: green"></div> +<script> + +SimpleTest.waitForExplicitFinish(); + +function startTest() { + let div1 = document.getElementById("div1"); + let divEvents = [ + "pointerdown", + "gotpointercapture", + "pointermove", + "pointerup", + "lostpointercapture", + "mousedown", + "mousemove", + "mouseup", + ]; + + let documentEvents = [ + "pointerdown", + "pointermove", + "pointerup", + "mousedown", + "mousemove", + "mouseup", + ]; + + divEvents.forEach((event) => { + div1.addEventListener(event, (e) => { + ok(divEvents.indexOf(e.type) >= 0, " don't expect " + e.type); + divEvents = divEvents.filter(item => item !== e.type); + }, { once: true }); + }); + + documentEvents.forEach((event) => { + document.addEventListener(event, (e) => { + is(e.target, div1, e.type + " should be dispatched to div1"); + }, { once: true }); + }); + + div1.addEventListener("pointerdown", (e) => { + div1.setPointerCapture(e.pointerId); + }); + + div1.addEventListener("gotpointercapture", (e) => { + div1.style.display = "none"; + }); + + synthesizeMouseAtCenter(div1, {type: "mousedown"}); + synthesizeMouseAtCenter(div2, {type: "mousemove"}); + synthesizeMouseAtCenter(div2, {type: "mouseup"}); + + ok(divEvents.length == 0, " expect " + divEvents); + + divEvents = [ + "pointerdown", + "gotpointercapture", + "pointermove", + "pointerup", + "lostpointercapture", + "touchstart", + "touchmove", + "touchend", + ]; + + documentEvents = [ + "pointerdown", + "pointermove", + "pointerup", + "touchstart", + "touchmove", + "touchend", + ]; + divEvents.forEach((event) => { + div1.addEventListener(event, (e) => { + ok(divEvents.indexOf(e.type) >= 0, " don't expect " + e.type); + divEvents = divEvents.filter(item => item !== e.type); + }, { once: true }); + }); + + documentEvents.forEach((event) => { + document.addEventListener(event, (e) => { + is(e.target, div1, e.type + " should be dispatched to div1"); + }, { once: true }); + }); + + div1.style.display = "block"; + synthesizeMouseAtCenter(div1, {type: "mousemove"}); + synthesizeTouch(div1, 5, 5, { type: "touchstart" }); + synthesizeTouch(div2, 5, 5, { type: "touchmove" }); + synthesizeTouch(div2, 5, 5, { type: "touchend" }); + + ok(divEvents.length == 0, " expect " + divEvents); + SimpleTest.finish(); +} + +SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({ + "set": [["dom.w3c_pointer_events.enabled", true]] + }, startTest); +}); + +</script> +</body> +</html>
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -7183,33 +7183,58 @@ PresShell::HandleEvent(nsIFrame* aFrame, PointerEventHandler::MaybeProcessPointerCapture(aEvent); // Prevent application crashes, in case damaged frame. if (!frameKeeper.IsAlive()) { NS_WARNING("Nothing to handle this event!"); return NS_OK; } } - nsIFrame* pointerCapturingFrame = - PointerEventHandler::GetPointerCapturingFrame(aEvent); - - if (pointerCapturingFrame) { - frame = pointerCapturingFrame; + // Only capture mouse events and pointer events. + nsIContent* pointerCapturingContent = + PointerEventHandler::GetPointerCapturingContent(aEvent); + + if (pointerCapturingContent) { + nsIFrame* pointerCapturingFrame = + pointerCapturingContent->GetPrimaryFrame(); + + if (!pointerCapturingFrame) { + // Dispatch events to the capturing content even it's frame is + // destroyed. + PointerEventHandler::DispatchPointerFromMouseOrTouch( + this, nullptr, pointerCapturingContent, aEvent, false, aEventStatus, + nullptr); + + PresShell* shell = GetShellForEventTarget(nullptr, + pointerCapturingContent); + + if (!shell) { + // The capturing element could be changed when dispatch pointer + // events. + return NS_OK; + } + return shell->HandleEventWithTarget(aEvent, nullptr, + pointerCapturingContent, + aEventStatus, true); + } else { + frame = pointerCapturingFrame; + } } WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); bool isWindowLevelMouseExit = (aEvent->mMessage == eMouseExitFromWidget) && (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel); // Get the frame at the event point. However, don't do this if we're // capturing and retargeting the event because the captured frame will // be used instead below. Also keep using the root frame if we're dealing // with a window-level mouse exit event since we want to start sending // mouse out events at the root EventStateManager. - if (!captureRetarget && !isWindowLevelMouseExit && !pointerCapturingFrame) { + if (!captureRetarget && !isWindowLevelMouseExit && + !pointerCapturingContent) { if (aEvent->mClass == eTouchEventClass) { frame = TouchManager::SetupTarget(aEvent->AsTouchEvent(), frame); } else { uint32_t flags = 0; nsPoint eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); if (mouseEvent && mouseEvent->mClass == eMouseEventClass && @@ -7223,17 +7248,17 @@ PresShell::HandleEvent(nsIFrame* aFrame, } } } // if a node is capturing the mouse, check if the event needs to be // retargeted at the capturing content instead. This will be the case when // capture retargeting is being used, no frame was found or the frame's // content is not a descendant of the capturing content. - if (capturingContent && !pointerCapturingFrame && + if (capturingContent && !pointerCapturingContent && (gCaptureInfo.mRetargetToElement || !frame->GetContent() || !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(), capturingContent))) { // A check was already done above to ensure that capturingContent is // in this presshell. NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(), "Unexpected document"); nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();