Auto merge of #29748 - Loirooriol:sync, r=mrobinson

Backport several style changes from Gecko

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-05-19 22:49:36 +02:00 committed by GitHub
commit 2426a38a4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
101 changed files with 2063 additions and 1553 deletions

1
Cargo.lock generated
View file

@ -6177,6 +6177,7 @@ dependencies = [
"log", "log",
"malloc_size_of", "malloc_size_of",
"malloc_size_of_derive", "malloc_size_of_derive",
"mime",
"new_debug_unreachable", "new_debug_unreachable",
"num-derive", "num-derive",
"num-integer", "num-integer",

View file

@ -272,8 +272,8 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
self.traversal.quote -= 1 self.traversal.quote -= 1
} }
}, },
GeneratedContentInfo::ContentItem(ContentItem::Url(..)) => { GeneratedContentInfo::ContentItem(ContentItem::Image(..)) => {
unreachable!("Servo doesn't parse content: url(..) yet") unreachable!("Servo doesn't parse content: url(..) nor image-set(..) yet")
}, },
} }
}; };

View file

@ -354,9 +354,9 @@ where
attr_val.map_or("".to_string(), |s| s.to_string()), attr_val.map_or("".to_string(), |s| s.to_string()),
)); ));
}, },
ContentItem::Url(image_url) => { ContentItem::Image(image) => {
if let Some(replaced_content) = if let Some(replaced_content) =
ReplacedContent::from_image_url(element, context, image_url) ReplacedContent::from_image(element, context, image)
{ {
vec.push(PseudoElementContentItem::Replaced(replaced_content)); vec.push(PseudoElementContentItem::Replaced(replaced_content));
} }

View file

@ -21,6 +21,7 @@ use std::fmt;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::servo::url::ComputedUrl; use style::servo::url::ComputedUrl;
use style::values::computed::image::Image as ComputedImage;
use style::values::computed::{Length, LengthOrAuto}; use style::values::computed::{Length, LengthOrAuto};
use style::values::CSSFloat; use style::values::CSSFloat;
use style::Zero; use style::Zero;
@ -184,6 +185,17 @@ impl ReplacedContent {
None None
} }
pub fn from_image<'dom>(
element: impl NodeExt<'dom>,
context: &LayoutContext,
image: &ComputedImage,
) -> Option<Self> {
match image {
ComputedImage::Url(image_url) => Self::from_image_url(element, context, image_url),
_ => None, // TODO
}
}
fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> { fn flow_relative_intrinsic_size(&self, style: &ComputedValues) -> Vec2<Option<Length>> {
let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height); let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height);
Vec2::from_physical_size(&intrinsic_size, style.writing_mode) Vec2::from_physical_size(&intrinsic_size, style.writing_mode)

View file

@ -96,7 +96,7 @@ use std::time::Duration;
use style::animation::{AnimationSetKey, DocumentAnimationSet, ElementAnimationSet}; use style::animation::{AnimationSetKey, DocumentAnimationSet, ElementAnimationSet};
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters}; use style::context::{QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters};
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode}; use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::driver; use style::driver;
use style::error_reporting::RustLogReporter; use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL}; use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
@ -1285,11 +1285,7 @@ impl LayoutThread {
); );
// Flush shadow roots stylesheets if dirty. // Flush shadow roots stylesheets if dirty.
document.flush_shadow_roots_stylesheets( document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author.clone());
&self.stylist.device(),
document.quirks_mode(),
guards.author.clone(),
);
let restyles = std::mem::take(&mut data.pending_restyles); let restyles = std::mem::take(&mut data.pending_restyles);
debug!("Draining restyles: {}", restyles.len()); debug!("Draining restyles: {}", restyles.len());

View file

@ -82,7 +82,7 @@ use style::animation::DocumentAnimationSet;
use style::context::{ use style::context::{
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext, QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
}; };
use style::dom::{TDocument, TElement, TNode}; use style::dom::{TElement, TNode};
use style::driver; use style::driver;
use style::error_reporting::RustLogReporter; use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL}; use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
@ -943,11 +943,7 @@ impl LayoutThread {
} }
// Flush shadow roots stylesheets if dirty. // Flush shadow roots stylesheets if dirty.
document.flush_shadow_roots_stylesheets( document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author.clone());
&self.stylist.device(),
document.quirks_mode(),
guards.author.clone(),
);
let restyles = std::mem::take(&mut data.pending_restyles); let restyles = std::mem::take(&mut data.pending_restyles);
debug!("Draining restyles: {}", restyles.len()); debug!("Draining restyles: {}", restyles.len());

View file

@ -461,6 +461,8 @@ impl GroupedClassifier {
fn image_classifer() -> GroupedClassifier { fn image_classifer() -> GroupedClassifier {
GroupedClassifier { GroupedClassifier {
byte_matchers: vec![ byte_matchers: vec![
// Keep this in sync with 'is_supported_mime_type' from
// components/style/servo/media_queries.rs
Box::new(ByteMatcher::image_x_icon()), Box::new(ByteMatcher::image_x_icon()),
Box::new(ByteMatcher::image_x_icon_cursor()), Box::new(ByteMatcher::image_x_icon_cursor()),
Box::new(ByteMatcher::image_bmp()), Box::new(ByteMatcher::image_bmp()),

View file

@ -20,11 +20,10 @@ use servo_arc::Arc;
use servo_atoms::Atom; use servo_atoms::Atom;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use style::context::QuirksMode;
use style::invalidation::media_queries::{MediaListKey, ToMediaListKey}; use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use style::media_queries::MediaList; use style::media_queries::MediaList;
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard}; use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::{CssRule, Origin, Stylesheet}; use style::stylesheets::{Stylesheet, StylesheetContents};
#[derive(Clone, JSTraceable, MallocSizeOf)] #[derive(Clone, JSTraceable, MallocSizeOf)]
#[unrooted_must_root_lint::must_root] #[unrooted_must_root_lint::must_root]
@ -48,19 +47,11 @@ impl PartialEq for StyleSheetInDocument {
impl ToMediaListKey for StyleSheetInDocument { impl ToMediaListKey for StyleSheetInDocument {
fn to_media_list_key(&self) -> MediaListKey { fn to_media_list_key(&self) -> MediaListKey {
self.sheet.to_media_list_key() self.sheet.contents.to_media_list_key()
} }
} }
impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument { impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.sheet.origin(guard)
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.sheet.quirks_mode(guard)
}
fn enabled(&self) -> bool { fn enabled(&self) -> bool {
self.sheet.enabled() self.sheet.enabled()
} }
@ -69,8 +60,8 @@ impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
self.sheet.media(guard) self.sheet.media(guard)
} }
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.sheet.rules(guard) self.sheet.contents()
} }
} }

View file

@ -3525,11 +3525,11 @@ impl Element {
} }
pub fn read_write_state(&self) -> bool { pub fn read_write_state(&self) -> bool {
self.state.get().contains(ElementState::IN_READ_WRITE_STATE) self.state.get().contains(ElementState::IN_READWRITE_STATE)
} }
pub fn set_read_write_state(&self, value: bool) { pub fn set_read_write_state(&self, value: bool) {
self.set_state(ElementState::IN_READ_WRITE_STATE, value) self.set_state(ElementState::IN_READWRITE_STATE, value)
} }
pub fn placeholder_shown_state(&self) -> bool { pub fn placeholder_shown_state(&self) -> bool {

View file

@ -297,7 +297,7 @@ impl HTMLInputElement {
.clone(); .clone();
HTMLInputElement { HTMLInputElement {
htmlelement: HTMLElement::new_inherited_with_state( htmlelement: HTMLElement::new_inherited_with_state(
ElementState::IN_ENABLED_STATE | ElementState::IN_READ_WRITE_STATE, ElementState::IN_ENABLED_STATE | ElementState::IN_READWRITE_STATE,
local_name, local_name,
prefix, prefix,
document, document,

View file

@ -148,7 +148,7 @@ impl HTMLTextAreaElement {
.clone(); .clone();
HTMLTextAreaElement { HTMLTextAreaElement {
htmlelement: HTMLElement::new_inherited_with_state( htmlelement: HTMLElement::new_inherited_with_state(
ElementState::IN_ENABLED_STATE | ElementState::IN_READ_WRITE_STATE, ElementState::IN_ENABLED_STATE | ElementState::IN_READWRITE_STATE,
local_name, local_name,
prefix, prefix,
document, document,

View file

@ -19,15 +19,13 @@ use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::stylesheet_set::StylesheetSetRef; use crate::stylesheet_set::StylesheetSetRef;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use selectors::context::QuirksMode;
use servo_arc::Arc; use servo_arc::Arc;
use servo_atoms::Atom; use servo_atoms::Atom;
use style::author_styles::AuthorStyles; use style::author_styles::AuthorStyles;
use style::dom::TElement; use style::dom::TElement;
use style::media_queries::Device;
use style::shared_lock::SharedRwLockReadGuard; use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::Stylesheet; use style::stylesheets::Stylesheet;
use style::stylist::CascadeData; use style::stylist::{CascadeData, Stylist};
/// Whether a shadow root hosts an User Agent widget. /// Whether a shadow root hosts an User Agent widget.
#[derive(JSTraceable, MallocSizeOf, PartialEq)] #[derive(JSTraceable, MallocSizeOf, PartialEq)]
@ -245,8 +243,7 @@ pub trait LayoutShadowRootHelpers<'dom> {
fn get_style_data_for_layout(self) -> &'dom CascadeData; fn get_style_data_for_layout(self) -> &'dom CascadeData;
unsafe fn flush_stylesheets<E: TElement>( unsafe fn flush_stylesheets<E: TElement>(
self, self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
); );
} }
@ -277,13 +274,12 @@ impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn flush_stylesheets<E: TElement>( unsafe fn flush_stylesheets<E: TElement>(
self, self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) { ) {
let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout(); let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout();
if author_styles.stylesheets.dirty() { if author_styles.stylesheets.dirty() {
author_styles.flush::<E>(device, quirks_mode, guard); author_styles.flush::<E>(stylist, guard);
} }
} }
} }

View file

@ -12,10 +12,10 @@ use script_layout_interface::wrapper_traits::LayoutDataTrait;
use selectors::matching::QuirksMode; use selectors::matching::QuirksMode;
use std::marker::PhantomData; use std::marker::PhantomData;
use style::dom::{TDocument, TNode}; use style::dom::{TDocument, TNode};
use style::media_queries::Device;
use style::shared_lock::{ use style::shared_lock::{
SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard as StyleSharedRwLockReadGuard, SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard as StyleSharedRwLockReadGuard,
}; };
use style::stylist::Stylist;
// A wrapper around documents that ensures ayout can only ever access safe properties. // A wrapper around documents that ensures ayout can only ever access safe properties.
pub struct ServoLayoutDocument<'dom, LayoutDataType: LayoutDataTrait> { pub struct ServoLayoutDocument<'dom, LayoutDataType: LayoutDataTrait> {
@ -90,8 +90,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy
pub fn flush_shadow_roots_stylesheets( pub fn flush_shadow_roots_stylesheets(
&self, &self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &StyleSharedRwLockReadGuard, guard: &StyleSharedRwLockReadGuard,
) { ) {
unsafe { unsafe {
@ -100,7 +99,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy
} }
self.document.flush_shadow_roots_stylesheets(); self.document.flush_shadow_roots_stylesheets();
for shadow_root in self.shadow_roots() { for shadow_root in self.shadow_roots() {
shadow_root.flush_stylesheets(device, quirks_mode, guard); shadow_root.flush_stylesheets(stylist, guard);
} }
} }
} }

View file

@ -7,13 +7,11 @@ use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot};
use crate::layout_dom::ServoLayoutElement; use crate::layout_dom::ServoLayoutElement;
use crate::layout_dom::ServoLayoutNode; use crate::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::LayoutDataTrait; use script_layout_interface::wrapper_traits::LayoutDataTrait;
use selectors::matching::QuirksMode;
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use style::dom::TShadowRoot; use style::dom::TShadowRoot;
use style::media_queries::Device;
use style::shared_lock::SharedRwLockReadGuard as StyleSharedRwLockReadGuard; use style::shared_lock::SharedRwLockReadGuard as StyleSharedRwLockReadGuard;
use style::stylist::CascadeData; use style::stylist::{CascadeData, Stylist};
pub struct ServoShadowRoot<'dom, LayoutDataType: LayoutDataTrait> { pub struct ServoShadowRoot<'dom, LayoutDataType: LayoutDataTrait> {
/// The wrapped private DOM ShadowRoot. /// The wrapped private DOM ShadowRoot.
@ -74,11 +72,10 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ServoShadowRoot<'dom, LayoutDataType
pub unsafe fn flush_stylesheets( pub unsafe fn flush_stylesheets(
&self, &self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &StyleSharedRwLockReadGuard, guard: &StyleSharedRwLockReadGuard,
) { ) {
self.shadow_root self.shadow_root
.flush_stylesheets::<ServoLayoutElement<LayoutDataType>>(device, quirks_mode, guard) .flush_stylesheets::<ServoLayoutElement<LayoutDataType>>(stylist, guard)
} }
} }

View file

@ -212,6 +212,12 @@ macro_rules! with_all_bounds {
/// pseudo-elements /// pseudo-elements
type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>; type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
/// Whether attribute hashes should be collected for filtering
/// purposes.
fn should_collect_attr_hash(_name: &Self::LocalName) -> bool {
false
}
} }
} }
} }
@ -482,6 +488,34 @@ where
Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => { Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => {
class.precomputed_hash() class.precomputed_hash()
}, },
Component::AttributeInNoNamespace { ref local_name, .. }
if Impl::should_collect_attr_hash(local_name) =>
{
// AttributeInNoNamespace is only used when local_name ==
// local_name_lower.
local_name.precomputed_hash()
},
Component::AttributeInNoNamespaceExists {
ref local_name,
ref local_name_lower,
..
} => {
// Only insert the local-name into the filter if it's all
// lowercase. Otherwise we would need to test both hashes, and
// our data structures aren't really set up for that.
if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) {
continue;
}
local_name.precomputed_hash()
},
Component::AttributeOther(ref selector) => {
if selector.local_name != selector.local_name_lower ||
!Impl::should_collect_attr_hash(&selector.local_name)
{
continue;
}
selector.local_name.precomputed_hash()
},
Component::Is(ref list) | Component::Where(ref list) => { Component::Is(ref list) | Component::Where(ref list) => {
// :where and :is OR their selectors, so we can't put any hash // :where and :is OR their selectors, so we can't put any hash
// in the filter if there's more than one selector, as that'd // in the filter if there's more than one selector, as that'd
@ -825,7 +859,7 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {
#[inline] #[inline]
pub(crate) fn is_featureless_host_selector(&mut self) -> bool { pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
self.selector_length() > 0 && self.selector_length() > 0 &&
self.all(|component| matches!(*component, Component::Host(..))) && self.all(|component| component.is_host()) &&
self.next_sequence().is_none() self.next_sequence().is_none()
} }
@ -1089,10 +1123,17 @@ pub enum Component<Impl: SelectorImpl> {
impl<Impl: SelectorImpl> Component<Impl> { impl<Impl: SelectorImpl> Component<Impl> {
/// Returns true if this is a combinator. /// Returns true if this is a combinator.
#[inline]
pub fn is_combinator(&self) -> bool { pub fn is_combinator(&self) -> bool {
matches!(*self, Component::Combinator(_)) matches!(*self, Component::Combinator(_))
} }
/// Returns true if this is a :host() selector.
#[inline]
pub fn is_host(&self) -> bool {
matches!(*self, Component::Host(..))
}
/// Returns the value as a combinator if applicable, None otherwise. /// Returns the value as a combinator if applicable, None otherwise.
pub fn as_combinator(&self) -> Option<Combinator> { pub fn as_combinator(&self) -> Option<Combinator> {
match *self { match *self {

View file

@ -50,6 +50,7 @@ lazy_static = "1"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
malloc_size_of = { path = "../malloc_size_of" } malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = "0.1" malloc_size_of_derive = "0.1"
mime = "0.3.13"
new_debug_unreachable = "1.0" new_debug_unreachable = "1.0"
num-derive = "0.3" num-derive = "0.3"
num-integer = "0.1" num-integer = "0.1"

View file

@ -5,16 +5,16 @@
//! A set of author stylesheets and their computed representation, such as the //! A set of author stylesheets and their computed representation, such as the
//! ones used for ShadowRoot. //! ones used for ShadowRoot.
use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::media_queries::ToMediaListKey; use crate::invalidation::media_queries::ToMediaListKey;
use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylist::Stylist;
use crate::stylesheet_set::AuthorStylesheetSet; use crate::stylesheet_set::AuthorStylesheetSet;
use crate::stylesheets::StylesheetInDocument; use crate::stylesheets::StylesheetInDocument;
use crate::stylist::CascadeData; use crate::stylist::CascadeData;
use servo_arc::Arc;
/// A set of author stylesheets and their computed representation, such as the /// A set of author stylesheets and their computed representation, such as the
/// ones used for ShadowRoot. /// ones used for ShadowRoot.
@ -27,7 +27,14 @@ where
/// and all that stuff. /// and all that stuff.
pub stylesheets: AuthorStylesheetSet<S>, pub stylesheets: AuthorStylesheetSet<S>,
/// The actual cascade data computed from the stylesheets. /// The actual cascade data computed from the stylesheets.
pub data: CascadeData, #[ignore_malloc_size_of = "Measured as part of the stylist"]
pub data: Arc<CascadeData>,
}
lazy_static! {
static ref EMPTY_CASCADE_DATA: Arc<CascadeData> = {
Arc::new_leaked(CascadeData::new())
};
} }
impl<S> AuthorStyles<S> impl<S> AuthorStyles<S>
@ -39,7 +46,7 @@ where
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
stylesheets: AuthorStylesheetSet::new(), stylesheets: AuthorStylesheetSet::new(),
data: CascadeData::new(), data: EMPTY_CASCADE_DATA.clone(),
} }
} }
@ -50,8 +57,7 @@ where
#[inline] #[inline]
pub fn flush<E>( pub fn flush<E>(
&mut self, &mut self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) where ) where
E: TElement, E: TElement,
@ -61,10 +67,10 @@ where
.stylesheets .stylesheets
.flush::<E>(/* host = */ None, /* snapshot_map = */ None); .flush::<E>(/* host = */ None, /* snapshot_map = */ None);
// Ignore OOM. let result = stylist.rebuild_author_data(&self.data, flusher.sheets, guard);
let _ = self if let Ok(Some(new_data)) = result {
.data self.data = new_data;
.rebuild(device, quirks_mode, flusher.sheets, guard); }
} }
} }

View file

@ -102,6 +102,16 @@ impl<E: TElement> PushedElement<E> {
} }
} }
/// Returns whether the attribute name is excluded from the bloom filter.
///
/// We do this for attributes that are very common but not commonly used in
/// selectors.
#[inline]
#[cfg(feature = "gecko")]
pub fn is_attr_name_excluded_from_filter(atom: &crate::Atom) -> bool {
*atom == atom!("class") || *atom == atom!("id") || *atom == atom!("style")
}
fn each_relevant_element_hash<E, F>(element: E, mut f: F) fn each_relevant_element_hash<E, F>(element: E, mut f: F)
where where
E: TElement, E: TElement,
@ -115,6 +125,15 @@ where
} }
element.each_class(|class| f(class.get_hash())); element.each_class(|class| f(class.get_hash()));
#[cfg(feature = "gecko")]
if static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") {
element.each_attr_name(|name| {
if !is_attr_name_excluded_from_filter(name) {
f(name.get_hash())
}
});
}
} }
impl<E: TElement> Drop for StyleBloom<E> { impl<E: TElement> Drop for StyleBloom<E> {

View file

@ -116,21 +116,6 @@ impl Default for StyleSystemOptions {
} }
} }
impl StyleSystemOptions {
#[cfg(feature = "servo")]
/// On Gecko's nightly build?
pub fn is_nightly(&self) -> bool {
false
}
#[cfg(feature = "gecko")]
/// On Gecko's nightly build?
#[inline]
pub fn is_nightly(&self) -> bool {
structs::GECKO_IS_NIGHTLY
}
}
/// A shared style context. /// A shared style context.
/// ///
/// There's exactly one of these during a given restyle traversal, and it's /// There's exactly one of these during a given restyle traversal, and it's

View file

@ -519,6 +519,12 @@ pub trait TElement:
{ {
} }
/// Internal iterator for the attribute names of this element.
#[cfg(feature = "gecko")]
fn each_attr_name<F>(&self, callback: F)
where
F: FnMut(&AtomIdent);
/// Internal iterator for the part names that this element exports for a /// Internal iterator for the part names that this element exports for a
/// given part name. /// given part name.
fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F) fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)

View file

@ -54,86 +54,79 @@ bitflags! {
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken
const IN_BROKEN_STATE = 1 << 14; const IN_BROKEN_STATE = 1 << 14;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading
const IN_LOADING_STATE = 1 << 17; const IN_LOADING_STATE = 1 << 15;
/// <https://html.spec.whatwg.org/multipage/#selector-required> /// <https://html.spec.whatwg.org/multipage/#selector-required>
const IN_REQUIRED_STATE = 1 << 21; const IN_REQUIRED_STATE = 1 << 16;
/// <https://html.spec.whatwg.org/multipage/#selector-optional> /// <https://html.spec.whatwg.org/multipage/#selector-optional>
const IN_OPTIONAL_STATE = 1 << 22; const IN_OPTIONAL_STATE = 1 << 17;
/// <https://html.spec.whatwg.org/multipage/#selector-read-write>
const IN_READ_WRITE_STATE = 1 << 22;
/// <https://html.spec.whatwg.org/multipage/#selector-defined> /// <https://html.spec.whatwg.org/multipage/#selector-defined>
const IN_DEFINED_STATE = 1 << 23; const IN_DEFINED_STATE = 1 << 18;
/// <https://html.spec.whatwg.org/multipage/#selector-visited> /// <https://html.spec.whatwg.org/multipage/#selector-visited>
const IN_VISITED_STATE = 1 << 24; const IN_VISITED_STATE = 1 << 19;
/// <https://html.spec.whatwg.org/multipage/#selector-link> /// <https://html.spec.whatwg.org/multipage/#selector-link>
const IN_UNVISITED_STATE = 1 << 25; const IN_UNVISITED_STATE = 1 << 20;
/// <https://drafts.csswg.org/selectors-4/#the-any-link-pseudo> /// <https://drafts.csswg.org/selectors-4/#the-any-link-pseudo>
const IN_VISITED_OR_UNVISITED_STATE = ElementState::IN_VISITED_STATE.bits | const IN_VISITED_OR_UNVISITED_STATE = ElementState::IN_VISITED_STATE.bits |
ElementState::IN_UNVISITED_STATE.bits; ElementState::IN_UNVISITED_STATE.bits;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over
const IN_DRAGOVER_STATE = 1 << 26; const IN_DRAGOVER_STATE = 1 << 21;
/// <https://html.spec.whatwg.org/multipage/#selector-in-range> /// <https://html.spec.whatwg.org/multipage/#selector-in-range>
const IN_INRANGE_STATE = 1 << 27; const IN_INRANGE_STATE = 1 << 22;
/// <https://html.spec.whatwg.org/multipage/#selector-out-of-range> /// <https://html.spec.whatwg.org/multipage/#selector-out-of-range>
const IN_OUTOFRANGE_STATE = 1 << 28; const IN_OUTOFRANGE_STATE = 1 << 23;
/// <https://html.spec.whatwg.org/multipage/#selector-read-only> /// <https://html.spec.whatwg.org/multipage/#selector-read-only>
const IN_READONLY_STATE = 1 << 29; const IN_READONLY_STATE = 1 << 24;
/// <https://html.spec.whatwg.org/multipage/#selector-read-write> /// <https://html.spec.whatwg.org/multipage/#selector-read-write>
const IN_READWRITE_STATE = 1 << 30; const IN_READWRITE_STATE = 1 << 25;
/// <https://html.spec.whatwg.org/multipage/#selector-default> /// <https://html.spec.whatwg.org/multipage/#selector-default>
const IN_DEFAULT_STATE = 1 << 31; const IN_DEFAULT_STATE = 1 << 26;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-submit-invalid /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-submit-invalid
const IN_MOZ_SUBMITINVALID_STATE = 1 << 32; const IN_MOZ_SUBMITINVALID_STATE = 1 << 27;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_OPTIMUM_STATE = 1 << 33; const IN_OPTIMUM_STATE = 1 << 28;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_SUB_OPTIMUM_STATE = 1 << 34; const IN_SUB_OPTIMUM_STATE = 1 << 29;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_SUB_SUB_OPTIMUM_STATE = 1 << 35; const IN_SUB_SUB_OPTIMUM_STATE = 1 << 30;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 36; const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 31;
/// Non-standard & undocumented. /// <https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo>
const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 37; const IN_FOCUSRING_STATE = 1 << 32;
/// Non-standard & undocumented.
const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-focusring
///
/// But also https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
const IN_FOCUSRING_STATE = 1 << 39;
/// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo> /// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo>
const IN_FOCUS_WITHIN_STATE = 1 << 43; const IN_FOCUS_WITHIN_STATE = 1 << 33;
/// :dir matching; the states are used for dynamic change detection. /// :dir matching; the states are used for dynamic change detection.
/// State that elements that match :dir(ltr) are in. /// State that elements that match :dir(ltr) are in.
const IN_LTR_STATE = 1 << 44; const IN_LTR_STATE = 1 << 34;
/// State that elements that match :dir(rtl) are in. /// State that elements that match :dir(rtl) are in.
const IN_RTL_STATE = 1 << 45; const IN_RTL_STATE = 1 << 35;
/// State that HTML elements that have a "dir" attr are in. /// State that HTML elements that have a "dir" attr are in.
const IN_HAS_DIR_ATTR_STATE = 1 << 46; const IN_HAS_DIR_ATTR_STATE = 1 << 36;
/// State that HTML elements with dir="ltr" (or something /// State that HTML elements with dir="ltr" (or something
/// case-insensitively equal to "ltr") are in. /// case-insensitively equal to "ltr") are in.
const IN_HAS_DIR_ATTR_LTR_STATE = 1 << 47; const IN_HAS_DIR_ATTR_LTR_STATE = 1 << 37;
/// State that HTML elements with dir="rtl" (or something /// State that HTML elements with dir="rtl" (or something
/// case-insensitively equal to "rtl") are in. /// case-insensitively equal to "rtl") are in.
const IN_HAS_DIR_ATTR_RTL_STATE = 1 << 48; const IN_HAS_DIR_ATTR_RTL_STATE = 1 << 38;
/// State that HTML <bdi> elements without a valid-valued "dir" attr or /// State that HTML <bdi> elements without a valid-valued "dir" attr or
/// any HTML elements (including <bdi>) with dir="auto" (or something /// any HTML elements (including <bdi>) with dir="auto" (or something
/// case-insensitively equal to "auto") are in. /// case-insensitively equal to "auto") are in.
const IN_HAS_DIR_ATTR_LIKE_AUTO_STATE = 1 << 49; const IN_HAS_DIR_ATTR_LIKE_AUTO_STATE = 1 << 39;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_AUTOFILL_STATE = 1 << 50; const IN_AUTOFILL_STATE = 1 << 40;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_AUTOFILL_PREVIEW_STATE = 1 << 51; const IN_AUTOFILL_PREVIEW_STATE = 1 << 41;
/// State that dialog element is modal, for centered alignment /// State that dialog element is modal, for centered alignment
/// /// <https://html.spec.whatwg.org/multipage/#centered-alignment>
/// https://html.spec.whatwg.org/multipage/#centered-alignment const IN_MODAL_DIALOG_STATE = 1 << 42;
const IN_MODAL_DIALOG_STATE = 1 << 53; /// <https://html.spec.whatwg.org/multipage/#inert-subtrees>
const IN_MOZINERT_STATE = 1 << 43;
/// https://html.spec.whatwg.org/multipage/#inert-subtrees
const IN_MOZINERT_STATE = 1 << 54;
/// State for the topmost dialog element in top layer /// State for the topmost dialog element in top layer
const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 55; const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 44;
/// Non-standard & undocumented. /// Initially used for the devtools highlighter, but now somehow only
const IN_HANDLER_NOPLUGINS = 1 << 56; /// used for the devtools accessibility inspector.
const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 45;
/// Used for the devtools style editor. Probably should go away.
const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 46;
} }
} }

View file

@ -54,6 +54,8 @@ pub enum ContextualParseError<'a> {
InvalidMediaRule(&'a str, ParseError<'a>), InvalidMediaRule(&'a str, ParseError<'a>),
/// A value was not recognized. /// A value was not recognized.
UnsupportedValue(&'a str, ParseError<'a>), UnsupportedValue(&'a str, ParseError<'a>),
/// A never-matching `:host` selector was found.
NeverMatchingHostSelector(String),
} }
impl<'a> fmt::Display for ContextualParseError<'a> { impl<'a> fmt::Display for ContextualParseError<'a> {
@ -210,6 +212,9 @@ impl<'a> fmt::Display for ContextualParseError<'a> {
parse_error_to_str(err, f) parse_error_to_str(err, f)
}, },
ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f), ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
ContextualParseError::NeverMatchingHostSelector(ref selector) => {
write!(f, ":host selector is not featureless: {}", selector)
}
} }
} }
} }

View file

@ -20,9 +20,13 @@ use crate::values::specified::font::SpecifiedFontStyle;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::values::specified::font::SpecifiedFontVariationSettings; use crate::values::specified::font::SpecifiedFontVariationSettings;
use crate::values::specified::font::{AbsoluteFontWeight, FontStretch}; use crate::values::specified::font::{AbsoluteFontWeight, FontStretch};
#[cfg(feature = "gecko")]
use crate::values::specified::font::MetricsOverride;
use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::Angle; use crate::values::specified::Angle;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::values::specified::NonNegativePercentage;
#[cfg(feature = "gecko")]
use cssparser::UnicodeRange; use cssparser::UnicodeRange;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{CowRcStr, SourceLocation}; use cssparser::{CowRcStr, SourceLocation};
@ -194,7 +198,7 @@ impl FontStretchRange {
fn compute_stretch(s: &FontStretch) -> f32 { fn compute_stretch(s: &FontStretch) -> f32 {
match *s { match *s {
FontStretch::Keyword(ref kw) => kw.compute().0, FontStretch::Keyword(ref kw) => kw.compute().0,
FontStretch::Stretch(ref p) => p.get(), FontStretch::Stretch(ref p) => p.0.get(),
FontStretch::System(..) => unreachable!(), FontStretch::System(..) => unreachable!(),
} }
} }
@ -418,6 +422,18 @@ macro_rules! is_descriptor_enabled {
("font-variation-settings") => { ("font-variation-settings") => {
static_prefs::pref!("layout.css.font-variations.enabled") static_prefs::pref!("layout.css.font-variations.enabled")
}; };
("ascent-override") => {
static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
};
("descent-override") => {
static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
};
("line-gap-override") => {
static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
};
("size-adjust") => {
static_prefs::pref!("layout.css.size-adjust.enabled")
};
($name:tt) => { ($name:tt) => {
true true
}; };
@ -576,6 +592,18 @@ font_face_descriptors! {
/// The language override of this font face. /// The language override of this font face.
"font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue, "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue,
/// The ascent override for this font face.
"ascent-override" ascent_override / mAscentOverride: MetricsOverride,
/// The descent override for this font face.
"descent-override" descent_override / mDescentOverride: MetricsOverride,
/// The line-gap override for this font face.
"line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride,
/// The size adjustment for this font face.
"size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage,
] ]
} }

View file

@ -4,7 +4,6 @@
//! Data needed to style a Gecko document. //! Data needed to style a Gecko document.
use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes}; use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes};
@ -15,7 +14,7 @@ use crate::media_queries::{Device, MediaList};
use crate::properties::ComputedValues; use crate::properties::ComputedValues;
use crate::selector_parser::SnapshotMap; use crate::selector_parser::SnapshotMap;
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument}; use crate::stylesheets::{StylesheetContents, StylesheetInDocument};
use crate::stylist::Stylist; use crate::stylist::Stylist;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use malloc_size_of::MallocSizeOfOps; use malloc_size_of::MallocSizeOfOps;
@ -69,16 +68,6 @@ impl GeckoStyleSheet {
fn inner(&self) -> &StyleSheetInfo { fn inner(&self) -> &StyleSheetInfo {
unsafe { &*(self.raw().mInner as *const StyleSheetInfo) } unsafe { &*(self.raw().mInner as *const StyleSheetInfo) }
} }
/// Gets the StylesheetContents for this stylesheet.
pub fn contents(&self) -> &StylesheetContents {
debug_assert!(!self.inner().mContents.mRawPtr.is_null());
unsafe {
let contents =
(&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
&*contents
}
}
} }
impl Drop for GeckoStyleSheet { impl Drop for GeckoStyleSheet {
@ -95,14 +84,6 @@ impl Clone for GeckoStyleSheet {
} }
impl StylesheetInDocument for GeckoStyleSheet { impl StylesheetInDocument for GeckoStyleSheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
self.contents().origin
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
self.contents().quirks_mode
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList; use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList;
use std::mem; use std::mem;
@ -120,13 +101,19 @@ impl StylesheetInDocument for GeckoStyleSheet {
// All the stylesheets Servo knows about are enabled, because that state is // All the stylesheets Servo knows about are enabled, because that state is
// handled externally by Gecko. // handled externally by Gecko.
#[inline]
fn enabled(&self) -> bool { fn enabled(&self) -> bool {
true true
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.contents().rules(guard) debug_assert!(!self.inner().mContents.mRawPtr.is_null());
unsafe {
let contents =
(&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
&*contents
}
} }
} }

View file

@ -275,10 +275,6 @@ enum PrefersReducedMotion {
Reduce, Reduce,
} }
fn color_scheme_no_preference_enabled(_: &crate::parser::ParserContext) -> bool {
static_prefs::pref!("layout.css.prefers-color-scheme-no-preference.enabled")
}
/// Values for the prefers-color-scheme media feature. /// Values for the prefers-color-scheme media feature.
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] #[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)] #[repr(u8)]
@ -286,8 +282,6 @@ fn color_scheme_no_preference_enabled(_: &crate::parser::ParserContext) -> bool
pub enum PrefersColorScheme { pub enum PrefersColorScheme {
Light, Light,
Dark, Dark,
#[parse(condition = "color_scheme_no_preference_enabled")]
NoPreference,
} }
/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion /// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion
@ -408,7 +402,46 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option<PrefersColorSc
unsafe { bindings::Gecko_MediaFeatures_PrefersColorScheme(device.document()) }; unsafe { bindings::Gecko_MediaFeatures_PrefersColorScheme(device.document()) };
match query_value { match query_value {
Some(v) => prefers_color_scheme == v, Some(v) => prefers_color_scheme == v,
None => prefers_color_scheme != PrefersColorScheme::NoPreference, None => true,
}
}
/// Values for the -moz-toolbar-prefers-color-scheme media feature.
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)]
enum ToolbarPrefersColorScheme {
Dark,
Light,
System,
}
/// The color-scheme of the toolbar in the current Firefox theme. This is based
/// on a pref managed by the front-end.
fn eval_toolbar_prefers_color_scheme(d: &Device, query_value: Option<ToolbarPrefersColorScheme>) -> bool {
let toolbar_value = match static_prefs::pref!("browser.theme.toolbar-theme") {
0 => ToolbarPrefersColorScheme::Dark,
1 => ToolbarPrefersColorScheme::Light,
_ => ToolbarPrefersColorScheme::System,
};
let query_value = match query_value {
Some(v) => v,
None => return true,
};
if query_value == toolbar_value {
return true;
}
if toolbar_value != ToolbarPrefersColorScheme::System {
return false;
}
// System might match light and dark as well.
match query_value {
ToolbarPrefersColorScheme::Dark => eval_prefers_color_scheme(d, Some(PrefersColorScheme::Dark)),
ToolbarPrefersColorScheme::Light => eval_prefers_color_scheme(d, Some(PrefersColorScheme::Light)),
ToolbarPrefersColorScheme::System => true,
} }
} }
@ -541,22 +574,6 @@ fn eval_moz_is_resource_document(
query_value.map_or(is_resource_doc, |v| v == is_resource_doc) query_value.map_or(is_resource_doc, |v| v == is_resource_doc)
} }
fn eval_system_metric(
device: &Device,
query_value: Option<bool>,
metric: Atom,
accessible_from_content: bool,
) -> bool {
let supports_metric = unsafe {
bindings::Gecko_MediaFeatures_HasSystemMetric(
device.document(),
metric.as_ptr(),
accessible_from_content,
)
};
query_value.map_or(supports_metric, |v| v == supports_metric)
}
fn eval_moz_os_version( fn eval_moz_os_version(
device: &Device, device: &Device,
query_value: Option<Atom>, query_value: Option<Atom>,
@ -573,15 +590,63 @@ fn eval_moz_os_version(
query_value.as_ptr() == os_version query_value.as_ptr() == os_version
} }
macro_rules! system_metric_feature { fn get_lnf_int(int_id: i32) -> i32 {
($feature_name:expr) => {{ unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
fn __eval(device: &Device, query_value: Option<bool>, _: Option<RangeOrOperator>) -> bool { }
eval_system_metric(
device, fn get_lnf_int_as_bool(int_id: i32) -> bool {
query_value, get_lnf_int(int_id) != 0
$feature_name, }
/* accessible_from_content = */ false,
) fn get_scrollbar_start_backward(int_id: i32) -> bool {
(get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartBackward as i32) != 0
}
fn get_scrollbar_start_forward(int_id: i32) -> bool {
(get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartForward as i32) != 0
}
fn get_scrollbar_end_backward(int_id: i32) -> bool {
(get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndBackward as i32) != 0
}
fn get_scrollbar_end_forward(int_id: i32) -> bool {
(get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndForward as i32) != 0
}
macro_rules! lnf_int_feature {
($feature_name:expr, $int_id:ident, $get_value:ident) => {{
fn __eval(_: &Device, query_value: Option<bool>, _: Option<RangeOrOperator>) -> bool {
let value = $get_value(bindings::LookAndFeel_IntID::$int_id as i32);
query_value.map_or(value, |v| v == value)
}
feature!(
$feature_name,
AllowsRanges::No,
Evaluator::BoolInteger(__eval),
ParsingRequirements::CHROME_AND_UA_ONLY,
)
}};
($feature_name:expr, $int_id:ident) => {{
lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool)
}};
}
/// bool pref-based features are an slightly less convenient to start using
/// version of @supports -moz-bool-pref, but with some benefits, mainly that
/// they can support dynamic changes, and don't require a pref lookup every time
/// they're used.
///
/// In order to use them you need to make sure that the pref defined as a static
/// pref, with `rust: true`. The feature name needs to be defined in
/// `StaticAtoms.py` just like the others. In order to support dynamic changes,
/// you also need to add them to kMediaQueryPrefs in nsXPLookAndFeel.cpp
macro_rules! bool_pref_feature {
($feature_name:expr, $pref:tt) => {{
fn __eval(_: &Device, query_value: Option<bool>, _: Option<RangeOrOperator>) -> bool {
let value = static_prefs::pref!($pref);
query_value.map_or(value, |v| v == value)
} }
feature!( feature!(
@ -598,7 +663,7 @@ macro_rules! system_metric_feature {
/// to support new types in these entries and (2) ensuring that either /// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that /// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change. /// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
feature!( feature!(
atom!("width"), atom!("width"),
AllowsRanges::Yes, AllowsRanges::Yes,
@ -808,27 +873,40 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [
Evaluator::BoolInteger(eval_moz_non_native_content_theme), Evaluator::BoolInteger(eval_moz_non_native_content_theme),
ParsingRequirements::CHROME_AND_UA_ONLY, ParsingRequirements::CHROME_AND_UA_ONLY,
), ),
system_metric_feature!(atom!("-moz-scrollbar-start-backward")), feature!(
system_metric_feature!(atom!("-moz-scrollbar-start-forward")), atom!("-moz-toolbar-prefers-color-scheme"),
system_metric_feature!(atom!("-moz-scrollbar-end-backward")), AllowsRanges::No,
system_metric_feature!(atom!("-moz-scrollbar-end-forward")), keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme),
system_metric_feature!(atom!("-moz-scrollbar-thumb-proportional")), ParsingRequirements::CHROME_AND_UA_ONLY,
system_metric_feature!(atom!("-moz-overlay-scrollbars")), ),
system_metric_feature!(atom!("-moz-windows-default-theme")),
system_metric_feature!(atom!("-moz-mac-graphite-theme")), lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward),
system_metric_feature!(atom!("-moz-mac-big-sur-theme")), lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward),
system_metric_feature!(atom!("-moz-windows-accent-color-in-titlebar")), lnf_int_feature!(atom!("-moz-scrollbar-end-backward"), ScrollArrowStyle, get_scrollbar_end_backward),
system_metric_feature!(atom!("-moz-windows-compositor")), lnf_int_feature!(atom!("-moz-scrollbar-end-forward"), ScrollArrowStyle, get_scrollbar_end_forward),
system_metric_feature!(atom!("-moz-windows-classic")), lnf_int_feature!(atom!("-moz-scrollbar-thumb-proportional"), ScrollSliderStyle),
system_metric_feature!(atom!("-moz-windows-glass")), lnf_int_feature!(atom!("-moz-overlay-scrollbars"), UseOverlayScrollbars),
system_metric_feature!(atom!("-moz-menubar-drag")), lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag),
system_metric_feature!(atom!("-moz-swipe-animation-enabled")), lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme),
system_metric_feature!(atom!("-moz-gtk-csd-available")), lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme),
system_metric_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default")), lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme),
system_metric_feature!(atom!("-moz-gtk-csd-transparent-background")), lnf_int_feature!(atom!("-moz-windows-accent-color-in-titlebar"), WindowsAccentColorInTitlebar),
system_metric_feature!(atom!("-moz-gtk-csd-minimize-button")), lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor),
system_metric_feature!(atom!("-moz-gtk-csd-maximize-button")), lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic),
system_metric_feature!(atom!("-moz-gtk-csd-close-button")), lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass),
system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")), lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled),
system_metric_feature!(atom!("-moz-system-dark-theme")), lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable),
lnf_int_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default"), GTKCSDHideTitlebarByDefault),
lnf_int_feature!(atom!("-moz-gtk-csd-transparent-background"), GTKCSDTransparentBackground),
lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton),
lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton),
lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement),
lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"),
bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"),
bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"),
bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"),
bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"),
]; ];

View file

@ -432,4 +432,11 @@ impl Device {
}; };
SideOffsets2D::new(top, right, bottom, left) SideOffsets2D::new(top, right, bottom, left)
} }
/// Returns true if the given MIME type is supported
pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
unsafe {
bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32)
}
}
} }

View file

@ -247,6 +247,11 @@ impl ::selectors::SelectorImpl for SelectorImpl {
type PseudoElement = PseudoElement; type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass; type NonTSPseudoClass = NonTSPseudoClass;
fn should_collect_attr_hash(name: &AtomIdent) -> bool {
static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") &&
!crate::bloom::is_attr_name_excluded_from_filter(name)
}
} }
impl<'a> SelectorParser<'a> { impl<'a> SelectorParser<'a> {

View file

@ -570,7 +570,7 @@ impl<'le> GeckoElement<'le> {
} }
#[inline(always)] #[inline(always)]
fn attrs(&self) -> &[structs::AttrArray_InternalAttr] { fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
unsafe { unsafe {
let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() { let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() {
Some(attrs) => attrs, Some(attrs) => attrs,
@ -581,12 +581,29 @@ impl<'le> GeckoElement<'le> {
} }
} }
#[inline(always)]
fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
unsafe {
let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() {
Some(attrs) => attrs,
None => return &[],
};
let attrs = match attrs.mMappedAttrs.as_ref() {
Some(attrs) => attrs,
None => return &[],
};
attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
}
}
#[inline(always)] #[inline(always)]
fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
if !self.has_part_attr() { if !self.has_part_attr() {
return None; return None;
} }
snapshot_helpers::find_attr(self.attrs(), &atom!("part")) snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part"))
} }
#[inline(always)] #[inline(always)]
@ -602,7 +619,7 @@ impl<'le> GeckoElement<'le> {
} }
} }
snapshot_helpers::find_attr(self.attrs(), &atom!("class")) snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class"))
} }
#[inline] #[inline]
@ -1167,7 +1184,7 @@ impl<'le> TElement for GeckoElement<'le> {
#[inline] #[inline]
fn exports_any_part(&self) -> bool { fn exports_any_part(&self) -> bool {
snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some() snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some()
} }
// FIXME(emilio): we should probably just return a reference to the Atom. // FIXME(emilio): we should probably just return a reference to the Atom.
@ -1177,7 +1194,25 @@ impl<'le> TElement for GeckoElement<'le> {
return None; return None;
} }
snapshot_helpers::get_id(self.attrs()) snapshot_helpers::get_id(self.non_mapped_attrs())
}
fn each_attr_name<F>(&self, mut callback: F)
where
F: FnMut(&AtomIdent),
{
for attr in self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter()) {
let is_nodeinfo = attr.mName.mBits & 1 != 0;
unsafe {
let atom = if is_nodeinfo {
let node_info = &*((attr.mName.mBits & !1) as *const structs::NodeInfo);
node_info.mInner.mName
} else {
attr.mName.mBits as *const nsAtom
};
AtomIdent::with(atom, |a| callback(a))
}
}
} }
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
@ -1197,7 +1232,7 @@ impl<'le> TElement for GeckoElement<'le> {
where where
F: FnMut(&AtomIdent), F: FnMut(&AtomIdent),
{ {
snapshot_helpers::each_exported_part(self.attrs(), name, callback) snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback)
} }
fn each_part<F>(&self, callback: F) fn each_part<F>(&self, callback: F)
@ -2058,7 +2093,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
return false; return false;
} }
let element_id = match snapshot_helpers::get_id(self.attrs()) { let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) {
Some(id) => id, Some(id) => id,
None => return false, None => return false,
}; };
@ -2078,7 +2113,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline] #[inline]
fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> { fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
snapshot_helpers::imported_part(self.attrs(), name) snapshot_helpers::imported_part(self.non_mapped_attrs(), name)
} }
#[inline(always)] #[inline(always)]

View file

@ -185,7 +185,7 @@ pub struct DocumentStateDependency {
/// In particular, we want to lookup as few things as possible to get the fewer /// In particular, we want to lookup as few things as possible to get the fewer
/// selectors the better, so this looks up by id, class, or looks at the list of /// selectors the better, so this looks up by id, class, or looks at the list of
/// state/other attribute affecting selectors. /// state/other attribute affecting selectors.
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct InvalidationMap { pub struct InvalidationMap {
/// A map from a given class name to all the selectors with that class /// A map from a given class name to all the selectors with that class
/// selector. /// selector.

View file

@ -20,10 +20,10 @@ pub trait InvalidationProcessor<'a, E>
where where
E: TElement, E: TElement,
{ {
/// Whether an invalidation that contains only an eager pseudo-element /// Whether an invalidation that contains only a pseudo-element selector
/// selector like ::before or ::after triggers invalidation of the element /// like ::before or ::after triggers invalidation of the element that would
/// that would originate it. /// originate it.
fn invalidates_on_eager_pseudo_element(&self) -> bool { fn invalidates_on_pseudo_element(&self) -> bool {
false false
} }
@ -878,76 +878,32 @@ where
.selector .selector
.combinator_at_parse_order(next_invalidation.offset - 1); .combinator_at_parse_order(next_invalidation.offset - 1);
if matches!(next_combinator, Combinator::PseudoElement) { if matches!(next_combinator, Combinator::PseudoElement) &&
// This will usually be the very next component, except for self.processor.invalidates_on_pseudo_element()
// the fact that we store compound selectors the other way {
// around, so there could also be state pseudo-classes. // We need to invalidate the element whenever pseudos change, for
let pseudo = next_invalidation // two reasons:
.dependency
.selector
.iter_raw_parse_order_from(next_invalidation.offset)
.flat_map(|c| {
if let Component::PseudoElement(ref pseudo) = *c {
return Some(pseudo);
}
// TODO: Would be nice to make this a diagnostic_assert! of
// sorts.
debug_assert!(
c.maybe_allowed_after_pseudo_element(),
"Someone seriously messed up selector parsing: \
{:?} at offset {:?}: {:?}",
next_invalidation.dependency,
next_invalidation.offset,
c,
);
None
})
.next()
.unwrap();
// FIXME(emilio): This is not ideal, and could not be
// accurate if we ever have stateful element-backed eager
// pseudos.
// //
// Ideally, we'd just remove element-backed eager pseudos // * Eager pseudo styles are stored as part of the originating
// altogether, given they work fine without it. Only gotcha // element's computed style.
// is that we wouldn't style them in parallel, which may or
// may not be an issue.
// //
// Also, this could be more fine grained now (perhaps a // * Lazy pseudo-styles might be cached on the originating
// RESTYLE_PSEUDOS hint?). // element's pseudo-style cache.
// //
// Note that we'll also restyle the pseudo-element because // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS
// it would match this invalidation. // hint?).
if self.processor.invalidates_on_eager_pseudo_element() { //
if pseudo.is_eager() { // Note that we'll also restyle the pseudo-element because it would
invalidated_self = true; // match this invalidation.
} //
// If we start or stop matching some marker rules, and // FIXME: For non-element-backed pseudos this is still not quite
// don't have a marker, then we need to restyle the // correct. For example for ::selection even though we invalidate
// element to potentially create one. // the style properly there's nothing that triggers a repaint
// // necessarily. Though this matches old Gecko behavior, and the
// Same caveats as for other eager pseudos apply, this // ::selection implementation needs to change significantly anyway
// could be more fine-grained. // to implement https://github.com/w3c/csswg-drafts/issues/2474 for
if pseudo.is_marker() && self.element.marker_pseudo_element().is_none() { // example.
invalidated_self = true; invalidated_self = true;
}
// FIXME: ::selection doesn't generate elements, so the
// regular invalidation doesn't work for it. We store
// the cached selection style holding off the originating
// element, so we need to restyle it in order to invalidate
// it. This is still not quite correct, since nothing
// triggers a repaint necessarily, but matches old Gecko
// behavior, and the ::selection implementation needs to
// change significantly anyway to implement
// https://github.com/w3c/csswg-drafts/issues/2474.
if pseudo.is_selection() {
invalidated_self = true;
}
}
} }
debug!( debug!(

View file

@ -158,10 +158,10 @@ impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E>
where where
E: TElement, E: TElement,
{ {
/// We need to invalidate style on an eager pseudo-element, in order to /// We need to invalidate style on pseudo-elements, in order to process
/// process changes that could otherwise end up in ::before or ::after /// changes that could otherwise end up in ::before or ::after content being
/// content being generated. /// generated, and invalidate lazy pseudo caches.
fn invalidates_on_eager_pseudo_element(&self) -> bool { fn invalidates_on_pseudo_element(&self) -> bool {
true true
} }

View file

@ -8,7 +8,7 @@ use crate::context::QuirksMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::{DocumentRule, ImportRule, MediaRule}; use crate::stylesheets::{DocumentRule, ImportRule, MediaRule};
use crate::stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule}; use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule};
use fxhash::FxHashSet; use fxhash::FxHashSet;
/// A key for a given media query result. /// A key for a given media query result.
@ -43,13 +43,13 @@ pub trait ToMediaListKey: Sized {
} }
} }
impl ToMediaListKey for Stylesheet {} impl ToMediaListKey for StylesheetContents {}
impl ToMediaListKey for ImportRule {} impl ToMediaListKey for ImportRule {}
impl ToMediaListKey for MediaRule {} impl ToMediaListKey for MediaRule {}
/// A struct that holds the result of a media query evaluation pass for the /// A struct that holds the result of a media query evaluation pass for the
/// media queries that evaluated successfully. /// media queries that evaluated successfully.
#[derive(Debug, MallocSizeOf, PartialEq)] #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct EffectiveMediaQueryResults { pub struct EffectiveMediaQueryResults {
/// The set of media lists that matched last time. /// The set of media lists that matched last time.
set: FxHashSet<MediaListKey>, set: FxHashSet<MediaListKey>,

View file

@ -196,23 +196,37 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
_ => return Err(()), _ => return Err(()),
} }
}; };
Ok(Some(match first_delim { let operator = match first_delim {
'=' => Operator::Equal, '=' => return Ok(Some(Operator::Equal)),
'>' => { '>' => Operator::GreaterThan,
if input.try_parse(|i| i.expect_delim('=')).is_ok() { '<' => Operator::LessThan,
Operator::GreaterThanEqual
} else {
Operator::GreaterThan
}
},
'<' => {
if input.try_parse(|i| i.expect_delim('=')).is_ok() {
Operator::LessThanEqual
} else {
Operator::LessThan
}
},
_ => return Err(()), _ => return Err(()),
};
// https://drafts.csswg.org/mediaqueries-4/#mq-syntax:
//
// No whitespace is allowed between the “<” or “>”
// <delim-token>s and the following “=” <delim-token>, if its
// present.
//
// TODO(emilio): Maybe we should ignore comments as well?
// https://github.com/w3c/csswg-drafts/issues/6248
let parsed_equal = input.try_parse(|i| {
let t = i.next_including_whitespace().map_err(|_| ())?;
if !matches!(t, Token::Delim('=')) {
return Err(())
}
Ok(())
}).is_ok();
if !parsed_equal {
return Ok(Some(operator));
}
Ok(Some(match operator {
Operator::GreaterThan => Operator::GreaterThanEqual,
Operator::LessThan => Operator::LessThanEqual,
_ => unreachable!(),
})) }))
} }
@ -221,7 +235,11 @@ fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
{ {
if *feature == atom!("forced-colors") { if *feature == atom!("forced-colors") {
return !static_prefs::pref!("layout.css.forced-colors.enabled"); // forced-colors is always enabled in the ua and chrome. On
// the web it is hidden behind a preference, which is defaulted
// to 'true' as of bug 1659511.
return !context.in_ua_or_chrome_sheet() &&
!static_prefs::pref!("layout.css.forced-colors.enabled");
} }
// prefers-contrast is always enabled in the ua and chrome. On // prefers-contrast is always enabled in the ua and chrome. On
// the web it is hidden behind a preference. // the web it is hidden behind a preference.

View file

@ -37,6 +37,7 @@ STYLE_STRUCT_LIST = [
"list", "list",
"margin", "margin",
"outline", "outline",
"page",
"padding", "padding",
"position", "position",
"svg", "svg",

View file

@ -379,13 +379,15 @@ where
type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>; type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
fn tweak_when_ignoring_colors( fn tweak_when_ignoring_colors(
builder: &StyleBuilder, context: &computed::Context,
longhand_id: LonghandId, longhand_id: LonghandId,
origin: Origin, origin: Origin,
declaration: &mut Cow<PropertyDeclaration>, declaration: &mut Cow<PropertyDeclaration>,
declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden, declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
) { ) {
use crate::values::specified::Color; use crate::values::specified::Color;
use crate::values::computed::ToComputedValue;
use cssparser::RGBA;
if !longhand_id.ignored_when_document_colors_disabled() { if !longhand_id.ignored_when_document_colors_disabled() {
return; return;
@ -399,39 +401,25 @@ fn tweak_when_ignoring_colors(
// Don't override background-color on ::-moz-color-swatch. It is set as an // Don't override background-color on ::-moz-color-swatch. It is set as an
// author style (via the style attribute), but it's pretty important for it // author style (via the style attribute), but it's pretty important for it
// to show up for obvious reasons :) // to show up for obvious reasons :)
if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && if context.builder.pseudo.map_or(false, |p| p.is_color_swatch()) &&
longhand_id == LonghandId::BackgroundColor longhand_id == LonghandId::BackgroundColor
{ {
return; return;
} }
fn alpha_channel(color: &Color) -> u8 { fn alpha_channel(color: &Color, context: &computed::Context) -> u8 {
match *color { // We assume here currentColor is opaque.
// Seems safe enough to assume that the default color and system let color = color.to_computed_value(context).to_rgba(RGBA::new(0, 0, 0, 255));
// colors are opaque in HCM, though maybe we shouldn't asume the color.alpha
// later?
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk | Color::System(..) => 255,
// We don't have the actual color here, but since except for color:
// transparent we force opaque text colors, it seems sane to do
// this. You can technically fool this bit of code with:
//
// color: transparent; background-color: currentcolor;
//
// but this is best-effort, and that seems unlikely to happen in
// practice.
Color::CurrentColor => 255,
// Complex colors are results of interpolation only and probably
// shouldn't show up around here in HCM, but we've always treated
// them as opaque effectively so keep doing it.
Color::Complex { .. } => 255,
Color::Numeric { ref parsed, .. } => parsed.alpha,
}
} }
// A few special-cases ahead. // A few special-cases ahead.
match **declaration { match **declaration {
PropertyDeclaration::BackgroundColor(ref color) => { PropertyDeclaration::BackgroundColor(ref color) => {
// We honor system colors.
if color.is_system() {
return;
}
// For background-color, we revert or initial-with-preserved-alpha // For background-color, we revert or initial-with-preserved-alpha
// otherwise, this is needed to preserve semi-transparent // otherwise, this is needed to preserve semi-transparent
// backgrounds. // backgrounds.
@ -440,24 +428,27 @@ fn tweak_when_ignoring_colors(
// should consider not doing that even if it causes some issues like // should consider not doing that even if it causes some issues like
// bug 1625036, or finding a performant way to preserve the original // bug 1625036, or finding a performant way to preserve the original
// widget background color's rgb channels but not alpha... // widget background color's rgb channels but not alpha...
let alpha = alpha_channel(color); let alpha = alpha_channel(color, context);
if alpha != 0 { if alpha != 0 {
let mut color = builder.device.default_background_color(); let mut color = context.builder.device.default_background_color();
color.alpha = alpha; color.alpha = alpha;
declarations_to_apply_unless_overriden declarations_to_apply_unless_overriden
.push(PropertyDeclaration::BackgroundColor(color.into())) .push(PropertyDeclaration::BackgroundColor(color.into()))
} }
}, },
PropertyDeclaration::Color(ref color) => { PropertyDeclaration::Color(ref color) => {
// We honor color: transparent, and "revert-or-initial" otherwise. // We honor color: transparent and system colors.
if alpha_channel(&color.0) == 0 { if color.0.is_system() {
return;
}
if alpha_channel(&color.0, context) == 0 {
return; return;
} }
// If the inherited color would be transparent, but we would // If the inherited color would be transparent, but we would
// override this with a non-transparent color, then override it with // override this with a non-transparent color, then override it with
// the default color. Otherwise just let it inherit through. // the default color. Otherwise just let it inherit through.
if builder.get_parent_inherited_text().clone_color().alpha == 0 { if context.builder.get_parent_inherited_text().clone_color().alpha == 0 {
let color = builder.device.default_color(); let color = context.builder.device.default_color();
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color( declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
specified::ColorPropertyValue(color.into()), specified::ColorPropertyValue(color.into()),
)) ))
@ -631,7 +622,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// properties that are marked as ignored in that mode. // properties that are marked as ignored in that mode.
if ignore_colors { if ignore_colors {
tweak_when_ignoring_colors( tweak_when_ignoring_colors(
&self.context.builder, &self.context,
longhand_id, longhand_id,
origin, origin,
&mut declaration, &mut declaration,

View file

@ -19,7 +19,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
"text-size-adjust", "text-size-adjust",
"-webkit-font-feature-settings", "-webkit-font-feature-settings",
"-webkit-user-drag", "-webkit-user-drag",
"size",
"-webkit-clip-path", "-webkit-clip-path",
"orphans", "orphans",
"widows", "widows",

View file

@ -261,6 +261,11 @@ class Property(object):
self.extra_prefixes = parse_property_aliases(extra_prefixes) self.extra_prefixes = parse_property_aliases(extra_prefixes)
self.flags = flags.split() if flags else [] self.flags = flags.split() if flags else []
def rule_types_allowed_names(self):
for name in RULE_VALUES:
if self.rule_types_allowed & RULE_VALUES[name] != 0:
yield name
def experimental(self, engine): def experimental(self, engine):
if engine == "gecko": if engine == "gecko":
return bool(self.gecko_pref) return bool(self.gecko_pref)
@ -478,6 +483,7 @@ class Longhand(Property):
"LineBreak", "LineBreak",
"MasonryAutoFlow", "MasonryAutoFlow",
"MozForceBrokenImageIcon", "MozForceBrokenImageIcon",
"text::MozControlCharacterVisibility",
"MozListReversed", "MozListReversed",
"MathDepth", "MathDepth",
"MozScriptMinSize", "MozScriptMinSize",
@ -495,6 +501,7 @@ class Longhand(Property):
"Percentage", "Percentage",
"PositiveIntegerOrNone", "PositiveIntegerOrNone",
"Resize", "Resize",
"RubyPosition",
"SVGOpacity", "SVGOpacity",
"SVGPaintOrder", "SVGPaintOrder",
"ScrollSnapAlign", "ScrollSnapAlign",
@ -505,6 +512,7 @@ class Longhand(Property):
"TextAlignLast", "TextAlignLast",
"TextDecorationLine", "TextDecorationLine",
"TextEmphasisPosition", "TextEmphasisPosition",
"TextJustify",
"TextTransform", "TextTransform",
"TextUnderlinePosition", "TextUnderlinePosition",
"TouchAction", "TouchAction",
@ -597,6 +605,11 @@ class Alias(object):
def type(): def type():
return "alias" return "alias"
def rule_types_allowed_names(self):
for name in RULE_VALUES:
if self.rule_types_allowed & RULE_VALUES[name] != 0:
yield name
def experimental(self, engine): def experimental(self, engine):
if engine == "gecko": if engine == "gecko":
return bool(self.gecko_pref) return bool(self.gecko_pref)
@ -866,6 +879,7 @@ class PropertyRestrictions:
def marker(data): def marker(data):
return set( return set(
[ [
"white-space",
"color", "color",
"text-combine-upright", "text-combine-upright",
"text-transform", "text-transform",

View file

@ -781,6 +781,9 @@ fn static_assert() {
% endfor % endfor
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="Page">
</%self:impl_trait>
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %> <% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
<%self:impl_trait style_struct_name="Position" <%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands} skip_longhands="${skip_position_longhands}

View file

@ -554,7 +554,7 @@
keyword = keyword=Keyword(name, values, **keyword_kwargs) keyword = keyword=Keyword(name, values, **keyword_kwargs)
%> %>
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
use crate::properties::longhands::system_font::SystemFont; use crate::values::specified::font::SystemFont;
pub mod computed_value { pub mod computed_value {
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
@ -707,8 +707,7 @@
</%def> </%def>
<%def name="single_keyword(name, values, vector=False, <%def name="single_keyword(name, values, vector=False,
extra_specified=None, needs_conversion=False, needs_conversion=False, **kwargs)">
gecko_pref_controlled_initial_value=None, **kwargs)">
<% <%
keyword_kwargs = {a: kwargs.pop(a, None) for a in [ keyword_kwargs = {a: kwargs.pop(a, None) for a in [
'gecko_constant_prefix', 'gecko_constant_prefix',
@ -725,11 +724,13 @@
]} ]}
%> %>
<%def name="inner_body(keyword, extra_specified=None, needs_conversion=False, <%def name="inner_body(keyword, needs_conversion=False)">
gecko_pref_controlled_initial_value=None)"> pub use self::computed_value::T as SpecifiedValue;
<%def name="variants(variants, include_aliases)"> pub mod computed_value {
% for variant in variants: #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
% if include_aliases: #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
pub enum T {
% for variant in keyword.values_for(engine):
<% <%
aliases = [] aliases = []
for alias, v in keyword.aliases_for(engine).items(): for alias, v in keyword.aliases_for(engine).items():
@ -739,56 +740,16 @@
% if aliases: % if aliases:
#[parse(aliases = "${','.join(sorted(aliases))}")] #[parse(aliases = "${','.join(sorted(aliases))}")]
% endif % endif
% endif
${to_camel_case(variant)}, ${to_camel_case(variant)},
% endfor % endfor
</%def>
% if extra_specified:
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToShmem,
)]
pub enum SpecifiedValue {
${variants(keyword.values_for(engine) + extra_specified.split(), bool(extra_specified))}
}
% else:
pub use self::computed_value::T as SpecifiedValue;
% endif
pub mod computed_value {
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
% if not extra_specified:
#[derive(Parse, SpecifiedValueInfo, ToComputedValue, ToShmem)]
% endif
pub enum T {
${variants(data.longhands_by_name[name].keyword.values_for(engine), not extra_specified)}
} }
} }
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
% if engine == "gecko" and gecko_pref_controlled_initial_value:
if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") {
return computed_value::T::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])};
}
% endif
computed_value::T::${to_camel_case(values.split()[0])} computed_value::T::${to_camel_case(values.split()[0])}
} }
#[inline] #[inline]
pub fn get_initial_specified_value() -> SpecifiedValue { pub fn get_initial_specified_value() -> SpecifiedValue {
% if engine == "gecko" and gecko_pref_controlled_initial_value:
if static_prefs::pref!("${gecko_pref_controlled_initial_value.split('=')[0]}") {
return SpecifiedValue::${to_camel_case(gecko_pref_controlled_initial_value.split('=')[1])};
}
% endif
SpecifiedValue::${to_camel_case(values.split()[0])} SpecifiedValue::${to_camel_case(values.split()[0])}
} }
#[inline] #[inline]
@ -799,10 +760,7 @@
% if needs_conversion: % if needs_conversion:
<% <%
conversion_values = keyword.values_for(engine) conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys())
if extra_specified:
conversion_values += extra_specified.split()
conversion_values += keyword.aliases_for(engine).keys()
%> %>
${gecko_keyword_conversion(keyword, values=conversion_values)} ${gecko_keyword_conversion(keyword, values=conversion_values)}
% endif % endif
@ -817,8 +775,7 @@
% else: % else:
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
${inner_body(Keyword(name, values, **keyword_kwargs), ${inner_body(Keyword(name, values, **keyword_kwargs),
extra_specified=extra_specified, needs_conversion=needs_conversion, needs_conversion=needs_conversion)}
gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)}
% if caller: % if caller:
${caller.body()} ${caller.body()}
% endif % endif

View file

@ -48,7 +48,6 @@ ${helpers.single_keyword(
engines="gecko servo-2013 servo-2020" engines="gecko servo-2013 servo-2020"
animation_value_type="discrete" animation_value_type="discrete"
gecko_enum_prefix="StylePositionProperty" gecko_enum_prefix="StylePositionProperty"
flags="CREATES_STACKING_CONTEXT ABSPOS_CB"
spec="https://drafts.csswg.org/css-position/#position-property" spec="https://drafts.csswg.org/css-position/#position-property"
servo_restyle_damage="rebuild_and_reflow" servo_restyle_damage="rebuild_and_reflow"
> >
@ -330,7 +329,7 @@ ${helpers.predefined_type(
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
extra_prefixes=transform_extra_prefixes, extra_prefixes=transform_extra_prefixes,
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.csswg.org/css-transforms/#propdef-transform", spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
servo_restyle_damage="reflow_out_of_flow", servo_restyle_damage="reflow_out_of_flow",
)} )}
@ -342,7 +341,7 @@ ${helpers.predefined_type(
engines="gecko servo-2013", engines="gecko servo-2013",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
boxed=True, boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled", gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage = "reflow_out_of_flow", servo_restyle_damage = "reflow_out_of_flow",
@ -355,7 +354,7 @@ ${helpers.predefined_type(
engines="gecko servo-2013", engines="gecko servo-2013",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
boxed=True, boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled", gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage = "reflow_out_of_flow", servo_restyle_damage = "reflow_out_of_flow",
@ -368,7 +367,7 @@ ${helpers.predefined_type(
engines="gecko servo-2013", engines="gecko servo-2013",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
boxed=True, boxed=True,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
gecko_pref="layout.css.individual-transform.enabled", gecko_pref="layout.css.individual-transform.enabled",
spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
servo_restyle_damage="reflow_out_of_flow", servo_restyle_damage="reflow_out_of_flow",
@ -382,7 +381,7 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
gecko_pref="layout.css.motion-path.enabled", gecko_pref="layout.css.motion-path.enabled",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.fxtf.org/motion-1/#offset-path-property", spec="https://drafts.fxtf.org/motion-1/#offset-path-property",
servo_restyle_damage="reflow_out_of_flow" servo_restyle_damage="reflow_out_of_flow"
)} )}
@ -477,7 +476,6 @@ ${helpers.single_keyword(
"auto isolate", "auto isolate",
engines="gecko", engines="gecko",
spec="https://drafts.fxtf.org/compositing/#isolation", spec="https://drafts.fxtf.org/compositing/#isolation",
flags="CREATES_STACKING_CONTEXT",
gecko_enum_prefix="StyleIsolation", gecko_enum_prefix="StyleIsolation",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
@ -530,7 +528,6 @@ ${helpers.predefined_type(
gecko_ffi_name="mChildPerspective", gecko_ffi_name="mChildPerspective",
spec="https://drafts.csswg.org/css-transforms/#perspective", spec="https://drafts.csswg.org/css-transforms/#perspective",
extra_prefixes=transform_extra_prefixes, extra_prefixes=transform_extra_prefixes,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
animation_value_type="AnimatedPerspective", animation_value_type="AnimatedPerspective",
servo_restyle_damage = "reflow_out_of_flow", servo_restyle_damage = "reflow_out_of_flow",
)} )}
@ -574,7 +571,6 @@ ${helpers.predefined_type(
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property", spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
extra_prefixes=transform_extra_prefixes, extra_prefixes=transform_extra_prefixes,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
animation_value_type="discrete", animation_value_type="discrete",
servo_restyle_damage = "reflow_out_of_flow", servo_restyle_damage = "reflow_out_of_flow",
)} )}
@ -598,7 +594,6 @@ ${helpers.predefined_type(
"specified::Contain::empty()", "specified::Contain::empty()",
engines="gecko", engines="gecko",
animation_value_type="none", animation_value_type="none",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
spec="https://drafts.csswg.org/css-contain/#contain-property", spec="https://drafts.csswg.org/css-contain/#contain-property",
)} )}

View file

@ -13,7 +13,7 @@ ${helpers.predefined_type(
"1.0", "1.0",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
flags="CREATES_STACKING_CONTEXT CAN_ANIMATE_ON_COMPOSITOR", flags="CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.csswg.org/css-color/#transparency", spec="https://drafts.csswg.org/css-color/#transparency",
servo_restyle_damage = "reflow_out_of_flow", servo_restyle_damage = "reflow_out_of_flow",
)} )}
@ -56,7 +56,6 @@ ${helpers.predefined_type(
animation_value_type="AnimatedFilterList", animation_value_type="AnimatedFilterList",
vector_animation_type="with_zero", vector_animation_type="with_zero",
extra_prefixes="webkit", extra_prefixes="webkit",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
spec="https://drafts.fxtf.org/filters/#propdef-filter", spec="https://drafts.fxtf.org/filters/#propdef-filter",
)} )}
@ -71,7 +70,6 @@ ${helpers.predefined_type(
separator="Space", separator="Space",
animation_value_type="AnimatedFilterList", animation_value_type="AnimatedFilterList",
vector_animation_type="with_zero", vector_animation_type="with_zero",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
gecko_pref="layout.css.backdrop-filter.enabled", gecko_pref="layout.css.backdrop-filter.enabled",
spec="https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter", spec="https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter",
)} )}
@ -84,6 +82,5 @@ ${helpers.single_keyword(
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
gecko_enum_prefix="StyleBlend", gecko_enum_prefix="StyleBlend",
animation_value_type="discrete", animation_value_type="discrete",
flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode", spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",
)} )}

View file

@ -288,208 +288,153 @@ ${helpers.predefined_type(
)} )}
% if engine == "gecko": % if engine == "gecko":
pub mod system_font { pub mod system_font {
//! We deal with system fonts here //! We deal with system fonts here
//! //!
//! System fonts can only be set as a group via the font shorthand. //! System fonts can only be set as a group via the font shorthand.
//! They resolve at compute time (not parse time -- this lets the //! They resolve at compute time (not parse time -- this lets the
//! browser respond to changes to the OS font settings). //! browser respond to changes to the OS font settings).
//! //!
//! While Gecko handles these as a separate property and keyword //! While Gecko handles these as a separate property and keyword
//! values on each property indicating that the font should be picked //! values on each property indicating that the font should be picked
//! from the -x-system-font property, we avoid this. Instead, //! from the -x-system-font property, we avoid this. Instead,
//! each font longhand has a special SystemFont variant which contains //! each font longhand has a special SystemFont variant which contains
//! the specified system font. When the cascade function (in helpers) //! the specified system font. When the cascade function (in helpers)
//! detects that a value has a system font, it will resolve it, and //! detects that a value has a system font, it will resolve it, and
//! cache it on the ComputedValues. After this, it can be just fetched //! cache it on the ComputedValues. After this, it can be just fetched
//! whenever a font longhand on the same element needs the system font. //! whenever a font longhand on the same element needs the system font.
//! //!
//! When a longhand property is holding a SystemFont, it's serialized //! When a longhand property is holding a SystemFont, it's serialized
//! to an empty string as if its value comes from a shorthand with //! to an empty string as if its value comes from a shorthand with
//! variable reference. We may want to improve this behavior at some //! variable reference. We may want to improve this behavior at some
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586. //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
use cssparser::{Parser, ToCss}; use crate::values::computed::font::GenericFontFamily;
use crate::values::computed::font::GenericFontFamily; use crate::properties::longhands;
use crate::properties::longhands; use std::hash::{Hash, Hasher};
use std::fmt; use crate::values::computed::{ToComputedValue, Context};
use std::hash::{Hash, Hasher}; use crate::values::specified::font::SystemFont;
use style_traits::ParseError;
use crate::values::computed::{ToComputedValue, Context};
<% <%
system_fonts = """caption icon menu message-box small-caption status-bar kw_font_props = """font_variant_caps
-moz-window -moz-document -moz-workspace -moz-desktop font_kerning font_variant_position font_variant_ligatures
-moz-info -moz-dialog -moz-button -moz-pull-down-menu font_variant_east_asian font_variant_numeric
-moz-list -moz-field""".split() font_optical_sizing""".split()
kw_font_props = """font_variant_caps kw_cast = """font_variant_caps font_kerning font_variant_position
font_kerning font_variant_position font_variant_ligatures font_optical_sizing""".split()
font_variant_east_asian font_variant_numeric %>
font_optical_sizing""".split()
kw_cast = """font_variant_caps font_kerning font_variant_position
font_optical_sizing""".split()
%>
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToCss, ToShmem)]
pub enum SystemFont {
% for font in system_fonts:
${to_camel_case(font)},
% endfor
}
// ComputedValues are compared at times // ComputedValues are compared at times
// so we need these impls. We don't want to // so we need these impls. We don't want to
// add Eq to Number (which contains a float) // add Eq to Number (which contains a float)
// so instead we have an eq impl which skips the // so instead we have an eq impl which skips the
// cached values // cached values
impl PartialEq for ComputedSystemFont { impl PartialEq for ComputedSystemFont {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.system_font == other.system_font self.system_font == other.system_font
}
}
impl Eq for ComputedSystemFont {}
impl Hash for ComputedSystemFont {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.system_font.hash(hasher)
}
}
impl ToComputedValue for SystemFont {
type ComputedValue = ComputedSystemFont;
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
use std::mem;
use crate::values::computed::Percentage;
use crate::values::specified::font::KeywordInfo;
use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
use crate::values::generics::NonNegative;
let id = match *self {
% for font in system_fonts:
SystemFont::${to_camel_case(font)} => {
LookAndFeel_FontID::${to_camel_case(font.replace("-moz-", ""))}
}
% endfor
};
let mut system = mem::MaybeUninit::<nsFont>::uninit();
let system = unsafe {
bindings::Gecko_nsFont_InitSystem(
system.as_mut_ptr(),
id as i32,
cx.style().get_font().gecko(),
cx.device().document()
);
&mut *system.as_mut_ptr()
};
let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
let font_stretch = FontStretch(NonNegative(Percentage(unsafe {
bindings::Gecko_FontStretch_ToFloat(system.stretch)
})));
let font_style = FontStyle::from_gecko(system.style);
let ret = ComputedSystemFont {
font_family: FontFamily {
families: FontFamilyList::SharedFontList(
unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
),
is_system_font: true,
},
font_size: FontSize {
size: NonNegative(cx.maybe_zoom_text(system.size.0)),
keyword_info: KeywordInfo::none()
},
font_weight,
font_stretch,
font_style,
font_size_adjust: longhands::font_size_adjust::computed_value
::T::from_gecko_adjust(system.sizeAdjust),
% for kwprop in kw_font_props:
${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
system.${to_camel_case_lower(kwprop.replace('font_', ''))}
% if kwprop in kw_cast:
as u32
% endif
),
% endfor
font_language_override: longhands::font_language_override::computed_value
::T(system.languageOverride),
font_feature_settings: longhands::font_feature_settings::get_initial_value(),
font_variation_settings: longhands::font_variation_settings::get_initial_value(),
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
system_font: *self,
default_font_type: system.fontlist.mDefaultFontType,
};
unsafe { bindings::Gecko_nsFont_Destroy(system); }
ret
}
fn from_computed_value(_: &ComputedSystemFont) -> Self {
unreachable!()
}
}
#[inline]
/// Compute and cache a system font
///
/// Must be called before attempting to compute a system font
/// specified value
pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
// Checking if context.cached_system_font.is_none() isn't enough,
// if animating from one system font to another the cached system font
// may change
if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
let computed = system.to_computed_value(context);
context.cached_system_font = Some(computed);
}
}
#[derive(Clone, Debug)]
pub struct ComputedSystemFont {
% for name in SYSTEM_FONT_LONGHANDS:
pub ${name}: longhands::${name}::computed_value::T,
% endfor
pub system_font: SystemFont,
pub default_font_type: GenericFontFamily,
}
impl SystemFont {
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
try_match_ident_ignore_ascii_case! { input,
% for font in system_fonts:
"${font}" => Ok(SystemFont::${to_camel_case(font)}),
% endfor
}
}
}
impl ToCss for SystemFont {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// We may want to do something better in the future, see
// w3c/csswg-drafts#1586.
dest.write_str("-moz-use-system-font")
}
} }
} }
% else: impl Eq for ComputedSystemFont {}
pub mod system_font {
use cssparser::Parser;
// We don't parse system fonts, but in the interest of not littering impl Hash for ComputedSystemFont {
// a lot of code with `if engine == "gecko"` conditionals, we have a fn hash<H: Hasher>(&self, hasher: &mut H) {
// dummy system font module that does nothing self.system_font.hash(hasher)
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
/// void enum for system font, can never exist
pub enum SystemFont {}
impl SystemFont {
pub fn parse(_: &mut Parser) -> Result<Self, ()> {
Err(())
}
} }
} }
impl ToComputedValue for SystemFont {
type ComputedValue = ComputedSystemFont;
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::nsFont;
use std::mem;
use crate::values::computed::Percentage;
use crate::values::specified::font::KeywordInfo;
use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
use crate::values::generics::NonNegative;
let mut system = mem::MaybeUninit::<nsFont>::uninit();
let system = unsafe {
bindings::Gecko_nsFont_InitSystem(
system.as_mut_ptr(),
*self,
cx.style().get_font().gecko(),
cx.device().document()
);
&mut *system.as_mut_ptr()
};
let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
let font_stretch = FontStretch(NonNegative(Percentage(unsafe {
bindings::Gecko_FontStretch_ToFloat(system.stretch)
})));
let font_style = FontStyle::from_gecko(system.style);
let ret = ComputedSystemFont {
font_family: FontFamily {
families: FontFamilyList::SharedFontList(
unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
),
is_system_font: true,
},
font_size: FontSize {
size: NonNegative(cx.maybe_zoom_text(system.size.0)),
keyword_info: KeywordInfo::none()
},
font_weight,
font_stretch,
font_style,
font_size_adjust: longhands::font_size_adjust::computed_value
::T::from_gecko_adjust(system.sizeAdjust),
% for kwprop in kw_font_props:
${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
system.${to_camel_case_lower(kwprop.replace('font_', ''))}
% if kwprop in kw_cast:
as u32
% endif
),
% endfor
font_language_override: longhands::font_language_override::computed_value
::T(system.languageOverride),
font_feature_settings: longhands::font_feature_settings::get_initial_value(),
font_variation_settings: longhands::font_variation_settings::get_initial_value(),
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
system_font: *self,
default_font_type: system.fontlist.mDefaultFontType,
};
unsafe { bindings::Gecko_nsFont_Destroy(system); }
ret
}
fn from_computed_value(_: &ComputedSystemFont) -> Self {
unreachable!()
}
}
#[inline]
/// Compute and cache a system font
///
/// Must be called before attempting to compute a system font
/// specified value
pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
// Checking if context.cached_system_font.is_none() isn't enough,
// if animating from one system font to another the cached system font
// may change
if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
let computed = system.to_computed_value(context);
context.cached_system_font = Some(computed);
}
}
#[derive(Clone, Debug)]
pub struct ComputedSystemFont {
% for name in SYSTEM_FONT_LONGHANDS:
pub ${name}: longhands::${name}::computed_value::T,
% endfor
pub system_font: SystemFont,
pub default_font_type: GenericFontFamily,
}
}
% endif % endif
${helpers.single_keyword( ${helpers.single_keyword(

View file

@ -84,8 +84,7 @@ ${helpers.single_keyword(
${helpers.single_keyword( ${helpers.single_keyword(
"image-orientation", "image-orientation",
"none from-image", "from-image none",
gecko_pref_controlled_initial_value="layout.css.image-orientation.initial-from-image=from-image",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleImageOrientation", gecko_enum_prefix="StyleImageOrientation",
animation_value_type="discrete", animation_value_type="discrete",

View file

@ -96,45 +96,16 @@ ${helpers.predefined_type(
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
// TODO(pcwalton): Support `text-justify: distribute`. ${helpers.predefined_type(
<%helpers:single_keyword "text-justify",
name="text-justify" "TextJustify",
values="auto none inter-word" "computed::TextJustify::Auto",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented", servo_2020_pref="layout.2020.unimplemented",
extra_gecko_values="inter-character" animation_value_type="discrete",
extra_specified="${'distribute' if engine == 'gecko' else ''}" spec="https://drafts.csswg.org/css-text/#propdef-text-justify",
gecko_enum_prefix="StyleTextJustify" servo_restyle_damage="rebuild_and_reflow",
animation_value_type="discrete" )}
spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
servo_restyle_damage="rebuild_and_reflow"
>
% if engine == 'gecko':
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
#[inline]
fn to_computed_value(&self, _: &Context) -> computed_value::T {
match *self {
% for value in "Auto None InterCharacter InterWord".split():
SpecifiedValue::${value} => computed_value::T::${value},
% endfor
// https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
SpecifiedValue::Distribute => computed_value::T::InterCharacter,
}
}
#[inline]
fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
match *computed {
% for value in "Auto None InterCharacter InterWord".split():
computed_value::T::${value} => SpecifiedValue::${value},
% endfor
}
}
}
% endif
</%helpers:single_keyword>
${helpers.predefined_type( ${helpers.predefined_type(
"text-align-last", "text-align-last",
@ -329,13 +300,13 @@ ${helpers.single_keyword(
spec="https://drafts.csswg.org/css-ruby/#ruby-align-property", spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"ruby-position", "ruby-position",
"over under", "RubyPosition",
"computed::RubyPosition::AlternateOver",
engines="gecko", engines="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ruby/#ruby-position-property", spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
gecko_enum_prefix="StyleRubyPosition", animation_value_type="discrete",
)} )}
// CSS Writing Modes Module Level 3 // CSS Writing Modes Module Level 3
@ -360,15 +331,13 @@ ${helpers.single_keyword(
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"-moz-control-character-visibility", "-moz-control-character-visibility",
"hidden visible", "text::MozControlCharacterVisibility",
"Default::default()",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleControlCharacterVisibility",
gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible",
animation_value_type="none", animation_value_type="none",
gecko_ffi_name="mControlCharacterVisibility", spec="Nonstandard"
spec="Nonstandard",
)} )}
// text underline offset // text underline offset

View file

@ -73,13 +73,26 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"caret-color", "caret-color",
"color::CaretColor",
"generics::color::CaretColor::auto()",
engines="gecko",
spec="https://drafts.csswg.org/css-ui/#caret-color",
animation_value_type="CaretColor",
boxed=True,
ignored_when_colors_disabled=True,
)}
${helpers.predefined_type(
"accent-color",
"ColorOrAuto", "ColorOrAuto",
"generics::color::ColorOrAuto::Auto", "generics::color::ColorOrAuto::Auto",
engines="gecko", engines="gecko",
spec="https://drafts.csswg.org/css-ui/#caret-color", spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
animation_value_type="AnimatedCaretColor", gecko_pref="layout.css.accent-color.enabled",
animation_value_type="ColorOrAuto",
boxed=True, boxed=True,
ignored_when_colors_disabled=True, ignored_when_colors_disabled=True,
has_effect_on_gecko_scrollbars=False,
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -51,6 +51,7 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
boxed=True, boxed=True,
animation_value_type="BorderCornerRadius", animation_value_type="BorderCornerRadius",
gecko_pref="layout.css.moz-outline-radius.enabled",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)",
)} )}
% endfor % endfor

View file

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import PAGE_RULE %>
<% data.new_style_struct("Page", inherited=False) %>
${helpers.predefined_type(
"size",
"PageSize",
"computed::PageSize::auto()",
engines="gecko",
gecko_pref="layout.css.page-size.enabled",
initial_specified_value="specified::PageSize::auto()",
spec="https://drafts.csswg.org/css-page-3/#page-size-prop",
boxed=True,
animation_value_type="none",
rule_types_allowed=PAGE_RULE,
)}

View file

@ -60,7 +60,6 @@ ${helpers.predefined_type(
"computed::ZIndex::auto()", "computed::ZIndex::auto()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
spec="https://www.w3.org/TR/CSS2/visuren.html#z-index", spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
flags="CREATES_STACKING_CONTEXT",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
)} )}

View file

@ -81,7 +81,6 @@ ${helpers.predefined_type(
"generics::basic_shape::ClipPath::None", "generics::basic_shape::ClipPath::None",
engines="gecko", engines="gecko",
animation_value_type="basic_shape::ClipPath", animation_value_type="basic_shape::ClipPath",
flags="CREATES_STACKING_CONTEXT",
spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)} )}
@ -183,7 +182,6 @@ ${helpers.predefined_type(
vector=True, vector=True,
extra_prefixes="webkit", extra_prefixes="webkit",
animation_value_type="discrete", animation_value_type="discrete",
flags="CREATES_STACKING_CONTEXT",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -53,7 +53,7 @@ ${helpers.single_keyword(
${helpers.single_keyword( ${helpers.single_keyword(
"-moz-window-shadow", "-moz-window-shadow",
"default none menu tooltip sheet", "default none menu tooltip sheet cliprounded",
engines="gecko", engines="gecko",
gecko_ffi_name="mWindowShadow", gecko_ffi_name="mWindowShadow",
gecko_enum_prefix="StyleWindowShadow", gecko_enum_prefix="StyleWindowShadow",

View file

@ -32,7 +32,6 @@ use crate::computed_value_flags::*;
use crate::hash::FxHashMap; use crate::hash::FxHashMap;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::properties::longhands::system_font::SystemFont;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
#[cfg(feature = "servo")] use servo_config::prefs; #[cfg(feature = "servo")] use servo_config::prefs;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
@ -44,6 +43,7 @@ use crate::values::generics::text::LineHeight;
use crate::values::{computed, resolved}; use crate::values::{computed, resolved};
use crate::values::computed::NonNegativeLength; use crate::values::computed::NonNegativeLength;
use crate::values::serialize_atom_name; use crate::values::serialize_atom_name;
use crate::values::specified::font::SystemFont;
use crate::rule_tree::StrongRuleNode; use crate::rule_tree::StrongRuleNode;
use crate::Zero; use crate::Zero;
use crate::str::{CssString, CssStringWriter}; use crate::str::{CssString, CssStringWriter};
@ -1068,28 +1068,20 @@ impl CSSWideKeyword {
bitflags! { bitflags! {
/// A set of flags for properties. /// A set of flags for properties.
pub struct PropertyFlags: u16 { pub struct PropertyFlags: u16 {
/// This property requires a stacking context.
const CREATES_STACKING_CONTEXT = 1 << 0;
/// This property has values that can establish a containing block for
/// fixed positioned and absolutely positioned elements.
const FIXPOS_CB = 1 << 1;
/// This property has values that can establish a containing block for
/// absolutely positioned elements.
const ABSPOS_CB = 1 << 2;
/// This longhand property applies to ::first-letter. /// This longhand property applies to ::first-letter.
const APPLIES_TO_FIRST_LETTER = 1 << 3; const APPLIES_TO_FIRST_LETTER = 1 << 1;
/// This longhand property applies to ::first-line. /// This longhand property applies to ::first-line.
const APPLIES_TO_FIRST_LINE = 1 << 4; const APPLIES_TO_FIRST_LINE = 1 << 2;
/// This longhand property applies to ::placeholder. /// This longhand property applies to ::placeholder.
const APPLIES_TO_PLACEHOLDER = 1 << 5; const APPLIES_TO_PLACEHOLDER = 1 << 3;
/// This longhand property applies to ::cue. /// This longhand property applies to ::cue.
const APPLIES_TO_CUE = 1 << 6; const APPLIES_TO_CUE = 1 << 4;
/// This longhand property applies to ::marker. /// This longhand property applies to ::marker.
const APPLIES_TO_MARKER = 1 << 7; const APPLIES_TO_MARKER = 1 << 5;
/// This property is a legacy shorthand. /// This property is a legacy shorthand.
/// ///
/// https://drafts.csswg.org/css-cascade/#legacy-shorthand /// https://drafts.csswg.org/css-cascade/#legacy-shorthand
const IS_LEGACY_SHORTHAND = 1 << 8; const IS_LEGACY_SHORTHAND = 1 << 6;
/* The following flags are currently not used in Rust code, they /* The following flags are currently not used in Rust code, they
* only need to be listed in corresponding properties so that * only need to be listed in corresponding properties so that
@ -1757,7 +1749,21 @@ impl UnparsedValue {
shorthand_cache.insert((shorthand, longhand), declaration); shorthand_cache.insert((shorthand, longhand), declaration);
} }
Cow::Borrowed(&shorthand_cache[&(shorthand, longhand_id)]) let key = (shorthand, longhand_id);
match shorthand_cache.get(&key) {
Some(decl) => Cow::Borrowed(decl),
None => {
// FIXME: We should always have the key here but it seems
// sometimes we don't, see bug 1696409.
#[cfg(feature = "gecko")]
{
if structs::GECKO_IS_NIGHTLY {
panic!("Expected {:?} to be in the cache but it was not!", key);
}
}
invalid_at_computed_value_time()
}
}
} }
} }

View file

@ -34,11 +34,11 @@
use crate::parser::Parse; use crate::parser::Parse;
use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch}; use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch};
use crate::properties::longhands::font_variant_caps; use crate::properties::longhands::font_variant_caps;
#[cfg(feature = "gecko")]
use crate::properties::longhands::system_font::SystemFont;
use crate::values::specified::text::LineHeight; use crate::values::specified::text::LineHeight;
use crate::values::specified::FontSize; use crate::values::specified::FontSize;
use crate::values::specified::font::{FontStretch, FontStretchKeyword}; use crate::values::specified::font::{FontStretch, FontStretchKeyword};
#[cfg(feature = "gecko")]
use crate::values::specified::font::SystemFont;
<% <%
gecko_sub_properties = "kerning language_override size_adjust \ gecko_sub_properties = "kerning language_override size_adjust \
@ -197,7 +197,7 @@
let font_stretch = match *self.font_stretch { let font_stretch = match *self.font_stretch {
FontStretch::Keyword(kw) => kw, FontStretch::Keyword(kw) => kw,
FontStretch::Stretch(percentage) => { FontStretch::Stretch(percentage) => {
match FontStretchKeyword::from_percentage(percentage.get()) { match FontStretchKeyword::from_percentage(percentage.0.get()) {
Some(kw) => kw, Some(kw) => kw,
None => return Ok(()), None => return Ok(()),
} }
@ -289,7 +289,9 @@
% for p in subprops_for_value_info: % for p in subprops_for_value_info:
${p}::collect_completion_keywords(f); ${p}::collect_completion_keywords(f);
% endfor % endfor
<longhands::system_font::SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f); % if engine == "gecko":
<SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
% endif
} }
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -83,6 +83,7 @@
<%helpers:shorthand <%helpers:shorthand
name="-moz-outline-radius" name="-moz-outline-radius"
engines="gecko" engines="gecko"
gecko_pref="layout.css.moz-outline-radius.enabled"
sub_properties="${' '.join( sub_properties="${' '.join(
'-moz-outline-radius-%s' % corner '-moz-outline-radius-%s' % corner
for corner in ['topleft', 'topright', 'bottomright', 'bottomleft'] for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']

View file

@ -94,7 +94,7 @@ pub trait SelectorMapEntry: Sized + Clone {
/// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755 /// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755
/// ///
/// TODO: Tune the initial capacity of the HashMap /// TODO: Tune the initial capacity of the HashMap
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct SelectorMap<T: 'static> { pub struct SelectorMap<T: 'static> {
/// Rules that have `:root` selectors. /// Rules that have `:root` selectors.
pub root: SmallVec<[T; 1]>, pub root: SmallVec<[T; 1]>,
@ -615,7 +615,7 @@ fn find_bucket<'a>(
} }
/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode. /// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>( pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>(
PrecomputedHashMap<K, V>, PrecomputedHashMap<K, V>,
); );

View file

@ -108,7 +108,7 @@ pub enum PseudoElementCascadeType {
} }
/// A per-pseudo map, from a given pseudo to a `T`. /// A per-pseudo map, from a given pseudo to a `T`.
#[derive(MallocSizeOf)] #[derive(Clone, MallocSizeOf)]
pub struct PerPseudoElementMap<T> { pub struct PerPseudoElementMap<T> {
entries: [Option<T>; PSEUDO_COUNT], entries: [Option<T>; PSEUDO_COUNT],
} }

View file

@ -19,6 +19,7 @@ use app_units::Au;
use cssparser::RGBA; use cssparser::RGBA;
use euclid::default::Size2D as UntypedSize2D; use euclid::default::Size2D as UntypedSize2D;
use euclid::{Scale, SideOffsets2D, Size2D}; use euclid::{Scale, SideOffsets2D, Size2D};
use mime::Mime;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use style_traits::viewport::ViewportConstraints; use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel}; use style_traits::{CSSPixel, DevicePixel};
@ -202,6 +203,23 @@ impl Device {
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> { pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
SideOffsets2D::zero() SideOffsets2D::zero()
} }
/// Returns true if the given MIME type is supported
pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
match mime_type.parse::<Mime>() {
Ok(m) => {
// Keep this in sync with 'image_classifer' from
// components/net/mime_classifier.rs
m == mime::IMAGE_BMP
|| m == mime::IMAGE_GIF
|| m == mime::IMAGE_PNG
|| m == mime::IMAGE_JPEG
|| m == "image/x-icon"
|| m == "image/webp"
}
_ => false,
}
}
} }
/// https://drafts.csswg.org/mediaqueries-4/#width /// https://drafts.csswg.org/mediaqueries-4/#width

View file

@ -372,7 +372,7 @@ impl NonTSPseudoClass {
Disabled => ElementState::IN_DISABLED_STATE, Disabled => ElementState::IN_DISABLED_STATE,
Checked => ElementState::IN_CHECKED_STATE, Checked => ElementState::IN_CHECKED_STATE,
Indeterminate => ElementState::IN_INDETERMINATE_STATE, Indeterminate => ElementState::IN_INDETERMINATE_STATE,
ReadOnly | ReadWrite => ElementState::IN_READ_WRITE_STATE, ReadOnly | ReadWrite => ElementState::IN_READWRITE_STATE,
PlaceholderShown => ElementState::IN_PLACEHOLDER_SHOWN_STATE, PlaceholderShown => ElementState::IN_PLACEHOLDER_SHOWN_STATE,
Target => ElementState::IN_TARGET_STATE, Target => ElementState::IN_TARGET_STATE,

View file

@ -184,7 +184,9 @@ pub struct SheetCollectionFlusher<'a, S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
iter: slice::IterMut<'a, StylesheetSetEntry<S>>, // TODO: This can be made an iterator again once
// https://github.com/rust-lang/rust/pull/82771 lands on stable.
entries: &'a mut [StylesheetSetEntry<S>],
validity: DataValidity, validity: DataValidity,
dirty: bool, dirty: bool,
} }
@ -204,32 +206,42 @@ where
pub fn data_validity(&self) -> DataValidity { pub fn data_validity(&self) -> DataValidity {
self.validity self.validity
} }
/// Returns an iterator over the remaining list of sheets to consume.
pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
self.entries.iter().map(|entry| &entry.sheet)
}
} }
impl<'a, S> Iterator for SheetCollectionFlusher<'a, S> impl<'a, S> SheetCollectionFlusher<'a, S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
type Item = (&'a S, SheetRebuildKind); /// Iterates over all sheets and values that we have to invalidate.
///
fn next(&mut self) -> Option<Self::Item> { /// TODO(emilio): This would be nicer as an iterator but we can't do that
loop { /// until https://github.com/rust-lang/rust/pull/82771 stabilizes.
let potential_sheet = self.iter.next()?; ///
/// Since we don't have a good use-case for partial iteration, this does the
/// trick for now.
pub fn each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool) {
for potential_sheet in self.entries.iter_mut() {
let committed = mem::replace(&mut potential_sheet.committed, true); let committed = mem::replace(&mut potential_sheet.committed, true);
if !committed { let rebuild_kind = if !committed {
// If the sheet was uncommitted, we need to do a full rebuild // If the sheet was uncommitted, we need to do a full rebuild
// anyway. // anyway.
return Some((&potential_sheet.sheet, SheetRebuildKind::Full)); SheetRebuildKind::Full
} } else {
match self.validity {
let rebuild_kind = match self.validity { DataValidity::Valid => continue,
DataValidity::Valid => continue, DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly, DataValidity::FullyInvalid => SheetRebuildKind::Full,
DataValidity::FullyInvalid => SheetRebuildKind::Full, }
}; };
return Some((&potential_sheet.sheet, rebuild_kind)); if !callback(&potential_sheet.sheet, rebuild_kind) {
return;
}
} }
} }
} }
@ -357,7 +369,7 @@ where
let validity = mem::replace(&mut self.data_validity, DataValidity::Valid); let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
SheetCollectionFlusher { SheetCollectionFlusher {
iter: self.entries.iter_mut(), entries: &mut self.entries,
dirty, dirty,
validity, validity,
} }
@ -408,7 +420,7 @@ macro_rules! sheet_set_methods {
) { ) {
debug!(concat!($set_name, "::append_stylesheet")); debug!(concat!($set_name, "::append_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.append(sheet); collection.append(sheet);
} }
@ -423,7 +435,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::insert_stylesheet_before")); debug!(concat!($set_name, "::insert_stylesheet_before"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.insert_before(sheet, &before_sheet); collection.insert_before(sheet, &before_sheet);
} }
@ -437,7 +449,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::remove_stylesheet")); debug!(concat!($set_name, "::remove_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.remove(&sheet) collection.remove(&sheet)
} }
@ -487,7 +499,7 @@ macro_rules! sheet_set_methods {
RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid, RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
}; };
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.set_data_validity_at_least(validity); collection.set_data_validity_at_least(validity);
} }
}; };
@ -505,12 +517,8 @@ where
} }
} }
fn collection_for( fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
&mut self, let origin = sheet.contents().origin;
sheet: &S,
guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
let origin = sheet.origin(guard);
self.collections.borrow_mut_for_origin(&origin) self.collections.borrow_mut_for_origin(&origin)
} }
@ -658,11 +666,7 @@ where
self.collection.len() self.collection.len()
} }
fn collection_for( fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
&mut self,
_sheet: &S,
_guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
&mut self.collection &mut self.collection
} }

View file

@ -6,12 +6,11 @@
//! //!
//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import //! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
use crate::context::QuirksMode;
use crate::media_queries::MediaList; use crate::media_queries::MediaList;
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock}; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter; use crate::str::CssStringWriter;
use crate::stylesheets::{CssRule, Origin, StylesheetInDocument}; use crate::stylesheets::{CssRule, StylesheetInDocument};
use crate::values::CssUrl; use crate::values::CssUrl;
use cssparser::SourceLocation; use cssparser::SourceLocation;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -61,6 +60,19 @@ impl ImportSheet {
ImportSheet::Pending(_) => None, ImportSheet::Pending(_) => None,
} }
} }
/// Returns the media list for this import rule.
pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.as_sheet().and_then(|s| s.media(guard))
}
/// Returns the rule list for this import rule.
pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
match self.as_sheet() {
Some(s) => s.rules(guard),
None => &[],
}
}
} }
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -85,68 +97,20 @@ impl DeepCloneWithLock for ImportSheet {
} }
} }
#[cfg(feature = "gecko")]
impl StylesheetInDocument for ImportSheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
match *self {
ImportSheet::Sheet(ref s) => s.contents().origin,
ImportSheet::Pending(ref p) => p.origin,
}
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
match *self {
ImportSheet::Sheet(ref s) => s.contents().quirks_mode,
ImportSheet::Pending(ref p) => p.quirks_mode,
}
}
fn enabled(&self) -> bool {
match *self {
ImportSheet::Sheet(ref s) => s.enabled(),
ImportSheet::Pending(_) => true,
}
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
match *self {
ImportSheet::Sheet(ref s) => s.media(guard),
ImportSheet::Pending(_) => None,
}
}
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
match *self {
ImportSheet::Sheet(ref s) => s.contents().rules(guard),
ImportSheet::Pending(_) => &[],
}
}
}
/// A sheet that is held from an import rule. /// A sheet that is held from an import rule.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
#[derive(Debug)] #[derive(Debug)]
pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>); pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>);
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
impl StylesheetInDocument for ImportSheet { impl ImportSheet {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin { /// Returns the media list for this import rule.
self.0.origin(guard) pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.0.quirks_mode(guard)
}
fn enabled(&self) -> bool {
self.0.enabled()
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.0.media(guard) self.0.media(guard)
} }
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { /// Returns the rules for this import rule.
pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
self.0.rules(guard) self.0.rules(guard)
} }
} }

View file

@ -53,7 +53,7 @@ pub struct TopLevelRuleParser<'a> {
/// This won't contain any namespaces, and only nested parsers created with /// This won't contain any namespaces, and only nested parsers created with
/// `ParserContext::new_with_rule_type` will. /// `ParserContext::new_with_rule_type` will.
pub context: ParserContext<'a>, pub context: ParserContext<'a>,
/// The current stajkj/te of the parser. /// The current state of the parser.
pub state: State, pub state: State,
/// Whether we have tried to parse was invalid due to being in the wrong /// Whether we have tried to parse was invalid due to being in the wrong
/// place (e.g. an @import rule was found while in the `Body` state). Reset /// place (e.g. an @import rule was found while in the `Body` state). Reset
@ -587,6 +587,38 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
} }
} }
#[inline(never)]
fn check_for_useless_selector(
input: &mut Parser,
context: &ParserContext,
selectors: &SelectorList<SelectorImpl>,
) {
use cssparser::ToCss;
'selector_loop: for selector in selectors.0.iter() {
let mut current = selector.iter();
loop {
let mut found_host = false;
let mut found_non_host = false;
for component in &mut current {
if component.is_host() {
found_host = true;
} else {
found_non_host = true;
}
if found_host && found_non_host {
let location = input.current_source_location();
context.log_css_error(location, ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()));
continue 'selector_loop;
}
}
if current.next_sequence().is_none() {
break;
}
}
}
}
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
type Prelude = SelectorList<SelectorImpl>; type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule; type QualifiedRule = CssRule;
@ -601,7 +633,11 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
namespaces: self.namespaces, namespaces: self.namespaces,
url_data: Some(self.context.url_data), url_data: Some(self.context.url_data),
}; };
SelectorList::parse(&selector_parser, input) let selectors = SelectorList::parse(&selector_parser, input)?;
if self.context.error_reporting_enabled() {
check_for_useless_selector(input, &self.context, &selectors);
}
Ok(selectors)
} }
fn parse_block<'t>( fn parse_block<'t>(

View file

@ -7,7 +7,6 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::StylesheetInDocument;
use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule}; use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::slice; use std::slice;
@ -227,10 +226,13 @@ impl NestedRuleIterationCondition for EffectiveRules {
fn process_import( fn process_import(
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
device: &Device, device: &Device,
_quirks_mode: QuirksMode, quirks_mode: QuirksMode,
rule: &ImportRule, rule: &ImportRule,
) -> bool { ) -> bool {
rule.stylesheet.is_effective_for_device(device, guard) match rule.stylesheet.media(guard) {
Some(m) => m.evaluate(device, quirks_mode),
None => true,
}
} }
fn process_media( fn process_media(

View file

@ -4,7 +4,6 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use crate::media_queries::{Device, MediaList}; use crate::media_queries::{Device, MediaList};
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
@ -102,10 +101,10 @@ impl StylesheetContents {
Self { Self {
rules: CssRules::new(rules, &shared_lock), rules: CssRules::new(rules, &shared_lock),
origin: origin, origin,
url_data: RwLock::new(url_data), url_data: RwLock::new(url_data),
namespaces: namespaces, namespaces,
quirks_mode: quirks_mode, quirks_mode,
source_map_url: RwLock::new(source_map_url), source_map_url: RwLock::new(source_map_url),
source_url: RwLock::new(source_url), source_url: RwLock::new(source_url),
} }
@ -218,12 +217,6 @@ macro_rules! rule_filter {
/// A trait to represent a given stylesheet in a document. /// A trait to represent a given stylesheet in a document.
pub trait StylesheetInDocument: ::std::fmt::Debug { pub trait StylesheetInDocument: ::std::fmt::Debug {
/// Get the stylesheet origin.
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin;
/// Get the stylesheet quirks mode.
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode;
/// Get whether this stylesheet is enabled. /// Get whether this stylesheet is enabled.
fn enabled(&self) -> bool; fn enabled(&self) -> bool;
@ -231,7 +224,12 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>; fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
/// Returns a reference to the list of rules in this stylesheet. /// Returns a reference to the list of rules in this stylesheet.
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule]; fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.contents().rules(guard)
}
/// Returns a reference to the contents of the stylesheet.
fn contents(&self) -> &StylesheetContents;
/// Return an iterator using the condition `C`. /// Return an iterator using the condition `C`.
#[inline] #[inline]
@ -243,18 +241,19 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
where where
C: NestedRuleIterationCondition, C: NestedRuleIterationCondition,
{ {
let contents = self.contents();
RulesIterator::new( RulesIterator::new(
device, device,
self.quirks_mode(guard), contents.quirks_mode,
guard, guard,
self.rules(guard).iter(), contents.rules(guard).iter(),
) )
} }
/// Returns whether the style-sheet applies for the current device. /// Returns whether the style-sheet applies for the current device.
fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool { fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
match self.media(guard) { match self.media(guard) {
Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)), Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
None => true, None => true,
} }
} }
@ -285,14 +284,6 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
} }
impl StylesheetInDocument for Stylesheet { impl StylesheetInDocument for Stylesheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
self.contents.origin
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
self.contents.quirks_mode
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
Some(self.media.read_with(guard)) Some(self.media.read_with(guard))
} }
@ -302,8 +293,8 @@ impl StylesheetInDocument for Stylesheet {
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.contents.rules(guard) &self.contents
} }
} }
@ -321,21 +312,7 @@ impl PartialEq for DocumentStyleSheet {
} }
} }
impl ToMediaListKey for DocumentStyleSheet {
fn to_media_list_key(&self) -> MediaListKey {
self.0.to_media_list_key()
}
}
impl StylesheetInDocument for DocumentStyleSheet { impl StylesheetInDocument for DocumentStyleSheet {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.0.origin(guard)
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.0.quirks_mode(guard)
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.0.media(guard) self.0.media(guard)
} }
@ -345,8 +322,8 @@ impl StylesheetInDocument for DocumentStyleSheet {
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.0.rules(guard) self.0.contents()
} }
} }

View file

@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
use crate::invalidation::element::invalidation_map::InvalidationMap; use crate::invalidation::element::invalidation_map::InvalidationMap;
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey}; use crate::invalidation::media_queries::EffectiveMediaQueryResults;
use crate::invalidation::stylesheets::RuleChangeKind; use crate::invalidation::stylesheets::RuleChangeKind;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::properties::{self, CascadeMode, ComputedValues}; use crate::properties::{self, CascadeMode, ComputedValues};
@ -60,17 +60,33 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet; pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
lazy_static! { trait CascadeDataCacheEntry : Sized {
/// A cache of computed user-agent data, to be shared across documents. /// Returns a reference to the cascade data.
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> = fn cascade_data(&self) -> &CascadeData;
Mutex::new(UserAgentCascadeDataCache::new()); /// Rebuilds the cascade data for the new stylesheet collection. The
/// collection is guaranteed to be dirty.
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
old_entry: &Self,
) -> Result<Arc<Self>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static;
/// Measures heap memory usage.
#[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);
} }
struct UserAgentCascadeDataCache { struct CascadeDataCache<Entry> {
entries: Vec<Arc<UserAgentCascadeData>>, entries: Vec<Arc<Entry>>,
} }
impl UserAgentCascadeDataCache { impl<Entry> CascadeDataCache<Entry>
where
Entry: CascadeDataCacheEntry,
{
fn new() -> Self { fn new() -> Self {
Self { entries: vec![] } Self { entries: vec![] }
} }
@ -79,53 +95,67 @@ impl UserAgentCascadeDataCache {
self.entries.len() self.entries.len()
} }
// FIXME(emilio): This may need to be keyed on quirks-mode too, though there // FIXME(emilio): This may need to be keyed on quirks-mode too, though for
// aren't class / id selectors on those sheets, usually, so it's probably // UA sheets there aren't class / id selectors on those sheets, usually, so
// ok... // it's probably ok... For the other cache the quirks mode shouldn't differ
fn lookup<'a, I, S>( // so also should be fine.
fn lookup<'a, S>(
&'a mut self, &'a mut self,
sheets: I,
device: &Device, device: &Device,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError> old_entry: &Entry,
) -> Result<Option<Arc<Entry>>, FailedAllocationError>
where where
I: Iterator<Item = &'a S> + Clone, S: StylesheetInDocument + PartialEq + 'static,
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
{ {
debug!("StyleSheetCache::lookup({})", self.len());
if !collection.dirty() {
return Ok(None);
}
let mut key = EffectiveMediaQueryResults::new(); let mut key = EffectiveMediaQueryResults::new();
debug!("UserAgentCascadeDataCache::lookup({:?})", device); for sheet in collection.sheets() {
for sheet in sheets.clone() {
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key) CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
} }
for entry in &self.entries { for entry in &self.entries {
if entry.cascade_data.effective_media_query_results == key { if std::ptr::eq(&**entry, old_entry) {
return Ok(entry.clone()); // Avoid reusing our old entry (this can happen if we get
// invalidated due to CSSOM mutations and our old stylesheet
// contents were already unique, for example). This old entry
// will be pruned from the cache with take_unused() afterwards.
continue;
} }
if entry.cascade_data().effective_media_query_results != key {
continue;
}
if log_enabled!(log::Level::Debug) {
debug!("cache hit for:");
for sheet in collection.sheets() {
debug!(" > {:?}", sheet);
}
}
// The line below ensures the "committed" bit is updated properly
// below.
collection.each(|_, _| true);
return Ok(Some(entry.clone()));
} }
let mut new_data = UserAgentCascadeData {
cascade_data: CascadeData::new(),
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
};
debug!("> Picking the slow path"); debug!("> Picking the slow path");
for sheet in sheets { let new_entry = Entry::rebuild(
new_data.cascade_data.add_stylesheet( device,
device, quirks_mode,
quirks_mode, collection,
sheet, guard,
guard, old_entry,
SheetRebuildKind::Full, )?;
Some(&mut new_data.precomputed_pseudo_element_decls),
)?;
}
let new_data = Arc::new(new_data); self.entries.push(new_entry.clone());
self.entries.push(new_data.clone()); Ok(Some(new_entry))
Ok(new_data)
} }
/// Returns all the cascade datas that are not being used (that is, that are /// Returns all the cascade datas that are not being used (that is, that are
@ -135,7 +165,7 @@ impl UserAgentCascadeDataCache {
/// keep alive some other documents (like the SVG documents kept alive by /// keep alive some other documents (like the SVG documents kept alive by
/// URL references), and thus we don't want to drop them while locking the /// URL references), and thus we don't want to drop them while locking the
/// cache to not deadlock. /// cache to not deadlock.
fn take_unused(&mut self) -> SmallVec<[Arc<UserAgentCascadeData>; 3]> { fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
let mut unused = SmallVec::new(); let mut unused = SmallVec::new();
for i in (0..self.entries.len()).rev() { for i in (0..self.entries.len()).rev() {
// is_unique() returns false for static references, but we never // is_unique() returns false for static references, but we never
@ -148,7 +178,7 @@ impl UserAgentCascadeDataCache {
unused unused
} }
fn take_all(&mut self) -> Vec<Arc<UserAgentCascadeData>> { fn take_all(&mut self) -> Vec<Arc<Entry>> {
mem::replace(&mut self.entries, Vec::new()) mem::replace(&mut self.entries, Vec::new())
} }
@ -173,6 +203,58 @@ pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSet
.add_size_of(ops, sizes); .add_size_of(ops, sizes);
} }
lazy_static! {
/// A cache of computed user-agent data, to be shared across documents.
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
Mutex::new(UserAgentCascadeDataCache::new());
}
impl CascadeDataCacheEntry for UserAgentCascadeData {
fn cascade_data(&self) -> &CascadeData {
&self.cascade_data
}
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
_old: &Self,
) -> Result<Arc<Self>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static
{
// TODO: Maybe we should support incremental rebuilds, though they seem
// uncommon and rebuild() doesn't deal with
// precomputed_pseudo_element_decls for now so...
let mut new_data = Self {
cascade_data: CascadeData::new(),
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
};
for sheet in collection.sheets() {
new_data.cascade_data.add_stylesheet(
device,
quirks_mode,
sheet,
guard,
SheetRebuildKind::Full,
Some(&mut new_data.precomputed_pseudo_element_decls),
)?;
}
Ok(Arc::new(new_data))
}
#[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.cascade_data.add_size_of(ops, sizes);
sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
}
}
type UserAgentCascadeDataCache = CascadeDataCache<UserAgentCascadeData>;
type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>; type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
#[derive(Default)] #[derive(Default)]
@ -188,14 +270,6 @@ struct UserAgentCascadeData {
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations, precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
} }
impl UserAgentCascadeData {
#[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.cascade_data.add_size_of(ops, sizes);
sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
}
}
/// All the computed information for all the stylesheets that apply to the /// All the computed information for all the stylesheets that apply to the
/// document. /// document.
#[derive(Default)] #[derive(Default)]
@ -262,19 +336,29 @@ impl DocumentCascadeData {
guards: &StylesheetGuards, guards: &StylesheetGuards,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
// First do UA sheets. // First do UA sheets.
{ {
if flusher.flush_origin(Origin::UserAgent).dirty() { let origin_flusher = flusher.flush_origin(Origin::UserAgent);
let origin_sheets = flusher.origin_sheets(Origin::UserAgent); // Dirty check is just a minor optimization (no need to grab the
let _unused_cascade_datas = { // lock if nothing has changed).
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); if origin_flusher.dirty() {
self.user_agent = let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?; let new_data = ua_cache.lookup(
debug!("User agent data cache size {:?}", ua_cache.len()); device,
ua_cache.take_unused() quirks_mode,
}; origin_flusher,
guards.ua_or_user,
&self.user_agent,
)?;
if let Some(new_data) = new_data {
self.user_agent = new_data;
}
let _unused_entries = ua_cache.take_unused();
// See the comments in take_unused() as for why the following
// line.
std::mem::drop(ua_cache);
} }
} }
@ -371,6 +455,10 @@ pub struct Stylist {
/// The list of stylesheets. /// The list of stylesheets.
stylesheets: StylistStylesheetSet, stylesheets: StylistStylesheetSet,
/// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM).
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "XXX: how to handle this?")]
author_data_cache: CascadeDataCache<CascadeData>,
/// If true, the quirks-mode stylesheet is applied. /// If true, the quirks-mode stylesheet is applied.
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")] #[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
@ -422,6 +510,7 @@ impl Stylist {
device, device,
quirks_mode, quirks_mode,
stylesheets: StylistStylesheetSet::new(), stylesheets: StylistStylesheetSet::new(),
author_data_cache: CascadeDataCache::new(),
cascade_data: Default::default(), cascade_data: Default::default(),
author_styles_enabled: AuthorStylesEnabled::Yes, author_styles_enabled: AuthorStylesEnabled::Yes,
rule_tree: RuleTree::new(), rule_tree: RuleTree::new(),
@ -447,6 +536,31 @@ impl Stylist {
self.cascade_data.iter_origins() self.cascade_data.iter_origins()
} }
/// Does what the name says, to prevent author_data_cache to grow without
/// bound.
pub fn remove_unique_author_data_cache_entries(&mut self) {
self.author_data_cache.take_unused();
}
/// Rebuilds (if needed) the CascadeData given a sheet collection.
pub fn rebuild_author_data<S>(
&mut self,
old_data: &CascadeData,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
) -> Result<Option<Arc<CascadeData>>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static,
{
self.author_data_cache.lookup(
&self.device,
self.quirks_mode,
collection,
guard,
old_data,
)
}
/// Iterate over the extra data in origin order. /// Iterate over the extra data in origin order.
#[inline] #[inline]
pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator { pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
@ -1355,6 +1469,7 @@ impl Stylist {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.cascade_data.add_size_of(ops, sizes); self.cascade_data.add_size_of(ops, sizes);
self.author_data_cache.add_size_of(ops, sizes);
sizes.mRuleTree += self.rule_tree.size_of(ops); sizes.mRuleTree += self.rule_tree.size_of(ops);
// We may measure other fields in the future if DMD says it's worth it. // We may measure other fields in the future if DMD says it's worth it.
@ -1368,7 +1483,7 @@ impl Stylist {
/// This struct holds data which users of Stylist may want to extract /// This struct holds data which users of Stylist may want to extract
/// from stylesheets which can be done at the same time as updating. /// from stylesheets which can be done at the same time as updating.
#[derive(Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))] #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct ExtraStyleData { pub struct ExtraStyleData {
/// A list of effective font-face rules and their origin. /// A list of effective font-face rules and their origin.
@ -1388,11 +1503,6 @@ pub struct ExtraStyleData {
pub pages: Vec<Arc<Locked<PageRule>>>, pub pages: Vec<Arc<Locked<PageRule>>>,
} }
#[cfg(feature = "gecko")]
unsafe impl Sync for ExtraStyleData {}
#[cfg(feature = "gecko")]
unsafe impl Send for ExtraStyleData {}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl ExtraStyleData { impl ExtraStyleData {
/// Add the given @font-face rule. /// Add the given @font-face rule.
@ -1633,7 +1743,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
} }
/// A set of rules for element and pseudo-elements. /// A set of rules for element and pseudo-elements.
#[derive(Debug, Default, MallocSizeOf)] #[derive(Clone, Debug, Default, MallocSizeOf)]
struct GenericElementAndPseudoRules<Map> { struct GenericElementAndPseudoRules<Map> {
/// Rules from stylesheets at this `CascadeData`'s origin. /// Rules from stylesheets at this `CascadeData`'s origin.
element_map: Map, element_map: Map,
@ -1712,7 +1822,7 @@ impl PartElementAndPseudoRules {
/// ///
/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and /// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
/// `InvalidationData`? That'd make `clear_cascade_data()` clearer. /// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
#[derive(Debug, MallocSizeOf)] #[derive(Debug, Clone, MallocSizeOf)]
pub struct CascadeData { pub struct CascadeData {
/// The data coming from normal style rules that apply to elements at this /// The data coming from normal style rules that apply to elements at this
/// cascade level. /// cascade level.
@ -1822,7 +1932,7 @@ impl CascadeData {
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
if !collection.dirty() { if !collection.dirty() {
return Ok(()); return Ok(());
@ -1836,18 +1946,21 @@ impl CascadeData {
DataValidity::FullyInvalid => self.clear(), DataValidity::FullyInvalid => self.clear(),
} }
for (stylesheet, rebuild_kind) in collection { let mut result = Ok(());
self.add_stylesheet(
collection.each(|stylesheet, rebuild_kind| {
result = self.add_stylesheet(
device, device,
quirks_mode, quirks_mode,
stylesheet, stylesheet,
guard, guard,
rebuild_kind, rebuild_kind,
/* precomputed_pseudo_element_decls = */ None, /* precomputed_pseudo_element_decls = */ None,
)?; );
} result.is_ok()
});
Ok(()) result
} }
/// Returns the invalidation map. /// Returns the invalidation map.
@ -1922,14 +2035,14 @@ impl CascadeData {
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
results: &mut EffectiveMediaQueryResults, results: &mut EffectiveMediaQueryResults,
) where ) where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return; return;
} }
debug!(" + {:?}", stylesheet); debug!(" + {:?}", stylesheet);
results.saw_effective(stylesheet); results.saw_effective(stylesheet.contents());
for rule in stylesheet.effective_rules(device, guard) { for rule in stylesheet.effective_rules(device, guard) {
match *rule { match *rule {
@ -1959,16 +2072,17 @@ impl CascadeData {
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>, mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return Ok(()); return Ok(());
} }
let origin = stylesheet.origin(guard); let contents = stylesheet.contents();
let origin = contents.origin;
if rebuild_kind.should_rebuild_invalidation() { if rebuild_kind.should_rebuild_invalidation() {
self.effective_media_query_results.saw_effective(stylesheet); self.effective_media_query_results.saw_effective(contents);
} }
for rule in stylesheet.effective_rules(device, guard) { for rule in stylesheet.effective_rules(device, guard) {
@ -2143,13 +2257,13 @@ impl CascadeData {
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
) -> bool ) -> bool
where where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules; use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules;
let effective_now = stylesheet.is_effective_for_device(device, guard); let effective_now = stylesheet.is_effective_for_device(device, guard);
let effective_then = self.effective_media_query_results.was_effective(stylesheet); let effective_then = self.effective_media_query_results.was_effective(stylesheet.contents());
if effective_now != effective_then { if effective_now != effective_then {
debug!( debug!(
@ -2184,9 +2298,10 @@ impl CascadeData {
}, },
CssRule::Import(ref lock) => { CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard); let import_rule = lock.read_with(guard);
let effective_now = import_rule let effective_now = match import_rule.stylesheet.media(guard) {
.stylesheet Some(m) => m.evaluate(device, quirks_mode),
.is_effective_for_device(&device, guard); None => true,
};
let effective_then = self let effective_then = self
.effective_media_query_results .effective_media_query_results
.was_effective(import_rule); .was_effective(import_rule);
@ -2258,8 +2373,33 @@ impl CascadeData {
self.selectors_for_cache_revalidation.clear(); self.selectors_for_cache_revalidation.clear();
self.effective_media_query_results.clear(); self.effective_media_query_results.clear();
} }
}
impl CascadeDataCacheEntry for CascadeData {
fn cascade_data(&self) -> &CascadeData {
self
}
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
old: &Self,
) -> Result<Arc<Self>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static
{
debug_assert!(collection.dirty(), "We surely need to do something?");
// If we're doing a full rebuild anyways, don't bother cloning the data.
let mut updatable_entry = match collection.data_validity() {
DataValidity::Valid | DataValidity::CascadeInvalid => old.clone(),
DataValidity::FullyInvalid => Self::new(),
};
updatable_entry.rebuild(device, quirks_mode, collection, guard)?;
Ok(Arc::new(updatable_entry))
}
/// Measures heap usage.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.normal_rules.add_size_of(ops, sizes); self.normal_rules.add_size_of(ops, sizes);

View file

@ -35,17 +35,14 @@ impl RGBA {
#[inline] #[inline]
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self { pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
RGBA { RGBA {
red: red, red,
green: green, green,
blue: blue, blue,
alpha: alpha, alpha,
} }
} }
} }
/// Unlike Animate for computed colors, we don't clamp any component values.
///
/// FIXME(nox): Why do computed colors even implement Animate?
impl Animate for RGBA { impl Animate for RGBA {
#[inline] #[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
@ -57,15 +54,11 @@ impl Animate for RGBA {
} }
alpha = alpha.min(1.); alpha = alpha.min(1.);
let red = let red = (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)?;
(self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha; let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)?;
let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? * let blue = (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)?;
1. / let inv = 1. / alpha;
alpha; Ok(RGBA::new(red * inv, green * inv, blue * inv, alpha))
let blue =
(self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha;
Ok(RGBA::new(red, green, blue, alpha))
} }
} }
@ -97,21 +90,75 @@ pub type Color = GenericColor<RGBA>;
impl Color { impl Color {
fn effective_intermediate_rgba(&self) -> RGBA { fn effective_intermediate_rgba(&self) -> RGBA {
match *self { if self.ratios.bg == 0. {
GenericColor::Numeric(color) => color, return RGBA::transparent();
GenericColor::CurrentColor => RGBA::transparent(), }
GenericColor::Complex { color, ratios } => RGBA {
alpha: color.alpha * ratios.bg, if self.ratios.bg == 1. {
..color.clone() return self.color;
}, }
RGBA {
alpha: self.color.alpha * self.ratios.bg,
..self.color
} }
} }
fn effective_ratios(&self) -> ComplexColorRatios { /// Mix two colors into one.
match *self { pub fn mix(
GenericColor::Numeric(..) => ComplexColorRatios::NUMERIC, left_color: &Color,
GenericColor::CurrentColor => ComplexColorRatios::CURRENT_COLOR, left_weight: f32,
GenericColor::Complex { ratios, .. } => ratios, right_color: &Color,
right_weight: f32,
) -> Self {
let left_bg = left_color.scaled_rgba();
let right_bg = right_color.scaled_rgba();
let alpha = (left_bg.alpha * left_weight +
right_bg.alpha * right_weight)
.min(1.);
let mut fg = 0.;
let mut red = 0.;
let mut green = 0.;
let mut blue = 0.;
let colors = [
(left_color, &left_bg, left_weight),
(right_color, &right_bg, right_weight),
];
for &(color, bg, weight) in &colors {
fg += color.ratios.fg * weight;
red += bg.red * bg.alpha * weight;
green += bg.green * bg.alpha * weight;
blue += bg.blue * bg.alpha * weight;
}
let color = if alpha <= 0. {
RGBA::transparent()
} else {
let inv = 1. / alpha;
RGBA::new(red * inv, green * inv, blue * inv, alpha)
};
Self::new(color, ComplexColorRatios { bg: 1., fg })
}
fn scaled_rgba(&self) -> RGBA {
if self.ratios.bg == 0. {
return RGBA::transparent();
}
if self.ratios.bg == 1. {
return self.color;
}
RGBA {
red: self.color.red * self.ratios.bg,
green: self.color.green * self.ratios.bg,
blue: self.color.blue * self.ratios.bg,
alpha: self.color.alpha * self.ratios.bg,
} }
} }
} }
@ -119,140 +166,140 @@ impl Color {
impl Animate for Color { impl Animate for Color {
#[inline] #[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use self::GenericColor::*; let self_numeric = self.is_numeric();
let other_numeric = other.is_numeric();
// Common cases are interpolating between two numeric colors, if self_numeric && other_numeric {
// two currentcolors, and a numeric color and a currentcolor. return Ok(Self::rgba(self.color.animate(&other.color, procedure)?));
let (this_weight, other_weight) = procedure.weights(); }
Ok(match (*self, *other, procedure) { let self_currentcolor = self.is_currentcolor();
// Any interpolation of currentcolor with currentcolor returns currentcolor. let other_currentcolor = other.is_currentcolor();
(CurrentColor, CurrentColor, Procedure::Interpolate { .. }) => CurrentColor,
// Animating two numeric colors.
(Numeric(c1), Numeric(c2), _) => Numeric(c1.animate(&c2, procedure)?),
// Combinations of numeric color and currentcolor
(CurrentColor, Numeric(color), _) => Self::with_ratios(
color,
ComplexColorRatios {
bg: other_weight as f32,
fg: this_weight as f32,
},
),
(Numeric(color), CurrentColor, _) => Self::with_ratios(
color,
ComplexColorRatios {
bg: this_weight as f32,
fg: other_weight as f32,
},
),
// Any other animation of currentcolor with currentcolor. if self_currentcolor && other_currentcolor {
(CurrentColor, CurrentColor, _) => Self::with_ratios( let (self_weight, other_weight) = procedure.weights();
return Ok(Self::new(
RGBA::transparent(), RGBA::transparent(),
ComplexColorRatios { ComplexColorRatios {
bg: 0., bg: 0.,
fg: (this_weight + other_weight) as f32, fg: (self_weight + other_weight) as f32,
}, },
), ));
}
// Defer to complex calculations // FIXME(emilio): Without these special cases tests fail, looks fairly
_ => { // sketchy!
// Compute the "scaled" contribution for `color`. if (self_currentcolor && other_numeric) || (self_numeric && other_currentcolor) {
fn scaled_rgba(color: &Color) -> RGBA { let (self_weight, other_weight) = procedure.weights();
match *color { return Ok(if self_numeric {
GenericColor::Numeric(color) => color, Self::new(
GenericColor::CurrentColor => RGBA::transparent(), self.color,
GenericColor::Complex { color, ratios } => RGBA { ComplexColorRatios {
red: color.red * ratios.bg, bg: self_weight as f32,
green: color.green * ratios.bg, fg: other_weight as f32,
blue: color.blue * ratios.bg, },
alpha: color.alpha * ratios.bg, )
}, } else {
} Self::new(
} other.color,
ComplexColorRatios {
bg: other_weight as f32,
fg: self_weight as f32,
},
)
});
}
// Each `Color`, represents a complex combination of foreground color and // Compute the "scaled" contribution for `color`.
// background color where fg and bg represent the overall // Each `Color`, represents a complex combination of foreground color and
// contributions. ie: // background color where fg and bg represent the overall
// // contributions. ie:
// color = { bg * mColor, fg * foreground } //
// = { bg_color , fg_color } // color = { bg * mColor, fg * foreground }
// = bg_color + fg_color // = { bg_color , fg_color }
// // = bg_color + fg_color
// where `foreground` is `currentcolor`, and `bg_color`, //
// `fg_color` are the scaled background and foreground // where `foreground` is `currentcolor`, and `bg_color`,
// contributions. // `fg_color` are the scaled background and foreground
// // contributions.
// Each operation, lerp, addition, or accumulate, can be //
// represented as a scaled-addition each complex color. ie: // Each operation, lerp, addition, or accumulate, can be
// // represented as a scaled-addition each complex color. ie:
// p * col1 + q * col2 //
// // p * col1 + q * col2
// where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for //
// addition, etc. // where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for
// // addition, etc.
// Therefore: //
// // Therefore:
// col1 op col2 //
// = p * col1 + q * col2 // col1 op col2
// = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 } // = p * col1 + q * col2
// = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2) // = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 }
// = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2 // = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2)
// = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2) // = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2
// = (bg_color1 op bg_color2) + (fg_color1 op fg_color2) // = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2)
// // = (bg_color1 op bg_color2) + (fg_color1 op fg_color2)
// fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground, //
// so the final color is: // fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground,
// // so the final color is:
// = { bg_color, fg_color } //
// = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground } // = { bg_color, fg_color }
// = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground }
//
// To perform the operation on two complex colors, we need to
// generate the scaled contributions of each background color
// component.
let bg_color1 = self.scaled_rgba();
let bg_color2 = other.scaled_rgba();
// To perform the operation on two complex colors, we need to // Perform bg_color1 op bg_color2
// generate the scaled contributions of each background color let bg_color = bg_color1.animate(&bg_color2, procedure)?;
// component.
let bg_color1 = scaled_rgba(self);
let bg_color2 = scaled_rgba(other);
// Perform bg_color1 op bg_color2
let bg_color = bg_color1.animate(&bg_color2, procedure)?;
// Calculate the final foreground color ratios; perform // Calculate the final foreground color ratios; perform
// animation on effective fg ratios. // animation on effective fg ratios.
let ComplexColorRatios { fg: fg1, .. } = self.effective_ratios(); let fg = self.ratios.fg.animate(&other.ratios.fg, procedure)?;
let ComplexColorRatios { fg: fg2, .. } = other.effective_ratios();
// Perform fg1 op fg2
let fg = fg1.animate(&fg2, procedure)?;
Self::with_ratios(bg_color, ComplexColorRatios { bg: 1., fg }) Ok(Self::new(bg_color, ComplexColorRatios { bg: 1., fg }))
},
})
} }
} }
impl ComputeSquaredDistance for Color { impl ComputeSquaredDistance for Color {
#[inline] #[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
use self::GenericColor::*; // All comments from the Animate impl also apply here.
let self_numeric = self.is_numeric();
let other_numeric = other.is_numeric();
// All comments from the Animate impl also applies here. if self_numeric && other_numeric {
Ok(match (*self, *other) { return self.color.compute_squared_distance(&other.color);
(CurrentColor, CurrentColor) => SquaredDistance::from_sqrt(0.), }
(Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?,
(CurrentColor, Numeric(color)) | (Numeric(color), CurrentColor) => {
// `computed_squared_distance` is symmetric.
color.compute_squared_distance(&RGBA::transparent())? +
SquaredDistance::from_sqrt(1.)
},
(_, _) => {
let self_color = self.effective_intermediate_rgba();
let other_color = other.effective_intermediate_rgba();
let self_ratios = self.effective_ratios();
let other_ratios = other.effective_ratios();
self_color.compute_squared_distance(&other_color)? + let self_currentcolor = self.is_currentcolor();
self_ratios.bg.compute_squared_distance(&other_ratios.bg)? + let other_currentcolor = other.is_currentcolor();
self_ratios.fg.compute_squared_distance(&other_ratios.fg)? if self_currentcolor && other_currentcolor {
}, return Ok(SquaredDistance::from_sqrt(0.));
}) }
if (self_currentcolor && other_numeric) || (self_numeric && other_currentcolor) {
let color = if self_numeric {
&self.color
} else {
&other.color
};
// `computed_squared_distance` is symmetric.
return Ok(color.compute_squared_distance(&RGBA::transparent())? +
SquaredDistance::from_sqrt(1.));
}
let self_color = self.effective_intermediate_rgba();
let other_color = other.effective_intermediate_rgba();
let self_ratios = self.ratios;
let other_ratios = other.ratios;
Ok(self_color.compute_squared_distance(&other_color)? +
self_ratios.bg.compute_squared_distance(&other_ratios.bg)? +
self_ratios.fg.compute_squared_distance(&other_ratios.fg)?)
} }
} }

View file

@ -6,7 +6,7 @@
use crate::values::animated::color::RGBA as AnimatedRGBA; use crate::values::animated::color::RGBA as AnimatedRGBA;
use crate::values::animated::ToAnimatedValue; use crate::values::animated::ToAnimatedValue;
use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto}; use crate::values::generics::color::{GenericColor, GenericColorOrAuto, GenericCaretColor};
use cssparser::{Color as CSSParserColor, RGBA}; use cssparser::{Color as CSSParserColor, RGBA};
use std::fmt; use std::fmt;
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -29,13 +29,18 @@ impl Color {
/// Combine this complex color with the given foreground color into /// Combine this complex color with the given foreground color into
/// a numeric RGBA color. It currently uses linear blending. /// a numeric RGBA color. It currently uses linear blending.
pub fn to_rgba(&self, fg_color: RGBA) -> RGBA { pub fn to_rgba(&self, fg_color: RGBA) -> RGBA {
let (color, ratios) = match *self { // Common cases that the complex color is either pure numeric color or
// Common cases that the complex color is either pure numeric // pure currentcolor.
// color or pure currentcolor. if self.is_numeric() {
GenericColor::Numeric(color) => return color, return self.color;
GenericColor::CurrentColor => return fg_color, }
GenericColor::Complex { color, ratios } => (color, ratios),
}; if self.is_currentcolor() {
return fg_color;
}
let ratios = &self.ratios;
let color = &self.color;
// For the more complicated case that the alpha value differs, // For the more complicated case that the alpha value differs,
// we use the following formula to compute the components: // we use the following formula to compute the components:
@ -59,13 +64,14 @@ impl Color {
if a <= 0. { if a <= 0. {
return RGBA::transparent(); return RGBA::transparent();
} }
let a = f32::min(a, 1.); let a = a.min(1.);
let inverse_a = 1. / a; let inv = 1. / a;
let r = (p1 * r1 + p2 * r2) * inverse_a;
let g = (p1 * g1 + p2 * g2) * inverse_a; let r = (p1 * r1 + p2 * r2) * inv;
let b = (p1 * b1 + p2 * b2) * inverse_a; let g = (p1 * g1 + p2 * g2) * inv;
return RGBA::from_floats(r, g, b, a); let b = (p1 * b1 + p2 * b2) * inv;
RGBA::from_floats(r, g, b, a)
} }
} }
@ -74,11 +80,13 @@ impl ToCss for Color {
where where
W: fmt::Write, W: fmt::Write,
{ {
match *self { if self.is_currentcolor() {
GenericColor::Numeric(color) => color.to_css(dest), return CSSParserColor::CurrentColor.to_css(dest);
GenericColor::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
_ => Ok(()),
} }
if self.is_numeric() {
return self.color.to_css(dest);
}
Ok(())
} }
} }
@ -104,3 +112,6 @@ impl ToAnimatedValue for RGBA {
/// auto | <color> /// auto | <color>
pub type ColorOrAuto = GenericColorOrAuto<Color>; pub type ColorOrAuto = GenericColorOrAuto<Color>;
/// caret-color
pub type CaretColor = GenericCaretColor<Color>;

View file

@ -4,7 +4,7 @@
//! Computed values for counter properties //! Computed values for counter properties
use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::image::Image;
use crate::values::generics::counters as generics; use crate::values::generics::counters as generics;
use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement; use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset; use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset;
@ -16,7 +16,7 @@ pub type CounterIncrement = GenericCounterIncrement<i32>;
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>; pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
/// A computed value for the `content` property. /// A computed value for the `content` property.
pub type Content = generics::GenericContent<ComputedImageUrl>; pub type Content = generics::GenericContent<Image>;
/// A computed content item. /// A computed content item.
pub type ContentItem = generics::GenericContentItem<ComputedImageUrl>; pub type ContentItem = generics::GenericContentItem<Image>;

View file

@ -828,14 +828,14 @@ impl ToComputedValue for specified::MathDepth {
let parent = cx.builder.get_parent_font().clone_math_depth() as i32; let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
let style = cx.builder.get_parent_font().clone_math_style(); let style = cx.builder.get_parent_font().clone_math_style();
if style == MathStyleValue::Compact { if style == MathStyleValue::Compact {
parent + 1 parent.saturating_add(1)
} else { } else {
parent parent
} }
}, },
specified::MathDepth::Add(rel) => { specified::MathDepth::Add(rel) => {
let parent = cx.builder.get_parent_font().clone_math_depth(); let parent = cx.builder.get_parent_font().clone_math_depth();
parent as i32 + rel.to_computed_value(cx) (parent as i32).saturating_add(rel.to_computed_value(cx))
}, },
specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx), specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
}; };

View file

@ -74,9 +74,20 @@ impl ToComputedValue for specified::ImageSet {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
let items = self.items.to_computed_value(context); let items = self.items.to_computed_value(context);
let dpr = context.device().device_pixel_ratio().get(); let dpr = context.device().device_pixel_ratio().get();
// If no item have a supported MIME type, the behavior is undefined by the standard
// By default, we select the first item
let mut supported_image = false;
let mut selected_index = 0; let mut selected_index = 0;
let mut selected_resolution = items[0].resolution.dppx(); let mut selected_resolution = items[0].resolution.dppx();
for (i, item) in items.iter().enumerate().skip(1) {
for (i, item) in items.iter().enumerate() {
// If the MIME type is not supported, we discard the ImageSetItem
if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
continue;
}
let candidate_resolution = item.resolution.dppx(); let candidate_resolution = item.resolution.dppx();
// https://drafts.csswg.org/css-images-4/#image-set-notation: // https://drafts.csswg.org/css-images-4/#image-set-notation:
@ -97,7 +108,9 @@ impl ToComputedValue for specified::ImageSet {
false false
}; };
if better_candidate() { // The first item with a supported MIME type is obviously the current best candidate
if !supported_image || better_candidate() {
supported_image = true;
selected_index = i; selected_index = i;
selected_resolution = candidate_resolution; selected_resolution = candidate_resolution;
} }

View file

@ -185,7 +185,10 @@ impl Size {
GenericSize::Auto => false, GenericSize::Auto => false,
GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(), GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
GenericSize::ExtremumLength(..) => false, GenericSize::MinContent |
GenericSize::MaxContent |
GenericSize::MozFitContent |
GenericSize::MozAvailable => false
} }
} }
} }
@ -495,37 +498,6 @@ pub type NonNegativeLengthPercentageOrNormal =
/// Either a non-negative `<length>` or a `<number>`. /// Either a non-negative `<length>` or a `<number>`.
pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>; pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
/// A type for possible values for min- and max- flavors of width, height,
/// block-size, and inline-size.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum ExtremumLength {
#[parse(aliases = "-moz-max-content")]
MaxContent,
#[parse(aliases = "-moz-min-content")]
MinContent,
MozFitContent,
MozAvailable,
}
/// A computed value for `min-width`, `min-height`, `width` or `height` property. /// A computed value for `min-width`, `min-height`, `width` or `height` property.
pub type Size = GenericSize<NonNegativeLengthPercentage>; pub type Size = GenericSize<NonNegativeLengthPercentage>;

View file

@ -63,7 +63,7 @@ pub use self::font::{FontVariantAlternates, FontWeight};
pub use self::font::{FontVariantEastAsian, FontVariationSettings}; pub use self::font::{FontVariantEastAsian, FontVariationSettings};
pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
pub use self::image::{Gradient, Image, LineDirection, MozImageRect}; pub use self::image::{Gradient, Image, LineDirection, MozImageRect};
pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength}; pub use self::length::{CSSPixelLength, NonNegativeLength};
pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
@ -87,9 +87,9 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::text::TextUnderlinePosition; pub use self::text::TextUnderlinePosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing}; pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
pub use self::text::{TextDecorationLength, TextDecorationSkipInk}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};

View file

@ -11,4 +11,4 @@ use crate::values::generics::size::Size2D;
pub use generics::page::Orientation; pub use generics::page::Orientation;
pub use generics::page::PaperSize; pub use generics::page::PaperSize;
/// Computed value of the @page size descriptor /// Computed value of the @page size descriptor
pub type PageSize = generics::page::PageSize<Size2D<NonNegativeLength>>; pub type PageSize = generics::page::GenericPageSize<Size2D<NonNegativeLength>>;

View file

@ -18,10 +18,10 @@ use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition}; pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition, MozControlCharacterVisibility};
pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak}; pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak};
pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
pub use crate::values::specified::{TextDecorationSkipInk, TextTransform}; pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform};
/// A computed value for the `initial-letter` property. /// A computed value for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>; pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;

View file

@ -5,7 +5,7 @@
//! Computed values for UI properties //! Computed values for UI properties
use crate::values::computed::color::Color; use crate::values::computed::color::Color;
use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::image::Image;
use crate::values::computed::Number; use crate::values::computed::Number;
use crate::values::generics::ui as generics; use crate::values::generics::ui as generics;
@ -16,7 +16,7 @@ pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
pub type Cursor = generics::GenericCursor<CursorImage>; pub type Cursor = generics::GenericCursor<CursorImage>;
/// A computed value for item of `image cursors`. /// A computed value for item of `image cursors`.
pub type CursorImage = generics::GenericCursorImage<ComputedImageUrl, Number>; pub type CursorImage = generics::GenericCursorImage<Image, Number>;
/// A computed value for `scrollbar-color` property. /// A computed value for `scrollbar-color` property.
pub type ScrollbarColor = generics::GenericScrollbarColor<Color>; pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;

View file

@ -6,6 +6,11 @@
/// Ratios representing the contribution of color and currentcolor to /// Ratios representing the contribution of color and currentcolor to
/// the final color value. /// the final color value.
///
/// NOTE(emilio): For animated colors, the sum of these two might be more than
/// one (because the background color would've been scaled down already). So
/// beware that it is not generally safe to assume that if bg is 1 then fg is 0,
/// for example.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)] #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
#[repr(C)] #[repr(C)]
pub struct ComplexColorRatios { pub struct ComplexColorRatios {
@ -22,59 +27,52 @@ impl ComplexColorRatios {
pub const CURRENT_COLOR: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. }; pub const CURRENT_COLOR: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. };
} }
/// This enum represents a combined color from a numeric color and /// This struct represents a combined color from a numeric color and
/// the current foreground color (currentcolor keyword). /// the current foreground color (currentcolor keyword).
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)] #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
#[repr(C, u8)] #[repr(C)]
pub enum GenericColor<RGBA> { pub struct GenericColor<RGBA> {
/// Numeric RGBA color. /// The actual numeric color.
Numeric(RGBA), pub color: RGBA,
/// The ratios of mixing between numeric and currentcolor.
/// The current foreground color.
CurrentColor,
/// A linear combination of numeric color and currentcolor.
/// The formula is: `color * ratios.bg + currentcolor * ratios.fg`. /// The formula is: `color * ratios.bg + currentcolor * ratios.fg`.
Complex { pub ratios: ComplexColorRatios,
/// The actual numeric color.
color: RGBA,
/// The ratios of mixing between numeric and currentcolor.
ratios: ComplexColorRatios,
},
} }
pub use self::GenericColor as Color; pub use self::GenericColor as Color;
impl Color<cssparser::RGBA> {
/// Returns a color value representing currentcolor.
pub fn currentcolor() -> Self {
Color {
color: cssparser::RGBA::transparent(),
ratios: ComplexColorRatios::CURRENT_COLOR,
}
}
}
impl<RGBA> Color<RGBA> { impl<RGBA> Color<RGBA> {
/// Create a color based upon the specified ratios. /// Create a color based upon the specified ratios.
pub fn with_ratios(color: RGBA, ratios: ComplexColorRatios) -> Self { pub fn new(color: RGBA, ratios: ComplexColorRatios) -> Self {
if ratios == ComplexColorRatios::NUMERIC { Self { color, ratios }
Color::Numeric(color)
} else if ratios == ComplexColorRatios::CURRENT_COLOR {
Color::CurrentColor
} else {
Color::Complex { color, ratios }
}
} }
/// Returns a numeric color representing the given RGBA value. /// Returns a numeric color representing the given RGBA value.
pub fn rgba(color: RGBA) -> Self { pub fn rgba(color: RGBA) -> Self {
Color::Numeric(color) Self {
} color,
ratios: ComplexColorRatios::NUMERIC,
/// Returns a complex color value representing currentcolor. }
pub fn currentcolor() -> Self {
Color::CurrentColor
} }
/// Whether it is a numeric color (no currentcolor component). /// Whether it is a numeric color (no currentcolor component).
pub fn is_numeric(&self) -> bool { pub fn is_numeric(&self) -> bool {
matches!(*self, Color::Numeric(..)) self.ratios == ComplexColorRatios::NUMERIC
} }
/// Whether it is a currentcolor value (no numeric color component). /// Whether it is a currentcolor value (no numeric color component).
pub fn is_currentcolor(&self) -> bool { pub fn is_currentcolor(&self) -> bool {
matches!(*self, Color::CurrentColor) self.ratios == ComplexColorRatios::CURRENT_COLOR
} }
} }
@ -98,6 +96,7 @@ impl<RGBA> From<RGBA> for Color<RGBA> {
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue, ToComputedValue,
ToResolvedValue,
ToCss, ToCss,
ToShmem, ToShmem,
)] )]
@ -110,3 +109,32 @@ pub enum GenericColorOrAuto<C> {
} }
pub use self::GenericColorOrAuto as ColorOrAuto; pub use self::GenericColorOrAuto as ColorOrAuto;
/// Caret color is effectively a ColorOrAuto, but resolves `auto` to
/// currentColor.
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToShmem,
)]
#[repr(transparent)]
pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
impl<C> GenericCaretColor<C> {
/// Returns the `auto` value.
pub fn auto() -> Self {
GenericCaretColor(GenericColorOrAuto::Auto)
}
}
pub use self::GenericCaretColor as CaretColor;

View file

@ -148,18 +148,18 @@ fn is_decimal(counter_type: &CounterStyleType) -> bool {
Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem, Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem,
)] )]
#[repr(u8)] #[repr(u8)]
pub enum GenericContent<ImageUrl> { pub enum GenericContent<Image> {
/// `normal` reserved keyword. /// `normal` reserved keyword.
Normal, Normal,
/// `none` reserved keyword. /// `none` reserved keyword.
None, None,
/// Content items. /// Content items.
Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<ImageUrl>>), Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<Image>>),
} }
pub use self::GenericContent as Content; pub use self::GenericContent as Content;
impl<ImageUrl> Content<ImageUrl> { impl<Image> Content<Image> {
/// Whether `self` represents list of items. /// Whether `self` represents list of items.
#[inline] #[inline]
pub fn is_items(&self) -> bool { pub fn is_items(&self) -> bool {
@ -180,14 +180,13 @@ impl<ImageUrl> Content<ImageUrl> {
Eq, Eq,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
SpecifiedValueInfo,
ToComputedValue, ToComputedValue,
ToCss, ToCss,
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(u8)] #[repr(u8)]
pub enum GenericContentItem<ImageUrl> { pub enum GenericContentItem<I> {
/// Literal string content. /// Literal string content.
String(crate::OwnedStr), String(crate::OwnedStr),
/// `counter(name, style)`. /// `counter(name, style)`.
@ -220,8 +219,8 @@ pub enum GenericContentItem<ImageUrl> {
/// `attr([namespace? `|`]? ident)` /// `attr([namespace? `|`]? ident)`
#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
Attr(Attr), Attr(Attr),
/// `url(url)` /// image-set(url) | url(url)
Url(ImageUrl), Image(I),
} }
pub use self::GenericContentItem as ContentItem; pub use self::GenericContentItem as ContentItem;

View file

@ -132,7 +132,7 @@ pub struct GenericImageSet<Image, Resolution> {
/// An optional percent and a cross fade image. /// An optional percent and a cross fade image.
#[derive( #[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)] )]
#[repr(C)] #[repr(C)]
pub struct GenericImageSetItem<Image, Resolution> { pub struct GenericImageSetItem<Image, Resolution> {
@ -142,7 +142,33 @@ pub struct GenericImageSetItem<Image, Resolution> {
/// ///
/// TODO: Skip serialization if it is 1x. /// TODO: Skip serialization if it is 1x.
pub resolution: Resolution, pub resolution: Resolution,
// TODO: type() function.
/// The `type(<string>)`
/// (Optional) Specify the image's MIME type
pub mime_type: crate::OwnedStr,
/// True if mime_type has been specified
pub has_mime_type: bool,
}
impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R>
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.image.to_css(dest)?;
dest.write_str(" ")?;
self.resolution.to_css(dest)?;
if self.has_mime_type {
dest.write_str(" ")?;
dest.write_str("type(")?;
self.mime_type.to_css(dest)?;
dest.write_str(")")?;
}
Ok(())
}
} }
pub use self::GenericImageSet as ImageSet; pub use self::GenericImageSet as ImageSet;

View file

@ -5,8 +5,6 @@
//! Generic types for CSS values related to length. //! Generic types for CSS values related to length.
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
#[cfg(feature = "gecko")]
use crate::values::computed::ExtremumLength;
use crate::Zero; use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use style_traits::ParseError; use style_traits::ParseError;
@ -153,7 +151,16 @@ pub enum GenericSize<LengthPercent> {
Auto, Auto,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[animation(error)] #[animation(error)]
ExtremumLength(ExtremumLength), MaxContent,
#[cfg(feature = "gecko")]
#[animation(error)]
MinContent,
#[cfg(feature = "gecko")]
#[animation(error)]
MozFitContent,
#[cfg(feature = "gecko")]
#[animation(error)]
MozAvailable,
} }
pub use self::GenericSize as Size; pub use self::GenericSize as Size;
@ -196,7 +203,18 @@ pub enum GenericMaxSize<LengthPercent> {
None, None,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[animation(error)] #[animation(error)]
ExtremumLength(ExtremumLength), #[parse(aliases = "-moz-max-content")]
MaxContent,
#[cfg(feature = "gecko")]
#[animation(error)]
#[parse(aliases = "-moz-min-content")]
MinContent,
#[cfg(feature = "gecko")]
#[animation(error)]
MozFitContent,
#[cfg(feature = "gecko")]
#[animation(error)]
MozAvailable,
} }
pub use self::GenericMaxSize as MaxSize; pub use self::GenericMaxSize as MaxSize;

View file

@ -86,7 +86,7 @@ pub enum Orientation {
ToShmem, ToShmem,
)] )]
#[repr(C, u8)] #[repr(C, u8)]
pub enum PageSize<S> { pub enum GenericPageSize<S> {
/// Page dimensions. /// Page dimensions.
Size(S), Size(S),
/// Paper size with no orientation. /// Paper size with no orientation.
@ -99,6 +99,8 @@ pub enum PageSize<S> {
Auto, Auto,
} }
pub use self::GenericPageSize as PageSize;
impl<S> PageSize<S> { impl<S> PageSize<S> {
/// `auto` value. /// `auto` value.
#[inline] #[inline]

View file

@ -61,15 +61,14 @@ impl<Image: ToCss> ToCss for Cursor<Image> {
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
SpecifiedValueInfo,
ToComputedValue, ToComputedValue,
ToResolvedValue, ToResolvedValue,
ToShmem, ToShmem,
)] )]
#[repr(C)] #[repr(C)]
pub struct GenericCursorImage<ImageUrl, Number> { pub struct GenericCursorImage<Image, Number> {
/// The url to parse images from. /// The url to parse images from.
pub url: ImageUrl, pub image: Image,
/// Whether the image has a hotspot or not. /// Whether the image has a hotspot or not.
pub has_hotspot: bool, pub has_hotspot: bool,
/// The x coordinate. /// The x coordinate.
@ -80,12 +79,12 @@ pub struct GenericCursorImage<ImageUrl, Number> {
pub use self::GenericCursorImage as CursorImage; pub use self::GenericCursorImage as CursorImage;
impl<ImageUrl: ToCss, Number: ToCss> ToCss for CursorImage<ImageUrl, Number> { impl<Image: ToCss, Number: ToCss> ToCss for CursorImage<Image, Number> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
W: Write, W: Write,
{ {
self.url.to_css(dest)?; self.image.to_css(dest)?;
if self.has_hotspot { if self.has_hotspot {
dest.write_str(" ")?; dest.write_str(" ")?;
self.hotspot_x.to_css(dest)?; self.hotspot_x.to_css(dest)?;

View file

@ -6,7 +6,7 @@
use super::{Context, ToResolvedValue}; use super::{Context, ToResolvedValue};
use crate::values::computed; use crate::values::computed::color as computed;
use crate::values::generics::color as generics; use crate::values::generics::color as generics;
impl ToResolvedValue for computed::Color { impl ToResolvedValue for computed::Color {
@ -20,26 +20,26 @@ impl ToResolvedValue for computed::Color {
#[inline] #[inline]
fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
generics::Color::Numeric(resolved) generics::Color::rgba(resolved)
} }
} }
impl ToResolvedValue for computed::ColorOrAuto { impl ToResolvedValue for computed::CaretColor {
// A resolved caret-color value is an rgba color, with auto resolving to // A resolved caret-color value is an rgba color, with auto resolving to
// currentcolor. // currentcolor.
type ResolvedValue = cssparser::RGBA; type ResolvedValue = cssparser::RGBA;
#[inline] #[inline]
fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue { fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
let color = match self { let color = match self.0 {
generics::ColorOrAuto::Color(color) => color, generics::ColorOrAuto::Color(color) => color,
generics::ColorOrAuto::Auto => generics::Color::CurrentColor, generics::ColorOrAuto::Auto => generics::Color::currentcolor(),
}; };
color.to_resolved_value(context) color.to_resolved_value(context)
} }
#[inline] #[inline]
fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved)) generics::CaretColor(generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved)))
} }
} }

View file

@ -6,7 +6,7 @@
use crate::custom_properties::Name as CustomPropertyName; use crate::custom_properties::Name as CustomPropertyName;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags}; use crate::properties::{LonghandId, PropertyDeclarationId};
use crate::properties::{PropertyId, ShorthandId}; use crate::properties::{PropertyId, ShorthandId};
use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
use crate::values::generics::box_::Perspective as GenericPerspective; use crate::values::generics::box_::Perspective as GenericPerspective;
@ -1086,46 +1086,58 @@ bitflags! {
/// The change bits that we care about. /// The change bits that we care about.
#[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(C)] #[repr(C)]
pub struct WillChangeBits: u8 { pub struct WillChangeBits: u16 {
/// Whether the stacking context will change. /// Whether a property which can create a stacking context **on any
const STACKING_CONTEXT = 1 << 0; /// box** will change.
/// Whether `transform` will change. const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
/// Whether `transform` or related properties will change.
const TRANSFORM = 1 << 1; const TRANSFORM = 1 << 1;
/// Whether `scroll-position` will change. /// Whether `scroll-position` will change.
const SCROLL = 1 << 2; const SCROLL = 1 << 2;
/// Whether `contain` will change.
const CONTAIN = 1 << 3;
/// Whether `opacity` will change. /// Whether `opacity` will change.
const OPACITY = 1 << 3; const OPACITY = 1 << 4;
/// Fixed pos containing block. /// Whether `perspective` will change.
const FIXPOS_CB = 1 << 4; const PERSPECTIVE = 1 << 5;
/// Abs pos containing block. /// Whether `z-index` will change.
const ABSPOS_CB = 1 << 5; const Z_INDEX = 1 << 6;
/// Whether any property which creates a containing block for non-svg
/// text frames will change.
const FIXPOS_CB_NON_SVG = 1 << 7;
/// Whether the position property will change.
const POSITION = 1 << 8;
} }
} }
#[cfg(feature = "gecko")]
fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
let mut flags = match longhand { match longhand {
LonghandId::Opacity => WillChangeBits::OPACITY, LonghandId::Opacity => WillChangeBits::OPACITY,
LonghandId::Transform => WillChangeBits::TRANSFORM, LonghandId::Contain => WillChangeBits::CONTAIN,
#[cfg(feature = "gecko")] LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => { LonghandId::Position => {
WillChangeBits::TRANSFORM WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
}, },
LonghandId::ZIndex => WillChangeBits::Z_INDEX,
LonghandId::Transform |
LonghandId::TransformStyle |
LonghandId::Translate |
LonghandId::Rotate |
LonghandId::Scale |
LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
LonghandId::BackdropFilter | LonghandId::Filter => {
WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::FIXPOS_CB_NON_SVG
},
LonghandId::MixBlendMode |
LonghandId::Isolation |
LonghandId::MaskImage |
LonghandId::ClipPath => WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL,
_ => WillChangeBits::empty(), _ => WillChangeBits::empty(),
};
let property_flags = longhand.flags();
if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) {
flags |= WillChangeBits::STACKING_CONTEXT;
} }
if property_flags.contains(PropertyFlags::FIXPOS_CB) {
flags |= WillChangeBits::FIXPOS_CB;
}
if property_flags.contains(PropertyFlags::ABSPOS_CB) {
flags |= WillChangeBits::ABSPOS_CB;
}
flags
} }
#[cfg(feature = "gecko")]
fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
let id = match PropertyId::parse_ignoring_rule_type(ident, context) { let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
Ok(id) => id, Ok(id) => id,
@ -1143,6 +1155,7 @@ fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillC
} }
} }
#[cfg(feature = "gecko")]
impl Parse for WillChange { impl Parse for WillChange {
/// auto | <animateable-feature># /// auto | <animateable-feature>#
fn parse<'i, 't>( fn parse<'i, 't>(
@ -1838,10 +1851,6 @@ pub enum Appearance {
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacSourceListSelection, MozMacSourceListSelection,
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacVibrancyDark,
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacVibrancyLight,
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacVibrantTitlebarDark, MozMacVibrantTitlebarDark,
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacVibrantTitlebarLight, MozMacVibrantTitlebarLight,

View file

@ -374,9 +374,9 @@ impl CalcNode {
rhs.negate(); rhs.negate();
sum.push(rhs); sum.push(rhs);
}, },
ref t => { _ => {
let t = t.clone(); input.reset(&start);
return Err(input.new_unexpected_token_error(t)); break;
}, },
} }
}, },

View file

@ -9,8 +9,9 @@ use super::AllowQuirks;
use crate::gecko_bindings::structs::nscolor; use crate::gecko_bindings::structs::nscolor;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto}; use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor};
use crate::values::specified::calc::CalcNode; use crate::values::specified::calc::CalcNode;
use crate::values::specified::Percentage;
use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA}; use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind}; use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
use itoa; use itoa;
@ -19,6 +20,117 @@ use std::io::Write as IoWrite;
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind}; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
/// A restricted version of the css `color-mix()` function, which only supports
/// percentages and sRGB color-space interpolation.
///
/// https://drafts.csswg.org/css-color-5/#color-mix
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
#[allow(missing_docs)]
pub struct ColorMix {
pub left: Color,
pub left_percentage: Percentage,
pub right: Color,
pub right_percentage: Percentage,
}
#[cfg(feature = "gecko")]
#[inline]
fn allow_color_mix() -> bool {
static_prefs::pref!("layout.css.color-mix.enabled")
}
#[cfg(feature = "servo")]
#[inline]
fn allow_color_mix() -> bool {
false
}
// NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem
// particularly complete, and disagrees with the examples.
//
// [1]: https://github.com/w3c/csswg-drafts/commit/a4316446112f9e814668c2caff7f826f512f8fed
impl Parse for ColorMix {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let enabled =
context.chrome_rules_enabled() || allow_color_mix();
if !enabled {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
input.expect_function_matching("color-mix")?;
// NOTE(emilio): This implements the syntax described here for now,
// might need to get updated in the future.
//
// https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765
input.parse_nested_block(|input| {
input.expect_ident_matching("in")?;
// TODO: support multiple interpolation spaces.
input.expect_ident_matching("srgb")?;
input.expect_comma()?;
let left = Color::parse(context, input)?;
let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok();
input.expect_comma()?;
let right = Color::parse(context, input)?;
let right_percentage = input
.try_parse(|input| Percentage::parse(context, input))
.unwrap_or_else(|_| {
Percentage::new(1.0 - left_percentage.map_or(0.5, |p| p.get()))
});
let left_percentage =
left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get()));
Ok(ColorMix {
left,
left_percentage,
right,
right_percentage,
})
})
}
}
impl ToCss for ColorMix {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
fn can_omit(percent: &Percentage, other: &Percentage, is_left: bool) -> bool {
if percent.is_calc() {
return false;
}
if percent.get() == 0.5 {
return other.get() == 0.5;
}
if is_left {
return false;
}
(1.0 - percent.get() - other.get()).abs() <= f32::EPSILON
}
dest.write_str("color-mix(in srgb, ")?;
self.left.to_css(dest)?;
if !can_omit(&self.left_percentage, &self.right_percentage, true) {
dest.write_str(" ")?;
self.left_percentage.to_css(dest)?;
}
dest.write_str(", ")?;
self.right.to_css(dest)?;
if !can_omit(&self.right_percentage, &self.left_percentage, false) {
dest.write_str(" ")?;
self.right_percentage.to_css(dest)?;
}
dest.write_str(")")
}
}
/// Specified color value /// Specified color value
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum Color { pub enum Color {
@ -36,6 +148,8 @@ pub enum Color {
/// A system color /// A system color
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
System(SystemColor), System(SystemColor),
/// A color mix.
ColorMix(Box<ColorMix>),
/// Quirksmode-only rule for inheriting color from the body /// Quirksmode-only rule for inheriting color from the body
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
InheritFromBodyQuirk, InheritFromBodyQuirk,
@ -72,8 +186,6 @@ pub enum SystemColor {
#[css(skip)] #[css(skip)]
TextSelectForeground, TextSelectForeground,
#[css(skip)] #[css(skip)]
TextSelectForegroundCustom,
#[css(skip)]
TextSelectBackgroundDisabled, TextSelectBackgroundDisabled,
#[css(skip)] #[css(skip)]
TextSelectBackgroundAttention, TextSelectBackgroundAttention,
@ -215,8 +327,6 @@ pub enum SystemColor {
/// Font smoothing background colors needed by the Mac OS X theme, based on /// Font smoothing background colors needed by the Mac OS X theme, based on
/// -moz-appearance names. /// -moz-appearance names.
MozMacVibrancyLight,
MozMacVibrancyDark,
MozMacVibrantTitlebarLight, MozMacVibrantTitlebarLight,
MozMacVibrantTitlebarDark, MozMacVibrantTitlebarDark,
MozMacMenupopup, MozMacMenupopup,
@ -235,10 +345,6 @@ pub enum SystemColor {
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozAccentColorForeground, MozAccentColorForeground,
/// Accent color for title bar.
MozWinAccentcolor,
/// Color from drawing text over the accent color.
MozWinAccentcolortext,
/// Media rebar text. /// Media rebar text.
MozWinMediatext, MozWinMediatext,
/// Communications rebar text. /// Communications rebar text.
@ -338,8 +444,6 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
} }
fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> { fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
use crate::values::specified::Percentage;
Ok(Percentage::parse(self.0, input)?.get()) Ok(Percentage::parse(self.0, input)?.get())
} }
@ -398,6 +502,10 @@ impl Parse for Color {
} }
} }
if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i)) {
return Ok(Color::ColorMix(Box::new(mix)));
}
match e.kind { match e.kind {
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => { ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError( Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
@ -425,7 +533,9 @@ impl ToCss for Color {
Color::Numeric { Color::Numeric {
parsed: ref rgba, .. parsed: ref rgba, ..
} => rgba.to_css(dest), } => rgba.to_css(dest),
// TODO: Could represent this as a color-mix() instead.
Color::Complex(_) => Ok(()), Color::Complex(_) => Ok(()),
Color::ColorMix(ref mix) => mix.to_css(dest),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::System(system) => system.to_css(dest), Color::System(system) => system.to_css(dest),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -447,6 +557,18 @@ fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
} }
impl Color { impl Color {
/// Returns whether this color is a system color.
#[cfg(feature = "gecko")]
pub fn is_system(&self) -> bool {
matches!(self, Color::System(..))
}
/// Returns whether this color is a system color.
#[cfg(feature = "servo")]
pub fn is_system(&self) -> bool {
false
}
/// Returns currentcolor value. /// Returns currentcolor value.
#[inline] #[inline]
pub fn currentcolor() -> Color { pub fn currentcolor() -> Color {
@ -562,17 +684,28 @@ impl Color {
/// ///
/// If `context` is `None`, and the specified color requires data from /// If `context` is `None`, and the specified color requires data from
/// the context to resolve, then `None` is returned. /// the context to resolve, then `None` is returned.
pub fn to_computed_color(&self, _context: Option<&Context>) -> Option<ComputedColor> { pub fn to_computed_color(&self, context: Option<&Context>) -> Option<ComputedColor> {
Some(match *self { Some(match *self {
Color::CurrentColor => ComputedColor::currentcolor(), Color::CurrentColor => ComputedColor::currentcolor(),
Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed), Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
Color::Complex(ref complex) => *complex, Color::Complex(ref complex) => *complex,
#[cfg(feature = "gecko")] Color::ColorMix(ref mix) => {
Color::System(system) => system.compute(_context?), use crate::values::animated::color::Color as AnimatedColor;
#[cfg(feature = "gecko")] use crate::values::animated::ToAnimatedValue;
Color::InheritFromBodyQuirk => {
ComputedColor::rgba(_context?.device().body_text_color()) let left = mix.left.to_computed_color(context)?.to_animated_value();
let right = mix.right.to_computed_color(context)?.to_animated_value();
ToAnimatedValue::from_animated_value(AnimatedColor::mix(
&left,
mix.left_percentage.get(),
&right,
mix.right_percentage.get(),
))
}, },
#[cfg(feature = "gecko")]
Color::System(system) => system.compute(context?),
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()),
}) })
} }
} }
@ -585,11 +718,13 @@ impl ToComputedValue for Color {
} }
fn from_computed_value(computed: &ComputedColor) -> Self { fn from_computed_value(computed: &ComputedColor) -> Self {
match *computed { if computed.is_numeric() {
GenericColor::Numeric(color) => Color::rgba(color), return Color::rgba(computed.color);
GenericColor::CurrentColor => Color::currentcolor(),
GenericColor::Complex { .. } => Color::Complex(*computed),
} }
if computed.is_currentcolor() {
return Color::currentcolor();
}
Color::Complex(*computed)
} }
} }
@ -671,3 +806,15 @@ impl Parse for ColorPropertyValue {
/// auto | <color> /// auto | <color>
pub type ColorOrAuto = GenericColorOrAuto<Color>; pub type ColorOrAuto = GenericColorOrAuto<Color>;
/// caret-color
pub type CaretColor = GenericCaretColor<Color>;
impl Parse for CaretColor {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
ColorOrAuto::parse(context, input).map(GenericCaretColor)
}
}

View file

@ -11,15 +11,15 @@ use crate::values::generics::counters as generics;
use crate::values::generics::counters::CounterPair; use crate::values::generics::counters::CounterPair;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::values::generics::CounterStyle; use crate::values::generics::CounterStyle;
use crate::values::specified::url::SpecifiedImageUrl; use crate::values::specified::image::Image;
#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
use crate::values::specified::Attr; use crate::values::specified::Attr;
use crate::values::specified::Integer; use crate::values::specified::Integer;
use crate::values::CustomIdent; use crate::values::CustomIdent;
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
#[cfg(feature = "servo-layout-2013")] #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
use selectors::parser::SelectorParseErrorKind; use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind}; use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind};
/// A specified value for the `counter-increment` property. /// A specified value for the `counter-increment` property.
pub type CounterIncrement = generics::GenericCounterIncrement<Integer>; pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
@ -83,10 +83,10 @@ fn parse_counters<'i, 't>(
} }
/// The specified value for the `content` property. /// The specified value for the `content` property.
pub type Content = generics::GenericContent<SpecifiedImageUrl>; pub type Content = generics::GenericContent<Image>;
/// The specified value for a content item in the `content` property. /// The specified value for a content item in the `content` property.
pub type ContentItem = generics::GenericContentItem<SpecifiedImageUrl>; pub type ContentItem = generics::GenericContentItem<Image>;
impl Content { impl Content {
#[cfg(feature = "servo-layout-2013")] #[cfg(feature = "servo-layout-2013")]
@ -137,8 +137,8 @@ impl Parse for Content {
loop { loop {
#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))] #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
{ {
if let Ok(url) = input.try_parse(|i| SpecifiedImageUrl::parse(context, i)) { if let Ok(image) = input.try_parse(|i| Image::parse_only_url(context, i)) {
content.push(generics::ContentItem::Url(url)); content.push(generics::ContentItem::Image(image));
continue; continue;
} }
} }
@ -171,6 +171,7 @@ impl Parse for Content {
Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
}), }),
_ => { _ => {
use style_traits::StyleParseErrorKind;
let name = name.clone(); let name = name.clone();
return Err(input.new_custom_error( return Err(input.new_custom_error(
StyleParseErrorKind::UnexpectedFunction(name), StyleParseErrorKind::UnexpectedFunction(name),
@ -213,3 +214,20 @@ impl Parse for Content {
Ok(generics::Content::Items(content.into())) Ok(generics::Content::Items(content.into()))
} }
} }
impl<Image> SpecifiedValueInfo for generics::GenericContentItem<Image> {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&[
"url",
"image-set",
"counter",
"counters",
"attr",
"open-quote",
"close-quote",
"no-open-quote",
"no-close-quote",
"-moz-alt-content",
]);
}
}

View file

@ -9,7 +9,6 @@ use crate::context::QuirksMode;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::properties::longhands::system_font::SystemFont;
use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily}; use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
use crate::values::computed::{font as computed, Length, NonNegativeLength}; use crate::values::computed::{font as computed, Length, NonNegativeLength};
use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage}; use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
@ -19,7 +18,7 @@ use crate::values::generics::font::{self as generics, FeatureTagValue, FontSetti
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
use crate::values::specified::length::{FontBaseSize, PX_PER_PT}; use crate::values::specified::length::{FontBaseSize, PX_PER_PT};
use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage}; use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, Percentage}; use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, NonNegativePercentage};
use crate::values::CustomIdent; use crate::values::CustomIdent;
use crate::Atom; use crate::Atom;
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
@ -65,6 +64,54 @@ macro_rules! system_font_methods {
}; };
} }
/// System fonts.
#[repr(u8)]
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
)]
#[allow(missing_docs)]
#[cfg(feature = "gecko")]
pub enum SystemFont {
Caption,
Icon,
Menu,
MessageBox,
SmallCaption,
StatusBar,
MozWindow,
MozDocument,
MozWorkspace,
MozDesktop,
MozInfo,
MozDialog,
MozButton,
MozPullDownMenu,
MozList,
MozField,
#[css(skip)]
End, // Just for indexing purposes.
}
// We don't parse system fonts in servo, but in the interest of not
// littering a lot of code with `if engine == "gecko"` conditionals,
// we have a dummy system font module that does nothing
#[derive(
Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
)]
#[allow(missing_docs)]
#[cfg(feature = "servo")]
/// void enum for system font, can never exist
pub enum SystemFont {}
#[allow(missing_docs)]
#[cfg(feature = "servo")]
impl SystemFont {
pub fn parse(_: &mut Parser) -> Result<Self, ()> {
Err(())
}
}
const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8; const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71; const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
@ -359,13 +406,11 @@ impl ToComputedValue for FontStyle {
/// A value for the `font-stretch` property. /// A value for the `font-stretch` property.
/// ///
/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
///
/// TODO(emilio): We could derive Parse if we had NonNegativePercentage.
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[repr(u8)] #[repr(u8)]
pub enum FontStretch { pub enum FontStretch {
Stretch(Percentage), Stretch(NonNegativePercentage),
Keyword(FontStretchKeyword), Keyword(FontStretchKeyword),
#[css(skip)] #[css(skip)]
System(SystemFont), System(SystemFont),
@ -452,32 +497,13 @@ impl FontStretch {
system_font_methods!(FontStretch, font_stretch); system_font_methods!(FontStretch, font_stretch);
} }
impl Parse for FontStretch {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
// From https://drafts.csswg.org/css-fonts-4/#font-stretch-prop:
//
// Values less than 0% are not allowed and are treated as parse
// errors.
if let Ok(percentage) =
input.try_parse(|input| Percentage::parse_non_negative(context, input))
{
return Ok(FontStretch::Stretch(percentage));
}
Ok(FontStretch::Keyword(FontStretchKeyword::parse(input)?))
}
}
impl ToComputedValue for FontStretch { impl ToComputedValue for FontStretch {
type ComputedValue = computed::FontStretch; type ComputedValue = computed::FontStretch;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self { match *self {
FontStretch::Stretch(ref percentage) => { FontStretch::Stretch(ref percentage) => {
computed::FontStretch(NonNegative(percentage.to_computed_value(context))) computed::FontStretch(percentage.to_computed_value(context))
}, },
FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())), FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())),
FontStretch::System(_) => self.compute_system(context), FontStretch::System(_) => self.compute_system(context),
@ -485,7 +511,7 @@ impl ToComputedValue for FontStretch {
} }
fn from_computed_value(computed: &Self::ComputedValue) -> Self { fn from_computed_value(computed: &Self::ComputedValue) -> Self {
FontStretch::Stretch(Percentage::from_computed_value(&(computed.0).0)) FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative((computed.0).0)))
} }
} }
@ -2226,6 +2252,37 @@ impl Parse for VariationValue<Number> {
} }
} }
/// A metrics override value for a @font-face descriptor
///
/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum MetricsOverride {
/// A non-negative `<percentage>` of the computed font size
Override(NonNegativePercentage),
/// Normal metrics from the font.
Normal,
}
impl MetricsOverride {
#[inline]
/// Get default value with `normal`
pub fn normal() -> MetricsOverride {
MetricsOverride::Normal
}
/// The ToComputedValue implementation, used for @font-face descriptors.
///
/// Valid override percentages must be non-negative; we return -1.0 to indicate
/// the absence of an override (i.e. 'normal').
#[inline]
pub fn compute(&self) -> ComputedPercentage {
match *self {
MetricsOverride::Normal => ComputedPercentage(-1.0),
MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
}
}
}
#[derive( #[derive(
Clone, Clone,
Copy, Copy,

View file

@ -181,7 +181,7 @@ impl Parse for Image {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Image, ParseError<'i>> { ) -> Result<Image, ParseError<'i>> {
Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true) Image::parse_with_cors_mode(context, input, CorsMode::None, /* allow_none = */ true, /* only_url = */ false)
} }
} }
@ -191,23 +191,32 @@ impl Image {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
cors_mode: CorsMode, cors_mode: CorsMode,
allow_none: bool, allow_none: bool,
only_url: bool,
) -> Result<Image, ParseError<'i>> { ) -> Result<Image, ParseError<'i>> {
if allow_none && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { if allow_none && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(generic::Image::None); return Ok(generic::Image::None);
} }
if let Ok(url) = input if let Ok(url) = input
.try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode)) .try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode))
{ {
return Ok(generic::Image::Url(url)); return Ok(generic::Image::Url(url));
} }
if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) {
return Ok(generic::Image::Gradient(Box::new(gradient)));
}
if image_set_enabled() { if image_set_enabled() {
if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode)) { if let Ok(is) = input.try_parse(|input| ImageSet::parse(context, input, cors_mode, only_url)) {
return Ok(generic::Image::ImageSet(Box::new(is))); return Ok(generic::Image::ImageSet(Box::new(is)));
} }
} }
if only_url {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) {
return Ok(generic::Image::Gradient(Box::new(gradient)));
}
if cross_fade_enabled() { if cross_fade_enabled() {
if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input, cors_mode)) { if let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input, cors_mode)) {
return Ok(generic::Image::CrossFade(Box::new(cf))); return Ok(generic::Image::CrossFade(Box::new(cf)));
@ -264,6 +273,21 @@ impl Image {
input, input,
CorsMode::Anonymous, CorsMode::Anonymous,
/* allow_none = */ true, /* allow_none = */ true,
/* only_url = */ false,
)
}
/// Provides an alternate method for parsing, but only for urls.
pub fn parse_only_url<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Image, ParseError<'i>> {
Self::parse_with_cors_mode(
context,
input,
CorsMode::None,
/* allow_none = */ false,
/* only_url = */ true,
) )
} }
} }
@ -310,7 +334,7 @@ impl CrossFadeImage {
cors_mode: CorsMode, cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
if let Ok(image) = input.try_parse(|input| { if let Ok(image) = input.try_parse(|input| {
Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false) Image::parse_with_cors_mode(context, input, cors_mode, /* allow_none = */ false, /* only_url = */ false)
}) { }) {
return Ok(Self::Image(image)); return Ok(Self::Image(image));
} }
@ -339,10 +363,11 @@ impl ImageSet {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
cors_mode: CorsMode, cors_mode: CorsMode,
only_url: bool,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("image-set")?; input.expect_function_matching("image-set")?;
let items = input.parse_nested_block(|input| { let items = input.parse_nested_block(|input| {
input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode)) input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url))
})?; })?;
Ok(Self { Ok(Self {
selected_index: 0, selected_index: 0,
@ -352,10 +377,18 @@ impl ImageSet {
} }
impl ImageSetItem { impl ImageSetItem {
fn parse_type<'i>(p: &mut Parser<'i, '_>) -> Result<crate::OwnedStr, ParseError<'i>> {
p.expect_function_matching("type")?;
p.parse_nested_block(|input| {
Ok(input.expect_string()?.as_ref().to_owned().into())
})
}
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
cors_mode: CorsMode, cors_mode: CorsMode,
only_url: bool,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let image = match input.try_parse(|i| i.expect_url_or_string()) { let image = match input.try_parse(|i| i.expect_url_or_string()) {
Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string( Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string(
@ -364,13 +397,23 @@ impl ImageSetItem {
cors_mode, cors_mode,
)), )),
Err(..) => Image::parse_with_cors_mode( Err(..) => Image::parse_with_cors_mode(
context, input, cors_mode, /* allow_none = */ false, context, input, cors_mode, /* allow_none = */ false, /* only_url = */ only_url
)?, )?,
}; };
let resolution = input
.try_parse(|input| Resolution::parse(context, input)) let mut resolution = input.try_parse(|input| Resolution::parse(context, input)).ok();
.unwrap_or(Resolution::X(1.0)); let mime_type = input.try_parse(Self::parse_type).ok();
Ok(Self { image, resolution })
// Try to parse resolution after type().
if mime_type.is_some() && resolution.is_none() {
resolution = input.try_parse(|input| Resolution::parse(context, input)).ok();
}
let resolution = resolution.unwrap_or(Resolution::X(1.0));
let has_mime_type = mime_type.is_some();
let mime_type = mime_type.unwrap_or_default();
Ok(Self { image, resolution, has_mime_type, mime_type })
} }
} }

View file

@ -1226,6 +1226,27 @@ impl Parse for Size {
} }
} }
macro_rules! parse_size_non_length {
($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{
let size = $input.try_parse(|input| {
Ok(try_match_ident_ignore_ascii_case! { input,
#[cfg(feature = "gecko")]
"min-content" | "-moz-min-content" => $size::MinContent,
#[cfg(feature = "gecko")]
"max-content" | "-moz-max-content" => $size::MaxContent,
#[cfg(feature = "gecko")]
"-moz-fit-content" => $size::MozFitContent,
#[cfg(feature = "gecko")]
"-moz-available" => $size::MozAvailable,
$auto_or_none => $size::$auto_or_none_ident,
})
});
if size.is_ok() {
return size;
}
}};
}
impl Size { impl Size {
/// Parses, with quirks. /// Parses, with quirks.
pub fn parse_quirky<'i, 't>( pub fn parse_quirky<'i, 't>(
@ -1233,16 +1254,7 @@ impl Size {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
#[cfg(feature = "gecko")] parse_size_non_length!(Size, input, "auto" => Auto);
{
if let Ok(l) = input.try_parse(computed::ExtremumLength::parse) {
return Ok(GenericSize::ExtremumLength(l));
}
}
if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
return Ok(GenericSize::Auto);
}
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
Ok(GenericSize::LengthPercentage(length)) Ok(GenericSize::LengthPercentage(length))
@ -1274,16 +1286,7 @@ impl MaxSize {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
#[cfg(feature = "gecko")] parse_size_non_length!(MaxSize, input, "none" => None);
{
if let Ok(l) = input.try_parse(computed::ExtremumLength::parse) {
return Ok(GenericMaxSize::ExtremumLength(l));
}
}
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(GenericMaxSize::None);
}
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
Ok(GenericMaxSize::LengthPercentage(length)) Ok(GenericMaxSize::LengthPercentage(length))

View file

@ -71,7 +71,7 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle; pub use self::outline::OutlineStyle;
pub use self::page::{Orientation, PageSize, PaperSize}; pub use self::page::{Orientation, PageSize, PaperSize};
pub use self::percentage::Percentage; pub use self::percentage::{Percentage, NonNegativePercentage};
pub use self::position::AspectRatio; pub use self::position::AspectRatio;
pub use self::position::{ pub use self::position::{
GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto,
@ -86,10 +86,11 @@ pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::svg_path::SVGPathData; pub use self::svg_path::SVGPathData;
pub use self::text::TextAlignLast; pub use self::text::TextAlignLast;
pub use self::text::TextUnderlinePosition; pub use self::text::TextUnderlinePosition;
pub use self::text::RubyPosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign}; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextTransform}; pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform}; pub use self::transform::{Rotate, Scale, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};

View file

@ -7,6 +7,7 @@
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::computed::percentage::Percentage as ComputedPercentage; use crate::values::computed::percentage::Percentage as ComputedPercentage;
use crate::values::computed::{Context, ToComputedValue}; use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::NonNegative;
use crate::values::specified::calc::CalcNode; use crate::values::specified::calc::CalcNode;
use crate::values::specified::Number; use crate::values::specified::Number;
use crate::values::{serialize_percentage, CSSFloat}; use crate::values::{serialize_percentage, CSSFloat};
@ -172,3 +173,24 @@ impl ToComputedValue for Percentage {
} }
impl SpecifiedValueInfo for Percentage {} impl SpecifiedValueInfo for Percentage {}
/// A wrapper of Percentage, whose value must be >= 0.
pub type NonNegativePercentage = NonNegative<Percentage>;
impl Parse for NonNegativePercentage {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Ok(NonNegative(Percentage::parse_non_negative(context, input)?))
}
}
impl NonNegativePercentage {
/// Convert to ComputedPercentage, for FontFaceRule size-adjust getter.
#[inline]
pub fn compute(&self) -> ComputedPercentage {
ComputedPercentage(self.0.get())
}
}

View file

@ -22,6 +22,7 @@ use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::values::SequenceWriter; use style_traits::values::SequenceWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use style_traits::{KeywordsCollectFn, SpecifiedValueInfo};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
/// A specified type for the `initial-letter` property. /// A specified type for the `initial-letter` property.
@ -1000,6 +1001,67 @@ pub enum WordBreak {
BreakWord, BreakWord,
} }
/// Values for the `text-justify` CSS property.
#[repr(u8)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
pub enum TextJustify {
Auto,
None,
InterWord,
// See https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
// and https://github.com/w3c/csswg-drafts/issues/6156 for the alias.
#[parse(aliases = "distribute")]
InterCharacter,
}
/// Values for the `-moz-control-character-visibility` CSS property.
#[repr(u8)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
pub enum MozControlCharacterVisibility {
Hidden,
Visible,
}
#[cfg(feature = "gecko")]
impl Default for MozControlCharacterVisibility {
fn default() -> Self {
if static_prefs::pref!("layout.css.control-characters.visible") {
Self::Visible
} else {
Self::Hidden
}
}
}
/// Values for the `line-break` property. /// Values for the `line-break` property.
#[repr(u8)] #[repr(u8)]
#[derive( #[derive(
@ -1193,3 +1255,72 @@ impl ToCss for TextUnderlinePosition {
Ok(()) Ok(())
} }
} }
/// Values for `ruby-position` property
#[repr(u8)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
pub enum RubyPosition {
AlternateOver,
AlternateUnder,
Over,
Under,
}
impl Parse for RubyPosition {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<RubyPosition, ParseError<'i>> {
// Parse alternate before
let alternate = input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok();
if alternate && input.is_exhausted() {
return Ok(RubyPosition::AlternateOver);
}
// Parse over / under
let over = try_match_ident_ignore_ascii_case! { input,
"over" => true,
"under" => false,
};
// Parse alternate after
let alternate = alternate ||
input.try_parse(|i| i.expect_ident_matching("alternate")).is_ok();
Ok(match (over, alternate) {
(true, true) => RubyPosition::AlternateOver,
(false, true) => RubyPosition::AlternateUnder,
(true, false) => RubyPosition::Over,
(false, false) => RubyPosition::Under,
})
}
}
impl ToCss for RubyPosition {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
dest.write_str(match self {
RubyPosition::AlternateOver => "alternate",
RubyPosition::AlternateUnder => "alternate under",
RubyPosition::Over => "over",
RubyPosition::Under => "under",
})
}
}
impl SpecifiedValueInfo for RubyPosition {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&["alternate", "over", "under"])
}
}

View file

@ -7,17 +7,17 @@
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::generics::ui as generics; use crate::values::generics::ui as generics;
use crate::values::specified::color::Color; use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedImageUrl; use crate::values::specified::image::Image;
use crate::values::specified::Number; use crate::values::specified::Number;
use cssparser::Parser; use cssparser::Parser;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
/// A specified value for the `cursor` property. /// A specified value for the `cursor` property.
pub type Cursor = generics::GenericCursor<CursorImage>; pub type Cursor = generics::GenericCursor<CursorImage>;
/// A specified value for item of `image cursors`. /// A specified value for item of `image cursors`.
pub type CursorImage = generics::GenericCursorImage<SpecifiedImageUrl, Number>; pub type CursorImage = generics::GenericCursorImage<Image, Number>;
impl Parse for Cursor { impl Parse for Cursor {
/// cursor: [<url> [<number> <number>]?]# [auto | default | ...] /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
@ -47,7 +47,7 @@ impl Parse for CursorImage {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
use crate::Zero; use crate::Zero;
let url = SpecifiedImageUrl::parse(context, input)?; let image = Image::parse_only_url(context, input)?;
let mut has_hotspot = false; let mut has_hotspot = false;
let mut hotspot_x = Number::zero(); let mut hotspot_x = Number::zero();
let mut hotspot_y = Number::zero(); let mut hotspot_y = Number::zero();
@ -59,7 +59,7 @@ impl Parse for CursorImage {
} }
Ok(Self { Ok(Self {
url, image,
has_hotspot, has_hotspot,
hotspot_x, hotspot_x,
hotspot_y, hotspot_y,
@ -67,6 +67,13 @@ impl Parse for CursorImage {
} }
} }
// This trait is manually implemented because we don't support the whole <image>
// syntax for cursors
impl SpecifiedValueInfo for CursorImage {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&["url", "image-set"]);
}
}
/// Specified value of `-moz-force-broken-image-icon` /// Specified value of `-moz-force-broken-image-icon`
#[derive( #[derive(
Clone, Clone,

View file

@ -91,6 +91,11 @@ packages = [
# Files that are ignored for all tidy and lint checks. # Files that are ignored for all tidy and lint checks.
files = [ files = [
"./components/net/tests/parsable_mime/text", "./components/net/tests/parsable_mime/text",
# These are ignored to avoid diverging from Gecko
"./components/style/properties/helpers.mako.rs",
"./components/style/stylesheets/rule_parser.rs",
"./components/style/stylist.rs",
"./components/style/values/computed/image.rs",
# Mako does not lend itself easily to splitting long lines # Mako does not lend itself easily to splitting long lines
"./components/style/properties/helpers/animated_properties.mako.rs", "./components/style/properties/helpers/animated_properties.mako.rs",
"./components/style/properties/shorthands/text.mako.rs", "./components/style/properties/shorthands/text.mako.rs",

View file

@ -1,12 +0,0 @@
[clamp-length-computed.html]
[Property letter-spacing value 'clamp(10px, 35px , 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px, 35px /*foo*/, 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px /* foo */ , 35px, 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px , 35px, 30px)']
expected: FAIL

View file

@ -1,7 +1,4 @@
[inheritance.html] [inheritance.html]
[Property text-justify inherits]
expected: FAIL
[Property text-align-all has initial value start] [Property text-align-all has initial value start]
expected: FAIL expected: FAIL

View file

@ -1,3 +0,0 @@
[text-justify-computed-legacy.html]
[Property text-justify value 'distribute']
expected: FAIL

View file

@ -1,7 +1,3 @@
[text-justify-computed.html] [text-justify-computed.html]
[Property text-justify value 'inter-character' computes to 'inter-character'] [Property text-justify value 'inter-character' computes to 'inter-character']
expected: FAIL expected: FAIL
[Property text-justify value 'inter-character']
expected: FAIL

View file

@ -1,4 +0,0 @@
[text-justify-valid.html]
[e.style['text-justify'\] = "inter-character" should set the property value]
expected: FAIL

View file

@ -1,3 +0,0 @@
[distribute-alias.tentative.html]
[text-justify: distribute is a parse-time alias of inter-character]
expected: FAIL

View file

@ -47,18 +47,6 @@
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [inter-character\]] [CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [inter-character\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0.5) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0.6) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (1) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (1.5) should be [inter-character\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [inter-character\]] [CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [inter-character\]]
expected: FAIL expected: FAIL
@ -68,39 +56,6 @@
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [inter-character\]] [CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [inter-character\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (0.5) should be [inter-character\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (0.6) should be [inter-character\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (1) should be [inter-character\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [auto\] to [inter-character\] at (1.5) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [auto\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (0) should be [auto\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [auto\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (0.5) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (0.6) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (1) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [auto\] to [inter-character\] at (1.5) should be [inter-character\]]
expected: FAIL
[Web Animations: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [auto\]] [Web Animations: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [auto\]]
expected: FAIL expected: FAIL
@ -131,60 +86,6 @@
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [distribute\]] [CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [distribute\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (-0.3) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (0) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-character\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (0) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [inter-character\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-character\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[Web Animations: property <text-justify> from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]] [Web Animations: property <text-justify> from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]]
expected: FAIL expected: FAIL
@ -215,18 +116,6 @@
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [distribute\]] [CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [distribute\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [distribute\]] [CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [distribute\]]
expected: FAIL expected: FAIL
@ -236,39 +125,6 @@
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [distribute\]] [CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [distribute\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [inter-word\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (0) should be [inter-word\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [inter-word\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (0.5) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (0.6) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (1) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [inter-word\] to [distribute\] at (1.5) should be [distribute\]]
expected: FAIL
[Web Animations: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]] [Web Animations: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]]
expected: FAIL expected: FAIL
@ -299,18 +155,6 @@
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]] [CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.5) should be [none\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.6) should be [none\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (1) should be [none\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [none\]] [CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [none\]]
expected: FAIL expected: FAIL
@ -320,39 +164,6 @@
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]] [CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (0.5) should be [none\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (0.6) should be [none\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (1) should be [none\]]
expected: FAIL
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (0) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (0.3) should be [distribute\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (0.5) should be [none\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (0.6) should be [none\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (1) should be [none\]]
expected: FAIL
[CSS Animations: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]]
expected: FAIL
[Web Animations: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [distribute\]] [Web Animations: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [distribute\]]
expected: FAIL expected: FAIL
@ -373,39 +184,3 @@
[Web Animations: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]] [Web Animations: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (-0.3) should be [auto\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0) should be [auto\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [auto\] to [inter-character\] at (0.3) should be [auto\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (-0.3) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [inter-character\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (-0.3) should be [inter-word\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0) should be [inter-word\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [inter-word\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (-0.3) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0) should be [distribute\]]
expected: FAIL
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.3) should be [distribute\]]
expected: FAIL

View file

@ -10,15 +10,3 @@
[Property letter-spacing value 'clamp(10px, 20px, 30px)' computes to '20px'] [Property letter-spacing value 'clamp(10px, 20px, 30px)' computes to '20px']
expected: FAIL expected: FAIL
[Property letter-spacing value 'clamp(10px, 35px , 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px, 35px /*foo*/, 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px /* foo */ , 35px, 30px)']
expected: FAIL
[Property letter-spacing value 'clamp(10px , 35px, 30px)']
expected: FAIL

Some files were not shown because too many files have changed in this diff Show more