示例#1
0
void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
{
  // Note that aPayload may be allocated by the caller, so we need to make sure
  // that we free it at some point.
  mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);

  if (!stack_key_initialized)
    return;

  // Don't insert a marker if we're not profiling to avoid
  // the heap copy (malloc).
  if (!profiler_is_active()) {
    return;
  }

  // Don't add a marker if we don't want to include personal information
  if (profiler_in_privacy_mode()) {
    return;
  }

  PseudoStack *stack = tlsPseudoStack.get();
  if (!stack) {
    return;
  }

  mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
                     aPayload->GetStartTime() : mozilla::TimeStamp::Now();
  mozilla::TimeDuration delta = origin - sStartTime;
  stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds());
}
示例#2
0
void
mozilla_sampler_log(const char *fmt, va_list args)
{
  if (profiler_is_active()) {
    // nsAutoCString AppendPrintf would be nicer but
    // this is mozilla external code
    char buf[2048];
    va_list argsCpy;
    VARARGS_ASSIGN(argsCpy, args);
    int required = vsnprintf(buf, sizeof(buf), fmt, argsCpy);
    va_end(argsCpy);

    if (required < 0) {
      return; // silently drop for now
    } else if (required < 2048) {
      profiler_tracing("log", buf, TRACING_EVENT);
    } else {
      char* heapBuf = new char[required+1];
      va_list argsCpy;
      VARARGS_ASSIGN(argsCpy, args);
      vsnprintf(heapBuf, required+1, fmt, argsCpy);
      va_end(argsCpy);
      // EVENT_BACKTRACE could be used to get a source
      // for all log events. This could be a runtime
      // flag later.
      profiler_tracing("log", heapBuf, TRACING_EVENT);
      delete[] heapBuf;
    }
  }
}
void
ThreadStackHelper::GetStack(Stack& aStack)
{
  // Always run PrepareStackBuffer first to clear aStack
  if (!PrepareStackBuffer(aStack)) {
    // Skip and return empty aStack
    return;
  }

#if defined(XP_LINUX)
  if (profiler_is_active()) {
    // Profiler can interfere with our Linux signal handling
    return;
  }
  if (!sInitialized) {
    MOZ_ASSERT(false);
    return;
  }
  sCurrent = this;
  struct sigaction sigact = {};
  sigact.sa_sigaction = SigAction;
  sigemptyset(&sigact.sa_mask);
  sigact.sa_flags = SA_SIGINFO | SA_RESTART;
  if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
    MOZ_ASSERT(false);
    return;
  }
  MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF));
  MOZ_ALWAYS_TRUE(!::sem_wait(&sSem));

#elif defined(XP_WIN)
  if (!mInitialized) {
    MOZ_ASSERT(false);
    return;
  }
  if (::SuspendThread(mThreadID) == DWORD(-1)) {
    MOZ_ASSERT(false);
    return;
  }
  FillStackBuffer();
  MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));

#elif defined(XP_MACOSX)
  if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
    MOZ_ASSERT(false);
    return;
  }
  FillStackBuffer();
  MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);

#endif
  aStack = Move(mStackBuffer);
}
void
CompositorVsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp)
{
  // In vsync thread
#ifdef MOZ_ENABLE_PROFILER_SPS
    if (profiler_is_active()) {
        layers::CompositorParent::PostInsertVsyncProfilerMarker(aVsyncTimestamp);
    }
#endif

  MutexAutoLock lock(mCompositorObserverLock);
  if (mCompositorVsyncObserver) {
    mCompositorVsyncObserver->NotifyVsync(aVsyncTimestamp);
  }
}
示例#5
0
bool mozilla_sampler_register_thread(const char* aName, void* stackTop)
{
#if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING)
  // The only way to profile secondary threads on b2g
  // is to build with profiling OR have the profiler
  // running on startup.
  if (!profiler_is_active()) {
    return false;
  }
#endif

  PseudoStack* stack = new PseudoStack();
  tlsPseudoStack.set(stack);
  bool isMainThread = is_main_thread_name(aName);
  return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
}
示例#6
0
void mozilla_sampler_get_gatherer(nsISupports** aRetVal)
{
  if (!aRetVal) {
    return;
  }

  if (NS_WARN_IF(!profiler_is_active())) {
    *aRetVal = nullptr;
    return;
  }

  GeckoSampler *t = tlsTicker.get();
  if (NS_WARN_IF(!t)) {
    *aRetVal = nullptr;
    return;
  }

  t->GetGatherer(aRetVal);
}
void
GeckoTouchDispatcher::DispatchTouchEvent(MultiTouchInput aMultiTouch)
{
  if ((aMultiTouch.mType == MultiTouchInput::MULTITOUCH_END ||
       aMultiTouch.mType == MultiTouchInput::MULTITOUCH_CANCEL) &&
      aMultiTouch.mTouches.Length() == 1) {
    MutexAutoLock lock(mTouchQueueLock);
    mTouchMoveEvents.clear();
  } else if (aMultiTouch.mType == MultiTouchInput::MULTITOUCH_START &&
             aMultiTouch.mTouches.Length() == 1) {
    mTouchEventsFiltered = IsExpired(aMultiTouch);
  }

  if (mTouchEventsFiltered) {
    return;
  }

  nsWindow::DispatchTouchInput(aMultiTouch);

  if (mEnabledUniformityInfo && profiler_is_active()) {
    const char* touchAction = "Invalid";
    switch (aMultiTouch.mType) {
      case MultiTouchInput::MULTITOUCH_START:
        touchAction = "Touch_Event_Down";
        break;
      case MultiTouchInput::MULTITOUCH_MOVE:
        touchAction = "Touch_Event_Move";
        break;
      case MultiTouchInput::MULTITOUCH_END:
      case MultiTouchInput::MULTITOUCH_CANCEL:
        touchAction = "Touch_Event_Up";
        break;
      case MultiTouchInput::MULTITOUCH_SENTINEL:
        MOZ_ASSERT_UNREACHABLE("Invalid MultTouchInput.");
        break;
    }

    const ScreenIntPoint& touchPoint = aMultiTouch.mTouches[0].mScreenPoint;
    TouchDataPayload* payload = new TouchDataPayload(touchPoint);
    PROFILER_MARKER_PAYLOAD(touchAction, payload);
  }
}
示例#8
0
ProfilerBacktrace* mozilla_sampler_get_backtrace()
{
  if (!stack_key_initialized)
    return nullptr;

  // Don't capture a stack if we're not profiling
  if (!profiler_is_active()) {
    return nullptr;
  }

  // Don't capture a stack if we don't want to include personal information
  if (profiler_in_privacy_mode()) {
    return nullptr;
  }

  GeckoSampler* t = tlsTicker.get();
  if (!t) {
    return nullptr;
  }

  return new ProfilerBacktrace(t->GetBacktrace());
}
示例#9
0
bool mozilla_sampler_register_thread(const char* aName, void* aGuessStackTop)
{
  if (sInitCount == 0) {
    return false;
  }

#if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING)
  // The only way to profile secondary threads on b2g
  // is to build with profiling OR have the profiler
  // running on startup.
  if (!profiler_is_active()) {
    return false;
  }
#endif

  MOZ_ASSERT(tlsPseudoStack.get() == nullptr);
  PseudoStack* stack = PseudoStack::create();
  tlsPseudoStack.set(stack);
  bool isMainThread = is_main_thread_name(aName);
  void* stackTop = GetStackTop(aGuessStackTop);
  return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
}
示例#10
0
bool mozilla_sampler_feature_active(const char* aName)
{
  if (!profiler_is_active()) {
    return false;
  }

  if (strcmp(aName, "gpu") == 0) {
    return sIsGPUProfiling;
  }

  if (strcmp(aName, "layersdump") == 0) {
    return sIsLayersDump;
  }

  if (strcmp(aName, "displaylistdump") == 0) {
    return sIsDisplayListDump;
  }

  if (strcmp(aName, "restyle") == 0) {
    return sIsRestyleProfiling;
  }

  return false;
}
示例#11
0
void
RestyleTracker::DoProcessRestyles()
{
  nsAutoCString docURL;
  if (profiler_is_active()) {
    nsIURI *uri = Document()->GetDocumentURI();
    if (uri) {
      uri->GetSpec(docURL);
    } else {
      docURL = "N/A";
    }
  }
  PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
                        js::ProfileEntry::Category::CSS, "(%s)", docURL.get());

  nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
  bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);

  // Create a AnimationsWithDestroyedFrame during restyling process to
  // stop animations and transitions on elements that have no frame at the end
  // of the restyling process.
  RestyleManager::AnimationsWithDestroyedFrame
    animationsWithDestroyedFrame(mRestyleManager);

  // Create a ReframingStyleContexts struct on the stack and put it in our
  // mReframingStyleContexts for almost all of the remaining scope of
  // this function.
  //
  // It needs to be *in* scope during BeginProcessingRestyles, which
  // might (if mDoRebuildAllStyleData is true) do substantial amounts of
  // restyle processing.
  //
  // However, it needs to be *out* of scope during
  // EndProcessingRestyles, since we should release the style contexts
  // it holds prior to any EndReconstruct call that
  // EndProcessingRestyles makes.  This is because in EndReconstruct we
  // try to destroy the old rule tree using the GC mechanism, which
  // means it only gets destroyed if it's unreferenced (and if it's
  // referenced, we assert).  So we want the ReframingStyleContexts
  // (which holds old style contexts) to be destroyed before the
  // EndReconstruct so those style contexts go away before
  // EndReconstruct.
  {
    RestyleManager::ReframingStyleContexts
      reframingStyleContexts(mRestyleManager);

    mRestyleManager->BeginProcessingRestyles(*this);

    LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s",
                mPendingRestyles.Count(),
                mRestyleManager->PresContext()->TransitionManager()->
                  InAnimationOnlyStyleUpdate()
                  ? (const char*) "animation " : (const char*) "",
                static_cast<int>(mRestyleRoots.Length()),
                GetDocumentURI(Document()).get());
    LOG_RESTYLE_INDENT();

    // loop so that we process any restyle events generated by processing
    while (mPendingRestyles.Count()) {
      if (mHaveLaterSiblingRestyles) {
        // Convert them to individual restyles on all the later siblings
        AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
        for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
          auto element = static_cast<dom::Element*>(iter.Key());
          MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here");
          // Only collect the entries that actually need restyling by us (and
          // haven't, for example, already been restyled).
          // It's important to not mess with the flags on entries not in our
          // document.
          if (element->GetComposedDoc() == Document() &&
              element->HasFlag(RestyleBit()) &&
              (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) {
            laterSiblingArr.AppendElement(element);
          }
        }
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          MOZ_ASSERT(!element->IsStyledByServo());
          for (nsIContent* sibling = element->GetNextSibling();
               sibling;
               sibling = sibling->GetNextSibling()) {
            if (sibling->IsElement()) {
              LOG_RESTYLE("adding pending restyle for %s due to "
                          "eRestyle_LaterSiblings hint on %s",
                          FrameTagToString(sibling->AsElement()).get(),
                          FrameTagToString(element->AsElement()).get());
              if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
                                    nsChangeHint(0))) {
                  // Nothing else to do here; we'll handle the following
                  // siblings when we get to |sibling| in laterSiblingArr.
                break;
              }
            }
          }
        }

        // Now remove all those eRestyle_LaterSiblings bits
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
          RestyleData* data;
#ifdef DEBUG
          bool found =
#endif
            mPendingRestyles.Get(element, &data);
          NS_ASSERTION(found, "Where did our entry go?");
          data->mRestyleHint =
            nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings);
        }

        LOG_RESTYLE("%d pending restyles after expanding out "
                    "eRestyle_LaterSiblings", mPendingRestyles.Count());

        mHaveLaterSiblingRestyles = false;
      }

      uint32_t rootCount;
      while ((rootCount = mRestyleRoots.Length())) {
        // Make sure to pop the element off our restyle root array, so
        // that we can freely append to the array as we process this
        // element.
        RefPtr<Element> element;
        element.swap(mRestyleRoots[rootCount - 1]);
        mRestyleRoots.RemoveElementAt(rootCount - 1);

        LOG_RESTYLE("processing style root %s at index %d",
                    FrameTagToString(element).get(), rootCount - 1);
        LOG_RESTYLE_INDENT();

        // Do the document check before calling GetRestyleData, since we
        // don't want to do the sibling-processing GetRestyleData does if
        // the node is no longer relevant.
        if (element->GetComposedDoc() != Document()) {
          // Content node has been removed from our document; nothing else
          // to do here
          LOG_RESTYLE("skipping, no longer in the document");
          continue;
        }

        nsAutoPtr<RestyleData> data;
        if (!GetRestyleData(element, data)) {
          LOG_RESTYLE("skipping, already restyled");
          continue;
        }

        if (isTimelineRecording) {
          timelines->AddMarkerForDocShell(docShell, Move(
            MakeUnique<RestyleTimelineMarker>(
              data->mRestyleHint, MarkerTracingType::START)));
        }

#if defined(MOZ_ENABLE_PROFILER_SPS)
        Maybe<GeckoProfilerTracingRAII> profilerRAII;
        if (profiler_feature_active("restyle")) {
          profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
        }
#endif
        ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
                          data->mRestyleHintData);
        AddRestyleRootsIfAwaitingRestyle(data->mDescendants);

        if (isTimelineRecording) {
          timelines->AddMarkerForDocShell(docShell, Move(
            MakeUnique<RestyleTimelineMarker>(
              data->mRestyleHint, MarkerTracingType::END)));
        }
      }

      if (mHaveLaterSiblingRestyles) {
        // Keep processing restyles for now
        continue;
      }

      // Now we only have entries with change hints left.  To be safe in
      // case of reentry from the handing of the change hint, use a
      // scratch array instead of calling out to ProcessOneRestyle while
      // enumerating the hashtable.  Use the stack if we can, otherwise
      // fall back on heap-allocation.
      AutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
      RestyleEnumerateData* restylesToProcess =
        restyleArr.AppendElements(mPendingRestyles.Count());
      if (restylesToProcess) {
        RestyleEnumerateData* restyle = restylesToProcess;
#ifdef RESTYLE_LOGGING
        uint32_t count = 0;
#endif
        for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
          auto element = static_cast<dom::Element*>(iter.Key());
          RestyleTracker::RestyleData* data = iter.Data();

          // Only collect the entries that actually need restyling by us (and
          // haven't, for example, already been restyled).
          // It's important to not mess with the flags on entries not in our
          // document.
          if (element->GetComposedDoc() != Document() ||
              !element->HasFlag(RestyleBit())) {
            LOG_RESTYLE("skipping pending restyle %s, already restyled or no "
                        "longer in the document",
                        FrameTagToString(element).get());
            continue;
          }

          NS_ASSERTION(
            !element->HasFlag(RootBit()) ||
            // Maybe we're just not reachable via the frame tree?
            (element->GetFlattenedTreeParent() &&
             (!element->GetFlattenedTreeParent()->GetPrimaryFrame() ||
              element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() ||
              element->GetComposedDoc()->GetShell()->FrameManager()
                ->GetDisplayContentsStyleFor(element))) ||
            // Or not reachable due to an async reinsert we have
            // pending?  If so, we'll have a reframe hint around.
            // That incidentally makes it safe that we still have
            // the bit, since any descendants that didn't get added
            // to the roots list because we had the bits will be
            // completely restyled in a moment.
            (data->mChangeHint & nsChangeHint_ReconstructFrame),
            "Why did this not get handled while processing mRestyleRoots?");

          // Unset the restyle bits now, so if they get readded later as we
          // process we won't clobber that adding of the bit.
          element->UnsetFlags(RestyleBit() |
                              RootBit() |
                              ConditionalDescendantsBit());

          restyle->mElement = element;
          restyle->mRestyleHint = data->mRestyleHint;
          restyle->mChangeHint = data->mChangeHint;
          // We can move data since we'll be clearing mPendingRestyles after
          // we finish enumerating it.
          restyle->mRestyleHintData = Move(data->mRestyleHintData);
#if defined(MOZ_ENABLE_PROFILER_SPS)
          restyle->mBacktrace = Move(data->mBacktrace);
#endif

#ifdef RESTYLE_LOGGING
          count++;
#endif

          // Increment to the next slot in the array
          restyle++;
        }

        RestyleEnumerateData* lastRestyle = restyle;

        // Clear the hashtable now that we don't need it anymore
        mPendingRestyles.Clear();

#ifdef RESTYLE_LOGGING
        uint32_t index = 0;
#endif
        for (RestyleEnumerateData* currentRestyle = restylesToProcess;
             currentRestyle != lastRestyle;
             ++currentRestyle) {
          LOG_RESTYLE("processing pending restyle %s at index %d/%d",
                      FrameTagToString(currentRestyle->mElement).get(),
                      index++, count);
          LOG_RESTYLE_INDENT();

#if defined(MOZ_ENABLE_PROFILER_SPS)
          Maybe<GeckoProfilerTracingRAII> profilerRAII;
          if (profiler_feature_active("restyle")) {
            profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
          }
#endif
          if (isTimelineRecording) {
            timelines->AddMarkerForDocShell(docShell, Move(
              MakeUnique<RestyleTimelineMarker>(
                currentRestyle->mRestyleHint, MarkerTracingType::START)));
          }

          ProcessOneRestyle(currentRestyle->mElement,
                            currentRestyle->mRestyleHint,
                            currentRestyle->mChangeHint,
                            currentRestyle->mRestyleHintData);

          if (isTimelineRecording) {
            timelines->AddMarkerForDocShell(docShell, Move(
              MakeUnique<RestyleTimelineMarker>(
                currentRestyle->mRestyleHint, MarkerTracingType::END)));
          }
        }
      }
    }
  }

  // mPendingRestyles is now empty.
  mHaveSelectors = false;

  mRestyleManager->EndProcessingRestyles();
}
示例#12
0
 static double GetProfilerTime() {
   if (!profiler_is_active()) {
     return 0.0;
   }
   return profiler_time();
 };
示例#13
0
void
RestyleTracker::DoProcessRestyles()
{
  nsAutoCString docURL;
  if (profiler_is_active()) {
    nsIURI *uri = Document()->GetDocumentURI();
    if (uri) {
      uri->GetSpec(docURL);
    } else {
      docURL = "N/A";
    }
  }
  PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
                        js::ProfileEntry::Category::CSS, "(%s)", docURL.get());

  bool isTimelineRecording = false;
  nsDocShell* docShell =
    static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
  if (docShell) {
    docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording);
  }

  // Create a ReframingStyleContexts struct on the stack and put it in our
  // mReframingStyleContexts for almost all of the remaining scope of
  // this function.
  //
  // It needs to be *in* scope during BeginProcessingRestyles, which
  // might (if mDoRebuildAllStyleData is true) do substantial amounts of
  // restyle processing.
  //
  // However, it needs to be *out* of scope during
  // EndProcessingRestyles, since we should release the style contexts
  // it holds prior to any EndReconstruct call that
  // EndProcessingRestyles makes.  This is because in EndReconstruct we
  // try to destroy the old rule tree using the GC mechanism, which
  // means it only gets destroyed if it's unreferenced (and if it's
  // referenced, we assert).  So we want the ReframingStyleContexts
  // (which holds old style contexts) to be destroyed before the
  // EndReconstruct so those style contexts go away before
  // EndReconstruct.
  {
    RestyleManager::ReframingStyleContexts
      reframingStyleContexts(mRestyleManager);

    mRestyleManager->BeginProcessingRestyles(*this);

    LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s",
                mPendingRestyles.Count(),
                mRestyleManager->PresContext()->TransitionManager()->
                  InAnimationOnlyStyleUpdate()
                  ? (const char*) "animation " : (const char*) "",
                static_cast<int>(mRestyleRoots.Length()),
                GetDocumentURI(Document()).get());
    LOG_RESTYLE_INDENT();

    // loop so that we process any restyle events generated by processing
    while (mPendingRestyles.Count()) {
      if (mHaveLaterSiblingRestyles) {
        // Convert them to individual restyles on all the later siblings
        nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
        LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
        mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          for (nsIContent* sibling = element->GetNextSibling();
               sibling;
               sibling = sibling->GetNextSibling()) {
            if (sibling->IsElement()) {
              LOG_RESTYLE("adding pending restyle for %s due to "
                          "eRestyle_LaterSiblings hint on %s",
                          FrameTagToString(sibling->AsElement()).get(),
                          FrameTagToString(element->AsElement()).get());
              if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
                                    NS_STYLE_HINT_NONE)) {
                  // Nothing else to do here; we'll handle the following
                  // siblings when we get to |sibling| in laterSiblingArr.
                break;
              }
            }
          }
        }

        // Now remove all those eRestyle_LaterSiblings bits
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
          RestyleData* data;
#ifdef DEBUG
          bool found =
#endif
            mPendingRestyles.Get(element, &data);
          NS_ASSERTION(found, "Where did our entry go?");
          data->mRestyleHint =
            nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings);
        }

        LOG_RESTYLE("%d pending restyles after expanding out "
                    "eRestyle_LaterSiblings", mPendingRestyles.Count());

        mHaveLaterSiblingRestyles = false;
      }

      uint32_t rootCount;
      while ((rootCount = mRestyleRoots.Length())) {
        // Make sure to pop the element off our restyle root array, so
        // that we can freely append to the array as we process this
        // element.
        nsRefPtr<Element> element;
        element.swap(mRestyleRoots[rootCount - 1]);
        mRestyleRoots.RemoveElementAt(rootCount - 1);

        LOG_RESTYLE("processing style root %s at index %d",
                    FrameTagToString(element).get(), rootCount - 1);
        LOG_RESTYLE_INDENT();

        // Do the document check before calling GetRestyleData, since we
        // don't want to do the sibling-processing GetRestyleData does if
        // the node is no longer relevant.
        if (element->GetCrossShadowCurrentDoc() != Document()) {
          // Content node has been removed from our document; nothing else
          // to do here
          LOG_RESTYLE("skipping, no longer in the document");
          continue;
        }

        nsAutoPtr<RestyleData> data;
        if (!GetRestyleData(element, data)) {
          LOG_RESTYLE("skipping, already restyled");
          continue;
        }

        if (isTimelineRecording) {
          mozilla::UniquePtr<TimelineMarker> marker =
            MakeUnique<RestyleTimelineMarker>(docShell,
                                              TRACING_INTERVAL_START,
                                              data->mRestyleHint);
          docShell->AddProfileTimelineMarker(Move(marker));
        }

#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
        Maybe<GeckoProfilerTracingRAII> profilerRAII;
        if (profiler_feature_active("restyle")) {
          profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
        }
#endif
        ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint);
        AddRestyleRootsIfAwaitingRestyle(data->mDescendants);

        if (isTimelineRecording) {
          mozilla::UniquePtr<TimelineMarker> marker =
            MakeUnique<RestyleTimelineMarker>(docShell,
                                              TRACING_INTERVAL_END,
                                              data->mRestyleHint);
          docShell->AddProfileTimelineMarker(Move(marker));
        }
      }

      if (mHaveLaterSiblingRestyles) {
        // Keep processing restyles for now
        continue;
      }

      // Now we only have entries with change hints left.  To be safe in
      // case of reentry from the handing of the change hint, use a
      // scratch array instead of calling out to ProcessOneRestyle while
      // enumerating the hashtable.  Use the stack if we can, otherwise
      // fall back on heap-allocation.
      nsAutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
      RestyleEnumerateData* restylesToProcess =
        restyleArr.AppendElements(mPendingRestyles.Count());
      if (restylesToProcess) {
        RestyleEnumerateData* lastRestyle = restylesToProcess;
        RestyleCollector collector = { this, &lastRestyle };
        mPendingRestyles.Enumerate(CollectRestyles, &collector);

        // Clear the hashtable now that we don't need it anymore
        mPendingRestyles.Clear();

#ifdef RESTYLE_LOGGING
        uint32_t index = 0;
#endif
        for (RestyleEnumerateData* currentRestyle = restylesToProcess;
             currentRestyle != lastRestyle;
             ++currentRestyle) {
          LOG_RESTYLE("processing pending restyle %s at index %d/%d",
                      FrameTagToString(currentRestyle->mElement).get(),
                      index++, collector.count);
          LOG_RESTYLE_INDENT();

#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
          Maybe<GeckoProfilerTracingRAII> profilerRAII;
          if (profiler_feature_active("restyle")) {
            profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
          }
#endif
          if (isTimelineRecording) {
            mozilla::UniquePtr<TimelineMarker> marker =
              MakeUnique<RestyleTimelineMarker>(docShell,
                                                TRACING_INTERVAL_START,
                                                currentRestyle->mRestyleHint);
            docShell->AddProfileTimelineMarker(Move(marker));
          }

          ProcessOneRestyle(currentRestyle->mElement,
                            currentRestyle->mRestyleHint,
                            currentRestyle->mChangeHint);

          if (isTimelineRecording) {
            mozilla::UniquePtr<TimelineMarker> marker =
              MakeUnique<RestyleTimelineMarker>(docShell,
                                                TRACING_INTERVAL_END,
                                                currentRestyle->mRestyleHint);
            docShell->AddProfileTimelineMarker(Move(marker));
          }
        }
      }
    }
  }

  mRestyleManager->EndProcessingRestyles();
}