1 2 /* 3 * Copyright (C) 2012 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Neither the name of Google Inc. nor the names of its 12 * contributors may be used to endorse or promote products derived from 13 * this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 #include "core/dom/shadow/ComposedTreeWalker.h" 30 31 #include "core/dom/Element.h" 32 #include "core/dom/shadow/ElementShadow.h" 33 #include "core/html/shadow/HTMLContentElement.h" 34 #include "core/html/shadow/HTMLShadowElement.h" 35 36 namespace WebCore { 37 38 static inline ElementShadow* shadowFor(const Node* node) 39 { 40 if (node && node->isElementNode()) 41 return toElement(node)->shadow(); 42 return 0; 43 } 44 45 Node* ComposedTreeWalker::traverseChild(const Node* node, TraversalDirection direction) const 46 { 47 ASSERT(node); 48 ElementShadow* shadow = shadowFor(node); 49 return shadow ? traverseLightChildren(shadow->youngestShadowRoot(), direction) 50 : traverseLightChildren(node, direction); 51 } 52 53 Node* ComposedTreeWalker::traverseLightChildren(const Node* node, TraversalDirection direction) 54 { 55 ASSERT(node); 56 return traverseSiblings(direction == TraversalDirectionForward ? node->firstChild() : node->lastChild(), direction); 57 } 58 59 Node* ComposedTreeWalker::traverseSiblings(const Node* node, TraversalDirection direction) 60 { 61 for (const Node* sibling = node; sibling; sibling = (direction == TraversalDirectionForward ? sibling->nextSibling() : sibling->previousSibling())) { 62 if (Node* found = traverseNode(sibling, direction)) 63 return found; 64 } 65 return 0; 66 } 67 68 Node* ComposedTreeWalker::traverseNode(const Node* node, TraversalDirection direction) 69 { 70 ASSERT(node); 71 if (!isActiveInsertionPoint(*node)) 72 return const_cast<Node*>(node); 73 const InsertionPoint* insertionPoint = toInsertionPoint(node); 74 if (Node* found = traverseDistributedNodes(direction == TraversalDirectionForward ? insertionPoint->first() : insertionPoint->last(), insertionPoint, direction)) 75 return found; 76 ASSERT(isHTMLShadowElement(node) || (isHTMLContentElement(node) && !node->hasChildNodes())); 77 return 0; 78 } 79 80 Node* ComposedTreeWalker::traverseDistributedNodes(const Node* node, const InsertionPoint* insertionPoint, TraversalDirection direction) 81 { 82 for (const Node* next = node; next; next = (direction == TraversalDirectionForward ? insertionPoint->nextTo(next) : insertionPoint->previousTo(next))) { 83 if (Node* found = traverseNode(next, direction)) 84 return found; 85 } 86 return 0; 87 } 88 89 Node* ComposedTreeWalker::traverseSiblingOrBackToInsertionPoint(const Node* node, TraversalDirection direction) 90 { 91 ASSERT(node); 92 93 if (!shadowWhereNodeCanBeDistributed(*node)) 94 return traverseSiblingInCurrentTree(node, direction); 95 96 const InsertionPoint* insertionPoint = resolveReprojection(node); 97 if (!insertionPoint) 98 return traverseSiblingInCurrentTree(node, direction); 99 100 if (Node* found = traverseDistributedNodes(direction == TraversalDirectionForward ? insertionPoint->nextTo(node) : insertionPoint->previousTo(node), insertionPoint, direction)) 101 return found; 102 return traverseSiblingOrBackToInsertionPoint(insertionPoint, direction); 103 } 104 105 Node* ComposedTreeWalker::traverseSiblingInCurrentTree(const Node* node, TraversalDirection direction) 106 { 107 ASSERT(node); 108 if (Node* found = traverseSiblings(direction == TraversalDirectionForward ? node->nextSibling() : node->previousSibling(), direction)) 109 return found; 110 if (Node* next = traverseBackToYoungerShadowRoot(node, direction)) 111 return next; 112 return 0; 113 } 114 115 Node* ComposedTreeWalker::traverseBackToYoungerShadowRoot(const Node* node, TraversalDirection direction) 116 { 117 ASSERT(node); 118 if (node->parentNode() && node->parentNode()->isShadowRoot()) { 119 ShadowRoot* parentShadowRoot = toShadowRoot(node->parentNode()); 120 if (!parentShadowRoot->isYoungest()) { 121 HTMLShadowElement* assignedInsertionPoint = parentShadowRoot->shadowInsertionPointOfYoungerShadowRoot(); 122 ASSERT(assignedInsertionPoint); 123 return traverseSiblingInCurrentTree(assignedInsertionPoint, direction); 124 } 125 } 126 return 0; 127 } 128 129 // FIXME: Use an iterative algorithm so that it can be inlined. 130 // https://bugs.webkit.org/show_bug.cgi?id=90415 131 Node* ComposedTreeWalker::traverseParent(const Node* node, ParentTraversalDetails* details) const 132 { 133 if (node->isPseudoElement()) 134 return node->parentOrShadowHostNode(); 135 136 if (shadowWhereNodeCanBeDistributed(*node)) { 137 if (const InsertionPoint* insertionPoint = resolveReprojection(node)) { 138 if (details) 139 details->didTraverseInsertionPoint(insertionPoint); 140 // The node is distributed. But the distribution was stopped at this insertion point. 141 if (shadowWhereNodeCanBeDistributed(*insertionPoint)) 142 return 0; 143 return traverseParentOrHost(insertionPoint, details); 144 } 145 return 0; 146 } 147 return traverseParentOrHost(node, details); 148 } 149 150 inline Node* ComposedTreeWalker::traverseParentOrHost(const Node* node, ParentTraversalDetails* details) const 151 { 152 Node* parent = node->parentNode(); 153 if (!parent) 154 return 0; 155 if (!parent->isShadowRoot()) 156 return parent; 157 ShadowRoot* shadowRoot = toShadowRoot(parent); 158 ASSERT(!shadowRoot->shadowInsertionPointOfYoungerShadowRoot()); 159 if (!shadowRoot->isYoungest()) 160 return 0; 161 if (details) 162 details->didTraverseShadowRoot(shadowRoot); 163 return shadowRoot->host(); 164 } 165 166 } // namespace 167