void ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { MOZ_ASSERT(!mInStyleRefresh); MOZ_ASSERT_IF(mSnapshots.Get(aElement), mSnapshots.Get(aElement)->HasAttrs()); nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); if (primaryFrame) { primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType); } nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType); if (hint) { PostRestyleEvent(aElement, nsRestyleHint(0), hint); } if (aAttribute == nsGkAtoms::style) { PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0)); } // <td> is affected by the cellpadding on its ancestor table, // so we should restyle the whole subtree if (aAttribute == nsGkAtoms::cellpadding && aElement->IsHTMLElement(nsGkAtoms::table)) { PostRestyleEvent(aElement, eRestyle_Subtree, nsChangeHint(0)); } if (aElement->IsAttributeMapped(aAttribute)) { Servo_NoteExplicitHints(aElement, eRestyle_Self, nsChangeHint(0)); } }
void KeyframeEffectReadOnly::CalculateCumulativeChangeHint( nsStyleContext *aStyleContext) { mCumulativeChangeHint = nsChangeHint(0); for (const AnimationProperty& property : mProperties) { for (const AnimationPropertySegment& segment : property.mSegments) { RefPtr<nsStyleContext> fromContext = CreateStyleContextForAnimationValue(property.mProperty, segment.mFromValue, aStyleContext); RefPtr<nsStyleContext> toContext = CreateStyleContextForAnimationValue(property.mProperty, segment.mToValue, aStyleContext); uint32_t equalStructs = 0; uint32_t samePointerStructs = 0; nsChangeHint changeHint = fromContext->CalcStyleDifference(toContext, nsChangeHint(0), &equalStructs, &samePointerStructs); mCumulativeChangeHint |= changeHint; } } }
nsresult nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, PRBool aCompileEventHandlers) { static const char kMathMLStyleSheetURI[] = "resource://gre/res/mathml.css"; nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); if (aDocument && !aDocument->GetMathMLEnabled()) { // Enable MathML and setup the style sheet during binding, not element // construction, because we could move a MathML element from the document // that created it to another document. aDocument->SetMathMLEnabled(); aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI); // Rebuild style data for all the presshells, because style system // optimizations may have taken place assuming MathML was disabled. // (See nsRuleNode::CheckSpecifiedProperties.) // nsPresShellIterator skips hidden presshells, but that's OK because // if we're changing the document for one of those presshells the whole // presshell will be torn down. nsPresShellIterator iter(aDocument); nsCOMPtr<nsIPresShell> shell; while ((shell = iter.GetNextShell()) != nsnull) { shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0)); } } return rv; }
bool RestyleTracker::GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData) { NS_PRECONDITION(aElement->GetCrossShadowCurrentDoc() == Document(), "Unexpected document; this will lead to incorrect behavior!"); if (!aElement->HasFlag(RestyleBit())) { NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?"); return false; } mPendingRestyles.RemoveAndForget(aElement, aData); NS_ASSERTION(aData.get(), "Must have data if restyle bit is set"); if (aData->mRestyleHint & eRestyle_LaterSiblings) { // Someone readded the eRestyle_LaterSiblings hint for this // element. Leave it around for now, but remove the other restyle // hints and the change hint for it. Also unset its root bit, // since it's no longer a root with the new restyle data. NS_ASSERTION(aData->mDescendants.IsEmpty(), "expected descendants to be handled by now"); RestyleData* newData = new RestyleData; newData->mChangeHint = nsChangeHint(0); newData->mRestyleHint = eRestyle_LaterSiblings; mPendingRestyles.Put(aElement, newData); aElement->UnsetFlags(RootBit()); aData->mRestyleHint = nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings); } else { aElement->UnsetFlags(mRestyleBits); } return true; }
nsresult nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, PRBool aCompileEventHandlers) { static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css"; nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); if (aDocument && !aDocument->GetMathMLEnabled()) { // Enable MathML and setup the style sheet during binding, not element // construction, because we could move a MathML element from the document // that created it to another document. aDocument->SetMathMLEnabled(); aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI); // Rebuild style data for the presshell, because style system // optimizations may have taken place assuming MathML was disabled. // (See nsRuleNode::CheckSpecifiedProperties.) nsCOMPtr<nsIPresShell> shell = aDocument->GetShell(); if (shell) { shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0)); } } return rv; }
void ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs()); if (aAttribute == nsGkAtoms::style) { PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0)); } }
ServoElementSnapshot::ServoElementSnapshot(Element* aElement) : mContains(Flags(0)) , mState(0) , mExplicitRestyleHint(nsRestyleHint(0)) , mExplicitChangeHint(nsChangeHint(0)) { mIsHTMLElementInHTMLDocument = aElement->IsHTMLElement() && aElement->IsInHTMLDocument(); mIsInChromeDocument = nsContentUtils::IsChromeDoc(aElement->OwnerDoc()); }
void ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { #ifdef DEBUG ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement); MOZ_ASSERT_IF(snapshot, snapshot->HasAttrs()); #endif if (aAttribute == nsGkAtoms::style) { PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0)); } }
bool RestyleTracker::GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData) { NS_PRECONDITION(aElement->GetComposedDoc() == Document(), "Unexpected document; this will lead to incorrect behavior!"); if (!aElement->HasFlag(RestyleBit())) { NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?"); return false; } mPendingRestyles.RemoveAndForget(aElement, aData); NS_ASSERTION(aData.get(), "Must have data if restyle bit is set"); if (aData->mRestyleHint & eRestyle_LaterSiblings) { // Someone readded the eRestyle_LaterSiblings hint for this // element. Leave it around for now, but remove the other restyle // hints and the change hint for it. Also unset its root bit, // since it's no longer a root with the new restyle data. // During a normal restyle, we should have already processed the // mDescendants array the last time we processed the restyle // for this element. But in RebuildAllStyleData, we don't initially // expand out eRestyle_LaterSiblings, so we can get in here the // first time we need to process a restyle for this element. In that // case, it's fine for us to have a non-empty mDescendants, since // we know that RebuildAllStyleData adds eRestyle_ForceDescendants // and we're guaranteed we'll restyle the entire tree. NS_ASSERTION(mRestyleManager->InRebuildAllStyleData() || aData->mDescendants.IsEmpty(), "expected descendants to be handled by now"); RestyleData* newData = new RestyleData; newData->mChangeHint = nsChangeHint(0); newData->mRestyleHint = eRestyle_LaterSiblings; mPendingRestyles.Put(aElement, newData); aElement->UnsetFlags(RootBit()); aData->mRestyleHint = nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings); } else { aElement->UnsetFlags(mRestyleBits); } return true; }
bool RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData) { NS_PRECONDITION(aElement->GetCurrentDoc() == Document(), "Unexpected document; this will lead to incorrect behavior!"); if (!aElement->HasFlag(RestyleBit())) { NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?"); return false; } #ifdef DEBUG bool gotData = #endif mPendingRestyles.Get(aElement, aData); NS_ASSERTION(gotData, "Must have data if restyle bit is set"); if (aData->mRestyleHint & eRestyle_LaterSiblings) { // Someone readded the eRestyle_LaterSiblings hint for this // element. Leave it around for now, but remove the other restyle // hints and the change hint for it. Also unset its root bit, // since it's no longer a root with the new restyle data. RestyleData newData; newData.mChangeHint = nsChangeHint(0); newData.mRestyleHint = eRestyle_LaterSiblings; mPendingRestyles.Put(aElement, newData); aElement->UnsetFlags(RootBit()); aData->mRestyleHint = nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings); } else { mPendingRestyles.Remove(aElement); aElement->UnsetFlags(mRestyleBits); } return true; }
/* static */ void ServoRestyleManager::PostRestyleEventForAnimations(Element* aElement, nsRestyleHint aRestyleHint) { Servo_NoteExplicitHints(aElement, aRestyleHint, nsChangeHint(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(); }
static void DoApplyRenderingChangeToTree(nsIFrame* aFrame, nsChangeHint aChange) { NS_PRECONDITION(gInApplyRenderingChangeToTree, "should only be called within ApplyRenderingChangeToTree"); for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) { // Invalidate and sync views on all descendant frames, following placeholders. // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because // there can't be any out-of-flows or popups that need to be transformed; // all out-of-flow descendants of the transformed element must also be // descendants of the transformed frame. SyncViewsAndInvalidateDescendants(aFrame, nsChangeHint(aChange & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView | nsChangeHint_UpdateOpacityLayer | nsChangeHint_SchedulePaint))); // This must be set to true if the rendering change needs to // invalidate content. If it's false, a composite-only paint // (empty transaction) will be scheduled. bool needInvalidatingPaint = false; // if frame has view, will already be invalidated if (aChange & nsChangeHint_RepaintFrame) { // Note that this whole block will be skipped when painting is suppressed // (due to our caller ApplyRendingChangeToTree() discarding the // nsChangeHint_RepaintFrame hint). If you add handling for any other // hints within this block, be sure that they too should be ignored when // painting is suppressed. needInvalidatingPaint = true; aFrame->InvalidateFrameSubtree(); if ((aChange & nsChangeHint_UpdateEffects) && aFrame->IsFrameOfType(nsIFrame::eSVG) && !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) { // Need to update our overflow rects: nsSVGUtils::ScheduleReflowSVG(aFrame); } } if (aChange & nsChangeHint_UpdateTextPath) { if (aFrame->IsSVGText()) { // Invalidate and reflow the entire SVGTextFrame: NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath), "expected frame for a <textPath> element"); nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame); NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame"); static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange(); } else { MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath"); } } if (aChange & nsChangeHint_UpdateOpacityLayer) { // FIXME/bug 796697: we can get away with empty transactions for // opacity updates in many cases. needInvalidatingPaint = true; ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity); if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) { // SVG effects paints the opacity without using // nsDisplayOpacity. We need to invalidate manually. aFrame->InvalidateFrameSubtree(); } } if ((aChange & nsChangeHint_UpdateTransformLayer) && aFrame->IsTransformed()) { ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform); // If we're not already going to do an invalidating paint, see // if we can get away with only updating the transform on a // layer for this frame, and not scheduling an invalidating // paint. if (!needInvalidatingPaint) { Layer* layer; needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer); if (!needInvalidatingPaint) { // Since we're not going to paint, we need to resend animation // data to the layer. MOZ_ASSERT(layer, "this can't happen if there's no layer"); nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer( layer, nullptr, nullptr, aFrame, eCSSProperty_transform); } } } if (aChange & nsChangeHint_ChildrenOnlyTransform) { needInvalidatingPaint = true; nsIFrame* childFrame = GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild(); for ( ; childFrame; childFrame = childFrame->GetNextSibling()) { ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform); } } if (aChange & nsChangeHint_SchedulePaint) { needInvalidatingPaint = true; } aFrame->SchedulePaint(needInvalidatingPaint ? nsIFrame::PAINT_DEFAULT : nsIFrame::PAINT_COMPOSITE_ONLY); } }
/** * Calculates the change hint and the restyle hint for a given content state * change. * * This is called from both Restyle managers. */ void RestyleManagerBase::ContentStateChangedInternal(Element* aElement, EventStates aStateMask, nsChangeHint* aOutChangeHint, nsRestyleHint* aOutRestyleHint) { MOZ_ASSERT(aOutChangeHint); MOZ_ASSERT(aOutRestyleHint); StyleSetHandle styleSet = PresContext()->StyleSet(); NS_ASSERTION(styleSet, "couldn't get style set"); *aOutChangeHint = nsChangeHint(0); // Any change to a content state that affects which frames we construct // must lead to a frame reconstruct here if we already have a frame. // Note that we never decide through non-CSS means to not create frames // based on content states, so if we already don't have a frame we don't // need to force a reframe -- if it's needed, the HasStateDependentStyle // call will handle things. nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo; if (primaryFrame) { // If it's generated content, ignore LOADING/etc state changes on it. if (!primaryFrame->IsGeneratedContentFrame() && aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | NS_EVENT_STATE_SUPPRESSED | NS_EVENT_STATE_LOADING)) { *aOutChangeHint = nsChangeHint_ReconstructFrame; } else { uint8_t app = primaryFrame->StyleDisplay()->mAppearance; if (app) { nsITheme* theme = PresContext()->GetTheme(); if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) { bool repaint = false; theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint, nullptr); if (repaint) { *aOutChangeHint |= nsChangeHint_RepaintFrame; } } } } pseudoType = primaryFrame->StyleContext()->GetPseudoType(); primaryFrame->ContentStatesChanged(aStateMask); } if (pseudoType >= CSSPseudoElementType::Count) { *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask); } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState( pseudoType)) { // If aElement is a pseudo-element, we want to check to see whether there // are any state-dependent rules applying to that pseudo. Element* ancestor = ElementForStyleContext(nullptr, primaryFrame, pseudoType); *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType, aElement, aStateMask); } else { *aOutRestyleHint = nsRestyleHint(0); } if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) { IncrementHoverGeneration(); } if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) { // Exposing information to the page about whether the link is // visited or not isn't really something we can worry about here. // FIXME: We could probably do this a bit better. *aOutChangeHint |= nsChangeHint_RepaintFrame; } }
void ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeListToProcess) { MOZ_ASSERT(aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)); nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); if (!primaryFrame && !aContent->IsDirtyForServo()) { // This happens when, for example, a display: none child of a // HAS_DIRTY_DESCENDANTS content is reached as part of the traversal. return; } // Work on text before. if (!aContent->IsElement()) { if (primaryFrame) { RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); RefPtr<nsStyleContext> newContext = aStyleSet->ResolveStyleForText(aContent, aParentContext); for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } } aContent->UnsetIsDirtyForServo(); return; } Element* element = aContent->AsElement(); if (element->IsDirtyForServo()) { RefPtr<ServoComputedValues> computedValues = Servo_ComputedValues_Get(aContent).Consume(); MOZ_ASSERT(computedValues); nsChangeHint changeHint = nsChangeHint(0); // Add an explicit change hint if appropriate. ServoElementSnapshot* snapshot; if (mModifiedElements.Get(element, &snapshot)) { changeHint |= snapshot->ExplicitChangeHint(); } // Add the stored change hint if there's a frame. If there isn't a frame, // generate a ReconstructFrame change hint if the new display value // (which we can get from the ComputedValues stored on the node) is not // none. if (primaryFrame) { changeHint |= primaryFrame->StyleContext()->ConsumeStoredChangeHint(); } else { const nsStyleDisplay* currentDisplay = Servo_GetStyleDisplay(computedValues); if (currentDisplay->mDisplay != StyleDisplay::None) { changeHint |= nsChangeHint_ReconstructFrame; } } // Add the new change hint to the list of elements to process if // we need to do any work. if (changeHint) { aChangeListToProcess.AppendChange(primaryFrame, element, changeHint); } // The frame reconstruction step (if needed) will ask for the descendants' // style correctly. If not needed, we're done too. if (!primaryFrame) { aContent->UnsetIsDirtyForServo(); return; } // Hold the old style context alive, because it could become a dangling // pointer during the replacement. In practice it's not a huge deal (on // GetNextContinuationWithSameStyle the pointer is not dereferenced, only // compared), but better not playing with dangling pointers if not needed. RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); MOZ_ASSERT(oldStyleContext); RefPtr<nsStyleContext> newContext = aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr, CSSPseudoElementType::NotPseudo); // XXX This could not always work as expected: there are kinds of content // with the first split and the last sharing style, but others not. We // should handle those properly. for (nsIFrame* f = primaryFrame; f; f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { f->SetStyleContext(newContext); } // Update pseudo-elements state if appropriate. const static CSSPseudoElementType pseudosToRestyle[] = { CSSPseudoElementType::before, CSSPseudoElementType::after, }; for (CSSPseudoElementType pseudoType : pseudosToRestyle) { nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType); if (nsIFrame* pseudoFrame = FrameForPseudoElement(element, pseudoTag)) { // TODO: we could maybe make this more performant via calling into // Servo just once to know which pseudo-elements we've got to restyle? RefPtr<nsStyleContext> pseudoContext = aStyleSet->ProbePseudoElementStyle(element, pseudoType, newContext); // If pseudoContext is null here, it means the frame is going away, so // our change hint computation should have already indicated we need // to reframe. MOZ_ASSERT_IF(!pseudoContext, changeHint & nsChangeHint_ReconstructFrame); if (pseudoContext) { pseudoFrame->SetStyleContext(pseudoContext); // We only care restyling text nodes, since other type of nodes // (images), are still not supported. If that eventually changes, we // may have to write more code here... Or not, I don't think too // many inherited properties can affect those other frames. StyleChildrenIterator it(pseudoFrame->GetContent()); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsNodeOfType(nsINode::eTEXT)) { RefPtr<nsStyleContext> childContext = aStyleSet->ResolveStyleForText(n, pseudoContext); MOZ_ASSERT(n->GetPrimaryFrame(), "How? This node is created at FC time!"); n->GetPrimaryFrame()->SetStyleContext(childContext); } } } } } aContent->UnsetIsDirtyForServo(); } if (aContent->HasDirtyDescendantsForServo()) { MOZ_ASSERT(primaryFrame, "Frame construction should be scheduled, and it takes the " "correct style for the children, so no need to be here."); StyleChildrenIterator it(aContent); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsElement() || n->IsNodeOfType(nsINode::eTEXT)) { RecreateStyleContexts(n, primaryFrame->StyleContext(), aStyleSet, aChangeListToProcess); } } aContent->UnsetHasDirtyDescendantsForServo(); } }