Chromium Code Reviews| Index: third_party/WebKit/Source/core/input/EventHandlingUtil.cpp |
| diff --git a/third_party/WebKit/Source/core/input/EventHandlingUtil.cpp b/third_party/WebKit/Source/core/input/EventHandlingUtil.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..477b85dffd6c3c464fd96a83c9888e389f1e5824 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/input/EventHandlingUtil.cpp |
| @@ -0,0 +1,183 @@ |
| +// 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/EventHandlingUtil.h" |
| + |
| +#include "core/frame/FrameView.h" |
| + |
| +namespace blink { |
| +namespace EventHandlingUtil { |
| + |
| +namespace { |
| + |
| +bool isInDocument(EventTarget* n) |
| +{ |
| + 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 |
| + |
| +HitTestResult hitTestResultInFrame(LocalFrame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType) |
| +{ |
| + HitTestResult result(HitTestRequest(hitType), point); |
| + |
| + if (!frame || frame->contentLayoutItem().isNull()) |
| + return result; |
| + if (frame->view()) { |
| + IntRect rect = frame->view()->visibleContentRect(IncludeScrollbars); |
| + if (!rect.contains(roundedIntPoint(point))) |
| + return result; |
| + } |
| + frame->contentLayoutItem().hitTest(result); |
| + return result; |
| +} |
| + |
| +WebInputEventResult mergeEventResult( |
| + WebInputEventResult resultA, WebInputEventResult resultB) |
| +{ |
| + // The ordering of the enumeration is specific. There are times that |
| + // multiple events fire and we need to combine them into a single |
| + // result code. The enumeration is based on the level of consumption that |
| + // is most significant. The enumeration is ordered with smaller specified |
| + // numbers first. Examples of merged results are: |
| + // (HandledApplication, HandledSystem) -> HandledSystem |
| + // (NotHandled, HandledApplication) -> HandledApplication |
| + static_assert(static_cast<int>(WebInputEventResult::NotHandled) == 0, "WebInputEventResult not ordered"); |
| + static_assert(static_cast<int>(WebInputEventResult::HandledSuppressed) < static_cast<int>(WebInputEventResult::HandledApplication), "WebInputEventResult not ordered"); |
| + static_assert(static_cast<int>(WebInputEventResult::HandledApplication) < static_cast<int>(WebInputEventResult::HandledSystem), "WebInputEventResult not ordered"); |
| + return static_cast<WebInputEventResult>(max(static_cast<int>(resultA), static_cast<int>(resultB))); |
| +} |
| + |
| +WebInputEventResult toWebInputEventResult( |
| + DispatchEventResult result) |
| +{ |
| + switch (result) { |
| + case DispatchEventResult::NotCanceled: |
| + return WebInputEventResult::NotHandled; |
| + case DispatchEventResult::CanceledByEventHandler: |
| + return WebInputEventResult::HandledApplication; |
| + case DispatchEventResult::CanceledByDefaultEventHandler: |
| + return WebInputEventResult::HandledSystem; |
| + case DispatchEventResult::CanceledBeforeDispatch: |
| + return WebInputEventResult::HandledSuppressed; |
| + default: |
| + NOTREACHED(); |
| + return WebInputEventResult::HandledSystem; |
| + } |
| +} |
| + |
| +void sendBoundaryEvents( |
|
mustaq
2016/08/23 15:20:48
Make it a member function in BoundaryEventDispatch
Navid Zolghadr
2016/08/24 16:07:28
You mean the methods in anonymous name space in th
mustaq
2016/08/26 19:31:44
- The methods in the anonymous namespace have no u
Navid Zolghadr
2016/08/30 18:46:26
I moved the BoundaryEventDispatcher class to anoth
|
| + EventTarget* exitedTarget, |
| + EventTarget* enteredTarget, |
| + BoundaryEventDispatcher* dispatcher) |
| +{ |
| + if (exitedTarget == enteredTarget) |
| + return; |
| + |
| + // Dispatch out/out events |
| + if (isInDocument(exitedTarget)) |
| + dispatcher->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(EventTypeNames::mouseleave) |
| + || (RuntimeEnabledFeatures::pointerEventEnabled() |
| + && exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::pointerleave))) |
| + exitedNodeHasCapturingAncestor = true; |
| + } |
| + |
| + // Dispatch leave events, in child-to-parent order. |
| + for (size_t j = 0; j < exitedAncestorsCommonParentIndex; j++) |
| + dispatcher->dispatchLeave(exitedTarget, enteredTarget, !exitedNodeHasCapturingAncestor); |
| + |
| + // Dispatch over |
| + if (isInDocument(enteredTarget)) |
| + dispatcher->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(EventTypeNames::mouseenter) |
| + || (RuntimeEnabledFeatures::pointerEventEnabled() |
| + && enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::pointerenter))) |
| + enteredNodeHasCapturingAncestor = true; |
| + } |
| + |
| + // Dispatch enter events, in parent-to-child order. |
| + for (size_t i = enteredAncestorsCommonParentIndex; i > 0; i--) |
| + dispatcher->dispatchEnter(enteredTarget, exitedTarget, !enteredNodeHasCapturingAncestor); |
| +} |
| + |
| +} // namespace EventHandlingUtil |
| +} // namespace blink |