Bug 1420589 Part9: Dispatch pointer events to the capturing target even it's frame is destroyed. r=smaug.

MozReview-Commit-ID: DxNx3ByTdCW
This commit is contained in:
Stone Shih 2017-11-30 16:10:03 +08:00
Родитель 58f49aba41
Коммит a4f6e53467
5 изменённых файлов: 156 добавлений и 25 удалений

Просмотреть файл

@ -331,8 +331,8 @@ PointerEventHandler::GetPointerCapturingContent(uint32_t aPointerId)
return nullptr;
}
/* static */ nsIFrame*
PointerEventHandler::GetPointerCapturingFrame(WidgetGUIEvent* aEvent)
/* static */ nsIContent*
PointerEventHandler::GetPointerCapturingContent(WidgetGUIEvent* aEvent)
{
if (!IsPointerEventEnabled() || (aEvent->mClass != ePointerEventClass &&
aEvent->mClass != eMouseEventClass) ||
@ -346,17 +346,7 @@ PointerEventHandler::GetPointerCapturingFrame(WidgetGUIEvent* aEvent)
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

Просмотреть файл

@ -89,17 +89,16 @@ public:
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);

Просмотреть файл

@ -145,6 +145,7 @@ support-files =
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 =

Просмотреть файл

@ -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>

Просмотреть файл

@ -7188,11 +7188,35 @@ PresShell::HandleEvent(nsIFrame* aFrame,
}
}
nsIFrame* pointerCapturingFrame =
PointerEventHandler::GetPointerCapturingFrame(aEvent);
// Only capture mouse events and pointer events.
nsIContent* pointerCapturingContent =
PointerEventHandler::GetPointerCapturingContent(aEvent);
if (pointerCapturingFrame) {
frame = pointerCapturingFrame;
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();
@ -7204,7 +7228,8 @@ PresShell::HandleEvent(nsIFrame* aFrame,
// 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 {
@ -7228,7 +7253,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
// 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))) {