LCOV - code coverage report
Current view: top level - layout/base - RestyleTracker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 134 180 74.4 %
Date: 2017-07-14 16:53:18 Functions: 8 10 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * A class which manages pending restyles.  This handles keeping track
       8             :  * of what nodes restyles need to happen on and so forth.
       9             :  */
      10             : 
      11             : #include "RestyleTracker.h"
      12             : 
      13             : #include "GeckoProfiler.h"
      14             : #include "nsFrameManager.h"
      15             : #include "nsIDocument.h"
      16             : #include "nsStyleChangeList.h"
      17             : #include "mozilla/GeckoRestyleManager.h"
      18             : #include "RestyleTrackerInlines.h"
      19             : #include "nsTransitionManager.h"
      20             : #include "mozilla/AutoRestyleTimelineMarker.h"
      21             : 
      22             : namespace mozilla {
      23             : 
      24             : #ifdef RESTYLE_LOGGING
      25             : static nsCString
      26           0 : GetDocumentURI(nsIDocument* aDocument)
      27             : {
      28           0 :   nsCString result;
      29           0 :   nsAutoString url;
      30           0 :   nsresult rv = aDocument->GetDocumentURI(url);
      31           0 :   if (NS_SUCCEEDED(rv)) {
      32           0 :     result.Append(NS_ConvertUTF16toUTF8(url).get());
      33             :   }
      34             : 
      35           0 :   return result;
      36             : }
      37             : 
      38             : static nsCString
      39           0 : FrameTagToString(dom::Element* aElement)
      40             : {
      41           0 :   nsCString result;
      42           0 :   nsIFrame* frame = aElement->GetPrimaryFrame();
      43           0 :   if (frame) {
      44           0 :     nsFrame::ListTag(result, frame);
      45             :   } else {
      46           0 :     nsAutoString buf;
      47           0 :     aElement->NodeInfo()->NameAtom()->ToString(buf);
      48           0 :     result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
      49             :   }
      50           0 :   return result;
      51             : }
      52             : #endif
      53             : 
      54             : inline nsIDocument*
      55        2129 : RestyleTracker::Document() const {
      56        2129 :   return mRestyleManager->PresContext()->Document();
      57             : }
      58             : 
      59             : #define RESTYLE_ARRAY_STACKSIZE 128
      60             : 
      61          38 : struct RestyleEnumerateData : RestyleTracker::Hints {
      62             :   RefPtr<dom::Element> mElement;
      63             :   UniqueProfilerBacktrace mBacktrace;
      64             : };
      65             : 
      66             : inline void
      67         120 : RestyleTracker::ProcessOneRestyle(Element* aElement,
      68             :                                   nsRestyleHint aRestyleHint,
      69             :                                   nsChangeHint aChangeHint,
      70             :                                   const RestyleHintData& aRestyleHintData)
      71             : {
      72         120 :   NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
      73             :                   "Someone should have handled this before calling us");
      74         120 :   NS_PRECONDITION(Document(), "Must have a document");
      75         120 :   NS_PRECONDITION(aElement->GetComposedDoc() == Document(),
      76             :                   "Element has unexpected document");
      77             : 
      78         120 :   LOG_RESTYLE("aRestyleHint = %s, aChangeHint = %s",
      79             :               GeckoRestyleManager::RestyleHintToString(aRestyleHint).get(),
      80             :               GeckoRestyleManager::ChangeHintToString(aChangeHint).get());
      81             : 
      82         120 :   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
      83             : 
      84         120 :   if (aRestyleHint & ~eRestyle_LaterSiblings) {
      85             : #ifdef RESTYLE_LOGGING
      86         109 :     if (ShouldLogRestyle() && primaryFrame &&
      87           0 :         GeckoRestyleManager::StructsToLog() != 0) {
      88           0 :       LOG_RESTYLE("style context tree before restyle:");
      89           0 :       LOG_RESTYLE_INDENT();
      90           0 :       primaryFrame->StyleContext()->AsGecko()->LogStyleContextTree(
      91           0 :           LoggingDepth(), GeckoRestyleManager::StructsToLog());
      92             :     }
      93             : #endif
      94         109 :     mRestyleManager->RestyleElement(aElement, primaryFrame, aChangeHint,
      95         109 :                                     *this, aRestyleHint, aRestyleHintData);
      96          22 :   } else if (aChangeHint &&
      97           1 :              (primaryFrame ||
      98           1 :               (aChangeHint & nsChangeHint_ReconstructFrame))) {
      99             :     // Don't need to recompute style; just apply the hint
     100          22 :     nsStyleChangeList changeList(StyleBackendType::Gecko);
     101          11 :     changeList.AppendChange(primaryFrame, aElement, aChangeHint);
     102          11 :     mRestyleManager->ProcessRestyledFrames(changeList);
     103             :   }
     104         120 : }
     105             : 
     106             : void
     107          25 : RestyleTracker::DoProcessRestyles()
     108             : {
     109          50 :   nsAutoCString docURL("N/A");
     110          25 :   if (profiler_is_active()) {
     111           0 :     nsIURI *uri = Document()->GetDocumentURI();
     112           0 :     if (uri) {
     113           0 :       docURL = uri->GetSpecOrDefault();
     114             :     }
     115             :   }
     116          50 :   AUTO_PROFILER_LABEL_DYNAMIC("RestyleTracker::DoProcessRestyles", CSS,
     117             :                               docURL.get());
     118             : 
     119             :   // Create a AnimationsWithDestroyedFrame during restyling process to
     120             :   // stop animations and transitions on elements that have no frame at the end
     121             :   // of the restyling process.
     122             :   RestyleManager::AnimationsWithDestroyedFrame
     123          50 :     animationsWithDestroyedFrame(mRestyleManager);
     124             : 
     125             :   // Create a ReframingStyleContexts struct on the stack and put it in our
     126             :   // mReframingStyleContexts for almost all of the remaining scope of
     127             :   // this function.
     128             :   //
     129             :   // It needs to be *in* scope during BeginProcessingRestyles, which
     130             :   // might (if mDoRebuildAllStyleData is true) do substantial amounts of
     131             :   // restyle processing.
     132             :   //
     133             :   // However, it needs to be *out* of scope during
     134             :   // EndProcessingRestyles, since we should release the style contexts
     135             :   // it holds prior to any EndReconstruct call that
     136             :   // EndProcessingRestyles makes.  This is because in EndReconstruct we
     137             :   // try to destroy the old rule tree using the GC mechanism, which
     138             :   // means it only gets destroyed if it's unreferenced (and if it's
     139             :   // referenced, we assert).  So we want the ReframingStyleContexts
     140             :   // (which holds old style contexts) to be destroyed before the
     141             :   // EndReconstruct so those style contexts go away before
     142             :   // EndReconstruct.
     143             :   {
     144             :     GeckoRestyleManager::ReframingStyleContexts
     145          50 :       reframingStyleContexts(mRestyleManager);
     146             : 
     147          25 :     mRestyleManager->BeginProcessingRestyles(*this);
     148             : 
     149          25 :     LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s",
     150             :                 mPendingRestyles.Count(),
     151             :                 mRestyleManager->PresContext()->TransitionManager()->
     152             :                   InAnimationOnlyStyleUpdate()
     153             :                   ? (const char*) "animation " : (const char*) "",
     154             :                 static_cast<int>(mRestyleRoots.Length()),
     155             :                 GetDocumentURI(Document()).get());
     156          50 :     LOG_RESTYLE_INDENT();
     157             : 
     158             :     // loop so that we process any restyle events generated by processing
     159          73 :     while (mPendingRestyles.Count()) {
     160          24 :       if (mHaveLaterSiblingRestyles) {
     161             :         // Convert them to individual restyles on all the later siblings
     162           6 :         AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
     163          36 :         for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
     164          33 :           auto element = static_cast<dom::Element*>(iter.Key());
     165          33 :           MOZ_ASSERT(!element->IsStyledByServo(),
     166             :                      "Should not have Servo-styled elements here");
     167             :           // Only collect the entries that actually need restyling by us (and
     168             :           // haven't, for example, already been restyled).
     169             :           // It's important to not mess with the flags on entries not in our
     170             :           // document.
     171          99 :           if (element->GetComposedDoc() == Document() &&
     172          66 :               element->HasFlag(RestyleBit()) &&
     173          33 :               (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) {
     174           6 :             laterSiblingArr.AppendElement(element);
     175             :           }
     176             :         }
     177           9 :         for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
     178           6 :           Element* element = laterSiblingArr[i];
     179           6 :           MOZ_ASSERT(!element->IsStyledByServo());
     180          21 :           for (nsIContent* sibling = element->GetNextSibling();
     181          21 :                sibling;
     182          15 :                sibling = sibling->GetNextSibling()) {
     183          15 :             if (sibling->IsElement()) {
     184          15 :               LOG_RESTYLE("adding pending restyle for %s due to "
     185             :                           "eRestyle_LaterSiblings hint on %s",
     186             :                           FrameTagToString(sibling->AsElement()).get(),
     187             :                           FrameTagToString(element->AsElement()).get());
     188          15 :               if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
     189             :                                     nsChangeHint(0))) {
     190             :                   // Nothing else to do here; we'll handle the following
     191             :                   // siblings when we get to |sibling| in laterSiblingArr.
     192           0 :                 break;
     193             :               }
     194             :             }
     195             :           }
     196             :         }
     197             : 
     198             :         // Now remove all those eRestyle_LaterSiblings bits
     199           9 :         for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
     200           6 :           Element* element = laterSiblingArr[i];
     201           6 :           NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
     202             :           RestyleData* data;
     203             : #ifdef DEBUG
     204             :           bool found =
     205             : #endif
     206           6 :             mPendingRestyles.Get(element, &data);
     207           6 :           NS_ASSERTION(found, "Where did our entry go?");
     208          12 :           data->mRestyleHint =
     209           6 :             nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings);
     210             :         }
     211             : 
     212           3 :         LOG_RESTYLE("%d pending restyles after expanding out "
     213             :                     "eRestyle_LaterSiblings", mPendingRestyles.Count());
     214             : 
     215           3 :         mHaveLaterSiblingRestyles = false;
     216             :       }
     217             : 
     218             :       uint32_t rootCount;
     219         240 :       while ((rootCount = mRestyleRoots.Length())) {
     220             :         // Make sure to pop the element off our restyle root array, so
     221             :         // that we can freely append to the array as we process this
     222             :         // element.
     223         214 :         RefPtr<Element> element;
     224         108 :         element.swap(mRestyleRoots[rootCount - 1]);
     225         108 :         mRestyleRoots.RemoveElementAt(rootCount - 1);
     226             : 
     227         108 :         LOG_RESTYLE("processing style root %s at index %d",
     228             :                     FrameTagToString(element).get(), rootCount - 1);
     229         214 :         LOG_RESTYLE_INDENT();
     230             : 
     231             :         // Do the document check before calling GetRestyleData, since we
     232             :         // don't want to do the sibling-processing GetRestyleData does if
     233             :         // the node is no longer relevant.
     234         108 :         if (element->GetComposedDoc() != Document()) {
     235             :           // Content node has been removed from our document; nothing else
     236             :           // to do here
     237           0 :           LOG_RESTYLE("skipping, no longer in the document");
     238           0 :           continue;
     239             :         }
     240             : 
     241         214 :         nsAutoPtr<RestyleData> data;
     242         108 :         if (!GetRestyleData(element, data)) {
     243           2 :           LOG_RESTYLE("skipping, already restyled");
     244           2 :           continue;
     245             :         }
     246             : 
     247             :         {
     248             :           AutoRestyleTimelineMarker marker(
     249         106 :             mRestyleManager->PresContext()->GetDocShell(),
     250         318 :             data->mRestyleHint & eRestyle_AllHintsWithAnimations);
     251         212 :           Maybe<AutoProfilerTracing> tracing;
     252         106 :           if (profiler_feature_active(ProfilerFeature::Restyle)) {
     253           0 :             tracing.emplace("Paint", "Styles", Move(data->mBacktrace));
     254             :           }
     255         106 :           ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
     256         212 :                             data->mRestyleHintData);
     257         106 :           AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
     258             :         }
     259             :       }
     260             : 
     261          24 :       if (mHaveLaterSiblingRestyles) {
     262             :         // Keep processing restyles for now
     263           0 :         continue;
     264             :       }
     265             : 
     266             :       // Now we only have entries with change hints left.  To be safe in
     267             :       // case of reentry from the handing of the change hint, use a
     268             :       // scratch array instead of calling out to ProcessOneRestyle while
     269             :       // enumerating the hashtable.  Use the stack if we can, otherwise
     270             :       // fall back on heap-allocation.
     271          48 :       AutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
     272             :       RestyleEnumerateData* restylesToProcess =
     273          24 :         restyleArr.AppendElements(mPendingRestyles.Count());
     274          24 :       if (restylesToProcess) {
     275          24 :         RestyleEnumerateData* restyle = restylesToProcess;
     276             : #ifdef RESTYLE_LOGGING
     277          24 :         uint32_t count = 0;
     278             : #endif
     279          43 :         for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
     280          19 :           auto element = static_cast<dom::Element*>(iter.Key());
     281          19 :           RestyleTracker::RestyleData* data = iter.Data();
     282             : 
     283             :           // Only collect the entries that actually need restyling by us (and
     284             :           // haven't, for example, already been restyled).
     285             :           // It's important to not mess with the flags on entries not in our
     286             :           // document.
     287          38 :           if (element->GetComposedDoc() != Document() ||
     288          19 :               !element->HasFlag(RestyleBit())) {
     289           5 :             LOG_RESTYLE("skipping pending restyle %s, already restyled or no "
     290             :                         "longer in the document",
     291             :                         FrameTagToString(element).get());
     292           5 :             continue;
     293             :           }
     294             : 
     295          14 :           NS_ASSERTION(
     296             :             !element->HasFlag(RootBit()) ||
     297             :             // Maybe we're just not reachable via the frame tree?
     298             :             (element->GetFlattenedTreeParent() &&
     299             :              (!element->GetFlattenedTreeParent()->GetPrimaryFrame() ||
     300             :               element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() ||
     301             :               element->GetComposedDoc()->GetShell()->FrameManager()
     302             :                 ->GetDisplayContentsStyleFor(element))) ||
     303             :             // Or not reachable due to an async reinsert we have
     304             :             // pending?  If so, we'll have a reframe hint around.
     305             :             // That incidentally makes it safe that we still have
     306             :             // the bit, since any descendants that didn't get added
     307             :             // to the roots list because we had the bits will be
     308             :             // completely restyled in a moment.
     309             :             (data->mChangeHint & nsChangeHint_ReconstructFrame),
     310             :             "Why did this not get handled while processing mRestyleRoots?");
     311             : 
     312             :           // Unset the restyle bits now, so if they get readded later as we
     313             :           // process we won't clobber that adding of the bit.
     314          42 :           element->UnsetFlags(RestyleBit() |
     315          14 :                               RootBit() |
     316          28 :                               ConditionalDescendantsBit());
     317             : 
     318          14 :           restyle->mElement = element;
     319          14 :           restyle->mRestyleHint = data->mRestyleHint;
     320          14 :           restyle->mChangeHint = data->mChangeHint;
     321             :           // We can move data since we'll be clearing mPendingRestyles after
     322             :           // we finish enumerating it.
     323          14 :           restyle->mRestyleHintData = Move(data->mRestyleHintData);
     324          14 :           restyle->mBacktrace = Move(data->mBacktrace);
     325             : 
     326             : #ifdef RESTYLE_LOGGING
     327          14 :           count++;
     328             : #endif
     329             : 
     330             :           // Increment to the next slot in the array
     331          14 :           restyle++;
     332             :         }
     333             : 
     334          24 :         RestyleEnumerateData* lastRestyle = restyle;
     335             : 
     336             :         // Clear the hashtable now that we don't need it anymore
     337          24 :         mPendingRestyles.Clear();
     338             : 
     339             : #ifdef RESTYLE_LOGGING
     340          24 :         uint32_t index = 0;
     341             : #endif
     342          38 :         for (RestyleEnumerateData* currentRestyle = restylesToProcess;
     343          38 :              currentRestyle != lastRestyle;
     344             :              ++currentRestyle) {
     345          14 :           LOG_RESTYLE("processing pending restyle %s at index %d/%d",
     346             :                       FrameTagToString(currentRestyle->mElement).get(),
     347             :                       index++, count);
     348          28 :           LOG_RESTYLE_INDENT();
     349             : 
     350          28 :           Maybe<AutoProfilerTracing> tracing;
     351          14 :           if (profiler_feature_active(ProfilerFeature::Restyle)) {
     352           0 :             tracing.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
     353             :           }
     354             : 
     355             :           {
     356             :             AutoRestyleTimelineMarker marker(
     357          14 :               mRestyleManager->PresContext()->GetDocShell(),
     358          42 :               currentRestyle->mRestyleHint & eRestyle_AllHintsWithAnimations);
     359          14 :             ProcessOneRestyle(currentRestyle->mElement,
     360             :                               currentRestyle->mRestyleHint,
     361             :                               currentRestyle->mChangeHint,
     362          14 :                               currentRestyle->mRestyleHintData);
     363             :           }
     364             :         }
     365             :       }
     366             :     }
     367             :   }
     368             : 
     369             :   // mPendingRestyles is now empty.
     370          25 :   mHaveSelectors = false;
     371             : 
     372          25 :   mRestyleManager->EndProcessingRestyles();
     373          25 : }
     374             : 
     375             : bool
     376        1729 : RestyleTracker::GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData)
     377             : {
     378        1729 :   NS_PRECONDITION(aElement->GetComposedDoc() == Document(),
     379             :                   "Unexpected document; this will lead to incorrect behavior!");
     380             : 
     381        1729 :   if (!aElement->HasFlag(RestyleBit())) {
     382        1587 :     NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
     383        1587 :     return false;
     384             :   }
     385             : 
     386         142 :   mPendingRestyles.Remove(aElement, &aData);
     387         142 :   NS_ASSERTION(aData.get(), "Must have data if restyle bit is set");
     388             : 
     389         142 :   if (aData->mRestyleHint & eRestyle_LaterSiblings) {
     390             :     // Someone readded the eRestyle_LaterSiblings hint for this
     391             :     // element.  Leave it around for now, but remove the other restyle
     392             :     // hints and the change hint for it.  Also unset its root bit,
     393             :     // since it's no longer a root with the new restyle data.
     394             : 
     395             :     // During a normal restyle, we should have already processed the
     396             :     // mDescendants array the last time we processed the restyle
     397             :     // for this element.  But in RebuildAllStyleData, we don't initially
     398             :     // expand out eRestyle_LaterSiblings, so we can get in here the
     399             :     // first time we need to process a restyle for this element.  In that
     400             :     // case, it's fine for us to have a non-empty mDescendants, since
     401             :     // we know that RebuildAllStyleData adds eRestyle_ForceDescendants
     402             :     // and we're guaranteed we'll restyle the entire tree.
     403           0 :     NS_ASSERTION(mRestyleManager->InRebuildAllStyleData() ||
     404             :                  aData->mDescendants.IsEmpty(),
     405             :                  "expected descendants to be handled by now");
     406             : 
     407           0 :     RestyleData* newData = new RestyleData;
     408           0 :     newData->mChangeHint = nsChangeHint(0);
     409           0 :     newData->mRestyleHint = eRestyle_LaterSiblings;
     410           0 :     mPendingRestyles.Put(aElement, newData);
     411           0 :     aElement->UnsetFlags(RootBit());
     412           0 :     aData->mRestyleHint =
     413           0 :       nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
     414             :   } else {
     415         142 :     aElement->UnsetFlags(mRestyleBits);
     416             :   }
     417             : 
     418         142 :   return true;
     419             : }
     420             : 
     421             : void
     422        1308 : RestyleTracker::AddRestyleRootsIfAwaitingRestyle(
     423             :                                    const nsTArray<RefPtr<Element>>& aElements)
     424             : {
     425             :   // The RestyleData for a given element has stored in mDescendants
     426             :   // the list of descendants we need to end up restyling.  Since we
     427             :   // won't necessarily end up restyling them, due to the restyle
     428             :   // process finishing early (see how RestyleResult::eStop is handled
     429             :   // in ElementRestyler::Restyle), we add them to the list of restyle
     430             :   // roots to handle the next time around the
     431             :   // RestyleTracker::DoProcessRestyles loop.
     432             :   //
     433             :   // Note that aElements must maintain the same invariant
     434             :   // that mRestyleRoots does, i.e. that ancestors appear after descendants.
     435             :   // Since we call AddRestyleRootsIfAwaitingRestyle only after we have
     436             :   // removed the restyle root we are currently processing from the end of
     437             :   // mRestyleRoots, and the only elements we get here in aElements are
     438             :   // descendants of that restyle root, we are safe to simply append to the
     439             :   // end of mRestyleRoots to maintain its invariant.
     440        1344 :   for (size_t i = 0; i < aElements.Length(); i++) {
     441          36 :     Element* element = aElements[i];
     442          36 :     if (element->HasFlag(RestyleBit())) {
     443          24 :       mRestyleRoots.AppendElement(element);
     444             :     }
     445             :   }
     446        1308 : }
     447             : 
     448             : void
     449          48 : RestyleTracker::ClearSelectors()
     450             : {
     451          48 :   if (!mHaveSelectors) {
     452          48 :     return;
     453             :   }
     454           0 :   for (auto it = mPendingRestyles.Iter(); !it.Done(); it.Next()) {
     455           0 :     RestyleData* data = it.Data();
     456           0 :     if (data->mRestyleHint & eRestyle_SomeDescendants) {
     457           0 :       data->mRestyleHint =
     458           0 :         (data->mRestyleHint & ~eRestyle_SomeDescendants) | eRestyle_Subtree;
     459           0 :       data->mRestyleHintData.mSelectorsForDescendants.Clear();
     460             :     } else {
     461           0 :       MOZ_ASSERT(data->mRestyleHintData.mSelectorsForDescendants.IsEmpty());
     462             :     }
     463             :   }
     464           0 :   mHaveSelectors = false;
     465             : }
     466             : 
     467             : } // namespace mozilla

Generated by: LCOV version 1.13