diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index b05ad121f579..f0d7ba38a0d3 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -9631,8 +9631,8 @@ nsDocument::ForgetImagePreload(nsIURI* aURI) } } -EventStates -nsDocument::GetDocumentState() +void +nsDocument::UpdatePossiblyStaleDocumentState() { if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) { if (IsDocumentRightToLeft()) { @@ -9648,9 +9648,21 @@ nsDocument::GetDocumentState() } mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; } +} + +EventStates +nsDocument::ThreadSafeGetDocumentState() const +{ return mDocumentState; } +EventStates +nsDocument::GetDocumentState() +{ + UpdatePossiblyStaleDocumentState(); + return ThreadSafeGetDocumentState(); +} + namespace { /** diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 2dc245147f38..9ef1d404de77 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -980,7 +980,12 @@ public: virtual nsISupports* GetCurrentContentSink() override; - virtual mozilla::EventStates GetDocumentState() override; + virtual mozilla::EventStates GetDocumentState() final; + // GetDocumentState() mutates the state due to lazy resolution; + // and can't be used during parallel traversal. Use this instead, + // and ensure GetDocumentState() has been called first. + // This will assert if the state is stale. + virtual mozilla::EventStates ThreadSafeGetDocumentState() const final; // Only BlockOnload should call this! void AsyncBlockOnload(); @@ -1391,6 +1396,7 @@ protected: // caches its result here. mozilla::Maybe mIsThirdParty; private: + void UpdatePossiblyStaleDocumentState(); static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp); /** diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 04653474a288..add31264367e 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2381,6 +2381,7 @@ public: * nsIDocument.h. */ virtual mozilla::EventStates GetDocumentState() = 0; + virtual mozilla::EventStates ThreadSafeGetDocumentState() const = 0; virtual nsISupports* GetCurrentContentSink() = 0; diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp index c000b1547922..6cf695d671cd 100644 --- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -486,6 +486,19 @@ nscolor Gecko_GetLookAndFeelSystemColor(int32_t aId, return result; } +bool +Gecko_MatchStringArgPseudo(RawGeckoElementBorrowed aElement, + CSSPseudoClassType aType, + const char16_t* aIdent, + bool* aSetSlowSelectorFlag) +{ + MOZ_ASSERT(aElement->OwnerDoc() == aElement->GetComposedDoc()); + EventStates dummyMask; // mask is never read because we pass aDependence=nullptr + return nsCSSRuleProcessor::StringPseudoMatches(aElement, aType, aIdent, + aElement->OwnerDoc(), true, + dummyMask, aSetSlowSelectorFlag, nullptr); +} + template static nsIAtom* AtomAttrValue(Implementor* aElement, nsIAtom* aName) diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 4ebbf373f41c..2ccad0cb372f 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -377,6 +377,11 @@ const nsMediaFeature* Gecko_GetMediaFeatures(); nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id, RawGeckoPresContextBorrowed pres_context); +bool Gecko_MatchStringArgPseudo(RawGeckoElementBorrowed element, + mozilla::CSSPseudoClassType type, + const char16_t* ident, + bool* set_slow_selector); + // Style-struct management. #define STYLE_STRUCT(name, checkdata_cb) \ void Gecko_Construct_Default_nsStyle##name( \ diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index fb1de404afe9..454b673b37a8 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -148,7 +148,7 @@ ServoStyleSet::GetContext(nsIContent* aContent, Element* element = aContent->AsElement(); - ResolveMappedAttrDeclarationBlocks(); + PreTraverseSync(); RefPtr computedValues; if (aMayCompute == LazyComputeBehavior::Allow) { computedValues = ResolveStyleLazily(element, nullptr); @@ -190,10 +190,20 @@ ServoStyleSet::ResolveMappedAttrDeclarationBlocks() } void -ServoStyleSet::PreTraverse() +ServoStyleSet::PreTraverseSync() { ResolveMappedAttrDeclarationBlocks(); + // This is lazily computed and pseudo matching needs to access + // it so force computation early. + mPresContext->Document()->GetDocumentState(); +} + +void +ServoStyleSet::PreTraverse() +{ + PreTraverseSync(); + // Process animation stuff that we should avoid doing during the parallel // traversal. mPresContext->EffectCompositor()->PreTraverse(); diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index ad438720b877..49481f916ab9 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -286,6 +286,8 @@ private: * Perform processes that we should do before traversing. */ void PreTraverse(); + // Subset of the pre-traverse steps that involve syncing up data + void PreTraverseSync(); already_AddRefed ResolveStyleLazily(dom::Element* aElement, nsIAtom* aPseudoTag); diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 9f1d5a19d463..254070a03662 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1638,20 +1638,29 @@ StateSelectorMatches(Element* aElement, } /* static */ bool -nsCSSRuleProcessor::StringPseudoMatches(mozilla::dom::Element* aElement, +nsCSSRuleProcessor::StringPseudoMatches(const mozilla::dom::Element* aElement, CSSPseudoClassType aPseudo, - char16_t* aString, - nsIDocument* aDocument, + const char16_t* aString, + const nsIDocument* aDocument, bool aForStyling, EventStates aStateMask, + bool* aSetSlowSelectorFlag, bool* const aDependence) { + MOZ_ASSERT(aSetSlowSelectorFlag); + switch (aPseudo) { case CSSPseudoClassType::mozLocaleDir: { - bool docIsRTL = - aDocument->GetDocumentState(). - HasState(NS_DOCUMENT_STATE_RTL_LOCALE); + bool docIsRTL; + if (aIsGecko) { + auto doc = const_cast(aDocument); + docIsRTL = doc->GetDocumentState() + .HasState(NS_DOCUMENT_STATE_RTL_LOCALE); + } else { + docIsRTL = aDocument->ThreadSafeGetDocumentState() + .HasState(NS_DOCUMENT_STATE_RTL_LOCALE); + } nsDependentString dirString(aString); @@ -1691,7 +1700,7 @@ nsCSSRuleProcessor::StringPseudoMatches(mozilla::dom::Element* aElement, // :-moz-empty-except-children-with-localname() ~ E // because we don't know to restyle the grandparent of the // inserted/removed element (as in bug 534804 for :empty). - aElement->SetFlags(NODE_HAS_SLOW_SELECTOR); + *aSetSlowSelectorFlag = true; } do { child = aElement->GetChildAt(++index); @@ -2141,13 +2150,18 @@ static bool SelectorMatches(Element* aElement, default: { MOZ_ASSERT(nsCSSPseudoClasses::HasStringArg(pseudoClass->mType)); + bool setSlowSelectorFlag = false; bool matched = nsCSSRuleProcessor::StringPseudoMatches(aElement, pseudoClass->mType, pseudoClass->u.mString, aTreeMatchContext.mDocument, aTreeMatchContext.mForStyling, aNodeMatchContext.mStateMask, + &setSlowSelectorFlag, aDependence); + if (setSlowSelectorFlag) { + aElement->SetFlags(NODE_HAS_SLOW_SELECTOR); + } if (!matched) { return false; diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index c729c3780dba..452985e8d161 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -141,9 +141,8 @@ public: * parses but does not match. Asserts if it fails to parse; only * call this when you're sure it's a string-like pseudo. * - * This will assert if the document has a stale document state, - * ensure that UpdatePossiblyStaleDocumentState() has been called - * first. + * In Servo mode, please ensure that UpdatePossiblyStaleDocumentState() + * has been called first. * * @param aElement The element we are trying to match * @param aPseudo The name of the pseudoselector @@ -156,12 +155,13 @@ public: * @param aDependence Pointer to be set to true if we ignored a state due to * aStateMask. Can be null. */ - static bool StringPseudoMatches(mozilla::dom::Element* aElement, + static bool StringPseudoMatches(const mozilla::dom::Element* aElement, mozilla::CSSPseudoClassType aPseudo, - char16_t* aString, - nsIDocument* aDocument, + const char16_t* aString, + const nsIDocument* aDocument, bool aForStyling, mozilla::EventStates aStateMask, + bool* aSetSlowSelectorFlag, bool* const aDependence = nullptr); // nsIStyleRuleProcessor