зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1413102 - Ensure Shadow DOM boundaries are dealt properly in event handling, r=masayuki
This commit is contained in:
Родитель
a3eb8e6045
Коммит
b9ac198b01
|
@ -917,6 +917,22 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
|
|||
return aContent;
|
||||
}
|
||||
|
||||
already_AddRefed<nsINode>
|
||||
FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
|
||||
{
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
|
||||
if (!node || !node->ChromeOnlyAccess()) {
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
if (!node->IsContent()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
|
@ -937,24 +953,12 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
// chrome access only subtree or if we are about to propagate out of
|
||||
// a shadow root to a shadow root host.
|
||||
((this == aVisitor.mEvent->mOriginalTarget &&
|
||||
!ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
|
||||
!ChromeOnlyAccess()) || isAnonForEvents)) {
|
||||
nsCOMPtr<nsIContent> relatedTarget =
|
||||
do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
|
||||
if (relatedTarget &&
|
||||
relatedTarget->OwnerDoc() == OwnerDoc()) {
|
||||
|
||||
// In the web components case, we may need to stop propagation of events
|
||||
// at shadow root host.
|
||||
if (GetShadowRoot()) {
|
||||
nsIContent* adjustedTarget =
|
||||
Event::GetShadowRelatedTarget(this, relatedTarget);
|
||||
if (this == adjustedTarget) {
|
||||
aVisitor.SetParentTarget(nullptr, false);
|
||||
aVisitor.mCanHandle = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If current target is anonymous for events or we know that related
|
||||
// target is descendant of an element which is anonymous for events,
|
||||
// we may want to stop event propagation.
|
||||
|
@ -1070,6 +1074,104 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
} else {
|
||||
aVisitor.SetParentTarget(GetComposedDoc(), false);
|
||||
}
|
||||
|
||||
if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
|
||||
// We don't support Shadow DOM in native anonymous content yet.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
|
||||
if (aVisitor.mEvent->mOriginalRelatedTarget) {
|
||||
// https://dom.spec.whatwg.org/#concept-event-dispatch
|
||||
// Step 3.
|
||||
// "Let relatedTarget be the result of retargeting event's relatedTarget
|
||||
// against target if event's relatedTarget is non-null, and null
|
||||
// otherwise."
|
||||
//
|
||||
// This is a bit complicated because the event might be from native
|
||||
// anonymous content, but we need to deal with non-native anonymous
|
||||
// content there.
|
||||
bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
|
||||
nsCOMPtr<nsINode> originalTargetAsNode;
|
||||
// Use of mOriginalTargetIsInAnon is an optimization here.
|
||||
if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
|
||||
originalTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
|
||||
initialTarget = originalTargetAsNode == this;
|
||||
}
|
||||
if (initialTarget) {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (!originalTargetAsNode) {
|
||||
originalTargetAsNode =
|
||||
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
|
||||
}
|
||||
|
||||
if (relatedTargetAsNode && originalTargetAsNode) {
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
|
||||
if (originalTargetAsNode == retargetedRelatedTarget &&
|
||||
retargetedRelatedTarget != relatedTargetAsNode) {
|
||||
// Step 4.
|
||||
// "If target is relatedTarget and target is not event's
|
||||
// relatedTarget, then return true."
|
||||
aVisitor.IgnoreCurrentTarget();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Part of step 5. Retargeting target has happened already higher
|
||||
// up in this method.
|
||||
// "Append to an event path with event, target, targetOverride,
|
||||
// relatedTarget, and false."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsINode> relatedTargetAsNode =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
|
||||
if (relatedTargetAsNode) {
|
||||
// Step 11.3.
|
||||
// "Let relatedTarget be the result of retargeting event's
|
||||
// relatedTarget against parent if event's relatedTarget is non-null,
|
||||
// and null otherwise.".
|
||||
nsINode* retargetedRelatedTarget =
|
||||
nsContentUtils::Retarget(relatedTargetAsNode, this);
|
||||
nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
|
||||
FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
this, targetInKnownToBeHandledScope->SubtreeRoot())) {
|
||||
// Part of step 11.4.
|
||||
// "If target's root is a shadow-including inclusive ancestor of
|
||||
// parent, then"
|
||||
// "...Append to an event path with event, parent, null, relatedTarget,
|
||||
// " and slot-in-closed-tree."
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
} else if (this == retargetedRelatedTarget) {
|
||||
// Step 11.5
|
||||
// "Otherwise, if parent and relatedTarget are identical, then set
|
||||
// parent to null."
|
||||
aVisitor.IgnoreCurrentTarget();
|
||||
// Old code relies on mTarget to point to the first element which
|
||||
// was not added to the event target chain because of mCanHandle
|
||||
// being false, but in Shadow DOM case mTarget really should
|
||||
// point to a node in Shadow DOM.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
|
||||
return NS_OK;
|
||||
} else {
|
||||
// Step 11.6
|
||||
aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slot) {
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -301,6 +301,8 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
{
|
||||
aVisitor.mCanHandle = true;
|
||||
aVisitor.mRootOfClosedTree = IsClosed();
|
||||
// Inform that we're about to exit the current scope.
|
||||
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
||||
|
||||
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
|
||||
if (!aVisitor.mEvent->mFlags.mComposed) {
|
||||
|
@ -323,12 +325,10 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|||
nsIContent* shadowHost = GetHost();
|
||||
aVisitor.SetParentTarget(shadowHost, false);
|
||||
|
||||
if (aVisitor.mOriginalTargetIsInAnon) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
||||
if (content && content->GetBindingParent() == shadowHost) {
|
||||
aVisitor.mEventTargetAtParent = shadowHost;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -2558,6 +2558,34 @@ nsContentUtils::ContentIsHostIncludingDescendantOf(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
|
||||
{
|
||||
MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
|
||||
MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
|
||||
|
||||
if (aPossibleAncestor == aPossibleDescendant->GetComposedDoc()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
if (aPossibleDescendant == aPossibleAncestor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aPossibleDescendant->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
|
||||
ShadowRoot* shadowRoot =
|
||||
ShadowRoot::FromNode(const_cast<nsINode*>(aPossibleDescendant));
|
||||
aPossibleDescendant = shadowRoot ? shadowRoot->GetHost() : nullptr;
|
||||
} else {
|
||||
aPossibleDescendant = aPossibleDescendant->GetParentNode();
|
||||
}
|
||||
} while (aPossibleDescendant);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
|
||||
|
@ -2615,6 +2643,30 @@ nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
|
|||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
nsINode*
|
||||
nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB)
|
||||
{
|
||||
while (true && aTargetA) {
|
||||
// If A's root is not a shadow root...
|
||||
nsINode* root = aTargetA->SubtreeRoot();
|
||||
if (!root->IsShadowRoot()) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
// or A's root is a shadow-including inclusive ancestor of B...
|
||||
if (nsContentUtils::ContentIsShadowIncludingDescendantOf(aTargetB, root)) {
|
||||
// ...then return A.
|
||||
return aTargetA;
|
||||
}
|
||||
|
||||
aTargetA = ShadowRoot::FromNode(root)->GetHost();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::GetAncestors(nsINode* aNode,
|
||||
|
|
|
@ -328,6 +328,13 @@ public:
|
|||
static bool ContentIsHostIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to above, but does special case only ShadowRoot,
|
||||
* not HTMLTemplateElement.
|
||||
*/
|
||||
static bool ContentIsShadowIncludingDescendantOf(
|
||||
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Similar to ContentIsDescendantOf except it crosses document boundaries,
|
||||
* this function uses ancestor/descendant relations in the composed document
|
||||
|
@ -356,6 +363,13 @@ public:
|
|||
ContentIsFlattenedTreeDescendantOfForStyle(const nsINode* aPossibleDescendant,
|
||||
const nsINode* aPossibleAncestor);
|
||||
|
||||
/**
|
||||
* Retarget an object A against an object B
|
||||
* @see https://dom.spec.whatwg.org/#retarget
|
||||
*/
|
||||
static nsINode*
|
||||
Retarget(nsINode* aTargetA, nsINode* aTargetB);
|
||||
|
||||
/*
|
||||
* This method fills the |aArray| with all ancestor nodes of |aNode|
|
||||
* including |aNode| at the zero index.
|
||||
|
|
|
@ -154,6 +154,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();
|
||||
|
@ -182,6 +183,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();
|
||||
|
@ -543,8 +545,6 @@ Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
|
|||
nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
|
||||
if (relatedTarget) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
|
||||
nsCOMPtr<nsIContent> currentTarget =
|
||||
do_QueryInterface(mEvent->mCurrentTarget);
|
||||
|
||||
if (content && content->ChromeOnlyAccess() &&
|
||||
!nsContentUtils::CanAccessNativeAnon()) {
|
||||
|
@ -552,12 +552,6 @@ Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
|
|||
relatedTarget = do_QueryInterface(content);
|
||||
}
|
||||
|
||||
nsIContent* shadowRelatedTarget =
|
||||
GetShadowRelatedTarget(currentTarget, content);
|
||||
if (shadowRelatedTarget) {
|
||||
relatedTarget = shadowRelatedTarget;
|
||||
}
|
||||
|
||||
if (relatedTarget) {
|
||||
relatedTarget = relatedTarget->GetTargetForDOMEvent();
|
||||
}
|
||||
|
@ -1218,44 +1212,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;
|
||||
}
|
||||
|
||||
void
|
||||
Event::GetWidgetEventType(WidgetEvent* aEvent, nsAString& aType)
|
||||
{
|
||||
|
|
|
@ -267,14 +267,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;
|
||||
|
|
|
@ -200,6 +200,16 @@ public:
|
|||
mNewTarget = aNewTarget;
|
||||
}
|
||||
|
||||
EventTarget* GetRetargetedRelatedTarget()
|
||||
{
|
||||
return mRetargetedRelatedTarget;
|
||||
}
|
||||
|
||||
void SetRetargetedRelatedTarget(EventTarget* aTarget)
|
||||
{
|
||||
mRetargetedRelatedTarget = aTarget;
|
||||
}
|
||||
|
||||
void SetForceContentDispatch(bool aForce)
|
||||
{
|
||||
mFlags.mForceContentDispatch = aForce;
|
||||
|
@ -350,6 +360,7 @@ public:
|
|||
|
||||
private:
|
||||
nsCOMPtr<EventTarget> mTarget;
|
||||
nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
|
||||
|
||||
class EventTargetChainFlags
|
||||
{
|
||||
|
@ -418,6 +429,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;
|
||||
}
|
||||
|
@ -450,6 +462,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);
|
||||
|
@ -479,6 +492,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
|
||||
|
@ -507,6 +544,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()) &&
|
||||
|
@ -529,6 +574,7 @@ EventTargetChainItem::HandleEventTargetChain(
|
|||
|
||||
// Setting back the original target of the event.
|
||||
aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
|
||||
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
|
||||
|
||||
// Special handling if PresShell (or some other caller)
|
||||
// used a callback object.
|
||||
|
@ -539,6 +585,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 = firstRelatedTarget;
|
||||
aVisitor.mEvent->mFlags.mInSystemGroup = true;
|
||||
HandleEventTargetChain(aChain,
|
||||
aVisitor,
|
||||
|
@ -596,6 +643,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);
|
||||
|
@ -767,9 +815,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;
|
||||
|
||||
|
@ -777,7 +826,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) {
|
||||
|
@ -815,12 +864,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);
|
||||
|
@ -830,6 +885,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
|
|||
topEtci,
|
||||
disabledTarget);
|
||||
if (parentEtci && preVisitor.mCanHandle) {
|
||||
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
|
||||
EventTargetChainItem* item =
|
||||
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
|
||||
item->SetNewTarget(parentTarget);
|
||||
|
@ -875,6 +931,18 @@ 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);
|
||||
if (finalTarget && finalTarget->SubtreeRoot()->IsShadowRoot()) {
|
||||
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.
|
||||
|
|
|
@ -115,7 +115,8 @@ public:
|
|||
WidgetEvent* aEvent,
|
||||
nsIDOMEvent* aDOMEvent,
|
||||
nsEventStatus aEventStatus,
|
||||
bool aIsInAnon)
|
||||
bool aIsInAnon,
|
||||
dom::EventTarget* aTargetInKnownToBeHandledScope)
|
||||
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
|
||||
, mCanHandle(true)
|
||||
, mAutomaticChromeDispatch(true)
|
||||
|
@ -128,8 +129,11 @@ public:
|
|||
, mRootOfClosedTree(false)
|
||||
, mParentIsSlotInClosedTree(false)
|
||||
, mParentIsChromeHandler(false)
|
||||
, mRelatedTargetRetargetedInCurrentScope(false)
|
||||
, mParentTarget(nullptr)
|
||||
, mEventTargetAtParent(nullptr)
|
||||
, mRetargetedRelatedTarget(nullptr)
|
||||
, mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -146,8 +150,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()
|
||||
|
@ -163,6 +171,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
|
||||
|
@ -231,6 +246,12 @@ 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.
|
||||
|
@ -243,6 +264,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
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
[event-composed-path-with-related-target.html]
|
||||
[Event path for an event with a relatedTarget. Event shoul be dispatched if 1) target and relatedTarget are same, and 2) they are not in a shadow tree.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. Event should stop at the shadow root]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. Event should not be dispatched if 1) target and relatedTarget are same, and 2) both are in a shadow tree.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. target and relaterTarget do not share any shadow-including ancestor. target is in a shadow tree.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. target and relaterTarget do not share any shadow-including ancestor. target is not in a shadow tree]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. target and relaterTarget share the same shadow-including ancestor. Both are in shadow trees.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. relaterTarget is a shadow-including ancestor of target.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. target is a shadow-including ancestor of relatedTarget.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. target is assigned to a slot.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. relatedTarget is assigned to a slot.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. Event should be dispatched at every slots.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. Event should be dispatched at every slots. relatedTarget should be correctly retargeted.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget which is identical to target. Event should be dispatched and should stop at the shadow root.]
|
||||
expected: FAIL
|
||||
|
||||
[Event path for an event with a relatedTarget. relatedTarget is a shadow-including ancestor of target.]
|
||||
expected: FAIL
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
[event-post-dispatch.html]
|
||||
[Event properties post dispatch with an open ShadowRoot (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with a closed ShadowRoot (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with nested ShadowRoots (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the same shadow tree. (composed: true)]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the same shadow tree. (composed: false)]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the document tree and the shadow tree. (composed: false)]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the different shadow trees. (composed: true)]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the different shadow trees. (composed: false)]
|
||||
expected: FAIL
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
[event-with-related-target.html]
|
||||
[Firing an event at B1a with relatedNode at B1 with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at B1 with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at B1b1 with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at B1b1 with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1b1 with relatedNode at B1a with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1b1 with relatedNode at B1a with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at D1 with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at D1 with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at D1 with relatedNode at B1a with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at D1 with relatedNode at B1a with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at A1a with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at A1a with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at A1a with relatedNode at B1a with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at A1a with relatedNode at B1a with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at A1a (detached) with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at B1a with relatedNode at A1a (detached) with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at A1a with relatedNode at B1a (detached) with open mode shadow trees]
|
||||
expected: FAIL
|
||||
|
||||
[Firing an event at A1a with relatedNode at B1a (detached) with closed mode shadow trees]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[test-001.html]
|
||||
[A_05_02_01_T1]
|
||||
expected: FAIL
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[test-002.html]
|
||||
[A_05_02_02_T01]
|
||||
expected: FAIL
|
||||
|
|
@ -566,6 +566,7 @@ public:
|
|||
, mCurrentTarget(Move(aOther.mCurrentTarget))
|
||||
, mOriginalTarget(Move(aOther.mOriginalTarget))
|
||||
, mRelatedTarget(Move(aOther.mRelatedTarget))
|
||||
, mOriginalRelatedTarget(Move(aOther.mOriginalRelatedTarget))
|
||||
, mPath(Move(aOther.mPath))
|
||||
{
|
||||
MOZ_COUNT_CTOR(WidgetEvent);
|
||||
|
@ -615,6 +616,7 @@ public:
|
|||
|
||||
/// The possible related target
|
||||
nsCOMPtr<dom::EventTarget> mRelatedTarget;
|
||||
nsCOMPtr<dom::EventTarget> mOriginalRelatedTarget;
|
||||
|
||||
nsTArray<EventTargetChainItem>* mPath;
|
||||
|
||||
|
@ -637,6 +639,8 @@ public:
|
|||
mCurrentTarget = aCopyTargets ? aEvent.mCurrentTarget : nullptr;
|
||||
mOriginalTarget = aCopyTargets ? aEvent.mOriginalTarget : nullptr;
|
||||
mRelatedTarget = aCopyTargets ? aEvent.mRelatedTarget : nullptr;
|
||||
mOriginalRelatedTarget =
|
||||
aCopyTargets ? aEvent.mOriginalRelatedTarget : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче