aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <infra@servo.org>2023-05-19 22:49:36 +0200
committerGitHub <noreply@github.com>2023-05-19 22:49:36 +0200
commit2426a38a4d11615d71c34649b83436aa1f109260 (patch)
treea5c682e2f3d93813aa5e42c8f50f3f6512b73574
parent71c5bb02b41b8d98b1612563558c2c14b3e20b1a (diff)
parentdbb92b99ad359634e8b84417b8264f84bfc5d056 (diff)
downloadservo-2426a38a4d11615d71c34649b83436aa1f109260.tar.gz
servo-2426a38a4d11615d71c34649b83436aa1f109260.zip
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. -->
-rw-r--r--Cargo.lock1
-rw-r--r--components/layout/generated_content.rs4
-rw-r--r--components/layout_2020/dom_traversal.rs4
-rw-r--r--components/layout_2020/replaced.rs12
-rw-r--r--components/layout_thread/lib.rs8
-rw-r--r--components/layout_thread_2020/lib.rs8
-rw-r--r--components/net/mime_classifier.rs2
-rw-r--r--components/script/dom/documentorshadowroot.rs17
-rw-r--r--components/script/dom/element.rs4
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs2
-rwxr-xr-xcomponents/script/dom/htmltextareaelement.rs2
-rw-r--r--components/script/dom/shadowroot.rs12
-rw-r--r--components/script/layout_dom/document.rs7
-rw-r--r--components/script/layout_dom/shadow_root.rs9
-rw-r--r--components/selectors/parser.rs43
-rw-r--r--components/style/Cargo.toml1
-rw-r--r--components/style/author_styles.rs26
-rw-r--r--components/style/bloom.rs19
-rw-r--r--components/style/context.rs15
-rw-r--r--components/style/dom.rs6
-rw-r--r--components/style/element_state.rs83
-rw-r--r--components/style/error_reporting.rs5
-rw-r--r--components/style/font_face.rs30
-rw-r--r--components/style/gecko/data.rs31
-rw-r--r--components/style/gecko/media_features.rs190
-rw-r--r--components/style/gecko/media_queries.rs7
-rw-r--r--components/style/gecko/selector_parser.rs5
-rw-r--r--components/style/gecko/wrapper.rs51
-rw-r--r--components/style/invalidation/element/invalidation_map.rs2
-rw-r--r--components/style/invalidation/element/invalidator.rs98
-rw-r--r--components/style/invalidation/element/state_and_attributes.rs8
-rw-r--r--components/style/invalidation/media_queries.rs6
-rw-r--r--components/style/media_queries/media_feature_expression.rs52
-rw-r--r--components/style/properties/build.py1
-rw-r--r--components/style/properties/cascade.rs53
-rw-r--r--components/style/properties/counted_unknown_properties.py1
-rw-r--r--components/style/properties/data.py14
-rw-r--r--components/style/properties/gecko.mako.rs3
-rw-r--r--components/style/properties/helpers.mako.rs65
-rw-r--r--components/style/properties/longhands/box.mako.rs15
-rw-r--r--components/style/properties/longhands/effects.mako.rs5
-rw-r--r--components/style/properties/longhands/font.mako.rs327
-rw-r--r--components/style/properties/longhands/inherited_box.mako.rs3
-rw-r--r--components/style/properties/longhands/inherited_text.mako.rs63
-rw-r--r--components/style/properties/longhands/inherited_ui.mako.rs17
-rw-r--r--components/style/properties/longhands/outline.mako.rs1
-rw-r--r--components/style/properties/longhands/page.mako.rs21
-rw-r--r--components/style/properties/longhands/position.mako.rs1
-rw-r--r--components/style/properties/longhands/svg.mako.rs2
-rw-r--r--components/style/properties/longhands/ui.mako.rs2
-rw-r--r--components/style/properties/properties.mako.rs38
-rw-r--r--components/style/properties/shorthands/font.mako.rs10
-rw-r--r--components/style/properties/shorthands/outline.mako.rs1
-rw-r--r--components/style/selector_map.rs4
-rw-r--r--components/style/selector_parser.rs2
-rw-r--r--components/style/servo/media_queries.rs18
-rw-r--r--components/style/servo/selector_parser.rs2
-rw-r--r--components/style/stylesheet_set.rs70
-rw-r--r--components/style/stylesheets/import_rule.rs74
-rw-r--r--components/style/stylesheets/rule_parser.rs40
-rw-r--r--components/style/stylesheets/rules_iterator.rs8
-rw-r--r--components/style/stylesheets/stylesheet.rs57
-rw-r--r--components/style/stylist.rs306
-rw-r--r--components/style/values/animated/color.rs349
-rw-r--r--components/style/values/computed/color.rs47
-rw-r--r--components/style/values/computed/counters.rs6
-rw-r--r--components/style/values/computed/font.rs4
-rw-r--r--components/style/values/computed/image.rs17
-rw-r--r--components/style/values/computed/length.rs36
-rw-r--r--components/style/values/computed/mod.rs6
-rw-r--r--components/style/values/computed/page.rs2
-rw-r--r--components/style/values/computed/text.rs6
-rw-r--r--components/style/values/computed/ui.rs4
-rw-r--r--components/style/values/generics/color.rs92
-rw-r--r--components/style/values/generics/counters.rs13
-rw-r--r--components/style/values/generics/image.rs30
-rw-r--r--components/style/values/generics/length.rs26
-rw-r--r--components/style/values/generics/page.rs4
-rw-r--r--components/style/values/generics/ui.rs9
-rw-r--r--components/style/values/resolved/color.rs12
-rw-r--r--components/style/values/specified/box.rs71
-rw-r--r--components/style/values/specified/calc.rs6
-rw-r--r--components/style/values/specified/color.rs187
-rw-r--r--components/style/values/specified/counters.rs32
-rw-r--r--components/style/values/specified/font.rs111
-rw-r--r--components/style/values/specified/image.rs67
-rw-r--r--components/style/values/specified/length.rs43
-rw-r--r--components/style/values/specified/mod.rs5
-rw-r--r--components/style/values/specified/percentage.rs22
-rw-r--r--components/style/values/specified/text.rs131
-rw-r--r--components/style/values/specified/ui.rs17
-rw-r--r--servo-tidy.toml5
-rw-r--r--tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini12
-rw-r--r--tests/wpt/metadata/css/css-text/inheritance.html.ini3
-rw-r--r--tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini3
-rw-r--r--tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini4
-rw-r--r--tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini4
-rw-r--r--tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini3
-rw-r--r--tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini225
-rw-r--r--tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini12
-rw-r--r--tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini6
101 files changed, 2068 insertions, 1558 deletions
diff --git a/Cargo.lock b/Cargo.lock
index df7b8eb2465..18e9f495468 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6177,6 +6177,7 @@ dependencies = [
"log",
"malloc_size_of",
"malloc_size_of_derive",
+ "mime",
"new_debug_unreachable",
"num-derive",
"num-integer",
diff --git a/components/layout/generated_content.rs b/components/layout/generated_content.rs
index d69ddd65991..89f65732761 100644
--- a/components/layout/generated_content.rs
+++ b/components/layout/generated_content.rs
@@ -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")
},
}
};
diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs
index ab20c88c721..7b619dacf45 100644
--- a/components/layout_2020/dom_traversal.rs
+++ b/components/layout_2020/dom_traversal.rs
@@ -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));
}
diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs
index 50722dab917..642e83f7905 100644
--- a/components/layout_2020/replaced.rs
+++ b/components/layout_2020/replaced.rs
@@ -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)
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index bf4ff1b85bc..1e8bd2848be 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -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());
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 60b00075ddb..b2392e441a1 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -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());
diff --git a/components/net/mime_classifier.rs b/components/net/mime_classifier.rs
index 2a32fcf9948..f3ced86dbc5 100644
--- a/components/net/mime_classifier.rs
+++ b/components/net/mime_classifier.rs
@@ -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()),
diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs
index 43d345e5e01..51bce1c7b2d 100644
--- a/components/script/dom/documentorshadowroot.rs
+++ b/components/script/dom/documentorshadowroot.rs
@@ -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()
}
}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index de34eb401e0..d8b80df754a 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -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 {
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 0463fb14f0b..696e4566b63 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -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,
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index c6d9331532f..aee9c1eadff 100755
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -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,
diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs
index c70c13d8cef..1fa9faae287 100644
--- a/components/script/dom/shadowroot.rs
+++ b/components/script/dom/shadowroot.rs
@@ -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);
}
}
}
diff --git a/components/script/layout_dom/document.rs b/components/script/layout_dom/document.rs
index e723f731cab..392c1686f97 100644
--- a/components/script/layout_dom/document.rs
+++ b/components/script/layout_dom/document.rs
@@ -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);
}
}
}
diff --git a/components/script/layout_dom/shadow_root.rs b/components/script/layout_dom/shadow_root.rs
index 2fd1511ccfa..19336befe74 100644
--- a/components/script/layout_dom/shadow_root.rs
+++ b/components/script/layout_dom/shadow_root.rs
@@ -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)
}
}
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index e99b1b18412..0b8d7d2331e 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -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 {
diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml
index a88ccf0fd88..70219416452 100644
--- a/components/style/Cargo.toml
+++ b/components/style/Cargo.toml
@@ -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"
diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs
index 58d9bda423a..dfd33711ed2 100644
--- a/components/style/author_styles.rs
+++ b/components/style/author_styles.rs
@@ -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;
+ }
}
}
diff --git a/components/style/bloom.rs b/components/style/bloom.rs
index c17b31d1bee..1840c780506 100644
--- a/components/style/bloom.rs
+++ b/components/style/bloom.rs
@@ -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> {
diff --git a/components/style/context.rs b/components/style/context.rs
index 08fdb4d1eef..a7d5aa4a67b 100644
--- a/components/style/context.rs
+++ b/components/style/context.rs
@@ -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
diff --git a/components/style/dom.rs b/components/style/dom.rs
index c23b985a628..3d088956289 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -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)
diff --git a/components/style/element_state.rs b/components/style/element_state.rs
index 0852b4574fa..8a9f4065b8b 100644
--- a/components/style/element_state.rs
+++ b/components/style/element_state.rs
@@ -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;
- /// Non-standard & undocumented.
- const IN_OPTIMUM_STATE = 1 << 33;
- /// Non-standard & undocumented.
- const IN_SUB_OPTIMUM_STATE = 1 << 34;
+ const IN_MOZ_SUBMITINVALID_STATE = 1 << 27;
/// Non-standard & undocumented.
- const IN_SUB_SUB_OPTIMUM_STATE = 1 << 35;
+ const IN_OPTIMUM_STATE = 1 << 28;
/// Non-standard & undocumented.
- const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 36;
+ const IN_SUB_OPTIMUM_STATE = 1 << 29;
/// Non-standard & undocumented.
- const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 37;
+ const IN_SUB_SUB_OPTIMUM_STATE = 1 << 30;
/// 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;
}
}
diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs
index 6e63b0ad68a..eebb0901309 100644
--- a/components/style/error_reporting.rs
+++ b/components/style/error_reporting.rs
@@ -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)
+ }
}
}
}
diff --git a/components/style/font_face.rs b/components/style/font_face.rs
index 884978c5c3b..5016f91182c 100644
--- a/components/style/font_face.rs
+++ b/components/style/font_face.rs
@@ -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,
]
}
diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs
index 0ce43bca46d..0689aa6c0c4 100644
--- a/components/style/gecko/data.rs
+++ b/components/style/gecko/data.rs
@@ -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
+ }
}
}
diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs
index bfc78b84b15..4c23620dd15 100644
--- a/components/style/gecko/media_features.rs
+++ b/components/style/gecko/media_features.rs
@@ -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,
- $feature_name,
- /* accessible_from_content = */ false,
- )
+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,
+ 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"),
];
diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs
index 08ef628f1ff..5d960a8e9e1 100644
--- a/components/style/gecko/media_queries.rs
+++ b/components/style/gecko/media_queries.rs
@@ -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)
+ }
+ }
}
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
index a9083581234..b2fe46a9a2c 100644
--- a/components/style/gecko/selector_parser.rs
+++ b/components/style/gecko/selector_parser.rs
@@ -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> {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 7d4b4e03f73..3f23981a823 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -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,
@@ -582,11 +582,28 @@ 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)]
diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs
index 29e0962a897..a7c2b04df13 100644
--- a/components/style/invalidation/element/invalidation_map.rs
+++ b/components/style/invalidation/element/invalidation_map.rs
@@ -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.
diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs
index c148b63e3e3..79131a8008f 100644
--- a/components/style/invalidation/element/invalidator.rs
+++ b/components/style/invalidation/element/invalidator.rs
@@ -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,76 +878,32 @@ 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() {
- 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;
- }
- }
+ // 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;
}
debug!(
diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs
index 128feae76be..bbb1fb46a80 100644
--- a/components/style/invalidation/element/state_and_attributes.rs
+++ b/components/style/invalidation/element/state_and_attributes.rs
@@ -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
}
diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs
index 75149a02891..6928b29d3d9 100644
--- a/components/style/invalidation/media_queries.rs
+++ b/components/style/invalidation/media_queries.rs
@@ -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>,
diff --git a/components/style/media_queries/media_feature_expression.rs b/components/style/media_queries/media_feature_expression.rs
index 41be485d5c0..1658439da21 100644
--- a/components/style/media_queries/media_feature_expression.rs
+++ b/components/style/media_queries/media_feature_expression.rs
@@ -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 it’s
+ // 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.
diff --git a/components/style/properties/build.py b/components/style/properties/build.py
index 97ad10408ea..6df38fcefe3 100644
--- a/components/style/properties/build.py
+++ b/components/style/properties/build.py
@@ -37,6 +37,7 @@ STYLE_STRUCT_LIST = [
"list",
"margin",
"outline",
+ "page",
"padding",
"position",
"svg",
diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs
index f7ab9f95fde..9c3e6ebd85e 100644
--- a/components/style/properties/cascade.rs
+++ b/components/style/properties/cascade.rs
@@ -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,
diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py
index 047d129a2ff..577547d099d 100644
--- a/components/style/properties/counted_unknown_properties.py
+++ b/components/style/properties/counted_unknown_properties.py
@@ -19,7 +19,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
"text-size-adjust",
"-webkit-font-feature-settings",
"-webkit-user-drag",
- "size",
"-webkit-clip-path",
"orphans",
"widows",
diff --git a/components/style/properties/data.py b/components/style/properties/data.py
index 4fe90fb560b..8a523ccb66f 100644
--- a/components/style/properties/data.py
+++ b/components/style/properties/data.py
@@ -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",
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
index 9bb13eb62b0..7a864c06d27 100644
--- a/components/style/properties/gecko.mako.rs
+++ b/components/style/properties/gecko.mako.rs
@@ -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}
diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs
index 30eaac343ac..ecd714cebfc 100644
--- a/components/style/properties/helpers.mako.rs
+++ b/components/style/properties/helpers.mako.rs
@@ -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
diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs
index 73a2ec87065..f2d0234a918 100644
--- a/components/style/properties/longhands/box.mako.rs
+++ b/components/style/properties/longhands/box.mako.rs
@@ -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",
)}
diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs
index 00ede146cd4..5470c74d47c 100644
--- a/components/style/properties/longhands/effects.mako.rs
+++ b/components/style/properties/longhands/effects.mako.rs
@@ -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",
)}
diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs
index 1fb52a01c77..e84a5574d1a 100644
--- a/components/style/properties/longhands/font.mako.rs
+++ b/components/style/properties/longhands/font.mako.rs
@@ -288,208 +288,153 @@ ${helpers.predefined_type(
)}
% if engine == "gecko":
- pub mod system_font {
- //! We deal with system fonts here
- //!
- //! System fonts can only be set as a group via the font shorthand.
- //! They resolve at compute time (not parse time -- this lets the
- //! browser respond to changes to the OS font settings).
- //!
- //! While Gecko handles these as a separate property and keyword
- //! values on each property indicating that the font should be picked
- //! from the -x-system-font property, we avoid this. Instead,
- //! each font longhand has a special SystemFont variant which contains
- //! the specified system font. When the cascade function (in helpers)
- //! detects that a value has a system font, it will resolve it, and
- //! cache it on the ComputedValues. After this, it can be just fetched
- //! whenever a font longhand on the same element needs the system font.
- //!
- //! When a longhand property is holding a SystemFont, it's serialized
- //! to an empty string as if its value comes from a shorthand with
- //! 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};
-
- <%
- 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
- font_optical_sizing""".split()
- kw_cast = """font_variant_caps font_kerning font_variant_position
- font_optical_sizing""".split()
- %>
- #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq,
- SpecifiedValueInfo, ToCss, ToShmem)]
- pub enum SystemFont {
- % for font in system_fonts:
- ${to_camel_case(font)},
- % endfor
- }
-
- // ComputedValues are compared at times
- // so we need these impls. We don't want to
- // add Eq to Number (which contains a float)
- // so instead we have an eq impl which skips the
- // cached values
- impl PartialEq for ComputedSystemFont {
- fn eq(&self, other: &Self) -> bool {
- self.system_font == other.system_font
- }
- }
- impl Eq for ComputedSystemFont {}
-
- impl Hash for ComputedSystemFont {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- self.system_font.hash(hasher)
- }
- }
-
- impl ToComputedValue for SystemFont {
- type ComputedValue = ComputedSystemFont;
-
- fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
- use crate::gecko_bindings::bindings;
- use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont};
- use std::mem;
- use crate::values::computed::Percentage;
- use crate::values::specified::font::KeywordInfo;
- use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
- use crate::values::generics::NonNegative;
-
- let id = match *self {
- % for font in system_fonts:
- SystemFont::${to_camel_case(font)} => {
- LookAndFeel_FontID::${to_camel_case(font.replace("-moz-", ""))}
- }
- % endfor
- };
-
- let mut system = mem::MaybeUninit::<nsFont>::uninit();
- let system = unsafe {
- bindings::Gecko_nsFont_InitSystem(
- system.as_mut_ptr(),
- id as i32,
- cx.style().get_font().gecko(),
- cx.device().document()
- );
- &mut *system.as_mut_ptr()
- };
- let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
- let font_stretch = FontStretch(NonNegative(Percentage(unsafe {
- bindings::Gecko_FontStretch_ToFloat(system.stretch)
- })));
- let font_style = FontStyle::from_gecko(system.style);
- let ret = ComputedSystemFont {
- font_family: FontFamily {
- families: FontFamilyList::SharedFontList(
- unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
- ),
- is_system_font: true,
- },
- font_size: FontSize {
- size: NonNegative(cx.maybe_zoom_text(system.size.0)),
- keyword_info: KeywordInfo::none()
- },
- font_weight,
- font_stretch,
- font_style,
- font_size_adjust: longhands::font_size_adjust::computed_value
- ::T::from_gecko_adjust(system.sizeAdjust),
- % for kwprop in kw_font_props:
- ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
- system.${to_camel_case_lower(kwprop.replace('font_', ''))}
- % if kwprop in kw_cast:
- as u32
- % endif
- ),
- % endfor
- font_language_override: longhands::font_language_override::computed_value
- ::T(system.languageOverride),
- font_feature_settings: longhands::font_feature_settings::get_initial_value(),
- font_variation_settings: longhands::font_variation_settings::get_initial_value(),
- font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
- system_font: *self,
- default_font_type: system.fontlist.mDefaultFontType,
- };
- unsafe { bindings::Gecko_nsFont_Destroy(system); }
- ret
- }
-
- fn from_computed_value(_: &ComputedSystemFont) -> Self {
- unreachable!()
- }
+pub mod system_font {
+ //! We deal with system fonts here
+ //!
+ //! System fonts can only be set as a group via the font shorthand.
+ //! They resolve at compute time (not parse time -- this lets the
+ //! browser respond to changes to the OS font settings).
+ //!
+ //! While Gecko handles these as a separate property and keyword
+ //! values on each property indicating that the font should be picked
+ //! from the -x-system-font property, we avoid this. Instead,
+ //! each font longhand has a special SystemFont variant which contains
+ //! the specified system font. When the cascade function (in helpers)
+ //! detects that a value has a system font, it will resolve it, and
+ //! cache it on the ComputedValues. After this, it can be just fetched
+ //! whenever a font longhand on the same element needs the system font.
+ //!
+ //! When a longhand property is holding a SystemFont, it's serialized
+ //! to an empty string as if its value comes from a shorthand with
+ //! variable reference. We may want to improve this behavior at some
+ //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
+
+ use crate::values::computed::font::GenericFontFamily;
+ use crate::properties::longhands;
+ use std::hash::{Hash, Hasher};
+ use crate::values::computed::{ToComputedValue, Context};
+ use crate::values::specified::font::SystemFont;
+
+ <%
+ kw_font_props = """font_variant_caps
+ font_kerning font_variant_position font_variant_ligatures
+ font_variant_east_asian font_variant_numeric
+ font_optical_sizing""".split()
+ kw_cast = """font_variant_caps font_kerning font_variant_position
+ font_optical_sizing""".split()
+ %>
+
+ // ComputedValues are compared at times
+ // so we need these impls. We don't want to
+ // add Eq to Number (which contains a float)
+ // so instead we have an eq impl which skips the
+ // cached values
+ impl PartialEq for ComputedSystemFont {
+ fn eq(&self, other: &Self) -> bool {
+ self.system_font == other.system_font
}
+ }
+ impl Eq for ComputedSystemFont {}
- #[inline]
- /// Compute and cache a system font
- ///
- /// Must be called before attempting to compute a system font
- /// specified value
- pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
- // Checking if context.cached_system_font.is_none() isn't enough,
- // if animating from one system font to another the cached system font
- // may change
- if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
- let computed = system.to_computed_value(context);
- context.cached_system_font = Some(computed);
- }
+ impl Hash for ComputedSystemFont {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ self.system_font.hash(hasher)
}
+ }
- #[derive(Clone, Debug)]
- pub struct ComputedSystemFont {
- % for name in SYSTEM_FONT_LONGHANDS:
- pub ${name}: longhands::${name}::computed_value::T,
- % endfor
- pub system_font: SystemFont,
- pub default_font_type: GenericFontFamily,
+ impl ToComputedValue for SystemFont {
+ type ComputedValue = ComputedSystemFont;
+
+ fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
+ use crate::gecko_bindings::bindings;
+ use crate::gecko_bindings::structs::nsFont;
+ use std::mem;
+ use crate::values::computed::Percentage;
+ use crate::values::specified::font::KeywordInfo;
+ use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
+ use crate::values::generics::NonNegative;
+
+ let mut system = mem::MaybeUninit::<nsFont>::uninit();
+ let system = unsafe {
+ bindings::Gecko_nsFont_InitSystem(
+ system.as_mut_ptr(),
+ *self,
+ cx.style().get_font().gecko(),
+ cx.device().document()
+ );
+ &mut *system.as_mut_ptr()
+ };
+ let font_weight = longhands::font_weight::computed_value::T::from_gecko_weight(system.weight);
+ let font_stretch = FontStretch(NonNegative(Percentage(unsafe {
+ bindings::Gecko_FontStretch_ToFloat(system.stretch)
+ })));
+ let font_style = FontStyle::from_gecko(system.style);
+ let ret = ComputedSystemFont {
+ font_family: FontFamily {
+ families: FontFamilyList::SharedFontList(
+ unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
+ ),
+ is_system_font: true,
+ },
+ font_size: FontSize {
+ size: NonNegative(cx.maybe_zoom_text(system.size.0)),
+ keyword_info: KeywordInfo::none()
+ },
+ font_weight,
+ font_stretch,
+ font_style,
+ font_size_adjust: longhands::font_size_adjust::computed_value
+ ::T::from_gecko_adjust(system.sizeAdjust),
+ % for kwprop in kw_font_props:
+ ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
+ system.${to_camel_case_lower(kwprop.replace('font_', ''))}
+ % if kwprop in kw_cast:
+ as u32
+ % endif
+ ),
+ % endfor
+ font_language_override: longhands::font_language_override::computed_value
+ ::T(system.languageOverride),
+ font_feature_settings: longhands::font_feature_settings::get_initial_value(),
+ font_variation_settings: longhands::font_variation_settings::get_initial_value(),
+ font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
+ system_font: *self,
+ default_font_type: system.fontlist.mDefaultFontType,
+ };
+ unsafe { bindings::Gecko_nsFont_Destroy(system); }
+ ret
}
- 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
- }
- }
+ fn from_computed_value(_: &ComputedSystemFont) -> Self {
+ unreachable!()
}
+ }
- 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")
- }
+ #[inline]
+ /// Compute and cache a system font
+ ///
+ /// Must be called before attempting to compute a system font
+ /// specified value
+ pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
+ // Checking if context.cached_system_font.is_none() isn't enough,
+ // if animating from one system font to another the cached system font
+ // may change
+ if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
+ let computed = system.to_computed_value(context);
+ context.cached_system_font = Some(computed);
}
}
-% 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(())
- }
- }
+
+ #[derive(Clone, Debug)]
+ pub struct ComputedSystemFont {
+ % for name in SYSTEM_FONT_LONGHANDS:
+ pub ${name}: longhands::${name}::computed_value::T,
+ % endfor
+ pub system_font: SystemFont,
+ pub default_font_type: GenericFontFamily,
}
+
+}
% endif
${helpers.single_keyword(
diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs
index 32f9819a42d..cea3fde021a 100644
--- a/components/style/properties/longhands/inherited_box.mako.rs
+++ b/components/style/properties/longhands/inherited_box.mako.rs
@@ -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",
diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs
index 0ce27e0e18f..246e7ff36a6 100644
--- a/components/style/properties/longhands/inherited_text.mako.rs
+++ b/components/style/properties/longhands/inherited_text.mako.rs
@@ -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
diff --git a/components/style/properties/longhands/inherited_ui.mako.rs b/components/style/properties/longhands/inherited_ui.mako.rs
index 233ef365329..f08abbc6235 100644
--- a/components/style/properties/longhands/inherited_ui.mako.rs
+++ b/components/style/properties/longhands/inherited_ui.mako.rs
@@ -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(
diff --git a/components/style/properties/longhands/outline.mako.rs b/components/style/properties/longhands/outline.mako.rs
index 5c3dd15cbb6..e5cd09fbe82 100644
--- a/components/style/properties/longhands/outline.mako.rs
+++ b/components/style/properties/longhands/outline.mako.rs
@@ -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
diff --git a/components/style/properties/longhands/page.mako.rs b/components/style/properties/longhands/page.mako.rs
new file mode 100644
index 00000000000..298456cb753
--- /dev/null
+++ b/components/style/properties/longhands/page.mako.rs
@@ -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,
+)}
diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs
index df118faed1a..1795bde7e5f 100644
--- a/components/style/properties/longhands/position.mako.rs
+++ b/components/style/properties/longhands/position.mako.rs
@@ -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",
)}
diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs
index a09f3e7b656..f6128693582 100644
--- a/components/style/properties/longhands/svg.mako.rs
+++ b/components/style/properties/longhands/svg.mako.rs
@@ -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(
diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs
index 1dfdfcc1a73..400eaedf577 100644
--- a/components/style/properties/longhands/ui.mako.rs
+++ b/components/style/properties/longhands/ui.mako.rs
@@ -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",
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
index 95d59df064d..99d283da46c 100644
--- a/components/style/properties/properties.mako.rs
+++ b/components/style/properties/properties.mako.rs
@@ -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()
+ }
+ }
}
}
diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs
index 176164c4b1e..26205c2ea9c 100644
--- a/components/style/properties/shorthands/font.mako.rs
+++ b/components/style/properties/shorthands/font.mako.rs
@@ -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>
diff --git a/components/style/properties/shorthands/outline.mako.rs b/components/style/properties/shorthands/outline.mako.rs
index 75b5fcb717b..db397498919 100644
--- a/components/style/properties/shorthands/outline.mako.rs
+++ b/components/style/properties/shorthands/outline.mako.rs
@@ -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']
diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs
index 4ea8cc1a018..40806ed47af 100644
--- a/components/style/selector_map.rs
+++ b/components/style/selector_map.rs
@@ -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>,
);
diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs
index e3841c15482..67d4d4f6981 100644
--- a/components/style/selector_parser.rs
+++ b/components/style/selector_parser.rs
@@ -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],
}
diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs
index 4b535a9b875..cee76053ec0 100644
--- a/components/style/servo/media_queries.rs
+++ b/components/style/servo/media_queries.rs
@@ -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
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index ce61c172553..7d5b8c5772a 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -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,
diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs
index 9e858a4566c..e2937e06e75 100644
--- a/components/style/stylesheet_set.rs
+++ b/components/style/stylesheet_set.rs
@@ -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 {
- DataValidity::Valid => continue,
- DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
- DataValidity::FullyInvalid => SheetRebuildKind::Full,
+ 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
}
diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs
index 5efae0a3ee5..396be242024 100644
--- a/components/style/stylesheets/import_rule.rs
+++ b/components/style/stylesheets/import_rule.rs
@@ -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)
}
}
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
index 524d54d9c33..914af2cfb66 100644
--- a/components/style/stylesheets/rule_parser.rs
+++ b/components/style/stylesheets/rule_parser.rs
@@ -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>(
diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs
index cc52a9550be..a7010ff066e 100644
--- a/components/style/stylesheets/rules_iterator.rs
+++ b/components/style/stylesheets/rules_iterator.rs
@@ -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(
diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs
index 41c80c3cc66..2f7706a6979 100644
--- a/components/style/stylesheets/stylesheet.rs
+++ b/components/style/stylesheets/stylesheet.rs
@@ -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()
}
}
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index 5cb40b968eb..74704e707df 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -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);
+ }
}
+ // The line below ensures the "committed" bit is updated properly
+ // below.
+ collection.each(|_, _| true);
+ return Ok(Some(entry.clone()));
}
- let mut new_data = UserAgentCascadeData {
- cascade_data: CascadeData::new(),
- precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
- };
-
debug!("> Picking the slow path");
- for sheet in sheets {
- new_data.cascade_data.add_stylesheet(
- device,
- quirks_mode,
- sheet,
- guard,
- SheetRebuildKind::Full,
- Some(&mut new_data.precomputed_pseudo_element_decls),
- )?;
- }
+ let new_entry = Entry::rebuild(
+ device,
+ quirks_mode,
+ collection,
+ guard,
+ 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 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 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();
+ 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.
@@ -1389,11 +1504,6 @@ pub struct ExtraStyleData {
}
#[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.
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
@@ -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);
diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs
index 9f4fa5c52b5..773310dd9b6 100644
--- a/components/style/values/animated/color.rs
+++ b/components/style/values/animated/color.rs
@@ -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
+ }
+ }
+
+ /// 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 effective_ratios(&self) -> ComplexColorRatios {
- match *self {
- GenericColor::Numeric(..) => ComplexColorRatios::NUMERIC,
- GenericColor::CurrentColor => ComplexColorRatios::CURRENT_COLOR,
- GenericColor::Complex { ratios, .. } => ratios,
+ fn scaled_rgba(&self) -> RGBA {
+ if self.ratios.bg == 0. {
+ return RGBA::transparent();
+ }
+
+ if self.ratios.bg == 1. {
+ return self.color;
+ }
+
+ RGBA {
+ red: self.color.red * self.ratios.bg,
+ green: self.color.green * self.ratios.bg,
+ blue: self.color.blue * self.ratios.bg,
+ alpha: self.color.alpha * self.ratios.bg,
}
}
}
@@ -119,140 +166,140 @@ impl Color {
impl Animate for Color {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- use self::GenericColor::*;
-
- // Common cases are interpolating between two numeric colors,
- // two currentcolors, and a numeric color and a currentcolor.
- let (this_weight, other_weight) = procedure.weights();
-
- 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_numeric = self.is_numeric();
+ let other_numeric = other.is_numeric();
+
+ if self_numeric && other_numeric {
+ return Ok(Self::rgba(self.color.animate(&other.color, procedure)?));
+ }
+
+ 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,
},
- ),
-
- // 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:
- //
- // color = { bg * mColor, fg * foreground }
- // = { bg_color , fg_color }
- // = bg_color + fg_color
- //
- // where `foreground` is `currentcolor`, and `bg_color`,
- // `fg_color` are the scaled background and foreground
- // contributions.
- //
- // Each operation, lerp, addition, or accumulate, can be
- // represented as a scaled-addition each complex color. ie:
- //
- // p * col1 + q * col2
- //
- // where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for
- // addition, etc.
- //
- // Therefore:
- //
- // col1 op col2
- // = p * col1 + q * col2
- // = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 }
- // = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2)
- // = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2
- // = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2)
- // = (bg_color1 op bg_color2) + (fg_color1 op fg_color2)
- //
- // fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground,
- // so the final color is:
- //
- // = { 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);
- // 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)?;
-
- Self::with_ratios(bg_color, ComplexColorRatios { bg: 1., fg })
- },
- })
+ ));
+ }
+
+ // 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,
+ },
+ )
+ });
+ }
+
+ // Compute the "scaled" contribution for `color`.
+ // Each `Color`, represents a complex combination of foreground color and
+ // background color where fg and bg represent the overall
+ // contributions. ie:
+ //
+ // color = { bg * mColor, fg * foreground }
+ // = { bg_color , fg_color }
+ // = bg_color + fg_color
+ //
+ // where `foreground` is `currentcolor`, and `bg_color`,
+ // `fg_color` are the scaled background and foreground
+ // contributions.
+ //
+ // Each operation, lerp, addition, or accumulate, can be
+ // represented as a scaled-addition each complex color. ie:
+ //
+ // p * col1 + q * col2
+ //
+ // where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for
+ // addition, etc.
+ //
+ // Therefore:
+ //
+ // col1 op col2
+ // = p * col1 + q * col2
+ // = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 }
+ // = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2)
+ // = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2
+ // = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2)
+ // = (bg_color1 op bg_color2) + (fg_color1 op fg_color2)
+ //
+ // fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground,
+ // so the final color is:
+ //
+ // = { bg_color, fg_color }
+ // = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground }
+ //
+ // To perform the operation on two complex colors, we need to
+ // generate the scaled contributions of each background color
+ // component.
+ let bg_color1 = self.scaled_rgba();
+ let bg_color2 = other.scaled_rgba();
+
+ // 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 fg = self.ratios.fg.animate(&other.ratios.fg, procedure)?;
+
+ 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 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) => {
- // `computed_squared_distance` is symmetric.
- color.compute_squared_distance(&RGBA::transparent())? +
- SquaredDistance::from_sqrt(1.)
- },
- (_, _) => {
- let self_color = self.effective_intermediate_rgba();
- let other_color = other.effective_intermediate_rgba();
- let self_ratios = self.effective_ratios();
- let other_ratios = other.effective_ratios();
-
- self_color.compute_squared_distance(&other_color)? +
- self_ratios.bg.compute_squared_distance(&other_ratios.bg)? +
- self_ratios.fg.compute_squared_distance(&other_ratios.fg)?
- },
- })
+ // All comments from the Animate impl also apply here.
+ let self_numeric = self.is_numeric();
+ let other_numeric = other.is_numeric();
+
+ 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.
+ return Ok(color.compute_squared_distance(&RGBA::transparent())? +
+ SquaredDistance::from_sqrt(1.));
+ }
+
+ let self_color = self.effective_intermediate_rgba();
+ let other_color = other.effective_intermediate_rgba();
+ let self_ratios = self.ratios;
+ let other_ratios = other.ratios;
+
+ Ok(self_color.compute_squared_distance(&other_color)? +
+ self_ratios.bg.compute_squared_distance(&other_ratios.bg)? +
+ self_ratios.fg.compute_squared_distance(&other_ratios.fg)?)
}
}
diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs
index 6098ea4590e..664fed95886 100644
--- a/components/style/values/computed/color.rs
+++ b/components/style/values/computed/color.rs
@@ -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>;
diff --git a/components/style/values/computed/counters.rs b/components/style/values/computed/counters.rs
index 40cfe46efa6..1ae46c772ab 100644
--- a/components/style/values/computed/counters.rs
+++ b/components/style/values/computed/counters.rs
@@ -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>;
diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs
index df6a8ed667e..0bd177bcc85 100644
--- a/components/style/values/computed/font.rs
+++ b/components/style/values/computed/font.rs
@@ -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),
};
diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs
index 5e123a07abb..6290ac68a5d 100644
--- a/components/style/values/computed/image.rs
+++ b/components/style/values/computed/image.rs
@@ -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;
}
diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs
index 0908273b88c..bcba3eb9bce 100644
--- a/components/style/values/computed/length.rs
+++ b/components/style/values/computed/length.rs
@@ -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>;
diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs
index 9dbbf40b5f4..826bb2a2448 100644
--- a/components/style/values/computed/mod.rs
+++ b/components/style/values/computed/mod.rs
@@ -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};
diff --git a/components/style/values/computed/page.rs b/components/style/values/computed/page.rs
index 4dd8804596e..d8ef03c1f6d 100644
--- a/components/style/values/computed/page.rs
+++ b/components/style/values/computed/page.rs
@@ -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>>;
diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs
index b77695e06c0..b42dfb488a3 100644
--- a/components/style/values/computed/text.rs
+++ b/components/style/values/computed/text.rs
@@ -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>;
diff --git a/components/style/values/computed/ui.rs b/components/style/values/computed/ui.rs
index ae12dfcdae0..562b7d15897 100644
--- a/components/style/values/computed/ui.rs
+++ b/components/style/values/computed/ui.rs
@@ -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>;
diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs
index b4f2e7445ea..5b477dee60d 100644
--- a/components/style/values/generics/color.rs
+++ b/components/style/values/generics/color.rs
@@ -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.
+#[repr(C)]
+pub struct GenericColor<RGBA> {
+ /// The actual numeric color.
+ pub color: RGBA,
+ /// The ratios of mixing between numeric and currentcolor.
/// The formula is: `color * ratios.bg + currentcolor * ratios.fg`.
- Complex {
- /// The actual numeric color.
- color: RGBA,
- /// The ratios of mixing between numeric and currentcolor.
- ratios: ComplexColorRatios,
- },
+ 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)
- }
-
- /// Returns a complex color value representing currentcolor.
- pub fn currentcolor() -> Self {
- Color::CurrentColor
+ Self {
+ color,
+ ratios: ComplexColorRatios::NUMERIC,
+ }
}
/// 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;
diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs
index 76ee97ce175..2a8f70c8f44 100644
--- a/components/style/values/generics/counters.rs
+++ b/components/style/values/generics/counters.rs
@@ -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;
diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs
index d49a8a2b4de..014ae46db02 100644
--- a/components/style/values/generics/image.rs
+++ b/components/style/values/generics/image.rs
@@ -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;
diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs
index d9a074623c3..f3e025f80b7 100644
--- a/components/style/values/generics/length.rs
+++ b/components/style/values/generics/length.rs
@@ -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;
diff --git a/components/style/values/generics/page.rs b/components/style/values/generics/page.rs
index fc771062333..35ffee7b97e 100644
--- a/components/style/values/generics/page.rs
+++ b/components/style/values/generics/page.rs
@@ -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]
diff --git a/components/style/values/generics/ui.rs b/components/style/values/generics/ui.rs
index 04c2951c70b..ff6aefdc63e 100644
--- a/components/style/values/generics/ui.rs
+++ b/components/style/values/generics/ui.rs
@@ -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)?;
diff --git a/components/style/values/resolved/color.rs b/components/style/values/resolved/color.rs
index 1b845c58e9f..dbf6375e5bf 100644
--- a/components/style/values/resolved/color.rs
+++ b/components/style/values/resolved/color.rs
@@ -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)))
}
}
diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs
index e30ea5215d0..32f4f9bacae 100644
--- a/components/style/values/specified/box.rs
+++ b/components/style/values/specified/box.rs
@@ -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,
diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs
index c42683bce1f..d5733a6ce4a 100644
--- a/components/style/values/specified/calc.rs
+++ b/components/style/values/specified/calc.rs
@@ -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;
},
}
},
diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs
index e6491049619..773a93366b8 100644
--- a/components/style/values/specified/color.rs
+++ b/components/style/values/specified/color.rs
@@ -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,
+ 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?),
+ Color::System(system) => system.compute(context?),
#[cfg(feature = "gecko")]
- Color::InheritFromBodyQuirk => {
- ComputedColor::rgba(_context?.device().body_text_color())
- },
+ 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)
+ }
+}
diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs
index ece5739ba0e..aa442549fa5 100644
--- a/components/style/values/specified/counters.rs
+++ b/components/style/values/specified/counters.rs
@@ -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",
+ ]);
+ }
+}
diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs
index b83b6411fee..329dedd31ca 100644
--- a/components/style/values/specified/font.rs
+++ b/components/style/values/specified/font.rs
@@ -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,
diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs
index 3ec678cd237..85c178e064d 100644
--- a/components/style/values/specified/image.rs
+++ b/components/style/values/specified/image.rs
@@ -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 })
}
}
diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs
index cf529408c98..aaad895dbce 100644
--- a/components/style/values/specified/length.rs
+++ b/components/style/values/specified/length.rs
@@ -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))
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
index d91f9b8195f..8a9cb73f4da 100644
--- a/components/style/values/specified/mod.rs
+++ b/components/style/values/specified/mod.rs
@@ -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};
diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs
index eac4d91cf8f..a152f7d8dc0 100644
--- a/components/style/values/specified/percentage.rs
+++ b/components/style/values/specified/percentage.rs
@@ -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())
+ }
+}
diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs
index 7c5c712eb3e..ce652734060 100644
--- a/components/style/values/specified/text.rs
+++ b/components/style/values/specified/text.rs
@@ -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"])
+ }
+}
diff --git a/components/style/values/specified/ui.rs b/components/style/values/specified/ui.rs
index 74440a67205..4594cbfcc77 100644
--- a/components/style/values/specified/ui.rs
+++ b/components/style/values/specified/ui.rs
@@ -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,
diff --git a/servo-tidy.toml b/servo-tidy.toml
index 7f1ce0b7ab2..74186f1b8ea 100644
--- a/servo-tidy.toml
+++ b/servo-tidy.toml
@@ -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",
diff --git a/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini
deleted file mode 100644
index 215c616a975..00000000000
--- a/tests/wpt/metadata-layout-2020/css/css-values/clamp-length-computed.html.ini
+++ /dev/null
@@ -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
diff --git a/tests/wpt/metadata/css/css-text/inheritance.html.ini b/tests/wpt/metadata/css/css-text/inheritance.html.ini
index f8cb0572c62..151ed41303a 100644
--- a/tests/wpt/metadata/css/css-text/inheritance.html.ini
+++ b/tests/wpt/metadata/css/css-text/inheritance.html.ini
@@ -1,7 +1,4 @@
[inheritance.html]
- [Property text-justify inherits]
- expected: FAIL
-
[Property text-align-all has initial value start]
expected: FAIL
diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini
deleted file mode 100644
index 210a79f60ac..00000000000
--- a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed-legacy.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[text-justify-computed-legacy.html]
- [Property text-justify value 'distribute']
- expected: FAIL
diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini
index ae897a3a05a..267731c47cc 100644
--- a/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini
+++ b/tests/wpt/metadata/css/css-text/parsing/text-justify-computed.html.ini
@@ -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
-
diff --git a/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini b/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini
deleted file mode 100644
index 96b9e419553..00000000000
--- a/tests/wpt/metadata/css/css-text/parsing/text-justify-valid.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[text-justify-valid.html]
- [e.style['text-justify'\] = "inter-character" should set the property value]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini b/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini
deleted file mode 100644
index 053373e177d..00000000000
--- a/tests/wpt/metadata/css/css-text/text-justify/distribute-alias.tentative.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[distribute-alias.tentative.html]
- [text-justify: distribute is a parse-time alias of inter-character]
- expected: FAIL
diff --git a/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini b/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini
index 9ddf45dbf47..b03f0bf0df1 100644
--- a/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini
+++ b/tests/wpt/metadata/css/css-text/text-justify/text-justify-interpolation.html.ini
@@ -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
diff --git a/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini b/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini
index 52278514aa7..add1bb314b6 100644
--- a/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini
+++ b/tests/wpt/metadata/css/css-values/clamp-length-computed.html.ini
@@ -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
diff --git a/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini b/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini
index fd0f36ba676..4d3fea1a136 100644
--- a/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini
+++ b/tests/wpt/metadata/css/mediaqueries/test_media_queries.html.ini
@@ -4402,3 +4402,9 @@
[expression_should_be_unknown: overflow-block: optional-paged]
expected: FAIL
+
+ [expression_should_be_parseable: width > = 0px]
+ expected: FAIL
+
+ [expression_should_be_parseable: width < = 0px]
+ expected: FAIL