/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
bitflags! { /// Bit-flags for pseudo-class. This should only be used for querying if a /// page-rule applies. /// /// https://drafts.csswg.org/css-page-3/#page-selectors #[derive(Clone, Copy)] #[repr(C)] pubstruct PagePseudoClassFlags : u8 { /// No pseudo-classes const NONE = 0; /// Flag for PagePseudoClass::First const FIRST = 1 << 0; /// Flag for PagePseudoClass::Blank const BLANK = 1 << 1; /// Flag for PagePseudoClass::Left const LEFT = 1 << 2; /// Flag for PagePseudoClass::Right const RIGHT = 1 << 3;
}
}
impl PagePseudoClassFlags { /// Creates a pseudo-class flags object with a single pseudo-class. #[inline] pubfn new(other: &PagePseudoClass) -> Self { match *other {
PagePseudoClass::First => PagePseudoClassFlags::FIRST,
PagePseudoClass::Blank => PagePseudoClassFlags::BLANK,
PagePseudoClass::Left => PagePseudoClassFlags::LEFT,
PagePseudoClass::Right => PagePseudoClassFlags::RIGHT,
}
} /// Checks if the given pseudo class applies to this set of flags. #[inline] pubfn contains_class(self, other: &PagePseudoClass) -> bool { self.intersects(PagePseudoClassFlags::new(other))
}
}
type PagePseudoClasses = SmallVec<[PagePseudoClass; 4]>;
/// Computes the [specificity] given the g, h, and f values as in the spec. /// /// g is number of `:first` or `:blank`, h is number of `:left` or `:right`, /// f is if the selector includes a page-name (selectors can only include one /// or zero page-names). /// /// This places hard limits of 65535 on h and 32767 on g, at which point all /// higher values are treated as those limits respectively. /// /// [specificity]: https://drafts.csswg.org/css-page/#specificity #[inline] fn selector_specificity(g: usize, h: usize, f: bool) -> u32 { let h = h.min(0xFFFF) as u32; let g = (g.min(0x7FFF) as u32) << 16; let f = if f { 0x80000000
} else { 0
};
h + g + f
}
impl PageSelector { /// Checks if the ident matches a page-name's ident. /// /// This does not take pseudo selectors into account. #[inline] pubfn ident_matches(&self, other: &CustomIdent) -> bool { self.name.0 == other.0
}
/// Checks that this selector matches the ident and all pseudo classes are /// present in the provided flags. #[inline] pubfn matches(&self, name: &CustomIdent, flags: PagePseudoClassFlags) -> bool { self.ident_matches(name) && self.flags_match(flags)
}
/// Checks that all pseudo classes in this selector are present in the /// provided flags. /// /// Equivalent to, but may be more efficient than: /// /// ``` /// match_specificity(flags).is_some() /// ``` pubfn flags_match(&self, flags: PagePseudoClassFlags) -> bool { self.pseudos.iter().all(|pc| flags.contains_class(pc))
}
/// Implements specificity calculation for a page selector given a set of /// page pseudo-classes to match with. /// If this selector includes any pseudo-classes that are not in the flags, /// then this will return None. /// /// To fit the specificity calculation into a 32-bit value, this limits the /// maximum count of :first and :blank to 32767, and the maximum count of /// :left and :right to 65535. /// /// https://drafts.csswg.org/css-page-3/#cascading-and-page-context pubfn match_specificity(&self, flags: PagePseudoClassFlags) -> Option<u32> { letmut g: usize = 0; letmut h: usize = 0; for pc inself.pseudos.iter() { if !flags.contains_class(pc) { return None;
} match pc {
PagePseudoClass::First | PagePseudoClass::Blank => g += 1,
PagePseudoClass::Left | PagePseudoClass::Right => h += 1,
}
}
Some(selector_specificity(g, h, !self.name.0.is_empty()))
}
}
impl ToCss for PageSelector { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: Write,
{ self.name.to_css(dest)?; for pc inself.pseudos.iter() {
dest.write_str(pc.to_str())?;
}
Ok(())
}
}
fn parse_page_name<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AtomIdent, ParseError<'i>> { let s = input.expect_ident()?;
Ok(AtomIdent::from(&**s))
}
impl Parse for PageSelector { fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { let name = input.try_parse(parse_page_name); letmut pseudos = PagePseudoClasses::default(); whilelet Ok(pc) = input.try_parse(PagePseudoClass::parse) {
pseudos.push(pc);
} // If the result was empty, then we didn't get a selector. let name = match name {
Ok(name) => name,
Err(..) if !pseudos.is_empty() => AtomIdent::new(atom!("")),
Err(err) => return Err(err),
};
Ok(PageSelector { name, pseudos })
}
}
/// A list of [`@page`][page selectors] /// /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors #[derive(Clone, Debug, Default, MallocSizeOf, ToCss, ToShmem)] #[css(comma)] pubstruct PageSelectors(#[css(iterable)] pubBox<[PageSelector]>);
impl PageSelectors { /// Creates a new PageSelectors from a Vec, as from parse_comma_separated #[inline] pubfn new(s: Vec<PageSelector>) -> Self {
PageSelectors(s.into())
} /// Returns true iff there are any page selectors #[inline] pubfn is_empty(&self) -> bool { self.as_slice().is_empty()
} /// Get the underlying PageSelector data as a slice #[inline] pubfn as_slice(&self) -> &[PageSelector] {
&*self.0
}
}
/// A [`@page`][page] rule. /// /// This implements only a limited subset of the CSS /// 2.2 syntax. /// /// [page]: https://drafts.csswg.org/css2/page.html#page-box /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors #[derive(Clone, Debug, ToShmem)] pubstruct PageRule { /// Selectors of the page-rule pub selectors: PageSelectors, /// Nested rules. pub rules: Arc<Locked<CssRules>>, /// The declaration block this page rule contains. pub block: Arc<Locked<PropertyDeclarationBlock>>, /// The source position this rule was found at. pub source_location: SourceLocation,
}
impl PageRule { /// Measure heap usage. #[cfg(feature = "gecko")] pubfn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { // Measurement of other fields may be added later. self.rules.unconditional_shallow_size_of(ops) + self.rules.read_with(guard).size_of(guard, ops) + self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops) + self.selectors.size_of(ops)
} /// Computes the specificity of this page rule when matched with flags. /// /// Computing this value has linear-complexity with the size of the /// selectors, so the caller should usually call this once and cache the /// result. /// /// Returns None if the flags do not match this page rule. /// /// The return type is ordered by page-rule specificity. pubfn match_specificity(&self, flags: PagePseudoClassFlags) -> Option<u32> { ifself.selectors.is_empty() { // A page-rule with no selectors matches all pages, but with the // lowest possible specificity. return Some(selector_specificity(0, 0, false));
} letmut specificity = None; for s inself.selectors.0.iter().map(|s| s.match_specificity(flags)) {
specificity = s.max(specificity);
}
specificity
}
}
impl ToCssWithGuard for PageRule { /// Serialization of PageRule is not specced, adapted from steps for StyleRule. fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { // https://drafts.csswg.org/cssom/#serialize-a-css-rule
dest.write_str("@page ")?; if !self.selectors.is_empty() { self.selectors.to_css(&mut CssWriter::new(dest))?;
dest.write_char(' ')?;
}
style_or_page_rule_to_css(Some(&self.rules), &self.block, guard, dest)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.