blob: fecad3ad96caf99f34a9efb2142d851cea6ce777 [file] [log] [blame]
/*
* 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 != &current_tree_scope) {
last_tree_scope_event_context =
EnsureTreeScopeEventContext(current_node, &current_tree_scope);
}
DCHECK(last_tree_scope_event_context);
context.SetTreeScopeEventContext(last_tree_scope_event_context);
last_tree_scope = &current_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