Bug 1328509 - Wire up visited values in ServoStyleSet::GetContext. r=emilio
Create an extra style context using the visited values (if they exist). This
mirrors the logic Gecko performs in nsStyleSet::GetContext for visited support.
MozReview-Commit-ID: EiJQXDgz8tX
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -405,16 +405,19 @@ SERVO_BINDING_FUNC(Servo_ComputedValues_
ServoComputedValuesStrong,
ServoComputedValuesBorrowedOrNull parent_style_or_null,
nsIAtom* pseudo_tag, bool skip_display_fixup,
RawServoStyleSetBorrowed set)
SERVO_BINDING_FUNC(Servo_ComputedValues_Inherit, ServoComputedValuesStrong,
RawServoStyleSetBorrowed set,
ServoComputedValuesBorrowedOrNull parent_style,
mozilla::InheritTarget target)
+SERVO_BINDING_FUNC(Servo_ComputedValues_GetVisitedStyle,
+ ServoComputedValuesStrong,
+ ServoComputedValuesBorrowed values)
// Initialize Servo components. Should be called exactly once at startup.
SERVO_BINDING_FUNC(Servo_Initialize, void,
RawGeckoURLExtraData* dummy_url_data)
// Shut down Servo components. Should be called exactly once at shutdown.
SERVO_BINDING_FUNC(Servo_Shutdown, void)
// Gets the source style rules for the element. This returns the result via
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -211,20 +211,64 @@ ServoStyleSet::GetContext(nsIContent* aC
already_AddRefed<nsStyleContext>
ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues,
nsStyleContext* aParentContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType,
Element* aElementForAnimation)
{
- // XXXbholley: nsStyleSet does visited handling here.
+ bool isLink = false;
+ bool isVisitedLink = false;
+ // If we need visited styles for callers where `aElementForAnimation` is null,
+ // we can precompute these and pass them as flags, similar to nsStyleSet.cpp.
+ if (aElementForAnimation) {
+ isLink = nsCSSRuleProcessor::IsLink(aElementForAnimation);
+ isVisitedLink = nsCSSRuleProcessor::GetContentState(aElementForAnimation)
+ .HasState(NS_EVENT_STATE_VISITED);
+ }
+
+ RefPtr<ServoComputedValues> computedValues = Move(aComputedValues);
+ RefPtr<ServoComputedValues> visitedComputedValues =
+ Servo_ComputedValues_GetVisitedStyle(computedValues).Consume();
- RefPtr<nsStyleContext> result = NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
- aPseudoType, Move(aComputedValues));
+ // If `visitedComputedValues` is non-null, then there was a relevant link and
+ // visited styles were computed. This corresponds to the cases where Gecko's
+ // style system produces `aVisitedRuleNode`.
+ // Set up `parentIfVisited` depending on whether our parent context has a
+ // a visited style. If it doesn't but we do have visited styles, use the
+ // regular parent context for visited.
+ nsStyleContext *parentIfVisited =
+ aParentContext ? aParentContext->GetStyleIfVisited() : nullptr;
+ if (!parentIfVisited) {
+ if (visitedComputedValues) {
+ parentIfVisited = aParentContext;
+ }
+ }
+
+ // The true visited state of the relevant link is used to decided whether
+ // visited styles should be consulted for all visited dependent properties.
+ bool relevantLinkVisited = isLink ? isVisitedLink :
+ (aParentContext && aParentContext->RelevantLinkVisited());
+
+ RefPtr<nsStyleContext> result =
+ NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag, aPseudoType,
+ computedValues.forget());
+
+ if (visitedComputedValues) {
+ RefPtr<nsStyleContext> resultIfVisited =
+ NS_NewStyleContext(parentIfVisited, mPresContext, aPseudoTag, aPseudoType,
+ visitedComputedValues.forget());
+ resultIfVisited->SetIsStyleIfVisited();
+ result->SetStyleIfVisited(resultIfVisited.forget());
+
+ if (relevantLinkVisited) {
+ result->AddStyleBit(NS_STYLE_RELEVANT_LINK_VISITED);
+ }
+ }
// Set the body color on the pres context. See nsStyleSet::GetContext
if (aElementForAnimation &&
aElementForAnimation->IsHTMLElement(nsGkAtoms::body) &&
aPseudoType == CSSPseudoElementType::NotPseudo &&
mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
nsIDocument* doc = aElementForAnimation->GetUncomposedDoc();
if (doc && doc->GetBodyElement() == aElementForAnimation) {
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1229,35 +1229,54 @@ nsCSSRuleProcessor::GetWindowsThemeIdent
{
nsCSSRuleProcessor::InitSystemMetrics();
return sWinThemeId;
}
#endif
/* static */
EventStates
-nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
+nsCSSRuleProcessor::GetContentState(Element* aElement, bool aUsingPrivateBrowsing)
{
EventStates state = aElement->StyleState();
// If we are not supposed to mark visited links as such, be sure to
// flip the bits appropriately. We want to do this here, rather
// than in GetContentStateForVisitedHandling, so that we don't
// expose that :visited support is disabled to the Web page.
if (state.HasState(NS_EVENT_STATE_VISITED) &&
(!gSupportVisitedPseudo ||
aElement->OwnerDoc()->IsBeingUsedAsImage() ||
- aTreeMatchContext.mUsingPrivateBrowsing)) {
+ aUsingPrivateBrowsing)) {
state &= ~NS_EVENT_STATE_VISITED;
state |= NS_EVENT_STATE_UNVISITED;
}
return state;
}
/* static */
+EventStates
+nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
+{
+ return nsCSSRuleProcessor::GetContentState(
+ aElement,
+ aTreeMatchContext.mUsingPrivateBrowsing
+ );
+}
+
+/* static */
+EventStates
+nsCSSRuleProcessor::GetContentState(Element* aElement)
+{
+ nsILoadContext* loadContext = aElement->OwnerDoc()->GetLoadContext();
+ bool usingPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
+ return nsCSSRuleProcessor::GetContentState(aElement, usingPrivateBrowsing);
+}
+
+/* static */
bool
nsCSSRuleProcessor::IsLink(const Element* aElement)
{
EventStates state = aElement->StyleState();
return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
}
/* static */
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -99,17 +99,22 @@ public:
nsCSSSelectorList* aSelectorList);
/*
* Helper to get the content state for a content node. This may be
* slightly adjusted from IntrinsicState().
*/
static mozilla::EventStates GetContentState(
mozilla::dom::Element* aElement,
+ bool aUsingPrivateBrowsing);
+ static mozilla::EventStates GetContentState(
+ mozilla::dom::Element* aElement,
const TreeMatchContext& aTreeMatchContext);
+ static mozilla::EventStates GetContentState(
+ mozilla::dom::Element* aElement);
/*
* Helper to get the content state for :visited handling for an element
*/
static mozilla::EventStates GetContentStateForVisitedHandling(
mozilla::dom::Element* aElement,
nsRuleWalker::VisitedHandlingType aVisitedHandling,
bool aIsRelevantLink);
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1161,16 +1161,19 @@ nsStyleContext::CalcStyleDifferenceInter
// call GetVisitedDependentColor.
nsStyleContext *thisVis = GetStyleIfVisited(),
*otherVis = aNewContext->GetStyleIfVisited();
if (!thisVis != !otherVis) {
// One style context has a style-if-visited and the other doesn't.
// Presume a difference.
hint |= nsChangeHint_RepaintFrame;
} else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
+ // Bug 1364484: Update comments here and potentially remove the assertion
+ // below once we return a non-null visited context in CalcStyleDifference
+ // using Servo values. The approach is becoming quite similar to Gecko.
// We'll handle visited style differently in servo. Assert against being
// in the parallel traversal to avoid static analysis hazards when calling
// StyleFoo() below.
MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
// Both style contexts have a style-if-visited.
bool change = false;
@@ -1244,18 +1247,21 @@ public:
explicit FakeStyleContext(const ServoComputedValues* aComputedValues)
: mComputedValues(aComputedValues) {}
mozilla::NonOwningStyleContextSource StyleSource() const {
return mozilla::NonOwningStyleContextSource(mComputedValues);
}
nsStyleContext* GetStyleIfVisited() {
- // XXXbholley: This is wrong. Need to implement to get visited handling
- // corrrect!
+ // Bug 1364484: Figure out what to do here for Stylo visited values. We can
+ // get the visited computed values:
+ // RefPtr<ServoComputedValues> visitedComputedValues =
+ // Servo_ComputedValues_GetVisitedStyle(mComputedValues).Consume();
+ // But what's the best way to create the nsStyleContext?
return nullptr;
}
#define STYLE_STRUCT(name_, checkdata_cb_) \
const nsStyle##name_ * Style##name_() { \
return Servo_GetStyle##name_(mComputedValues); \
} \
const nsStyle##name_ * ThreadsafeStyle##name_() { \
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -269,37 +269,39 @@ public:
//
// Structs on this context should never be examined without also
// examining the corresponding struct on |this|. Doing so will likely
// both (1) lead to a privacy leak and (2) lead to dynamic change bugs
// related to the Peek code in nsStyleContext::CalcStyleDifference.
nsStyleContext* GetStyleIfVisited() const
{ return mStyleIfVisited; }
- // To be called only from nsStyleSet.
+ // To be called only from nsStyleSet / ServoStyleSet.
void SetStyleIfVisited(already_AddRefed<nsStyleContext> aStyleIfVisited)
{
MOZ_ASSERT(!IsStyleIfVisited(), "this context is not visited data");
NS_ASSERTION(!mStyleIfVisited, "should only be set once");
mStyleIfVisited = aStyleIfVisited;
MOZ_ASSERT(mStyleIfVisited->IsStyleIfVisited(),
"other context is visited data");
MOZ_ASSERT(!mStyleIfVisited->GetStyleIfVisited(),
"other context does not have visited data");
NS_ASSERTION(GetStyleIfVisited()->GetPseudo() == GetPseudo(),
"pseudo tag mismatch");
- if (GetParent() && GetParent()->GetStyleIfVisited()) {
- NS_ASSERTION(GetStyleIfVisited()->GetParent() ==
- GetParent()->GetStyleIfVisited() ||
- GetStyleIfVisited()->GetParent() == GetParent(),
+ if (GetParentAllowServo() && GetParentAllowServo()->GetStyleIfVisited()) {
+ NS_ASSERTION(GetStyleIfVisited()->GetParentAllowServo() ==
+ GetParentAllowServo()->GetStyleIfVisited() ||
+ GetStyleIfVisited()->GetParentAllowServo() ==
+ GetParentAllowServo(),
"parent mismatch");
} else {
- NS_ASSERTION(GetStyleIfVisited()->GetParent() == GetParent(),
+ NS_ASSERTION(GetStyleIfVisited()->GetParentAllowServo() ==
+ GetParentAllowServo(),
"parent mismatch");
}
}
// Does any descendant of this style context have any style values
// that were computed based on this style context's ancestors?
bool HasChildThatUsesGrandancestorStyle() const
{ return !!(mBits & NS_STYLE_CHILD_USES_GRANDANCESTOR_STYLE); }