| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/dom/events/event_path.h" |
| |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/events/window_event_context.h" |
| #include "third_party/blink/renderer/core/dom/shadow_root.h" |
| #include "third_party/blink/renderer/core/dom/v0_insertion_point.h" |
| #include "third_party/blink/renderer/core/event_names.h" |
| #include "third_party/blink/renderer/core/events/touch_event.h" |
| #include "third_party/blink/renderer/core/events/touch_event_context.h" |
| #include "third_party/blink/renderer/core/html/html_slot_element.h" |
| #include "third_party/blink/renderer/core/input/touch.h" |
| #include "third_party/blink/renderer/core/input/touch_list.h" |
| |
| namespace blink { |
| |
| EventTarget* EventPath::EventTargetRespectingTargetRules(Node& reference_node) { |
| if (reference_node.IsPseudoElement()) { |
| DCHECK(reference_node.parentNode()); |
| return reference_node.parentNode(); |
| } |
| |
| return &reference_node; |
| } |
| |
| static inline bool ShouldStopAtShadowRoot(Event& event, |
| ShadowRoot& shadow_root, |
| EventTarget& target) { |
| if (shadow_root.IsV1()) { |
| // In v1, an event is scoped by default unless event.composed flag is set. |
| return !event.composed() && target.ToNode() && |
| target.ToNode()->OwnerShadowHost() == shadow_root.host(); |
| } |
| // Ignores event.composed() for v0. |
| // Instead, use event.isScopedInV0() for backward compatibility. |
| return event.IsScopedInV0() && target.ToNode() && |
| target.ToNode()->OwnerShadowHost() == shadow_root.host(); |
| } |
| |
| EventPath::EventPath(Node& node, Event* event) : node_(node), event_(event) { |
| Initialize(); |
| } |
| |
| void EventPath::InitializeWith(Node& node, Event* event) { |
| node_ = &node; |
| event_ = event; |
| window_event_context_ = nullptr; |
| node_event_contexts_.clear(); |
| tree_scope_event_contexts_.clear(); |
| Initialize(); |
| } |
| |
| static inline bool EventPathShouldBeEmptyFor(Node& node) { |
| return node.IsPseudoElement() && !node.parentElement(); |
| } |
| |
| void EventPath::Initialize() { |
| if (EventPathShouldBeEmptyFor(*node_)) |
| return; |
| |
| CalculatePath(); |
| CalculateAdjustedTargets(); |
| CalculateTreeOrderAndSetNearestAncestorClosedTree(); |
| } |
| |
| void EventPath::CalculatePath() { |
| DCHECK(node_); |
| DCHECK(node_event_contexts_.IsEmpty()); |
| node_->UpdateDistributionForLegacyDistributedNodes(); |
| |
| // For performance and memory usage reasons we want to store the |
| // path using as few bytes as possible and with as few allocations |
| // as possible which is why we gather the data on the stack before |
| // storing it in a perfectly sized m_nodeEventContexts Vector. |
| HeapVector<Member<Node>, 64> nodes_in_path; |
| Node* current = node_; |
| |
| nodes_in_path.push_back(current); |
| while (current) { |
| if (event_ && current->KeepEventInNode(*event_)) |
| break; |
| HeapVector<Member<V0InsertionPoint>, 8> insertion_points; |
| CollectDestinationInsertionPoints(*current, insertion_points); |
| if (!insertion_points.IsEmpty()) { |
| for (const auto& insertion_point : insertion_points) |
| nodes_in_path.push_back(insertion_point); |
| current = insertion_points.back(); |
| continue; |
| } |
| if (current->IsChildOfV1ShadowHost()) { |
| if (HTMLSlotElement* slot = current->AssignedSlot()) { |
| current = slot; |
| nodes_in_path.push_back(current); |
| continue; |
| } |
| } |
| if (current->IsShadowRoot()) { |
| if (event_ && |
| ShouldStopAtShadowRoot(*event_, *ToShadowRoot(current), *node_)) |
| break; |
| current = current->OwnerShadowHost(); |
| nodes_in_path.push_back(current); |
| } else { |
| current = current->parentNode(); |
| if (current) |
| nodes_in_path.push_back(current); |
| } |
| } |
| |
| node_event_contexts_.ReserveCapacity(nodes_in_path.size()); |
| for (Node* node_in_path : nodes_in_path) { |
| node_event_contexts_.push_back(NodeEventContext( |
| node_in_path, EventTargetRespectingTargetRules(*node_in_path))); |
| } |
| } |
| |
| void EventPath::CalculateTreeOrderAndSetNearestAncestorClosedTree() { |
| // Precondition: |
| // - TreeScopes in m_treeScopeEventContexts must be *connected* in the same |
| // composed tree. |
| // - The root tree must be included. |
| TreeScopeEventContext* root_tree = nullptr; |
| for (const auto& tree_scope_event_context : tree_scope_event_contexts_) { |
| TreeScope* parent = |
| tree_scope_event_context.Get()->GetTreeScope().ParentTreeScope(); |
| if (!parent) { |
| DCHECK(!root_tree); |
| root_tree = tree_scope_event_context.Get(); |
| continue; |
| } |
| TreeScopeEventContext* parent_tree_scope_event_context = |
| GetTreeScopeEventContext(parent); |
| DCHECK(parent_tree_scope_event_context); |
| parent_tree_scope_event_context->AddChild(*tree_scope_event_context.Get()); |
| } |
| DCHECK(root_tree); |
| root_tree->CalculateTreeOrderAndSetNearestAncestorClosedTree(0, nullptr); |
| } |
| |
| TreeScopeEventContext* EventPath::GetTreeScopeEventContext( |
| TreeScope* tree_scope) { |
| DCHECK(tree_scope); |
| for (TreeScopeEventContext* tree_scope_event_context : |
| tree_scope_event_contexts_) { |
| if (tree_scope_event_context->GetTreeScope() == tree_scope) { |
| return tree_scope_event_context; |
| } |
| } |
| return nullptr; |
| } |
| |
| TreeScopeEventContext* EventPath::EnsureTreeScopeEventContext( |
| Node* current_target, |
| TreeScope* tree_scope) { |
| if (!tree_scope) |
| return nullptr; |
| TreeScopeEventContext* tree_scope_event_context = |
| GetTreeScopeEventContext(tree_scope); |
| if (!tree_scope_event_context) { |
| tree_scope_event_context = TreeScopeEventContext::Create(*tree_scope); |
| tree_scope_event_contexts_.push_back(tree_scope_event_context); |
| |
| TreeScopeEventContext* parent_tree_scope_event_context = |
| EnsureTreeScopeEventContext(nullptr, tree_scope->ParentTreeScope()); |
| if (parent_tree_scope_event_context && |
| parent_tree_scope_event_context->Target()) { |
| tree_scope_event_context->SetTarget( |
| parent_tree_scope_event_context->Target()); |
| } else if (current_target) { |
| tree_scope_event_context->SetTarget( |
| EventTargetRespectingTargetRules(*current_target)); |
| } |
| } else if (!tree_scope_event_context->Target() && current_target) { |
| tree_scope_event_context->SetTarget( |
| EventTargetRespectingTargetRules(*current_target)); |
| } |
| return tree_scope_event_context; |
| } |
| |
| void EventPath::CalculateAdjustedTargets() { |
| const TreeScope* last_tree_scope = nullptr; |
| TreeScopeEventContext* last_tree_scope_event_context = nullptr; |
| |
| for (auto& context : node_event_contexts_) { |
| Node* current_node = context.GetNode(); |
| TreeScope& current_tree_scope = current_node->GetTreeScope(); |
| if (last_tree_scope != ¤t_tree_scope) { |
| last_tree_scope_event_context = |
| EnsureTreeScopeEventContext(current_node, ¤t_tree_scope); |
| } |
| DCHECK(last_tree_scope_event_context); |
| context.SetTreeScopeEventContext(last_tree_scope_event_context); |
| last_tree_scope = ¤t_tree_scope; |
| } |
| } |
| |
| void EventPath::BuildRelatedNodeMap(const Node& related_node, |
| RelatedTargetMap& related_target_map) { |
| EventPath* related_target_event_path = |
| new EventPath(const_cast<Node&>(related_node)); |
| for (const auto& tree_scope_event_context : |
| related_target_event_path->tree_scope_event_contexts_) { |
| related_target_map.insert(&tree_scope_event_context->GetTreeScope(), |
| tree_scope_event_context->Target()); |
| } |
| // Oilpan: It is important to explicitly clear the vectors to reuse |
| // the memory in subsequent event dispatchings. |
| related_target_event_path->Clear(); |
| } |
| |
| EventTarget* EventPath::FindRelatedNode(TreeScope& scope, |
| RelatedTargetMap& related_target_map) { |
| HeapVector<Member<TreeScope>, 32> parent_tree_scopes; |
| EventTarget* related_node = nullptr; |
| for (TreeScope* current = &scope; current; |
| current = current->ParentTreeScope()) { |
| parent_tree_scopes.push_back(current); |
| RelatedTargetMap::const_iterator iter = related_target_map.find(current); |
| if (iter != related_target_map.end() && iter->value) { |
| related_node = iter->value; |
| break; |
| } |
| } |
| DCHECK(related_node); |
| for (const auto& entry : parent_tree_scopes) |
| related_target_map.insert(entry, related_node); |
| |
| return related_node; |
| } |
| |
| void EventPath::AdjustForRelatedTarget(Node& target, |
| EventTarget* related_target) { |
| if (!related_target) |
| return; |
| Node* related_target_node = related_target->ToNode(); |
| if (!related_target_node) |
| return; |
| if (target.GetDocument() != related_target_node->GetDocument()) |
| return; |
| RetargetRelatedTarget(*related_target_node); |
| ShrinkForRelatedTarget(target, *related_target_node); |
| } |
| |
| void EventPath::RetargetRelatedTarget(const Node& related_target_node) { |
| RelatedTargetMap related_node_map; |
| BuildRelatedNodeMap(related_target_node, related_node_map); |
| |
| for (const auto& tree_scope_event_context : tree_scope_event_contexts_) { |
| EventTarget* adjusted_related_target = FindRelatedNode( |
| tree_scope_event_context->GetTreeScope(), related_node_map); |
| DCHECK(adjusted_related_target); |
| tree_scope_event_context.Get()->SetRelatedTarget(adjusted_related_target); |
| } |
| } |
| |
| namespace { |
| |
| bool ShouldStopEventPath(EventTarget& adjusted_target, |
| EventTarget& adjusted_related_target, |
| const Node& event_target_node, |
| const Node& event_related_target_node) { |
| if (&adjusted_target != &adjusted_related_target) |
| return false; |
| Node* adjusted_target_node = adjusted_target.ToNode(); |
| if (!adjusted_target_node) |
| return false; |
| Node* adjusted_related_target_node = adjusted_related_target.ToNode(); |
| if (!adjusted_related_target_node) |
| return false; |
| // Events should be dispatched at least until its root even when event's |
| // target and related_target are identical. |
| if (adjusted_target_node->GetTreeScope() == |
| event_target_node.GetTreeScope() && |
| adjusted_related_target_node->GetTreeScope() == |
| event_related_target_node.GetTreeScope()) |
| return false; |
| return true; |
| } |
| |
| } // anonymous namespace |
| |
| void EventPath::ShrinkForRelatedTarget(const Node& event_target_node, |
| const Node& event_related_target_node) { |
| for (size_t i = 0; i < size(); ++i) { |
| if (ShouldStopEventPath(*at(i).Target(), *at(i).RelatedTarget(), |
| event_target_node, event_related_target_node)) { |
| Shrink(i); |
| break; |
| } |
| } |
| } |
| |
| void EventPath::AdjustForTouchEvent(const TouchEvent& touch_event) { |
| // Each vector and a TouchEventContext share the same TouchList instance. |
| HeapVector<Member<TouchList>> adjusted_touches; |
| HeapVector<Member<TouchList>> adjusted_target_touches; |
| HeapVector<Member<TouchList>> adjusted_changed_touches; |
| HeapVector<Member<TreeScope>> tree_scopes; |
| |
| for (const auto& tree_scope_event_context : tree_scope_event_contexts_) { |
| TouchEventContext* touch_event_context = |
| tree_scope_event_context->EnsureTouchEventContext(); |
| adjusted_touches.push_back(&touch_event_context->Touches()); |
| adjusted_target_touches.push_back(&touch_event_context->TargetTouches()); |
| adjusted_changed_touches.push_back(&touch_event_context->ChangedTouches()); |
| tree_scopes.push_back(&tree_scope_event_context->GetTreeScope()); |
| } |
| |
| // AdjustTouchList appends adjusted Touch(es) to each member TouchList |
| // instance in |adjusted_touch_list| argument, which is reflected on |
| // TouchEventContext because they refer to the same TouchList instance. |
| AdjustTouchList(touch_event.touches(), adjusted_touches, tree_scopes); |
| AdjustTouchList(touch_event.targetTouches(), adjusted_target_touches, |
| tree_scopes); |
| AdjustTouchList(touch_event.changedTouches(), adjusted_changed_touches, |
| tree_scopes); |
| |
| #if DCHECK_IS_ON() |
| for (const auto& tree_scope_event_context : tree_scope_event_contexts_) { |
| TreeScope& tree_scope = tree_scope_event_context->GetTreeScope(); |
| TouchEventContext* touch_event_context = |
| tree_scope_event_context->GetTouchEventContext(); |
| CheckReachability(tree_scope, touch_event_context->Touches()); |
| CheckReachability(tree_scope, touch_event_context->TargetTouches()); |
| CheckReachability(tree_scope, touch_event_context->ChangedTouches()); |
| } |
| #endif |
| } |
| |
| void EventPath::AdjustTouchList( |
| const TouchList* const touch_list, |
| HeapVector<Member<TouchList>> adjusted_touch_list, |
| const HeapVector<Member<TreeScope>>& tree_scopes) { |
| if (!touch_list) |
| return; |
| for (size_t i = 0; i < touch_list->length(); ++i) { |
| const Touch& touch = *touch_list->item(i); |
| if (!touch.target()) |
| continue; |
| |
| Node* target_node = touch.target()->ToNode(); |
| if (!target_node) |
| continue; |
| |
| RelatedTargetMap related_node_map; |
| BuildRelatedNodeMap(*target_node, related_node_map); |
| for (size_t j = 0; j < tree_scopes.size(); ++j) { |
| adjusted_touch_list[j]->Append(touch.CloneWithNewTarget( |
| FindRelatedNode(*tree_scopes[j], related_node_map))); |
| } |
| } |
| } |
| |
| bool EventPath::DisabledFormControlExistsInPath() const { |
| for (const auto& context : node_event_contexts_) { |
| const Node* target_node = context.GetNode(); |
| if (target_node && IsDisabledFormControl(target_node)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool EventPath::HasEventListenersInPath(const AtomicString& event_type) const { |
| for (const auto& context : node_event_contexts_) { |
| const Node* target_node = context.GetNode(); |
| if (target_node && target_node->HasEventListeners(event_type)) |
| return true; |
| } |
| return false; |
| } |
| |
| NodeEventContext& EventPath::TopNodeEventContext() { |
| DCHECK(!IsEmpty()); |
| return Last(); |
| } |
| |
| void EventPath::EnsureWindowEventContext() { |
| DCHECK(event_); |
| if (!window_event_context_) |
| window_event_context_ = |
| new WindowEventContext(*event_, TopNodeEventContext()); |
| } |
| |
| #if DCHECK_IS_ON() |
| void EventPath::CheckReachability(TreeScope& tree_scope, |
| TouchList& touch_list) { |
| for (size_t i = 0; i < touch_list.length(); ++i) |
| DCHECK(touch_list.item(i) |
| ->target() |
| ->ToNode() |
| ->GetTreeScope() |
| .IsInclusiveOlderSiblingShadowRootOrAncestorTreeScopeOf( |
| tree_scope)); |
| } |
| #endif |
| |
| void EventPath::Trace(blink::Visitor* visitor) { |
| visitor->Trace(node_event_contexts_); |
| visitor->Trace(node_); |
| visitor->Trace(event_); |
| visitor->Trace(tree_scope_event_contexts_); |
| visitor->Trace(window_event_context_); |
| } |
| |
| } // namespace blink |