Skip to content

Commit 4f9ad35

Browse files
committed
Bug 1850974 - Make :is(:host) work. r=zrhoffman
This should work per spec, see w3c/csswg-drafts#9509. Tweak a bit the selector flags set up so that checking for :host selectors during CascadeData rebuilds is cheap. Differential Revision: https://phabricator.services.mozilla.com/D191570
1 parent 47bda64 commit 4f9ad35

File tree

2 files changed

+261
-222
lines changed

2 files changed

+261
-222
lines changed

selectors/builder.rs

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,24 @@ bitflags! {
176176
const HAS_SLOTTED = 1 << 1;
177177
const HAS_PART = 1 << 2;
178178
const HAS_PARENT = 1 << 3;
179+
const HAS_NON_FEATURELESS_COMPONENT = 1 << 4;
180+
const HAS_HOST = 1 << 5;
179181
}
180182
}
181183

182-
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
184+
impl SelectorFlags {
185+
/// When you nest a pseudo-element with something like:
186+
///
187+
/// ::before { & { .. } }
188+
///
189+
/// It is not supposed to work, because :is(::before) is invalid. We can't propagate the
190+
/// pseudo-flags from inner to outer selectors, to avoid breaking our invariants.
191+
pub (crate) fn for_nesting() -> Self {
192+
Self::all() - (Self::HAS_PSEUDO | Self::HAS_SLOTTED | Self::HAS_PART)
193+
}
194+
}
195+
196+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ToShmem)]
183197
pub struct SpecificityAndFlags {
184198
/// There are two free bits here, since we use ten bits for each specificity
185199
/// kind (id, class, element).
@@ -188,33 +202,6 @@ pub struct SpecificityAndFlags {
188202
pub(crate) flags: SelectorFlags,
189203
}
190204

191-
impl SpecificityAndFlags {
192-
#[inline]
193-
pub fn specificity(&self) -> u32 {
194-
self.specificity
195-
}
196-
197-
#[inline]
198-
pub fn has_pseudo_element(&self) -> bool {
199-
self.flags.intersects(SelectorFlags::HAS_PSEUDO)
200-
}
201-
202-
#[inline]
203-
pub fn has_parent_selector(&self) -> bool {
204-
self.flags.intersects(SelectorFlags::HAS_PARENT)
205-
}
206-
207-
#[inline]
208-
pub fn is_slotted(&self) -> bool {
209-
self.flags.intersects(SelectorFlags::HAS_SLOTTED)
210-
}
211-
212-
#[inline]
213-
pub fn is_part(&self) -> bool {
214-
self.flags.intersects(SelectorFlags::HAS_PART)
215-
}
216-
}
217-
218205
const MAX_10BIT: u32 = (1u32 << 10) - 1;
219206

220207
#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
@@ -276,9 +263,12 @@ where
276263
flags.insert(SelectorFlags::HAS_PSEUDO);
277264
specificity.element_selectors += 1
278265
},
279-
Component::LocalName(..) => specificity.element_selectors += 1,
266+
Component::LocalName(..) => {
267+
flags.insert(SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
268+
specificity.element_selectors += 1
269+
},
280270
Component::Slotted(ref selector) => {
281-
flags.insert(SelectorFlags::HAS_SLOTTED);
271+
flags.insert(SelectorFlags::HAS_SLOTTED | SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
282272
specificity.element_selectors += 1;
283273
// Note that due to the way ::slotted works we only compete with
284274
// other ::slotted rules, so the above rule doesn't really
@@ -287,21 +277,19 @@ where
287277
//
288278
// See: https://github.com/w3c/csswg-drafts/issues/1915
289279
*specificity += Specificity::from(selector.specificity());
290-
if selector.has_parent_selector() {
291-
flags.insert(SelectorFlags::HAS_PARENT);
292-
}
280+
flags.insert(selector.flags());
293281
},
294282
Component::Host(ref selector) => {
283+
flags.insert(SelectorFlags::HAS_HOST);
295284
specificity.class_like_selectors += 1;
296285
if let Some(ref selector) = *selector {
297286
// See: https://github.com/w3c/csswg-drafts/issues/1915
298287
*specificity += Specificity::from(selector.specificity());
299-
if selector.has_parent_selector() {
300-
flags.insert(SelectorFlags::HAS_PARENT);
301-
}
288+
flags.insert(selector.flags() - SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
302289
}
303290
},
304291
Component::ID(..) => {
292+
flags.insert(SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
305293
specificity.id_selectors += 1;
306294
},
307295
Component::Class(..) |
@@ -313,6 +301,7 @@ where
313301
Component::Scope |
314302
Component::Nth(..) |
315303
Component::NonTSPseudoClass(..) => {
304+
flags.insert(SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
316305
specificity.class_like_selectors += 1;
317306
},
318307
Component::NthOf(ref nth_of_data) => {
@@ -325,7 +314,7 @@ where
325314
specificity.class_like_selectors += 1;
326315
let sf = selector_list_specificity_and_flags(nth_of_data.selectors().iter());
327316
*specificity += Specificity::from(sf.specificity);
328-
flags.insert(sf.flags);
317+
flags.insert(sf.flags | SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
329318
},
330319
// https://drafts.csswg.org/selectors/#specificity-rules:
331320
//
@@ -344,7 +333,7 @@ where
344333
Component::Has(ref relative_selectors) => {
345334
let sf = relative_selector_list_specificity_and_flags(relative_selectors);
346335
*specificity += Specificity::from(sf.specificity);
347-
flags.insert(sf.flags);
336+
flags.insert(sf.flags | SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
348337
},
349338
Component::ExplicitUniversalType |
350339
Component::ExplicitAnyNamespace |
@@ -354,6 +343,7 @@ where
354343
Component::RelativeSelectorAnchor |
355344
Component::Invalid(..) => {
356345
// Does not affect specificity
346+
flags.insert(SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);
357347
},
358348
}
359349
}
@@ -377,9 +367,7 @@ pub(crate) fn selector_list_specificity_and_flags<'a, Impl: SelectorImpl>(
377367
let mut flags = SelectorFlags::empty();
378368
for selector in itr {
379369
specificity = std::cmp::max(specificity, selector.specificity());
380-
if selector.has_parent_selector() {
381-
flags.insert(SelectorFlags::HAS_PARENT);
382-
}
370+
flags.insert(selector.flags());
383371
}
384372
SpecificityAndFlags { specificity, flags }
385373
}

0 commit comments

Comments
 (0)