void TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) { aStream << aPrefix; aStream << nsPrintfCString("%s (0x%p)", Name(), this).get(); // Note: the TextureHost needs to be locked before it is safe to call // GetSize() and GetFormat() on it. if (Lock()) { AppendToString(aStream, GetSize(), " [size=", "]"); AppendToString(aStream, GetFormat(), " [format=", "]"); Unlock(); } AppendToString(aStream, mFlags, " [flags=", "]"); #ifdef MOZ_DUMP_PAINTING if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) { nsAutoCString pfx(aPrefix); pfx += " "; aStream << "\n" << pfx.get() << "Surface: "; RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface(); if (dSurf) { aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get(); } } #endif }
void ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix) { aStream << aPrefix; aStream << nsPrintfCString("ContentClient (0x%p)", this).get(); if (profiler_feature_active("displaylistdump")) { nsAutoCString pfx(aPrefix); pfx += " "; Dump(aStream, pfx.get(), false); } }
void TiledContentHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) { aStream << aPrefix; aStream << nsPrintfCString("TiledContentHost (0x%p)", this).get(); if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) { nsAutoCString pfx(aPrefix); pfx += " "; Dump(aStream, pfx.get(), false); } }
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(); }
void LayerManagerComposite::Render() { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } /** Our more efficient but less powerful alter ego, if one is available. */ nsRefPtr<Composer2D> composer2D; composer2D = mCompositor->GetWidget()->GetComposer2D(); // We can't use composert2D if we have layer effects if (!mTarget && !haveLayerEffects && gfxPrefs::Composer2DCompositionEnabled() && composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot, mCompositor->GetWidget(), mGeometryChanged)) { LayerScope::SetHWComposed(); if (mFPS) { double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now()); if (gfxPrefs::LayersDrawFPS()) { printf_stderr("HWComposer: FPS is %g\n", fps); } } mCompositor->EndFrameForExternalComposition(Matrix()); // Reset the invalid region as compositing is done mInvalidRegion.SetEmpty(); mLastFrameMissedHWC = false; return; } else if (!mTarget && !haveLayerEffects) { mLastFrameMissedHWC = !!composer2D; } { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(this)) { return; } } nsIntRegion invalid; if (mTarget) { invalid = mTargetBounds; } else { invalid = mInvalidRegion; // Reset the invalid region now that we've begun compositing. mInvalidRegion.SetEmpty(); } ParentLayerIntRect clipRect; Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); Rect actualBounds; CompositorBench(mCompositor, bounds); if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(invalid, &rect, bounds, nullptr, &actualBounds); } else { gfx::Rect rect; mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(this); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay(this, IntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(ParentLayerIntRect::ToUntyped(clipRect)); if (!mRegionToClear.IsEmpty()) { nsIntRegionRectIterator iter(mRegionToClear); const IntRect *r; while ((r = iter.Next())) { mCompositor->ClearRect(Rect(r->x, r->y, r->width, r->height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, ParentLayerIntRect::ToUntyped(clipRect), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay(this, IntRect(actualBounds.x, actualBounds.y, actualBounds.width, actualBounds.height)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame() } if (composer2D) { composer2D->Render(mCompositor->GetWidget()); } mCompositor->GetWidget()->PostRender(this); RecordFrame(); }
void LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { NS_WARNING("Call on destroyed layer manager"); return; } ClearLayerFlags(mRoot); // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(/* aSorted= */true); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } mozilla::widget::WidgetRenderingContext widgetContext; #if defined(XP_MACOSX) widgetContext.mLayerManager = this; #elif defined(MOZ_WIDGET_ANDROID) widgetContext.mCompositor = GetCompositor(); #endif { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { return; } } ParentLayerIntRect clipRect; IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); IntRect actualBounds; CompositorBench(mCompositor, bounds); MOZ_ASSERT(mRoot->GetOpacity() == 1); #if defined(MOZ_WIDGET_ANDROID) LayerMetricsWrapper wrapper = GetRootContentLayer(); if (wrapper) { mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); } else { mCompositor->SetClearColorToDefault(); } #endif if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds); } else { gfx::IntRect rect; mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(&widgetContext); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(clipRect.ToUnknownRect()); if (!mRegionToClear.IsEmpty()) { for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { const IntRect& r = iter.Get(); mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); // Call after EndFrame() mCompositor->SetDispAcquireFence(mRoot); } mCompositor->GetWidget()->PostRender(&widgetContext); RecordFrame(); }
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(); }