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",
"malloc_size_of",
"malloc_size_of_derive",
"mime",
"new_debug_unreachable",
"num-derive",
"num-integer",

View file

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

View file

@ -21,6 +21,7 @@ use std::fmt;
use std::sync::{Arc, Mutex};
use style::properties::ComputedValues;
use style::servo::url::ComputedUrl;
use style::values::computed::image::Image as ComputedImage;
use style::values::computed::{Length, LengthOrAuto};
use style::values::CSSFloat;
use style::Zero;
@ -184,6 +185,17 @@ impl ReplacedContent {
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>> {
let intrinsic_size = PhysicalSize::new(self.intrinsic.width, self.intrinsic.height);
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::context::SharedStyleContext;
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::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
@ -1285,11 +1285,7 @@ impl LayoutThread {
);
// Flush shadow roots stylesheets if dirty.
document.flush_shadow_roots_stylesheets(
&self.stylist.device(),
document.quirks_mode(),
guards.author.clone(),
);
document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author.clone());
let restyles = std::mem::take(&mut data.pending_restyles);
debug!("Draining restyles: {}", restyles.len());

View file

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

View file

@ -461,6 +461,8 @@ impl GroupedClassifier {
fn image_classifer() -> GroupedClassifier {
GroupedClassifier {
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_cursor()),
Box::new(ByteMatcher::image_bmp()),

View file

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

View file

@ -3525,11 +3525,11 @@ impl Element {
}
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) {
self.set_state(ElementState::IN_READ_WRITE_STATE, value)
self.set_state(ElementState::IN_READWRITE_STATE, value)
}
pub fn placeholder_shown_state(&self) -> bool {

View file

@ -297,7 +297,7 @@ impl HTMLInputElement {
.clone();
HTMLInputElement {
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,
prefix,
document,

View file

@ -148,7 +148,7 @@ impl HTMLTextAreaElement {
.clone();
HTMLTextAreaElement {
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,
prefix,
document,

View file

@ -19,15 +19,13 @@ use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::window::Window;
use crate::stylesheet_set::StylesheetSetRef;
use dom_struct::dom_struct;
use selectors::context::QuirksMode;
use servo_arc::Arc;
use servo_atoms::Atom;
use style::author_styles::AuthorStyles;
use style::dom::TElement;
use style::media_queries::Device;
use style::shared_lock::SharedRwLockReadGuard;
use style::stylesheets::Stylesheet;
use style::stylist::CascadeData;
use style::stylist::{CascadeData, Stylist};
/// Whether a shadow root hosts an User Agent widget.
#[derive(JSTraceable, MallocSizeOf, PartialEq)]
@ -245,8 +243,7 @@ pub trait LayoutShadowRootHelpers<'dom> {
fn get_style_data_for_layout(self) -> &'dom CascadeData;
unsafe fn flush_stylesheets<E: TElement>(
self,
device: &Device,
quirks_mode: QuirksMode,
stylist: &mut Stylist,
guard: &SharedRwLockReadGuard,
);
}
@ -277,13 +274,12 @@ impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
#[allow(unsafe_code)]
unsafe fn flush_stylesheets<E: TElement>(
self,
device: &Device,
quirks_mode: QuirksMode,
stylist: &mut Stylist,
guard: &SharedRwLockReadGuard,
) {
let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout();
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 std::marker::PhantomData;
use style::dom::{TDocument, TNode};
use style::media_queries::Device;
use style::shared_lock::{
SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard as StyleSharedRwLockReadGuard,
};
use style::stylist::Stylist;
// A wrapper around documents that ensures ayout can only ever access safe properties.
pub struct ServoLayoutDocument<'dom, LayoutDataType: LayoutDataTrait> {
@ -90,8 +90,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy
pub fn flush_shadow_roots_stylesheets(
&self,
device: &Device,
quirks_mode: QuirksMode,
stylist: &mut Stylist,
guard: &StyleSharedRwLockReadGuard,
) {
unsafe {
@ -100,7 +99,7 @@ impl<'ld, LayoutDataType: LayoutDataTrait> ServoLayoutDocument<'ld, LayoutDataTy
}
self.document.flush_shadow_roots_stylesheets();
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::ServoLayoutNode;
use script_layout_interface::wrapper_traits::LayoutDataTrait;
use selectors::matching::QuirksMode;
use std::fmt;
use std::marker::PhantomData;
use style::dom::TShadowRoot;
use style::media_queries::Device;
use style::shared_lock::SharedRwLockReadGuard as StyleSharedRwLockReadGuard;
use style::stylist::CascadeData;
use style::stylist::{CascadeData, Stylist};
pub struct ServoShadowRoot<'dom, LayoutDataType: LayoutDataTrait> {
/// The wrapped private DOM ShadowRoot.
@ -74,11 +72,10 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ServoShadowRoot<'dom, LayoutDataType
pub unsafe fn flush_stylesheets(
&self,
device: &Device,
quirks_mode: QuirksMode,
stylist: &mut Stylist,
guard: &StyleSharedRwLockReadGuard,
) {
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
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 => {
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) => {
// :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
@ -825,7 +859,7 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {
#[inline]
pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
self.selector_length() > 0 &&
self.all(|component| matches!(*component, Component::Host(..))) &&
self.all(|component| component.is_host()) &&
self.next_sequence().is_none()
}
@ -1089,10 +1123,17 @@ pub enum Component<Impl: SelectorImpl> {
impl<Impl: SelectorImpl> Component<Impl> {
/// Returns true if this is a combinator.
#[inline]
pub fn is_combinator(&self) -> bool {
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.
pub fn as_combinator(&self) -> Option<Combinator> {
match *self {

View file

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

View file

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

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)
where
E: TElement,
@ -115,6 +125,15 @@ where
}
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> {

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.
///
/// 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
/// given part name.
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
const IN_BROKEN_STATE = 1 << 14;
/// 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>
const IN_REQUIRED_STATE = 1 << 21;
const IN_REQUIRED_STATE = 1 << 16;
/// <https://html.spec.whatwg.org/multipage/#selector-optional>
const IN_OPTIONAL_STATE = 1 << 22;
/// <https://html.spec.whatwg.org/multipage/#selector-read-write>
const IN_READ_WRITE_STATE = 1 << 22;
const IN_OPTIONAL_STATE = 1 << 17;
/// <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>
const IN_VISITED_STATE = 1 << 24;
const IN_VISITED_STATE = 1 << 19;
/// <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>
const IN_VISITED_OR_UNVISITED_STATE = ElementState::IN_VISITED_STATE.bits |
ElementState::IN_UNVISITED_STATE.bits;
/// 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>
const IN_INRANGE_STATE = 1 << 27;
const IN_INRANGE_STATE = 1 << 22;
/// <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>
const IN_READONLY_STATE = 1 << 29;
const IN_READONLY_STATE = 1 << 24;
/// <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>
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
const IN_MOZ_SUBMITINVALID_STATE = 1 << 32;
const IN_MOZ_SUBMITINVALID_STATE = 1 << 27;
/// Non-standard & undocumented.
const IN_OPTIMUM_STATE = 1 << 33;
const IN_OPTIMUM_STATE = 1 << 28;
/// Non-standard & undocumented.
const IN_SUB_OPTIMUM_STATE = 1 << 34;
const IN_SUB_OPTIMUM_STATE = 1 << 29;
/// Non-standard & undocumented.
const IN_SUB_SUB_OPTIMUM_STATE = 1 << 35;
const IN_SUB_SUB_OPTIMUM_STATE = 1 << 30;
/// Non-standard & undocumented.
const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 36;
/// Non-standard & undocumented.
const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 37;
/// 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;
const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 31;
/// <https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo>
const IN_FOCUSRING_STATE = 1 << 32;
/// <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.
/// 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.
const IN_RTL_STATE = 1 << 45;
const IN_RTL_STATE = 1 << 35;
/// 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
/// 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
/// 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
/// any HTML elements (including <bdi>) with dir="auto" (or something
/// 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.
const IN_AUTOFILL_STATE = 1 << 50;
const IN_AUTOFILL_STATE = 1 << 40;
/// 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
///
/// https://html.spec.whatwg.org/multipage/#centered-alignment
const IN_MODAL_DIALOG_STATE = 1 << 53;
/// https://html.spec.whatwg.org/multipage/#inert-subtrees
const IN_MOZINERT_STATE = 1 << 54;
/// <https://html.spec.whatwg.org/multipage/#centered-alignment>
const IN_MODAL_DIALOG_STATE = 1 << 42;
/// <https://html.spec.whatwg.org/multipage/#inert-subtrees>
const IN_MOZINERT_STATE = 1 << 43;
/// State for the topmost dialog element in top layer
const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 55;
/// Non-standard & undocumented.
const IN_HANDLER_NOPLUGINS = 1 << 56;
const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 44;
/// Initially used for the devtools highlighter, but now somehow only
/// 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>),
/// A value was not recognized.
UnsupportedValue(&'a str, ParseError<'a>),
/// A never-matching `:host` selector was found.
NeverMatchingHostSelector(String),
}
impl<'a> fmt::Display for ContextualParseError<'a> {
@ -210,6 +212,9 @@ impl<'a> fmt::Display for ContextualParseError<'a> {
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")]
use crate::values::specified::font::SpecifiedFontVariationSettings;
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::Angle;
#[cfg(feature = "gecko")]
use crate::values::specified::NonNegativePercentage;
#[cfg(feature = "gecko")]
use cssparser::UnicodeRange;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{CowRcStr, SourceLocation};
@ -194,7 +198,7 @@ impl FontStretchRange {
fn compute_stretch(s: &FontStretch) -> f32 {
match *s {
FontStretch::Keyword(ref kw) => kw.compute().0,
FontStretch::Stretch(ref p) => p.get(),
FontStretch::Stretch(ref p) => p.0.get(),
FontStretch::System(..) => unreachable!(),
}
}
@ -418,6 +422,18 @@ macro_rules! is_descriptor_enabled {
("font-variation-settings") => {
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) => {
true
};
@ -576,6 +592,18 @@ font_face_descriptors! {
/// The language override of this font face.
"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.
use crate::context::QuirksMode;
use crate::dom::TElement;
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes};
@ -15,7 +14,7 @@ use crate::media_queries::{Device, MediaList};
use crate::properties::ComputedValues;
use crate::selector_parser::SnapshotMap;
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument};
use crate::stylesheets::{StylesheetContents, StylesheetInDocument};
use crate::stylist::Stylist;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use malloc_size_of::MallocSizeOfOps;
@ -69,16 +68,6 @@ impl GeckoStyleSheet {
fn inner(&self) -> &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 {
@ -95,14 +84,6 @@ impl Clone 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> {
use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList;
use std::mem;
@ -120,13 +101,19 @@ impl StylesheetInDocument for GeckoStyleSheet {
// All the stylesheets Servo knows about are enabled, because that state is
// handled externally by Gecko.
#[inline]
fn enabled(&self) -> bool {
true
}
#[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.contents().rules(guard)
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
}
}
}

View file

@ -275,10 +275,6 @@ enum PrefersReducedMotion {
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.
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)]
@ -286,8 +282,6 @@ fn color_scheme_no_preference_enabled(_: &crate::parser::ParserContext) -> bool
pub enum PrefersColorScheme {
Light,
Dark,
#[parse(condition = "color_scheme_no_preference_enabled")]
NoPreference,
}
/// 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()) };
match query_value {
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)
}
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(
device: &Device,
query_value: Option<Atom>,
@ -573,15 +590,63 @@ fn eval_moz_os_version(
query_value.as_ptr() == os_version
}
macro_rules! system_metric_feature {
($feature_name:expr) => {{
fn __eval(device: &Device, query_value: Option<bool>, _: Option<RangeOrOperator>) -> bool {
eval_system_metric(
device,
query_value,
fn get_lnf_int(int_id: i32) -> i32 {
unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
}
fn get_lnf_int_as_bool(int_id: i32) -> bool {
get_lnf_int(int_id) != 0
}
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,
/* accessible_from_content = */ false,
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!(
@ -598,7 +663,7 @@ macro_rules! system_metric_feature {
/// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [
pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -808,27 +873,40 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [
Evaluator::BoolInteger(eval_moz_non_native_content_theme),
ParsingRequirements::CHROME_AND_UA_ONLY,
),
system_metric_feature!(atom!("-moz-scrollbar-start-backward")),
system_metric_feature!(atom!("-moz-scrollbar-start-forward")),
system_metric_feature!(atom!("-moz-scrollbar-end-backward")),
system_metric_feature!(atom!("-moz-scrollbar-end-forward")),
system_metric_feature!(atom!("-moz-scrollbar-thumb-proportional")),
system_metric_feature!(atom!("-moz-overlay-scrollbars")),
system_metric_feature!(atom!("-moz-windows-default-theme")),
system_metric_feature!(atom!("-moz-mac-graphite-theme")),
system_metric_feature!(atom!("-moz-mac-big-sur-theme")),
system_metric_feature!(atom!("-moz-windows-accent-color-in-titlebar")),
system_metric_feature!(atom!("-moz-windows-compositor")),
system_metric_feature!(atom!("-moz-windows-classic")),
system_metric_feature!(atom!("-moz-windows-glass")),
system_metric_feature!(atom!("-moz-menubar-drag")),
system_metric_feature!(atom!("-moz-swipe-animation-enabled")),
system_metric_feature!(atom!("-moz-gtk-csd-available")),
system_metric_feature!(atom!("-moz-gtk-csd-hide-titlebar-by-default")),
system_metric_feature!(atom!("-moz-gtk-csd-transparent-background")),
system_metric_feature!(atom!("-moz-gtk-csd-minimize-button")),
system_metric_feature!(atom!("-moz-gtk-csd-maximize-button")),
system_metric_feature!(atom!("-moz-gtk-csd-close-button")),
system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")),
system_metric_feature!(atom!("-moz-system-dark-theme")),
feature!(
atom!("-moz-toolbar-prefers-color-scheme"),
AllowsRanges::No,
keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme),
ParsingRequirements::CHROME_AND_UA_ONLY,
),
lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward),
lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward),
lnf_int_feature!(atom!("-moz-scrollbar-end-backward"), ScrollArrowStyle, get_scrollbar_end_backward),
lnf_int_feature!(atom!("-moz-scrollbar-end-forward"), ScrollArrowStyle, get_scrollbar_end_forward),
lnf_int_feature!(atom!("-moz-scrollbar-thumb-proportional"), ScrollSliderStyle),
lnf_int_feature!(atom!("-moz-overlay-scrollbars"), UseOverlayScrollbars),
lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag),
lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme),
lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme),
lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme),
lnf_int_feature!(atom!("-moz-windows-accent-color-in-titlebar"), WindowsAccentColorInTitlebar),
lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor),
lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic),
lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass),
lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled),
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)
}
/// 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 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> {

View file

@ -570,7 +570,7 @@ impl<'le> GeckoElement<'le> {
}
#[inline(always)]
fn attrs(&self) -> &[structs::AttrArray_InternalAttr] {
fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
unsafe {
let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() {
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)]
fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
if !self.has_part_attr() {
return None;
}
snapshot_helpers::find_attr(self.attrs(), &atom!("part"))
snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part"))
}
#[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]
@ -1167,7 +1184,7 @@ impl<'le> TElement for GeckoElement<'le> {
#[inline]
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.
@ -1177,7 +1194,25 @@ impl<'le> TElement for GeckoElement<'le> {
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)
@ -1197,7 +1232,7 @@ impl<'le> TElement for GeckoElement<'le> {
where
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)
@ -2058,7 +2093,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
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,
None => return false,
};
@ -2078,7 +2113,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline]
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)]

View file

@ -185,7 +185,7 @@ pub struct DocumentStateDependency {
/// 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
/// state/other attribute affecting selectors.
#[derive(Debug, MallocSizeOf)]
#[derive(Clone, Debug, MallocSizeOf)]
pub struct InvalidationMap {
/// A map from a given class name to all the selectors with that class
/// selector.

View file

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

View file

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

View file

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

View file

@ -196,23 +196,37 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
_ => return Err(()),
}
};
Ok(Some(match first_delim {
'=' => Operator::Equal,
'>' => {
if input.try_parse(|i| i.expect_delim('=')).is_ok() {
Operator::GreaterThanEqual
} else {
Operator::GreaterThan
}
},
'<' => {
if input.try_parse(|i| i.expect_delim('=')).is_ok() {
Operator::LessThanEqual
} else {
Operator::LessThan
}
},
let operator = match first_delim {
'=' => return Ok(Some(Operator::Equal)),
'>' => Operator::GreaterThan,
'<' => Operator::LessThan,
_ => 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")]
{
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
// the web it is hidden behind a preference.

View file

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

View file

@ -379,13 +379,15 @@ where
type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
fn tweak_when_ignoring_colors(
builder: &StyleBuilder,
context: &computed::Context,
longhand_id: LonghandId,
origin: Origin,
declaration: &mut Cow<PropertyDeclaration>,
declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
) {
use crate::values::specified::Color;
use crate::values::computed::ToComputedValue;
use cssparser::RGBA;
if !longhand_id.ignored_when_document_colors_disabled() {
return;
@ -399,39 +401,25 @@ fn tweak_when_ignoring_colors(
// 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
// 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
{
return;
}
fn alpha_channel(color: &Color) -> u8 {
match *color {
// Seems safe enough to assume that the default color and system
// colors are opaque in HCM, though maybe we shouldn't asume the
// 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,
}
fn alpha_channel(color: &Color, context: &computed::Context) -> u8 {
// We assume here currentColor is opaque.
let color = color.to_computed_value(context).to_rgba(RGBA::new(0, 0, 0, 255));
color.alpha
}
// A few special-cases ahead.
match **declaration {
PropertyDeclaration::BackgroundColor(ref color) => {
// We honor system colors.
if color.is_system() {
return;
}
// For background-color, we revert or initial-with-preserved-alpha
// otherwise, this is needed to preserve semi-transparent
// backgrounds.
@ -440,24 +428,27 @@ fn tweak_when_ignoring_colors(
// should consider not doing that even if it causes some issues like
// bug 1625036, or finding a performant way to preserve the original
// widget background color's rgb channels but not alpha...
let alpha = alpha_channel(color);
let alpha = alpha_channel(color, context);
if alpha != 0 {
let mut color = builder.device.default_background_color();
let mut color = context.builder.device.default_background_color();
color.alpha = alpha;
declarations_to_apply_unless_overriden
.push(PropertyDeclaration::BackgroundColor(color.into()))
}
},
PropertyDeclaration::Color(ref color) => {
// We honor color: transparent, and "revert-or-initial" otherwise.
if alpha_channel(&color.0) == 0 {
// We honor color: transparent and system colors.
if color.0.is_system() {
return;
}
if alpha_channel(&color.0, context) == 0 {
return;
}
// If the inherited color would be transparent, but we would
// override this with a non-transparent color, then override it with
// the default color. Otherwise just let it inherit through.
if builder.get_parent_inherited_text().clone_color().alpha == 0 {
let color = builder.device.default_color();
if context.builder.get_parent_inherited_text().clone_color().alpha == 0 {
let color = context.builder.device.default_color();
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
specified::ColorPropertyValue(color.into()),
))
@ -631,7 +622,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// properties that are marked as ignored in that mode.
if ignore_colors {
tweak_when_ignoring_colors(
&self.context.builder,
&self.context,
longhand_id,
origin,
&mut declaration,

View file

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

View file

@ -261,6 +261,11 @@ class Property(object):
self.extra_prefixes = parse_property_aliases(extra_prefixes)
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):
if engine == "gecko":
return bool(self.gecko_pref)
@ -478,6 +483,7 @@ class Longhand(Property):
"LineBreak",
"MasonryAutoFlow",
"MozForceBrokenImageIcon",
"text::MozControlCharacterVisibility",
"MozListReversed",
"MathDepth",
"MozScriptMinSize",
@ -495,6 +501,7 @@ class Longhand(Property):
"Percentage",
"PositiveIntegerOrNone",
"Resize",
"RubyPosition",
"SVGOpacity",
"SVGPaintOrder",
"ScrollSnapAlign",
@ -505,6 +512,7 @@ class Longhand(Property):
"TextAlignLast",
"TextDecorationLine",
"TextEmphasisPosition",
"TextJustify",
"TextTransform",
"TextUnderlinePosition",
"TouchAction",
@ -597,6 +605,11 @@ class Alias(object):
def type():
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):
if engine == "gecko":
return bool(self.gecko_pref)
@ -866,6 +879,7 @@ class PropertyRestrictions:
def marker(data):
return set(
[
"white-space",
"color",
"text-combine-upright",
"text-transform",

View file

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

View file

@ -554,7 +554,7 @@
keyword = keyword=Keyword(name, values, **keyword_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 {
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
@ -707,8 +707,7 @@
</%def>
<%def name="single_keyword(name, values, vector=False,
extra_specified=None, needs_conversion=False,
gecko_pref_controlled_initial_value=None, **kwargs)">
needs_conversion=False, **kwargs)">
<%
keyword_kwargs = {a: kwargs.pop(a, None) for a in [
'gecko_constant_prefix',
@ -725,11 +724,13 @@
]}
%>
<%def name="inner_body(keyword, extra_specified=None, needs_conversion=False,
gecko_pref_controlled_initial_value=None)">
<%def name="variants(variants, include_aliases)">
% for variant in variants:
% if include_aliases:
<%def name="inner_body(keyword, needs_conversion=False)">
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[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 = []
for alias, v in keyword.aliases_for(engine).items():
@ -739,56 +740,16 @@
% if aliases:
#[parse(aliases = "${','.join(sorted(aliases))}")]
% endif
% endif
${to_camel_case(variant)},
% 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]
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])}
}
#[inline]
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])}
}
#[inline]
@ -799,10 +760,7 @@
% if needs_conversion:
<%
conversion_values = keyword.values_for(engine)
if extra_specified:
conversion_values += extra_specified.split()
conversion_values += keyword.aliases_for(engine).keys()
conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys())
%>
${gecko_keyword_conversion(keyword, values=conversion_values)}
% endif
@ -817,8 +775,7 @@
% else:
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
${inner_body(Keyword(name, values, **keyword_kwargs),
extra_specified=extra_specified, needs_conversion=needs_conversion,
gecko_pref_controlled_initial_value=gecko_pref_controlled_initial_value)}
needs_conversion=needs_conversion)}
% if caller:
${caller.body()}
% endif

View file

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

View file

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

View file

@ -288,7 +288,7 @@ ${helpers.predefined_type(
)}
% if engine == "gecko":
pub mod system_font {
pub mod system_font {
//! We deal with system fonts here
//!
//! System fonts can only be set as a group via the font shorthand.
@ -309,19 +309,13 @@ ${helpers.predefined_type(
//! variable reference. We may want to improve this behavior at some
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
use cssparser::{Parser, ToCss};
use crate::values::computed::font::GenericFontFamily;
use crate::properties::longhands;
use std::fmt;
use std::hash::{Hash, Hasher};
use style_traits::ParseError;
use crate::values::computed::{ToComputedValue, Context};
use crate::values::specified::font::SystemFont;
<%
system_fonts = """caption icon menu message-box small-caption status-bar
-moz-window -moz-document -moz-workspace -moz-desktop
-moz-info -moz-dialog -moz-button -moz-pull-down-menu
-moz-list -moz-field""".split()
kw_font_props = """font_variant_caps
font_kerning font_variant_position font_variant_ligatures
font_variant_east_asian font_variant_numeric
@ -329,13 +323,6 @@ ${helpers.predefined_type(
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
// so we need these impls. We don't want to
@ -360,26 +347,18 @@ ${helpers.predefined_type(
fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
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 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,
*self,
cx.style().get_font().gecko(),
cx.device().document()
);
@ -455,41 +434,7 @@ ${helpers.predefined_type(
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:
pub mod system_font {
use cssparser::Parser;
// We don't parse system fonts, 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)]
/// void enum for system font, can never exist
pub enum SystemFont {}
impl SystemFont {
pub fn parse(_: &mut Parser) -> Result<Self, ()> {
Err(())
}
}
}
}
% endif
${helpers.single_keyword(

View file

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

View file

@ -96,45 +96,16 @@ ${helpers.predefined_type(
servo_restyle_damage="rebuild_and_reflow",
)}
// TODO(pcwalton): Support `text-justify: distribute`.
<%helpers:single_keyword
name="text-justify"
values="auto none inter-word"
${helpers.predefined_type(
"text-justify",
"TextJustify",
"computed::TextJustify::Auto",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
extra_gecko_values="inter-character"
extra_specified="${'distribute' if engine == 'gecko' else ''}"
gecko_enum_prefix="StyleTextJustify"
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>
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-justify",
servo_restyle_damage="rebuild_and_reflow",
)}
${helpers.predefined_type(
"text-align-last",
@ -329,13 +300,13 @@ ${helpers.single_keyword(
spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
)}
${helpers.single_keyword(
${helpers.predefined_type(
"ruby-position",
"over under",
"RubyPosition",
"computed::RubyPosition::AlternateOver",
engines="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
gecko_enum_prefix="StyleRubyPosition",
animation_value_type="discrete",
)}
// CSS Writing Modes Module Level 3
@ -360,15 +331,13 @@ ${helpers.single_keyword(
servo_restyle_damage="rebuild_and_reflow",
)}
${helpers.single_keyword(
${helpers.predefined_type(
"-moz-control-character-visibility",
"hidden visible",
"text::MozControlCharacterVisibility",
"Default::default()",
engines="gecko",
gecko_enum_prefix="StyleControlCharacterVisibility",
gecko_pref_controlled_initial_value="layout.css.control-characters.visible=visible",
animation_value_type="none",
gecko_ffi_name="mControlCharacterVisibility",
spec="Nonstandard",
spec="Nonstandard"
)}
// text underline offset

View file

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

View file

@ -51,6 +51,7 @@ ${helpers.predefined_type(
engines="gecko",
boxed=True,
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)",
)}
% 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()",
engines="gecko servo-2013 servo-2020",
spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
flags="CREATES_STACKING_CONTEXT",
animation_value_type="ComputedValue",
)}

View file

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

View file

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

View file

@ -32,7 +32,6 @@ use crate::computed_value_flags::*;
use crate::hash::FxHashMap;
use crate::media_queries::Device;
use crate::parser::ParserContext;
use crate::properties::longhands::system_font::SystemFont;
use crate::selector_parser::PseudoElement;
#[cfg(feature = "servo")] use servo_config::prefs;
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::NonNegativeLength;
use crate::values::serialize_atom_name;
use crate::values::specified::font::SystemFont;
use crate::rule_tree::StrongRuleNode;
use crate::Zero;
use crate::str::{CssString, CssStringWriter};
@ -1068,28 +1068,20 @@ impl CSSWideKeyword {
bitflags! {
/// A set of flags for properties.
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.
const APPLIES_TO_FIRST_LETTER = 1 << 3;
const APPLIES_TO_FIRST_LETTER = 1 << 1;
/// 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.
const APPLIES_TO_PLACEHOLDER = 1 << 5;
const APPLIES_TO_PLACEHOLDER = 1 << 3;
/// This longhand property applies to ::cue.
const APPLIES_TO_CUE = 1 << 6;
const APPLIES_TO_CUE = 1 << 4;
/// This longhand property applies to ::marker.
const APPLIES_TO_MARKER = 1 << 7;
const APPLIES_TO_MARKER = 1 << 5;
/// This property is a 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
* only need to be listed in corresponding properties so that
@ -1757,7 +1749,21 @@ impl UnparsedValue {
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::properties::longhands::{font_family, font_style, font_weight, font_stretch};
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::FontSize;
use crate::values::specified::font::{FontStretch, FontStretchKeyword};
#[cfg(feature = "gecko")]
use crate::values::specified::font::SystemFont;
<%
gecko_sub_properties = "kerning language_override size_adjust \
@ -197,7 +197,7 @@
let font_stretch = match *self.font_stretch {
FontStretch::Keyword(kw) => kw,
FontStretch::Stretch(percentage) => {
match FontStretchKeyword::from_percentage(percentage.get()) {
match FontStretchKeyword::from_percentage(percentage.0.get()) {
Some(kw) => kw,
None => return Ok(()),
}
@ -289,7 +289,9 @@
% for p in subprops_for_value_info:
${p}::collect_completion_keywords(f);
% endfor
<longhands::system_font::SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
% if engine == "gecko":
<SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
% endif
}
}
</%helpers:shorthand>

View file

@ -83,6 +83,7 @@
<%helpers:shorthand
name="-moz-outline-radius"
engines="gecko"
gecko_pref="layout.css.moz-outline-radius.enabled"
sub_properties="${' '.join(
'-moz-outline-radius-%s' % corner
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
///
/// TODO: Tune the initial capacity of the HashMap
#[derive(Debug, MallocSizeOf)]
#[derive(Clone, Debug, MallocSizeOf)]
pub struct SelectorMap<T: 'static> {
/// Rules that have `:root` selectors.
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.
#[derive(Debug, MallocSizeOf)]
#[derive(Clone, Debug, MallocSizeOf)]
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>(
PrecomputedHashMap<K, V>,
);

View file

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

View file

@ -19,6 +19,7 @@ use app_units::Au;
use cssparser::RGBA;
use euclid::default::Size2D as UntypedSize2D;
use euclid::{Scale, SideOffsets2D, Size2D};
use mime::Mime;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};
@ -202,6 +203,23 @@ impl Device {
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
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

View file

@ -372,7 +372,7 @@ impl NonTSPseudoClass {
Disabled => ElementState::IN_DISABLED_STATE,
Checked => ElementState::IN_CHECKED_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,
Target => ElementState::IN_TARGET_STATE,

View file

@ -184,7 +184,9 @@ pub struct SheetCollectionFlusher<'a, S>
where
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,
dirty: bool,
}
@ -204,32 +206,42 @@ where
pub fn data_validity(&self) -> DataValidity {
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
S: StylesheetInDocument + PartialEq + 'static,
{
type Item = (&'a S, SheetRebuildKind);
fn next(&mut self) -> Option<Self::Item> {
loop {
let potential_sheet = self.iter.next()?;
/// Iterates over all sheets and values that we have to invalidate.
///
/// TODO(emilio): This would be nicer as an iterator but we can't do that
/// until https://github.com/rust-lang/rust/pull/82771 stabilizes.
///
/// 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);
if !committed {
let rebuild_kind = if !committed {
// If the sheet was uncommitted, we need to do a full rebuild
// anyway.
return Some((&potential_sheet.sheet, SheetRebuildKind::Full));
}
let rebuild_kind = match self.validity {
SheetRebuildKind::Full
} else {
match self.validity {
DataValidity::Valid => continue,
DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
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);
SheetCollectionFlusher {
iter: self.entries.iter_mut(),
entries: &mut self.entries,
dirty,
validity,
}
@ -408,7 +420,7 @@ macro_rules! sheet_set_methods {
) {
debug!(concat!($set_name, "::append_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard);
let collection = self.collection_for(&sheet);
collection.append(sheet);
}
@ -423,7 +435,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::insert_stylesheet_before"));
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);
}
@ -437,7 +449,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::remove_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard);
let collection = self.collection_for(&sheet);
collection.remove(&sheet)
}
@ -487,7 +499,7 @@ macro_rules! sheet_set_methods {
RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
};
let collection = self.collection_for(&sheet, guard);
let collection = self.collection_for(&sheet);
collection.set_data_validity_at_least(validity);
}
};
@ -505,12 +517,8 @@ where
}
}
fn collection_for(
&mut self,
sheet: &S,
guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
let origin = sheet.origin(guard);
fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
let origin = sheet.contents().origin;
self.collections.borrow_mut_for_origin(&origin)
}
@ -658,11 +666,7 @@ where
self.collection.len()
}
fn collection_for(
&mut self,
_sheet: &S,
_guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
&mut self.collection
}

View file

@ -6,12 +6,11 @@
//!
//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
use crate::context::QuirksMode;
use crate::media_queries::MediaList;
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::{CssRule, Origin, StylesheetInDocument};
use crate::stylesheets::{CssRule, StylesheetInDocument};
use crate::values::CssUrl;
use cssparser::SourceLocation;
use std::fmt::{self, Write};
@ -61,6 +60,19 @@ impl ImportSheet {
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")]
@ -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.
#[cfg(feature = "servo")]
#[derive(Debug)]
pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>);
#[cfg(feature = "servo")]
impl StylesheetInDocument for ImportSheet {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.0.origin(guard)
}
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> {
impl ImportSheet {
/// Returns the media list for this import rule.
pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
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)
}
}

View file

@ -53,7 +53,7 @@ pub struct TopLevelRuleParser<'a> {
/// This won't contain any namespaces, and only nested parsers created with
/// `ParserContext::new_with_rule_type` will.
pub context: ParserContext<'a>,
/// The current stajkj/te of the parser.
/// The current state of the parser.
pub state: State,
/// 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
@ -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> {
type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule;
@ -601,7 +633,11 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
namespaces: self.namespaces,
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>(

View file

@ -7,7 +7,6 @@
use crate::context::QuirksMode;
use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::StylesheetInDocument;
use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule};
use smallvec::SmallVec;
use std::slice;
@ -227,10 +226,13 @@ impl NestedRuleIterationCondition for EffectiveRules {
fn process_import(
guard: &SharedRwLockReadGuard,
device: &Device,
_quirks_mode: QuirksMode,
quirks_mode: QuirksMode,
rule: &ImportRule,
) -> 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(

View file

@ -4,7 +4,6 @@
use crate::context::QuirksMode;
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use crate::media_queries::{Device, MediaList};
use crate::parser::ParserContext;
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
@ -102,10 +101,10 @@ impl StylesheetContents {
Self {
rules: CssRules::new(rules, &shared_lock),
origin: origin,
origin,
url_data: RwLock::new(url_data),
namespaces: namespaces,
quirks_mode: quirks_mode,
namespaces,
quirks_mode,
source_map_url: RwLock::new(source_map_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.
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.
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>;
/// 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`.
#[inline]
@ -243,18 +241,19 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
where
C: NestedRuleIterationCondition,
{
let contents = self.contents();
RulesIterator::new(
device,
self.quirks_mode(guard),
contents.quirks_mode,
guard,
self.rules(guard).iter(),
contents.rules(guard).iter(),
)
}
/// Returns whether the style-sheet applies for the current device.
fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
match self.media(guard) {
Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)),
Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
None => true,
}
}
@ -285,14 +284,6 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
}
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> {
Some(self.media.read_with(guard))
}
@ -302,8 +293,8 @@ impl StylesheetInDocument for Stylesheet {
}
#[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.contents.rules(guard)
fn contents(&self) -> &StylesheetContents {
&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 {
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> {
self.0.media(guard)
}
@ -345,8 +322,8 @@ impl StylesheetInDocument for DocumentStyleSheet {
}
#[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.0.rules(guard)
fn contents(&self) -> &StylesheetContents {
self.0.contents()
}
}

View file

@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState};
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
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::media_queries::Device;
use crate::properties::{self, CascadeMode, ComputedValues};
@ -60,17 +60,33 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
#[cfg(feature = "gecko")]
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
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());
trait CascadeDataCacheEntry : Sized {
/// Returns a reference to the cascade data.
fn cascade_data(&self) -> &CascadeData;
/// 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 {
entries: Vec<Arc<UserAgentCascadeData>>,
struct CascadeDataCache<Entry> {
entries: Vec<Arc<Entry>>,
}
impl UserAgentCascadeDataCache {
impl<Entry> CascadeDataCache<Entry>
where
Entry: CascadeDataCacheEntry,
{
fn new() -> Self {
Self { entries: vec![] }
}
@ -79,53 +95,67 @@ impl UserAgentCascadeDataCache {
self.entries.len()
}
// FIXME(emilio): This may need to be keyed on quirks-mode too, though there
// aren't class / id selectors on those sheets, usually, so it's probably
// ok...
fn lookup<'a, I, S>(
// FIXME(emilio): This may need to be keyed on quirks-mode too, though for
// UA sheets there aren't class / id selectors on those sheets, usually, so
// it's probably ok... For the other cache the quirks mode shouldn't differ
// so also should be fine.
fn lookup<'a, S>(
&'a mut self,
sheets: I,
device: &Device,
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
old_entry: &Entry,
) -> Result<Option<Arc<Entry>>, FailedAllocationError>
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
S: StylesheetInDocument + PartialEq + 'static,
{
debug!("StyleSheetCache::lookup({})", self.len());
if !collection.dirty() {
return Ok(None);
}
let mut key = EffectiveMediaQueryResults::new();
debug!("UserAgentCascadeDataCache::lookup({:?})", device);
for sheet in sheets.clone() {
for sheet in collection.sheets() {
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
}
for entry in &self.entries {
if entry.cascade_data.effective_media_query_results == key {
return Ok(entry.clone());
if std::ptr::eq(&**entry, old_entry) {
// 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);
}
}
let mut new_data = UserAgentCascadeData {
cascade_data: CascadeData::new(),
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
};
// The line below ensures the "committed" bit is updated properly
// below.
collection.each(|_, _| true);
return Ok(Some(entry.clone()));
}
debug!("> Picking the slow path");
for sheet in sheets {
new_data.cascade_data.add_stylesheet(
let new_entry = Entry::rebuild(
device,
quirks_mode,
sheet,
collection,
guard,
SheetRebuildKind::Full,
Some(&mut new_data.precomputed_pseudo_element_decls),
old_entry,
)?;
}
let new_data = Arc::new(new_data);
self.entries.push(new_data.clone());
Ok(new_data)
self.entries.push(new_entry.clone());
Ok(Some(new_entry))
}
/// 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
/// URL references), and thus we don't want to drop them while locking the
/// 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();
for i in (0..self.entries.len()).rev() {
// is_unique() returns false for static references, but we never
@ -148,7 +178,7 @@ impl UserAgentCascadeDataCache {
unused
}
fn take_all(&mut self) -> Vec<Arc<UserAgentCascadeData>> {
fn take_all(&mut self) -> Vec<Arc<Entry>> {
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);
}
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>>;
#[derive(Default)]
@ -188,14 +270,6 @@ struct UserAgentCascadeData {
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
/// document.
#[derive(Default)]
@ -262,19 +336,29 @@ impl DocumentCascadeData {
guards: &StylesheetGuards,
) -> Result<(), FailedAllocationError>
where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
S: StylesheetInDocument + PartialEq + 'static,
{
// First do UA sheets.
{
if flusher.flush_origin(Origin::UserAgent).dirty() {
let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
let _unused_cascade_datas = {
let origin_flusher = flusher.flush_origin(Origin::UserAgent);
// Dirty check is just a minor optimization (no need to grab the
// lock if nothing has changed).
if origin_flusher.dirty() {
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
self.user_agent =
ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?;
debug!("User agent data cache size {:?}", ua_cache.len());
ua_cache.take_unused()
};
let new_data = ua_cache.lookup(
device,
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.
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.
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
quirks_mode: QuirksMode,
@ -422,6 +510,7 @@ impl Stylist {
device,
quirks_mode,
stylesheets: StylistStylesheetSet::new(),
author_data_cache: CascadeDataCache::new(),
cascade_data: Default::default(),
author_styles_enabled: AuthorStylesEnabled::Yes,
rule_tree: RuleTree::new(),
@ -447,6 +536,31 @@ impl Stylist {
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.
#[inline]
pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
@ -1355,6 +1469,7 @@ impl Stylist {
#[cfg(feature = "gecko")]
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
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);
// 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
/// 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))]
pub struct ExtraStyleData {
/// A list of effective font-face rules and their origin.
@ -1388,11 +1503,6 @@ pub struct ExtraStyleData {
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")]
impl ExtraStyleData {
/// 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.
#[derive(Debug, Default, MallocSizeOf)]
#[derive(Clone, Debug, Default, MallocSizeOf)]
struct GenericElementAndPseudoRules<Map> {
/// Rules from stylesheets at this `CascadeData`'s origin.
element_map: Map,
@ -1712,7 +1822,7 @@ impl PartElementAndPseudoRules {
///
/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
/// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
#[derive(Debug, MallocSizeOf)]
#[derive(Debug, Clone, MallocSizeOf)]
pub struct CascadeData {
/// The data coming from normal style rules that apply to elements at this
/// cascade level.
@ -1822,7 +1932,7 @@ impl CascadeData {
guard: &SharedRwLockReadGuard,
) -> Result<(), FailedAllocationError>
where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
S: StylesheetInDocument + PartialEq + 'static,
{
if !collection.dirty() {
return Ok(());
@ -1836,18 +1946,21 @@ impl CascadeData {
DataValidity::FullyInvalid => self.clear(),
}
for (stylesheet, rebuild_kind) in collection {
self.add_stylesheet(
let mut result = Ok(());
collection.each(|stylesheet, rebuild_kind| {
result = self.add_stylesheet(
device,
quirks_mode,
stylesheet,
guard,
rebuild_kind,
/* precomputed_pseudo_element_decls = */ None,
)?;
}
);
result.is_ok()
});
Ok(())
result
}
/// Returns the invalidation map.
@ -1922,14 +2035,14 @@ impl CascadeData {
guard: &SharedRwLockReadGuard,
results: &mut EffectiveMediaQueryResults,
) where
S: StylesheetInDocument + ToMediaListKey + 'static,
S: StylesheetInDocument + 'static,
{
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return;
}
debug!(" + {:?}", stylesheet);
results.saw_effective(stylesheet);
results.saw_effective(stylesheet.contents());
for rule in stylesheet.effective_rules(device, guard) {
match *rule {
@ -1959,16 +2072,17 @@ impl CascadeData {
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError>
where
S: StylesheetInDocument + ToMediaListKey + 'static,
S: StylesheetInDocument + 'static,
{
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return Ok(());
}
let origin = stylesheet.origin(guard);
let contents = stylesheet.contents();
let origin = contents.origin;
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) {
@ -2143,13 +2257,13 @@ impl CascadeData {
quirks_mode: QuirksMode,
) -> bool
where
S: StylesheetInDocument + ToMediaListKey + 'static,
S: StylesheetInDocument + 'static,
{
use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules;
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 {
debug!(
@ -2184,9 +2298,10 @@ impl CascadeData {
},
CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard);
let effective_now = import_rule
.stylesheet
.is_effective_for_device(&device, guard);
let effective_now = match import_rule.stylesheet.media(guard) {
Some(m) => m.evaluate(device, quirks_mode),
None => true,
};
let effective_then = self
.effective_media_query_results
.was_effective(import_rule);
@ -2258,8 +2373,33 @@ impl CascadeData {
self.selectors_for_cache_revalidation.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")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.normal_rules.add_size_of(ops, sizes);

View file

@ -35,17 +35,14 @@ impl RGBA {
#[inline]
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
RGBA {
red: red,
green: green,
blue: blue,
alpha: alpha,
red,
green,
blue,
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 {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
@ -57,15 +54,11 @@ impl Animate for RGBA {
}
alpha = alpha.min(1.);
let red =
(self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha;
let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? *
1. /
alpha;
let blue =
(self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha;
Ok(RGBA::new(red, green, blue, alpha))
let red = (self.red * self.alpha).animate(&(other.red * 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)?;
let inv = 1. / alpha;
Ok(RGBA::new(red * inv, green * inv, blue * inv, alpha))
}
}
@ -97,21 +90,75 @@ pub type Color = GenericColor<RGBA>;
impl Color {
fn effective_intermediate_rgba(&self) -> RGBA {
match *self {
GenericColor::Numeric(color) => color,
GenericColor::CurrentColor => RGBA::transparent(),
GenericColor::Complex { color, ratios } => RGBA {
alpha: color.alpha * ratios.bg,
..color.clone()
},
if self.ratios.bg == 0. {
return RGBA::transparent();
}
if self.ratios.bg == 1. {
return self.color;
}
RGBA {
alpha: self.color.alpha * self.ratios.bg,
..self.color
}
}
fn effective_ratios(&self) -> ComplexColorRatios {
match *self {
GenericColor::Numeric(..) => ComplexColorRatios::NUMERIC,
GenericColor::CurrentColor => ComplexColorRatios::CURRENT_COLOR,
GenericColor::Complex { ratios, .. } => ratios,
/// Mix two colors into one.
pub fn mix(
left_color: &Color,
left_weight: f32,
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,58 +166,51 @@ impl Color {
impl Animate for Color {
#[inline]
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,
// two currentcolors, and a numeric color and a currentcolor.
let (this_weight, other_weight) = procedure.weights();
if self_numeric && other_numeric {
return Ok(Self::rgba(self.color.animate(&other.color, procedure)?));
}
Ok(match (*self, *other, procedure) {
// Any interpolation of currentcolor with currentcolor returns 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,
},
),
let self_currentcolor = self.is_currentcolor();
let other_currentcolor = other.is_currentcolor();
// Any other animation of currentcolor with currentcolor.
(CurrentColor, CurrentColor, _) => Self::with_ratios(
if self_currentcolor && other_currentcolor {
let (self_weight, other_weight) = procedure.weights();
return Ok(Self::new(
RGBA::transparent(),
ComplexColorRatios {
bg: 0.,
fg: (this_weight + other_weight) as f32,
fg: (self_weight + other_weight) as f32,
},
),
));
}
// FIXME(emilio): Without these special cases tests fail, looks fairly
// sketchy!
if (self_currentcolor && other_numeric) || (self_numeric && other_currentcolor) {
let (self_weight, other_weight) = procedure.weights();
return Ok(if self_numeric {
Self::new(
self.color,
ComplexColorRatios {
bg: self_weight as f32,
fg: other_weight as f32,
},
)
} else {
Self::new(
other.color,
ComplexColorRatios {
bg: other_weight as f32,
fg: self_weight as f32,
},
)
});
}
// Defer to complex calculations
_ => {
// Compute the "scaled" contribution for `color`.
fn scaled_rgba(color: &Color) -> RGBA {
match *color {
GenericColor::Numeric(color) => color,
GenericColor::CurrentColor => RGBA::transparent(),
GenericColor::Complex { color, ratios } => RGBA {
red: color.red * ratios.bg,
green: color.green * ratios.bg,
blue: color.blue * ratios.bg,
alpha: color.alpha * ratios.bg,
},
}
}
// Each `Color`, represents a complex combination of foreground color and
// background color where fg and bg represent the overall
// contributions. ie:
@ -206,53 +246,60 @@ impl Animate for Color {
//
// = { 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 = scaled_rgba(self);
let bg_color2 = scaled_rgba(other);
let bg_color1 = self.scaled_rgba();
let bg_color2 = other.scaled_rgba();
// Perform bg_color1 op bg_color2
let bg_color = bg_color1.animate(&bg_color2, procedure)?;
// Calculate the final foreground color ratios; perform
// animation on effective fg ratios.
let ComplexColorRatios { fg: fg1, .. } = self.effective_ratios();
let ComplexColorRatios { fg: fg2, .. } = other.effective_ratios();
// Perform fg1 op fg2
let fg = fg1.animate(&fg2, procedure)?;
let fg = self.ratios.fg.animate(&other.ratios.fg, procedure)?;
Self::with_ratios(bg_color, ComplexColorRatios { bg: 1., fg })
},
})
Ok(Self::new(bg_color, ComplexColorRatios { bg: 1., fg }))
}
}
impl ComputeSquaredDistance for Color {
#[inline]
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.
Ok(match (*self, *other) {
(CurrentColor, CurrentColor) => SquaredDistance::from_sqrt(0.),
(Numeric(c1), Numeric(c2)) => c1.compute_squared_distance(&c2)?,
(CurrentColor, Numeric(color)) | (Numeric(color), CurrentColor) => {
if self_numeric && other_numeric {
return self.color.compute_squared_distance(&other.color);
}
let self_currentcolor = self.is_currentcolor();
let other_currentcolor = other.is_currentcolor();
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.
color.compute_squared_distance(&RGBA::transparent())? +
SquaredDistance::from_sqrt(1.)
},
(_, _) => {
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.effective_ratios();
let other_ratios = other.effective_ratios();
let self_ratios = self.ratios;
let other_ratios = other.ratios;
self_color.compute_squared_distance(&other_color)? +
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)?
},
})
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::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 std::fmt;
use style_traits::{CssWriter, ToCss};
@ -29,13 +29,18 @@ impl Color {
/// Combine this complex color with the given foreground color into
/// a numeric RGBA color. It currently uses linear blending.
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 pure currentcolor.
GenericColor::Numeric(color) => return color,
GenericColor::CurrentColor => return fg_color,
GenericColor::Complex { color, ratios } => (color, ratios),
};
// Common cases that the complex color is either pure numeric color or
// pure currentcolor.
if self.is_numeric() {
return self.color;
}
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,
// we use the following formula to compute the components:
@ -59,13 +64,14 @@ impl Color {
if a <= 0. {
return RGBA::transparent();
}
let a = f32::min(a, 1.);
let a = a.min(1.);
let inverse_a = 1. / a;
let r = (p1 * r1 + p2 * r2) * inverse_a;
let g = (p1 * g1 + p2 * g2) * inverse_a;
let b = (p1 * b1 + p2 * b2) * inverse_a;
return RGBA::from_floats(r, g, b, a);
let inv = 1. / a;
let r = (p1 * r1 + p2 * r2) * inv;
let g = (p1 * g1 + p2 * g2) * inv;
let b = (p1 * b1 + p2 * b2) * inv;
RGBA::from_floats(r, g, b, a)
}
}
@ -74,11 +80,13 @@ impl ToCss for Color {
where
W: fmt::Write,
{
match *self {
GenericColor::Numeric(color) => color.to_css(dest),
GenericColor::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
_ => Ok(()),
if self.is_currentcolor() {
return CSSParserColor::CurrentColor.to_css(dest);
}
if self.is_numeric() {
return self.color.to_css(dest);
}
Ok(())
}
}
@ -104,3 +112,6 @@ impl ToAnimatedValue for RGBA {
/// auto | <color>
pub type ColorOrAuto = GenericColorOrAuto<Color>;
/// caret-color
pub type CaretColor = GenericCaretColor<Color>;

View file

@ -4,7 +4,7 @@
//! 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::CounterIncrement as GenericCounterIncrement;
use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset;
@ -16,7 +16,7 @@ pub type CounterIncrement = GenericCounterIncrement<i32>;
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
/// A computed value for the `content` property.
pub type Content = generics::GenericContent<ComputedImageUrl>;
pub type Content = generics::GenericContent<Image>;
/// 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 style = cx.builder.get_parent_font().clone_math_style();
if style == MathStyleValue::Compact {
parent + 1
parent.saturating_add(1)
} else {
parent
}
},
specified::MathDepth::Add(rel) => {
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),
};

View file

@ -74,9 +74,20 @@ impl ToComputedValue for specified::ImageSet {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
let items = self.items.to_computed_value(context);
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_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();
// https://drafts.csswg.org/css-images-4/#image-set-notation:
@ -97,7 +108,9 @@ impl ToComputedValue for specified::ImageSet {
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_resolution = candidate_resolution;
}

View file

@ -185,7 +185,10 @@ impl Size {
GenericSize::Auto => false,
GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(),
#[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>`.
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.
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::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
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::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
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::text::TextUnderlinePosition;
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::{TextDecorationLength, TextDecorationSkipInk};
pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify};
pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
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::PaperSize;
/// 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 style_traits::{CssWriter, ToCss};
pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition};
pub use crate::values::specified::{LineBreak, OverflowWrap, WordBreak};
pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition, MozControlCharacterVisibility};
pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak};
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.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;

View file

@ -5,7 +5,7 @@
//! Computed values for UI properties
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::generics::ui as generics;
@ -16,7 +16,7 @@ pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
pub type Cursor = generics::GenericCursor<CursorImage>;
/// 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.
pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;

View file

@ -6,6 +6,11 @@
/// Ratios representing the contribution of color and currentcolor to
/// 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)]
#[repr(C)]
pub struct ComplexColorRatios {
@ -22,59 +27,52 @@ impl ComplexColorRatios {
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).
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
#[repr(C, u8)]
pub enum GenericColor<RGBA> {
/// Numeric RGBA color.
Numeric(RGBA),
/// The current foreground color.
CurrentColor,
/// A linear combination of numeric color and currentcolor.
/// The formula is: `color * ratios.bg + currentcolor * ratios.fg`.
Complex {
#[repr(C)]
pub struct GenericColor<RGBA> {
/// The actual numeric color.
color: RGBA,
pub color: RGBA,
/// The ratios of mixing between numeric and currentcolor.
ratios: ComplexColorRatios,
},
/// The formula is: `color * ratios.bg + currentcolor * ratios.fg`.
pub ratios: ComplexColorRatios,
}
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> {
/// Create a color based upon the specified ratios.
pub fn with_ratios(color: RGBA, ratios: ComplexColorRatios) -> Self {
if ratios == ComplexColorRatios::NUMERIC {
Color::Numeric(color)
} else if ratios == ComplexColorRatios::CURRENT_COLOR {
Color::CurrentColor
} else {
Color::Complex { color, ratios }
}
pub fn new(color: RGBA, ratios: ComplexColorRatios) -> Self {
Self { color, ratios }
}
/// Returns a numeric color representing the given RGBA value.
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).
pub fn is_numeric(&self) -> bool {
matches!(*self, Color::Numeric(..))
self.ratios == ComplexColorRatios::NUMERIC
}
/// Whether it is a currentcolor value (no numeric color component).
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,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToCss,
ToShmem,
)]
@ -110,3 +109,32 @@ pub enum GenericColorOrAuto<C> {
}
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,
)]
#[repr(u8)]
pub enum GenericContent<ImageUrl> {
pub enum GenericContent<Image> {
/// `normal` reserved keyword.
Normal,
/// `none` reserved keyword.
None,
/// Content items.
Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<ImageUrl>>),
Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<Image>>),
}
pub use self::GenericContent as Content;
impl<ImageUrl> Content<ImageUrl> {
impl<Image> Content<Image> {
/// Whether `self` represents list of items.
#[inline]
pub fn is_items(&self) -> bool {
@ -180,14 +180,13 @@ impl<ImageUrl> Content<ImageUrl> {
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum GenericContentItem<ImageUrl> {
pub enum GenericContentItem<I> {
/// Literal string content.
String(crate::OwnedStr),
/// `counter(name, style)`.
@ -220,8 +219,8 @@ pub enum GenericContentItem<ImageUrl> {
/// `attr([namespace? `|`]? ident)`
#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
Attr(Attr),
/// `url(url)`
Url(ImageUrl),
/// image-set(url) | url(url)
Image(I),
}
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.
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
)]
#[repr(C)]
pub struct GenericImageSetItem<Image, Resolution> {
@ -142,7 +142,33 @@ pub struct GenericImageSetItem<Image, Resolution> {
///
/// TODO: Skip serialization if it is 1x.
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;

View file

@ -5,8 +5,6 @@
//! Generic types for CSS values related to length.
use crate::parser::{Parse, ParserContext};
#[cfg(feature = "gecko")]
use crate::values::computed::ExtremumLength;
use crate::Zero;
use cssparser::Parser;
use style_traits::ParseError;
@ -153,7 +151,16 @@ pub enum GenericSize<LengthPercent> {
Auto,
#[cfg(feature = "gecko")]
#[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;
@ -196,7 +203,18 @@ pub enum GenericMaxSize<LengthPercent> {
None,
#[cfg(feature = "gecko")]
#[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;

View file

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

View file

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

View file

@ -6,7 +6,7 @@
use super::{Context, ToResolvedValue};
use crate::values::computed;
use crate::values::computed::color as computed;
use crate::values::generics::color as generics;
impl ToResolvedValue for computed::Color {
@ -20,26 +20,26 @@ impl ToResolvedValue for computed::Color {
#[inline]
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
// currentcolor.
type ResolvedValue = cssparser::RGBA;
#[inline]
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::Auto => generics::Color::CurrentColor,
generics::ColorOrAuto::Auto => generics::Color::currentcolor(),
};
color.to_resolved_value(context)
}
#[inline]
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::parser::{Parse, ParserContext};
use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags};
use crate::properties::{LonghandId, PropertyDeclarationId};
use crate::properties::{PropertyId, ShorthandId};
use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
use crate::values::generics::box_::Perspective as GenericPerspective;
@ -1086,46 +1086,58 @@ bitflags! {
/// The change bits that we care about.
#[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(C)]
pub struct WillChangeBits: u8 {
/// Whether the stacking context will change.
const STACKING_CONTEXT = 1 << 0;
/// Whether `transform` will change.
pub struct WillChangeBits: u16 {
/// Whether a property which can create a stacking context **on any
/// box** will change.
const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
/// Whether `transform` or related properties will change.
const TRANSFORM = 1 << 1;
/// Whether `scroll-position` will change.
const SCROLL = 1 << 2;
/// Whether `contain` will change.
const CONTAIN = 1 << 3;
/// Whether `opacity` will change.
const OPACITY = 1 << 3;
/// Fixed pos containing block.
const FIXPOS_CB = 1 << 4;
/// Abs pos containing block.
const ABSPOS_CB = 1 << 5;
const OPACITY = 1 << 4;
/// Whether `perspective` will change.
const PERSPECTIVE = 1 << 5;
/// Whether `z-index` will change.
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 {
let mut flags = match longhand {
match longhand {
LonghandId::Opacity => WillChangeBits::OPACITY,
LonghandId::Transform => WillChangeBits::TRANSFORM,
#[cfg(feature = "gecko")]
LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => {
WillChangeBits::TRANSFORM
LonghandId::Contain => WillChangeBits::CONTAIN,
LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
LonghandId::Position => {
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(),
};
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 {
let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
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 {
/// auto | <animateable-feature>#
fn parse<'i, 't>(
@ -1838,10 +1851,6 @@ pub enum Appearance {
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacSourceListSelection,
#[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,
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozMacVibrantTitlebarLight,

View file

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

View file

@ -9,8 +9,9 @@ use super::AllowQuirks;
use crate::gecko_bindings::structs::nscolor;
use crate::parser::{Parse, ParserContext};
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::Percentage;
use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
use itoa;
@ -19,6 +20,117 @@ use std::io::Write as IoWrite;
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
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
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum Color {
@ -36,6 +148,8 @@ pub enum Color {
/// A system color
#[cfg(feature = "gecko")]
System(SystemColor),
/// A color mix.
ColorMix(Box<ColorMix>),
/// Quirksmode-only rule for inheriting color from the body
#[cfg(feature = "gecko")]
InheritFromBodyQuirk,
@ -72,8 +186,6 @@ pub enum SystemColor {
#[css(skip)]
TextSelectForeground,
#[css(skip)]
TextSelectForegroundCustom,
#[css(skip)]
TextSelectBackgroundDisabled,
#[css(skip)]
TextSelectBackgroundAttention,
@ -215,8 +327,6 @@ pub enum SystemColor {
/// Font smoothing background colors needed by the Mac OS X theme, based on
/// -moz-appearance names.
MozMacVibrancyLight,
MozMacVibrancyDark,
MozMacVibrantTitlebarLight,
MozMacVibrantTitlebarDark,
MozMacMenupopup,
@ -235,10 +345,6 @@ pub enum SystemColor {
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
MozAccentColorForeground,
/// Accent color for title bar.
MozWinAccentcolor,
/// Color from drawing text over the accent color.
MozWinAccentcolortext,
/// Media rebar text.
MozWinMediatext,
/// 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>> {
use crate::values::specified::Percentage;
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 {
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
@ -425,7 +533,9 @@ impl ToCss for Color {
Color::Numeric {
parsed: ref rgba, ..
} => rgba.to_css(dest),
// TODO: Could represent this as a color-mix() instead.
Color::Complex(_) => Ok(()),
Color::ColorMix(ref mix) => mix.to_css(dest),
#[cfg(feature = "gecko")]
Color::System(system) => system.to_css(dest),
#[cfg(feature = "gecko")]
@ -447,6 +557,18 @@ fn parse_hash_color(value: &[u8]) -> Result<RGBA, ()> {
}
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.
#[inline]
pub fn currentcolor() -> Color {
@ -562,17 +684,28 @@ impl Color {
///
/// If `context` is `None`, and the specified color requires data from
/// 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 {
Color::CurrentColor => ComputedColor::currentcolor(),
Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
Color::Complex(ref complex) => *complex,
#[cfg(feature = "gecko")]
Color::System(system) => system.compute(_context?),
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => {
ComputedColor::rgba(_context?.device().body_text_color())
Color::ColorMix(ref mix) => {
use crate::values::animated::color::Color as AnimatedColor;
use crate::values::animated::ToAnimatedValue;
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 {
match *computed {
GenericColor::Numeric(color) => Color::rgba(color),
GenericColor::CurrentColor => Color::currentcolor(),
GenericColor::Complex { .. } => Color::Complex(*computed),
if computed.is_numeric() {
return Color::rgba(computed.color);
}
if computed.is_currentcolor() {
return Color::currentcolor();
}
Color::Complex(*computed)
}
}
@ -671,3 +806,15 @@ impl Parse for ColorPropertyValue {
/// auto | <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;
#[cfg(feature = "gecko")]
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"))]
use crate::values::specified::Attr;
use crate::values::specified::Integer;
use crate::values::CustomIdent;
use cssparser::{Parser, Token};
#[cfg(feature = "servo-layout-2013")]
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind};
use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind};
/// A specified value for the `counter-increment` property.
pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
@ -83,10 +83,10 @@ fn parse_counters<'i, 't>(
}
/// 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.
pub type ContentItem = generics::GenericContentItem<SpecifiedImageUrl>;
pub type ContentItem = generics::GenericContentItem<Image>;
impl Content {
#[cfg(feature = "servo-layout-2013")]
@ -137,8 +137,8 @@ impl Parse for Content {
loop {
#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
{
if let Ok(url) = input.try_parse(|i| SpecifiedImageUrl::parse(context, i)) {
content.push(generics::ContentItem::Url(url));
if let Ok(image) = input.try_parse(|i| Image::parse_only_url(context, i)) {
content.push(generics::ContentItem::Image(image));
continue;
}
}
@ -171,6 +171,7 @@ impl Parse for Content {
Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
}),
_ => {
use style_traits::StyleParseErrorKind;
let name = name.clone();
return Err(input.new_custom_error(
StyleParseErrorKind::UnexpectedFunction(name),
@ -213,3 +214,20 @@ impl Parse for Content {
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")]
use crate::gecko_bindings::bindings;
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 as computed, Length, NonNegativeLength};
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::specified::length::{FontBaseSize, PX_PER_PT};
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::Atom;
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_SIZE_MULTIPLIER: f64 = 0.71;
@ -359,13 +406,11 @@ impl ToComputedValue for FontStyle {
/// A value for the `font-stretch` property.
///
/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
///
/// TODO(emilio): We could derive Parse if we had NonNegativePercentage.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[repr(u8)]
pub enum FontStretch {
Stretch(Percentage),
Stretch(NonNegativePercentage),
Keyword(FontStretchKeyword),
#[css(skip)]
System(SystemFont),
@ -452,32 +497,13 @@ impl FontStretch {
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 {
type ComputedValue = computed::FontStretch;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
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::System(_) => self.compute_system(context),
@ -485,7 +511,7 @@ impl ToComputedValue for FontStretch {
}
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(
Clone,
Copy,

View file

@ -181,7 +181,7 @@ impl Parse for Image {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> 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>,
cors_mode: CorsMode,
allow_none: bool,
only_url: bool,
) -> Result<Image, ParseError<'i>> {
if allow_none && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(generic::Image::None);
}
if let Ok(url) = input
.try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode))
{
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 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)));
}
}
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 let Ok(cf) = input.try_parse(|input| CrossFade::parse(context, input, cors_mode)) {
return Ok(generic::Image::CrossFade(Box::new(cf)));
@ -264,6 +273,21 @@ impl Image {
input,
CorsMode::Anonymous,
/* 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,
) -> Result<Self, ParseError<'i>> {
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));
}
@ -339,10 +363,11 @@ impl ImageSet {
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
only_url: bool,
) -> Result<Self, ParseError<'i>> {
input.expect_function_matching("image-set")?;
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 {
selected_index: 0,
@ -352,10 +377,18 @@ impl ImageSet {
}
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>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
only_url: bool,
) -> Result<Self, ParseError<'i>> {
let image = match input.try_parse(|i| i.expect_url_or_string()) {
Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string(
@ -364,13 +397,23 @@ impl ImageSetItem {
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))
.unwrap_or(Resolution::X(1.0));
Ok(Self { image, resolution })
let mut resolution = input.try_parse(|input| Resolution::parse(context, input)).ok();
let mime_type = input.try_parse(Self::parse_type).ok();
// 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 {
/// Parses, with quirks.
pub fn parse_quirky<'i, 't>(
@ -1233,16 +1254,7 @@ impl Size {
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> {
#[cfg(feature = "gecko")]
{
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);
}
parse_size_non_length!(Size, input, "auto" => Auto);
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
Ok(GenericSize::LengthPercentage(length))
@ -1274,16 +1286,7 @@ impl MaxSize {
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> {
#[cfg(feature = "gecko")]
{
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);
}
parse_size_non_length!(MaxSize, input, "none" => None);
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
Ok(GenericMaxSize::LengthPercentage(length))

View file

@ -71,7 +71,7 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle;
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::{
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::text::TextAlignLast;
pub use self::text::TextUnderlinePosition;
pub use self::text::RubyPosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
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::transform::{Rotate, Scale, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate};

View file

@ -7,6 +7,7 @@
use crate::parser::{Parse, ParserContext};
use crate::values::computed::percentage::Percentage as ComputedPercentage;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::NonNegative;
use crate::values::specified::calc::CalcNode;
use crate::values::specified::Number;
use crate::values::{serialize_percentage, CSSFloat};
@ -172,3 +173,24 @@ impl ToComputedValue 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 style_traits::values::SequenceWriter;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use style_traits::{KeywordsCollectFn, SpecifiedValueInfo};
use unicode_segmentation::UnicodeSegmentation;
/// A specified type for the `initial-letter` property.
@ -1000,6 +1001,67 @@ pub enum WordBreak {
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.
#[repr(u8)]
#[derive(
@ -1193,3 +1255,72 @@ impl ToCss for TextUnderlinePosition {
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::values::generics::ui as generics;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedImageUrl;
use crate::values::specified::image::Image;
use crate::values::specified::Number;
use cssparser::Parser;
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.
pub type Cursor = generics::GenericCursor<CursorImage>;
/// 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 {
/// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
@ -47,7 +47,7 @@ impl Parse for CursorImage {
) -> Result<Self, ParseError<'i>> {
use crate::Zero;
let url = SpecifiedImageUrl::parse(context, input)?;
let image = Image::parse_only_url(context, input)?;
let mut has_hotspot = false;
let mut hotspot_x = Number::zero();
let mut hotspot_y = Number::zero();
@ -59,7 +59,7 @@ impl Parse for CursorImage {
}
Ok(Self {
url,
image,
has_hotspot,
hotspot_x,
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`
#[derive(
Clone,

View file

@ -91,6 +91,11 @@ packages = [
# Files that are ignored for all tidy and lint checks.
files = [
"./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
"./components/style/properties/helpers/animated_properties.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]
[Property text-justify inherits]
expected: FAIL
[Property text-align-all has initial value start]
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]
[Property text-justify value 'inter-character' computes to 'inter-character']
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\]]
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\]]
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\]]
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\]]
expected: FAIL
@ -131,60 +86,6 @@
[CSS Transitions: property <text-justify> from [inter-character\] to [distribute\] at (0.3) should be [distribute\]]
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\]]
expected: FAIL
@ -215,18 +116,6 @@
[CSS Transitions: property <text-justify> from [inter-word\] to [distribute\] at (0.3) should be [distribute\]]
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\]]
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\]]
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\]]
expected: FAIL
@ -299,18 +155,6 @@
[CSS Transitions: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]]
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\]]
expected: FAIL
@ -320,39 +164,6 @@
[CSS Transitions with transition: all: property <text-justify> from [distribute\] to [none\] at (0.3) should be [none\]]
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\]]
expected: FAIL
@ -373,39 +184,3 @@
[Web Animations: property <text-justify> from [distribute\] to [none\] at (1.5) should be [none\]]
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']
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