| 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..ee36458c433ce87e166dc4753c44fa63de4e5d89
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/input/BoundaryEventDispatcher.cpp
|
| @@ -0,0 +1,134 @@
|
| +// 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* target)
|
| +{
|
| + return target && target->toNode() && target->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;
|
| + const AtomicString &leaveEvent = getLeaveEvent();
|
| + for (size_t j = 0; j < exitedAncestors.size(); j++) {
|
| + if (exitedAncestors[j]->hasCapturingEventListeners(leaveEvent)) {
|
| + exitedNodeHasCapturingAncestor = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // 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;
|
| + const AtomicString &enterEvent = getEnterEvent();
|
| + for (size_t i = 0; i < enteredAncestors.size(); i++) {
|
| + if (enteredAncestors[i]->hasCapturingEventListeners(enterEvent)) {
|
| + enteredNodeHasCapturingAncestor = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Dispatch enter events, in parent-to-child order.
|
| + for (size_t i = enteredAncestorsCommonParentIndex; i > 0; i--)
|
| + dispatchEnter(enteredAncestors[i - 1], exitedTarget, !enteredNodeHasCapturingAncestor);
|
| +}
|
| +
|
| +} // namespace blink
|
|
|