1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/ChildIterator.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,285 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ChildIterator.h" 1.11 +#include "nsContentUtils.h" 1.12 +#include "mozilla/dom/XBLChildrenElement.h" 1.13 +#include "mozilla/dom/HTMLContentElement.h" 1.14 +#include "mozilla/dom/HTMLShadowElement.h" 1.15 +#include "mozilla/dom/ShadowRoot.h" 1.16 + 1.17 +namespace mozilla { 1.18 +namespace dom { 1.19 + 1.20 +class MatchedNodes { 1.21 +public: 1.22 + MatchedNodes(HTMLContentElement* aInsertionPoint) 1.23 + : mIsContentElement(true), mContentElement(aInsertionPoint) {} 1.24 + 1.25 + MatchedNodes(XBLChildrenElement* aInsertionPoint) 1.26 + : mIsContentElement(false), mChildrenElement(aInsertionPoint) {} 1.27 + 1.28 + uint32_t Length() const 1.29 + { 1.30 + return mIsContentElement ? mContentElement->MatchedNodes().Length() 1.31 + : mChildrenElement->mInsertedChildren.Length(); 1.32 + } 1.33 + 1.34 + nsIContent* operator[](int32_t aIndex) const 1.35 + { 1.36 + return mIsContentElement ? mContentElement->MatchedNodes()[aIndex] 1.37 + : mChildrenElement->mInsertedChildren[aIndex]; 1.38 + } 1.39 + 1.40 + bool IsEmpty() const 1.41 + { 1.42 + return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty() 1.43 + : mChildrenElement->mInsertedChildren.IsEmpty(); 1.44 + } 1.45 +protected: 1.46 + bool mIsContentElement; 1.47 + union { 1.48 + HTMLContentElement* mContentElement; 1.49 + XBLChildrenElement* mChildrenElement; 1.50 + }; 1.51 +}; 1.52 + 1.53 +static inline MatchedNodes 1.54 +GetMatchedNodesForPoint(nsIContent* aContent) 1.55 +{ 1.56 + if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { 1.57 + // XBL case 1.58 + return MatchedNodes(static_cast<XBLChildrenElement*>(aContent)); 1.59 + } 1.60 + 1.61 + // Web components case 1.62 + MOZ_ASSERT(aContent->IsHTML(nsGkAtoms::content)); 1.63 + return MatchedNodes(static_cast<HTMLContentElement*>(aContent)); 1.64 +} 1.65 + 1.66 +nsIContent* 1.67 +ExplicitChildIterator::GetNextChild() 1.68 +{ 1.69 + // If we're already in the inserted-children array, look there first 1.70 + if (mIndexInInserted) { 1.71 + MOZ_ASSERT(mChild); 1.72 + MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild)); 1.73 + MOZ_ASSERT(!mDefaultChild); 1.74 + 1.75 + MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); 1.76 + if (mIndexInInserted < assignedChildren.Length()) { 1.77 + return assignedChildren[mIndexInInserted++]; 1.78 + } 1.79 + mIndexInInserted = 0; 1.80 + mChild = mChild->GetNextSibling(); 1.81 + } else if (mShadowIterator) { 1.82 + // If we're inside of a <shadow> element, look through the 1.83 + // explicit children of the projected ShadowRoot via 1.84 + // the mShadowIterator. 1.85 + nsIContent* nextChild = mShadowIterator->GetNextChild(); 1.86 + if (nextChild) { 1.87 + return nextChild; 1.88 + } 1.89 + 1.90 + mShadowIterator = nullptr; 1.91 + mChild = mChild->GetNextSibling(); 1.92 + } else if (mDefaultChild) { 1.93 + // If we're already in default content, check if there are more nodes there 1.94 + MOZ_ASSERT(mChild); 1.95 + MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild)); 1.96 + 1.97 + mDefaultChild = mDefaultChild->GetNextSibling(); 1.98 + if (mDefaultChild) { 1.99 + return mDefaultChild; 1.100 + } 1.101 + 1.102 + mChild = mChild->GetNextSibling(); 1.103 + } else if (mIsFirst) { // at the beginning of the child list 1.104 + mChild = mParent->GetFirstChild(); 1.105 + mIsFirst = false; 1.106 + } else if (mChild) { // in the middle of the child list 1.107 + mChild = mChild->GetNextSibling(); 1.108 + } 1.109 + 1.110 + // Iterate until we find a non-insertion point, or an insertion point with 1.111 + // content. 1.112 + while (mChild) { 1.113 + // If the current child being iterated is a shadow insertion point then 1.114 + // the iterator needs to go into the projected ShadowRoot. 1.115 + if (ShadowRoot::IsShadowInsertionPoint(mChild)) { 1.116 + // Look for the next child in the projected ShadowRoot for the <shadow> 1.117 + // element. 1.118 + HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild); 1.119 + ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot(); 1.120 + if (projectedShadow) { 1.121 + mShadowIterator = new ExplicitChildIterator(projectedShadow); 1.122 + nsIContent* nextChild = mShadowIterator->GetNextChild(); 1.123 + if (nextChild) { 1.124 + return nextChild; 1.125 + } 1.126 + mShadowIterator = nullptr; 1.127 + } 1.128 + mChild = mChild->GetNextSibling(); 1.129 + } else if (nsContentUtils::IsContentInsertionPoint(mChild)) { 1.130 + // If the current child being iterated is a content insertion point 1.131 + // then the iterator needs to return the nodes distributed into 1.132 + // the content insertion point. 1.133 + MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); 1.134 + if (!assignedChildren.IsEmpty()) { 1.135 + // Iterate through elements projected on insertion point. 1.136 + mIndexInInserted = 1; 1.137 + return assignedChildren[0]; 1.138 + } 1.139 + 1.140 + // Insertion points inside fallback/default content 1.141 + // are considered inactive and do not get assigned nodes. 1.142 + mDefaultChild = mChild->GetFirstChild(); 1.143 + if (mDefaultChild) { 1.144 + return mDefaultChild; 1.145 + } 1.146 + 1.147 + // If we have an insertion point with no assigned nodes and 1.148 + // no default content, move on to the next node. 1.149 + mChild = mChild->GetNextSibling(); 1.150 + } else { 1.151 + // mChild is not an insertion point, thus it is the next node to 1.152 + // return from this iterator. 1.153 + break; 1.154 + } 1.155 + } 1.156 + 1.157 + return mChild; 1.158 +} 1.159 + 1.160 +FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent) 1.161 + : ExplicitChildIterator(aParent), mXBLInvolved(false) 1.162 +{ 1.163 + nsXBLBinding* binding = 1.164 + aParent->OwnerDoc()->BindingManager()->GetBindingWithContent(aParent); 1.165 + 1.166 + if (binding) { 1.167 + nsIContent* anon = binding->GetAnonymousContent(); 1.168 + if (anon) { 1.169 + mParent = anon; 1.170 + mXBLInvolved = true; 1.171 + } 1.172 + } 1.173 + 1.174 + // We set mXBLInvolved to true if either: 1.175 + // - The node we're iterating has a binding with content attached to it. 1.176 + // - The node is generated XBL content and has an <xbl:children> child. 1.177 + if (!mXBLInvolved && aParent->GetBindingParent()) { 1.178 + for (nsIContent* child = aParent->GetFirstChild(); 1.179 + child; 1.180 + child = child->GetNextSibling()) { 1.181 + if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { 1.182 + MOZ_ASSERT(child->GetBindingParent()); 1.183 + mXBLInvolved = true; 1.184 + break; 1.185 + } 1.186 + } 1.187 + } 1.188 +} 1.189 + 1.190 +nsIContent* 1.191 +ExplicitChildIterator::Get() 1.192 +{ 1.193 + MOZ_ASSERT(!mIsFirst); 1.194 + 1.195 + if (mIndexInInserted) { 1.196 + XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild); 1.197 + return point->mInsertedChildren[mIndexInInserted - 1]; 1.198 + } else if (mShadowIterator) { 1.199 + return mShadowIterator->Get(); 1.200 + } 1.201 + return mDefaultChild ? mDefaultChild : mChild; 1.202 +} 1.203 + 1.204 +nsIContent* 1.205 +ExplicitChildIterator::GetPreviousChild() 1.206 +{ 1.207 + // If we're already in the inserted-children array, look there first 1.208 + if (mIndexInInserted) { 1.209 + // NB: mIndexInInserted points one past the last returned child so we need 1.210 + // to look *two* indices back in order to return the previous child. 1.211 + MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); 1.212 + if (--mIndexInInserted) { 1.213 + return assignedChildren[mIndexInInserted - 1]; 1.214 + } 1.215 + mChild = mChild->GetPreviousSibling(); 1.216 + } else if (mShadowIterator) { 1.217 + nsIContent* previousChild = mShadowIterator->GetPreviousChild(); 1.218 + if (previousChild) { 1.219 + return previousChild; 1.220 + } 1.221 + mShadowIterator = nullptr; 1.222 + mChild = mChild->GetPreviousSibling(); 1.223 + } else if (mDefaultChild) { 1.224 + // If we're already in default content, check if there are more nodes there 1.225 + mDefaultChild = mDefaultChild->GetPreviousSibling(); 1.226 + if (mDefaultChild) { 1.227 + return mDefaultChild; 1.228 + } 1.229 + 1.230 + mChild = mChild->GetPreviousSibling(); 1.231 + } else if (mIsFirst) { // at the beginning of the child list 1.232 + return nullptr; 1.233 + } else if (mChild) { // in the middle of the child list 1.234 + mChild = mChild->GetPreviousSibling(); 1.235 + } else { // at the end of the child list 1.236 + mChild = mParent->GetLastChild(); 1.237 + } 1.238 + 1.239 + // Iterate until we find a non-insertion point, or an insertion point with 1.240 + // content. 1.241 + while (mChild) { 1.242 + if (ShadowRoot::IsShadowInsertionPoint(mChild)) { 1.243 + // If the current child being iterated is a shadow insertion point then 1.244 + // the iterator needs to go into the projected ShadowRoot. 1.245 + HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild); 1.246 + ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot(); 1.247 + if (projectedShadow) { 1.248 + // Create a ExplicitChildIterator that begins iterating from the end. 1.249 + mShadowIterator = new ExplicitChildIterator(projectedShadow, false); 1.250 + nsIContent* previousChild = mShadowIterator->GetPreviousChild(); 1.251 + if (previousChild) { 1.252 + return previousChild; 1.253 + } 1.254 + mShadowIterator = nullptr; 1.255 + } 1.256 + mChild = mChild->GetPreviousSibling(); 1.257 + } else if (nsContentUtils::IsContentInsertionPoint(mChild)) { 1.258 + // If the current child being iterated is a content insertion point 1.259 + // then the iterator needs to return the nodes distributed into 1.260 + // the content insertion point. 1.261 + MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild); 1.262 + if (!assignedChildren.IsEmpty()) { 1.263 + mIndexInInserted = assignedChildren.Length(); 1.264 + return assignedChildren[mIndexInInserted - 1]; 1.265 + } 1.266 + 1.267 + mDefaultChild = mChild->GetLastChild(); 1.268 + if (mDefaultChild) { 1.269 + return mDefaultChild; 1.270 + } 1.271 + 1.272 + mChild = mChild->GetPreviousSibling(); 1.273 + } else { 1.274 + // mChild is not an insertion point, thus it is the next node to 1.275 + // return from this iterator. 1.276 + break; 1.277 + } 1.278 + } 1.279 + 1.280 + if (!mChild) { 1.281 + mIsFirst = true; 1.282 + } 1.283 + 1.284 + return mChild; 1.285 +} 1.286 + 1.287 +} // namespace dom 1.288 +} // namespace mozilla