LCOV - code coverage report
Current view: top level - dom/base - ChildIterator.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 42 208 20.2 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
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/HTMLSlotElement.h"
      10             : #include "mozilla/dom/XBLChildrenElement.h"
      11             : #include "mozilla/dom/ShadowRoot.h"
      12             : #include "nsIAnonymousContentCreator.h"
      13             : #include "nsIFrame.h"
      14             : #include "nsCSSAnonBoxes.h"
      15             : #include "nsDocument.h"
      16             : 
      17             : namespace mozilla {
      18             : namespace dom {
      19             : 
      20           0 : ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent,
      21           0 :                                              bool aStartAtBeginning)
      22             :   : mParent(aParent),
      23             :     mChild(nullptr),
      24             :     mDefaultChild(nullptr),
      25             :     mIsFirst(aStartAtBeginning),
      26           0 :     mIndexInInserted(0)
      27             : {
      28           0 :   mParentAsSlot = nsDocument::IsShadowDOMEnabled(mParent) ?
      29           0 :     HTMLSlotElement::FromNode(mParent) : nullptr;
      30           0 : }
      31             : 
      32             : nsIContent*
      33           0 : ExplicitChildIterator::GetNextChild()
      34             : {
      35             :   // If we're already in the inserted-children array, look there first
      36           0 :   if (mIndexInInserted) {
      37           0 :     MOZ_ASSERT(mChild);
      38           0 :     MOZ_ASSERT(!mDefaultChild);
      39             : 
      40           0 :     if (mParentAsSlot) {
      41             :       const nsTArray<RefPtr<nsINode>>& assignedNodes =
      42           0 :         mParentAsSlot->AssignedNodes();
      43             : 
      44           0 :       mChild = (mIndexInInserted < assignedNodes.Length()) ?
      45           0 :         assignedNodes[mIndexInInserted++]->AsContent() : nullptr;
      46           0 :       return mChild;
      47             :     }
      48             : 
      49           0 :     MOZ_ASSERT(mChild->IsActiveChildrenElement());
      50             :     auto* childrenElement =
      51           0 :       static_cast<XBLChildrenElement*>(mChild);
      52           0 :     if (mIndexInInserted < childrenElement->InsertedChildrenLength()) {
      53           0 :       return childrenElement->InsertedChild(mIndexInInserted++);
      54             :     }
      55           0 :     mIndexInInserted = 0;
      56           0 :     mChild = mChild->GetNextSibling();
      57           0 :   } else if (mDefaultChild) {
      58             :     // If we're already in default content, check if there are more nodes there
      59           0 :     MOZ_ASSERT(mChild);
      60           0 :     MOZ_ASSERT(mChild->IsActiveChildrenElement());
      61             : 
      62           0 :     mDefaultChild = mDefaultChild->GetNextSibling();
      63           0 :     if (mDefaultChild) {
      64             :       return mDefaultChild;
      65             :     }
      66             : 
      67           0 :     mChild = mChild->GetNextSibling();
      68           0 :   } else if (mIsFirst) {  // at the beginning of the child list
      69             :     // For slot parent, iterate over assigned nodes if not empty, otherwise
      70             :     // fall through and iterate over direct children (fallback content).
      71           0 :     if (mParentAsSlot) {
      72             :       const nsTArray<RefPtr<nsINode>>& assignedNodes =
      73           0 :         mParentAsSlot->AssignedNodes();
      74           0 :       if (!assignedNodes.IsEmpty()) {
      75           0 :         mIndexInInserted = 1;
      76           0 :         mChild = assignedNodes[0]->AsContent();
      77           0 :         mIsFirst = false;
      78           0 :         return mChild;
      79             :       }
      80             :     }
      81             : 
      82           0 :     mChild = mParent->GetFirstChild();
      83           0 :     mIsFirst = false;
      84           0 :   } else if (mChild) { // in the middle of the child list
      85           0 :     mChild = mChild->GetNextSibling();
      86             :   }
      87             : 
      88             :   // Iterate until we find a non-insertion point, or an insertion point with
      89             :   // content.
      90           0 :   while (mChild) {
      91           0 :     if (mChild->IsActiveChildrenElement()) {
      92             :       // If the current child being iterated is a content insertion point
      93             :       // then the iterator needs to return the nodes distributed into
      94             :       // the content insertion point.
      95             :       auto* childrenElement =
      96           0 :         static_cast<XBLChildrenElement*>(mChild);
      97           0 :       if (childrenElement->HasInsertedChildren()) {
      98             :         // Iterate through elements projected on insertion point.
      99           0 :         mIndexInInserted = 1;
     100           0 :         return childrenElement->InsertedChild(0);
     101             :       }
     102             : 
     103             :       // Insertion points inside fallback/default content
     104             :       // are considered inactive and do not get assigned nodes.
     105           0 :       mDefaultChild = mChild->GetFirstChild();
     106           0 :       if (mDefaultChild) {
     107             :         return mDefaultChild;
     108             :       }
     109             : 
     110             :       // If we have an insertion point with no assigned nodes and
     111             :       // no default content, move on to the next node.
     112           0 :       mChild = mChild->GetNextSibling();
     113             :     } else {
     114             :       // mChild is not an insertion point, thus it is the next node to
     115             :       // return from this iterator.
     116             :       break;
     117             :     }
     118             :   }
     119             : 
     120           0 :   return mChild;
     121             : }
     122             : 
     123             : void
     124           0 : FlattenedChildIterator::Init(bool aIgnoreXBL)
     125             : {
     126           0 :   if (aIgnoreXBL) {
     127           0 :     mXBLInvolved = Some(false);
     128           0 :     return;
     129             :   }
     130             : 
     131             :   // TODO(emilio): I think it probably makes sense to only allow constructing
     132             :   // FlattenedChildIterators with Element.
     133           0 :   if (mParent->IsElement()) {
     134           0 :     if (ShadowRoot* shadow = mParent->AsElement()->GetShadowRoot()) {
     135           0 :       mParent = shadow;
     136           0 :       mXBLInvolved = Some(true);
     137           0 :       return;
     138             :     }
     139             :   }
     140             : 
     141             :   nsXBLBinding* binding =
     142           0 :     mParent->OwnerDoc()->BindingManager()->GetBindingWithContent(mParent);
     143             : 
     144           0 :   if (binding) {
     145           0 :     MOZ_ASSERT(binding->GetAnonymousContent());
     146           0 :     mParent = binding->GetAnonymousContent();
     147           0 :     mXBLInvolved = Some(true);
     148             :   }
     149             : }
     150             : 
     151             : bool
     152           0 : FlattenedChildIterator::ComputeWhetherXBLIsInvolved() const
     153             : {
     154           0 :   MOZ_ASSERT(mXBLInvolved.isNothing());
     155             :   // We set mXBLInvolved to true if either the node we're iterating has a
     156             :   // binding with content attached to it (in which case it is handled in Init),
     157             :   // the node is generated XBL content and has an <xbl:children> child, or the
     158             :   // node is a <slot> element.
     159         174 :   if (!mParent->GetBindingParent()) {
     160             :     return false;
     161             :   }
     162             : 
     163           0 :   if (mParentAsSlot) {
     164             :     return true;
     165             :   }
     166             : 
     167         140 :   for (nsIContent* child = mParent->GetFirstChild();
     168         140 :        child;
     169          62 :        child = child->GetNextSibling()) {
     170         216 :     if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     171          10 :       MOZ_ASSERT(child->GetBindingParent());
     172             :       return true;
     173             :     }
     174             :   }
     175             : 
     176             :   return false;
     177             : }
     178             : 
     179             : bool
     180          11 : ExplicitChildIterator::Seek(const nsIContent* aChildToFind)
     181             : {
     182           0 :   if (aChildToFind->GetParent() == mParent &&
     183           0 :       !aChildToFind->IsRootOfAnonymousSubtree()) {
     184             :     // Fast path: just point ourselves to aChildToFind, which is a
     185             :     // normal DOM child of ours.
     186           6 :     mChild = const_cast<nsIContent*>(aChildToFind);
     187           6 :     mIndexInInserted = 0;
     188           6 :     mDefaultChild = nullptr;
     189           6 :     mIsFirst = false;
     190           6 :     MOZ_ASSERT(!mChild->IsActiveChildrenElement());
     191             :     return true;
     192             :   }
     193             : 
     194             :   // Can we add more fast paths here based on whether the parent of aChildToFind
     195             :   // is a shadow insertion point or content insertion point?
     196             : 
     197             :   // Slow path: just walk all our kids.
     198           5 :   return Seek(aChildToFind, nullptr);
     199             : }
     200             : 
     201             : nsIContent*
     202          42 : ExplicitChildIterator::Get() const
     203             : {
     204           0 :   MOZ_ASSERT(!mIsFirst);
     205             : 
     206             :   // When mParentAsSlot is set, mChild is always set to the current child. It
     207             :   // does not matter whether mChild is an assigned node or a fallback content.
     208           0 :   if (mParentAsSlot) {
     209           0 :     return mChild;
     210             :   }
     211             : 
     212          42 :   if (mIndexInInserted) {
     213           0 :     MOZ_ASSERT(mChild->IsActiveChildrenElement());
     214           4 :     auto* childrenElement = static_cast<XBLChildrenElement*>(mChild);
     215           4 :     return childrenElement->InsertedChild(mIndexInInserted - 1);
     216             :   }
     217             : 
     218          38 :   return mDefaultChild ? mDefaultChild : mChild;
     219             : }
     220             : 
     221             : nsIContent*
     222           0 : ExplicitChildIterator::GetPreviousChild()
     223             : {
     224             :   // If we're already in the inserted-children array, look there first
     225          47 :   if (mIndexInInserted) {
     226             : 
     227           0 :     if (mParentAsSlot) {
     228             :       const nsTArray<RefPtr<nsINode>>& assignedNodes =
     229           0 :         mParentAsSlot->AssignedNodes();
     230             : 
     231           0 :       mChild = (--mIndexInInserted) ?
     232           0 :         assignedNodes[mIndexInInserted - 1]->AsContent() : nullptr;
     233             : 
     234           0 :       if (!mChild) {
     235           0 :         mIsFirst = true;
     236             :       }
     237             :       return mChild;
     238             :     }
     239             : 
     240             :     // NB: mIndexInInserted points one past the last returned child so we need
     241             :     // to look *two* indices back in order to return the previous child.
     242           0 :     MOZ_ASSERT(mChild->IsActiveChildrenElement());
     243           0 :     auto* childrenElement = static_cast<XBLChildrenElement*>(mChild);
     244           2 :     if (--mIndexInInserted) {
     245           0 :       return childrenElement->InsertedChild(mIndexInInserted - 1);
     246             :     }
     247           2 :     mChild = mChild->GetPreviousSibling();
     248          45 :   } else if (mDefaultChild) {
     249             :     // If we're already in default content, check if there are more nodes there
     250           0 :     mDefaultChild = mDefaultChild->GetPreviousSibling();
     251           0 :     if (mDefaultChild) {
     252             :       return mDefaultChild;
     253             :     }
     254             : 
     255           0 :     mChild = mChild->GetPreviousSibling();
     256          45 :   } else if (mIsFirst) { // at the beginning of the child list
     257             :     return nullptr;
     258           0 :   } else if (mChild) { // in the middle of the child list
     259          20 :     mChild = mChild->GetPreviousSibling();
     260             :   } else { // at the end of the child list
     261             :     // For slot parent, iterate over assigned nodes if not empty, otherwise
     262             :     // fall through and iterate over direct children (fallback content).
     263           0 :     if (mParentAsSlot) {
     264             :       const nsTArray<RefPtr<nsINode>>& assignedNodes =
     265           0 :         mParentAsSlot->AssignedNodes();
     266           0 :       if (!assignedNodes.IsEmpty()) {
     267           0 :         mIndexInInserted = assignedNodes.Length();
     268           0 :         mChild = assignedNodes[mIndexInInserted - 1]->AsContent();
     269           0 :         return mChild;
     270             :       }
     271             :     }
     272             : 
     273           0 :     mChild = mParent->GetLastChild();
     274             :   }
     275             : 
     276             :   // Iterate until we find a non-insertion point, or an insertion point with
     277             :   // content.
     278           0 :   while (mChild) {
     279           0 :     if (mChild->IsActiveChildrenElement()) {
     280             :       // If the current child being iterated is a content insertion point
     281             :       // then the iterator needs to return the nodes distributed into
     282             :       // the content insertion point.
     283           0 :       auto* childrenElement = static_cast<XBLChildrenElement*>(mChild);
     284           0 :       if (childrenElement->HasInsertedChildren()) {
     285           0 :         mIndexInInserted = childrenElement->InsertedChildrenLength();
     286           0 :         return childrenElement->InsertedChild(mIndexInInserted - 1);
     287             :       }
     288             : 
     289           0 :       mDefaultChild = mChild->GetLastChild();
     290           0 :       if (mDefaultChild) {
     291             :         return mDefaultChild;
     292             :       }
     293             : 
     294           0 :       mChild = mChild->GetPreviousSibling();
     295             :     } else {
     296             :       // mChild is not an insertion point, thus it is the next node to
     297             :       // return from this iterator.
     298             :       break;
     299             :     }
     300             :   }
     301             : 
     302          47 :   if (!mChild) {
     303           6 :     mIsFirst = true;
     304             :   }
     305             : 
     306             :   return mChild;
     307             : }
     308             : 
     309             : nsIContent*
     310           0 : AllChildrenIterator::Get() const
     311             : {
     312           0 :   switch (mPhase) {
     313             :     case eAtBeforeKid: {
     314           0 :       Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     315           0 :       MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
     316             :       return before;
     317             :     }
     318             : 
     319             :     case eAtExplicitKids:
     320           0 :       return ExplicitChildIterator::Get();
     321             : 
     322             :     case eAtAnonKids:
     323           0 :       return mAnonKids[mAnonKidsIdx];
     324             : 
     325             :     case eAtAfterKid: {
     326           0 :       Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     327           0 :       MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
     328             :       return after;
     329             :     }
     330             : 
     331             :     default:
     332             :       return nullptr;
     333             :   }
     334             : }
     335             : 
     336             : 
     337             : bool
     338           0 : AllChildrenIterator::Seek(const nsIContent* aChildToFind)
     339             : {
     340           0 :   if (mPhase == eAtBegin || mPhase == eAtBeforeKid) {
     341           0 :     mPhase = eAtExplicitKids;
     342           0 :     Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     343           0 :     if (beforePseudo && beforePseudo == aChildToFind) {
     344           0 :       mPhase = eAtBeforeKid;
     345           0 :       return true;
     346             :     }
     347             :   }
     348             : 
     349           0 :   if (mPhase == eAtExplicitKids) {
     350           0 :     if (ExplicitChildIterator::Seek(aChildToFind)) {
     351             :       return true;
     352             :     }
     353           0 :     mPhase = eAtAnonKids;
     354             :   }
     355             : 
     356             :   nsIContent* child = nullptr;
     357             :   do {
     358           0 :     child = GetNextChild();
     359           0 :   } while (child && child != aChildToFind);
     360             : 
     361             :   return child == aChildToFind;
     362             : }
     363             : 
     364             : void
     365      116096 : AllChildrenIterator::AppendNativeAnonymousChildren()
     366             : {
     367           0 :   nsContentUtils::AppendNativeAnonymousChildren(
     368      116096 :       mOriginalContent, mAnonKids, mFlags);
     369           0 : }
     370             : 
     371             : nsIContent*
     372           0 : AllChildrenIterator::GetNextChild()
     373             : {
     374           0 :   if (mPhase == eAtBegin) {
     375      116100 :     mPhase = eAtExplicitKids;
     376      116100 :     Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     377      116100 :     if (beforeContent) {
     378           0 :       mPhase = eAtBeforeKid;
     379          15 :       return beforeContent;
     380             :     }
     381             :   }
     382             : 
     383           0 :   if (mPhase == eAtBeforeKid) {
     384             :     // Advance into our explicit kids.
     385           0 :     mPhase = eAtExplicitKids;
     386             :   }
     387             : 
     388           0 :   if (mPhase == eAtExplicitKids) {
     389      245314 :     nsIContent* kid = ExplicitChildIterator::GetNextChild();
     390      245314 :     if (kid) {
     391             :       return kid;
     392             :     }
     393           0 :     mPhase = eAtAnonKids;
     394             :   }
     395             : 
     396      117344 :   if (mPhase == eAtAnonKids) {
     397      234306 :     if (mAnonKids.IsEmpty()) {
     398           0 :       MOZ_ASSERT(mAnonKidsIdx == UINT32_MAX);
     399           1 :       AppendNativeAnonymousChildren();
     400      116096 :       mAnonKidsIdx = 0;
     401             :     }
     402             :     else {
     403        1057 :       if (mAnonKidsIdx == UINT32_MAX) {
     404           0 :         mAnonKidsIdx = 0;
     405             :       }
     406             :       else {
     407           0 :         mAnonKidsIdx++;
     408             :       }
     409             :     }
     410             : 
     411           0 :     if (mAnonKidsIdx < mAnonKids.Length()) {
     412           0 :       return mAnonKids[mAnonKidsIdx];
     413             :     }
     414             : 
     415      116096 :     Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     416      116096 :     if (afterContent) {
     417           0 :       mPhase = eAtAfterKid;
     418           0 :       return afterContent;
     419             :     }
     420             :   }
     421             : 
     422           0 :   mPhase = eAtEnd;
     423      116096 :   return nullptr;
     424             : }
     425             : 
     426             : nsIContent*
     427           0 : AllChildrenIterator::GetPreviousChild()
     428             : {
     429           0 :   if (mPhase == eAtEnd) {
     430           0 :     MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length());
     431           0 :     mPhase = eAtAnonKids;
     432           0 :     Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
     433           0 :     if (afterContent) {
     434           0 :       mPhase = eAtAfterKid;
     435           0 :       return afterContent;
     436             :     }
     437             :   }
     438             : 
     439           0 :   if (mPhase == eAtAfterKid) {
     440           0 :     mPhase = eAtAnonKids;
     441             :   }
     442             : 
     443           0 :   if (mPhase == eAtAnonKids) {
     444           0 :     if (mAnonKids.IsEmpty()) {
     445           0 :       AppendNativeAnonymousChildren();
     446           0 :       mAnonKidsIdx = mAnonKids.Length();
     447             :     }
     448             : 
     449             :     // If 0 then it turns into UINT32_MAX, which indicates the iterator is
     450             :     // before the anonymous children.
     451           0 :     --mAnonKidsIdx;
     452           0 :     if (mAnonKidsIdx < mAnonKids.Length()) {
     453           0 :       return mAnonKids[mAnonKidsIdx];
     454             :     }
     455           0 :     mPhase = eAtExplicitKids;
     456             :   }
     457             : 
     458           0 :   if (mPhase == eAtExplicitKids) {
     459           0 :     nsIContent* kid = ExplicitChildIterator::GetPreviousChild();
     460           0 :     if (kid) {
     461             :       return kid;
     462             :     }
     463             : 
     464           0 :     Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
     465           0 :     if (beforeContent) {
     466           0 :       mPhase = eAtBeforeKid;
     467           0 :       return beforeContent;
     468             :     }
     469             :   }
     470             : 
     471           0 :   mPhase = eAtBegin;
     472           0 :   return nullptr;
     473             : }
     474             : 
     475             : nsIContent*
     476             : StyleChildrenIterator::GetNextChild()
     477             : {
     478             :   return AllChildrenIterator::GetNextChild();
     479             : }
     480             : 
     481             : } // namespace dom
     482             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952