mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 03:47:31 +09:00
Issue #2135 - Bug 1413102: Ensure Shadow DOM boundaries are dealt properly in event handling
* RE: BasicEvents.h - our WidgetEvent is not movable (yet), so the change that requires that wasn't included. * Parts of this use code that was introduced in bug 1427511. For now, they were replaced with their equivalents.
This commit is contained in:
parent
e286eb8adb
commit
24572438a0
9 changed files with 299 additions and 70 deletions
|
|
@ -148,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
|
|||
tmp->mEvent->mCurrentTarget = nullptr;
|
||||
tmp->mEvent->mOriginalTarget = nullptr;
|
||||
tmp->mEvent->mRelatedTarget = nullptr;
|
||||
tmp->mEvent->mOriginalRelatedTarget = nullptr;
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
|
|
@ -176,6 +177,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget)
|
||||
switch (tmp->mEvent->mClass) {
|
||||
case eDragEventClass: {
|
||||
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
|
||||
|
|
@ -1227,44 +1229,6 @@ Event::SetOwner(EventTarget* aOwner)
|
|||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
nsIContent*
|
||||
Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget)
|
||||
{
|
||||
if (!aCurrentTarget || !aRelatedTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Walk up the ancestor node trees of the related target until
|
||||
// we encounter the node tree of the current target in order
|
||||
// to find the adjusted related target. Walking up the tree may
|
||||
// not find a common ancestor node tree if the related target is in
|
||||
// an ancestor tree, but in that case it does not need to be adjusted.
|
||||
ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
|
||||
if (!currentTargetShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* relatedTarget = aCurrentTarget;
|
||||
while (relatedTarget) {
|
||||
ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
|
||||
if (currentTargetShadow == ancestorShadow) {
|
||||
return relatedTarget;
|
||||
}
|
||||
|
||||
// Didn't find the ancestor tree, thus related target does not have to
|
||||
// adjusted.
|
||||
if (!ancestorShadow) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
relatedTarget = ancestorShadow->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Event::GetCancelBubble(bool* aCancelBubble)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -251,14 +251,6 @@ public:
|
|||
return mIsMainThreadEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given current target, returns the related target adjusted with
|
||||
* shadow DOM retargeting rules. Returns nullptr if related target
|
||||
* is not adjusted.
|
||||
*/
|
||||
static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
|
||||
nsIContent* aRelatedTarget);
|
||||
|
||||
void MarkUninitialized()
|
||||
{
|
||||
mEvent->mMessage = eVoidEvent;
|
||||
|
|
|
|||
|
|
@ -204,6 +204,16 @@ public:
|
|||
mNewTarget = aNewTarget;
|
||||
}
|
||||
|
||||
EventTarget* GetRetargetedRelatedTarget()
|
||||
{
|
||||
return mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
void SetRetargetedRelatedTarget(EventTarget* aTarget)
|
||||
{
|
||||
mRetargetedRelatedTarget = aTarget;
|
||||
}
|
||||
|
||||
void SetForceContentDispatch(bool aForce)
|
||||
{
|
||||
mFlags.mForceContentDispatch = aForce;
|
||||
|
|
@ -354,6 +364,7 @@ public:
|
|||
|
||||
private:
|
||||
nsCOMPtr<EventTarget> mTarget;
|
||||
nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
|
||||
|
||||
class EventTargetChainFlags
|
||||
{
|
||||
|
|
@ -422,6 +433,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
|
||||
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
|
||||
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
|
||||
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
|
||||
mItemFlags = aVisitor.mItemFlags;
|
||||
mItemData = aVisitor.mItemData;
|
||||
}
|
||||
|
|
@ -454,6 +466,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
{
|
||||
// Save the target so that it can be restored later.
|
||||
nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
|
||||
nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
|
||||
uint32_t chainLength = aChain.Length();
|
||||
uint32_t firstCanHandleEventTargetIdx =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
|
||||
|
|
@ -483,6 +496,30 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 14.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
// Note, the initial retargeting was done already when creating
|
||||
// event target chain, so we need to do this only after calling
|
||||
// HandleEvent, not before, like in the specification.
|
||||
if (item.GetRetargetedRelatedTarget()) {
|
||||
bool found = false;
|
||||
for (uint32_t j = i; j > 0; --j) {
|
||||
uint32_t childIndex = j - 1;
|
||||
EventTarget* relatedTarget =
|
||||
aChain[childIndex].GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
found = true;
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
aVisitor.mEvent->mRelatedTarget =
|
||||
aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Target
|
||||
|
|
@ -511,6 +548,14 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
aVisitor.mEvent->mTarget = newTarget;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dispatching-events
|
||||
// Step 15.2
|
||||
// "Set event's relatedTarget to tuple's relatedTarget."
|
||||
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
|
||||
if (relatedTarget) {
|
||||
aVisitor.mEvent->mRelatedTarget = relatedTarget;
|
||||
}
|
||||
|
||||
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
|
||||
if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
|
||||
item.ForceContentDispatch()) &&
|
||||
|
|
@ -542,6 +587,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
// Retarget for system event group (which does the default handling too).
|
||||
// Setting back the target which was used also for default event group.
|
||||
aVisitor.mEvent->mTarget = firstTarget;
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
aVisitor.mEvent->mFlags.mInSystemGroup = true;
|
||||
HandleEventTargetChain(aChain,
|
||||
aVisitor,
|
||||
|
|
@ -599,6 +645,7 @@ MayRetargetToChromeIfCanNotHandleEvent(
|
|||
EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
|
||||
}
|
||||
if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
|
||||
aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
// Event target couldn't handle the event. Try to propagate to chrome.
|
||||
EventTargetChainItem* chromeTargetEtci =
|
||||
EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
|
||||
|
|
@ -762,9 +809,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
aEvent->mOriginalTarget = aEvent->mTarget;
|
||||
}
|
||||
|
||||
aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
|
||||
bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
|
||||
content->IsInShadowTree()));
|
||||
bool isInAnon = (content && content->IsInAnonymousSubtree());
|
||||
|
||||
aEvent->mFlags.mIsBeingDispatched = true;
|
||||
|
||||
|
|
@ -772,7 +820,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
// GetEventTargetParent for the original target.
|
||||
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
|
||||
EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
|
||||
isInAnon);
|
||||
isInAnon, aEvent->mTarget);
|
||||
targetEtci->GetEventTargetParent(preVisitor);
|
||||
|
||||
if (!preVisitor.mCanHandle) {
|
||||
|
|
@ -810,12 +858,18 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
if (preVisitor.mEventTargetAtParent) {
|
||||
// Need to set the target of the event
|
||||
// so that also the next retargeting works.
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
|
||||
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
|
||||
}
|
||||
|
||||
if (preVisitor.mRetargetedRelatedTarget) {
|
||||
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
parentEtci->GetEventTargetParent(preVisitor);
|
||||
if (preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
topEtci = parentEtci;
|
||||
} else {
|
||||
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
|
||||
|
|
@ -825,6 +879,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
topEtci,
|
||||
disabledTarget);
|
||||
if (parentEtci && preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
EventTargetChainItem* item =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
|
||||
item->SetNewTarget(parentTarget);
|
||||
|
|
@ -871,6 +926,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
aEvent->mFlags.mIsBeingDispatched = false;
|
||||
aEvent->mFlags.mDispatchedAtLeastOnce = true;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 18
|
||||
// "If target's root is a shadow root, then set event's target attribute and
|
||||
// event's relatedTarget to null."
|
||||
nsCOMPtr<nsIContent> finalTarget = do_QueryInterface(aEvent->mTarget);
|
||||
// TODO: replace with IsShadowRoot() check once bug 1427511 lands.
|
||||
if (finalTarget && ShadowRoot::FromNode(finalTarget->SubtreeRoot())) {
|
||||
aEvent->mTarget = nullptr;
|
||||
aEvent->mOriginalTarget = nullptr;
|
||||
aEvent->mRelatedTarget = nullptr;
|
||||
aEvent->mOriginalRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
if (!externalDOMEvent && preVisitor.mDOMEvent) {
|
||||
// An dom::Event was created while dispatching the event.
|
||||
// Duplicate private data if someone holds a pointer to it.
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ public:
|
|||
WidgetEvent* aEvent,
|
||||
nsIDOMEvent* aDOMEvent,
|
||||
nsEventStatus aEventStatus,
|
||||
bool aIsInAnon)
|
||||
bool aIsInAnon,
|
||||
dom::EventTarget* aTargetInKnownToBeHandledScope)
|
||||
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
|
||||
, mCanHandle(true)
|
||||
, mAutomaticChromeDispatch(true)
|
||||
|
|
@ -126,8 +127,11 @@ public:
|
|||
, mRootOfClosedTree(false)
|
||||
, mParentIsSlotInClosedTree(false)
|
||||
, mParentIsChromeHandler(false)
|
||||
, mRelatedTargetRetargetedInCurrentScope(false)
|
||||
, mParentTarget(nullptr)
|
||||
, mEventTargetAtParent(nullptr)
|
||||
, mRetargetedRelatedTarget(nullptr)
|
||||
, mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -144,8 +148,12 @@ public:
|
|||
mRootOfClosedTree = false;
|
||||
mParentIsSlotInClosedTree = false;
|
||||
mParentIsChromeHandler = false;
|
||||
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
|
||||
// since it is used during event path creation to indicate whether
|
||||
// relatedTarget may need to be retargeted.
|
||||
mParentTarget = nullptr;
|
||||
mEventTargetAtParent = nullptr;
|
||||
mRetargetedRelatedTarget = nullptr;
|
||||
}
|
||||
|
||||
dom::EventTarget* GetParentTarget()
|
||||
|
|
@ -161,6 +169,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void IgnoreCurrentTarget()
|
||||
{
|
||||
mCanHandle = false;
|
||||
SetParentTarget(nullptr, false);
|
||||
mEventTargetAtParent = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member that must be set in GetEventTargetParent by event targets. If set to
|
||||
* false, indicates that this event target will not be handling the event and
|
||||
|
|
@ -229,6 +244,13 @@ public:
|
|||
*/
|
||||
bool mParentIsChromeHandler;
|
||||
|
||||
/**
|
||||
* True if event's related target has been already retargeted in the
|
||||
* current 'scope'. This should be set to false initially and whenever
|
||||
* event path creation crosses shadow boundary.
|
||||
*/
|
||||
bool mRelatedTargetRetargetedInCurrentScope;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parent item in the event target chain.
|
||||
|
|
@ -241,6 +263,19 @@ public:
|
|||
* which should be used when the event is handled at mParentTarget.
|
||||
*/
|
||||
dom::EventTarget* mEventTargetAtParent;
|
||||
|
||||
/**
|
||||
* If the related target of the event needs to be retargeted, set this
|
||||
* to a new EventTarget.
|
||||
*/
|
||||
dom::EventTarget* mRetargetedRelatedTarget;
|
||||
|
||||
/**
|
||||
* Set to the value of mEvent->mTarget of the previous scope in case of
|
||||
* Shadow DOM or such, and if there is no anonymous content this just points
|
||||
* to the initial target.
|
||||
*/
|
||||
dom::EventTarget* mTargetInKnownToBeHandledScope;
|
||||
};
|
||||
|
||||
class EventChainPostVisitor : public mozilla::EventChainVisitor
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue