Chromium Code Reviews| Index: third_party/WebKit/Source/core/input/BoundaryEventDispatcher.cpp |
| diff --git a/third_party/WebKit/Source/core/input/BoundaryEventDispatcher.cpp b/third_party/WebKit/Source/core/input/BoundaryEventDispatcher.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..10a7665c90d4235f418ea84cbc8721f5a51b7e25 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/input/BoundaryEventDispatcher.cpp |
| @@ -0,0 +1,128 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "core/input/BoundaryEventDispatcher.h" |
| + |
| +#include "core/dom/Node.h" |
| +#include "core/dom/shadow/FlatTreeTraversal.h" |
| + |
| +namespace blink { |
| + |
| +namespace { |
| + |
| +bool isInDocument(EventTarget* n) |
|
haraken
2016/09/01 17:21:58
n => target
Navid Zolghadr
2016/09/01 19:31:54
Done.
|
| +{ |
| + return n && n->toNode() && n->toNode()->isConnected(); |
| +} |
| + |
| +void buildAncestorChain( |
| + EventTarget* target, |
| + HeapVector<Member<Node>, 20>* ancestors) |
| +{ |
| + if (!isInDocument(target)) |
| + return; |
| + Node* targetNode = target->toNode(); |
| + DCHECK(targetNode); |
| + targetNode->updateDistribution(); |
| + // Index 0 element in the ancestors arrays will be the corresponding |
| + // target. So the root of their document will be their last element. |
| + for (Node* node = targetNode; node; node = FlatTreeTraversal::parent(*node)) |
| + ancestors->append(node); |
| +} |
| + |
| +void buildAncestorChainsAndFindCommonAncestors( |
| + EventTarget* exitedTarget, EventTarget* enteredTarget, |
| + HeapVector<Member<Node>, 20>* exitedAncestorsOut, |
| + HeapVector<Member<Node>, 20>* enteredAncestorsOut, |
| + size_t* exitedAncestorsCommonParentIndexOut, |
| + size_t* enteredAncestorsCommonParentIndexOut) |
| +{ |
| + DCHECK(exitedAncestorsOut); |
| + DCHECK(enteredAncestorsOut); |
| + DCHECK(exitedAncestorsCommonParentIndexOut); |
| + DCHECK(enteredAncestorsCommonParentIndexOut); |
| + |
| + buildAncestorChain(exitedTarget, exitedAncestorsOut); |
| + buildAncestorChain(enteredTarget, enteredAncestorsOut); |
| + |
| + *exitedAncestorsCommonParentIndexOut = exitedAncestorsOut->size(); |
| + *enteredAncestorsCommonParentIndexOut = enteredAncestorsOut->size(); |
| + while (*exitedAncestorsCommonParentIndexOut > 0 |
| + && *enteredAncestorsCommonParentIndexOut > 0) { |
| + if ((*exitedAncestorsOut)[(*exitedAncestorsCommonParentIndexOut) - 1] |
| + != (*enteredAncestorsOut)[(*enteredAncestorsCommonParentIndexOut) - 1]) |
| + break; |
| + (*exitedAncestorsCommonParentIndexOut)--; |
| + (*enteredAncestorsCommonParentIndexOut)--; |
| + } |
| +} |
| + |
| +} // namespace |
| + |
| +void BoundaryEventDispatcher::sendBoundaryEvents( |
| + EventTarget* exitedTarget, |
| + EventTarget* enteredTarget) |
| +{ |
| + if (exitedTarget == enteredTarget) |
| + return; |
| + |
| + // Dispatch out event |
| + if (isInDocument(exitedTarget)) |
| + dispatchOut(exitedTarget, enteredTarget); |
| + |
| + // Create lists of all exited/entered ancestors, locate the common ancestor |
| + // Based on httparchive, in more than 97% cases the depth of DOM is less |
| + // than 20. |
| + HeapVector<Member<Node>, 20> exitedAncestors; |
| + HeapVector<Member<Node>, 20> enteredAncestors; |
| + size_t exitedAncestorsCommonParentIndex = 0; |
| + size_t enteredAncestorsCommonParentIndex = 0; |
| + |
| + // A note on mouseenter and mouseleave: These are non-bubbling events, and they are dispatched if there |
| + // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special |
| + // handling is necessary to avoid O(n^2) capturing event handler checks. |
| + // |
| + // Note, however, that this optimization can possibly cause some unanswered/missing/redundant mouseenter or |
| + // mouseleave events in certain contrived eventhandling scenarios, e.g., when: |
| + // - the mouseleave handler for a node sets the only capturing-mouseleave-listener in its ancestor, or |
| + // - DOM mods in any mouseenter/mouseleave handler changes the common ancestor of exited & entered nodes, etc. |
| + // We think the spec specifies a "frozen" state to avoid such corner cases (check the discussion on "candidate event |
| + // listeners" at http://www.w3.org/TR/uievents), but our code below preserves one such behavior from past only to |
| + // match Firefox and IE behavior. |
| + // |
| + // TODO(mustaq): Confirm spec conformance, double-check with other browsers. |
| + |
| + buildAncestorChainsAndFindCommonAncestors( |
| + exitedTarget, enteredTarget, |
| + &exitedAncestors, &enteredAncestors, |
| + &exitedAncestorsCommonParentIndex, &enteredAncestorsCommonParentIndex); |
| + |
| + bool exitedNodeHasCapturingAncestor = false; |
| + for (size_t j = 0; j < exitedAncestors.size(); j++) { |
| + if (exitedAncestors[j]->hasCapturingEventListeners(getLeaveEvent())) |
| + exitedNodeHasCapturingAncestor = true; |
| + } |
| + |
| + // Dispatch leave events, in child-to-parent order. |
| + for (size_t j = 0; j < exitedAncestorsCommonParentIndex; j++) |
| + dispatchLeave(exitedAncestors[j], enteredTarget, !exitedNodeHasCapturingAncestor); |
| + |
| + // Dispatch over event |
| + if (isInDocument(enteredTarget)) |
| + dispatchOver(enteredTarget, exitedTarget); |
| + |
| + // Defer locating capturing enter listener until /after/ dispatching the leave events because |
| + // the leave handlers might set a capturing enter handler. |
| + bool enteredNodeHasCapturingAncestor = false; |
| + for (size_t i = 0; i < enteredAncestors.size(); i++) { |
| + if (enteredAncestors[i]->hasCapturingEventListeners(getEnterEvent())) |
| + enteredNodeHasCapturingAncestor = true; |
| + } |
| + |
| + // Dispatch enter events, in parent-to-child order. |
| + for (size_t i = enteredAncestorsCommonParentIndex; i > 0; i--) |
| + dispatchEnter(enteredAncestors[i - 1], exitedTarget, !enteredNodeHasCapturingAncestor); |
| +} |
| + |
| +} // namespace blink |