LCOV - code coverage report
Current view: top level - dom/base - ChildIterator.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 101 242 41.7 %
Date: 2017-07-14 16:53:18 Functions: 10 17 58.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ChildIterator.h"
       8             : #include "nsContentUtils.h"
       9             : #include "mozilla/dom/XBLChildrenElement.h"
      10             : #include "mozilla/dom/HTMLContentElement.h"
      11             : #include "mozilla/dom/HTMLShadowElement.h"
      12             : #include "mozilla/dom/ShadowRoot.h"
      13             : #include "nsIAnonymousContentCreator.h"
      14             : #include "nsIFrame.h"
      15             : #include "nsCSSAnonBoxes.h"
      16             : 
      17             : namespace mozilla {
      18             : namespace dom {
      19             : 
      20             : class MatchedNodes {
      21             : public:
      22           0 :   explicit MatchedNodes(HTMLContentElement* aInsertionPoint)
      23           0 :     : mIsContentElement(true), mContentElement(aInsertionPoint) {}
      24             : 
      25         260 :   explicit MatchedNodes(XBLChildrenElement* aInsertionPoint)
      26         260 :     : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}
      27             : 
      28          95 :   uint32_t Length() const
      29             :   {
      30         190 :     return mIsContentElement ? mContentElement->MatchedNodes().Length()
      31         190 :                              : mChildrenElement->InsertedChildrenLength();
      32             :   }
      33             : 
      34         113 :   nsIContent* operator[](int32_t aIndex) const
      35             :   {
      36         226 :     return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
      37         226 :                              : mChildrenElement->InsertedChild(aIndex);
      38             :   }
      39             : 
      40         148 :   bool IsEmpty() const
      41             :   {
      42         296 :     return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
      43         296 :                              : !mChildrenElement->HasInsertedChildren();
      44             :   }
      45             : protected:
      46             :   bool mIsContentElement;
      47             :   union {
      48             :     HTMLContentElement* mContentElement;
      49             :     XBLChildrenElement* mChildrenElement;
      50             :   };
      51             : };
      52             : 
      53             : static inline MatchedNodes
      54         260 : GetMatchedNodesForPoint(nsIContent* aContent)
      55             : {
      56         260 :   if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
      57             :     // XBL case
      58         260 :     return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
      59             :   }
      60             : 
      61             :   // Web components case
      62           0 :   MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::content));
      63           0 :   return MatchedNodes(HTMLContentElement::FromContent(aContent));
      64             : }
      65             : 
      66             : nsIContent*
      67        2203 : ExplicitChildIterator::GetNextChild()
      68             : {
      69             :   // If we're already in the inserted-children array, look there first
      70        2203 :   if (mIndexInInserted) {
      71          95 :     MOZ_ASSERT(mChild);
      72          95 :     MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
      73          95 :     MOZ_ASSERT(!mDefaultChild);
      74             : 
      75          95 :     MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
      76          95 :     if (mIndexInInserted < assignedChildren.Length()) {
      77          34 :       return assignedChildren[mIndexInInserted++];
      78             :     }
      79          61 :     mIndexInInserted = 0;
      80          61 :     mChild = mChild->GetNextSibling();
      81        2108 :   } else if (mShadowIterator) {
      82             :     // If we're inside of a <shadow> element, look through the
      83             :     // explicit children of the projected ShadowRoot via
      84             :     // the mShadowIterator.
      85           0 :     nsIContent* nextChild = mShadowIterator->GetNextChild();
      86           0 :     if (nextChild) {
      87           0 :       return nextChild;
      88             :     }
      89             : 
      90           0 :     mShadowIterator = nullptr;
      91           0 :     mChild = mChild->GetNextSibling();
      92        2108 :   } else if (mDefaultChild) {
      93             :     // If we're already in default content, check if there are more nodes there
      94           0 :     MOZ_ASSERT(mChild);
      95           0 :     MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
      96             : 
      97           0 :     mDefaultChild = mDefaultChild->GetNextSibling();
      98           0 :     if (mDefaultChild) {
      99           0 :       return mDefaultChild;
     100             :     }
     101             : 
     102           0 :     mChild = mChild->GetNextSibling();
     103        2108 :   } else if (mIsFirst) {  // at the beginning of the child list
     104         659 :     mChild = mParent->GetFirstChild();
     105         659 :     mIsFirst = false;
     106        1449 :   } else if (mChild) { // in the middle of the child list
     107        1449 :     mChild = mChild->GetNextSibling();
     108             :   }
     109             : 
     110             :   // Iterate until we find a non-insertion point, or an insertion point with
     111             :   // content.
     112        2329 :   while (mChild) {
     113             :     // If the current child being iterated is a shadow insertion point then
     114             :     // the iterator needs to go into the projected ShadowRoot.
     115        1604 :     if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
     116             :       // Look for the next child in the projected ShadowRoot for the <shadow>
     117             :       // element.
     118           0 :       HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
     119           0 :       ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
     120           0 :       if (projectedShadow) {
     121           0 :         mShadowIterator = new ExplicitChildIterator(projectedShadow);
     122           0 :         nsIContent* nextChild = mShadowIterator->GetNextChild();
     123           0 :         if (nextChild) {
     124           0 :           return nextChild;
     125             :         }
     126           0 :         mShadowIterator = nullptr;
     127             :       }
     128           0 :       mChild = mChild->GetNextSibling();
     129        1604 :     } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
     130             :       // If the current child being iterated is a content insertion point
     131             :       // then the iterator needs to return the nodes distributed into
     132             :       // the content insertion point.
     133         147 :       MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     134         147 :       if (!assignedChildren.IsEmpty()) {
     135             :         // Iterate through elements projected on insertion point.
     136          67 :         mIndexInInserted = 1;
     137         134 :         return assignedChildren[0];
     138             :       }
     139             : 
     140             :       // Insertion points inside fallback/default content
     141             :       // are considered inactive and do not get assigned nodes.
     142          80 :       mDefaultChild = mChild->GetFirstChild();
     143          80 :       if (mDefaultChild) {
     144           0 :         return mDefaultChild;
     145             :       }
     146             : 
     147             :       // If we have an insertion point with no assigned nodes and
     148             :       // no default content, move on to the next node.
     149          80 :       mChild = mChild->GetNextSibling();
     150             :     } else {
     151             :       // mChild is not an insertion point, thus it is the next node to
     152             :       // return from this iterator.
     153        1457 :       break;
     154             :     }
     155             :   }
     156             : 
     157        2102 :   return mChild;
     158             : }
     159             : 
     160             : void
     161         582 : FlattenedChildIterator::Init(bool aIgnoreXBL)
     162             : {
     163         582 :   if (aIgnoreXBL) {
     164           0 :     return;
     165             :   }
     166             : 
     167             :   nsXBLBinding* binding =
     168         582 :     mParent->OwnerDoc()->BindingManager()->GetBindingWithContent(mParent);
     169             : 
     170         582 :   if (binding) {
     171         117 :     nsIContent* anon = binding->GetAnonymousContent();
     172         117 :     if (anon) {
     173         117 :       mParent = anon;
     174         117 :       mXBLInvolved = true;
     175             :     }
     176             :   }
     177             : 
     178             :   // We set mXBLInvolved to true if either:
     179             :   // - The node we're iterating has a binding with content attached to it.
     180             :   // - The node is generated XBL content and has an <xbl:children> child.
     181         582 :   if (!mXBLInvolved && mParent->GetBindingParent()) {
     182         318 :     for (nsIContent* child = mParent->GetFirstChild();
     183         318 :          child;
     184         144 :          child = child->GetNextSibling()) {
     185         161 :       if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     186          17 :         MOZ_ASSERT(child->GetBindingParent());
     187          17 :         mXBLInvolved = true;
     188          17 :         break;
     189             :       }
     190             :     }
     191             :   }
     192             : }
     193             : 
     194             : bool
     195          24 : ExplicitChildIterator::Seek(nsIContent* aChildToFind)
     196             : {
     197          39 :   if (aChildToFind->GetParent() == mParent &&
     198          15 :       !aChildToFind->IsRootOfAnonymousSubtree()) {
     199             :     // Fast path: just point ourselves to aChildToFind, which is a
     200             :     // normal DOM child of ours.
     201          15 :     MOZ_ASSERT(!ShadowRoot::IsShadowInsertionPoint(aChildToFind));
     202          15 :     MOZ_ASSERT(!nsContentUtils::IsContentInsertionPoint(aChildToFind));
     203          15 :     mChild = aChildToFind;
     204          15 :     mIndexInInserted = 0;
     205          15 :     mShadowIterator = nullptr;
     206          15 :     mDefaultChild = nullptr;
     207          15 :     mIsFirst = false;
     208          15 :     return true;
     209             :   }
     210             : 
     211             :   // Can we add more fast paths here based on whether the parent of aChildToFind
     212             :   // is a shadow insertion point or content insertion point?
     213             : 
     214             :   // Slow path: just walk all our kids.
     215           9 :   return Seek(aChildToFind, nullptr);
     216             : }
     217             : 
     218             : nsIContent*
     219          37 : ExplicitChildIterator::Get() const
     220             : {
     221          37 :   MOZ_ASSERT(!mIsFirst);
     222             : 
     223          37 :   if (mIndexInInserted) {
     224          10 :     MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     225          10 :     return assignedChildren[mIndexInInserted - 1];
     226          27 :   } else if (mShadowIterator)  {
     227           0 :     return mShadowIterator->Get();
     228             :   }
     229          27 :   return mDefaultChild ? mDefaultChild : mChild;
     230             : }
     231             : 
     232             : nsIContent*
     233          33 : ExplicitChildIterator::GetPreviousChild()
     234             : {
     235             :   // If we're already in the inserted-children array, look there first
     236          33 :   if (mIndexInInserted) {
     237             :     // NB: mIndexInInserted points one past the last returned child so we need
     238             :     // to look *two* indices back in order to return the previous child.
     239           7 :     MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     240           7 :     if (--mIndexInInserted) {
     241           2 :       return assignedChildren[mIndexInInserted - 1];
     242             :     }
     243           5 :     mChild = mChild->GetPreviousSibling();
     244          26 :   } else if (mShadowIterator) {
     245           0 :     nsIContent* previousChild = mShadowIterator->GetPreviousChild();
     246           0 :     if (previousChild) {
     247           0 :       return previousChild;
     248             :     }
     249           0 :     mShadowIterator = nullptr;
     250           0 :     mChild = mChild->GetPreviousSibling();
     251          26 :   } else if (mDefaultChild) {
     252             :     // If we're already in default content, check if there are more nodes there
     253           0 :     mDefaultChild = mDefaultChild->GetPreviousSibling();
     254           0 :     if (mDefaultChild) {
     255           0 :       return mDefaultChild;
     256             :     }
     257             : 
     258           0 :     mChild = mChild->GetPreviousSibling();
     259          26 :   } else if (mIsFirst) { // at the beginning of the child list
     260           0 :     return nullptr;
     261          26 :   } else if (mChild) { // in the middle of the child list
     262          26 :     mChild = mChild->GetPreviousSibling();
     263             :   } else { // at the end of the child list
     264           0 :     mChild = mParent->GetLastChild();
     265             :   }
     266             : 
     267             :   // Iterate until we find a non-insertion point, or an insertion point with
     268             :   // content.
     269          33 :   while (mChild) {
     270          19 :     if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
     271             :       // If the current child being iterated is a shadow insertion point then
     272             :       // the iterator needs to go into the projected ShadowRoot.
     273           0 :       HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(mChild);
     274           0 :       ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
     275           0 :       if (projectedShadow) {
     276             :         // Create a ExplicitChildIterator that begins iterating from the end.
     277           0 :         mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
     278           0 :         nsIContent* previousChild = mShadowIterator->GetPreviousChild();
     279           0 :         if (previousChild) {
     280           0 :           return previousChild;
     281             :         }
     282           0 :         mShadowIterator = nullptr;
     283             :       }
     284           0 :       mChild = mChild->GetPreviousSibling();
     285          19 :     } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
     286             :       // If the current child being iterated is a content insertion point
     287             :       // then the iterator needs to return the nodes distributed into
     288             :       // the content insertion point.
     289           1 :       MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     290           1 :       if (!assignedChildren.IsEmpty()) {
     291           0 :         mIndexInInserted = assignedChildren.Length();
     292           0 :         return assignedChildren[mIndexInInserted - 1];
     293             :       }
     294             : 
     295           1 :       mDefaultChild = mChild->GetLastChild();
     296           1 :       if (mDefaultChild) {
     297           0 :         return mDefaultChild;
     298             :       }
     299             : 
     300           1 :       mChild = mChild->GetPreviousSibling();
     301             :     } else {
     302             :       // mChild is not an insertion point, thus it is the next node to
     303             :       // return from this iterator.
     304          18 :       break;
     305             :     }
     306             :   }
     307             : 
     308          31 :   if (!mChild) {
     309          13 :     mIsFirst = true;
     310             :   }
     311             : 
     312          31 :   return mChild;
     313             : }
     314             : 
     315             : nsIContent*
     316           0 : AllChildrenIterator::Get() const
     317             : {
     318           0 :   switch (mPhase) {
     319             :     case eAtBeforeKid: {
     320           0 :       Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     321           0 :       MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
     322           0 :       return before;
     323             :     }
     324             : 
     325             :     case eAtExplicitKids:
     326           0 :       return ExplicitChildIterator::Get();
     327             : 
     328             :     case eAtAnonKids:
     329           0 :       return mAnonKids[mAnonKidsIdx];
     330             : 
     331             :     case eAtAfterKid: {
     332           0 :       Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     333           0 :       MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
     334           0 :       return after;
     335             :     }
     336             : 
     337             :     default:
     338           0 :       return nullptr;
     339             :   }
     340             : }
     341             : 
     342             : 
     343             : bool
     344           0 : AllChildrenIterator::Seek(nsIContent* aChildToFind)
     345             : {
     346           0 :   if (mPhase == eAtBegin || mPhase == eAtBeforeKid) {
     347           0 :     mPhase = eAtExplicitKids;
     348           0 :     Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     349           0 :     if (beforePseudo && beforePseudo == aChildToFind) {
     350           0 :       mPhase = eAtBeforeKid;
     351           0 :       return true;
     352             :     }
     353             :   }
     354             : 
     355           0 :   if (mPhase == eAtExplicitKids) {
     356           0 :     if (ExplicitChildIterator::Seek(aChildToFind)) {
     357           0 :       return true;
     358             :     }
     359           0 :     mPhase = eAtAnonKids;
     360             :   }
     361             : 
     362           0 :   nsIContent* child = nullptr;
     363           0 :   do {
     364           0 :     child = GetNextChild();
     365           0 :   } while (child && child != aChildToFind);
     366             : 
     367           0 :   return child == aChildToFind;
     368             : }
     369             : 
     370             : void
     371           0 : AllChildrenIterator::AppendNativeAnonymousChildren()
     372             : {
     373           0 :   nsContentUtils::AppendNativeAnonymousChildren(
     374           0 :       mOriginalContent, mAnonKids, mFlags);
     375           0 : }
     376             : 
     377             : nsIContent*
     378           0 : AllChildrenIterator::GetNextChild()
     379             : {
     380           0 :   if (mPhase == eAtBegin) {
     381           0 :     mPhase = eAtExplicitKids;
     382           0 :     Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     383           0 :     if (beforeContent) {
     384           0 :       mPhase = eAtBeforeKid;
     385           0 :       return beforeContent;
     386             :     }
     387             :   }
     388             : 
     389           0 :   if (mPhase == eAtBeforeKid) {
     390             :     // Advance into our explicit kids.
     391           0 :     mPhase = eAtExplicitKids;
     392             :   }
     393             : 
     394           0 :   if (mPhase == eAtExplicitKids) {
     395           0 :     nsIContent* kid = ExplicitChildIterator::GetNextChild();
     396           0 :     if (kid) {
     397           0 :       return kid;
     398             :     }
     399           0 :     mPhase = eAtAnonKids;
     400             :   }
     401             : 
     402           0 :   if (mPhase == eAtAnonKids) {
     403           0 :     if (mAnonKids.IsEmpty()) {
     404           0 :       MOZ_ASSERT(mAnonKidsIdx == UINT32_MAX);
     405           0 :       AppendNativeAnonymousChildren();
     406           0 :       mAnonKidsIdx = 0;
     407             :     }
     408             :     else {
     409           0 :       if (mAnonKidsIdx == UINT32_MAX) {
     410           0 :         mAnonKidsIdx = 0;
     411             :       }
     412             :       else {
     413           0 :         mAnonKidsIdx++;
     414             :       }
     415             :     }
     416             : 
     417           0 :     if (mAnonKidsIdx < mAnonKids.Length()) {
     418           0 :       return mAnonKids[mAnonKidsIdx];
     419             :     }
     420             : 
     421           0 :     Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     422           0 :     if (afterContent) {
     423           0 :       mPhase = eAtAfterKid;
     424           0 :       return afterContent;
     425             :     }
     426             :   }
     427             : 
     428           0 :   mPhase = eAtEnd;
     429           0 :   return nullptr;
     430             : }
     431             : 
     432             : nsIContent*
     433           0 : AllChildrenIterator::GetPreviousChild()
     434             : {
     435           0 :   if (mPhase == eAtEnd) {
     436           0 :     MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length());
     437           0 :     mPhase = eAtAnonKids;
     438           0 :     Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     439           0 :     if (afterContent) {
     440           0 :       mPhase = eAtAfterKid;
     441           0 :       return afterContent;
     442             :     }
     443             :   }
     444             : 
     445           0 :   if (mPhase == eAtAfterKid) {
     446           0 :     mPhase = eAtAnonKids;
     447             :   }
     448             : 
     449           0 :   if (mPhase == eAtAnonKids) {
     450           0 :     if (mAnonKids.IsEmpty()) {
     451           0 :       AppendNativeAnonymousChildren();
     452           0 :       mAnonKidsIdx = mAnonKids.Length();
     453             :     }
     454             : 
     455             :     // If 0 then it turns into UINT32_MAX, which indicates the iterator is
     456             :     // before the anonymous children.
     457           0 :     --mAnonKidsIdx;
     458           0 :     if (mAnonKidsIdx < mAnonKids.Length()) {
     459           0 :       return mAnonKids[mAnonKidsIdx];
     460             :     }
     461           0 :     mPhase = eAtExplicitKids;
     462             :   }
     463             : 
     464           0 :   if (mPhase == eAtExplicitKids) {
     465           0 :     nsIContent* kid = ExplicitChildIterator::GetPreviousChild();
     466           0 :     if (kid) {
     467           0 :       return kid;
     468             :     }
     469             : 
     470           0 :     Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     471           0 :     if (beforeContent) {
     472           0 :       mPhase = eAtBeforeKid;
     473           0 :       return beforeContent;
     474             :     }
     475             :   }
     476             : 
     477           0 :   mPhase = eAtBegin;
     478           0 :   return nullptr;
     479             : }
     480             : 
     481             : nsIContent*
     482           0 : StyleChildrenIterator::GetNextChild()
     483             : {
     484           0 :   return AllChildrenIterator::GetNextChild();
     485             : }
     486             : 
     487             : } // namespace dom
     488             : } // namespace mozilla

Generated by: LCOV version 1.13