Commit 4c7bc246 authored by Kyoko Muto's avatar Kyoko Muto Committed by Commit Bot

Merge FlatTreeTraversal and FlatTreeTraversalNG

BUG= 867331

Change-Id: I886542193ed17743d8e10c11ae9c2c5036c8187b
Reviewed-on: https://chromium-review.googlesource.com/1159937
Commit-Queue: Kyoko Muto <kymuto@google.com>
Reviewed-by: default avatarHayato Ito <hayato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580464}
parent 37427655
......@@ -1729,7 +1729,6 @@ jumbo_source_set("unit_tests") {
"dom/events/event_target_test.cc",
"dom/events/listener_leak_test.cc",
"dom/first_letter_pseudo_element_test.cc",
"dom/flat_tree_traversal_ng_test.cc",
"dom/flat_tree_traversal_test.cc",
"dom/idle_deadline_test.cc",
"dom/layout_tree_builder_traversal_test.cc",
......
......@@ -140,8 +140,6 @@ blink_core_sources("dom") {
"first_letter_pseudo_element.h",
"flat_tree_traversal.cc",
"flat_tree_traversal.h",
"flat_tree_traversal_ng.cc",
"flat_tree_traversal_ng.h",
"frame_request_callback_collection.cc",
"frame_request_callback_collection.h",
"global_event_handlers.h",
......
......@@ -32,44 +32,55 @@
namespace blink {
bool CanBeDistributedToV0InsertionPoint(const Node& node) {
return node.IsInV0ShadowTree() || node.IsChildOfV0ShadowHost();
}
Node* FlatTreeTraversal::TraverseChild(const Node& node,
TraversalDirection direction) {
if (auto* slot = ToHTMLSlotElementIfSupportsAssignmentOrNull(node)) {
if (slot->AssignedNodes().IsEmpty()) {
return direction == kTraversalDirectionForward ? slot->firstChild()
: slot->lastChild();
}
return direction == kTraversalDirectionForward ? slot->FirstAssignedNode()
: slot->LastAssignedNode();
}
Node* child;
if (ShadowRoot* shadow_root = node.GetShadowRoot()) {
return ResolveDistributionStartingAt(direction == kTraversalDirectionForward
? shadow_root->firstChild()
: shadow_root->lastChild(),
direction);
child = direction == kTraversalDirectionForward ? shadow_root->firstChild()
: shadow_root->lastChild();
} else {
child = direction == kTraversalDirectionForward ? node.firstChild()
: node.lastChild();
}
return ResolveDistributionStartingAt(direction == kTraversalDirectionForward
? node.firstChild()
: node.lastChild(),
direction);
}
Node* FlatTreeTraversal::ResolveDistributionStartingAt(
const Node* node,
TraversalDirection direction) {
if (!node)
if (!child)
return nullptr;
for (const Node* sibling = node; sibling;
sibling = (direction == kTraversalDirectionForward
? sibling->nextSibling()
: sibling->previousSibling())) {
if (const HTMLSlotElement* slot =
ToHTMLSlotElementIfSupportsAssignmentOrNull(*sibling)) {
if (Node* found = (direction == kTraversalDirectionForward
? slot->FirstDistributedNode()
: slot->LastDistributedNode()))
return found;
continue;
}
if (node->IsInV0ShadowTree())
return V0ResolveDistributionStartingAt(*sibling, direction);
return const_cast<Node*>(sibling);
if (child->IsInV0ShadowTree()) {
return V0ResolveDistributionStartingAt(*child, direction);
}
return nullptr;
return child;
}
// This needs only for v0
// Node* FlatTreeTraversalNg::ResolveDistributionStartingAt(
// const Node* node,
// TraversalDirection direction) {
// if (!node)
// return nullptr;
// for (const Node* sibling = node; sibling;
// sibling = (direction == kTraversalDirectionForward
// ? sibling->nextSibling()
// : sibling->previousSibling())) {
// if (node->IsInV0ShadowTree())
// return V0ResolveDistributionStartingAt(*sibling, direction);
// return const_cast<Node*>(sibling);
// }
// return nullptr;
// }
Node* FlatTreeTraversal::V0ResolveDistributionStartingAt(
const Node& node,
TraversalDirection direction) {
......@@ -103,35 +114,36 @@ Node* FlatTreeTraversal::TraverseSiblings(const Node& node,
if (ShadowRootWhereNodeCanBeDistributedForV0(node))
return TraverseSiblingsForV0Distribution(node, direction);
if (Node* found = ResolveDistributionStartingAt(
direction == kTraversalDirectionForward ? node.nextSibling()
: node.previousSibling(),
direction))
return found;
Node* sibling = direction == kTraversalDirectionForward
? node.nextSibling()
: node.previousSibling();
// Slotted nodes are already handled in traverseSiblingsForV1HostChild()
// above, here is for fallback contents.
if (auto* slot =
ToHTMLSlotElementIfSupportsAssignmentOrNull(node.parentElement())) {
if (slot->AssignedNodes().IsEmpty())
return TraverseSiblings(*slot, direction);
if (!node.IsInV0ShadowTree())
return sibling;
if (sibling) {
if (Node* found = V0ResolveDistributionStartingAt(*sibling, direction))
return found;
}
// // Slotted nodes are already handled in traverseSiblingsForV1HostChild()
// // above, here is for fallback contents.
// if (auto* slot = ToHTMLSlotElementOrNull(node.parentElement())) {
// if (slot->SupportsAssignment() && slot->AssignedNodes().IsEmpty())
// return TraverseSiblings(*slot, direction);
// }
return nullptr;
}
Node* FlatTreeTraversal::TraverseSiblingsForV1HostChild(
const Node& node,
TraversalDirection direction) {
HTMLSlotElement* slot = node.FinalDestinationSlot();
HTMLSlotElement* slot = node.AssignedSlot();
if (!slot)
return nullptr;
if (Node* sibling_in_distributed_nodes =
(direction == kTraversalDirectionForward
? slot->DistributedNodeNextTo(node)
: slot->DistributedNodePreviousTo(node)))
return sibling_in_distributed_nodes;
return TraverseSiblings(*slot, direction);
return direction == kTraversalDirectionForward
? slot->AssignedNodeNextTo(node)
: slot->AssignedNodePreviousTo(node);
}
Node* FlatTreeTraversal::TraverseSiblingsForV0Distribution(
......@@ -155,18 +167,14 @@ ContainerNode* FlatTreeTraversal::TraverseParent(
if (node.IsPseudoElement())
return node.ParentOrShadowHostNode();
if (node.IsChildOfV1ShadowHost()) {
HTMLSlotElement* slot = node.FinalDestinationSlot();
if (!slot)
return nullptr;
return TraverseParent(*slot);
}
if (node.IsChildOfV1ShadowHost())
return node.AssignedSlot();
if (auto* slot =
if (auto* parent_slot =
ToHTMLSlotElementIfSupportsAssignmentOrNull(node.parentElement())) {
if (!slot->AssignedNodes().IsEmpty())
if (!parent_slot->AssignedNodes().IsEmpty())
return nullptr;
return TraverseParent(*slot, details);
return parent_slot;
}
if (CanBeDistributedToV0InsertionPoint(node))
......@@ -208,8 +216,6 @@ ContainerNode* FlatTreeTraversal::TraverseParentOrHost(const Node& node) {
}
Node* FlatTreeTraversal::ChildAt(const Node& node, unsigned index) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::ChildAt(node, index);
AssertPrecondition(node);
Node* child = TraverseFirstChild(node);
while (child && index--)
......@@ -219,8 +225,6 @@ Node* FlatTreeTraversal::ChildAt(const Node& node, unsigned index) {
}
Node* FlatTreeTraversal::NextSkippingChildren(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::NextSkippingChildren(node);
if (Node* next_sibling = TraverseNextSibling(node))
return next_sibling;
return TraverseNextAncestorSibling(node);
......@@ -229,8 +233,6 @@ Node* FlatTreeTraversal::NextSkippingChildren(const Node& node) {
bool FlatTreeTraversal::ContainsIncludingPseudoElement(
const ContainerNode& container,
const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::ContainsIncludingPseudoElement(container, node);
AssertPrecondition(container);
AssertPrecondition(node);
// This can be slower than FlatTreeTraversal::contains() because we
......@@ -244,8 +246,6 @@ bool FlatTreeTraversal::ContainsIncludingPseudoElement(
}
Node* FlatTreeTraversal::PreviousSkippingChildren(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::PreviousSkippingChildren(node);
if (Node* previous_sibling = TraversePreviousSibling(node))
return previous_sibling;
return TraversePreviousAncestorSibling(node);
......@@ -269,8 +269,6 @@ Node* FlatTreeTraversal::PreviousAncestorSiblingPostOrder(
// between DOM tree traversal and flat tree tarversal.
Node* FlatTreeTraversal::PreviousPostOrder(const Node& current,
const Node* stay_within) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::PreviousPostOrder(current, stay_within);
AssertPrecondition(current);
if (stay_within)
AssertPrecondition(*stay_within);
......@@ -288,8 +286,6 @@ Node* FlatTreeTraversal::PreviousPostOrder(const Node& current,
}
bool FlatTreeTraversal::IsDescendantOf(const Node& node, const Node& other) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::IsDescendantOf(node, other);
AssertPrecondition(node);
AssertPrecondition(other);
if (!HasChildren(other) || node.isConnected() != other.isConnected())
......@@ -304,8 +300,6 @@ bool FlatTreeTraversal::IsDescendantOf(const Node& node, const Node& other) {
Node* FlatTreeTraversal::CommonAncestor(const Node& node_a,
const Node& node_b) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::CommonAncestor(node_a, node_b);
AssertPrecondition(node_a);
AssertPrecondition(node_b);
Node* result = node_a.CommonAncestor(
......@@ -335,8 +329,6 @@ Node* FlatTreeTraversal::TraversePreviousAncestorSibling(const Node& node) {
}
unsigned FlatTreeTraversal::Index(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Index(node);
AssertPrecondition(node);
unsigned count = 0;
for (Node* runner = TraversePreviousSibling(node); runner;
......@@ -346,8 +338,6 @@ unsigned FlatTreeTraversal::Index(const Node& node) {
}
unsigned FlatTreeTraversal::CountChildren(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::CountChildren(node);
AssertPrecondition(node);
unsigned count = 0;
for (Node* runner = TraverseFirstChild(node); runner;
......@@ -357,8 +347,6 @@ unsigned FlatTreeTraversal::CountChildren(const Node& node) {
}
Node* FlatTreeTraversal::LastWithin(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::LastWithin(node);
AssertPrecondition(node);
Node* descendant = TraverseLastChild(node);
for (Node* child = descendant; child; child = LastChild(*child))
......@@ -368,8 +356,6 @@ Node* FlatTreeTraversal::LastWithin(const Node& node) {
}
Node& FlatTreeTraversal::LastWithinOrSelf(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::LastWithinOrSelf(node);
AssertPrecondition(node);
Node* last_descendant = LastWithin(node);
Node& result = last_descendant ? *last_descendant : const_cast<Node&>(node);
......
......@@ -29,7 +29,6 @@
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/flat_tree_traversal_ng.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
......@@ -41,6 +40,8 @@ namespace blink {
class ContainerNode;
class Node;
bool CanBeDistributedToV0InsertionPoint(const Node& node);
// Flat tree version of |NodeTraversal|.
//
// None of member functions takes a |ShadowRoot| or an active insertion point,
......@@ -98,8 +99,6 @@ class CORE_EXPORT FlatTreeTraversal {
static bool IsDescendantOf(const Node& /*node*/, const Node& other);
static bool Contains(const ContainerNode& container, const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Contains(container, node);
AssertPrecondition(container);
AssertPrecondition(node);
return container == node || IsDescendantOf(node, container);
......@@ -194,8 +193,6 @@ class CORE_EXPORT FlatTreeTraversal {
inline ContainerNode* FlatTreeTraversal::Parent(
const Node& node,
ParentTraversalDetails* details) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Parent(node, details);
AssertPrecondition(node);
ContainerNode* result = TraverseParent(node, details);
AssertPostcondition(result);
......@@ -203,15 +200,11 @@ inline ContainerNode* FlatTreeTraversal::Parent(
}
inline Element* FlatTreeTraversal::ParentElement(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::ParentElement(node);
ContainerNode* parent = FlatTreeTraversal::Parent(node);
return parent && parent->IsElementNode() ? ToElement(parent) : nullptr;
}
inline Node* FlatTreeTraversal::NextSibling(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::NextSibling(node);
AssertPrecondition(node);
Node* result = TraverseSiblings(node, kTraversalDirectionForward);
AssertPostcondition(result);
......@@ -219,8 +212,6 @@ inline Node* FlatTreeTraversal::NextSibling(const Node& node) {
}
inline Node* FlatTreeTraversal::PreviousSibling(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::PreviousSibling(node);
AssertPrecondition(node);
Node* result = TraverseSiblings(node, kTraversalDirectionBackward);
AssertPostcondition(result);
......@@ -228,8 +219,6 @@ inline Node* FlatTreeTraversal::PreviousSibling(const Node& node) {
}
inline Node* FlatTreeTraversal::Next(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Next(node);
AssertPrecondition(node);
Node* result = TraverseNext(node);
AssertPostcondition(result);
......@@ -238,8 +227,6 @@ inline Node* FlatTreeTraversal::Next(const Node& node) {
inline Node* FlatTreeTraversal::Next(const Node& node,
const Node* stay_within) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Next(node, stay_within);
AssertPrecondition(node);
Node* result = TraverseNext(node, stay_within);
AssertPostcondition(result);
......@@ -248,8 +235,6 @@ inline Node* FlatTreeTraversal::Next(const Node& node,
inline Node* FlatTreeTraversal::NextSkippingChildren(const Node& node,
const Node* stay_within) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::NextSkippingChildren(node, stay_within);
AssertPrecondition(node);
Node* result = TraverseNextSkippingChildren(node, stay_within);
AssertPostcondition(result);
......@@ -286,8 +271,6 @@ inline Node* FlatTreeTraversal::TraverseNextSkippingChildren(
}
inline Node* FlatTreeTraversal::Previous(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::Previous(node);
AssertPrecondition(node);
Node* result = TraversePrevious(node);
AssertPostcondition(result);
......@@ -304,8 +287,6 @@ inline Node* FlatTreeTraversal::TraversePrevious(const Node& node) {
}
inline Node* FlatTreeTraversal::FirstChild(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::FirstChild(node);
AssertPrecondition(node);
Node* result = TraverseChild(node, kTraversalDirectionForward);
AssertPostcondition(result);
......@@ -313,8 +294,6 @@ inline Node* FlatTreeTraversal::FirstChild(const Node& node) {
}
inline Node* FlatTreeTraversal::LastChild(const Node& node) {
if (RuntimeEnabledFeatures::SlotInFlatTreeEnabled())
return FlatTreeTraversalNg::LastChild(node);
AssertPrecondition(node);
Node* result = TraverseLastChild(node);
AssertPostcondition(result);
......@@ -360,4 +339,4 @@ FlatTreeTraversal::InclusiveAncestorsOf(const Node& node) {
} // namespace blink
#endif
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_TRAVERSAL_H_
/*
* Copyright (C) 2012 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/flat_tree_traversal_ng.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/html/html_shadow_element.h"
#include "third_party/blink/renderer/core/html/html_slot_element.h"
namespace blink {
bool CanBeDistributedToV0InsertionPoint(const Node& node) {
return node.IsInV0ShadowTree() || node.IsChildOfV0ShadowHost();
}
Node* FlatTreeTraversalNg::TraverseChild(const Node& node,
TraversalDirection direction) {
if (auto* slot = ToHTMLSlotElementIfSupportsAssignmentOrNull(node)) {
if (slot->AssignedNodes().IsEmpty()) {
return direction == kTraversalDirectionForward ? slot->firstChild()
: slot->lastChild();
}
return direction == kTraversalDirectionForward ? slot->FirstAssignedNode()
: slot->LastAssignedNode();
}
Node* child;
if (ShadowRoot* shadow_root = node.GetShadowRoot()) {
child = direction == kTraversalDirectionForward ? shadow_root->firstChild()
: shadow_root->lastChild();
} else {
child = direction == kTraversalDirectionForward ? node.firstChild()
: node.lastChild();
}
if (!child)
return nullptr;
if (child->IsInV0ShadowTree()) {
return V0ResolveDistributionStartingAt(*child, direction);
}
return child;
}
// This needs only for v0
// Node* FlatTreeTraversalNg::ResolveDistributionStartingAt(
// const Node* node,
// TraversalDirection direction) {
// if (!node)
// return nullptr;
// for (const Node* sibling = node; sibling;
// sibling = (direction == kTraversalDirectionForward
// ? sibling->nextSibling()
// : sibling->previousSibling())) {
// if (node->IsInV0ShadowTree())
// return V0ResolveDistributionStartingAt(*sibling, direction);
// return const_cast<Node*>(sibling);
// }
// return nullptr;
// }
Node* FlatTreeTraversalNg::V0ResolveDistributionStartingAt(
const Node& node,
TraversalDirection direction) {
DCHECK(!ToHTMLSlotElementIfSupportsAssignmentOrNull(node));
for (const Node* sibling = &node; sibling;
sibling = (direction == kTraversalDirectionForward
? sibling->nextSibling()
: sibling->previousSibling())) {
if (!IsActiveV0InsertionPoint(*sibling))
return const_cast<Node*>(sibling);
const V0InsertionPoint& insertion_point = ToV0InsertionPoint(*sibling);
if (Node* found = (direction == kTraversalDirectionForward
? insertion_point.FirstDistributedNode()
: insertion_point.LastDistributedNode()))
return found;
DCHECK(IsHTMLShadowElement(insertion_point) ||
(IsHTMLContentElement(insertion_point) &&
!insertion_point.HasChildren()));
}
return nullptr;
}
// TODO(hayato): This may return a wrong result for a node which is not in a
// document flat tree. See FlatTreeTraversalNgTest's redistribution test for
// details.
Node* FlatTreeTraversalNg::TraverseSiblings(const Node& node,
TraversalDirection direction) {
if (node.IsChildOfV1ShadowHost())
return TraverseSiblingsForV1HostChild(node, direction);
if (ShadowRootWhereNodeCanBeDistributedForV0(node))
return TraverseSiblingsForV0Distribution(node, direction);
Node* sibling = direction == kTraversalDirectionForward
? node.nextSibling()
: node.previousSibling();
if (!node.IsInV0ShadowTree())
return sibling;
if (sibling) {
if (Node* found = V0ResolveDistributionStartingAt(*sibling, direction))
return found;
}
// // Slotted nodes are already handled in traverseSiblingsForV1HostChild()
// // above, here is for fallback contents.
// if (auto* slot = ToHTMLSlotElementOrNull(node.parentElement())) {
// if (slot->SupportsAssignment() && slot->AssignedNodes().IsEmpty())
// return TraverseSiblings(*slot, direction);
// }
return nullptr;
}
Node* FlatTreeTraversalNg::TraverseSiblingsForV1HostChild(
const Node& node,
TraversalDirection direction) {
HTMLSlotElement* slot = node.AssignedSlot();
if (!slot)
return nullptr;
return direction == kTraversalDirectionForward
? slot->AssignedNodeNextTo(node)
: slot->AssignedNodePreviousTo(node);
}
Node* FlatTreeTraversalNg::TraverseSiblingsForV0Distribution(
const Node& node,
TraversalDirection direction) {
const V0InsertionPoint* final_destination = ResolveReprojection(&node);
if (!final_destination)
return nullptr;
if (Node* found = (direction == kTraversalDirectionForward
? final_destination->DistributedNodeNextTo(&node)
: final_destination->DistributedNodePreviousTo(&node)))
return found;
return TraverseSiblings(*final_destination, direction);
}
ContainerNode* FlatTreeTraversalNg::TraverseParent(
const Node& node,
ParentTraversalDetails* details) {
// TODO(hayato): Stop this hack for a pseudo element because a pseudo element
// is not a child of its parentOrShadowHostNode() in a flat tree.
if (node.IsPseudoElement())
return node.ParentOrShadowHostNode();
if (node.IsChildOfV1ShadowHost())
return node.AssignedSlot();
if (auto* parent_slot =
ToHTMLSlotElementIfSupportsAssignmentOrNull(node.parentElement())) {
if (!parent_slot->AssignedNodes().IsEmpty())
return nullptr;
return parent_slot;
}
if (CanBeDistributedToV0InsertionPoint(node))
return TraverseParentForV0(node, details);
DCHECK(!ShadowRootWhereNodeCanBeDistributedForV0(node));
return TraverseParentOrHost(node);
}
ContainerNode* FlatTreeTraversalNg::TraverseParentForV0(
const Node& node,
ParentTraversalDetails* details) {
if (ShadowRootWhereNodeCanBeDistributedForV0(node)) {
if (const V0InsertionPoint* insertion_point = ResolveReprojection(&node)) {
if (details)
details->DidTraverseInsertionPoint(insertion_point);
// The node is distributed. But the distribution was stopped at this
// insertion point.
if (ShadowRootWhereNodeCanBeDistributedForV0(*insertion_point))
return nullptr;
return TraverseParent(*insertion_point);
}
return nullptr;
}
ContainerNode* parent = TraverseParentOrHost(node);
if (IsActiveV0InsertionPoint(*parent))
return nullptr;
return parent;
}
ContainerNode* FlatTreeTraversalNg::TraverseParentOrHost(const Node& node) {
ContainerNode* parent = node.parentNode();
if (!parent)
return nullptr;
if (!parent->IsShadowRoot())
return parent;
ShadowRoot* shadow_root = ToShadowRoot(parent);
return &shadow_root->host();
}
Node* FlatTreeTraversalNg::ChildAt(const Node& node, unsigned index) {
AssertPrecondition(node);
Node* child = TraverseFirstChild(node);
while (child && index--)
child = NextSibling(*child);
AssertPostcondition(child);
return child;
}
Node* FlatTreeTraversalNg::NextSkippingChildren(const Node& node) {
if (Node* next_sibling = TraverseNextSibling(node))
return next_sibling;
return TraverseNextAncestorSibling(node);
}
bool FlatTreeTraversalNg::ContainsIncludingPseudoElement(
const ContainerNode& container,
const Node& node) {
AssertPrecondition(container);
AssertPrecondition(node);
// This can be slower than FlatTreeTraversalNg::contains() because we
// can't early exit even when container doesn't have children.
for (const Node* current = &node; current;
current = TraverseParent(*current)) {
if (current == &container)
return true;
}
return false;
}
Node* FlatTreeTraversalNg::PreviousSkippingChildren(const Node& node) {
if (Node* previous_sibling = TraversePreviousSibling(node))
return previous_sibling;
return TraversePreviousAncestorSibling(node);
}
Node* FlatTreeTraversalNg::PreviousAncestorSiblingPostOrder(
const Node& current,
const Node* stay_within) {
DCHECK(!FlatTreeTraversalNg::PreviousSibling(current));
for (Node* parent = FlatTreeTraversalNg::Parent(current); parent;
parent = FlatTreeTraversalNg::Parent(*parent)) {
if (parent == stay_within)
return nullptr;
if (Node* previous_sibling = FlatTreeTraversalNg::PreviousSibling(*parent))
return previous_sibling;
}
return nullptr;
}
// TODO(yosin) We should consider introducing template class to share code
// between DOM tree traversal and flat tree tarversal.
Node* FlatTreeTraversalNg::PreviousPostOrder(const Node& current,
const Node* stay_within) {
AssertPrecondition(current);
if (stay_within)
AssertPrecondition(*stay_within);
if (Node* last_child = TraverseLastChild(current)) {
AssertPostcondition(last_child);
return last_child;
}
if (current == stay_within)
return nullptr;
if (Node* previous_sibling = TraversePreviousSibling(current)) {
AssertPostcondition(previous_sibling);
return previous_sibling;
}
return PreviousAncestorSiblingPostOrder(current, stay_within);
}
bool FlatTreeTraversalNg::IsDescendantOf(const Node& node, const Node& other) {
AssertPrecondition(node);
AssertPrecondition(other);
if (!HasChildren(other) || node.isConnected() != other.isConnected())
return false;
for (const ContainerNode* n = TraverseParent(node); n;
n = TraverseParent(*n)) {
if (n == other)
return true;
}
return false;
}
Node* FlatTreeTraversalNg::CommonAncestor(const Node& node_a,
const Node& node_b) {
AssertPrecondition(node_a);
AssertPrecondition(node_b);
Node* result = node_a.CommonAncestor(node_b, [](const Node& node) {
return FlatTreeTraversalNg::Parent(node);
});
AssertPostcondition(result);
return result;
}
Node* FlatTreeTraversalNg::TraverseNextAncestorSibling(const Node& node) {
DCHECK(!TraverseNextSibling(node));
for (Node* parent = TraverseParent(node); parent;
parent = TraverseParent(*parent)) {
if (Node* next_sibling = TraverseNextSibling(*parent))
return next_sibling;
}
return nullptr;
}
Node* FlatTreeTraversalNg::TraversePreviousAncestorSibling(const Node& node) {
DCHECK(!TraversePreviousSibling(node));
for (Node* parent = TraverseParent(node); parent;
parent = TraverseParent(*parent)) {
if (Node* previous_sibling = TraversePreviousSibling(*parent))
return previous_sibling;
}
return nullptr;
}
unsigned FlatTreeTraversalNg::Index(const Node& node) {
AssertPrecondition(node);
unsigned count = 0;
for (Node* runner = TraversePreviousSibling(node); runner;
runner = PreviousSibling(*runner))
++count;
return count;
}
unsigned FlatTreeTraversalNg::CountChildren(const Node& node) {
AssertPrecondition(node);
unsigned count = 0;
for (Node* runner = TraverseFirstChild(node); runner;
runner = TraverseNextSibling(*runner))
++count;
return count;
}
Node* FlatTreeTraversalNg::LastWithin(const Node& node) {
AssertPrecondition(node);
Node* descendant = TraverseLastChild(node);
for (Node* child = descendant; child; child = LastChild(*child))
descendant = child;
AssertPostcondition(descendant);
return descendant;
}
Node& FlatTreeTraversalNg::LastWithinOrSelf(const Node& node) {
AssertPrecondition(node);
Node* last_descendant = LastWithin(node);
Node& result = last_descendant ? *last_descendant : const_cast<Node&>(node);
AssertPostcondition(&result);
return result;
}
} // namespace blink
/*
* Copyright (C) 2012 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.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_TRAVERSAL_NG_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_TRAVERSAL_NG_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/node_traversal.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/platform/wtf/allocator.h"
namespace blink {
class ContainerNode;
class Node;
bool CanBeDistributedToV0InsertionPoint(const Node& node);
// Flat tree version of |NodeTraversal|.
//
// None of member functions takes a |ShadowRoot| or an active insertion point,
// e.g. roughly speaking <content> and <shadow> in the shadow tree, see
// |InsertionPoint::isActive()| for details of active insertion points, since
// they aren't appeared in the flat tree. |assertPrecondition()| and
// |assertPostCondition()| check this condition.
//
// FIXME: Make some functions inline to optimise the performance.
// https://bugs.webkit.org/show_bug.cgi?id=82702
class CORE_EXPORT FlatTreeTraversalNg {
STATIC_ONLY(FlatTreeTraversalNg);
public:
typedef LayoutTreeBuilderTraversal::ParentDetails ParentTraversalDetails;
using TraversalNodeType = Node;
static Node* Next(const Node&);
static Node* Next(const Node&, const Node* stay_within);
static Node* Previous(const Node&);
static Node* FirstChild(const Node&);
static Node* LastChild(const Node&);
static bool HasChildren(const Node&);
static ContainerNode* Parent(const Node&, ParentTraversalDetails* = nullptr);
static Element* ParentElement(const Node&);
static Node* NextSibling(const Node&);
static Node* PreviousSibling(const Node&);
// Returns a child node at |index|. If |index| is greater than or equal to
// the children, this function returns |nullptr|.
static Node* ChildAt(const Node&, unsigned index);
// Flat tree version of |NodeTraversal::nextSkippingChildren()|. This
// function is similar to |next()| but skips child nodes of a specified
// node.
static Node* NextSkippingChildren(const Node&);
static Node* NextSkippingChildren(const Node&, const Node* stay_within);
static Node* FirstWithin(const Node& current) { return FirstChild(current); }
// Flat tree version of |NodeTraversal::previousSkippingChildren()|
// similar to |previous()| but skipping child nodes of the specified node.
static Node* PreviousSkippingChildren(const Node&);
// Like previous, but visits parents before their children.
static Node* PreviousPostOrder(const Node&,
const Node* stay_within = nullptr);
// Flat tree version of |Node::isDescendantOf(other)|. This function
// returns true if |other| contains |node|, otherwise returns
// false. If |other| is |node|, this function returns false.
static bool IsDescendantOf(const Node& /*node*/, const Node& other);
static bool Contains(const ContainerNode& container, const Node& node) {
AssertPrecondition(container);
AssertPrecondition(node);
return container == node || IsDescendantOf(node, container);
}
static bool ContainsIncludingPseudoElement(const ContainerNode&, const Node&);
// Returns a common ancestor of |nodeA| and |nodeB| if exists, otherwise
// returns |nullptr|.
static Node* CommonAncestor(const Node& node_a, const Node& node_b);
// Flat tree version of |Node::nodeIndex()|. This function returns a
// zero base position number of the specified node in child nodes list, or
// zero if the specified node has no parent.
static unsigned Index(const Node&);
// Flat tree version of |ContainerNode::countChildren()|. This function
// returns the number of the child nodes of the specified node in the
// flat tree.
static unsigned CountChildren(const Node&);
static Node* LastWithin(const Node&);
static Node& LastWithinOrSelf(const Node&);
// Flat tree range helper functions for range based for statement.
// TODO(dom-team): We should have following functions to match with
// |NodeTraversal|:
// - AncestorsOf()
// - DescendantsOf()
// - InclusiveAncestorsOf()
// - InclusiveDescendantsOf()
// - StartsAt()
// - StartsAfter()
static TraversalRange<TraversalChildrenIterator<FlatTreeTraversalNg>>
ChildrenOf(const Node&);
private:
enum TraversalDirection {
kTraversalDirectionForward,
kTraversalDirectionBackward
};
static void AssertPrecondition(const Node& node) {
DCHECK(!node.NeedsDistributionRecalc());
DCHECK(node.CanParticipateInFlatTree());
}
static void AssertPostcondition(const Node* node) {
#if DCHECK_IS_ON()
if (node)
AssertPrecondition(*node);
#endif
}
// static Node* ResolveDistributionStartingAt(const Node*,
// TraversalDirection);
static Node* V0ResolveDistributionStartingAt(const Node&, TraversalDirection);
static Node* TraverseNext(const Node&);
static Node* TraverseNext(const Node&, const Node* stay_within);
static Node* TraverseNextSkippingChildren(const Node&,
const Node* stay_within);
static Node* TraversePrevious(const Node&);
static Node* TraverseFirstChild(const Node&);
static Node* TraverseLastChild(const Node&);
static Node* TraverseChild(const Node&, TraversalDirection);
static ContainerNode* TraverseParent(const Node&,
ParentTraversalDetails* = nullptr);
// TODO(hayato): Make ParentTraversalDetails be aware of slot elements too.
static ContainerNode* TraverseParentForV0(const Node&,
ParentTraversalDetails* = nullptr);
static ContainerNode* TraverseParentOrHost(const Node&);
static Node* TraverseNextSibling(const Node&);
static Node* TraversePreviousSibling(const Node&);
static Node* TraverseSiblings(const Node&, TraversalDirection);
static Node* TraverseSiblingsForV1HostChild(const Node&, TraversalDirection);
static Node* TraverseSiblingsForV0Distribution(const Node&,
TraversalDirection);
static Node* TraverseNextAncestorSibling(const Node&);
static Node* TraversePreviousAncestorSibling(const Node&);
static Node* PreviousAncestorSiblingPostOrder(const Node& current,
const Node* stay_within);
};
inline ContainerNode* FlatTreeTraversalNg::Parent(
const Node& node,
ParentTraversalDetails* details) {
AssertPrecondition(node);
ContainerNode* result = TraverseParent(node, details);
AssertPostcondition(result);
return result;
}
inline Element* FlatTreeTraversalNg::ParentElement(const Node& node) {
ContainerNode* parent = FlatTreeTraversalNg::Parent(node);
return parent && parent->IsElementNode() ? ToElement(parent) : nullptr;
}
inline Node* FlatTreeTraversalNg::NextSibling(const Node& node) {
AssertPrecondition(node);
Node* result = TraverseSiblings(node, kTraversalDirectionForward);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::PreviousSibling(const Node& node) {
AssertPrecondition(node);
Node* result = TraverseSiblings(node, kTraversalDirectionBackward);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::Next(const Node& node) {
AssertPrecondition(node);
Node* result = TraverseNext(node);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::Next(const Node& node,
const Node* stay_within) {
AssertPrecondition(node);
Node* result = TraverseNext(node, stay_within);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::NextSkippingChildren(
const Node& node,
const Node* stay_within) {
AssertPrecondition(node);
Node* result = TraverseNextSkippingChildren(node, stay_within);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::TraverseNext(const Node& node) {
if (Node* next = TraverseFirstChild(node))
return next;
for (const Node* next = &node; next; next = TraverseParent(*next)) {
if (Node* sibling = TraverseNextSibling(*next))
return sibling;
}
return nullptr;
}
inline Node* FlatTreeTraversalNg::TraverseNext(const Node& node,
const Node* stay_within) {
if (Node* next = TraverseFirstChild(node))
return next;
return TraverseNextSkippingChildren(node, stay_within);
}
inline Node* FlatTreeTraversalNg::TraverseNextSkippingChildren(
const Node& node,
const Node* stay_within) {
for (const Node* next = &node; next; next = TraverseParent(*next)) {
if (next == stay_within)
return nullptr;
if (Node* sibling = TraverseNextSibling(*next))
return sibling;
}
return nullptr;
}
inline Node* FlatTreeTraversalNg::Previous(const Node& node) {
AssertPrecondition(node);
Node* result = TraversePrevious(node);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::TraversePrevious(const Node& node) {
if (Node* previous = TraversePreviousSibling(node)) {
while (Node* child = TraverseLastChild(*previous))
previous = child;
return previous;
}
return TraverseParent(node);
}
inline Node* FlatTreeTraversalNg::FirstChild(const Node& node) {
AssertPrecondition(node);
Node* result = TraverseChild(node, kTraversalDirectionForward);
AssertPostcondition(result);
return result;
}
inline Node* FlatTreeTraversalNg::LastChild(const Node& node) {
AssertPrecondition(node);
Node* result = TraverseLastChild(node);
AssertPostcondition(result);
return result;
}
inline bool FlatTreeTraversalNg::HasChildren(const Node& node) {
return FirstChild(node);
}
inline Node* FlatTreeTraversalNg::TraverseNextSibling(const Node& node) {
return TraverseSiblings(node, kTraversalDirectionForward);
}
inline Node* FlatTreeTraversalNg::TraversePreviousSibling(const Node& node) {
return TraverseSiblings(node, kTraversalDirectionBackward);
}
inline Node* FlatTreeTraversalNg::TraverseFirstChild(const Node& node) {
return TraverseChild(node, kTraversalDirectionForward);
}
inline Node* FlatTreeTraversalNg::TraverseLastChild(const Node& node) {
return TraverseChild(node, kTraversalDirectionBackward);
}
// TraverseRange<T> implementations
inline TraversalRange<TraversalChildrenIterator<FlatTreeTraversalNg>>
FlatTreeTraversalNg::ChildrenOf(const Node& parent) {
return TraversalRange<TraversalChildrenIterator<FlatTreeTraversalNg>>(
&parent);
}
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_TRAVERSAL_NG_H_
// Copyright 2015 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 "third_party/blink/renderer/core/dom/flat_tree_traversal_ng.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/compiler.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// To avoid symbol collisions in jumbo builds.
namespace flat_tree_traversal_ng_test {
class FlatTreeTraversalNgTest : public PageTestBase,
private ScopedSlotInFlatTreeForTest,
ScopedIncrementalShadowDOMForTest {
public:
FlatTreeTraversalNgTest()
: ScopedSlotInFlatTreeForTest(true),
ScopedIncrementalShadowDOMForTest(true) {}
protected:
// Sets |mainHTML| to BODY element with |innerHTML| property and attaches
// shadow root to child with |shadowHTML|, then update distribution for
// calling member functions in |FlatTreeTraversalNg|.
void SetupSampleHTML(const char* main_html,
const char* shadow_html,
unsigned);
void SetupDocumentTree(const char* main_html);
void AttachV0ShadowRoot(Element& shadow_host, const char* shadow_inner_html);
void AttachOpenShadowRoot(Element& shadow_host,
const char* shadow_inner_html);
};
void FlatTreeTraversalNgTest::SetupSampleHTML(const char* main_html,
const char* shadow_html,
unsigned index) {
Element* body = GetDocument().body();
body->SetInnerHTMLFromString(String::FromUTF8(main_html));
Element* shadow_host = ToElement(NodeTraversal::ChildAt(*body, index));
ShadowRoot& shadow_root = shadow_host->CreateV0ShadowRootForTesting();
shadow_root.SetInnerHTMLFromString(String::FromUTF8(shadow_html));
body->UpdateDistributionForFlatTreeTraversal();
}
void FlatTreeTraversalNgTest::SetupDocumentTree(const char* main_html) {
Element* body = GetDocument().body();
body->SetInnerHTMLFromString(String::FromUTF8(main_html));
}
void FlatTreeTraversalNgTest::AttachV0ShadowRoot(
Element& shadow_host,
const char* shadow_inner_html) {
ShadowRoot& shadow_root = shadow_host.CreateV0ShadowRootForTesting();
shadow_root.SetInnerHTMLFromString(String::FromUTF8(shadow_inner_html));
GetDocument().body()->UpdateDistributionForFlatTreeTraversal();
}
void FlatTreeTraversalNgTest::AttachOpenShadowRoot(
Element& shadow_host,
const char* shadow_inner_html) {
ShadowRoot& shadow_root =
shadow_host.AttachShadowRootInternal(ShadowRootType::kOpen);
shadow_root.SetInnerHTMLFromString(String::FromUTF8(shadow_inner_html));
GetDocument().body()->UpdateDistributionForFlatTreeTraversal();
}
namespace {
void TestCommonAncestor(Node* expected_result,
const Node& node_a,
const Node& node_b) {
Node* result1 = FlatTreeTraversalNg::CommonAncestor(node_a, node_b);
EXPECT_EQ(expected_result, result1)
<< "commonAncestor(" << node_a.textContent() << ","
<< node_b.textContent() << ")";
Node* result2 = FlatTreeTraversalNg::CommonAncestor(node_b, node_a);
EXPECT_EQ(expected_result, result2)
<< "commonAncestor(" << node_b.textContent() << ","
<< node_a.textContent() << ")";
}
} // namespace
// Test case for
// - childAt
// - countChildren
// - hasChildren
// - index
// - isDescendantOf
TEST_F(FlatTreeTraversalNgTest, childAt) {
const char* main_html =
"<div id='m0'>"
"<span id='m00'>m00</span>"
"<span id='m01'>m01</span>"
"</div>";
const char* shadow_html =
"<a id='s00'>s00</a>"
"<content select='#m01'></content>"
"<a id='s02'>s02</a>"
"<a id='s03'><content select='#m00'></content></a>"
"<a id='s04'>s04</a>";
SetupSampleHTML(main_html, shadow_html, 0);
Element* body = GetDocument().body();
Element* m0 = body->QuerySelector("#m0");
Element* m00 = m0->QuerySelector("#m00");
Element* m01 = m0->QuerySelector("#m01");
Element* shadow_host = m0;
ShadowRoot* shadow_root = shadow_host->OpenShadowRoot();
Element* s00 = shadow_root->QuerySelector("#s00");
Element* s02 = shadow_root->QuerySelector("#s02");
Element* s03 = shadow_root->QuerySelector("#s03");
Element* s04 = shadow_root->QuerySelector("#s04");
const unsigned kNumberOfChildNodes = 5;
Node* expected_child_nodes[5] = {s00, m01, s02, s03, s04};
ASSERT_EQ(kNumberOfChildNodes,
FlatTreeTraversalNg::CountChildren(*shadow_host));
EXPECT_TRUE(FlatTreeTraversalNg::HasChildren(*shadow_host));
for (unsigned index = 0; index < kNumberOfChildNodes; ++index) {
Node* child = FlatTreeTraversalNg::ChildAt(*shadow_host, index);
EXPECT_EQ(expected_child_nodes[index], child)
<< "FlatTreeTraversalNg::childAt(*shadowHost, " << index << ")";
EXPECT_EQ(index, FlatTreeTraversalNg::Index(*child))
<< "FlatTreeTraversalNg::index(FlatTreeTraversalNg(*shadowHost, "
<< index << "))";
EXPECT_TRUE(FlatTreeTraversalNg::IsDescendantOf(*child, *shadow_host))
<< "FlatTreeTraversalNg::isDescendantOf(*FlatTreeTraversalNg(*"
"shadowHost, "
<< index << "), *shadowHost)";
}
EXPECT_EQ(nullptr,
FlatTreeTraversalNg::ChildAt(*shadow_host, kNumberOfChildNodes + 1))
<< "Out of bounds childAt() returns nullptr.";
// Distribute node |m00| is child of node in shadow tree |s03|.
EXPECT_EQ(m00, FlatTreeTraversalNg::ChildAt(*s03, 0));
}
TEST_F(FlatTreeTraversalNgTest, ChildrenOf) {
SetupSampleHTML(
"<p id=sample>ZERO<span slot=three>three</b><span "
"slot=one>one</b>FOUR</p>",
"zero<slot name=one></slot>two<slot name=three></slot>four", 0);
Element* const sample = GetDocument().getElementById("sample");
HeapVector<Member<Node>> expected_nodes;
for (Node* runner = FlatTreeTraversalNg::FirstChild(*sample); runner;
runner = FlatTreeTraversalNg::NextSibling(*runner)) {
expected_nodes.push_back(runner);
}
HeapVector<Member<Node>> actual_nodes;
for (Node& child : FlatTreeTraversalNg::ChildrenOf(*sample))
actual_nodes.push_back(&child);
EXPECT_EQ(expected_nodes, actual_nodes);
}
// Test case for
// - commonAncestor
// - isDescendantOf
TEST_F(FlatTreeTraversalNgTest, commonAncestor) {
// We build following flat tree:
// ____BODY___
// | | |
// m0 m1 m2 m1 is shadow host having m10, m11, m12.
// _|_ | __|__
// | | | | |
// m00 m01 | m20 m21
// _____|_____________
// | | | | |
// s10 s11 s12 s13 s14
// |
// __|__
// | | |
// m12 m10 m11 <-- distributed
// where: each symbol consists with prefix, child index, child-child index.
// prefix "m" means node in main tree,
// prefix "d" means node in main tree and distributed
// prefix "s" means node in shadow tree
const char* main_html =
"<a id='m0'><b id='m00'>m00</b><b id='m01'>m01</b></a>"
"<a id='m1'>"
"<b id='m10'>m10</b>"
"<b id='m11'>m11</b>"
"<b id='m12'>m12</b>"
"</a>"
"<a id='m2'><b id='m20'>m20</b><b id='m21'>m21</b></a>";
const char* shadow_html =
"<a id='s10'>s10</a>"
"<a id='s11'><content select='#m12'></content></a>"
"<a id='s12'>s12</a>"
"<a id='s13'>"
"<content select='#m10'></content>"
"<content select='#m11'></content>"
"</a>"
"<a id='s14'>s14</a>";
SetupSampleHTML(main_html, shadow_html, 1);
Element* body = GetDocument().body();
Element* m0 = body->QuerySelector("#m0");
Element* m1 = body->QuerySelector("#m1");
Element* m2 = body->QuerySelector("#m2");
Element* m00 = body->QuerySelector("#m00");
Element* m01 = body->QuerySelector("#m01");
Element* m10 = body->QuerySelector("#m10");
Element* m11 = body->QuerySelector("#m11");
Element* m12 = body->QuerySelector("#m12");
Element* m20 = body->QuerySelector("#m20");
Element* m21 = body->QuerySelector("#m21");
ShadowRoot* shadow_root = m1->OpenShadowRoot();
Element* s10 = shadow_root->QuerySelector("#s10");
Element* s11 = shadow_root->QuerySelector("#s11");
Element* s12 = shadow_root->QuerySelector("#s12");
Element* s13 = shadow_root->QuerySelector("#s13");
Element* s14 = shadow_root->QuerySelector("#s14");
TestCommonAncestor(body, *m0, *m1);
TestCommonAncestor(body, *m1, *m2);
TestCommonAncestor(body, *m1, *m20);
TestCommonAncestor(body, *s14, *m21);
TestCommonAncestor(m0, *m0, *m0);
TestCommonAncestor(m0, *m00, *m01);
TestCommonAncestor(m1, *m1, *m1);
TestCommonAncestor(m1, *s10, *s14);
TestCommonAncestor(m1, *s10, *m12);
TestCommonAncestor(m1, *s12, *m12);
TestCommonAncestor(m1, *m10, *m12);
TestCommonAncestor(m01, *m01, *m01);
TestCommonAncestor(s11, *s11, *m12);
TestCommonAncestor(s13, *m10, *m11);
s12->remove(ASSERT_NO_EXCEPTION);
TestCommonAncestor(s12, *s12, *s12);
TestCommonAncestor(nullptr, *s12, *s11);
TestCommonAncestor(nullptr, *s12, *m01);
TestCommonAncestor(nullptr, *s12, *m20);
m20->remove(ASSERT_NO_EXCEPTION);
TestCommonAncestor(m20, *m20, *m20);
TestCommonAncestor(nullptr, *m20, *s12);
TestCommonAncestor(nullptr, *m20, *m1);
}
// Test case for
// - nextSkippingChildren
// - previousSkippingChildren
TEST_F(FlatTreeTraversalNgTest, nextSkippingChildren) {
const char* main_html =
"<div id='m0'>m0</div>"
"<div id='m1'>"
"<span id='m10'>m10</span>"
"<span id='m11'>m11</span>"
"</div>"
"<div id='m2'>m2</div>";
const char* shadow_html =
"<content select='#m11'></content>"
"<a id='s11'>s11</a>"
"<a id='s12'>"
"<b id='s120'>s120</b>"
"<content select='#m10'></content>"
"</a>";
SetupSampleHTML(main_html, shadow_html, 1);
Element* body = GetDocument().body();
Element* m0 = body->QuerySelector("#m0");
Element* m1 = body->QuerySelector("#m1");
Element* m2 = body->QuerySelector("#m2");
Element* m10 = body->QuerySelector("#m10");
Element* m11 = body->QuerySelector("#m11");
ShadowRoot* shadow_root = m1->OpenShadowRoot();
Element* s11 = shadow_root->QuerySelector("#s11");
Element* s12 = shadow_root->QuerySelector("#s12");
Element* s120 = shadow_root->QuerySelector("#s120");
// Main tree node to main tree node
EXPECT_EQ(*m1, FlatTreeTraversalNg::NextSkippingChildren(*m0));
EXPECT_EQ(*m0, FlatTreeTraversalNg::PreviousSkippingChildren(*m1));
// Distribute node to main tree node
EXPECT_EQ(*m2, FlatTreeTraversalNg::NextSkippingChildren(*m10));
EXPECT_EQ(*m1, FlatTreeTraversalNg::PreviousSkippingChildren(*m2));
// Distribute node to node in shadow tree
EXPECT_EQ(*s11, FlatTreeTraversalNg::NextSkippingChildren(*m11));
EXPECT_EQ(*m11, FlatTreeTraversalNg::PreviousSkippingChildren(*s11));
// Node in shadow tree to distributed node
EXPECT_EQ(*s11, FlatTreeTraversalNg::NextSkippingChildren(*m11));
EXPECT_EQ(*m11, FlatTreeTraversalNg::PreviousSkippingChildren(*s11));
EXPECT_EQ(*m10, FlatTreeTraversalNg::NextSkippingChildren(*s120));
EXPECT_EQ(*s120, FlatTreeTraversalNg::PreviousSkippingChildren(*m10));
// Node in shadow tree to main tree
EXPECT_EQ(*m2, FlatTreeTraversalNg::NextSkippingChildren(*s12));
EXPECT_EQ(*m1, FlatTreeTraversalNg::PreviousSkippingChildren(*m2));
}
// Test case for
// - lastWithin
// - lastWithinOrSelf
TEST_F(FlatTreeTraversalNgTest, lastWithin) {
const char* main_html =
"<div id='m0'>m0</div>"
"<div id='m1'>"
"<span id='m10'>m10</span>"
"<span id='m11'>m11</span>"
"<span id='m12'>m12</span>" // #m12 is not distributed.
"</div>"
"<div id='m2'></div>";
const char* shadow_html =
"<content select='#m11'></content>"
"<a id='s11'>s11</a>"
"<a id='s12'>"
"<content select='#m10'></content>"
"</a>";
SetupSampleHTML(main_html, shadow_html, 1);
Element* body = GetDocument().body();
Element* m0 = body->QuerySelector("#m0");
Element* m1 = body->QuerySelector("#m1");
Element* m2 = body->QuerySelector("#m2");
Element* m10 = body->QuerySelector("#m10");
ShadowRoot* shadow_root = m1->OpenShadowRoot();
Element* s11 = shadow_root->QuerySelector("#s11");
Element* s12 = shadow_root->QuerySelector("#s12");
EXPECT_EQ(m0->firstChild(), FlatTreeTraversalNg::LastWithin(*m0));
EXPECT_EQ(*m0->firstChild(), FlatTreeTraversalNg::LastWithinOrSelf(*m0));
EXPECT_EQ(m10->firstChild(), FlatTreeTraversalNg::LastWithin(*m1));
EXPECT_EQ(*m10->firstChild(), FlatTreeTraversalNg::LastWithinOrSelf(*m1));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::LastWithin(*m2));
EXPECT_EQ(*m2, FlatTreeTraversalNg::LastWithinOrSelf(*m2));
EXPECT_EQ(s11->firstChild(), FlatTreeTraversalNg::LastWithin(*s11));
EXPECT_EQ(*s11->firstChild(), FlatTreeTraversalNg::LastWithinOrSelf(*s11));
EXPECT_EQ(m10->firstChild(), FlatTreeTraversalNg::LastWithin(*s12));
EXPECT_EQ(*m10->firstChild(), FlatTreeTraversalNg::LastWithinOrSelf(*s12));
}
TEST_F(FlatTreeTraversalNgTest, previousPostOrder) {
const char* main_html =
"<div id='m0'>m0</div>"
"<div id='m1'>"
"<span id='m10'>m10</span>"
"<span id='m11'>m11</span>"
"</div>"
"<div id='m2'>m2</div>";
const char* shadow_html =
"<content select='#m11'></content>"
"<a id='s11'>s11</a>"
"<a id='s12'>"
"<b id='s120'>s120</b>"
"<content select='#m10'></content>"
"</a>";
SetupSampleHTML(main_html, shadow_html, 1);
Element* body = GetDocument().body();
Element* m0 = body->QuerySelector("#m0");
Element* m1 = body->QuerySelector("#m1");
Element* m2 = body->QuerySelector("#m2");
Element* m10 = body->QuerySelector("#m10");
Element* m11 = body->QuerySelector("#m11");
ShadowRoot* shadow_root = m1->OpenShadowRoot();
Element* s11 = shadow_root->QuerySelector("#s11");
Element* s12 = shadow_root->QuerySelector("#s12");
Element* s120 = shadow_root->QuerySelector("#s120");
EXPECT_EQ(*m0->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*m0));
EXPECT_EQ(*s12, FlatTreeTraversalNg::PreviousPostOrder(*m1));
EXPECT_EQ(*m10->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*m10));
EXPECT_EQ(*s120, FlatTreeTraversalNg::PreviousPostOrder(*m10->firstChild()));
EXPECT_EQ(*s120,
FlatTreeTraversalNg::PreviousPostOrder(*m10->firstChild(), s12));
EXPECT_EQ(*m11->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*m11));
EXPECT_EQ(*m0, FlatTreeTraversalNg::PreviousPostOrder(*m11->firstChild()));
EXPECT_EQ(nullptr,
FlatTreeTraversalNg::PreviousPostOrder(*m11->firstChild(), m11));
EXPECT_EQ(*m2->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*m2));
EXPECT_EQ(*s11->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*s11));
EXPECT_EQ(*m10, FlatTreeTraversalNg::PreviousPostOrder(*s12));
EXPECT_EQ(*s120->firstChild(), FlatTreeTraversalNg::PreviousPostOrder(*s120));
EXPECT_EQ(*s11, FlatTreeTraversalNg::PreviousPostOrder(*s120->firstChild()));
EXPECT_EQ(nullptr,
FlatTreeTraversalNg::PreviousPostOrder(*s120->firstChild(), s12));
}
TEST_F(FlatTreeTraversalNgTest, nextSiblingNotInDocumentFlatTree) {
const char* main_html =
"<div id='m0'>m0</div>"
"<div id='m1'>"
"<span id='m10'>m10</span>"
"<span id='m11'>m11</span>"
"</div>"
"<div id='m2'>m2</div>";
const char* shadow_html = "<content select='#m11'></content>";
SetupSampleHTML(main_html, shadow_html, 1);
Element* body = GetDocument().body();
Element* m10 = body->QuerySelector("#m10");
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*m10));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*m10));
}
TEST_F(FlatTreeTraversalNgTest, redistribution) {
const char* main_html =
"<div id='m0'>m0</div>"
"<div id='m1'>"
"<span id='m10'>m10</span>"
"<span id='m11'>m11</span>"
"</div>"
"<div id='m2'>m2</div>";
const char* shadow_html1 =
"<div id='s1'>"
"<content></content>"
"</div>";
SetupSampleHTML(main_html, shadow_html1, 1);
const char* shadow_html2 =
"<div id='s2'>"
"<content select='#m10'></content>"
"<span id='s21'>s21</span>"
"</div>";
Element* body = GetDocument().body();
Element* m1 = body->QuerySelector("#m1");
Element* m10 = body->QuerySelector("#m10");
ShadowRoot* shadow_root1 = m1->OpenShadowRoot();
Element* s1 = shadow_root1->QuerySelector("#s1");
AttachV0ShadowRoot(*s1, shadow_html2);
ShadowRoot* shadow_root2 = s1->OpenShadowRoot();
Element* s21 = shadow_root2->QuerySelector("#s21");
EXPECT_EQ(s21, FlatTreeTraversalNg::NextSibling(*m10));
EXPECT_EQ(m10, FlatTreeTraversalNg::PreviousSibling(*s21));
// FlatTreeTraversalNg::traverseSiblings does not work for a node which is not
// in a document flat tree.
// e.g. The following test fails. The result of
// FlatTreeTraversalNg::previousSibling(*m11)) will be #m10, instead of
// nullptr. Element* m11 = body->querySelector("#m11"); EXPECT_EQ(nullptr,
// FlatTreeTraversalNg::previousSibling(*m11));
}
TEST_F(FlatTreeTraversalNgTest, v1Simple) {
const char* main_html =
"<div id='host'>"
"<div id='child1' slot='slot1'></div>"
"<div id='child2' slot='slot2'></div>"
"</div>";
const char* shadow_html =
"<div id='shadow-child1'></div>"
"<slot name='slot1'></slot>"
"<slot name='slot2'></slot>"
"<div id='shadow-child2'></div>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* host = body->QuerySelector("#host");
Element* child1 = body->QuerySelector("#child1");
Element* child2 = body->QuerySelector("#child2");
AttachOpenShadowRoot(*host, shadow_html);
ShadowRoot* shadow_root = host->OpenShadowRoot();
Element* slot1 = shadow_root->QuerySelector("[name=slot1]");
Element* slot2 = shadow_root->QuerySelector("[name=slot2]");
Element* shadow_child1 = shadow_root->QuerySelector("#shadow-child1");
Element* shadow_child2 = shadow_root->QuerySelector("#shadow-child2");
EXPECT_TRUE(slot1);
EXPECT_TRUE(slot2);
EXPECT_EQ(shadow_child1, FlatTreeTraversalNg::FirstChild(*host));
EXPECT_EQ(slot1, FlatTreeTraversalNg::NextSibling(*shadow_child1));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*child1));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*child2));
EXPECT_EQ(slot2, FlatTreeTraversalNg::NextSibling(*slot1));
EXPECT_EQ(shadow_child2, FlatTreeTraversalNg::NextSibling(*slot2));
}
TEST_F(FlatTreeTraversalNgTest, v1Redistribution) {
// composed tree:
// d1
// ├──/shadow-root
// │ └── d1-1
// │ ├──/shadow-root
// │ │ ├── d1-1-1
// │ │ ├── slot name=d1-1-s1
// │ │ ├── slot name=d1-1-s2
// │ │ └── d1-1-2
// │ ├── d1-2
// │ ├── slot id=d1-s0
// │ ├── slot name=d1-s1 slot=d1-1-s1
// │ ├── slot name=d1-s2
// │ ├── d1-3
// │ └── d1-4 slot=d1-1-s1
// ├── d2 slot=d1-s1
// ├── d3 slot=d1-s2
// ├── d4 slot=nonexistent
// └── d5
// flat tree:
// d1
// └── d1-1
// ├── d1-1-1
// ├── slot name=d1-1-s1
// │ ├── slot name=d1-s1 slot=d1-1-s1
// │ │ └── d2 slot=d1-s1
// │ └── d1-4 slot=d1-1-s1
// ├── slot name=d1-1-s2
// └── d1-1-2
const char* main_html =
"<div id='d1'>"
"<div id='d2' slot='d1-s1'></div>"
"<div id='d3' slot='d1-s2'></div>"
"<div id='d4' slot='nonexistent'></div>"
"<div id='d5'></div>"
"</div>"
"<div id='d6'></div>";
const char* shadow_html1 =
"<div id='d1-1'>"
"<div id='d1-2'></div>"
"<slot id='d1-s0'></slot>"
"<slot name='d1-s1' slot='d1-1-s1'></slot>"
"<slot name='d1-s2'></slot>"
"<div id='d1-3'></div>"
"<div id='d1-4' slot='d1-1-s1'></div>"
"</div>";
const char* shadow_html2 =
"<div id='d1-1-1'></div>"
"<slot name='d1-1-s1'></slot>"
"<slot name='d1-1-s2'></slot>"
"<div id='d1-1-2'></div>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* d1 = body->QuerySelector("#d1");
Element* d2 = body->QuerySelector("#d2");
Element* d3 = body->QuerySelector("#d3");
Element* d4 = body->QuerySelector("#d4");
Element* d5 = body->QuerySelector("#d5");
Element* d6 = body->QuerySelector("#d6");
AttachOpenShadowRoot(*d1, shadow_html1);
ShadowRoot* shadow_root1 = d1->OpenShadowRoot();
Element* d11 = shadow_root1->QuerySelector("#d1-1");
Element* d12 = shadow_root1->QuerySelector("#d1-2");
Element* d13 = shadow_root1->QuerySelector("#d1-3");
Element* d14 = shadow_root1->QuerySelector("#d1-4");
Element* d1s0 = shadow_root1->QuerySelector("#d1-s0");
Element* d1s1 = shadow_root1->QuerySelector("[name=d1-s1]");
Element* d1s2 = shadow_root1->QuerySelector("[name=d1-s2]");
AttachOpenShadowRoot(*d11, shadow_html2);
ShadowRoot* shadow_root2 = d11->OpenShadowRoot();
Element* d111 = shadow_root2->QuerySelector("#d1-1-1");
Element* d112 = shadow_root2->QuerySelector("#d1-1-2");
Element* d11s1 = shadow_root2->QuerySelector("[name=d1-1-s1]");
Element* d11s2 = shadow_root2->QuerySelector("[name=d1-1-s2]");
EXPECT_TRUE(d5);
EXPECT_TRUE(d12);
EXPECT_TRUE(d13);
EXPECT_TRUE(d1s0);
EXPECT_TRUE(d1s1);
EXPECT_TRUE(d1s2);
EXPECT_TRUE(d11s1);
EXPECT_TRUE(d11s2);
EXPECT_EQ(d11, FlatTreeTraversalNg::Next(*d1));
EXPECT_EQ(d111, FlatTreeTraversalNg::Next(*d11));
EXPECT_EQ(d11s1, FlatTreeTraversalNg::Next(*d111));
EXPECT_EQ(d1s1, FlatTreeTraversalNg::Next(*d11s1));
EXPECT_EQ(d2, FlatTreeTraversalNg::Next(*d1s1));
EXPECT_EQ(d14, FlatTreeTraversalNg::Next(*d2));
EXPECT_EQ(d11s2, FlatTreeTraversalNg::Next(*d14));
EXPECT_EQ(d112, FlatTreeTraversalNg::Next(*d11s2));
EXPECT_EQ(d6, FlatTreeTraversalNg::Next(*d112));
EXPECT_EQ(d112, FlatTreeTraversalNg::Previous(*d6));
EXPECT_EQ(d11, FlatTreeTraversalNg::Parent(*d111));
EXPECT_EQ(d11, FlatTreeTraversalNg::Parent(*d112));
EXPECT_EQ(d1s1, FlatTreeTraversalNg::Parent(*d2));
EXPECT_EQ(d11s1, FlatTreeTraversalNg::Parent(*d14));
EXPECT_EQ(d1s2, FlatTreeTraversalNg::Parent(*d3));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::Parent(*d4));
}
TEST_F(FlatTreeTraversalNgTest, v1SlotInDocumentTree) {
const char* main_html =
"<div id='parent'>"
"<slot>"
"<div id='child1'></div>"
"<div id='child2'></div>"
"</slot>"
"</div>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* parent = body->QuerySelector("#parent");
Element* slot = body->QuerySelector("slot");
Element* child1 = body->QuerySelector("#child1");
Element* child2 = body->QuerySelector("#child2");
EXPECT_EQ(slot, FlatTreeTraversalNg::FirstChild(*parent));
EXPECT_EQ(child1, FlatTreeTraversalNg::FirstChild(*slot));
EXPECT_EQ(child2, FlatTreeTraversalNg::NextSibling(*child1));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*child2));
EXPECT_EQ(slot, FlatTreeTraversalNg::Parent(*child1));
EXPECT_EQ(slot, FlatTreeTraversalNg::Parent(*child2));
EXPECT_EQ(parent, FlatTreeTraversalNg::Parent(*slot));
}
TEST_F(FlatTreeTraversalNgTest, v1FallbackContent) {
const char* main_html = "<div id='d1'></div>";
const char* shadow_html =
"<div id='before'></div>"
"<slot><p>fallback content</p></slot>"
"<div id='after'></div>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* d1 = body->QuerySelector("#d1");
AttachOpenShadowRoot(*d1, shadow_html);
ShadowRoot* shadow_root = d1->OpenShadowRoot();
Element* before = shadow_root->QuerySelector("#before");
Element* after = shadow_root->QuerySelector("#after");
Element* fallback_content = shadow_root->QuerySelector("p");
Element* slot = shadow_root->QuerySelector("slot");
EXPECT_EQ(before, FlatTreeTraversalNg::FirstChild(*d1));
EXPECT_EQ(after, FlatTreeTraversalNg::LastChild(*d1));
EXPECT_EQ(slot, FlatTreeTraversalNg::Parent(*fallback_content));
EXPECT_EQ(slot, FlatTreeTraversalNg::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversalNg::NextSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*after));
EXPECT_EQ(slot, FlatTreeTraversalNg::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversalNg::PreviousSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*before));
}
TEST_F(FlatTreeTraversalNgTest, v1FallbackContentSkippedInTraversal) {
const char* main_html = "<div id='d1'><span></span></div>";
const char* shadow_html =
"<div id='before'></div>"
"<slot><p>fallback content</p></slot>"
"<div id='after'></div>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* d1 = body->QuerySelector("#d1");
Element* span = body->QuerySelector("span");
AttachOpenShadowRoot(*d1, shadow_html);
ShadowRoot* shadow_root = d1->OpenShadowRoot();
Element* before = shadow_root->QuerySelector("#before");
Element* after = shadow_root->QuerySelector("#after");
Element* fallback_content = shadow_root->QuerySelector("p");
Element* slot = shadow_root->QuerySelector("slot");
EXPECT_EQ(before, FlatTreeTraversalNg::FirstChild(*d1));
EXPECT_EQ(after, FlatTreeTraversalNg::LastChild(*d1));
EXPECT_EQ(slot, FlatTreeTraversalNg::Parent(*span));
EXPECT_EQ(d1, FlatTreeTraversalNg::Parent(*slot));
EXPECT_EQ(slot, FlatTreeTraversalNg::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversalNg::NextSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*after));
EXPECT_EQ(slot, FlatTreeTraversalNg::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversalNg::PreviousSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*before));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::Parent(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*fallback_content));
}
TEST_F(FlatTreeTraversalNgTest, v1AllFallbackContent) {
const char* main_html = "<div id='d1'></div>";
const char* shadow_html =
"<slot name='a'><p id='x'>fallback content X</p></slot>"
"<slot name='b'><p id='y'>fallback content Y</p></slot>"
"<slot name='c'><p id='z'>fallback content Z</p></slot>";
SetupDocumentTree(main_html);
Element* body = GetDocument().body();
Element* d1 = body->QuerySelector("#d1");
AttachOpenShadowRoot(*d1, shadow_html);
ShadowRoot* shadow_root = d1->OpenShadowRoot();
Element* slot_a = shadow_root->QuerySelector("slot[name=a]");
Element* slot_b = shadow_root->QuerySelector("slot[name=b]");
Element* slot_c = shadow_root->QuerySelector("slot[name=c]");
Element* fallback_x = shadow_root->QuerySelector("#x");
Element* fallback_y = shadow_root->QuerySelector("#y");
Element* fallback_z = shadow_root->QuerySelector("#z");
EXPECT_EQ(slot_a, FlatTreeTraversalNg::FirstChild(*d1));
EXPECT_EQ(slot_c, FlatTreeTraversalNg::LastChild(*d1));
EXPECT_EQ(fallback_x, FlatTreeTraversalNg::FirstChild(*slot_a));
EXPECT_EQ(fallback_y, FlatTreeTraversalNg::FirstChild(*slot_b));
EXPECT_EQ(fallback_z, FlatTreeTraversalNg::FirstChild(*slot_c));
EXPECT_EQ(slot_a, FlatTreeTraversalNg::Parent(*fallback_x));
EXPECT_EQ(slot_b, FlatTreeTraversalNg::Parent(*fallback_y));
EXPECT_EQ(slot_c, FlatTreeTraversalNg::Parent(*fallback_z));
EXPECT_EQ(d1, FlatTreeTraversalNg::Parent(*slot_a));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*fallback_x));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::NextSibling(*fallback_z));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*fallback_z));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversalNg::PreviousSibling(*fallback_x));
}
} // namespace flat_tree_traversal_ng_test
} // namespace blink
......@@ -16,20 +16,23 @@
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/compiler.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
// To avoid symbol collisions in jumbo builds.
namespace flat_tree_traversal_test {
class FlatTreeTraversalTest : public PageTestBase,
private ScopedSlotInFlatTreeForTest,
ScopedIncrementalShadowDOMForTest {
public:
FlatTreeTraversalTest()
: ScopedSlotInFlatTreeForTest(false),
ScopedIncrementalShadowDOMForTest(false) {}
: ScopedSlotInFlatTreeForTest(true),
ScopedIncrementalShadowDOMForTest(true) {}
protected:
// Sets |mainHTML| to BODY element with |innerHTML| property and attaches
......@@ -142,7 +145,8 @@ TEST_F(FlatTreeTraversalTest, childAt) {
<< "FlatTreeTraversal::index(FlatTreeTraversal(*shadowHost, " << index
<< "))";
EXPECT_TRUE(FlatTreeTraversal::IsDescendantOf(*child, *shadow_host))
<< "FlatTreeTraversal::isDescendantOf(*FlatTreeTraversal(*shadowHost, "
<< "FlatTreeTraversal::isDescendantOf(*FlatTreeTraversal(*"
"shadowHost, "
<< index << "), *shadowHost)";
}
EXPECT_EQ(nullptr,
......@@ -508,9 +512,9 @@ TEST_F(FlatTreeTraversalTest, redistribution) {
// FlatTreeTraversal::traverseSiblings does not work for a node which is not
// in a document flat tree.
// e.g. The following test fails. The result of
// FlatTreeTraversal::previousSibling(*m11)) will be #m10, instead of nullptr.
// Element* m11 = body->querySelector("#m11");
// EXPECT_EQ(nullptr, FlatTreeTraversal::previousSibling(*m11));
// FlatTreeTraversal::previousSibling(*m11)) will be #m10, instead of
// nullptr. Element* m11 = body->querySelector("#m11"); EXPECT_EQ(nullptr,
// FlatTreeTraversal::previousSibling(*m11));
}
TEST_F(FlatTreeTraversalTest, v1Simple) {
......@@ -541,12 +545,44 @@ TEST_F(FlatTreeTraversalTest, v1Simple) {
EXPECT_TRUE(slot1);
EXPECT_TRUE(slot2);
EXPECT_EQ(shadow_child1, FlatTreeTraversal::FirstChild(*host));
EXPECT_EQ(child1, FlatTreeTraversal::NextSibling(*shadow_child1));
EXPECT_EQ(child2, FlatTreeTraversal::NextSibling(*child1));
EXPECT_EQ(shadow_child2, FlatTreeTraversal::NextSibling(*child2));
EXPECT_EQ(slot1, FlatTreeTraversal::NextSibling(*shadow_child1));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*child1));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*child2));
EXPECT_EQ(slot2, FlatTreeTraversal::NextSibling(*slot1));
EXPECT_EQ(shadow_child2, FlatTreeTraversal::NextSibling(*slot2));
}
TEST_F(FlatTreeTraversalTest, v1Redistribution) {
// composed tree:
// d1
// ├──/shadow-root
// │ └── d1-1
// │ ├──/shadow-root
// │ │ ├── d1-1-1
// │ │ ├── slot name=d1-1-s1
// │ │ ├── slot name=d1-1-s2
// │ │ └── d1-1-2
// │ ├── d1-2
// │ ├── slot id=d1-s0
// │ ├── slot name=d1-s1 slot=d1-1-s1
// │ ├── slot name=d1-s2
// │ ├── d1-3
// │ └── d1-4 slot=d1-1-s1
// ├── d2 slot=d1-s1
// ├── d3 slot=d1-s2
// ├── d4 slot=nonexistent
// └── d5
// flat tree:
// d1
// └── d1-1
// ├── d1-1-1
// ├── slot name=d1-1-s1
// │ ├── slot name=d1-s1 slot=d1-1-s1
// │ │ └── d2 slot=d1-s1
// │ └── d1-4 slot=d1-1-s1
// ├── slot name=d1-1-s2
// └── d1-1-2
const char* main_html =
"<div id='d1'>"
"<div id='d2' slot='d1-s1'></div>"
......@@ -605,20 +641,24 @@ TEST_F(FlatTreeTraversalTest, v1Redistribution) {
EXPECT_TRUE(d1s2);
EXPECT_TRUE(d11s1);
EXPECT_TRUE(d11s2);
EXPECT_EQ(d11, FlatTreeTraversal::Next(*d1));
EXPECT_EQ(d111, FlatTreeTraversal::Next(*d11));
EXPECT_EQ(d2, FlatTreeTraversal::Next(*d111));
EXPECT_EQ(d11s1, FlatTreeTraversal::Next(*d111));
EXPECT_EQ(d1s1, FlatTreeTraversal::Next(*d11s1));
EXPECT_EQ(d2, FlatTreeTraversal::Next(*d1s1));
EXPECT_EQ(d14, FlatTreeTraversal::Next(*d2));
EXPECT_EQ(d112, FlatTreeTraversal::Next(*d14));
EXPECT_EQ(d11s2, FlatTreeTraversal::Next(*d14));
EXPECT_EQ(d112, FlatTreeTraversal::Next(*d11s2));
EXPECT_EQ(d6, FlatTreeTraversal::Next(*d112));
EXPECT_EQ(d112, FlatTreeTraversal::Previous(*d6));
EXPECT_EQ(d11, FlatTreeTraversal::Parent(*d111));
EXPECT_EQ(d11, FlatTreeTraversal::Parent(*d112));
EXPECT_EQ(d11, FlatTreeTraversal::Parent(*d2));
EXPECT_EQ(d11, FlatTreeTraversal::Parent(*d14));
EXPECT_EQ(nullptr, FlatTreeTraversal::Parent(*d3));
EXPECT_EQ(d1s1, FlatTreeTraversal::Parent(*d2));
EXPECT_EQ(d11s1, FlatTreeTraversal::Parent(*d14));
EXPECT_EQ(d1s2, FlatTreeTraversal::Parent(*d3));
EXPECT_EQ(nullptr, FlatTreeTraversal::Parent(*d4));
}
......@@ -664,17 +704,20 @@ TEST_F(FlatTreeTraversalTest, v1FallbackContent) {
Element* before = shadow_root->QuerySelector("#before");
Element* after = shadow_root->QuerySelector("#after");
Element* fallback_content = shadow_root->QuerySelector("p");
Element* slot = shadow_root->QuerySelector("slot");
EXPECT_EQ(before, FlatTreeTraversal::FirstChild(*d1));
EXPECT_EQ(after, FlatTreeTraversal::LastChild(*d1));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*fallback_content));
EXPECT_EQ(slot, FlatTreeTraversal::Parent(*fallback_content));
EXPECT_EQ(fallback_content, FlatTreeTraversal::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversal::NextSibling(*fallback_content));
EXPECT_EQ(slot, FlatTreeTraversal::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversal::NextSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*after));
EXPECT_EQ(fallback_content, FlatTreeTraversal::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversal::PreviousSibling(*fallback_content));
EXPECT_EQ(slot, FlatTreeTraversal::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversal::PreviousSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*fallback_content));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*before));
}
......@@ -696,17 +739,19 @@ TEST_F(FlatTreeTraversalTest, v1FallbackContentSkippedInTraversal) {
Element* before = shadow_root->QuerySelector("#before");
Element* after = shadow_root->QuerySelector("#after");
Element* fallback_content = shadow_root->QuerySelector("p");
Element* slot = shadow_root->QuerySelector("slot");
EXPECT_EQ(before, FlatTreeTraversal::FirstChild(*d1));
EXPECT_EQ(after, FlatTreeTraversal::LastChild(*d1));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*span));
EXPECT_EQ(slot, FlatTreeTraversal::Parent(*span));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*slot));
EXPECT_EQ(span, FlatTreeTraversal::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversal::NextSibling(*span));
EXPECT_EQ(slot, FlatTreeTraversal::NextSibling(*before));
EXPECT_EQ(after, FlatTreeTraversal::NextSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*after));
EXPECT_EQ(span, FlatTreeTraversal::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversal::PreviousSibling(*span));
EXPECT_EQ(slot, FlatTreeTraversal::PreviousSibling(*after));
EXPECT_EQ(before, FlatTreeTraversal::PreviousSibling(*slot));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*before));
EXPECT_EQ(nullptr, FlatTreeTraversal::Parent(*fallback_content));
......@@ -728,40 +773,33 @@ TEST_F(FlatTreeTraversalTest, v1AllFallbackContent) {
AttachOpenShadowRoot(*d1, shadow_html);
ShadowRoot* shadow_root = d1->OpenShadowRoot();
Element* slot_a = shadow_root->QuerySelector("slot[name=a]");
Element* slot_b = shadow_root->QuerySelector("slot[name=b]");
Element* slot_c = shadow_root->QuerySelector("slot[name=c]");
Element* fallback_x = shadow_root->QuerySelector("#x");
Element* fallback_y = shadow_root->QuerySelector("#y");
Element* fallback_z = shadow_root->QuerySelector("#z");
EXPECT_EQ(fallback_x, FlatTreeTraversal::FirstChild(*d1));
EXPECT_EQ(fallback_z, FlatTreeTraversal::LastChild(*d1));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*fallback_x));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*fallback_y));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*fallback_z));
EXPECT_EQ(fallback_y, FlatTreeTraversal::NextSibling(*fallback_x));
EXPECT_EQ(fallback_z, FlatTreeTraversal::NextSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*fallback_z));
EXPECT_EQ(fallback_y, FlatTreeTraversal::PreviousSibling(*fallback_z));
EXPECT_EQ(fallback_x, FlatTreeTraversal::PreviousSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*fallback_x));
}
EXPECT_EQ(slot_a, FlatTreeTraversal::FirstChild(*d1));
EXPECT_EQ(slot_c, FlatTreeTraversal::LastChild(*d1));
TEST_F(FlatTreeTraversalTest, v0ParentDetailsInsertionPoint) {
const char* main_html = "<div><span></span></div>";
const char* shadow_html = "<content></content>";
EXPECT_EQ(fallback_x, FlatTreeTraversal::FirstChild(*slot_a));
EXPECT_EQ(fallback_y, FlatTreeTraversal::FirstChild(*slot_b));
EXPECT_EQ(fallback_z, FlatTreeTraversal::FirstChild(*slot_c));
SetupSampleHTML(main_html, shadow_html, 0);
EXPECT_EQ(slot_a, FlatTreeTraversal::Parent(*fallback_x));
EXPECT_EQ(slot_b, FlatTreeTraversal::Parent(*fallback_y));
EXPECT_EQ(slot_c, FlatTreeTraversal::Parent(*fallback_z));
EXPECT_EQ(d1, FlatTreeTraversal::Parent(*slot_a));
Element* span = GetDocument().body()->QuerySelector("span");
ASSERT_TRUE(span);
FlatTreeTraversal::ParentTraversalDetails details;
EXPECT_FALSE(details.GetInsertionPoint());
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*fallback_x));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversal::NextSibling(*fallback_z));
ContainerNode* parent = FlatTreeTraversal::Parent(*span, &details);
ASSERT_TRUE(parent);
EXPECT_TRUE(details.GetInsertionPoint());
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*fallback_z));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*fallback_y));
EXPECT_EQ(nullptr, FlatTreeTraversal::PreviousSibling(*fallback_x));
}
} // namespace flat_tree_traversal_test
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment