Bug 1366144: Consider a display: none or empty-content ::before and ::after as not matching directly. r?heycam
I believe this was the approach Bobby wanted to take in
bug 1352565.
Fine for me to do it this way, though I suspect I need to special-case
Servo_ResolvePseudoStyle to account for this too.
What we do right now is pretty broken anyway I believe, see
bug 1364880.
MozReview-Commit-ID: BYs57CCwDCr
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -779,27 +779,29 @@ ServoStyleSet::ProbePseudoElementStyle(E
/* is_probe = */ true, mRawSet.get()).Consume();
if (!computedValues) {
return nullptr;
}
// For :before and :after pseudo-elements, having display: none or no
// 'content' property is equivalent to not having the pseudo-element
// at all.
+ //
+ // Servo handles this during the traversal, so we should never arrive here
+ // with display: none or no content.
+#ifdef DEBUG
bool isBeforeOrAfter = aType == CSSPseudoElementType::before ||
aType == CSSPseudoElementType::after;
if (isBeforeOrAfter) {
const nsStyleDisplay* display = Servo_GetStyleDisplay(computedValues);
const nsStyleContent* content = Servo_GetStyleContent(computedValues);
- // XXXldb What is contentCount for |content: ""|?
- if (display->mDisplay == StyleDisplay::None ||
- content->ContentCount() == 0) {
- return nullptr;
- }
+ MOZ_ASSERT(display->mDisplay != StyleDisplay::None);
+ MOZ_ASSERT(content->ContentCount() > 0);
}
+#endif
nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
return GetContext(computedValues.forget(), aParentContext, pseudoTag, aType,
isBeforeOrAfter ? aOriginatingElement : nullptr);
}
already_AddRefed<nsStyleContext>
ServoStyleSet::ProbePseudoElementStyle(Element* aOriginatingElement,
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -567,47 +567,47 @@ trait PrivateMatchMethods: TElement {
}
fn cascade_eager_pseudo(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
pseudo: &PseudoElement) {
debug_assert!(pseudo.is_eager());
let (mut styles, restyle) = data.styles_and_restyle_mut();
- let mut pseudo_style = styles.pseudos.get_mut(pseudo).unwrap();
- let old_values = pseudo_style.values.take();
- let new_values =
- self.cascade_internal(context, &styles.primary, Some(pseudo_style));
+ let should_be_removed = {
+ let mut pseudo_style = styles.pseudos.get_mut(pseudo).unwrap();
+ let old_values = pseudo_style.values.take();
+
+ let new_values =
+ self.cascade_internal(context, &styles.primary, Some(pseudo_style));
- if let Some(old) = old_values {
- // ::before and ::after are element-backed in Gecko, so they do the
- // damage calculation for themselves, when there's an actual pseudo.
- //
- // NOTE(emilio): An alternative to this check is to just remove the
- // pseudo-style entry here if new_values is display: none or has
- // an ineffective content property...
- //
- // I think it's nice to handle it here instead for symmetry with how
- // we handle display: none elements, but the other approach may be
- // ok too?
- let is_unexisting_before_or_after =
+ if let Some(old) = old_values {
+ if cfg!(feature = "servo") || !pseudo.is_before_or_after() {
+ self.accumulate_damage(&context.shared,
+ restyle.unwrap(),
+ &old,
+ &new_values,
+ Some(pseudo));
+ }
+ }
+
+ let should_be_removed =
pseudo.is_before_or_after() &&
- self.existing_style_for_restyle_damage(&old, Some(pseudo)).is_none();
+ (new_values.get_box().clone_display() == display::T::none ||
+ new_values.ineffective_content_property());
+
+ pseudo_style.values = Some(new_values);
- if cfg!(feature = "servo") || is_unexisting_before_or_after {
- self.accumulate_damage(&context.shared,
- restyle.unwrap(),
- &old,
- &new_values,
- Some(pseudo));
- }
+ should_be_removed
+ };
+
+ if should_be_removed {
+ styles.pseudos.take(pseudo);
}
-
- pseudo_style.values = Some(new_values)
}
/// get_after_change_style removes the transition rules from the ComputedValues.
/// If there is no transition rule in the ComputedValues, it returns None.
#[cfg(feature = "gecko")]
fn get_after_change_style(&self,
context: &mut StyleContext<Self>,
@@ -1411,28 +1411,16 @@ pub trait MatchMethods : TElement {
//
// This happens with display:none elements, and not-yet-existing
// pseudo-elements.
if new_style_is_display_none && old_style_is_display_none {
// The style remains display:none. No need for damage.
return RestyleDamage::empty()
}
- if pseudo.map_or(false, |p| p.is_before_or_after()) {
- debug_assert!(old_style_is_display_none ||
- old_values.ineffective_content_property());
- if new_style_is_display_none ||
- new_values.ineffective_content_property() {
- // The pseudo-element will remain undisplayed, so just avoid
- // triggering any change.
- return RestyleDamage::empty()
- }
- return RestyleDamage::reconstruct()
- }
-
// Something else. Be conservative for now.
warn!("Reframing due to lack of old style source: {:?}, pseudo: {:?}",
self, pseudo);
RestyleDamage::reconstruct()
}
/// Cascade the eager pseudo-elements of this element.
fn cascade_pseudos(&self,