Bug 1671657 - setPointerCapture should do nothing if the element's node document is not active document for the pointer; r=smaug

Depends on D94297

Differential Revision: https://phabricator.services.mozilla.com/D94001
This commit is contained in:
Edgar Chen 2020-10-30 08:31:42 +00:00
parent 33c996c701
commit 2737da4055
6 changed files with 42 additions and 122 deletions

View File

@ -1197,13 +1197,14 @@ class Element : public FragmentOrElement {
ErrorResult& aError); ErrorResult& aError);
void SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { void SetPointerCapture(int32_t aPointerId, ErrorResult& aError) {
bool activeState = false;
if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) && if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) &&
aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) {
aError.ThrowNotFoundError("Invalid pointer id"); aError.ThrowNotFoundError("Invalid pointer id");
return; return;
} }
if (!PointerEventHandler::GetPointerInfo(aPointerId, activeState)) { const PointerInfo* pointerInfo =
PointerEventHandler::GetPointerInfo(aPointerId);
if (!pointerInfo) {
aError.ThrowNotFoundError("Invalid pointer id"); aError.ThrowNotFoundError("Invalid pointer id");
return; return;
} }
@ -1217,19 +1218,19 @@ class Element : public FragmentOrElement {
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
if (!activeState) { if (!pointerInfo->mActiveState ||
pointerInfo->mActiveDocument != OwnerDoc()) {
return; return;
} }
PointerEventHandler::RequestPointerCaptureById(aPointerId, this); PointerEventHandler::RequestPointerCaptureById(aPointerId, this);
} }
void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) {
bool activeState = false;
if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) && if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) &&
aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) {
aError.ThrowNotFoundError("Invalid pointer id"); aError.ThrowNotFoundError("Invalid pointer id");
return; return;
} }
if (!PointerEventHandler::GetPointerInfo(aPointerId, activeState)) { if (!PointerEventHandler::GetPointerInfo(aPointerId)) {
aError.ThrowNotFoundError("Invalid pointer id"); aError.ThrowNotFoundError("Invalid pointer id");
return; return;
} }

View File

@ -567,7 +567,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
} }
} }
PointerEventHandler::UpdateActivePointerState(mouseEvent); PointerEventHandler::UpdateActivePointerState(mouseEvent, aTargetContent);
switch (aEvent->mMessage) { switch (aEvent->mMessage) {
case eContextMenu: case eContextMenu:

View File

@ -19,20 +19,6 @@ using namespace dom;
Maybe<int32_t> PointerEventHandler::sSpoofedPointerId; Maybe<int32_t> PointerEventHandler::sSpoofedPointerId;
class PointerInfo final {
public:
uint16_t mPointerType;
bool mActiveState;
bool mPrimaryState;
bool mPreventMouseEventByContent;
explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
bool aPrimaryState)
: mPointerType(aPointerType),
mActiveState(aActiveState),
mPrimaryState(aPrimaryState),
mPreventMouseEventByContent(false) {}
};
// Keeps a map between pointerId and element that currently capturing pointer // Keeps a map between pointerId and element that currently capturing pointer
// with such pointerId. If pointerId is absent in this map then nobody is // with such pointerId. If pointerId is absent in this map then nobody is
// capturing it. Additionally keep information about pending capturing content. // capturing it. Additionally keep information about pending capturing content.
@ -81,7 +67,8 @@ bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
} }
/* static */ /* static */
void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent) { void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
nsIContent* aTargetContent) {
if (!StaticPrefs::dom_w3c_pointer_events_enabled() || !aEvent) { if (!StaticPrefs::dom_w3c_pointer_events_enabled() || !aEvent) {
return; return;
} }
@ -90,17 +77,21 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent) {
// In this case we have to know information about available mouse pointers // In this case we have to know information about available mouse pointers
sActivePointersIds->Put( sActivePointersIds->Put(
aEvent->pointerId, aEvent->pointerId,
new PointerInfo(false, aEvent->mInputSource, true)); new PointerInfo(false, aEvent->mInputSource, true, nullptr));
MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId); MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
break; break;
case ePointerDown: case ePointerDown:
// In this case we switch pointer to active state // In this case we switch pointer to active state
if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
// XXXedgar, test could possibly synthesize a mousedown event on a
// coordinate outside the browser window and cause aTargetContent to be
// nullptr, not sure if this also happens on real usage.
sActivePointersIds->Put( sActivePointersIds->Put(
pointerEvent->pointerId, pointerEvent->pointerId,
new PointerInfo(true, pointerEvent->mInputSource, new PointerInfo(
pointerEvent->mIsPrimary)); true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
MaybeCacheSpoofedPointerID(pointerEvent->mInputSource, MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
pointerEvent->pointerId); pointerEvent->pointerId);
} }
@ -118,7 +109,7 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent) {
sActivePointersIds->Put( sActivePointersIds->Put(
pointerEvent->pointerId, pointerEvent->pointerId,
new PointerInfo(false, pointerEvent->mInputSource, new PointerInfo(false, pointerEvent->mInputSource,
pointerEvent->mIsPrimary)); pointerEvent->mIsPrimary, nullptr));
} else { } else {
sActivePointersIds->Remove(pointerEvent->pointerId); sActivePointersIds->Remove(pointerEvent->pointerId);
} }
@ -267,14 +258,8 @@ void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
} }
/* static */ /* static */
bool PointerEventHandler::GetPointerInfo(uint32_t aPointerId, const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
bool& aActiveState) { return sActivePointersIds->Get(aPointerId);
PointerInfo* pointerInfo = nullptr;
if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
aActiveState = pointerInfo->mActiveState;
return true;
}
return false;
} }
/* static */ /* static */

View File

@ -10,6 +10,7 @@
#include "mozilla/EventForwards.h" #include "mozilla/EventForwards.h"
#include "mozilla/MouseEvents.h" #include "mozilla/MouseEvents.h"
#include "mozilla/TouchEvents.h" #include "mozilla/TouchEvents.h"
#include "mozilla/WeakPtr.h"
class nsIFrame; class nsIFrame;
class nsIContent; class nsIContent;
@ -20,6 +21,7 @@ class PresShell;
namespace dom { namespace dom {
class BrowserParent; class BrowserParent;
class Document;
class Element; class Element;
}; // namespace dom }; // namespace dom
@ -38,6 +40,22 @@ class PointerCaptureInfo final {
bool Empty() { return !(mPendingElement || mOverrideElement); } bool Empty() { return !(mPendingElement || mOverrideElement); }
}; };
class PointerInfo final {
public:
uint16_t mPointerType;
bool mActiveState;
bool mPrimaryState;
bool mPreventMouseEventByContent;
WeakPtr<dom::Document> mActiveDocument;
explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
bool aPrimaryState, dom::Document* aActiveDocument)
: mPointerType(aPointerType),
mActiveState(aActiveState),
mPrimaryState(aPrimaryState),
mPreventMouseEventByContent(false),
mActiveDocument(aActiveDocument) {}
};
class PointerEventHandler final { class PointerEventHandler final {
public: public:
// Called in nsLayoutStatics::Initialize/Shutdown to initialize pointer event // Called in nsLayoutStatics::Initialize/Shutdown to initialize pointer event
@ -50,7 +68,8 @@ class PointerEventHandler final {
// Called in ESM::PreHandleEvent to update current active pointers in a hash // Called in ESM::PreHandleEvent to update current active pointers in a hash
// table. // table.
static void UpdateActivePointerState(WidgetMouseEvent* aEvent); static void UpdateActivePointerState(WidgetMouseEvent* aEvent,
nsIContent* aTargetContent);
// Request/release pointer capture of the specified pointer by the element. // Request/release pointer capture of the specified pointer by the element.
static void RequestPointerCaptureById(uint32_t aPointerId, static void RequestPointerCaptureById(uint32_t aPointerId,
@ -74,11 +93,9 @@ class PointerEventHandler final {
// Get the pointer captured info of the specified pointer. // Get the pointer captured info of the specified pointer.
static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId); static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId);
// GetPointerInfo returns true if pointer with aPointerId is situated in // Return the PointerInfo if the pointer with aPointerId is situated in device
// device, false otherwise. // , nullptr otherwise.
// aActiveState is additional information, which shows state of pointer like static const PointerInfo* GetPointerInfo(uint32_t aPointerId);
// button state for mouse.
static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
// CheckPointerCaptureState checks cases, when got/lostpointercapture events // CheckPointerCaptureState checks cases, when got/lostpointercapture events
// should be fired. // should be fired.

View File

@ -25,8 +25,6 @@
topLevelDocumentEventHandling, topLevelDocumentEventHandling,
topLevelDocumentEventHandlingWithTouch, topLevelDocumentEventHandlingWithTouch,
iframeEventHandling, iframeEventHandling,
iframeEventHandlingWithCapturingInParent,
iframeEventHandlingwithHiddenCapturingInParent
]; ];
function next() { function next() {
@ -229,79 +227,6 @@
next(); next();
} }
function iframeEventHandlingWithCapturingInParent(
name = "iframeEventHandlingWithCapturingInParent") {
var pid;
var iframe = document.getElementById("iframe");
var doc = iframe.contentDocument;
doc.body.innerHTML = "";
doc.head.innerHTML =
"<style>" + document.getElementsByTagName("style")[0].textContent + "</style>";
var area = doc.createElement("div");
area.id = "area";
var target = doc.createElement("div");
target.id = "target";
area.appendChild(target);
doc.body.appendChild(area);
var body = doc.body;
var html = doc.documentElement;
var win = doc.defaultView;
var targetOutsideIframe = document.getElementById("targetOutsideIframe");
var eventLog = [];
function captureEvent(e) {
eventLog.push([e.type, e.composedPath()]);
}
var expectedEvents = [
["pointerdown", [ target, area, body, html, doc, win ]],
["mousedown", [ target, area, body, html, doc, win ]],
["pointerup", [ targetOutsideIframe, document.body, document.documentElement, document, window ]],
["mouseup", [ targetOutsideIframe, document.body, document.documentElement, document, window ]],
["click", [ target, area, body, html, doc, win ]],
];
win.addEventListener("pointerdown",
function(e) {
captureEvent(e);
pid = e.pointerId;
targetOutsideIframe.setPointerCapture(pid);
}, { once: true});
win.addEventListener("mousedown",
function(e) {
captureEvent(e);
}, { once: true});
window.addEventListener("pointerup",
function(e) {
captureEvent(e);
targetOutsideIframe.releasePointerCapture(pid);
}, { once: true});
window.addEventListener("mouseup", function(e) {
captureEvent(e);
}, { once: true});
win.addEventListener("click", function(e) {
captureEvent(e);
}, { once: true});
synthesizeMouseAtCenter(target, {}, win);
opener.is(eventLog.length, expectedEvents.length,
`[${name}] Same number events expected.`);
for (var i = 0; i < eventLog.length; ++i) {
opener.is(eventLog[i][0], expectedEvents[i][0],
`${name} ${i}`);
for (var j = 0; j < eventLog[i][1].length; ++j) {
opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
`${name} ${i} ${j}`);
}
}
next();
}
function iframeEventHandlingwithHiddenCapturingInParent() {
document.getElementById("targetOutsideIframe").style.display = "none";
document.getElementById("targetOutsideIframe").offsetLeft;
iframeEventHandlingWithCapturingInParent("iframeEventHandlingwithHiddenCapturingInParent");
}
</script> </script>
</head> </head>
<body onload="start();"> <body onload="start();">

View File

@ -1,8 +0,0 @@
[pointerevent_mouse_pointercapture_inactivate_pointer.html]
expected: TIMEOUT
[setPointerCapture: pointer active in outer frame, set capture to inner frame]
expected: TIMEOUT
[setPointerCapture: pointer active in inner frame, set capture to outer frame]
expected: NOTRUN