mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #25717 - emilio:gecko-sync, r=emilio,nox
style: Sync changes from mozilla-central. See individual commits for details. https://bugzilla.mozilla.org/show_bug.cgi?id=1614394
This commit is contained in:
commit
6d6d16f7f4
93 changed files with 2715 additions and 1753 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -1532,7 +1532,7 @@ name = "fallible"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashglobe",
|
"hashglobe",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1751,7 +1751,6 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"malloc_size_of",
|
"malloc_size_of",
|
||||||
"net_traits",
|
"net_traits",
|
||||||
"ordered-float",
|
|
||||||
"range",
|
"range",
|
||||||
"serde",
|
"serde",
|
||||||
"servo-fontconfig",
|
"servo-fontconfig",
|
||||||
|
@ -3313,7 +3312,7 @@ dependencies = [
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
"smallbitvec",
|
"smallbitvec",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"thin-slice",
|
"thin-slice",
|
||||||
"time",
|
"time",
|
||||||
|
@ -4912,7 +4911,7 @@ dependencies = [
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
"precomputed-hash",
|
"precomputed-hash",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
"thin-slice",
|
"thin-slice",
|
||||||
"to_shmem",
|
"to_shmem",
|
||||||
"to_shmem_derive",
|
"to_shmem_derive",
|
||||||
|
@ -5507,9 +5506,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.0.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86"
|
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay-client-toolkit"
|
name = "smithay-client-toolkit"
|
||||||
|
@ -5659,7 +5658,6 @@ dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"ordered-float",
|
|
||||||
"owning_ref",
|
"owning_ref",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"precomputed-hash",
|
"precomputed-hash",
|
||||||
|
@ -5672,7 +5670,7 @@ dependencies = [
|
||||||
"servo_config",
|
"servo_config",
|
||||||
"servo_url",
|
"servo_url",
|
||||||
"smallbitvec",
|
"smallbitvec",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"style_derive",
|
"style_derive",
|
||||||
"style_traits",
|
"style_traits",
|
||||||
|
@ -6017,7 +6015,7 @@ dependencies = [
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
"smallbitvec",
|
"smallbitvec",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"thin-slice",
|
"thin-slice",
|
||||||
]
|
]
|
||||||
|
@ -6784,7 +6782,7 @@ dependencies = [
|
||||||
"rendy-descriptor",
|
"rendy-descriptor",
|
||||||
"rendy-memory",
|
"rendy-memory",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec 1.0.0",
|
"smallvec 1.2.0",
|
||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
malloc_size_of = { path = "../malloc_size_of" }
|
malloc_size_of = { path = "../malloc_size_of" }
|
||||||
net_traits = {path = "../net_traits"}
|
net_traits = {path = "../net_traits"}
|
||||||
ordered-float = "1.0"
|
|
||||||
range = {path = "../range"}
|
range = {path = "../range"}
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
servo_arc = {path = "../servo_arc"}
|
servo_arc = {path = "../servo_arc"}
|
||||||
|
|
|
@ -13,7 +13,6 @@ use crate::text::shaping::ShaperMethods;
|
||||||
use crate::text::Shaper;
|
use crate::text::Shaper;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
use ordered_float::NotNan;
|
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -38,6 +37,7 @@ macro_rules! ot_tag {
|
||||||
pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S');
|
pub const GPOS: u32 = ot_tag!('G', 'P', 'O', 'S');
|
||||||
pub const GSUB: u32 = ot_tag!('G', 'S', 'U', 'B');
|
pub const GSUB: u32 = ot_tag!('G', 'S', 'U', 'B');
|
||||||
pub const KERN: u32 = ot_tag!('k', 'e', 'r', 'n');
|
pub const KERN: u32 = ot_tag!('k', 'e', 'r', 'n');
|
||||||
|
pub const LAST_RESORT_GLYPH_ADVANCE: FractionalPixel = 10.0;
|
||||||
|
|
||||||
static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ pub struct ShapingOptions {
|
||||||
/// NB: You will probably want to set the `IGNORE_LIGATURES_SHAPING_FLAG` if this is non-null.
|
/// NB: You will probably want to set the `IGNORE_LIGATURES_SHAPING_FLAG` if this is non-null.
|
||||||
pub letter_spacing: Option<Au>,
|
pub letter_spacing: Option<Au>,
|
||||||
/// Spacing to add between each word. Corresponds to the CSS 2.1 `word-spacing` property.
|
/// Spacing to add between each word. Corresponds to the CSS 2.1 `word-spacing` property.
|
||||||
pub word_spacing: (Au, NotNan<f32>),
|
pub word_spacing: Au,
|
||||||
/// The Unicode script property of the characters in this run.
|
/// The Unicode script property of the characters in this run.
|
||||||
pub script: Script,
|
pub script: Script,
|
||||||
/// Various flags.
|
/// Various flags.
|
||||||
|
@ -278,8 +278,7 @@ impl Font {
|
||||||
let mut advance = Au::from_f64_px(self.glyph_h_advance(glyph_id));
|
let mut advance = Au::from_f64_px(self.glyph_h_advance(glyph_id));
|
||||||
if character == ' ' {
|
if character == ' ' {
|
||||||
// https://drafts.csswg.org/css-text-3/#word-spacing-property
|
// https://drafts.csswg.org/css-text-3/#word-spacing-property
|
||||||
let (length, percent) = options.word_spacing;
|
advance += options.word_spacing;
|
||||||
advance = (advance + length) + Au((advance.0 as f32 * percent.into_inner()) as i32);
|
|
||||||
}
|
}
|
||||||
if let Some(letter_spacing) = options.letter_spacing {
|
if let Some(letter_spacing) = options.letter_spacing {
|
||||||
advance += letter_spacing;
|
advance += letter_spacing;
|
||||||
|
@ -343,7 +342,7 @@ impl Font {
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
match self.handle.glyph_h_advance(glyph) {
|
match self.handle.glyph_h_advance(glyph) {
|
||||||
Some(adv) => adv,
|
Some(adv) => adv,
|
||||||
None => 10f64 as FractionalPixel, // FIXME: Need fallback strategy
|
None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel, // FIXME: Need fallback strategy
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -516,8 +515,8 @@ impl RunMetrics {
|
||||||
RunMetrics {
|
RunMetrics {
|
||||||
advance_width: advance,
|
advance_width: advance,
|
||||||
bounding_box: bounds,
|
bounding_box: bounds,
|
||||||
ascent: ascent,
|
ascent,
|
||||||
descent: descent,
|
descent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -434,9 +434,7 @@ impl Shaper {
|
||||||
// We elect to only space the two required code points.
|
// We elect to only space the two required code points.
|
||||||
if character == ' ' || character == '\u{a0}' {
|
if character == ' ' || character == '\u{a0}' {
|
||||||
// https://drafts.csswg.org/css-text-3/#word-spacing-property
|
// https://drafts.csswg.org/css-text-3/#word-spacing-property
|
||||||
let (length, percent) = options.word_spacing;
|
advance += options.word_spacing;
|
||||||
advance =
|
|
||||||
(advance + length) + Au::new((advance.0 as f32 * percent.into_inner()) as i32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
advance
|
advance
|
||||||
|
|
|
@ -840,7 +840,10 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
|
|
||||||
match text_content {
|
match text_content {
|
||||||
TextContent::Text(string) => {
|
TextContent::Text(string) => {
|
||||||
let info = Box::new(UnscannedTextFragmentInfo::new(string, node.selection()));
|
let info = Box::new(UnscannedTextFragmentInfo::new(
|
||||||
|
string.into(),
|
||||||
|
node.selection(),
|
||||||
|
));
|
||||||
let specific_fragment_info = SpecificFragmentInfo::UnscannedText(info);
|
let specific_fragment_info = SpecificFragmentInfo::UnscannedText(info);
|
||||||
fragments
|
fragments
|
||||||
.fragments
|
.fragments
|
||||||
|
@ -857,7 +860,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
for content_item in content_items.into_iter() {
|
for content_item in content_items.into_iter() {
|
||||||
let specific_fragment_info = match content_item {
|
let specific_fragment_info = match content_item {
|
||||||
ContentItem::String(string) => {
|
ContentItem::String(string) => {
|
||||||
let info = Box::new(UnscannedTextFragmentInfo::new(string, None));
|
let info =
|
||||||
|
Box::new(UnscannedTextFragmentInfo::new(string.into(), None));
|
||||||
SpecificFragmentInfo::UnscannedText(info)
|
SpecificFragmentInfo::UnscannedText(info)
|
||||||
},
|
},
|
||||||
content_item => {
|
content_item => {
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl ToLayout for TransformStyle {
|
||||||
type Type = wr::TransformStyle;
|
type Type = wr::TransformStyle;
|
||||||
fn to_layout(&self) -> Self::Type {
|
fn to_layout(&self) -> Self::Type {
|
||||||
match *self {
|
match *self {
|
||||||
TransformStyle::Auto | TransformStyle::Flat => wr::TransformStyle::Flat,
|
TransformStyle::Flat => wr::TransformStyle::Flat,
|
||||||
TransformStyle::Preserve3d => wr::TransformStyle::Preserve3D,
|
TransformStyle::Preserve3d => wr::TransformStyle::Preserve3D,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,7 +309,7 @@ impl Flow for TableFlow {
|
||||||
percentage: match *specified_inline_size {
|
percentage: match *specified_inline_size {
|
||||||
Size::Auto => 0.0,
|
Size::Auto => 0.0,
|
||||||
Size::LengthPercentage(ref lp) => {
|
Size::LengthPercentage(ref lp) => {
|
||||||
lp.0.as_percentage().map_or(0.0, |p| p.0)
|
lp.0.to_percentage().map_or(0.0, |p| p.0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preferred: Au(0),
|
preferred: Au(0),
|
||||||
|
|
|
@ -437,7 +437,7 @@ impl Flow for TableRowFlow {
|
||||||
.unwrap_or(child_base.intrinsic_inline_sizes.minimum_inline_size),
|
.unwrap_or(child_base.intrinsic_inline_sizes.minimum_inline_size),
|
||||||
percentage: match child_specified_inline_size {
|
percentage: match child_specified_inline_size {
|
||||||
Size::Auto => 0.0,
|
Size::Auto => 0.0,
|
||||||
Size::LengthPercentage(ref lp) => lp.0.as_percentage().map_or(0.0, |p| p.0),
|
Size::LengthPercentage(ref lp) => lp.0.to_percentage().map_or(0.0, |p| p.0),
|
||||||
},
|
},
|
||||||
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
|
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
|
||||||
constrained: match child_specified_inline_size {
|
constrained: match child_specified_inline_size {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTe
|
||||||
use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
|
use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
|
||||||
use crate::linked_list::split_off_head;
|
use crate::linked_list::split_off_head;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use gfx::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
use gfx::font::{self, FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||||
use gfx::text::glyph::ByteIndex;
|
use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
use gfx::text::util::{self, CompressionMode};
|
use gfx::text::util::{self, CompressionMode};
|
||||||
|
@ -195,7 +195,24 @@ impl TextRunScanner {
|
||||||
};
|
};
|
||||||
text_transform = inherited_text_style.text_transform;
|
text_transform = inherited_text_style.text_transform;
|
||||||
letter_spacing = inherited_text_style.letter_spacing;
|
letter_spacing = inherited_text_style.letter_spacing;
|
||||||
word_spacing = inherited_text_style.word_spacing.to_hash_key();
|
word_spacing = inherited_text_style
|
||||||
|
.word_spacing
|
||||||
|
.to_length()
|
||||||
|
.map(|l| l.into())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let space_width = font_group
|
||||||
|
.borrow_mut()
|
||||||
|
.find_by_codepoint(&mut font_context, ' ')
|
||||||
|
.and_then(|font| {
|
||||||
|
let font = font.borrow();
|
||||||
|
font.glyph_index(' ')
|
||||||
|
.map(|glyph_id| font.glyph_h_advance(glyph_id))
|
||||||
|
})
|
||||||
|
.unwrap_or(font::LAST_RESORT_GLYPH_ADVANCE);
|
||||||
|
inherited_text_style
|
||||||
|
.word_spacing
|
||||||
|
.to_used_value(Au::from_f64_px(space_width))
|
||||||
|
});
|
||||||
text_rendering = inherited_text_style.text_rendering;
|
text_rendering = inherited_text_style.text_rendering;
|
||||||
word_break = inherited_text_style.word_break;
|
word_break = inherited_text_style.word_break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,8 +159,12 @@ impl InlineFormattingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_lengthpercentage(&mut self, lp: LengthPercentage) {
|
fn add_lengthpercentage(&mut self, lp: LengthPercentage) {
|
||||||
self.add_length(lp.length_component());
|
if let Some(l) = lp.to_length() {
|
||||||
self.current_line_percentages += lp.percentage_component();
|
self.add_length(l);
|
||||||
|
}
|
||||||
|
if let Some(p) = lp.to_percentage() {
|
||||||
|
self.current_line_percentages += p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_length(&mut self, l: Length) {
|
fn add_length(&mut self, l: Length) {
|
||||||
|
@ -601,13 +605,6 @@ impl TextRun {
|
||||||
flags.insert(ShapingFlags::KEEP_ALL_FLAG);
|
flags.insert(ShapingFlags::KEEP_ALL_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
let shaping_options = gfx::font::ShapingOptions {
|
|
||||||
letter_spacing,
|
|
||||||
word_spacing: inherited_text_style.word_spacing.to_hash_key(),
|
|
||||||
script: unicode_script::Script::Common,
|
|
||||||
flags,
|
|
||||||
};
|
|
||||||
|
|
||||||
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
||||||
let font_group = font_context.font_group(font_style);
|
let font_group = font_context.font_group(font_style);
|
||||||
let font = font_group
|
let font = font_group
|
||||||
|
@ -616,6 +613,25 @@ impl TextRun {
|
||||||
.expect("could not find font");
|
.expect("could not find font");
|
||||||
let mut font = font.borrow_mut();
|
let mut font = font.borrow_mut();
|
||||||
|
|
||||||
|
let word_spacing = &inherited_text_style.word_spacing;
|
||||||
|
let word_spacing = word_spacing
|
||||||
|
.to_length()
|
||||||
|
.map(|l| l.into())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let space_width = font
|
||||||
|
.glyph_index(' ')
|
||||||
|
.map(|glyph_id| font.glyph_h_advance(glyph_id))
|
||||||
|
.unwrap_or(gfx::font::LAST_RESORT_GLYPH_ADVANCE);
|
||||||
|
word_spacing.to_used_value(Au::from_f64_px(space_width))
|
||||||
|
});
|
||||||
|
|
||||||
|
let shaping_options = gfx::font::ShapingOptions {
|
||||||
|
letter_spacing,
|
||||||
|
word_spacing,
|
||||||
|
script: unicode_script::Script::Common,
|
||||||
|
flags,
|
||||||
|
};
|
||||||
|
|
||||||
let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
||||||
&mut font,
|
&mut font,
|
||||||
&self.text,
|
&self.text,
|
||||||
|
|
|
@ -117,12 +117,12 @@ impl BoxContentSizes {
|
||||||
.auto_is(Length::zero);
|
.auto_is(Length::zero);
|
||||||
let max_inline_size = match style.max_box_size().inline {
|
let max_inline_size = match style.max_box_size().inline {
|
||||||
MaxSize::None => None,
|
MaxSize::None => None,
|
||||||
MaxSize::LengthPercentage(ref lp) => lp.as_length(),
|
MaxSize::LengthPercentage(ref lp) => lp.to_length(),
|
||||||
};
|
};
|
||||||
let clamp = |l: Length| l.clamp_between_extremums(min_inline_size, max_inline_size);
|
let clamp = |l: Length| l.clamp_between_extremums(min_inline_size, max_inline_size);
|
||||||
|
|
||||||
// Percentages for 'width' are treated as 'auto'
|
// Percentages for 'width' are treated as 'auto'
|
||||||
let inline_size = inline_size.map(|lp| lp.as_length());
|
let inline_size = inline_size.map(|lp| lp.to_length());
|
||||||
// The (inner) min/max-content are only used for 'auto'
|
// The (inner) min/max-content are only used for 'auto'
|
||||||
let mut outer = match inline_size.non_auto().flatten() {
|
let mut outer = match inline_size.non_auto().flatten() {
|
||||||
None => {
|
None => {
|
||||||
|
@ -148,8 +148,12 @@ impl BoxContentSizes {
|
||||||
let margin = style.margin();
|
let margin = style.margin();
|
||||||
pbm_lengths += border.inline_sum();
|
pbm_lengths += border.inline_sum();
|
||||||
let mut add = |x: LengthPercentage| {
|
let mut add = |x: LengthPercentage| {
|
||||||
pbm_lengths += x.length_component();
|
if let Some(l) = x.to_length() {
|
||||||
pbm_percentages += x.percentage_component();
|
pbm_lengths += l;
|
||||||
|
}
|
||||||
|
if let Some(p) = x.to_percentage() {
|
||||||
|
pbm_percentages += p;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
add(padding.inline_start);
|
add(padding.inline_start);
|
||||||
add(padding.inline_end);
|
add(padding.inline_end);
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl ComputedValuesExt for ComputedValues {
|
||||||
} else {
|
} else {
|
||||||
&position.height
|
&position.height
|
||||||
};
|
};
|
||||||
matches!(size, Size::LengthPercentage(lp) if lp.0.as_length().is_some())
|
matches!(size, Size::LengthPercentage(lp) if lp.0.to_length().is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
//! Note: WebRender has a reduced fork of this crate, so that we can avoid
|
//! Note: WebRender has a reduced fork of this crate, so that we can avoid
|
||||||
//! publishing this crate on crates.io.
|
//! publishing this crate on crates.io.
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
extern crate accountable_refcell;
|
extern crate accountable_refcell;
|
||||||
extern crate app_units;
|
extern crate app_units;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
|
|
@ -55,7 +55,6 @@ num_cpus = {version = "1.1.0"}
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-derive = "0.2"
|
num-derive = "0.2"
|
||||||
ordered-float = "1.0"
|
|
||||||
owning_ref = "0.4"
|
owning_ref = "0.4"
|
||||||
parking_lot = "0.9"
|
parking_lot = "0.9"
|
||||||
precomputed-hash = "0.1.1"
|
precomputed-hash = "0.1.1"
|
||||||
|
|
|
@ -409,6 +409,7 @@ impl ToCss for System {
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
|
/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
|
||||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Symbol {
|
pub enum Symbol {
|
||||||
/// <string>
|
/// <string>
|
||||||
String(crate::OwnedStr),
|
String(crate::OwnedStr),
|
||||||
|
@ -554,6 +555,7 @@ impl Parse for Fallback {
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
|
/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
|
||||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
|
pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
|
||||||
|
|
||||||
impl Parse for Symbols {
|
impl Parse for Symbols {
|
||||||
|
|
|
@ -7,11 +7,14 @@
|
||||||
//! [custom]: https://drafts.csswg.org/css-variables/
|
//! [custom]: https://drafts.csswg.org/css-variables/
|
||||||
|
|
||||||
use crate::hash::map::Entry;
|
use crate::hash::map::Entry;
|
||||||
|
use crate::media_queries::Device;
|
||||||
use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
|
use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
|
||||||
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
|
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
|
||||||
use crate::stylesheets::{Origin, PerOrigin};
|
use crate::stylesheets::{Origin, PerOrigin};
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
|
use cssparser::{
|
||||||
|
CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,
|
||||||
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
@ -29,50 +32,50 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
#[derive(Debug, MallocSizeOf)]
|
#[derive(Debug, MallocSizeOf)]
|
||||||
pub struct CssEnvironment;
|
pub struct CssEnvironment;
|
||||||
|
|
||||||
|
type EnvironmentEvaluator = fn(device: &Device) -> VariableValue;
|
||||||
|
|
||||||
struct EnvironmentVariable {
|
struct EnvironmentVariable {
|
||||||
name: Atom,
|
name: Atom,
|
||||||
value: VariableValue,
|
evaluator: EnvironmentEvaluator,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! make_variable {
|
macro_rules! make_variable {
|
||||||
($name:expr, $value:expr) => {{
|
($name:expr, $evaluator:expr) => {{
|
||||||
EnvironmentVariable {
|
EnvironmentVariable {
|
||||||
name: $name,
|
name: $name,
|
||||||
value: {
|
evaluator: $evaluator,
|
||||||
// TODO(emilio): We could make this be more efficient (though a
|
|
||||||
// bit less convenient).
|
|
||||||
let mut input = ParserInput::new($value);
|
|
||||||
let mut input = Parser::new(&mut input);
|
|
||||||
|
|
||||||
let (first_token_type, css, last_token_type) =
|
|
||||||
parse_self_contained_declaration_value(&mut input, None).unwrap();
|
|
||||||
|
|
||||||
VariableValue {
|
|
||||||
css: css.into_owned(),
|
|
||||||
first_token_type,
|
|
||||||
last_token_type,
|
|
||||||
references: Default::default(),
|
|
||||||
references_environment: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
fn get_safearea_inset_top(device: &Device) -> VariableValue {
|
||||||
static ref ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
VariableValue::pixel(device.safe_area_insets().top)
|
||||||
make_variable!(atom!("safe-area-inset-top"), "0px"),
|
|
||||||
make_variable!(atom!("safe-area-inset-bottom"), "0px"),
|
|
||||||
make_variable!(atom!("safe-area-inset-left"), "0px"),
|
|
||||||
make_variable!(atom!("safe-area-inset-right"), "0px"),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_safearea_inset_bottom(device: &Device) -> VariableValue {
|
||||||
|
VariableValue::pixel(device.safe_area_insets().bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_safearea_inset_left(device: &Device) -> VariableValue {
|
||||||
|
VariableValue::pixel(device.safe_area_insets().left)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_safearea_inset_right(device: &Device) -> VariableValue {
|
||||||
|
VariableValue::pixel(device.safe_area_insets().right)
|
||||||
|
}
|
||||||
|
|
||||||
|
static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
||||||
|
make_variable!(atom!("safe-area-inset-top"), get_safearea_inset_top),
|
||||||
|
make_variable!(atom!("safe-area-inset-bottom"), get_safearea_inset_bottom),
|
||||||
|
make_variable!(atom!("safe-area-inset-left"), get_safearea_inset_left),
|
||||||
|
make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right),
|
||||||
|
];
|
||||||
|
|
||||||
impl CssEnvironment {
|
impl CssEnvironment {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get(&self, name: &Atom) -> Option<&VariableValue> {
|
fn get(&self, name: &Atom, device: &Device) -> Option<VariableValue> {
|
||||||
let var = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?;
|
let var = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?;
|
||||||
Some(&var.value)
|
Some((var.evaluator)(device))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,6 +255,28 @@ impl VariableValue {
|
||||||
references_environment: references.references_environment,
|
references_environment: references.references_environment,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create VariableValue from css pixel value
|
||||||
|
pub fn pixel(number: f32) -> Self {
|
||||||
|
// FIXME (https://github.com/servo/rust-cssparser/issues/266):
|
||||||
|
// No way to get TokenSerializationType::Dimension without creating
|
||||||
|
// Token object.
|
||||||
|
let token = Token::Dimension {
|
||||||
|
has_sign: false,
|
||||||
|
value: number,
|
||||||
|
int_value: None,
|
||||||
|
unit: CowRcStr::from("px"),
|
||||||
|
};
|
||||||
|
let token_type = token.serialization_type();
|
||||||
|
|
||||||
|
VariableValue {
|
||||||
|
css: token.to_css_string(),
|
||||||
|
first_token_type: token_type,
|
||||||
|
last_token_type: token_type,
|
||||||
|
references: Default::default(),
|
||||||
|
references_environment: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the value of a non-custom property that contains `var()` references.
|
/// Parse the value of a non-custom property that contains `var()` references.
|
||||||
|
@ -497,22 +522,19 @@ pub struct CustomPropertiesBuilder<'a> {
|
||||||
may_have_cycles: bool,
|
may_have_cycles: bool,
|
||||||
custom_properties: Option<CustomPropertiesMap>,
|
custom_properties: Option<CustomPropertiesMap>,
|
||||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
environment: &'a CssEnvironment,
|
device: &'a Device,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CustomPropertiesBuilder<'a> {
|
impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Create a new builder, inheriting from a given custom properties map.
|
/// Create a new builder, inheriting from a given custom properties map.
|
||||||
pub fn new(
|
pub fn new(inherited: Option<&'a Arc<CustomPropertiesMap>>, device: &'a Device) -> Self {
|
||||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
|
||||||
environment: &'a CssEnvironment,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
seen: PrecomputedHashSet::default(),
|
seen: PrecomputedHashSet::default(),
|
||||||
reverted: Default::default(),
|
reverted: Default::default(),
|
||||||
may_have_cycles: false,
|
may_have_cycles: false,
|
||||||
custom_properties: None,
|
custom_properties: None,
|
||||||
inherited,
|
inherited,
|
||||||
environment,
|
device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,8 +575,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
// environment variable here, perform substitution here instead
|
// environment variable here, perform substitution here instead
|
||||||
// of forcing a full traversal in `substitute_all` afterwards.
|
// of forcing a full traversal in `substitute_all` afterwards.
|
||||||
let value = if !has_references && unparsed_value.references_environment {
|
let value = if !has_references && unparsed_value.references_environment {
|
||||||
let result =
|
let result = substitute_references_in_value(unparsed_value, &map, &self.device);
|
||||||
substitute_references_in_value(unparsed_value, &map, &self.environment);
|
|
||||||
match result {
|
match result {
|
||||||
Ok(new_value) => Arc::new(new_value),
|
Ok(new_value) => Arc::new(new_value),
|
||||||
Err(..) => {
|
Err(..) => {
|
||||||
|
@ -632,7 +653,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
None => return self.inherited.cloned(),
|
None => return self.inherited.cloned(),
|
||||||
};
|
};
|
||||||
if self.may_have_cycles {
|
if self.may_have_cycles {
|
||||||
substitute_all(&mut map, self.environment);
|
substitute_all(&mut map, self.device);
|
||||||
}
|
}
|
||||||
Some(Arc::new(map))
|
Some(Arc::new(map))
|
||||||
}
|
}
|
||||||
|
@ -641,7 +662,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Resolve all custom properties to either substituted or invalid.
|
/// Resolve all custom properties to either substituted or invalid.
|
||||||
///
|
///
|
||||||
/// It does cycle dependencies removal at the same time as substitution.
|
/// It does cycle dependencies removal at the same time as substitution.
|
||||||
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment: &CssEnvironment) {
|
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, device: &Device) {
|
||||||
// The cycle dependencies removal in this function is a variant
|
// The cycle dependencies removal in this function is a variant
|
||||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||||
// listed in
|
// listed in
|
||||||
|
@ -677,8 +698,8 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment:
|
||||||
/// all unfinished strong connected components.
|
/// all unfinished strong connected components.
|
||||||
stack: SmallVec<[usize; 5]>,
|
stack: SmallVec<[usize; 5]>,
|
||||||
map: &'a mut CustomPropertiesMap,
|
map: &'a mut CustomPropertiesMap,
|
||||||
/// The environment to substitute `env()` variables.
|
/// to resolve the environment to substitute `env()` variables.
|
||||||
environment: &'a CssEnvironment,
|
device: &'a Device,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function combines the traversal for cycle removal and value
|
/// This function combines the traversal for cycle removal and value
|
||||||
|
@ -813,7 +834,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment:
|
||||||
// Now we have shown that this variable is not in a loop, and
|
// Now we have shown that this variable is not in a loop, and
|
||||||
// all of its dependencies should have been resolved. We can
|
// all of its dependencies should have been resolved. We can
|
||||||
// start substitution now.
|
// start substitution now.
|
||||||
let result = substitute_references_in_value(&value, &context.map, &context.environment);
|
let result = substitute_references_in_value(&value, &context.map, &context.device);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(computed_value) => {
|
Ok(computed_value) => {
|
||||||
|
@ -838,7 +859,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment:
|
||||||
stack: SmallVec::new(),
|
stack: SmallVec::new(),
|
||||||
var_info: SmallVec::new(),
|
var_info: SmallVec::new(),
|
||||||
map: custom_properties_map,
|
map: custom_properties_map,
|
||||||
environment,
|
device,
|
||||||
};
|
};
|
||||||
traverse(name, &mut context);
|
traverse(name, &mut context);
|
||||||
}
|
}
|
||||||
|
@ -848,7 +869,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment:
|
||||||
fn substitute_references_in_value<'i>(
|
fn substitute_references_in_value<'i>(
|
||||||
value: &'i VariableValue,
|
value: &'i VariableValue,
|
||||||
custom_properties: &CustomPropertiesMap,
|
custom_properties: &CustomPropertiesMap,
|
||||||
environment: &CssEnvironment,
|
device: &Device,
|
||||||
) -> Result<ComputedValue, ParseError<'i>> {
|
) -> Result<ComputedValue, ParseError<'i>> {
|
||||||
debug_assert!(!value.references.is_empty() || value.references_environment);
|
debug_assert!(!value.references.is_empty() || value.references_environment);
|
||||||
|
|
||||||
|
@ -862,7 +883,7 @@ fn substitute_references_in_value<'i>(
|
||||||
&mut position,
|
&mut position,
|
||||||
&mut computed_value,
|
&mut computed_value,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
environment,
|
device,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
computed_value.push_from(&input, position, last_token_type)?;
|
computed_value.push_from(&input, position, last_token_type)?;
|
||||||
|
@ -884,7 +905,7 @@ fn substitute_block<'i>(
|
||||||
position: &mut (SourcePosition, TokenSerializationType),
|
position: &mut (SourcePosition, TokenSerializationType),
|
||||||
partial_computed_value: &mut ComputedValue,
|
partial_computed_value: &mut ComputedValue,
|
||||||
custom_properties: &CustomPropertiesMap,
|
custom_properties: &CustomPropertiesMap,
|
||||||
env: &CssEnvironment,
|
device: &Device,
|
||||||
) -> Result<TokenSerializationType, ParseError<'i>> {
|
) -> Result<TokenSerializationType, ParseError<'i>> {
|
||||||
let mut last_token_type = TokenSerializationType::nothing();
|
let mut last_token_type = TokenSerializationType::nothing();
|
||||||
let mut set_position_at_next_iteration = false;
|
let mut set_position_at_next_iteration = false;
|
||||||
|
@ -928,8 +949,14 @@ fn substitute_block<'i>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let env_value;
|
||||||
let value = if is_env {
|
let value = if is_env {
|
||||||
env.get(&name)
|
if let Some(v) = device.environment().get(&name, device) {
|
||||||
|
env_value = v;
|
||||||
|
Some(&env_value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
custom_properties.get(&name).map(|v| &**v)
|
custom_properties.get(&name).map(|v| &**v)
|
||||||
};
|
};
|
||||||
|
@ -956,7 +983,7 @@ fn substitute_block<'i>(
|
||||||
&mut position,
|
&mut position,
|
||||||
partial_computed_value,
|
partial_computed_value,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
env,
|
device,
|
||||||
)?;
|
)?;
|
||||||
partial_computed_value.push_from(input, position, last_token_type)?;
|
partial_computed_value.push_from(input, position, last_token_type)?;
|
||||||
}
|
}
|
||||||
|
@ -974,7 +1001,7 @@ fn substitute_block<'i>(
|
||||||
position,
|
position,
|
||||||
partial_computed_value,
|
partial_computed_value,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
env,
|
device,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
// It's the same type for CloseCurlyBracket and CloseSquareBracket.
|
// It's the same type for CloseCurlyBracket and CloseSquareBracket.
|
||||||
|
@ -1000,7 +1027,7 @@ pub fn substitute<'i>(
|
||||||
input: &'i str,
|
input: &'i str,
|
||||||
first_token_type: TokenSerializationType,
|
first_token_type: TokenSerializationType,
|
||||||
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
||||||
env: &CssEnvironment,
|
device: &Device,
|
||||||
) -> Result<String, ParseError<'i>> {
|
) -> Result<String, ParseError<'i>> {
|
||||||
let mut substituted = ComputedValue::empty();
|
let mut substituted = ComputedValue::empty();
|
||||||
let mut input = ParserInput::new(input);
|
let mut input = ParserInput::new(input);
|
||||||
|
@ -1016,7 +1043,7 @@ pub fn substitute<'i>(
|
||||||
&mut position,
|
&mut position,
|
||||||
&mut substituted,
|
&mut substituted,
|
||||||
&custom_properties,
|
&custom_properties,
|
||||||
env,
|
device,
|
||||||
)?;
|
)?;
|
||||||
substituted.push_from(&input, position, last_token_type)?;
|
substituted.push_from(&input, position, last_token_type)?;
|
||||||
Ok(substituted.css)
|
Ok(substituted.css)
|
||||||
|
|
|
@ -15,7 +15,6 @@ use crate::gecko_bindings::structs::{self, Matrix4x4Components};
|
||||||
use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
|
use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
|
||||||
use crate::stylesheets::RulesMutateError;
|
use crate::stylesheets::RulesMutateError;
|
||||||
use crate::values::computed::transform::Matrix3D;
|
use crate::values::computed::transform::Matrix3D;
|
||||||
use crate::values::computed::url::ComputedImageUrl;
|
|
||||||
use crate::values::computed::{Gradient, Image, TextAlign};
|
use crate::values::computed::{Gradient, Image, TextAlign};
|
||||||
use crate::values::generics::image::GenericImage;
|
use crate::values::generics::image::GenericImage;
|
||||||
use crate::values::generics::rect::Rect;
|
use crate::values::generics::rect::Rect;
|
||||||
|
@ -63,7 +62,7 @@ impl nsStyleImage {
|
||||||
match self.mType {
|
match self.mType {
|
||||||
nsStyleImageType::eStyleImageType_Null => None,
|
nsStyleImageType::eStyleImageType_Null => None,
|
||||||
nsStyleImageType::eStyleImageType_Image => {
|
nsStyleImageType::eStyleImageType_Image => {
|
||||||
let url = self.get_image_url();
|
let url = self.__bindgen_anon_1.mImage.as_ref().clone();
|
||||||
if self.mCropRect.mPtr.is_null() {
|
if self.mCropRect.mPtr.is_null() {
|
||||||
Some(GenericImage::Url(url))
|
Some(GenericImage::Url(url))
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,13 +87,6 @@ impl nsStyleImage {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_image_url(&self) -> ComputedImageUrl {
|
|
||||||
let image_request = bindings::Gecko_GetImageRequest(self)
|
|
||||||
.as_ref()
|
|
||||||
.expect("Null image request?");
|
|
||||||
ComputedImageUrl::from_image_request(image_request)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod basic_shape {
|
pub mod basic_shape {
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::values::{CustomIdent, KeyframesName};
|
||||||
use app_units::{Au, AU_PER_PX};
|
use app_units::{Au, AU_PER_PX};
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
use euclid::default::Size2D;
|
use euclid::default::Size2D;
|
||||||
use euclid::Scale;
|
use euclid::{Scale, SideOffsets2D};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
|
||||||
|
@ -281,6 +281,11 @@ impl Device {
|
||||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
|
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the default foreground color.
|
||||||
|
pub fn default_color(&self) -> RGBA {
|
||||||
|
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the current effective text zoom.
|
/// Returns the current effective text zoom.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn effective_text_zoom(&self) -> f32 {
|
fn effective_text_zoom(&self) -> f32 {
|
||||||
|
@ -302,4 +307,9 @@ impl Device {
|
||||||
pub fn unzoom_text(&self, size: Au) -> Au {
|
pub fn unzoom_text(&self, size: Au) -> Au {
|
||||||
size.scale_by(1. / self.effective_text_zoom())
|
size.scale_by(1. / self.effective_text_zoom())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns safe area insets
|
||||||
|
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
|
||||||
|
SideOffsets2D::zero()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
use crate::gecko_bindings::bindings;
|
use crate::gecko_bindings::bindings;
|
||||||
use crate::gecko_bindings::structs;
|
use crate::gecko_bindings::structs;
|
||||||
use crate::gecko_bindings::structs::nsStyleImageRequest;
|
|
||||||
use crate::gecko_bindings::sugar::refptr::RefPtr;
|
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::stylesheets::{CorsMode, UrlExtraData};
|
use crate::stylesheets::{CorsMode, UrlExtraData};
|
||||||
use crate::values::computed::{Context, ToComputedValue};
|
use crate::values::computed::{Context, ToComputedValue};
|
||||||
|
@ -150,31 +148,52 @@ struct LoadDataKey(*const LoadDataSource);
|
||||||
unsafe impl Sync for LoadDataKey {}
|
unsafe impl Sync for LoadDataKey {}
|
||||||
unsafe impl Send for LoadDataKey {}
|
unsafe impl Send for LoadDataKey {}
|
||||||
|
|
||||||
/// The load data for a given URL. This is mutable from C++, for now at least.
|
bitflags! {
|
||||||
|
/// Various bits of mutable state that are kept for image loads.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LoadDataFlags: u8 {
|
||||||
|
/// Whether we tried to resolve the uri at least once.
|
||||||
|
const TRIED_TO_RESOLVE_URI = 1 << 0;
|
||||||
|
/// Whether we tried to resolve the image at least once.
|
||||||
|
const TRIED_TO_RESOLVE_IMAGE = 1 << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is usable and movable from multiple threads just fine, as long as it's
|
||||||
|
/// not cloned (it is not clonable), and the methods that mutate it run only on
|
||||||
|
/// the main thread (when all the other threads we care about are paused).
|
||||||
|
unsafe impl Sync for LoadData {}
|
||||||
|
unsafe impl Send for LoadData {}
|
||||||
|
|
||||||
|
/// The load data for a given URL. This is mutable from C++, and shouldn't be
|
||||||
|
/// accessed from rust for anything.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LoadData {
|
pub struct LoadData {
|
||||||
resolved: RefPtr<structs::nsIURI>,
|
/// A strong reference to the imgRequestProxy, if any, that should be
|
||||||
load_id: u64,
|
/// released on drop.
|
||||||
tried_to_resolve: bool,
|
///
|
||||||
|
/// These are raw pointers because they are not safe to reference-count off
|
||||||
|
/// the main thread.
|
||||||
|
resolved_image: *mut structs::imgRequestProxy,
|
||||||
|
/// A strong reference to the resolved URI of this image.
|
||||||
|
resolved_uri: *mut structs::nsIURI,
|
||||||
|
/// A few flags that are set when resolving the image or such.
|
||||||
|
flags: LoadDataFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for LoadData {
|
impl Drop for LoadData {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.load_id != 0 {
|
unsafe { bindings::Gecko_LoadData_Drop(self) }
|
||||||
unsafe {
|
|
||||||
bindings::Gecko_LoadData_DeregisterLoad(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LoadData {
|
impl Default for LoadData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
resolved: RefPtr::null(),
|
resolved_image: std::ptr::null_mut(),
|
||||||
load_id: 0,
|
resolved_uri: std::ptr::null_mut(),
|
||||||
tried_to_resolve: false,
|
flags: LoadDataFlags::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,13 +361,6 @@ impl ToCss for ComputedUrl {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ComputedImageUrl(pub ComputedUrl);
|
pub struct ComputedImageUrl(pub ComputedUrl);
|
||||||
|
|
||||||
impl ComputedImageUrl {
|
|
||||||
/// Convert from nsStyleImageRequest to ComputedImageUrl.
|
|
||||||
pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
|
|
||||||
image_request.mImageURL.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for ComputedImageUrl {
|
impl ToCss for ComputedImageUrl {
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
//! Different kind of helpers to interact with Gecko values.
|
//! Different kind of helpers to interact with Gecko values.
|
||||||
|
|
||||||
use crate::counter_style::{Symbol, Symbols};
|
use crate::counter_style::{Symbol, Symbols};
|
||||||
|
use crate::gecko_bindings::bindings;
|
||||||
use crate::gecko_bindings::structs::CounterStylePtr;
|
use crate::gecko_bindings::structs::CounterStylePtr;
|
||||||
use crate::values::generics::CounterStyle;
|
use crate::values::generics::CounterStyle;
|
||||||
use crate::values::Either;
|
use crate::values::Either;
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
use nsstring::{nsACString, nsCStr};
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
/// Convert a given RGBA value to `nscolor`.
|
/// Convert a given RGBA value to `nscolor`.
|
||||||
|
@ -51,43 +51,13 @@ pub fn round_border_to_device_pixels(width: Au, au_per_device_px: Au) -> Au {
|
||||||
|
|
||||||
impl CounterStyle {
|
impl CounterStyle {
|
||||||
/// Convert this counter style to a Gecko CounterStylePtr.
|
/// Convert this counter style to a Gecko CounterStylePtr.
|
||||||
pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr) {
|
#[inline]
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToName as set_name;
|
pub fn to_gecko_value(&self, gecko_value: &mut CounterStylePtr) {
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToSymbols as set_symbols;
|
unsafe { bindings::Gecko_CounterStyle_ToPtr(self, gecko_value) }
|
||||||
match self {
|
|
||||||
CounterStyle::Name(name) => unsafe {
|
|
||||||
debug_assert_ne!(name.0, atom!("none"));
|
|
||||||
set_name(gecko_value, name.0.into_addrefed());
|
|
||||||
},
|
|
||||||
CounterStyle::Symbols(symbols_type, symbols) => {
|
|
||||||
let symbols: Vec<_> = symbols
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|symbol| match *symbol {
|
|
||||||
Symbol::String(ref s) => nsCStr::from(&**s),
|
|
||||||
Symbol::Ident(_) => unreachable!("Should not have identifier in symbols()"),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let symbols: Vec<_> = symbols
|
|
||||||
.iter()
|
|
||||||
.map(|symbol| symbol as &nsACString as *const _)
|
|
||||||
.collect();
|
|
||||||
unsafe {
|
|
||||||
set_symbols(
|
|
||||||
gecko_value,
|
|
||||||
symbols_type.to_gecko_keyword(),
|
|
||||||
symbols.as_ptr(),
|
|
||||||
symbols.len() as u32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Gecko CounterStylePtr to CounterStyle or String.
|
/// Convert Gecko CounterStylePtr to CounterStyle or String.
|
||||||
pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either<Self, String> {
|
pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either<Self, String> {
|
||||||
use crate::gecko_bindings::bindings;
|
|
||||||
use crate::values::generics::SymbolsType;
|
|
||||||
use crate::values::CustomIdent;
|
use crate::values::CustomIdent;
|
||||||
|
|
||||||
let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) };
|
let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) };
|
||||||
|
@ -103,7 +73,7 @@ impl CounterStyle {
|
||||||
debug_assert_eq!(symbols.len(), 1);
|
debug_assert_eq!(symbols.len(), 1);
|
||||||
Either::Second(symbols[0].to_string())
|
Either::Second(symbols[0].to_string())
|
||||||
} else {
|
} else {
|
||||||
let symbol_type = SymbolsType::from_gecko_keyword(anonymous.mSystem as u32);
|
let symbol_type = anonymous.mSymbolsType;
|
||||||
let symbols = symbols
|
let symbols = symbols
|
||||||
.iter()
|
.iter()
|
||||||
.map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into()))
|
.map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into()))
|
||||||
|
|
|
@ -956,9 +956,8 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
|
||||||
|
|
||||||
let (wm, font) = match base_size {
|
let (wm, font) = match base_size {
|
||||||
FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
|
FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
|
||||||
// These are only used for font-size computation, and the first is
|
// This is only used for font-size computation.
|
||||||
// really dubious...
|
FontBaseSize::InheritedStyle => {
|
||||||
FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => {
|
|
||||||
(*style.inherited_writing_mode(), style.get_parent_font())
|
(*style.inherited_writing_mode(), style.get_parent_font())
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1869,22 +1868,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||||
debug_assert!(self.is_pseudo_element());
|
debug_assert!(self.is_pseudo_element());
|
||||||
let parent = self.closest_anon_subtree_root_parent()?;
|
self.closest_anon_subtree_root_parent()
|
||||||
|
|
||||||
// FIXME(emilio): Special-case for <input type="number">s
|
|
||||||
// pseudo-elements, which are nested NAC. Probably nsNumberControlFrame
|
|
||||||
// should instead inherit from nsTextControlFrame, and then this could
|
|
||||||
// go away.
|
|
||||||
if let Some(PseudoElement::MozNumberText) = parent.implemented_pseudo_element() {
|
|
||||||
debug_assert_eq!(
|
|
||||||
self.implemented_pseudo_element().unwrap(),
|
|
||||||
PseudoElement::Placeholder,
|
|
||||||
"You added a new pseudo, do you really want this?"
|
|
||||||
);
|
|
||||||
return parent.closest_anon_subtree_root_parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -303,11 +303,6 @@ impl_threadsafe_refcount!(
|
||||||
bindings::Gecko_AddRefURLExtraDataArbitraryThread,
|
bindings::Gecko_AddRefURLExtraDataArbitraryThread,
|
||||||
bindings::Gecko_ReleaseURLExtraDataArbitraryThread
|
bindings::Gecko_ReleaseURLExtraDataArbitraryThread
|
||||||
);
|
);
|
||||||
impl_threadsafe_refcount!(
|
|
||||||
structs::nsIReferrerInfo,
|
|
||||||
bindings::Gecko_AddRefnsIReferrerInfoArbitraryThread,
|
|
||||||
bindings::Gecko_ReleasensIReferrerInfoArbitraryThread
|
|
||||||
);
|
|
||||||
impl_threadsafe_refcount!(
|
impl_threadsafe_refcount!(
|
||||||
structs::nsIURI,
|
structs::nsIURI,
|
||||||
bindings::Gecko_AddRefnsIURIArbitraryThread,
|
bindings::Gecko_AddRefnsIURIArbitraryThread,
|
||||||
|
|
|
@ -25,6 +25,7 @@ macro_rules! ns {
|
||||||
|
|
||||||
/// A Gecko namespace is just a wrapped atom.
|
/// A Gecko namespace is just a wrapped atom.
|
||||||
#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct Namespace(pub Atom);
|
pub struct Namespace(pub Atom);
|
||||||
|
|
||||||
impl PrecomputedHash for Namespace {
|
impl PrecomputedHash for Namespace {
|
||||||
|
|
|
@ -72,7 +72,6 @@ extern crate num_cpus;
|
||||||
extern crate num_derive;
|
extern crate num_derive;
|
||||||
extern crate num_integer;
|
extern crate num_integer;
|
||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
extern crate ordered_float;
|
|
||||||
extern crate owning_ref;
|
extern crate owning_ref;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate precomputed_hash;
|
extern crate precomputed_hash;
|
||||||
|
@ -120,6 +119,8 @@ pub mod attr;
|
||||||
pub mod author_styles;
|
pub mod author_styles;
|
||||||
pub mod bezier;
|
pub mod bezier;
|
||||||
pub mod bloom;
|
pub mod bloom;
|
||||||
|
#[path = "properties/computed_value_flags.rs"]
|
||||||
|
pub mod computed_value_flags;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod counter_style;
|
pub mod counter_style;
|
||||||
pub mod custom_properties;
|
pub mod custom_properties;
|
||||||
|
@ -173,10 +174,12 @@ pub mod values;
|
||||||
pub use crate::gecko_string_cache as string_cache;
|
pub use crate::gecko_string_cache as string_cache;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use crate::gecko_string_cache::Atom;
|
pub use crate::gecko_string_cache::Atom;
|
||||||
|
/// The namespace prefix type for Gecko, which is just an atom.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use crate::gecko_string_cache::Atom as Prefix;
|
pub type Prefix = crate::gecko_string_cache::Atom;
|
||||||
|
/// The local name of an element for Gecko, which is just an atom.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use crate::gecko_string_cache::Atom as LocalName;
|
pub type LocalName = crate::gecko_string_cache::Atom;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use crate::gecko_string_cache::Namespace;
|
pub use crate::gecko_string_cache::Namespace;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
||||||
use crate::context::{SharedStyleContext, StyleContext};
|
use crate::context::{SharedStyleContext, StyleContext};
|
||||||
use crate::data::ElementData;
|
use crate::data::ElementData;
|
||||||
|
@ -705,7 +706,10 @@ pub trait MatchMethods: TElement {
|
||||||
let new_primary_style = data.styles.primary.as_ref().unwrap();
|
let new_primary_style = data.styles.primary.as_ref().unwrap();
|
||||||
|
|
||||||
let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
|
let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
|
||||||
if self.is_root() && !self.is_in_native_anonymous_subtree() {
|
if new_primary_style
|
||||||
|
.flags
|
||||||
|
.contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE)
|
||||||
|
{
|
||||||
let device = context.shared.stylist.device();
|
let device = context.shared.stylist.device();
|
||||||
let new_font_size = new_primary_style.get_font().clone_font_size();
|
let new_font_size = new_primary_style.get_font().clone_font_size();
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use crate::style_adjuster::StyleAdjuster;
|
use crate::style_adjuster::StyleAdjuster;
|
||||||
use crate::values::computed;
|
use crate::values::{computed, specified};
|
||||||
|
|
||||||
/// We split the cascade in two phases: 'early' properties, and 'late'
|
/// We split the cascade in two phases: 'early' properties, and 'late'
|
||||||
/// properties.
|
/// properties.
|
||||||
|
@ -250,7 +250,7 @@ where
|
||||||
let custom_properties = {
|
let custom_properties = {
|
||||||
let mut builder = CustomPropertiesBuilder::new(
|
let mut builder = CustomPropertiesBuilder::new(
|
||||||
inherited_style.custom_properties(),
|
inherited_style.custom_properties(),
|
||||||
device.environment(),
|
device,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (declaration, origin) in iter_declarations() {
|
for (declaration, origin) in iter_declarations() {
|
||||||
|
@ -263,8 +263,11 @@ where
|
||||||
builder.build()
|
builder.build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let is_root_element =
|
||||||
|
pseudo.is_none() && element.map_or(false, |e| e.is_root());
|
||||||
|
|
||||||
let mut context = computed::Context {
|
let mut context = computed::Context {
|
||||||
is_root_element: pseudo.is_none() && element.map_or(false, |e| e.is_root()),
|
|
||||||
// We'd really like to own the rules here to avoid refcount traffic, but
|
// We'd really like to own the rules here to avoid refcount traffic, but
|
||||||
// animation's usage of `apply_declarations` make this tricky. See bug
|
// animation's usage of `apply_declarations` make this tricky. See bug
|
||||||
// 1375525.
|
// 1375525.
|
||||||
|
@ -275,6 +278,7 @@ where
|
||||||
pseudo,
|
pseudo,
|
||||||
Some(rules.clone()),
|
Some(rules.clone()),
|
||||||
custom_properties,
|
custom_properties,
|
||||||
|
is_root_element,
|
||||||
),
|
),
|
||||||
cached_system_font: None,
|
cached_system_font: None,
|
||||||
in_media_query: false,
|
in_media_query: false,
|
||||||
|
@ -333,27 +337,37 @@ where
|
||||||
context.builder.build()
|
context.builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_ignore_declaration_when_ignoring_document_colors(
|
/// How should a declaration behave when ignoring document colors?
|
||||||
device: &Device,
|
enum DeclarationApplication {
|
||||||
|
/// We should apply the declaration.
|
||||||
|
Apply,
|
||||||
|
/// We should ignore the declaration.
|
||||||
|
Ignore,
|
||||||
|
/// We should apply the following declaration, only if any other declaration
|
||||||
|
/// hasn't set it before.
|
||||||
|
ApplyUnlessOverriden(PropertyDeclaration),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn application_when_ignoring_colors(
|
||||||
|
builder: &StyleBuilder,
|
||||||
longhand_id: LonghandId,
|
longhand_id: LonghandId,
|
||||||
origin: Origin,
|
origin: Origin,
|
||||||
pseudo: Option<&PseudoElement>,
|
declaration: &PropertyDeclaration,
|
||||||
declaration: &mut Cow<PropertyDeclaration>,
|
) -> DeclarationApplication {
|
||||||
) -> bool {
|
|
||||||
if !longhand_id.ignored_when_document_colors_disabled() {
|
if !longhand_id.ignored_when_document_colors_disabled() {
|
||||||
return false;
|
return DeclarationApplication::Apply;
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
|
let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
|
||||||
if is_ua_or_user_rule {
|
if is_ua_or_user_rule {
|
||||||
return false;
|
return DeclarationApplication::Apply;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't override background-color on ::-moz-color-swatch. It is set as an
|
// Don't override background-color on ::-moz-color-swatch. It is set as an
|
||||||
// author style (via the style attribute), but it's pretty important for it
|
// author style (via the style attribute), but it's pretty important for it
|
||||||
// to show up for obvious reasons :)
|
// to show up for obvious reasons :)
|
||||||
if pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
|
if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
|
||||||
return false;
|
return DeclarationApplication::Apply;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat background-color a bit differently. If the specified color is
|
// Treat background-color a bit differently. If the specified color is
|
||||||
|
@ -365,26 +379,41 @@ fn should_ignore_declaration_when_ignoring_document_colors(
|
||||||
// a background image, if we're ignoring document colors).
|
// a background image, if we're ignoring document colors).
|
||||||
// Here we check backplate status to decide if ignoring background-image
|
// Here we check backplate status to decide if ignoring background-image
|
||||||
// is the right decision.
|
// is the right decision.
|
||||||
{
|
match *declaration {
|
||||||
let background_color = match **declaration {
|
PropertyDeclaration::BackgroundColor(ref color) => {
|
||||||
PropertyDeclaration::BackgroundColor(ref color) => color,
|
if color.is_transparent() {
|
||||||
// In the future, if/when we remove the backplate pref, we can remove this
|
return DeclarationApplication::Apply;
|
||||||
// special case along with the 'ignored_when_colors_disabled=True' mako line
|
}
|
||||||
// for the "background-image" property.
|
let color = builder.device.default_background_color();
|
||||||
#[cfg(feature = "gecko")]
|
DeclarationApplication::ApplyUnlessOverriden(
|
||||||
PropertyDeclaration::BackgroundImage(..) => return !static_prefs::pref!("browser.display.permit_backplate"),
|
PropertyDeclaration::BackgroundColor(color.into())
|
||||||
_ => return true,
|
)
|
||||||
};
|
|
||||||
|
|
||||||
if background_color.is_transparent() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
PropertyDeclaration::Color(ref color) => {
|
||||||
|
if color.0.is_transparent() {
|
||||||
|
return DeclarationApplication::Apply;
|
||||||
|
}
|
||||||
|
if builder.get_parent_inherited_text().clone_color().alpha != 0 {
|
||||||
|
return DeclarationApplication::Ignore;
|
||||||
|
}
|
||||||
|
let color = builder.device.default_color();
|
||||||
|
DeclarationApplication::ApplyUnlessOverriden(
|
||||||
|
PropertyDeclaration::Color(specified::ColorPropertyValue(color.into()))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// In the future, if/when we remove the backplate pref, we can remove this
|
||||||
|
// special case along with the 'ignored_when_colors_disabled=True' mako line
|
||||||
|
// for the "background-image" property.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
PropertyDeclaration::BackgroundImage(..) => {
|
||||||
|
if static_prefs::pref!("browser.display.permit_backplate") {
|
||||||
|
DeclarationApplication::Apply
|
||||||
|
} else {
|
||||||
|
DeclarationApplication::Ignore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => DeclarationApplication::Ignore,
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = device.default_background_color();
|
|
||||||
*declaration.to_mut() = PropertyDeclaration::BackgroundColor(color.into());
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Cascade<'a, 'b: 'a> {
|
struct Cascade<'a, 'b: 'a> {
|
||||||
|
@ -424,7 +453,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
self.context.builder.custom_properties.as_ref(),
|
self.context.builder.custom_properties.as_ref(),
|
||||||
self.context.quirks_mode,
|
self.context.quirks_mode,
|
||||||
self.context.device().environment(),
|
self.context.device(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,6 +490,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let ignore_colors = !self.context.builder.device.use_document_colors();
|
let ignore_colors = !self.context.builder.device.use_document_colors();
|
||||||
|
let mut declarations_to_apply_unless_overriden =
|
||||||
|
SmallVec::<[PropertyDeclaration; 2]>::new();
|
||||||
|
|
||||||
for (declaration, origin) in declarations {
|
for (declaration, origin) in declarations {
|
||||||
let declaration_id = declaration.id();
|
let declaration_id = declaration.id();
|
||||||
|
@ -502,21 +533,25 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut declaration = self.substitute_variables_if_needed(declaration);
|
let declaration = self.substitute_variables_if_needed(declaration);
|
||||||
|
|
||||||
// When document colors are disabled, skip properties that are
|
// When document colors are disabled, do special handling of
|
||||||
// marked as ignored in that mode, unless they come from a UA or
|
// properties that are marked as ignored in that mode.
|
||||||
// user style sheet.
|
|
||||||
if ignore_colors {
|
if ignore_colors {
|
||||||
let should_ignore = should_ignore_declaration_when_ignoring_document_colors(
|
let application = application_when_ignoring_colors(
|
||||||
self.context.builder.device,
|
&self.context.builder,
|
||||||
longhand_id,
|
longhand_id,
|
||||||
origin,
|
origin,
|
||||||
self.context.builder.pseudo,
|
&declaration,
|
||||||
&mut declaration,
|
|
||||||
);
|
);
|
||||||
if should_ignore {
|
|
||||||
continue;
|
match application {
|
||||||
|
DeclarationApplication::Ignore => continue,
|
||||||
|
DeclarationApplication::Apply => {},
|
||||||
|
DeclarationApplication::ApplyUnlessOverriden(decl) => {
|
||||||
|
declarations_to_apply_unless_overriden.push(decl);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,6 +589,20 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
self.apply_declaration(longhand_id, &*declaration);
|
self.apply_declaration(longhand_id, &*declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ignore_colors {
|
||||||
|
for declaration in declarations_to_apply_unless_overriden.iter() {
|
||||||
|
let longhand_id = match declaration.id() {
|
||||||
|
PropertyDeclarationId::Longhand(id) => id,
|
||||||
|
PropertyDeclarationId::Custom(..) => unreachable!(),
|
||||||
|
};
|
||||||
|
debug_assert!(!longhand_id.is_logical());
|
||||||
|
if self.seen.contains(longhand_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.apply_declaration(longhand_id, declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if Phase::is_early() {
|
if Phase::is_early() {
|
||||||
self.fixup_font_stuff();
|
self.fixup_font_stuff();
|
||||||
self.compute_writing_mode();
|
self.compute_writing_mode();
|
||||||
|
|
|
@ -11,6 +11,7 @@ bitflags! {
|
||||||
/// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.
|
/// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.
|
||||||
/// If we ever want to add some flags that shouldn't inherit for them,
|
/// If we ever want to add some flags that shouldn't inherit for them,
|
||||||
/// we might want to add a function to handle this.
|
/// we might want to add a function to handle this.
|
||||||
|
#[repr(C)]
|
||||||
pub struct ComputedValueFlags: u16 {
|
pub struct ComputedValueFlags: u16 {
|
||||||
/// Whether the style or any of the ancestors has a text-decoration-line
|
/// Whether the style or any of the ancestors has a text-decoration-line
|
||||||
/// property that should get propagated to descendants.
|
/// property that should get propagated to descendants.
|
||||||
|
@ -63,6 +64,9 @@ bitflags! {
|
||||||
///
|
///
|
||||||
/// Only used in Servo.
|
/// Only used in Servo.
|
||||||
const CAN_BE_FRAGMENTED = 1 << 10;
|
const CAN_BE_FRAGMENTED = 1 << 10;
|
||||||
|
|
||||||
|
/// Whether this style is the style of the document element.
|
||||||
|
const IS_ROOT_ELEMENT_STYLE = 1 << 11;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +75,9 @@ impl ComputedValueFlags {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inherited_flags() -> Self {
|
fn inherited_flags() -> Self {
|
||||||
ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
|
ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
|
||||||
ComputedValueFlags::CAN_BE_FRAGMENTED |
|
ComputedValueFlags::CAN_BE_FRAGMENTED |
|
||||||
ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
|
||||||
ComputedValueFlags::HAS_TEXT_DECORATION_LINES
|
ComputedValueFlags::HAS_TEXT_DECORATION_LINES
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flags that may be propagated to descendants.
|
/// Flags that may be propagated to descendants.
|
||||||
|
@ -97,22 +101,3 @@ impl ComputedValueFlags {
|
||||||
self & Self::maybe_inherited_flags()
|
self & Self::maybe_inherited_flags()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that the relevant servo and Gecko representations match.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
#[inline]
|
|
||||||
pub fn assert_match() {
|
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
macro_rules! assert_bit {
|
|
||||||
($rust:ident, $cpp:ident) => {
|
|
||||||
debug_assert_eq!(ComputedValueFlags::$rust.bits, structs::$cpp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_bit!(HAS_TEXT_DECORATION_LINES, ComputedStyleBit_HasTextDecorationLines);
|
|
||||||
assert_bit!(IS_IN_PSEUDO_ELEMENT_SUBTREE, ComputedStyleBit_HasPseudoElementData);
|
|
||||||
assert_bit!(SHOULD_SUPPRESS_LINEBREAK, ComputedStyleBit_SuppressLineBreak);
|
|
||||||
assert_bit!(IS_TEXT_COMBINED, ComputedStyleBit_IsTextCombined);
|
|
||||||
assert_bit!(IS_RELEVANT_LINK_VISITED, ComputedStyleBit_RelevantLinkVisited);
|
|
||||||
assert_bit!(DEPENDS_ON_FONT_METRICS, ComputedStyleBit_DependsOnFontMetrics);
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
use crate::custom_properties::{CssEnvironment, CustomPropertiesBuilder};
|
use crate::custom_properties::CustomPropertiesBuilder;
|
||||||
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
|
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||||
|
@ -778,6 +778,7 @@ impl PropertyDeclarationBlock {
|
||||||
dest: &mut CssStringWriter,
|
dest: &mut CssStringWriter,
|
||||||
computed_values: Option<&ComputedValues>,
|
computed_values: Option<&ComputedValues>,
|
||||||
custom_properties_block: Option<&PropertyDeclarationBlock>,
|
custom_properties_block: Option<&PropertyDeclarationBlock>,
|
||||||
|
device: &Device,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
if let Ok(shorthand) = property.as_shorthand() {
|
if let Ok(shorthand) = property.as_shorthand() {
|
||||||
return self.shorthand_to_css(shorthand, dest);
|
return self.shorthand_to_css(shorthand, dest);
|
||||||
|
@ -790,19 +791,13 @@ impl PropertyDeclarationBlock {
|
||||||
None => return Err(fmt::Error),
|
None => return Err(fmt::Error),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(emilio): When we implement any environment variable without
|
|
||||||
// hard-coding the values we're going to need to get something
|
|
||||||
// meaningful out of here... All this code path is so terribly hacky
|
|
||||||
// ;_;.
|
|
||||||
let env = CssEnvironment;
|
|
||||||
|
|
||||||
let custom_properties = if let Some(cv) = computed_values {
|
let custom_properties = if let Some(cv) = computed_values {
|
||||||
// If there are extra custom properties for this declaration block,
|
// If there are extra custom properties for this declaration block,
|
||||||
// factor them in too.
|
// factor them in too.
|
||||||
if let Some(block) = custom_properties_block {
|
if let Some(block) = custom_properties_block {
|
||||||
// FIXME(emilio): This is not super-efficient here, and all this
|
// FIXME(emilio): This is not super-efficient here, and all this
|
||||||
// feels like a hack anyway...
|
// feels like a hack anyway...
|
||||||
block.cascade_custom_properties(cv.custom_properties(), &env)
|
block.cascade_custom_properties(cv.custom_properties(), device)
|
||||||
} else {
|
} else {
|
||||||
cv.custom_properties().cloned()
|
cv.custom_properties().cloned()
|
||||||
}
|
}
|
||||||
|
@ -825,7 +820,7 @@ impl PropertyDeclarationBlock {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties.as_ref(),
|
custom_properties.as_ref(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
&env,
|
device,
|
||||||
)
|
)
|
||||||
.to_css(dest)
|
.to_css(dest)
|
||||||
},
|
},
|
||||||
|
@ -873,7 +868,7 @@ impl PropertyDeclarationBlock {
|
||||||
) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
|
||||||
self.cascade_custom_properties(
|
self.cascade_custom_properties(
|
||||||
context.style().custom_properties(),
|
context.style().custom_properties(),
|
||||||
context.device().environment(),
|
context.device(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,9 +878,9 @@ impl PropertyDeclarationBlock {
|
||||||
fn cascade_custom_properties(
|
fn cascade_custom_properties(
|
||||||
&self,
|
&self,
|
||||||
inherited_custom_properties: Option<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
inherited_custom_properties: Option<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
||||||
environment: &CssEnvironment,
|
device: &Device,
|
||||||
) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties, environment);
|
let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties, device);
|
||||||
|
|
||||||
for declaration in self.normal_declaration_iter() {
|
for declaration in self.normal_declaration_iter() {
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use crate::computed_value_flags::*;
|
||||||
use crate::custom_properties::CustomPropertiesMap;
|
use crate::custom_properties::CustomPropertiesMap;
|
||||||
use crate::gecko_bindings::bindings;
|
use crate::gecko_bindings::bindings;
|
||||||
% for style_struct in data.style_structs:
|
% for style_struct in data.style_structs:
|
||||||
|
@ -24,28 +25,20 @@ use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
|
use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
|
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
|
use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCursorArrayLength;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCursorImageValue;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
||||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
|
use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
|
||||||
use crate::gecko_bindings::structs;
|
use crate::gecko_bindings::structs;
|
||||||
use crate::gecko_bindings::structs::nsCSSPropertyID;
|
use crate::gecko_bindings::structs::nsCSSPropertyID;
|
||||||
use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
|
use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
|
||||||
use crate::gecko_bindings::sugar::refptr::RefPtr;
|
|
||||||
use crate::gecko::values::round_border_to_device_pixels;
|
use crate::gecko::values::round_border_to_device_pixels;
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::properties::computed_value_flags::*;
|
|
||||||
use crate::properties::longhands;
|
use crate::properties::longhands;
|
||||||
use crate::rule_tree::StrongRuleNode;
|
use crate::rule_tree::StrongRuleNode;
|
||||||
use crate::selector_parser::PseudoElement;
|
use crate::selector_parser::PseudoElement;
|
||||||
use servo_arc::{Arc, RawOffsetArc, UniqueArc};
|
use servo_arc::{Arc, RawOffsetArc, UniqueArc};
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::mem::{forget, MaybeUninit};
|
use std::mem::{forget, MaybeUninit};
|
||||||
use std::{cmp, ops, ptr};
|
use std::{cmp, ops, ptr};
|
||||||
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
|
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
|
||||||
|
@ -55,8 +48,6 @@ use crate::values::computed::BorderStyle;
|
||||||
use crate::values::computed::font::FontSize;
|
use crate::values::computed::font::FontSize;
|
||||||
use crate::values::generics::column::ColumnCount;
|
use crate::values::generics::column::ColumnCount;
|
||||||
use crate::values::generics::image::ImageLayer;
|
use crate::values::generics::image::ImageLayer;
|
||||||
use crate::values::generics::transform::TransformStyle;
|
|
||||||
use crate::values::generics::url::UrlOrNone;
|
|
||||||
|
|
||||||
|
|
||||||
pub mod style_structs {
|
pub mod style_structs {
|
||||||
|
@ -959,48 +950,16 @@ fn static_assert() {
|
||||||
|
|
||||||
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
|
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
|
||||||
<%self:impl_trait style_struct_name="Position"
|
<%self:impl_trait style_struct_name="Position"
|
||||||
skip_longhands="${skip_position_longhands}
|
skip_longhands="${skip_position_longhands} grid-auto-flow">
|
||||||
align-content justify-content align-self
|
|
||||||
justify-self align-items justify-items
|
|
||||||
grid-auto-flow">
|
|
||||||
% for side in SIDES:
|
% for side in SIDES:
|
||||||
<% impl_split_style_coord(side.ident, "mOffset", side.index) %>
|
<% impl_split_style_coord(side.ident, "mOffset", side.index) %>
|
||||||
% endfor
|
% endfor
|
||||||
|
${impl_simple_type_with_conversion("grid_auto_flow")}
|
||||||
% for kind in ["align", "justify"]:
|
|
||||||
${impl_simple_type_with_conversion(kind + "_content")}
|
|
||||||
${impl_simple_type_with_conversion(kind + "_self")}
|
|
||||||
% endfor
|
|
||||||
${impl_simple_type_with_conversion("align_items")}
|
|
||||||
|
|
||||||
pub fn set_justify_items(&mut self, v: longhands::justify_items::computed_value::T) {
|
|
||||||
self.gecko.mSpecifiedJustifyItems = v.specified.into();
|
|
||||||
self.set_computed_justify_items(v.computed);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
|
pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
|
||||||
debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
|
debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
|
||||||
self.gecko.mJustifyItems = v.into();
|
self.gecko.mJustifyItems.computed = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_justify_items(&mut self, reset_style: &Self) {
|
|
||||||
self.gecko.mJustifyItems = reset_style.gecko.mJustifyItems;
|
|
||||||
self.gecko.mSpecifiedJustifyItems = reset_style.gecko.mSpecifiedJustifyItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_justify_items_from(&mut self, other: &Self) {
|
|
||||||
self.gecko.mJustifyItems = other.gecko.mJustifyItems;
|
|
||||||
self.gecko.mSpecifiedJustifyItems = other.gecko.mJustifyItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_justify_items(&self) -> longhands::justify_items::computed_value::T {
|
|
||||||
longhands::justify_items::computed_value::T {
|
|
||||||
computed: self.gecko.mJustifyItems.into(),
|
|
||||||
specified: self.gecko.mSpecifiedJustifyItems.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${impl_simple_type_with_conversion("grid_auto_flow")}
|
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
||||||
|
@ -1507,7 +1466,7 @@ fn static_assert() {
|
||||||
animation-iteration-count animation-timing-function
|
animation-iteration-count animation-timing-function
|
||||||
clear transition-duration transition-delay
|
clear transition-duration transition-delay
|
||||||
transition-timing-function transition-property
|
transition-timing-function transition-property
|
||||||
transform-style shape-outside -webkit-line-clamp""" %>
|
shape-outside -webkit-line-clamp""" %>
|
||||||
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
||||||
|
@ -1664,25 +1623,6 @@ fn static_assert() {
|
||||||
self.copy_transition_property_from(other)
|
self.copy_transition_property_from(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
|
|
||||||
pub fn set_transform_style(&mut self, v: TransformStyle) {
|
|
||||||
self.gecko.mTransformStyle = match v {
|
|
||||||
TransformStyle::Flat => structs::NS_STYLE_TRANSFORM_STYLE_FLAT as u8,
|
|
||||||
TransformStyle::Preserve3d => structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D as u8,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
|
|
||||||
pub fn clone_transform_style(&self) -> TransformStyle {
|
|
||||||
match self.gecko.mTransformStyle as u32 {
|
|
||||||
structs::NS_STYLE_TRANSFORM_STYLE_FLAT => TransformStyle::Flat,
|
|
||||||
structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D => TransformStyle::Preserve3d,
|
|
||||||
_ => panic!("illegal transform style"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${impl_simple_copy('transform_style', 'mTransformStyle')}
|
|
||||||
|
|
||||||
${impl_transition_count('property', 'Property')}
|
${impl_transition_count('property', 'Property')}
|
||||||
|
|
||||||
pub fn animations_equals(&self, other: &Self) -> bool {
|
pub fn animations_equals(&self, other: &Self) -> bool {
|
||||||
|
@ -2162,57 +2102,20 @@ fn static_assert() {
|
||||||
<% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
|
<% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<%self:impl_trait style_struct_name="List"
|
<%self:impl_trait style_struct_name="List" skip_longhands="list-style-type">
|
||||||
skip_longhands="list-style-image list-style-type">
|
|
||||||
|
|
||||||
pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
|
|
||||||
match image {
|
|
||||||
UrlOrNone::None => {
|
|
||||||
unsafe {
|
|
||||||
Gecko_SetListStyleImageNone(&mut *self.gecko);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UrlOrNone::Url(ref url) => {
|
|
||||||
unsafe {
|
|
||||||
Gecko_SetListStyleImageImageValue(&mut *self.gecko, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_list_style_image_from(&mut self, other: &Self) {
|
|
||||||
unsafe { Gecko_CopyListStyleImageFrom(&mut *self.gecko, &*other.gecko); }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_list_style_image(&mut self, other: &Self) {
|
|
||||||
self.copy_list_style_image_from(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T {
|
|
||||||
if self.gecko.mListStyleImage.mRawPtr.is_null() {
|
|
||||||
return UrlOrNone::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
|
|
||||||
UrlOrNone::Url(ComputedImageUrl::from_image_request(gecko_image_request))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
|
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToName;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToString;
|
|
||||||
use nsstring::{nsACString, nsCStr};
|
use nsstring::{nsACString, nsCStr};
|
||||||
use self::longhands::list_style_type::computed_value::T;
|
use self::longhands::list_style_type::computed_value::T;
|
||||||
match v {
|
match v {
|
||||||
T::None => unsafe {
|
T::None => unsafe {
|
||||||
Gecko_SetCounterStyleToName(&mut self.gecko.mCounterStyle,
|
bindings::Gecko_SetCounterStyleToNone(&mut self.gecko.mCounterStyle)
|
||||||
atom!("none").into_addrefed());
|
|
||||||
}
|
}
|
||||||
T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle),
|
T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle),
|
||||||
T::String(s) => unsafe {
|
T::String(s) => unsafe {
|
||||||
Gecko_SetCounterStyleToString(&mut self.gecko.mCounterStyle,
|
bindings::Gecko_SetCounterStyleToString(
|
||||||
&nsCStr::from(&s) as &nsACString)
|
&mut self.gecko.mCounterStyle,
|
||||||
|
&nsCStr::from(&s) as &nsACString,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2488,14 +2391,11 @@ clip-path
|
||||||
pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
|
pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
|
||||||
self.gecko.mCursor = v.keyword;
|
self.gecko.mCursor = v.keyword;
|
||||||
unsafe {
|
unsafe {
|
||||||
Gecko_SetCursorArrayLength(&mut *self.gecko, v.images.len());
|
bindings::Gecko_SetCursorArrayCapacity(&mut *self.gecko, v.images.len());
|
||||||
}
|
}
|
||||||
for i in 0..v.images.len() {
|
for i in 0..v.images.len() {
|
||||||
unsafe {
|
unsafe {
|
||||||
Gecko_SetCursorImageValue(
|
bindings::Gecko_AppendCursorImage(&mut *self.gecko, &v.images[i].url);
|
||||||
&mut self.gecko.mCursorImages[i],
|
|
||||||
&v.images[i].url
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match v.images[i].hotspot {
|
match v.images[i].hotspot {
|
||||||
|
@ -2528,10 +2428,7 @@ clip-path
|
||||||
let keyword = self.gecko.mCursor;
|
let keyword = self.gecko.mCursor;
|
||||||
|
|
||||||
let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| {
|
let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| {
|
||||||
let url = unsafe {
|
let url = gecko_cursor_image.mImage.clone();
|
||||||
let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap();
|
|
||||||
ComputedImageUrl::from_image_request(&gecko_image_request)
|
|
||||||
};
|
|
||||||
|
|
||||||
let hotspot =
|
let hotspot =
|
||||||
if gecko_cursor_image.mHaveHotspot {
|
if gecko_cursor_image.mHaveHotspot {
|
||||||
|
@ -2580,236 +2477,9 @@ clip-path
|
||||||
${impl_simple('column_rule_style', 'mColumnRuleStyle')}
|
${impl_simple('column_rule_style', 'mColumnRuleStyle')}
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<%self:impl_trait style_struct_name="Counters" skip_longhands="content">
|
<%self:impl_trait style_struct_name="Counters">
|
||||||
pub fn ineffective_content_property(&self) -> bool {
|
pub fn ineffective_content_property(&self) -> bool {
|
||||||
self.gecko.mContents.is_empty()
|
!self.gecko.mContent.is_items()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
|
|
||||||
use crate::values::CustomIdent;
|
|
||||||
use crate::values::generics::counters::{Content, ContentItem};
|
|
||||||
use crate::values::generics::CounterStyle;
|
|
||||||
use crate::gecko_bindings::structs::nsStyleContentData;
|
|
||||||
use crate::gecko_bindings::structs::nsStyleContentAttr;
|
|
||||||
use crate::gecko_bindings::structs::StyleContentType;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
|
|
||||||
|
|
||||||
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
|
|
||||||
fn as_utf16_and_forget(s: &str) -> *mut u16 {
|
|
||||||
use std::mem;
|
|
||||||
let mut vec = s.encode_utf16().collect::<Vec<_>>();
|
|
||||||
vec.push(0u16);
|
|
||||||
let ptr = vec.as_mut_ptr();
|
|
||||||
mem::forget(vec);
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_counter_function(
|
|
||||||
data: &mut nsStyleContentData,
|
|
||||||
content_type: StyleContentType,
|
|
||||||
name: CustomIdent,
|
|
||||||
sep: &str,
|
|
||||||
style: CounterStyle,
|
|
||||||
) {
|
|
||||||
debug_assert!(content_type == StyleContentType::Counter ||
|
|
||||||
content_type == StyleContentType::Counters);
|
|
||||||
let counter_func = unsafe {
|
|
||||||
bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
|
|
||||||
};
|
|
||||||
counter_func.mIdent.set_move(unsafe {
|
|
||||||
RefPtr::from_addrefed(name.0.into_addrefed())
|
|
||||||
});
|
|
||||||
if content_type == StyleContentType::Counters {
|
|
||||||
counter_func.mSeparator.assign_str(sep);
|
|
||||||
}
|
|
||||||
style.to_gecko_value(&mut counter_func.mCounterStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
match v {
|
|
||||||
Content::None |
|
|
||||||
Content::Normal => {
|
|
||||||
// Ensure destructors run, otherwise we could leak.
|
|
||||||
if !self.gecko.mContents.is_empty() {
|
|
||||||
unsafe {
|
|
||||||
Gecko_ClearAndResizeStyleContents(&mut *self.gecko, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Content::MozAltContent => {
|
|
||||||
unsafe {
|
|
||||||
Gecko_ClearAndResizeStyleContents(&mut *self.gecko, 1);
|
|
||||||
*self.gecko.mContents[0].mContent.mString.as_mut() = ptr::null_mut();
|
|
||||||
}
|
|
||||||
self.gecko.mContents[0].mType = StyleContentType::AltContent;
|
|
||||||
},
|
|
||||||
Content::Items(items) => {
|
|
||||||
unsafe {
|
|
||||||
Gecko_ClearAndResizeStyleContents(&mut *self.gecko,
|
|
||||||
items.len() as u32);
|
|
||||||
}
|
|
||||||
for (i, item) in items.into_vec().into_iter().enumerate() {
|
|
||||||
// NB: Gecko compares the mString value if type is not image
|
|
||||||
// or URI independently of whatever gets there. In the quote
|
|
||||||
// cases, they set it to null, so do the same here.
|
|
||||||
unsafe {
|
|
||||||
*self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut();
|
|
||||||
}
|
|
||||||
match item {
|
|
||||||
ContentItem::String(ref value) => {
|
|
||||||
self.gecko.mContents[i].mType = StyleContentType::String;
|
|
||||||
unsafe {
|
|
||||||
// NB: we share allocators, so doing this is fine.
|
|
||||||
*self.gecko.mContents[i].mContent.mString.as_mut() =
|
|
||||||
as_utf16_and_forget(&value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentItem::Attr(ref attr) => {
|
|
||||||
self.gecko.mContents[i].mType = StyleContentType::Attr;
|
|
||||||
unsafe {
|
|
||||||
// NB: we share allocators, so doing this is fine.
|
|
||||||
let maybe_ns = attr.namespace.clone();
|
|
||||||
let attr_struct = Box::new(nsStyleContentAttr {
|
|
||||||
mName: structs::RefPtr {
|
|
||||||
mRawPtr: attr.attribute.clone().into_addrefed(),
|
|
||||||
_phantom_0: PhantomData,
|
|
||||||
},
|
|
||||||
mNamespaceURL: structs::RefPtr {
|
|
||||||
mRawPtr: maybe_ns.map_or(ptr::null_mut(), |x| (x.1).0.into_addrefed()),
|
|
||||||
_phantom_0: PhantomData,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
*self.gecko.mContents[i].mContent.mAttr.as_mut() =
|
|
||||||
Box::into_raw(attr_struct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentItem::OpenQuote
|
|
||||||
=> self.gecko.mContents[i].mType = StyleContentType::OpenQuote,
|
|
||||||
ContentItem::CloseQuote
|
|
||||||
=> self.gecko.mContents[i].mType = StyleContentType::CloseQuote,
|
|
||||||
ContentItem::NoOpenQuote
|
|
||||||
=> self.gecko.mContents[i].mType = StyleContentType::NoOpenQuote,
|
|
||||||
ContentItem::NoCloseQuote
|
|
||||||
=> self.gecko.mContents[i].mType = StyleContentType::NoCloseQuote,
|
|
||||||
ContentItem::Counter(name, style) => {
|
|
||||||
set_counter_function(
|
|
||||||
&mut self.gecko.mContents[i],
|
|
||||||
StyleContentType::Counter,
|
|
||||||
name,
|
|
||||||
"",
|
|
||||||
style,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ContentItem::Counters(name, sep, style) => {
|
|
||||||
set_counter_function(
|
|
||||||
&mut self.gecko.mContents[i],
|
|
||||||
StyleContentType::Counters,
|
|
||||||
name,
|
|
||||||
&sep,
|
|
||||||
style,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ContentItem::Url(ref url) => {
|
|
||||||
unsafe {
|
|
||||||
bindings::Gecko_SetContentDataImageValue(
|
|
||||||
&mut self.gecko.mContents[i],
|
|
||||||
url,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_content_from(&mut self, other: &Self) {
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyStyleContentsFrom;
|
|
||||||
unsafe {
|
|
||||||
Gecko_CopyStyleContentsFrom(&mut *self.gecko, &*other.gecko)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_content(&mut self, other: &Self) {
|
|
||||||
self.copy_content_from(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_content(&self) -> longhands::content::computed_value::T {
|
|
||||||
use {Atom, Namespace};
|
|
||||||
use crate::gecko::conversions::string_from_chars_pointer;
|
|
||||||
use crate::gecko_bindings::structs::StyleContentType;
|
|
||||||
use crate::values::generics::counters::{Content, ContentItem};
|
|
||||||
use crate::values::{CustomIdent, Either};
|
|
||||||
use crate::values::generics::CounterStyle;
|
|
||||||
use crate::values::specified::Attr;
|
|
||||||
|
|
||||||
if self.gecko.mContents.is_empty() {
|
|
||||||
return Content::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.gecko.mContents.len() == 1 &&
|
|
||||||
self.gecko.mContents[0].mType == StyleContentType::AltContent {
|
|
||||||
return Content::MozAltContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
Content::Items(
|
|
||||||
self.gecko.mContents.iter().map(|gecko_content| {
|
|
||||||
match gecko_content.mType {
|
|
||||||
StyleContentType::OpenQuote => ContentItem::OpenQuote,
|
|
||||||
StyleContentType::CloseQuote => ContentItem::CloseQuote,
|
|
||||||
StyleContentType::NoOpenQuote => ContentItem::NoOpenQuote,
|
|
||||||
StyleContentType::NoCloseQuote => ContentItem::NoCloseQuote,
|
|
||||||
StyleContentType::String => {
|
|
||||||
let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
|
|
||||||
let string = unsafe { string_from_chars_pointer(*gecko_chars) };
|
|
||||||
ContentItem::String(string.into_boxed_str())
|
|
||||||
},
|
|
||||||
StyleContentType::Attr => {
|
|
||||||
let (namespace, attribute) = unsafe {
|
|
||||||
let s = &**gecko_content.mContent.mAttr.as_ref();
|
|
||||||
let ns = if s.mNamespaceURL.mRawPtr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// FIXME(bholley): We don't have any way to get the prefix here. :-(
|
|
||||||
let prefix = atom!("");
|
|
||||||
Some((prefix, Namespace(Atom::from_raw(s.mNamespaceURL.mRawPtr))))
|
|
||||||
};
|
|
||||||
(ns, Atom::from_raw(s.mName.mRawPtr))
|
|
||||||
};
|
|
||||||
ContentItem::Attr(Attr { namespace, attribute })
|
|
||||||
},
|
|
||||||
StyleContentType::Counter | StyleContentType::Counters => {
|
|
||||||
let gecko_function =
|
|
||||||
unsafe { &**gecko_content.mContent.mCounters.as_ref() };
|
|
||||||
let ident = CustomIdent(unsafe {
|
|
||||||
Atom::from_raw(gecko_function.mIdent.mRawPtr)
|
|
||||||
});
|
|
||||||
let style =
|
|
||||||
CounterStyle::from_gecko_value(&gecko_function.mCounterStyle);
|
|
||||||
let style = match style {
|
|
||||||
Either::First(counter_style) => counter_style,
|
|
||||||
Either::Second(_) =>
|
|
||||||
unreachable!("counter function shouldn't have single string type"),
|
|
||||||
};
|
|
||||||
if gecko_content.mType == StyleContentType::Counter {
|
|
||||||
ContentItem::Counter(ident, style)
|
|
||||||
} else {
|
|
||||||
let separator = gecko_function.mSeparator.to_string();
|
|
||||||
ContentItem::Counters(ident, separator.into_boxed_str(), style)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
StyleContentType::Image => {
|
|
||||||
unsafe {
|
|
||||||
let gecko_image_request =
|
|
||||||
&**gecko_content.mContent.mImage.as_ref();
|
|
||||||
ContentItem::Url(
|
|
||||||
ComputedImageUrl::from_image_request(gecko_image_request)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("Found unexpected value in style struct for content property"),
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>().into_boxed_slice()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
|
|
|
@ -563,6 +563,7 @@
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
Eq,
|
Eq,
|
||||||
|
FromPrimitive,
|
||||||
Hash,
|
Hash,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
Parse,
|
Parse,
|
||||||
|
@ -762,7 +763,7 @@
|
||||||
% endif
|
% endif
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
|
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
|
||||||
% if not extra_specified:
|
% if not extra_specified:
|
||||||
#[derive(Parse, SpecifiedValueInfo, ToComputedValue, ToShmem)]
|
#[derive(Parse, SpecifiedValueInfo, ToComputedValue, ToShmem)]
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -507,7 +507,7 @@ impl AnimationValue {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
context.quirks_mode,
|
context.quirks_mode,
|
||||||
context.device().environment(),
|
context.device(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
return AnimationValue::from_declaration(
|
return AnimationValue::from_declaration(
|
||||||
|
|
|
@ -47,6 +47,7 @@ ${helpers.single_keyword(
|
||||||
values="static absolute relative fixed ${'sticky' if engine in ['gecko', 'servo-2013'] else ''}"
|
values="static absolute relative fixed ${'sticky' if engine in ['gecko', 'servo-2013'] else ''}"
|
||||||
engines="gecko servo-2013 servo-2020"
|
engines="gecko servo-2013 servo-2020"
|
||||||
animation_value_type="discrete"
|
animation_value_type="discrete"
|
||||||
|
gecko_enum_prefix="StylePositionProperty"
|
||||||
flags="CREATES_STACKING_CONTEXT ABSPOS_CB"
|
flags="CREATES_STACKING_CONTEXT ABSPOS_CB"
|
||||||
spec="https://drafts.csswg.org/css-position/#position-property"
|
spec="https://drafts.csswg.org/css-position/#position-property"
|
||||||
servo_restyle_damage="rebuild_and_reflow"
|
servo_restyle_damage="rebuild_and_reflow"
|
||||||
|
@ -452,6 +453,7 @@ ${helpers.single_keyword(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
|
spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
|
gecko_enum_prefix="StyleScrollBehavior",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
@ -472,13 +474,15 @@ ${helpers.predefined_type(
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
% for axis in ["x", "y"]:
|
% for (axis, logical) in ALL_AXES:
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"overscroll-behavior-" + axis,
|
"overscroll-behavior-" + axis,
|
||||||
"OverscrollBehavior",
|
"OverscrollBehavior",
|
||||||
"computed::OverscrollBehavior::Auto",
|
"computed::OverscrollBehavior::Auto",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
needs_context=False,
|
needs_context=False,
|
||||||
|
logical_group="overscroll-behavior",
|
||||||
|
logical=logical,
|
||||||
gecko_pref="layout.css.overscroll-behavior.enabled",
|
gecko_pref="layout.css.overscroll-behavior.enabled",
|
||||||
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
|
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
|
@ -589,7 +593,7 @@ ${helpers.single_keyword(
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"transform-style",
|
"transform-style",
|
||||||
"TransformStyle",
|
"TransformStyle",
|
||||||
"computed::TransformStyle::" + ("Flat" if engine == "gecko" else "Auto"),
|
"computed::TransformStyle::Flat",
|
||||||
engines="gecko servo-2013 servo-2020",
|
engines="gecko servo-2013 servo-2020",
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
servo_2020_pref="layout.2020.unimplemented",
|
||||||
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
|
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
|
||||||
|
|
|
@ -13,7 +13,7 @@ ${helpers.predefined_type(
|
||||||
engines="gecko servo-2013 servo-2020",
|
engines="gecko servo-2013 servo-2020",
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
servo_2020_pref="layout.2020.unimplemented",
|
||||||
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
|
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
animation_value_type="NonNegativeLengthOrAuto",
|
animation_value_type="NonNegativeLengthOrAuto",
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
|
||||||
|
@ -29,7 +29,7 @@ ${helpers.predefined_type(
|
||||||
initial_specified_value="specified::ColumnCount::auto()",
|
initial_specified_value="specified::ColumnCount::auto()",
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
animation_value_type="AnimatedColumnCount",
|
animation_value_type="AnimatedColumnCount",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
|
||||||
servo_restyle_damage="rebuild_and_reflow",
|
servo_restyle_damage="rebuild_and_reflow",
|
||||||
)}
|
)}
|
||||||
|
@ -38,7 +38,7 @@ ${helpers.single_keyword(
|
||||||
"column-fill",
|
"column-fill",
|
||||||
"balance auto",
|
"balance auto",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
gecko_enum_prefix="StyleColumnFill",
|
gecko_enum_prefix="StyleColumnFill",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
|
||||||
|
@ -53,7 +53,7 @@ ${helpers.predefined_type(
|
||||||
computed_type="crate::values::computed::NonNegativeLength",
|
computed_type="crate::values::computed::NonNegativeLength",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
|
||||||
animation_value_type="NonNegativeLength",
|
animation_value_type="NonNegativeLength",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-multicol-1/#crc
|
// https://drafts.csswg.org/css-multicol-1/#crc
|
||||||
|
@ -64,22 +64,19 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
animation_value_type="AnimatedColor",
|
animation_value_type="AnimatedColor",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// FIXME: Remove enabled_in="ua" once column-span is enabled on nightly (bug 1423383).
|
|
||||||
${helpers.single_keyword(
|
${helpers.single_keyword(
|
||||||
"column-span",
|
"column-span",
|
||||||
"none all",
|
"none all",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
gecko_enum_prefix="StyleColumnSpan",
|
gecko_enum_prefix="StyleColumnSpan",
|
||||||
gecko_pref="layout.css.column-span.enabled",
|
|
||||||
enabled_in="ua",
|
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
||||||
extra_prefixes="moz:layout.css.column-span.enabled",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
@ -89,7 +86,7 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
needs_context=False,
|
needs_context=False,
|
||||||
initial_specified_value="specified::BorderStyle::None",
|
initial_specified_value="specified::BorderStyle::None",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -43,7 +43,7 @@ ${helpers.single_keyword(
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
servo_2020_pref="layout.2020.unimplemented",
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
|
spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
|
||||||
needs_conversion=True,
|
gecko_enum_prefix="StyleDirection",
|
||||||
servo_restyle_damage="rebuild_and_reflow",
|
servo_restyle_damage="rebuild_and_reflow",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ ${helpers.single_keyword(
|
||||||
extra_servo_2013_values="pixelated",
|
extra_servo_2013_values="pixelated",
|
||||||
extra_servo_2020_values="pixelated",
|
extra_servo_2020_values="pixelated",
|
||||||
gecko_aliases="-moz-crisp-edges=crisp-edges",
|
gecko_aliases="-moz-crisp-edges=crisp-edges",
|
||||||
|
gecko_enum_prefix="StyleImageRendering",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
|
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -17,6 +17,7 @@ ${helpers.single_keyword(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://www.w3.org/TR/css-inline-3/#propdef-dominant-baseline",
|
spec="https://www.w3.org/TR/css-inline-3/#propdef-dominant-baseline",
|
||||||
|
gecko_enum_prefix="StyleDominantBaseline",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword(
|
${helpers.single_keyword(
|
||||||
|
@ -118,6 +119,7 @@ ${helpers.single_keyword(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty",
|
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty",
|
||||||
|
gecko_enum_prefix = "StyleStrokeLinejoin",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
|
|
@ -20,7 +20,7 @@ ${helpers.single_keyword(
|
||||||
"empty-cells",
|
"empty-cells",
|
||||||
"show hide",
|
"show hide",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013",
|
||||||
gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
|
gecko_enum_prefix="StyleEmptyCells",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
|
spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
|
||||||
servo_restyle_damage="rebuild_and_reflow",
|
servo_restyle_damage="rebuild_and_reflow",
|
||||||
|
|
|
@ -382,8 +382,8 @@ ${helpers.single_keyword(
|
||||||
// text underline offset
|
// text underline offset
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"text-underline-offset",
|
"text-underline-offset",
|
||||||
"TextDecorationLength",
|
"LengthPercentageOrAuto",
|
||||||
"generics::text::GenericTextDecorationLength::Auto",
|
"computed::LengthPercentageOrAuto::auto()",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="ComputedValue",
|
animation_value_type="ComputedValue",
|
||||||
gecko_pref="layout.css.text-underline-offset.enabled",
|
gecko_pref="layout.css.text-underline-offset.enabled",
|
||||||
|
|
|
@ -398,7 +398,7 @@ ${helpers.predefined_type(
|
||||||
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
|
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013",
|
||||||
alias="grid-column-gap" if engine == "gecko" else "",
|
alias="grid-column-gap" if engine == "gecko" else "",
|
||||||
extra_prefixes="moz",
|
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
|
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
|
||||||
animation_value_type="NonNegativeLengthPercentageOrNormal",
|
animation_value_type="NonNegativeLengthPercentageOrNormal",
|
||||||
|
|
|
@ -69,6 +69,7 @@ ${helpers.single_keyword(
|
||||||
"mask-type",
|
"mask-type",
|
||||||
"luminance alpha",
|
"luminance alpha",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
|
gecko_enum_prefix="StyleMaskType",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
|
spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -12,6 +12,7 @@ ${helpers.single_keyword(
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013",
|
||||||
gecko_ffi_name="mLayoutStrategy",
|
gecko_ffi_name="mLayoutStrategy",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
|
gecko_enum_prefix="StyleTableLayout",
|
||||||
spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
|
spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
|
||||||
servo_restyle_damage="reflow",
|
servo_restyle_damage="reflow",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::context::QuirksMode;
|
||||||
#[cfg(feature = "servo")] use crate::computed_values;
|
#[cfg(feature = "servo")] use crate::computed_values;
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
|
use crate::computed_value_flags::*;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::properties::longhands::system_font::SystemFont;
|
use crate::properties::longhands::system_font::SystemFont;
|
||||||
|
@ -45,7 +46,6 @@ use crate::values::computed::NonNegativeLength;
|
||||||
use crate::values::serialize_atom_name;
|
use crate::values::serialize_atom_name;
|
||||||
use crate::rule_tree::StrongRuleNode;
|
use crate::rule_tree::StrongRuleNode;
|
||||||
use crate::Zero;
|
use crate::Zero;
|
||||||
use self::computed_value_flags::*;
|
|
||||||
use crate::str::{CssString, CssStringBorrow, CssStringWriter};
|
use crate::str::{CssString, CssStringBorrow, CssStringWriter};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
@ -58,8 +58,6 @@ pub use self::cascade::*;
|
||||||
import os.path
|
import os.path
|
||||||
%>
|
%>
|
||||||
|
|
||||||
#[path="${repr(os.path.join(os.path.dirname(__file__), 'computed_value_flags.rs'))[1:-1]}"]
|
|
||||||
pub mod computed_value_flags;
|
|
||||||
#[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
|
#[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
|
||||||
pub mod declaration_block;
|
pub mod declaration_block;
|
||||||
#[path="${repr(os.path.join(os.path.dirname(__file__), 'cascade.rs'))[1:-1]}"]
|
#[path="${repr(os.path.join(os.path.dirname(__file__), 'cascade.rs'))[1:-1]}"]
|
||||||
|
@ -1584,7 +1582,7 @@ impl UnparsedValue {
|
||||||
longhand_id: LonghandId,
|
longhand_id: LonghandId,
|
||||||
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
environment: &::custom_properties::CssEnvironment,
|
device: &Device,
|
||||||
) -> PropertyDeclaration {
|
) -> PropertyDeclaration {
|
||||||
let invalid_at_computed_value_time = || {
|
let invalid_at_computed_value_time = || {
|
||||||
let keyword = if longhand_id.inherited() {
|
let keyword = if longhand_id.inherited() {
|
||||||
|
@ -1602,7 +1600,7 @@ impl UnparsedValue {
|
||||||
&self.css,
|
&self.css,
|
||||||
self.first_token_type,
|
self.first_token_type,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
environment,
|
device,
|
||||||
) {
|
) {
|
||||||
Ok(css) => css,
|
Ok(css) => css,
|
||||||
Err(..) => return invalid_at_computed_value_time(),
|
Err(..) => return invalid_at_computed_value_time(),
|
||||||
|
@ -1803,8 +1801,11 @@ impl ToCss for PropertyId {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The counted unknown property list which is used for css use counters.
|
/// The counted unknown property list which is used for css use counters.
|
||||||
|
///
|
||||||
|
/// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
|
||||||
|
/// see https://bugs.llvm.org/show_bug.cgi?id=44228.
|
||||||
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u32)]
|
||||||
pub enum CountedUnknownProperty {
|
pub enum CountedUnknownProperty {
|
||||||
% for prop in data.counted_unknown_properties:
|
% for prop in data.counted_unknown_properties:
|
||||||
/// ${prop.name}
|
/// ${prop.name}
|
||||||
|
@ -2894,9 +2895,18 @@ pub struct ComputedValues {
|
||||||
/// We maintain this distinction in servo to reduce the amount of special
|
/// We maintain this distinction in servo to reduce the amount of special
|
||||||
/// casing.
|
/// casing.
|
||||||
inner: ComputedValuesInner,
|
inner: ComputedValuesInner,
|
||||||
|
|
||||||
|
/// The pseudo-element that we're using.
|
||||||
|
pseudo: Option<PseudoElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValues {
|
impl ComputedValues {
|
||||||
|
/// Returns the pseudo-element that this style represents.
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
pub fn pseudo(&self) -> Option<<&PseudoElement> {
|
||||||
|
self.pseudo.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether this style's display value is equal to contents.
|
/// Returns whether this style's display value is equal to contents.
|
||||||
pub fn is_display_contents(&self) -> bool {
|
pub fn is_display_contents(&self) -> bool {
|
||||||
self.get_box().clone_display().is_contents()
|
self.get_box().clone_display().is_contents()
|
||||||
|
@ -2999,7 +3009,7 @@ impl ComputedValues {
|
||||||
impl ComputedValues {
|
impl ComputedValues {
|
||||||
/// Create a new refcounted `ComputedValues`
|
/// Create a new refcounted `ComputedValues`
|
||||||
pub fn new(
|
pub fn new(
|
||||||
_: Option<<&PseudoElement>,
|
pseudo: Option<<&PseudoElement>,
|
||||||
custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
|
||||||
writing_mode: WritingMode,
|
writing_mode: WritingMode,
|
||||||
flags: ComputedValueFlags,
|
flags: ComputedValueFlags,
|
||||||
|
@ -3019,7 +3029,8 @@ impl ComputedValues {
|
||||||
% for style_struct in data.active_style_structs():
|
% for style_struct in data.active_style_structs():
|
||||||
${style_struct.ident},
|
${style_struct.ident},
|
||||||
% endfor
|
% endfor
|
||||||
}
|
},
|
||||||
|
pseudo: pseudo.cloned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3433,6 +3444,9 @@ pub struct StyleBuilder<'a> {
|
||||||
/// `StyleAdjuster` did any work.
|
/// `StyleAdjuster` did any work.
|
||||||
modified_reset: bool,
|
modified_reset: bool,
|
||||||
|
|
||||||
|
/// Whether this is the style for the root element.
|
||||||
|
pub is_root_element: bool,
|
||||||
|
|
||||||
/// The writing mode flags.
|
/// The writing mode flags.
|
||||||
///
|
///
|
||||||
/// TODO(emilio): Make private.
|
/// TODO(emilio): Make private.
|
||||||
|
@ -3459,6 +3473,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
pseudo: Option<<&'a PseudoElement>,
|
pseudo: Option<<&'a PseudoElement>,
|
||||||
rules: Option<StrongRuleNode>,
|
rules: Option<StrongRuleNode>,
|
||||||
custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
|
||||||
|
is_root_element: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -3480,6 +3495,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
pseudo,
|
pseudo,
|
||||||
rules,
|
rules,
|
||||||
modified_reset: false,
|
modified_reset: false,
|
||||||
|
is_root_element,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
writing_mode: inherited_style.writing_mode,
|
writing_mode: inherited_style.writing_mode,
|
||||||
flags: Cell::new(flags),
|
flags: Cell::new(flags),
|
||||||
|
@ -3518,6 +3534,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
reset_style,
|
reset_style,
|
||||||
pseudo: None,
|
pseudo: None,
|
||||||
modified_reset: false,
|
modified_reset: false,
|
||||||
|
is_root_element: false,
|
||||||
rules: None,
|
rules: None,
|
||||||
custom_properties: style_to_derive_from.custom_properties().cloned(),
|
custom_properties: style_to_derive_from.custom_properties().cloned(),
|
||||||
writing_mode: style_to_derive_from.writing_mode,
|
writing_mode: style_to_derive_from.writing_mode,
|
||||||
|
@ -3645,6 +3662,7 @@ impl<'a> StyleBuilder<'a> {
|
||||||
pseudo,
|
pseudo,
|
||||||
/* rules = */ None,
|
/* rules = */ None,
|
||||||
parent.and_then(|p| p.custom_properties().cloned()),
|
parent.and_then(|p| p.custom_properties().cloned()),
|
||||||
|
/* is_root_element = */ false,
|
||||||
);
|
);
|
||||||
ret.visited_style = visited_style;
|
ret.visited_style = visited_style;
|
||||||
ret
|
ret
|
||||||
|
@ -3827,9 +3845,9 @@ pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
mod lazy_static_module {
|
mod lazy_static_module {
|
||||||
use crate::logical_geometry::WritingMode;
|
use crate::logical_geometry::WritingMode;
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
|
use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
|
||||||
use super::computed_value_flags::ComputedValueFlags;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// The initial values for all style structs as defined by the specification.
|
/// The initial values for all style structs as defined by the specification.
|
||||||
|
@ -3859,7 +3877,8 @@ mod lazy_static_module {
|
||||||
rules: None,
|
rules: None,
|
||||||
visited_style: None,
|
visited_style: None,
|
||||||
flags: ComputedValueFlags::empty(),
|
flags: ComputedValueFlags::empty(),
|
||||||
}
|
},
|
||||||
|
pseudo: None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
sub_properties="column-width column-count"
|
sub_properties="column-width column-count"
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
derive_serialize="True"
|
derive_serialize="True"
|
||||||
extra_prefixes="moz" spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
extra_prefixes="moz:layout.css.prefixes.columns"
|
||||||
|
spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
||||||
use crate::properties::longhands::{column_count, column_width};
|
use crate::properties::longhands::{column_count, column_width};
|
||||||
|
|
||||||
pub fn parse_value<'i, 't>(
|
pub fn parse_value<'i, 't>(
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
<%helpers:shorthand
|
<%helpers:shorthand
|
||||||
name="column-rule"
|
name="column-rule"
|
||||||
engines="gecko"
|
engines="gecko"
|
||||||
extra_prefixes="moz"
|
extra_prefixes="moz:layout.css.prefixes.columns"
|
||||||
sub_properties="column-rule-width column-rule-style column-rule-color"
|
sub_properties="column-rule-width column-rule-style column-rule-color"
|
||||||
derive_serialize="True"
|
derive_serialize="True"
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
|
||||||
|
|
|
@ -136,6 +136,29 @@ impl StyleSource {
|
||||||
let _ = write!(writer, " -> {:?}", self.read(guard).declarations());
|
let _ = write!(writer, " -> {:?}", self.read(guard).declarations());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is totally unsafe, should be removed when we figure out the cause of
|
||||||
|
// bug 1607553.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
unsafe fn dump_unchecked<W: Write>(&self, writer: &mut W) {
|
||||||
|
if let Some(ref rule) = self.0.as_first() {
|
||||||
|
let rule = rule.read_unchecked();
|
||||||
|
let _ = write!(writer, "{:?}", rule.selectors);
|
||||||
|
}
|
||||||
|
let _ = write!(writer, " -> {:?}", self.read_unchecked().declarations());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is totally unsafe, should be removed when we figure out the cause of
|
||||||
|
// bug 1607553.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
unsafe fn read_unchecked(&self) -> &PropertyDeclarationBlock {
|
||||||
|
let block: &Locked<PropertyDeclarationBlock> = match self.0.borrow() {
|
||||||
|
ArcUnionBorrow::First(ref rule) => &rule.get().read_unchecked().block,
|
||||||
|
ArcUnionBorrow::Second(ref block) => block.get(),
|
||||||
|
};
|
||||||
|
block.read_unchecked()
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the style source guard, and obtain thus read access to the
|
/// Read the style source guard, and obtain thus read access to the
|
||||||
/// underlying property declaration block.
|
/// underlying property declaration block.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1437,7 +1460,6 @@ impl StrongRuleNode {
|
||||||
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING;
|
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING;
|
||||||
use crate::properties::{CSSWideKeyword, LonghandId};
|
use crate::properties::{CSSWideKeyword, LonghandId};
|
||||||
use crate::properties::{PropertyDeclaration, PropertyDeclarationId};
|
use crate::properties::{PropertyDeclaration, PropertyDeclarationId};
|
||||||
use crate::values::specified::Color;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
// Reset properties:
|
// Reset properties:
|
||||||
|
@ -1560,11 +1582,11 @@ impl StrongRuleNode {
|
||||||
|
|
||||||
if is_author {
|
if is_author {
|
||||||
if !author_colors_allowed {
|
if !author_colors_allowed {
|
||||||
// FIXME(emilio): this looks wrong, this should
|
|
||||||
// do: if color is not transparent, then return
|
|
||||||
// true, or something.
|
|
||||||
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
|
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
|
||||||
return *color == Color::transparent();
|
if color.is_transparent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1696,6 +1718,7 @@ impl Clone for StrongRuleNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for StrongRuleNode {
|
impl Drop for StrongRuleNode {
|
||||||
|
#[cfg_attr(feature = "servo", allow(unused_mut))]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let node = unsafe { &*self.ptr() };
|
let node = unsafe { &*self.ptr() };
|
||||||
|
|
||||||
|
@ -1719,7 +1742,47 @@ impl Drop for StrongRuleNode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(node.children.read().is_empty());
|
#[cfg(feature = "gecko")]
|
||||||
|
#[inline(always)]
|
||||||
|
fn assert_on_release() -> bool {
|
||||||
|
crate::gecko_bindings::structs::GECKO_IS_NIGHTLY
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
fn assert_on_release() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) || assert_on_release() {
|
||||||
|
let children = node.children.read();
|
||||||
|
if !children.is_empty() {
|
||||||
|
let mut crash_str = vec![];
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
unsafe {
|
||||||
|
// Try to unsafely collect some information of this before
|
||||||
|
// crashing the process.
|
||||||
|
if let Some(ref s) = node.source {
|
||||||
|
s.dump_unchecked(&mut crash_str);
|
||||||
|
crash_str.push(b'\n');
|
||||||
|
}
|
||||||
|
children.each(|child| {
|
||||||
|
(*child.ptr())
|
||||||
|
.source
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.dump_unchecked(&mut crash_str);
|
||||||
|
crash_str.push(b'\n');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!(
|
||||||
|
"Children left in the rule tree on drop: {}",
|
||||||
|
String::from_utf8_lossy(&crash_str).trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if node.parent.is_none() {
|
if node.parent.is_none() {
|
||||||
debug!("Dropping root node!");
|
debug!("Dropping root node!");
|
||||||
// The free list should be null by this point
|
// The free list should be null by this point
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::values::KeyframesName;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
use euclid::default::Size2D as UntypedSize2D;
|
use euclid::default::Size2D as UntypedSize2D;
|
||||||
use euclid::{Scale, Size2D};
|
use euclid::{Scale, SideOffsets2D, Size2D};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
use style_traits::{CSSPixel, DevicePixel};
|
use style_traits::{CSSPixel, DevicePixel};
|
||||||
|
@ -164,6 +164,16 @@ impl Device {
|
||||||
pub fn default_background_color(&self) -> RGBA {
|
pub fn default_background_color(&self) -> RGBA {
|
||||||
RGBA::new(255, 255, 255, 255)
|
RGBA::new(255, 255, 255, 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the default color color.
|
||||||
|
pub fn default_color(&self) -> RGBA {
|
||||||
|
RGBA::new(0, 0, 0, 255)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns safe area insets
|
||||||
|
pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
|
||||||
|
SideOffsets2D::zero()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/mediaqueries-4/#width
|
/// https://drafts.csswg.org/mediaqueries-4/#width
|
||||||
|
|
|
@ -81,7 +81,7 @@ where
|
||||||
target.pres_hints() == candidate.pres_hints()
|
target.pres_hints() == candidate.pres_hints()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a given element has the same class attribute than a given candidate.
|
/// Whether a given element has the same class attribute as a given candidate.
|
||||||
///
|
///
|
||||||
/// We don't try to share style across elements with different class attributes.
|
/// We don't try to share style across elements with different class attributes.
|
||||||
pub fn have_same_class<E>(
|
pub fn have_same_class<E>(
|
||||||
|
@ -94,6 +94,19 @@ where
|
||||||
target.class_list() == candidate.class_list()
|
target.class_list() == candidate.class_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a given element has the same part attribute as a given candidate.
|
||||||
|
///
|
||||||
|
/// We don't try to share style across elements with different part attributes.
|
||||||
|
pub fn have_same_parts<E>(
|
||||||
|
target: &mut StyleSharingTarget<E>,
|
||||||
|
candidate: &mut StyleSharingCandidate<E>,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
target.part_list() == candidate.part_list()
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether a given element and a candidate match the same set of "revalidation"
|
/// Whether a given element and a candidate match the same set of "revalidation"
|
||||||
/// selectors.
|
/// selectors.
|
||||||
///
|
///
|
||||||
|
|
|
@ -124,10 +124,16 @@ impl OpaqueComputedValues {
|
||||||
pub struct ValidationData {
|
pub struct ValidationData {
|
||||||
/// The class list of this element.
|
/// The class list of this element.
|
||||||
///
|
///
|
||||||
/// TODO(emilio): See if it's worth to sort them, or doing something else in
|
/// TODO(emilio): Maybe check whether rules for these classes apply to the
|
||||||
/// a similar fashion as what Boris is doing for the ID attribute.
|
/// element?
|
||||||
class_list: Option<SmallVec<[Atom; 5]>>,
|
class_list: Option<SmallVec<[Atom; 5]>>,
|
||||||
|
|
||||||
|
/// The part list of this element.
|
||||||
|
///
|
||||||
|
/// TODO(emilio): Maybe check whether rules with these part names apply to
|
||||||
|
/// the element?
|
||||||
|
part_list: Option<SmallVec<[Atom; 5]>>,
|
||||||
|
|
||||||
/// The list of presentational attributes of the element.
|
/// The list of presentational attributes of the element.
|
||||||
pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
|
pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
|
||||||
|
|
||||||
|
@ -161,22 +167,41 @@ impl ValidationData {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get or compute the part-list associated with this element.
|
||||||
|
pub fn part_list<E>(&mut self, element: E) -> &[Atom]
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
if !element.has_part_attr() {
|
||||||
|
return &[];
|
||||||
|
}
|
||||||
|
self.part_list.get_or_insert_with(|| {
|
||||||
|
let mut list = SmallVec::<[Atom; 5]>::new();
|
||||||
|
element.each_part(|p| list.push(p.clone()));
|
||||||
|
// See below for the reasoning.
|
||||||
|
if !list.spilled() {
|
||||||
|
list.sort_unstable_by_key(|a| a.get_hash());
|
||||||
|
}
|
||||||
|
list
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Get or compute the class-list associated with this element.
|
/// Get or compute the class-list associated with this element.
|
||||||
pub fn class_list<E>(&mut self, element: E) -> &[Atom]
|
pub fn class_list<E>(&mut self, element: E) -> &[Atom]
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
self.class_list.get_or_insert_with(|| {
|
self.class_list.get_or_insert_with(|| {
|
||||||
let mut class_list = SmallVec::<[Atom; 5]>::new();
|
let mut list = SmallVec::<[Atom; 5]>::new();
|
||||||
element.each_class(|c| class_list.push(c.clone()));
|
element.each_class(|c| list.push(c.clone()));
|
||||||
// Assuming there are a reasonable number of classes (we use the
|
// Assuming there are a reasonable number of classes (we use the
|
||||||
// inline capacity as "reasonable number"), sort them to so that
|
// inline capacity as "reasonable number"), sort them to so that
|
||||||
// we don't mistakenly reject sharing candidates when one element
|
// we don't mistakenly reject sharing candidates when one element
|
||||||
// has "foo bar" and the other has "bar foo".
|
// has "foo bar" and the other has "bar foo".
|
||||||
if !class_list.spilled() {
|
if !list.spilled() {
|
||||||
class_list.sort_by(|a, b| a.get_hash().cmp(&b.get_hash()));
|
list.sort_unstable_by_key(|a| a.get_hash());
|
||||||
}
|
}
|
||||||
class_list
|
list
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +298,11 @@ impl<E: TElement> StyleSharingCandidate<E> {
|
||||||
self.validation_data.class_list(self.element)
|
self.validation_data.class_list(self.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the part list of this candidate.
|
||||||
|
fn part_list(&mut self) -> &[Atom] {
|
||||||
|
self.validation_data.part_list(self.element)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the pres hints of this candidate.
|
/// Get the pres hints of this candidate.
|
||||||
fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
|
fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
|
||||||
self.validation_data.pres_hints(self.element)
|
self.validation_data.pres_hints(self.element)
|
||||||
|
@ -335,6 +365,10 @@ impl<E: TElement> StyleSharingTarget<E> {
|
||||||
self.validation_data.class_list(self.element)
|
self.validation_data.class_list(self.element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part_list(&mut self) -> &[Atom] {
|
||||||
|
self.validation_data.part_list(self.element)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the pres hints of this candidate.
|
/// Get the pres hints of this candidate.
|
||||||
fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
|
fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
|
||||||
self.validation_data.pres_hints(self.element)
|
self.validation_data.pres_hints(self.element)
|
||||||
|
@ -772,6 +806,11 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !checks::have_same_parts(target, candidate) {
|
||||||
|
trace!("Miss: Shadow parts");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if !checks::revalidate(
|
if !checks::revalidate(
|
||||||
target,
|
target,
|
||||||
candidate,
|
candidate,
|
||||||
|
|
|
@ -5,15 +5,13 @@
|
||||||
//! A struct to encapsulate all the style fixups and flags propagations
|
//! A struct to encapsulate all the style fixups and flags propagations
|
||||||
//! a computed style needs in order for it to adhere to the CSS spec.
|
//! a computed style needs in order for it to adhere to the CSS spec.
|
||||||
|
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::dom::TElement;
|
use crate::dom::TElement;
|
||||||
use crate::properties::computed_value_flags::ComputedValueFlags;
|
|
||||||
use crate::properties::longhands::display::computed_value::T as Display;
|
use crate::properties::longhands::display::computed_value::T as Display;
|
||||||
use crate::properties::longhands::float::computed_value::T as Float;
|
use crate::properties::longhands::float::computed_value::T as Float;
|
||||||
use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
|
use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
|
||||||
use crate::properties::longhands::position::computed_value::T as Position;
|
use crate::properties::longhands::position::computed_value::T as Position;
|
||||||
use crate::properties::{self, ComputedValues, StyleBuilder};
|
use crate::properties::{self, ComputedValues, StyleBuilder};
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
use crate::values::specified::box_::DisplayInside;
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
|
||||||
/// A struct that implements all the adjustment methods.
|
/// A struct that implements all the adjustment methods.
|
||||||
|
@ -191,8 +189,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
|
blockify_if!(self.style.is_root_element);
|
||||||
blockify_if!(is_root);
|
|
||||||
if !self.skip_item_display_fixup(element) {
|
if !self.skip_item_display_fixup(element) {
|
||||||
let parent_display = layout_parent_style.get_box().clone_display();
|
let parent_display = layout_parent_style.get_box().clone_display();
|
||||||
blockify_if!(parent_display.is_item_container());
|
blockify_if!(parent_display.is_item_container());
|
||||||
|
@ -207,7 +204,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
||||||
self.style.get_parent_list().clone_list_style_position() ==
|
self.style.get_parent_list().clone_list_style_position() ==
|
||||||
ListStylePosition::Outside &&
|
ListStylePosition::Outside &&
|
||||||
layout_parent_style.get_box().clone_display().inside() != DisplayInside::Inline
|
!layout_parent_style
|
||||||
|
.get_box()
|
||||||
|
.clone_display()
|
||||||
|
.is_inline_flow()
|
||||||
);
|
);
|
||||||
|
|
||||||
if !blockify {
|
if !blockify {
|
||||||
|
@ -215,7 +215,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let display = self.style.get_box().clone_display();
|
let display = self.style.get_box().clone_display();
|
||||||
let blockified_display = display.equivalent_block_display(is_root);
|
let blockified_display = display.equivalent_block_display(self.style.is_root_element);
|
||||||
if display != blockified_display {
|
if display != blockified_display {
|
||||||
self.style
|
self.style
|
||||||
.mutate_box()
|
.mutate_box()
|
||||||
|
@ -224,7 +224,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a few common flags for both text and element's style.
|
/// Compute a few common flags for both text and element's style.
|
||||||
pub fn set_bits(&mut self) {
|
fn set_bits(&mut self) {
|
||||||
let display = self.style.get_box().clone_display();
|
let display = self.style.get_box().clone_display();
|
||||||
|
|
||||||
if !display.is_contents() &&
|
if !display.is_contents() &&
|
||||||
|
@ -243,6 +243,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
.add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
|
.add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.style.is_root_element {
|
||||||
|
self.style
|
||||||
|
.add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo-layout-2013")]
|
#[cfg(feature = "servo-layout-2013")]
|
||||||
{
|
{
|
||||||
if self.style.get_parent_column().is_multicol() {
|
if self.style.get_parent_column().is_multicol() {
|
||||||
|
@ -259,6 +264,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
/// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
|
/// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn adjust_for_text(&mut self) {
|
pub fn adjust_for_text(&mut self) {
|
||||||
|
debug_assert!(!self.style.is_root_element);
|
||||||
self.adjust_for_text_combine_upright();
|
self.adjust_for_text_combine_upright();
|
||||||
self.adjust_for_text_in_ruby();
|
self.adjust_for_text_in_ruby();
|
||||||
self.set_bits();
|
self.set_bits();
|
||||||
|
@ -283,8 +289,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
let writing_mode = self.style.get_inherited_box().clone_writing_mode();
|
let writing_mode = self.style.get_inherited_box().clone_writing_mode();
|
||||||
let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
|
let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
|
||||||
|
|
||||||
if writing_mode != WritingMode::HorizontalTb &&
|
if matches!(
|
||||||
text_combine_upright == TextCombineUpright::All
|
writing_mode,
|
||||||
|
WritingMode::VerticalRl | WritingMode::VerticalLr
|
||||||
|
) && text_combine_upright == TextCombineUpright::All
|
||||||
{
|
{
|
||||||
self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
|
self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
|
||||||
self.style
|
self.style
|
||||||
|
@ -549,7 +557,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn should_suppress_linebreak(&self, layout_parent_style: &ComputedValues) -> bool {
|
fn should_suppress_linebreak<E>(
|
||||||
|
&self,
|
||||||
|
layout_parent_style: &ComputedValues,
|
||||||
|
element: Option<E>,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
// Line break suppression should only be propagated to in-flow children.
|
// Line break suppression should only be propagated to in-flow children.
|
||||||
if self.style.is_floating() || self.style.is_absolutely_positioned() {
|
if self.style.is_floating() || self.style.is_absolutely_positioned() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -569,11 +584,18 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
// Ruby base and text are always non-breakable.
|
// Ruby base and text are always non-breakable.
|
||||||
Display::RubyBase | Display::RubyText => true,
|
Display::RubyBase | Display::RubyText => true,
|
||||||
// Ruby base container and text container are breakable.
|
// Ruby base container and text container are breakable.
|
||||||
|
// Non-HTML elements may not form ruby base / text container because
|
||||||
|
// they may not respect ruby-internal display values, so we can't
|
||||||
|
// make them escaped from line break suppression.
|
||||||
// Note that, when certain HTML tags, e.g. form controls, have ruby
|
// Note that, when certain HTML tags, e.g. form controls, have ruby
|
||||||
// level container display type, they could also escape from the
|
// level container display type, they could also escape from the
|
||||||
// line break suppression flag while they shouldn't. However, it is
|
// line break suppression flag while they shouldn't. However, it is
|
||||||
// generally fine since they themselves are non-breakable.
|
// generally fine as far as they can't break the line inside them.
|
||||||
Display::RubyBaseContainer | Display::RubyTextContainer => false,
|
Display::RubyBaseContainer | Display::RubyTextContainer
|
||||||
|
if element.map_or(true, |e| e.is_html_element()) =>
|
||||||
|
{
|
||||||
|
false
|
||||||
|
},
|
||||||
// Anything else is non-breakable if and only if its layout parent
|
// Anything else is non-breakable if and only if its layout parent
|
||||||
// has a ruby display type, because any of the ruby boxes can be
|
// has a ruby display type, because any of the ruby boxes can be
|
||||||
// anonymous.
|
// anonymous.
|
||||||
|
@ -595,7 +617,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
|
|
||||||
let self_display = self.style.get_box().clone_display();
|
let self_display = self.style.get_box().clone_display();
|
||||||
// Check whether line break should be suppressed for this element.
|
// Check whether line break should be suppressed for this element.
|
||||||
if self.should_suppress_linebreak(layout_parent_style) {
|
if self.should_suppress_linebreak(layout_parent_style, element) {
|
||||||
self.style
|
self.style
|
||||||
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
|
.add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
|
||||||
// Inlinify the display type if allowed.
|
// Inlinify the display type if allowed.
|
||||||
|
|
|
@ -110,7 +110,7 @@ fn eager_pseudo_is_definitely_not_generated(
|
||||||
pseudo: &PseudoElement,
|
pseudo: &PseudoElement,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use crate::properties::computed_value_flags::ComputedValueFlags;
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
|
|
||||||
if !pseudo.is_before_or_after() {
|
if !pseudo.is_before_or_after() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -733,7 +733,6 @@ impl MaybeNew for ViewportConstraints {
|
||||||
|
|
||||||
let mut conditions = RuleCacheConditions::default();
|
let mut conditions = RuleCacheConditions::default();
|
||||||
let context = Context {
|
let context = Context {
|
||||||
is_root_element: false,
|
|
||||||
// Note: DEVICE-ADAPT § 5. states that relative length values are
|
// Note: DEVICE-ADAPT § 5. states that relative length values are
|
||||||
// resolved against initial values
|
// resolved against initial values
|
||||||
builder: StyleBuilder::for_inheritance(device, None, None),
|
builder: StyleBuilder::for_inheritance(device, None, None),
|
||||||
|
|
|
@ -775,7 +775,7 @@ fn note_children<E, D, F>(
|
||||||
child_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
|
child_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
|
||||||
},
|
},
|
||||||
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle => {
|
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle => {
|
||||||
use crate::properties::computed_value_flags::ComputedValueFlags;
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
if child_data
|
if child_data
|
||||||
.styles
|
.styles
|
||||||
.primary()
|
.primary()
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use super::{Animate, Procedure};
|
use super::{Animate, Procedure};
|
||||||
use crate::values::computed::length::LengthPercentage;
|
use crate::values::computed::length::LengthPercentage;
|
||||||
use crate::values::computed::Percentage;
|
use crate::values::computed::Percentage;
|
||||||
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
||||||
impl Animate for LengthPercentage {
|
impl Animate for LengthPercentage {
|
||||||
|
@ -26,10 +27,13 @@ impl Animate for LengthPercentage {
|
||||||
.animate(&other.unclamped_length(), procedure)?;
|
.animate(&other.unclamped_length(), procedure)?;
|
||||||
let percentage =
|
let percentage =
|
||||||
animate_percentage_half(self.specified_percentage(), other.specified_percentage())?;
|
animate_percentage_half(self.specified_percentage(), other.specified_percentage())?;
|
||||||
Ok(Self::with_clamping_mode(
|
|
||||||
|
// Gets clamped as needed after the animation if needed, so no need to
|
||||||
|
// specify any particular AllowedNumericType.
|
||||||
|
Ok(LengthPercentage::new_calc(
|
||||||
length,
|
length,
|
||||||
percentage,
|
percentage,
|
||||||
self.clamping_mode,
|
AllowedNumericType::All,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ pub fn animate_multiplicative_factor(
|
||||||
/// be equal or an error is returned.
|
/// be equal or an error is returned.
|
||||||
///
|
///
|
||||||
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
||||||
/// `match` arm is not generated.
|
/// `match` arm returns an error.
|
||||||
///
|
///
|
||||||
/// If the two values are not similar, an error is returned unless a fallback
|
/// If the two values are not similar, an error is returned unless a fallback
|
||||||
/// function has been specified through `#[animate(fallback)]`.
|
/// function has been specified through `#[animate(fallback)]`.
|
||||||
|
@ -424,6 +424,16 @@ impl ToAnimatedZero for i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> ToAnimatedZero for Box<T>
|
||||||
|
where
|
||||||
|
T: ToAnimatedZero,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(Box::new((**self).to_animated_zero()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ToAnimatedZero for Option<T>
|
impl<T> ToAnimatedZero for Option<T>
|
||||||
where
|
where
|
||||||
T: ToAnimatedZero,
|
T: ToAnimatedZero,
|
||||||
|
|
|
@ -1168,10 +1168,11 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
|
||||||
// However, dropping percentage makes us impossible to compute
|
// However, dropping percentage makes us impossible to compute
|
||||||
// the distance for the percentage-percentage case, but Gecko
|
// the distance for the percentage-percentage case, but Gecko
|
||||||
// uses the same formula, so it's fine for now.
|
// uses the same formula, so it's fine for now.
|
||||||
let fx = fx.length_component().px();
|
let basis = Length::new(0.);
|
||||||
let fy = fy.length_component().px();
|
let fx = fx.resolve(basis).px();
|
||||||
let tx = tx.length_component().px();
|
let fy = fy.resolve(basis).px();
|
||||||
let ty = ty.length_component().px();
|
let tx = tx.resolve(basis).px();
|
||||||
|
let ty = ty.resolve(basis).px();
|
||||||
|
|
||||||
Ok(fx.compute_squared_distance(&tx)? +
|
Ok(fx.compute_squared_distance(&tx)? +
|
||||||
fy.compute_squared_distance(&ty)? +
|
fy.compute_squared_distance(&ty)? +
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
use crate::values::computed::{Context, ToComputedValue};
|
use crate::values::computed::{Context, ToComputedValue};
|
||||||
use crate::values::specified;
|
use crate::values::specified;
|
||||||
|
|
||||||
pub use super::specified::{AlignContent, AlignItems, JustifyContent, SelfAlignment};
|
pub use super::specified::{
|
||||||
|
AlignContent, AlignItems, ContentDistribution, JustifyContent, SelfAlignment,
|
||||||
|
};
|
||||||
pub use super::specified::{AlignSelf, JustifySelf};
|
pub use super::specified::{AlignSelf, JustifySelf};
|
||||||
|
|
||||||
/// The computed value for the `justify-items` property.
|
/// The computed value for the `justify-items` property.
|
||||||
|
@ -34,7 +36,8 @@ pub use super::specified::{AlignSelf, JustifySelf};
|
||||||
///
|
///
|
||||||
/// See the discussion in https://bugzil.la/1384542.
|
/// See the discussion in https://bugzil.la/1384542.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss, ToResolvedValue)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss, ToResolvedValue)]
|
||||||
pub struct JustifyItems {
|
#[repr(C)]
|
||||||
|
pub struct ComputedJustifyItems {
|
||||||
/// The specified value for the property. Can contain the bare `legacy`
|
/// The specified value for the property. Can contain the bare `legacy`
|
||||||
/// keyword.
|
/// keyword.
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
|
@ -45,6 +48,8 @@ pub struct JustifyItems {
|
||||||
pub computed: specified::JustifyItems,
|
pub computed: specified::JustifyItems,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::ComputedJustifyItems as JustifyItems;
|
||||||
|
|
||||||
impl JustifyItems {
|
impl JustifyItems {
|
||||||
/// Returns the `legacy` value.
|
/// Returns the `legacy` value.
|
||||||
pub fn legacy() -> Self {
|
pub fn legacy() -> Self {
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub type CounterIncrement = GenericCounterIncrement<i32>;
|
||||||
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
|
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
|
||||||
|
|
||||||
/// A computed value for the `content` property.
|
/// A computed value for the `content` property.
|
||||||
pub type Content = generics::Content<ComputedImageUrl>;
|
pub type Content = generics::GenericContent<ComputedImageUrl>;
|
||||||
|
|
||||||
/// A computed content item.
|
/// A computed content item.
|
||||||
pub type ContentItem = generics::ContentItem<ComputedImageUrl>;
|
pub type ContentItem = generics::GenericContentItem<ComputedImageUrl>;
|
||||||
|
|
|
@ -4,27 +4,24 @@
|
||||||
|
|
||||||
//! `<length>` computed values, and related ones.
|
//! `<length>` computed values, and related ones.
|
||||||
|
|
||||||
use super::{Context, Number, Percentage, ToComputedValue};
|
use super::{Context, Number, ToComputedValue};
|
||||||
use crate::values::animated::ToAnimatedValue;
|
use crate::values::animated::ToAnimatedValue;
|
||||||
use crate::values::computed::NonNegativeNumber;
|
use crate::values::computed::NonNegativeNumber;
|
||||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
||||||
use crate::values::generics::length as generics;
|
use crate::values::generics::length as generics;
|
||||||
use crate::values::generics::length::{
|
use crate::values::generics::length::{
|
||||||
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
|
||||||
};
|
};
|
||||||
use crate::values::generics::NonNegative;
|
use crate::values::generics::NonNegative;
|
||||||
use crate::values::specified::length::ViewportPercentageLength;
|
use crate::values::specified::length::{AbsoluteLength, FontBaseSize};
|
||||||
use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
|
|
||||||
use crate::values::{specified, CSSFloat};
|
use crate::values::{specified, CSSFloat};
|
||||||
use crate::Zero;
|
use crate::Zero;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use ordered_float::NotNan;
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
||||||
use style_traits::values::specified::AllowedNumericType;
|
|
||||||
use style_traits::{CSSPixel, CssWriter, ToCss};
|
use style_traits::{CSSPixel, CssWriter, ToCss};
|
||||||
|
|
||||||
pub use super::image::Image;
|
pub use super::image::Image;
|
||||||
|
pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
|
||||||
pub use crate::values::specified::url::UrlOrNone;
|
pub use crate::values::specified::url::UrlOrNone;
|
||||||
pub use crate::values::specified::{Angle, BorderStyle, Time};
|
pub use crate::values::specified::{Angle, BorderStyle, Time};
|
||||||
|
|
||||||
|
@ -54,13 +51,15 @@ impl ToComputedValue for specified::NoCalcLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for specified::Length {
|
impl ToComputedValue for specified::Length {
|
||||||
type ComputedValue = CSSPixelLength;
|
type ComputedValue = Length;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
match *self {
|
match *self {
|
||||||
specified::Length::NoCalc(l) => l.to_computed_value(context),
|
specified::Length::NoCalc(l) => l.to_computed_value(context),
|
||||||
specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(),
|
specified::Length::Calc(ref calc) => {
|
||||||
|
calc.to_computed_value(context).to_length().unwrap()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,382 +69,6 @@ impl ToComputedValue for specified::Length {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `<length-percentage>` value. This can be either a `<length>`, a
|
|
||||||
/// `<percentage>`, or a combination of both via `calc()`.
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct LengthPercentage {
|
|
||||||
length: Length,
|
|
||||||
percentage: Percentage,
|
|
||||||
#[animation(constant)]
|
|
||||||
pub clamping_mode: AllowedNumericType,
|
|
||||||
/// Whether we specified a percentage or not.
|
|
||||||
#[animation(constant)]
|
|
||||||
pub has_percentage: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
|
|
||||||
// invariant that `from_computed_value(length).to_computed_value(..) == length`.
|
|
||||||
//
|
|
||||||
// Right now for e.g. a non-negative length, we set clamping_mode to `All`
|
|
||||||
// unconditionally for non-calc values, and to `NonNegative` for calc.
|
|
||||||
//
|
|
||||||
// If we determine that it's sound, from_computed_value() can generate an
|
|
||||||
// absolute length, which then would get `All` as the clamping mode.
|
|
||||||
//
|
|
||||||
// We may want to just eagerly-detect whether we can clamp in
|
|
||||||
// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
|
|
||||||
// maybe.
|
|
||||||
impl PartialEq for LengthPercentage {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.length == other.length &&
|
|
||||||
self.percentage == other.percentage &&
|
|
||||||
self.has_percentage == other.has_percentage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComputeSquaredDistance for LengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
// FIXME(nox): This looks incorrect to me, to add a distance between lengths
|
|
||||||
// with a distance between percentages.
|
|
||||||
Ok(self
|
|
||||||
.unclamped_length()
|
|
||||||
.compute_squared_distance(&other.unclamped_length())? +
|
|
||||||
self.percentage
|
|
||||||
.compute_squared_distance(&other.percentage)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LengthPercentage {
|
|
||||||
/// Returns a new `LengthPercentage`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(length: Length, percentage: Option<Percentage>) -> Self {
|
|
||||||
Self::with_clamping_mode(length, percentage, AllowedNumericType::All)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new `LengthPercentage` with zero length and some percentage.
|
|
||||||
pub fn new_percent(percentage: Percentage) -> Self {
|
|
||||||
Self::new(Length::zero(), Some(percentage))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new `LengthPercentage` with a specific clamping mode.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_clamping_mode(
|
|
||||||
length: Length,
|
|
||||||
percentage: Option<Percentage>,
|
|
||||||
clamping_mode: AllowedNumericType,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
clamping_mode,
|
|
||||||
length,
|
|
||||||
percentage: percentage.unwrap_or_default(),
|
|
||||||
has_percentage: percentage.is_some(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns this `calc()` as a `<length>`.
|
|
||||||
///
|
|
||||||
/// Panics in debug mode if a percentage is present in the expression.
|
|
||||||
#[inline]
|
|
||||||
pub fn length(&self) -> CSSPixelLength {
|
|
||||||
debug_assert!(!self.has_percentage);
|
|
||||||
self.length_component()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length component of this `calc()`
|
|
||||||
#[inline]
|
|
||||||
pub fn length_component(&self) -> CSSPixelLength {
|
|
||||||
CSSPixelLength::new(self.clamping_mode.clamp(self.length.px()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `<length>` component of this `calc()`, unclamped.
|
|
||||||
#[inline]
|
|
||||||
pub fn unclamped_length(&self) -> CSSPixelLength {
|
|
||||||
self.length
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the percentage component of this `calc()`
|
|
||||||
#[inline]
|
|
||||||
pub fn percentage_component(&self) -> Percentage {
|
|
||||||
Percentage(self.clamping_mode.clamp(self.percentage.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the percentage value as CSSFloat.
|
|
||||||
#[inline]
|
|
||||||
pub fn percentage(&self) -> CSSFloat {
|
|
||||||
self.percentage.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the specified percentage if any.
|
|
||||||
#[inline]
|
|
||||||
pub fn specified_percentage(&self) -> Option<Percentage> {
|
|
||||||
if self.has_percentage {
|
|
||||||
Some(self.percentage)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length component if this could be represented as a
|
|
||||||
/// non-calc length.
|
|
||||||
pub fn as_length(&self) -> Option<Length> {
|
|
||||||
if !self.has_percentage {
|
|
||||||
Some(self.length_component())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the percentage component if this could be represented as a
|
|
||||||
/// non-calc percentage.
|
|
||||||
pub fn as_percentage(&self) -> Option<Percentage> {
|
|
||||||
if !self.has_percentage || self.length.px() != 0. {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves the percentage.
|
|
||||||
#[inline]
|
|
||||||
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
|
||||||
let length = self.unclamped_length().0 + basis.0 * self.percentage.0;
|
|
||||||
Length::new(self.clamping_mode.clamp(length))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the computed value into used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
|
|
||||||
self.maybe_percentage_relative_to(container_len)
|
|
||||||
.map(Au::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If there are special rules for computing percentages in a value (e.g.
|
|
||||||
/// the height property), they apply whenever a calc() expression contains
|
|
||||||
/// percentages.
|
|
||||||
pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
|
|
||||||
if self.has_percentage {
|
|
||||||
return Some(self.percentage_relative_to(container_len?));
|
|
||||||
}
|
|
||||||
Some(self.length())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for LengthPercentage {
|
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl specified::CalcLengthPercentage {
|
|
||||||
/// Compute the value, zooming any absolute units by the zoom function.
|
|
||||||
fn to_computed_value_with_zoom<F>(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
zoom_fn: F,
|
|
||||||
base_size: FontBaseSize,
|
|
||||||
) -> LengthPercentage
|
|
||||||
where
|
|
||||||
F: Fn(Length) -> Length,
|
|
||||||
{
|
|
||||||
use std::f32;
|
|
||||||
let mut length = 0.;
|
|
||||||
|
|
||||||
if let Some(absolute) = self.absolute {
|
|
||||||
length += zoom_fn(absolute.to_computed_value(context)).px();
|
|
||||||
}
|
|
||||||
|
|
||||||
for val in &[
|
|
||||||
self.vw.map(ViewportPercentageLength::Vw),
|
|
||||||
self.vh.map(ViewportPercentageLength::Vh),
|
|
||||||
self.vmin.map(ViewportPercentageLength::Vmin),
|
|
||||||
self.vmax.map(ViewportPercentageLength::Vmax),
|
|
||||||
] {
|
|
||||||
if let Some(val) = *val {
|
|
||||||
let viewport_size = context.viewport_size_for_viewport_unit_resolution();
|
|
||||||
length += val.to_computed_value(viewport_size).px();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for val in &[
|
|
||||||
self.ch.map(FontRelativeLength::Ch),
|
|
||||||
self.em.map(FontRelativeLength::Em),
|
|
||||||
self.ex.map(FontRelativeLength::Ex),
|
|
||||||
self.rem.map(FontRelativeLength::Rem),
|
|
||||||
] {
|
|
||||||
if let Some(val) = *val {
|
|
||||||
length += val.to_computed_value(context, base_size).px();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LengthPercentage::with_clamping_mode(
|
|
||||||
Length::new(length.min(f32::MAX).max(f32::MIN)),
|
|
||||||
self.percentage,
|
|
||||||
self.clamping_mode,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
|
||||||
pub fn to_computed_value_zoomed(
|
|
||||||
&self,
|
|
||||||
context: &Context,
|
|
||||||
base_size: FontBaseSize,
|
|
||||||
) -> LengthPercentage {
|
|
||||||
self.to_computed_value_with_zoom(
|
|
||||||
context,
|
|
||||||
|abs| context.maybe_zoom_text(abs.into()),
|
|
||||||
base_size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute the value into pixel length as CSSFloat without context,
|
|
||||||
/// so it returns Err(()) if there is any non-absolute unit.
|
|
||||||
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
|
||||||
if self.vw.is_some() ||
|
|
||||||
self.vh.is_some() ||
|
|
||||||
self.vmin.is_some() ||
|
|
||||||
self.vmax.is_some() ||
|
|
||||||
self.em.is_some() ||
|
|
||||||
self.ex.is_some() ||
|
|
||||||
self.ch.is_some() ||
|
|
||||||
self.rem.is_some() ||
|
|
||||||
self.percentage.is_some()
|
|
||||||
{
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.absolute {
|
|
||||||
Some(abs) => Ok(abs.to_px()),
|
|
||||||
None => {
|
|
||||||
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::CalcLengthPercentage {
|
|
||||||
type ComputedValue = LengthPercentage;
|
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
|
||||||
// normal properties don't zoom, and compute em units against the current style's font-size
|
|
||||||
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &LengthPercentage) -> Self {
|
|
||||||
specified::CalcLengthPercentage {
|
|
||||||
clamping_mode: computed.clamping_mode,
|
|
||||||
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
|
||||||
percentage: computed.specified_percentage(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LengthPercentage {
|
|
||||||
/// 1px length value for SVG defaults
|
|
||||||
#[inline]
|
|
||||||
pub fn one() -> LengthPercentage {
|
|
||||||
LengthPercentage::new(Length::new(1.), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the computed value is absolute 0 or 0%.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_definitely_zero(&self) -> bool {
|
|
||||||
self.unclamped_length().px() == 0.0 && self.percentage.0 == 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSSFloat doesn't implement Hash, so does CSSPixelLength. Therefore, we
|
|
||||||
// still use Au as the hash key.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn to_hash_key(&self) -> (Au, NotNan<f32>) {
|
|
||||||
(
|
|
||||||
Au::from(self.unclamped_length()),
|
|
||||||
NotNan::new(self.percentage.0).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value.
|
|
||||||
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
|
||||||
Au::from(self.to_pixel_length(containing_length))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value as CSSPixelLength.
|
|
||||||
pub fn to_pixel_length(&self, containing_length: Au) -> Length {
|
|
||||||
self.percentage_relative_to(containing_length.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the clamped non-negative values.
|
|
||||||
#[inline]
|
|
||||||
pub fn clamp_to_non_negative(self) -> Self {
|
|
||||||
if let Some(p) = self.specified_percentage() {
|
|
||||||
// If we can eagerly clamp the percentage then just do that.
|
|
||||||
if self.length.is_zero() {
|
|
||||||
return Self::with_clamping_mode(
|
|
||||||
Length::zero(),
|
|
||||||
Some(p.clamp_to_non_negative()),
|
|
||||||
AllowedNumericType::NonNegative,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Self::with_clamping_mode(self.length, Some(p), AllowedNumericType::NonNegative);
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::with_clamping_mode(
|
|
||||||
self.length.clamp_to_non_negative(),
|
|
||||||
None,
|
|
||||||
AllowedNumericType::NonNegative,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for specified::LengthPercentage {
|
|
||||||
type ComputedValue = LengthPercentage;
|
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
|
||||||
match *self {
|
|
||||||
specified::LengthPercentage::Length(ref value) => {
|
|
||||||
LengthPercentage::new(value.to_computed_value(context), None)
|
|
||||||
},
|
|
||||||
specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),
|
|
||||||
specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_computed_value(computed: &LengthPercentage) -> Self {
|
|
||||||
if let Some(p) = computed.as_percentage() {
|
|
||||||
return specified::LengthPercentage::Percentage(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !computed.has_percentage {
|
|
||||||
return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(
|
|
||||||
&computed.length(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
specified::LengthPercentage::Calc(Box::new(ToComputedValue::from_computed_value(computed)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Zero for LengthPercentage {
|
|
||||||
fn zero() -> Self {
|
|
||||||
LengthPercentage::new(Length::zero(), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_zero(&self) -> bool {
|
|
||||||
self.is_definitely_zero()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Some boilerplate to share between negative and non-negative
|
/// Some boilerplate to share between negative and non-negative
|
||||||
/// length-percentage or auto.
|
/// length-percentage or auto.
|
||||||
macro_rules! computed_length_percentage_or_auto {
|
macro_rules! computed_length_percentage_or_auto {
|
||||||
|
@ -521,70 +144,6 @@ impl NonNegativeLengthPercentageOrAuto {
|
||||||
computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
|
computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
|
||||||
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
|
||||||
|
|
||||||
impl ToAnimatedValue for NonNegativeLengthPercentage {
|
|
||||||
type AnimatedValue = LengthPercentage;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self::AnimatedValue {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
NonNegative(animated.clamp_to_non_negative())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NonNegativeLength> for NonNegativeLengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(length: NonNegativeLength) -> Self {
|
|
||||||
NonNegative(LengthPercentage::new(length.0, None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LengthPercentage> for NonNegativeLengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(lp: LengthPercentage) -> Self {
|
|
||||||
NonNegative(lp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(emilio): This is a really generic impl which is only needed to implement
|
|
||||||
// Animated and co for Spacing<>. Get rid of this, probably?
|
|
||||||
impl From<Au> for LengthPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn from(length: Au) -> Self {
|
|
||||||
LengthPercentage::new(length.into(), None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NonNegativeLengthPercentage {
|
|
||||||
/// Returns true if the computed value is absolute 0 or 0%.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_definitely_zero(&self) -> bool {
|
|
||||||
self.0.is_definitely_zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
|
||||||
let resolved = self.0.to_used_value(containing_length);
|
|
||||||
::std::cmp::max(resolved, Au(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the computed value into used value.
|
|
||||||
#[inline]
|
|
||||||
pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
|
|
||||||
let resolved = self
|
|
||||||
.0
|
|
||||||
.maybe_to_used_value(containing_length.map(|v| v.into()))?;
|
|
||||||
Some(::std::cmp::max(resolved, Au(0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
impl MaxSize {
|
impl MaxSize {
|
||||||
/// Convert the computed value into used value.
|
/// Convert the computed value into used value.
|
||||||
|
|
793
components/style/values/computed/length_percentage.rs
Normal file
793
components/style/values/computed/length_percentage.rs
Normal file
|
@ -0,0 +1,793 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! `<length-percentage>` computed values, and related ones.
|
||||||
|
//!
|
||||||
|
//! The over-all design is a tagged pointer, with the lower bits of the pointer
|
||||||
|
//! being non-zero if it is a non-calc value.
|
||||||
|
//!
|
||||||
|
//! It is expected to take 64 bits both in x86 and x86-64. This is implemented
|
||||||
|
//! as a `union`, with 4 different variants:
|
||||||
|
//!
|
||||||
|
//! * The length and percentage variants have a { tag, f32 } (effectively)
|
||||||
|
//! layout. The tag has to overlap with the lower 2 bits of the calc variant.
|
||||||
|
//!
|
||||||
|
//! * The `calc()` variant is a { tag, pointer } in x86 (so same as the
|
||||||
|
//! others), or just a { pointer } in x86-64 (so that the two bits of the tag
|
||||||
|
//! can be obtained from the lower bits of the pointer).
|
||||||
|
//!
|
||||||
|
//! * There's a `tag` variant just to make clear when only the tag is intended
|
||||||
|
//! to be read. Note that the tag needs to be masked always by `TAG_MASK`, to
|
||||||
|
//! deal with the pointer variant in x86-64.
|
||||||
|
//!
|
||||||
|
//! The assertions in the constructor methods ensure that the tag getter matches
|
||||||
|
//! our expectations.
|
||||||
|
|
||||||
|
use super::{Context, Length, Percentage, ToComputedValue};
|
||||||
|
use crate::values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||||
|
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
use crate::values::generics::NonNegative;
|
||||||
|
use crate::values::specified::length::FontBaseSize;
|
||||||
|
use crate::values::{specified, CSSFloat};
|
||||||
|
use crate::Zero;
|
||||||
|
use app_units::Au;
|
||||||
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::{self, Write};
|
||||||
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
|
use style_traits::{CssWriter, ToCss};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LengthVariant {
|
||||||
|
tag: u8,
|
||||||
|
length: Length,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PercentageVariant {
|
||||||
|
tag: u8,
|
||||||
|
percentage: Percentage,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(emilio): cbindgen only understands the #[cfg] on the top level
|
||||||
|
// definition.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
pub struct CalcVariant {
|
||||||
|
tag: u8,
|
||||||
|
ptr: *mut CalcLengthPercentage,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
pub struct CalcVariant {
|
||||||
|
ptr: usize, // In little-endian byte order
|
||||||
|
}
|
||||||
|
|
||||||
|
// `CalcLengthPercentage` is `Send + Sync` as asserted below.
|
||||||
|
unsafe impl Send for CalcVariant {}
|
||||||
|
unsafe impl Sync for CalcVariant {}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TagVariant {
|
||||||
|
tag: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `<length-percentage>` value. This can be either a `<length>`, a
|
||||||
|
/// `<percentage>`, or a combination of both via `calc()`.
|
||||||
|
///
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
|
/// cbindgen:derive-mut-casts=true
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
|
||||||
|
///
|
||||||
|
/// The tag is stored in the lower two bits.
|
||||||
|
///
|
||||||
|
/// We need to use a struct instead of the union directly because unions with
|
||||||
|
/// Drop implementations are unstable, looks like.
|
||||||
|
///
|
||||||
|
/// Also we need the union and the variants to be `pub` (even though the member
|
||||||
|
/// is private) so that cbindgen generates it. They're not part of the public
|
||||||
|
/// API otherwise.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct LengthPercentage(LengthPercentageUnion);
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union LengthPercentageUnion {
|
||||||
|
length: LengthVariant,
|
||||||
|
percentage: PercentageVariant,
|
||||||
|
calc: CalcVariant,
|
||||||
|
tag: TagVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LengthPercentageUnion {
|
||||||
|
#[doc(hidden)] // Need to be public so that cbindgen generates it.
|
||||||
|
pub const TAG_CALC: u8 = 0;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const TAG_LENGTH: u8 = 1;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const TAG_PERCENTAGE: u8 = 2;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const TAG_MASK: u8 = 0b11;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum Tag {
|
||||||
|
Calc = LengthPercentageUnion::TAG_CALC,
|
||||||
|
Length = LengthPercentageUnion::TAG_LENGTH,
|
||||||
|
Percentage = LengthPercentageUnion::TAG_PERCENTAGE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the members should be 64 bits, even in 32-bit builds.
|
||||||
|
#[allow(unused)]
|
||||||
|
unsafe fn static_assert() {
|
||||||
|
fn assert_send_and_sync<T: Send + Sync>() {}
|
||||||
|
std::mem::transmute::<u64, LengthVariant>(0u64);
|
||||||
|
std::mem::transmute::<u64, PercentageVariant>(0u64);
|
||||||
|
std::mem::transmute::<u64, CalcVariant>(0u64);
|
||||||
|
std::mem::transmute::<u64, LengthPercentage>(0u64);
|
||||||
|
assert_send_and_sync::<LengthVariant>();
|
||||||
|
assert_send_and_sync::<PercentageVariant>();
|
||||||
|
assert_send_and_sync::<CalcLengthPercentage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LengthPercentage {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.tag() == Tag::Calc {
|
||||||
|
let _ = unsafe { Box::from_raw(self.calc_ptr()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MallocSizeOf for LengthPercentage {
|
||||||
|
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(..) | Unpacked::Percentage(..) => 0,
|
||||||
|
Unpacked::Calc(c) => unsafe { ops.malloc_size_of(c) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum Unpacked<'a> {
|
||||||
|
Calc(&'a CalcLengthPercentage),
|
||||||
|
Length(Length),
|
||||||
|
Percentage(Percentage),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unpacked `<length-percentage>` that owns the `calc()` variant, for
|
||||||
|
/// serialization purposes.
|
||||||
|
#[derive(Deserialize, PartialEq, Serialize)]
|
||||||
|
enum Serializable {
|
||||||
|
Calc(CalcLengthPercentage),
|
||||||
|
Length(Length),
|
||||||
|
Percentage(Percentage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LengthPercentage {
|
||||||
|
/// 1px length value for SVG defaults
|
||||||
|
#[inline]
|
||||||
|
pub fn one() -> Self {
|
||||||
|
Self::new_length(Length::new(1.))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a length value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_length(length: Length) -> Self {
|
||||||
|
let length = Self(LengthPercentageUnion {
|
||||||
|
length: LengthVariant {
|
||||||
|
tag: LengthPercentageUnion::TAG_LENGTH,
|
||||||
|
length,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
debug_assert_eq!(length.tag(), Tag::Length);
|
||||||
|
length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a percentage value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_percent(percentage: Percentage) -> Self {
|
||||||
|
let percent = Self(LengthPercentageUnion {
|
||||||
|
percentage: PercentageVariant {
|
||||||
|
tag: LengthPercentageUnion::TAG_PERCENTAGE,
|
||||||
|
percentage,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
debug_assert_eq!(percent.tag(), Tag::Percentage);
|
||||||
|
percent
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a `calc()` value.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_calc(
|
||||||
|
length: Length,
|
||||||
|
percentage: Option<Percentage>,
|
||||||
|
clamping_mode: AllowedNumericType,
|
||||||
|
) -> Self {
|
||||||
|
let percentage = match percentage {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return Self::new_length(Length::new(clamping_mode.clamp(length.px()))),
|
||||||
|
};
|
||||||
|
if length.is_zero() {
|
||||||
|
return Self::new_percent(Percentage(clamping_mode.clamp(percentage.0)));
|
||||||
|
}
|
||||||
|
Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
|
||||||
|
length,
|
||||||
|
percentage,
|
||||||
|
clamping_mode,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private version of new_calc() that constructs a calc() variant without
|
||||||
|
/// checking.
|
||||||
|
fn new_calc_unchecked(calc: Box<CalcLengthPercentage>) -> Self {
|
||||||
|
let ptr = Box::into_raw(calc);
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
let calc = CalcVariant {
|
||||||
|
tag: LengthPercentageUnion::TAG_CALC,
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
let calc = CalcVariant {
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
ptr: ptr as usize,
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
ptr: (ptr as usize).swap_bytes(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let calc = Self(LengthPercentageUnion { calc });
|
||||||
|
debug_assert_eq!(calc.tag(), Tag::Calc);
|
||||||
|
calc
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn tag(&self) -> Tag {
|
||||||
|
match unsafe { self.0.tag.tag & LengthPercentageUnion::TAG_MASK } {
|
||||||
|
LengthPercentageUnion::TAG_CALC => Tag::Calc,
|
||||||
|
LengthPercentageUnion::TAG_LENGTH => Tag::Length,
|
||||||
|
LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage,
|
||||||
|
_ => unreachable!("Bogus tag?"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unpack<'a>(&'a self) -> Unpacked<'a> {
|
||||||
|
unsafe {
|
||||||
|
match self.tag() {
|
||||||
|
Tag::Calc => Unpacked::Calc(&*self.calc_ptr()),
|
||||||
|
Tag::Length => Unpacked::Length(self.0.length.length),
|
||||||
|
Tag::Percentage => Unpacked::Percentage(self.0.percentage.percentage),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn calc_ptr(&self) -> *mut CalcLengthPercentage {
|
||||||
|
#[cfg(not(all(target_endian = "big", target_pointer_width = "64")))]
|
||||||
|
{
|
||||||
|
self.0.calc.ptr as *mut _
|
||||||
|
}
|
||||||
|
#[cfg(all(target_endian = "big", target_pointer_width = "64"))]
|
||||||
|
{
|
||||||
|
self.0.calc.ptr.swap_bytes() as *mut _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_serializable(&self) -> Serializable {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Calc(c) => Serializable::Calc(c.clone()),
|
||||||
|
Unpacked::Length(l) => Serializable::Length(l),
|
||||||
|
Unpacked::Percentage(p) => Serializable::Percentage(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_serializable(s: Serializable) -> Self {
|
||||||
|
match s {
|
||||||
|
Serializable::Calc(c) => Self::new_calc_unchecked(Box::new(c)),
|
||||||
|
Serializable::Length(l) => Self::new_length(l),
|
||||||
|
Serializable::Percentage(p) => Self::new_percent(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the computed value is absolute 0 or 0%.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_definitely_zero(&self) -> bool {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => l.px() == 0.0,
|
||||||
|
Unpacked::Percentage(p) => p.0 == 0.0,
|
||||||
|
Unpacked::Calc(ref c) => {
|
||||||
|
debug_assert_ne!(
|
||||||
|
c.length.px(),
|
||||||
|
0.0,
|
||||||
|
"Should've been simplified to a percentage"
|
||||||
|
);
|
||||||
|
false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `<length>` component of this `calc()`, unclamped.
|
||||||
|
#[inline]
|
||||||
|
pub fn unclamped_length(&self) -> Length {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => l,
|
||||||
|
Unpacked::Percentage(..) => Zero::zero(),
|
||||||
|
Unpacked::Calc(c) => c.unclamped_length(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns this `calc()` as a `<length>`.
|
||||||
|
///
|
||||||
|
/// Panics in debug mode if a percentage is present in the expression.
|
||||||
|
#[inline]
|
||||||
|
fn length(&self) -> Length {
|
||||||
|
debug_assert!(!self.has_percentage());
|
||||||
|
self.length_component()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `<length>` component of this `calc()`, clamped.
|
||||||
|
#[inline]
|
||||||
|
pub fn length_component(&self) -> Length {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => l,
|
||||||
|
Unpacked::Percentage(..) => Zero::zero(),
|
||||||
|
Unpacked::Calc(c) => c.length_component(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `<percentage>` component of this `calc()`, unclamped, as a
|
||||||
|
/// float.
|
||||||
|
///
|
||||||
|
/// FIXME: This are very different semantics from length(), we should
|
||||||
|
/// probably rename this.
|
||||||
|
#[inline]
|
||||||
|
pub fn percentage(&self) -> CSSFloat {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(..) => 0.,
|
||||||
|
Unpacked::Percentage(p) => p.0,
|
||||||
|
Unpacked::Calc(c) => c.percentage.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage.
|
||||||
|
#[inline]
|
||||||
|
pub fn resolve(&self, basis: Length) -> Length {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => l,
|
||||||
|
Unpacked::Percentage(p) => Length::new(basis.px() * p.0),
|
||||||
|
Unpacked::Calc(ref c) => c.resolve(basis),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage. Just an alias of resolve().
|
||||||
|
#[inline]
|
||||||
|
pub fn percentage_relative_to(&self, basis: Length) -> Length {
|
||||||
|
self.resolve(basis)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether there's any percentage in this value.
|
||||||
|
#[inline]
|
||||||
|
pub fn has_percentage(&self) -> bool {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(..) => false,
|
||||||
|
Unpacked::Percentage(..) | Unpacked::Calc(..) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a `<length>` if possible.
|
||||||
|
pub fn to_length(&self) -> Option<Length> {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => Some(l),
|
||||||
|
Unpacked::Percentage(..) | Unpacked::Calc(..) => {
|
||||||
|
debug_assert!(self.has_percentage());
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a `<percentage>` if possible.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_percentage(&self) -> Option<Percentage> {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(..) => None,
|
||||||
|
Unpacked::Percentage(p) => Some(p),
|
||||||
|
Unpacked::Calc(ref c) => {
|
||||||
|
debug_assert!(!c.length.is_zero());
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the specified percentage if any.
|
||||||
|
#[inline]
|
||||||
|
pub fn specified_percentage(&self) -> Option<Percentage> {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(..) => None,
|
||||||
|
Unpacked::Percentage(p) => Some(p),
|
||||||
|
Unpacked::Calc(ref c) => {
|
||||||
|
debug_assert!(self.has_percentage());
|
||||||
|
Some(c.percentage)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
||||||
|
Au::from(self.to_pixel_length(containing_length))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value as CSSPixelLength.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_pixel_length(&self, containing_length: Au) -> Length {
|
||||||
|
self.resolve(containing_length.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the computed value into used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
|
||||||
|
self.maybe_percentage_relative_to(container_len)
|
||||||
|
.map(Au::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If there are special rules for computing percentages in a value (e.g.
|
||||||
|
/// the height property), they apply whenever a calc() expression contains
|
||||||
|
/// percentages.
|
||||||
|
pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
|
||||||
|
if self.has_percentage() {
|
||||||
|
return Some(self.resolve(container_len?));
|
||||||
|
}
|
||||||
|
Some(self.length())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the clamped non-negative values.
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp_to_non_negative(&self) -> Self {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => Self::new_length(l.clamp_to_non_negative()),
|
||||||
|
Unpacked::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
|
||||||
|
Unpacked::Calc(c) => c.clamp_to_non_negative(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for LengthPercentage {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.unpack() == other.unpack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for LengthPercentage {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.unpack().fmt(formatter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for LengthPercentage {
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(match self.unpack() {
|
||||||
|
Unpacked::Length(l) => Self::new_length(l.to_animated_zero()?),
|
||||||
|
Unpacked::Percentage(p) => Self::new_percent(p.to_animated_zero()?),
|
||||||
|
Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.to_animated_zero()?)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for LengthPercentage {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self.unpack() {
|
||||||
|
Unpacked::Length(l) => Self::new_length(l),
|
||||||
|
Unpacked::Percentage(p) => Self::new_percent(p),
|
||||||
|
Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for specified::LengthPercentage {
|
||||||
|
type ComputedValue = LengthPercentage;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
||||||
|
match *self {
|
||||||
|
specified::LengthPercentage::Length(ref value) => {
|
||||||
|
LengthPercentage::new_length(value.to_computed_value(context))
|
||||||
|
},
|
||||||
|
specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),
|
||||||
|
specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &LengthPercentage) -> Self {
|
||||||
|
match computed.unpack() {
|
||||||
|
Unpacked::Length(ref l) => {
|
||||||
|
specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
|
||||||
|
},
|
||||||
|
Unpacked::Percentage(p) => specified::LengthPercentage::Percentage(p),
|
||||||
|
Unpacked::Calc(c) => {
|
||||||
|
// We simplify before constructing the LengthPercentage if
|
||||||
|
// needed, so this is always fine.
|
||||||
|
specified::LengthPercentage::Calc(Box::new(
|
||||||
|
specified::CalcLengthPercentage::from_computed_value(c),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for LengthPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
// A somewhat arbitrary base, it doesn't really make sense to mix
|
||||||
|
// lengths with percentages, but we can't do much better here, and this
|
||||||
|
// ensures that the distance between length-only and percentage-only
|
||||||
|
// lengths makes sense.
|
||||||
|
let basis = Length::new(100.);
|
||||||
|
self.resolve(basis)
|
||||||
|
.compute_squared_distance(&other.resolve(basis))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for LengthPercentage {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
specified::LengthPercentage::from_computed_value(self).to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zero for LengthPercentage {
|
||||||
|
fn zero() -> Self {
|
||||||
|
LengthPercentage::new_length(Length::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.is_definitely_zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for LengthPercentage {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.to_serializable().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for LengthPercentage {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self::from_serializable(Serializable::deserialize(
|
||||||
|
deserializer,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The representation of a calc() function with mixed lengths and percentages.
|
||||||
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CalcLengthPercentage {
|
||||||
|
length: Length,
|
||||||
|
|
||||||
|
percentage: Percentage,
|
||||||
|
|
||||||
|
#[animation(constant)]
|
||||||
|
clamping_mode: AllowedNumericType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CalcLengthPercentage {
|
||||||
|
/// Returns the length component of this `calc()`, clamped.
|
||||||
|
#[inline]
|
||||||
|
fn length_component(&self) -> Length {
|
||||||
|
Length::new(self.clamping_mode.clamp(self.length.px()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves the percentage.
|
||||||
|
#[inline]
|
||||||
|
pub fn resolve(&self, basis: Length) -> Length {
|
||||||
|
let length = self.length.px() + basis.px() * self.percentage.0;
|
||||||
|
Length::new(self.clamping_mode.clamp(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length, without clamping.
|
||||||
|
#[inline]
|
||||||
|
fn unclamped_length(&self) -> Length {
|
||||||
|
self.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the clamped non-negative values.
|
||||||
|
#[inline]
|
||||||
|
fn clamp_to_non_negative(&self) -> LengthPercentage {
|
||||||
|
LengthPercentage::new_calc(
|
||||||
|
self.length,
|
||||||
|
Some(self.percentage),
|
||||||
|
AllowedNumericType::NonNegative,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
|
||||||
|
// invariant that `from_computed_value(length).to_computed_value(..) == length`.
|
||||||
|
//
|
||||||
|
// Right now for e.g. a non-negative length, we set clamping_mode to `All`
|
||||||
|
// unconditionally for non-calc values, and to `NonNegative` for calc.
|
||||||
|
//
|
||||||
|
// If we determine that it's sound, from_computed_value() can generate an
|
||||||
|
// absolute length, which then would get `All` as the clamping mode.
|
||||||
|
//
|
||||||
|
// We may want to just eagerly-detect whether we can clamp in
|
||||||
|
// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
|
||||||
|
// maybe.
|
||||||
|
impl PartialEq for CalcLengthPercentage {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.length == other.length && self.percentage == other.percentage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl specified::CalcLengthPercentage {
|
||||||
|
/// Compute the value, zooming any absolute units by the zoom function.
|
||||||
|
fn to_computed_value_with_zoom<F>(
|
||||||
|
&self,
|
||||||
|
context: &Context,
|
||||||
|
zoom_fn: F,
|
||||||
|
base_size: FontBaseSize,
|
||||||
|
) -> LengthPercentage
|
||||||
|
where
|
||||||
|
F: Fn(Length) -> Length,
|
||||||
|
{
|
||||||
|
use crate::values::specified::length::{FontRelativeLength, ViewportPercentageLength};
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
|
let mut length = 0.;
|
||||||
|
|
||||||
|
if let Some(absolute) = self.absolute {
|
||||||
|
length += zoom_fn(absolute.to_computed_value(context)).px();
|
||||||
|
}
|
||||||
|
|
||||||
|
for val in &[
|
||||||
|
self.vw.map(ViewportPercentageLength::Vw),
|
||||||
|
self.vh.map(ViewportPercentageLength::Vh),
|
||||||
|
self.vmin.map(ViewportPercentageLength::Vmin),
|
||||||
|
self.vmax.map(ViewportPercentageLength::Vmax),
|
||||||
|
] {
|
||||||
|
if let Some(val) = *val {
|
||||||
|
let viewport_size = context.viewport_size_for_viewport_unit_resolution();
|
||||||
|
length += val.to_computed_value(viewport_size).px();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for val in &[
|
||||||
|
self.ch.map(FontRelativeLength::Ch),
|
||||||
|
self.em.map(FontRelativeLength::Em),
|
||||||
|
self.ex.map(FontRelativeLength::Ex),
|
||||||
|
self.rem.map(FontRelativeLength::Rem),
|
||||||
|
] {
|
||||||
|
if let Some(val) = *val {
|
||||||
|
length += val.to_computed_value(context, base_size).px();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LengthPercentage::new_calc(
|
||||||
|
Length::new(length.min(f32::MAX).max(f32::MIN)),
|
||||||
|
self.percentage,
|
||||||
|
self.clamping_mode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute font-size or line-height taking into account text-zoom if necessary.
|
||||||
|
pub fn to_computed_value_zoomed(
|
||||||
|
&self,
|
||||||
|
context: &Context,
|
||||||
|
base_size: FontBaseSize,
|
||||||
|
) -> LengthPercentage {
|
||||||
|
self.to_computed_value_with_zoom(
|
||||||
|
context,
|
||||||
|
|abs| context.maybe_zoom_text(abs.into()),
|
||||||
|
base_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the value into pixel length as CSSFloat without context,
|
||||||
|
/// so it returns Err(()) if there is any non-absolute unit.
|
||||||
|
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||||
|
if self.vw.is_some() ||
|
||||||
|
self.vh.is_some() ||
|
||||||
|
self.vmin.is_some() ||
|
||||||
|
self.vmax.is_some() ||
|
||||||
|
self.em.is_some() ||
|
||||||
|
self.ex.is_some() ||
|
||||||
|
self.ch.is_some() ||
|
||||||
|
self.rem.is_some() ||
|
||||||
|
self.percentage.is_some()
|
||||||
|
{
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.absolute {
|
||||||
|
Some(abs) => Ok(abs.to_px()),
|
||||||
|
None => {
|
||||||
|
debug_assert!(false, "Someone forgot to handle an unit here: {:?}", self);
|
||||||
|
Err(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the calc using the current font-size (and without text-zoom).
|
||||||
|
pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {
|
||||||
|
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
|
||||||
|
use crate::values::specified::length::AbsoluteLength;
|
||||||
|
|
||||||
|
specified::CalcLengthPercentage {
|
||||||
|
clamping_mode: computed.clamping_mode,
|
||||||
|
absolute: Some(AbsoluteLength::from_computed_value(&computed.length)),
|
||||||
|
percentage: Some(computed.percentage),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper of LengthPercentage, whose value must be >= 0.
|
||||||
|
pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
|
||||||
|
|
||||||
|
impl ToAnimatedValue for NonNegativeLengthPercentage {
|
||||||
|
type AnimatedValue = LengthPercentage;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self::AnimatedValue {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
NonNegative(animated.clamp_to_non_negative())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NonNegativeLengthPercentage {
|
||||||
|
/// Returns true if the computed value is absolute 0 or 0%.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_definitely_zero(&self) -> bool {
|
||||||
|
self.0.is_definitely_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_used_value(&self, containing_length: Au) -> Au {
|
||||||
|
let resolved = self.0.to_used_value(containing_length);
|
||||||
|
std::cmp::max(resolved, Au(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the computed value into used value.
|
||||||
|
#[inline]
|
||||||
|
pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
|
||||||
|
let resolved = self
|
||||||
|
.0
|
||||||
|
.maybe_to_used_value(containing_length.map(|v| v.into()))?;
|
||||||
|
Some(std::cmp::max(resolved, Au(0)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -108,6 +108,7 @@ pub mod flex;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod length;
|
pub mod length;
|
||||||
|
pub mod length_percentage;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
pub mod motion;
|
pub mod motion;
|
||||||
pub mod outline;
|
pub mod outline;
|
||||||
|
@ -125,9 +126,6 @@ pub mod url;
|
||||||
/// A `Context` is all the data a specified value could ever need to compute
|
/// A `Context` is all the data a specified value could ever need to compute
|
||||||
/// itself and be transformed to a computed value.
|
/// itself and be transformed to a computed value.
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
/// Whether the current element is the root element.
|
|
||||||
pub is_root_element: bool,
|
|
||||||
|
|
||||||
/// Values accessed through this need to be in the properties "computed
|
/// Values accessed through this need to be in the properties "computed
|
||||||
/// early": color, text-decoration, font-size, display, position, float,
|
/// early": color, text-decoration, font-size, display, position, float,
|
||||||
/// border-*-style, outline-style, font-family, writing-mode...
|
/// border-*-style, outline-style, font-family, writing-mode...
|
||||||
|
@ -186,7 +184,6 @@ impl<'a> Context<'a> {
|
||||||
let provider = get_metrics_provider_for_product();
|
let provider = get_metrics_provider_for_product();
|
||||||
|
|
||||||
let context = Context {
|
let context = Context {
|
||||||
is_root_element: false,
|
|
||||||
builder: StyleBuilder::for_inheritance(device, None, None),
|
builder: StyleBuilder::for_inheritance(device, None, None),
|
||||||
font_metrics_provider: &provider,
|
font_metrics_provider: &provider,
|
||||||
cached_system_font: None,
|
cached_system_font: None,
|
||||||
|
@ -200,11 +197,6 @@ impl<'a> Context<'a> {
|
||||||
f(&context)
|
f(&context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the current element is the root element.
|
|
||||||
pub fn is_root_element(&self) -> bool {
|
|
||||||
self.is_root_element
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The current device.
|
/// The current device.
|
||||||
pub fn device(&self) -> &Device {
|
pub fn device(&self) -> &Device {
|
||||||
self.builder.device
|
self.builder.device
|
||||||
|
@ -471,6 +463,53 @@ trivial_to_computed_value!(Atom);
|
||||||
trivial_to_computed_value!(Prefix);
|
trivial_to_computed_value!(Prefix);
|
||||||
trivial_to_computed_value!(String);
|
trivial_to_computed_value!(String);
|
||||||
trivial_to_computed_value!(Box<str>);
|
trivial_to_computed_value!(Box<str>);
|
||||||
|
trivial_to_computed_value!(crate::OwnedStr);
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(
|
||||||
|
Animate,
|
||||||
|
Clone,
|
||||||
|
ComputeSquaredDistance,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
ToAnimatedZero,
|
||||||
|
ToCss,
|
||||||
|
ToResolvedValue,
|
||||||
|
)]
|
||||||
|
#[repr(C, u8)]
|
||||||
|
pub enum AngleOrPercentage {
|
||||||
|
Percentage(Percentage),
|
||||||
|
Angle(Angle),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for specified::AngleOrPercentage {
|
||||||
|
type ComputedValue = AngleOrPercentage;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {
|
||||||
|
match *self {
|
||||||
|
specified::AngleOrPercentage::Percentage(percentage) => {
|
||||||
|
AngleOrPercentage::Percentage(percentage.to_computed_value(context))
|
||||||
|
},
|
||||||
|
specified::AngleOrPercentage::Angle(angle) => {
|
||||||
|
AngleOrPercentage::Angle(angle.to_computed_value(context))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn from_computed_value(computed: &AngleOrPercentage) -> Self {
|
||||||
|
match *computed {
|
||||||
|
AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(
|
||||||
|
ToComputedValue::from_computed_value(&percentage),
|
||||||
|
),
|
||||||
|
AngleOrPercentage::Angle(angle) => {
|
||||||
|
specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A `<number>` value.
|
/// A `<number>` value.
|
||||||
pub type Number = CSSFloat;
|
pub type Number = CSSFloat;
|
||||||
|
|
|
@ -23,8 +23,10 @@ fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool {
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToCss,
|
ToCss,
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
|
|
|
@ -27,8 +27,8 @@ pub use crate::values::specified::{TextDecorationSkipInk, TextTransform};
|
||||||
/// A computed value for the `initial-letter` property.
|
/// A computed value for the `initial-letter` property.
|
||||||
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
|
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
|
||||||
|
|
||||||
/// Implements type for `text-underline-offset` and `text-decoration-thickness` properties
|
/// Implements type for `text-decoration-thickness` property.
|
||||||
pub type TextDecorationLength = GenericTextDecorationLength<Length>;
|
pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
|
||||||
|
|
||||||
/// A computed value for the `letter-spacing` property.
|
/// A computed value for the `letter-spacing` property.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::ops::Add;
|
||||||
/// on each fields of the values.
|
/// on each fields of the values.
|
||||||
///
|
///
|
||||||
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
/// If a variant is annotated with `#[animation(error)]`, the corresponding
|
||||||
/// `match` arm is not generated.
|
/// `match` arm returns an error.
|
||||||
///
|
///
|
||||||
/// If the two values are not similar, an error is returned unless a fallback
|
/// If the two values are not similar, an error is returned unless a fallback
|
||||||
/// function has been specified through `#[distance(fallback)]`.
|
/// function has been specified through `#[distance(fallback)]`.
|
||||||
|
|
|
@ -145,30 +145,27 @@ fn is_decimal(counter_type: &CounterStyleType) -> bool {
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-content/#propdef-content
|
/// https://drafts.csswg.org/css-content/#propdef-content
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem,
|
||||||
Debug,
|
|
||||||
Eq,
|
|
||||||
MallocSizeOf,
|
|
||||||
PartialEq,
|
|
||||||
SpecifiedValueInfo,
|
|
||||||
ToComputedValue,
|
|
||||||
ToCss,
|
|
||||||
ToResolvedValue,
|
|
||||||
ToShmem,
|
|
||||||
)]
|
)]
|
||||||
pub enum Content<ImageUrl> {
|
#[repr(u8)]
|
||||||
|
pub enum GenericContent<ImageUrl> {
|
||||||
/// `normal` reserved keyword.
|
/// `normal` reserved keyword.
|
||||||
Normal,
|
Normal,
|
||||||
/// `none` reserved keyword.
|
/// `none` reserved keyword.
|
||||||
None,
|
None,
|
||||||
/// `-moz-alt-content`.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
MozAltContent,
|
|
||||||
/// Content items.
|
/// Content items.
|
||||||
Items(#[css(iterable)] Box<[ContentItem<ImageUrl>]>),
|
Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<ImageUrl>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::GenericContent as Content;
|
||||||
|
|
||||||
impl<ImageUrl> Content<ImageUrl> {
|
impl<ImageUrl> Content<ImageUrl> {
|
||||||
|
/// Whether `self` represents list of items.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_items(&self) -> bool {
|
||||||
|
matches!(*self, Self::Items(..))
|
||||||
|
}
|
||||||
|
|
||||||
/// Set `content` property to `normal`.
|
/// Set `content` property to `normal`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn normal() -> Self {
|
pub fn normal() -> Self {
|
||||||
|
@ -189,9 +186,10 @@ impl<ImageUrl> Content<ImageUrl> {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
pub enum ContentItem<ImageUrl> {
|
#[repr(u8)]
|
||||||
|
pub enum GenericContentItem<ImageUrl> {
|
||||||
/// Literal string content.
|
/// Literal string content.
|
||||||
String(Box<str>),
|
String(crate::OwnedStr),
|
||||||
/// `counter(name, style)`.
|
/// `counter(name, style)`.
|
||||||
#[css(comma, function)]
|
#[css(comma, function)]
|
||||||
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
|
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
|
||||||
|
@ -199,7 +197,7 @@ pub enum ContentItem<ImageUrl> {
|
||||||
#[css(comma, function)]
|
#[css(comma, function)]
|
||||||
Counters(
|
Counters(
|
||||||
CustomIdent,
|
CustomIdent,
|
||||||
Box<str>,
|
crate::OwnedStr,
|
||||||
#[css(skip_if = "is_decimal")] CounterStyleType,
|
#[css(skip_if = "is_decimal")] CounterStyleType,
|
||||||
),
|
),
|
||||||
/// `open-quote`.
|
/// `open-quote`.
|
||||||
|
@ -210,9 +208,14 @@ pub enum ContentItem<ImageUrl> {
|
||||||
NoOpenQuote,
|
NoOpenQuote,
|
||||||
/// `no-close-quote`.
|
/// `no-close-quote`.
|
||||||
NoCloseQuote,
|
NoCloseQuote,
|
||||||
|
/// `-moz-alt-content`.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
MozAltContent,
|
||||||
/// `attr([namespace? `|`]? ident)`
|
/// `attr([namespace? `|`]? ident)`
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Attr(Attr),
|
Attr(Attr),
|
||||||
/// `url(url)`
|
/// `url(url)`
|
||||||
Url(ImageUrl),
|
Url(ImageUrl),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::GenericContentItem as ContentItem;
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub mod transform;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod url;
|
pub mod url;
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
|
/// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
#[derive(
|
#[derive(
|
||||||
|
@ -55,6 +55,7 @@ pub mod url;
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum SymbolsType {
|
pub enum SymbolsType {
|
||||||
Cyclic,
|
Cyclic,
|
||||||
Numeric,
|
Numeric,
|
||||||
|
@ -63,39 +64,12 @@ pub enum SymbolsType {
|
||||||
Fixed,
|
Fixed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl SymbolsType {
|
|
||||||
/// Convert symbols type to their corresponding Gecko values.
|
|
||||||
pub fn to_gecko_keyword(self) -> u8 {
|
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
match self {
|
|
||||||
SymbolsType::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as u8,
|
|
||||||
SymbolsType::Numeric => structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as u8,
|
|
||||||
SymbolsType::Alphabetic => structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as u8,
|
|
||||||
SymbolsType::Symbolic => structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as u8,
|
|
||||||
SymbolsType::Fixed => structs::NS_STYLE_COUNTER_SYSTEM_FIXED as u8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert Gecko value to symbol type.
|
|
||||||
pub fn from_gecko_keyword(gecko_value: u32) -> SymbolsType {
|
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
match gecko_value {
|
|
||||||
structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC => SymbolsType::Cyclic,
|
|
||||||
structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC => SymbolsType::Numeric,
|
|
||||||
structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC => SymbolsType::Alphabetic,
|
|
||||||
structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC => SymbolsType::Symbolic,
|
|
||||||
structs::NS_STYLE_COUNTER_SYSTEM_FIXED => SymbolsType::Fixed,
|
|
||||||
x => panic!("Unexpected value for symbol type {}", x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
|
/// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
|
||||||
///
|
///
|
||||||
/// Note that 'none' is not a valid name.
|
/// Note that 'none' is not a valid name.
|
||||||
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
|
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum CounterStyle {
|
pub enum CounterStyle {
|
||||||
/// `<counter-style-name>`
|
/// `<counter-style-name>`
|
||||||
Name(CustomIdent),
|
Name(CustomIdent),
|
||||||
|
|
|
@ -73,13 +73,16 @@ pub struct RayFunction<Angle> {
|
||||||
/// The offset-path value.
|
/// The offset-path value.
|
||||||
///
|
///
|
||||||
/// https://drafts.fxtf.org/motion-1/#offset-path-property
|
/// https://drafts.fxtf.org/motion-1/#offset-path-property
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
#[derive(
|
#[derive(
|
||||||
Animate,
|
Animate,
|
||||||
Clone,
|
Clone,
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedValue,
|
ToAnimatedValue,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
|
@ -44,15 +46,18 @@ impl<H, V> Position<H, V> {
|
||||||
/// A generic type for representing an `Auto | <position>`.
|
/// A generic type for representing an `Auto | <position>`.
|
||||||
/// This is used by <offset-anchor> for now.
|
/// This is used by <offset-anchor> for now.
|
||||||
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
|
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
#[derive(
|
#[derive(
|
||||||
Animate,
|
Animate,
|
||||||
Clone,
|
Clone,
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
Parse,
|
Parse,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
|
|
@ -123,8 +123,8 @@ impl<N, L> LineHeight<N, L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements type for text-underline-offset and text-decoration-thickness
|
/// Implements type for text-decoration-thickness
|
||||||
/// which take the grammar of auto | from-font | <length>
|
/// which takes the grammar of auto | from-font | <length> | <percentage>
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-text-decor-4/
|
/// https://drafts.csswg.org/css-text-decor-4/
|
||||||
#[repr(C, u8)]
|
#[repr(C, u8)]
|
||||||
|
@ -148,7 +148,7 @@ impl<N, L> LineHeight<N, L> {
|
||||||
)]
|
)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum GenericTextDecorationLength<L> {
|
pub enum GenericTextDecorationLength<L> {
|
||||||
Length(L),
|
LengthPercentage(L),
|
||||||
Auto,
|
Auto,
|
||||||
FromFont,
|
FromFont,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ use style_traits::{CssWriter, ToCss};
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToCss,
|
ToCss,
|
||||||
|
@ -51,8 +53,10 @@ pub use self::GenericMatrix as Matrix;
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToCss,
|
ToCss,
|
||||||
|
@ -141,8 +145,10 @@ fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToCss,
|
ToCss,
|
||||||
|
@ -267,8 +273,10 @@ pub use self::GenericTransformOperation as TransformOperation;
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToCss,
|
ToCss,
|
||||||
|
@ -369,7 +377,7 @@ impl ToAbsoluteLength for ComputedLengthPercentage {
|
||||||
// distance without any layout info.
|
// distance without any layout info.
|
||||||
//
|
//
|
||||||
// FIXME(emilio): This looks wrong.
|
// FIXME(emilio): This looks wrong.
|
||||||
None => Ok(self.length_component().px()),
|
None => Ok(self.resolve(Zero::zero()).px()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,8 +619,10 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
@ -623,6 +633,7 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
|
||||||
/// A value of the `Rotate` property
|
/// A value of the `Rotate` property
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
pub enum GenericRotate<Number, Angle> {
|
pub enum GenericRotate<Number, Angle> {
|
||||||
/// 'none'
|
/// 'none'
|
||||||
None,
|
None,
|
||||||
|
@ -685,8 +696,10 @@ where
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
@ -697,6 +710,7 @@ where
|
||||||
/// A value of the `Scale` property
|
/// A value of the `Scale` property
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
pub enum GenericScale<Number> {
|
pub enum GenericScale<Number> {
|
||||||
/// 'none'
|
/// 'none'
|
||||||
None,
|
None,
|
||||||
|
@ -749,8 +763,10 @@ fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero, Length: Zero>(
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
@ -772,6 +788,7 @@ fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero, Length: Zero>(
|
||||||
/// https://github.com/w3c/csswg-drafts/issues/3305
|
/// https://github.com/w3c/csswg-drafts/issues/3305
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
||||||
|
/// cbindgen:private-default-tagged-enum-constructor=false
|
||||||
pub enum GenericTranslate<LengthPercentage, Length>
|
pub enum GenericTranslate<LengthPercentage, Length>
|
||||||
where
|
where
|
||||||
LengthPercentage: Zero,
|
LengthPercentage: Zero,
|
||||||
|
@ -803,9 +820,8 @@ pub use self::GenericTranslate as Translate;
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum TransformStyle {
|
pub enum TransformStyle {
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
Auto,
|
|
||||||
Flat,
|
Flat,
|
||||||
#[css(keyword = "preserve-3d")]
|
#[css(keyword = "preserve-3d")]
|
||||||
Preserve3d,
|
Preserve3d,
|
||||||
|
|
47
components/style/values/resolved/counters.rs
Normal file
47
components/style/values/resolved/counters.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! Resolved values for counter properties
|
||||||
|
|
||||||
|
use super::{Context, ToResolvedValue};
|
||||||
|
use crate::values::computed;
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-content/#content-property
|
||||||
|
///
|
||||||
|
/// We implement this at resolved value time because otherwise it causes us to
|
||||||
|
/// allocate a bunch of useless initial structs for ::before / ::after, which is
|
||||||
|
/// a bit unfortunate.
|
||||||
|
///
|
||||||
|
/// Though these should be temporary, mostly, so if this causes complexity in
|
||||||
|
/// other places, it should be fine to move to `StyleAdjuster`.
|
||||||
|
///
|
||||||
|
/// See https://github.com/w3c/csswg-drafts/issues/4632 for where some related
|
||||||
|
/// issues are being discussed.
|
||||||
|
impl ToResolvedValue for computed::Content {
|
||||||
|
type ResolvedValue = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_resolved_value(self, context: &Context) -> Self {
|
||||||
|
let is_before_or_after = context
|
||||||
|
.style
|
||||||
|
.pseudo()
|
||||||
|
.map_or(false, |p| p.is_before_or_after());
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Normal if is_before_or_after => Self::None,
|
||||||
|
// For now, make `content: none` compute to `normal` on other
|
||||||
|
// elements, as we don't respect it.
|
||||||
|
//
|
||||||
|
// FIXME(emilio, bug 1605473): for marker this should be preserved
|
||||||
|
// and respected, probably.
|
||||||
|
Self::None if !is_before_or_after => Self::Normal,
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_resolved_value(resolved: Self) -> Self {
|
||||||
|
resolved
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ use cssparser;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
mod color;
|
mod color;
|
||||||
|
mod counters;
|
||||||
|
|
||||||
use crate::values::computed;
|
use crate::values::computed;
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ trivial_to_resolved_value!(u32);
|
||||||
trivial_to_resolved_value!(usize);
|
trivial_to_resolved_value!(usize);
|
||||||
trivial_to_resolved_value!(String);
|
trivial_to_resolved_value!(String);
|
||||||
trivial_to_resolved_value!(Box<str>);
|
trivial_to_resolved_value!(Box<str>);
|
||||||
|
trivial_to_resolved_value!(crate::OwnedStr);
|
||||||
trivial_to_resolved_value!(cssparser::RGBA);
|
trivial_to_resolved_value!(cssparser::RGBA);
|
||||||
trivial_to_resolved_value!(crate::Atom);
|
trivial_to_resolved_value!(crate::Atom);
|
||||||
trivial_to_resolved_value!(app_units::Au);
|
trivial_to_resolved_value!(app_units::Au);
|
||||||
|
@ -76,6 +78,7 @@ trivial_to_resolved_value!(computed::url::ComputedUrl);
|
||||||
trivial_to_resolved_value!(computed::url::ComputedImageUrl);
|
trivial_to_resolved_value!(computed::url::ComputedImageUrl);
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
trivial_to_resolved_value!(html5ever::Prefix);
|
trivial_to_resolved_value!(html5ever::Prefix);
|
||||||
|
trivial_to_resolved_value!(computed::LengthPercentage);
|
||||||
|
|
||||||
impl<A, B> ToResolvedValue for (A, B)
|
impl<A, B> ToResolvedValue for (A, B)
|
||||||
where
|
where
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
//!
|
//!
|
||||||
//! https://drafts.csswg.org/css-align/
|
//! https://drafts.csswg.org/css-align/
|
||||||
|
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
@ -14,56 +13,55 @@ use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo,
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Constants shared by multiple CSS Box Alignment properties
|
/// Constants shared by multiple CSS Box Alignment properties
|
||||||
///
|
|
||||||
/// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
|
|
||||||
#[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
|
#[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct AlignFlags: u8 {
|
pub struct AlignFlags: u8 {
|
||||||
// Enumeration stored in the lower 5 bits:
|
// Enumeration stored in the lower 5 bits:
|
||||||
/// 'auto'
|
/// {align,justify}-{content,items,self}: 'auto'
|
||||||
const AUTO = structs::NS_STYLE_ALIGN_AUTO as u8;
|
const AUTO = 0;
|
||||||
/// 'normal'
|
/// 'normal'
|
||||||
const NORMAL = structs::NS_STYLE_ALIGN_NORMAL as u8;
|
const NORMAL = 1;
|
||||||
/// 'start'
|
/// 'start'
|
||||||
const START = structs::NS_STYLE_ALIGN_START as u8;
|
const START = 2;
|
||||||
/// 'end'
|
/// 'end'
|
||||||
const END = structs::NS_STYLE_ALIGN_END as u8;
|
const END = 3;
|
||||||
/// 'flex-start'
|
/// 'flex-start'
|
||||||
const FLEX_START = structs::NS_STYLE_ALIGN_FLEX_START as u8;
|
const FLEX_START = 4;
|
||||||
/// 'flex-end'
|
/// 'flex-end'
|
||||||
const FLEX_END = structs::NS_STYLE_ALIGN_FLEX_END as u8;
|
const FLEX_END = 5;
|
||||||
/// 'center'
|
/// 'center'
|
||||||
const CENTER = structs::NS_STYLE_ALIGN_CENTER as u8;
|
const CENTER = 6;
|
||||||
/// 'left'
|
/// 'left'
|
||||||
const LEFT = structs::NS_STYLE_ALIGN_LEFT as u8;
|
const LEFT = 7;
|
||||||
/// 'right'
|
/// 'right'
|
||||||
const RIGHT = structs::NS_STYLE_ALIGN_RIGHT as u8;
|
const RIGHT = 8;
|
||||||
/// 'baseline'
|
/// 'baseline'
|
||||||
const BASELINE = structs::NS_STYLE_ALIGN_BASELINE as u8;
|
const BASELINE = 9;
|
||||||
/// 'last-baseline'
|
/// 'last-baseline'
|
||||||
const LAST_BASELINE = structs::NS_STYLE_ALIGN_LAST_BASELINE as u8;
|
const LAST_BASELINE = 10;
|
||||||
/// 'stretch'
|
/// 'stretch'
|
||||||
const STRETCH = structs::NS_STYLE_ALIGN_STRETCH as u8;
|
const STRETCH = 11;
|
||||||
/// 'self-start'
|
/// 'self-start'
|
||||||
const SELF_START = structs::NS_STYLE_ALIGN_SELF_START as u8;
|
const SELF_START = 12;
|
||||||
/// 'self-end'
|
/// 'self-end'
|
||||||
const SELF_END = structs::NS_STYLE_ALIGN_SELF_END as u8;
|
const SELF_END = 13;
|
||||||
/// 'space-between'
|
/// 'space-between'
|
||||||
const SPACE_BETWEEN = structs::NS_STYLE_ALIGN_SPACE_BETWEEN as u8;
|
const SPACE_BETWEEN = 14;
|
||||||
/// 'space-around'
|
/// 'space-around'
|
||||||
const SPACE_AROUND = structs::NS_STYLE_ALIGN_SPACE_AROUND as u8;
|
const SPACE_AROUND = 15;
|
||||||
/// 'space-evenly'
|
/// 'space-evenly'
|
||||||
const SPACE_EVENLY = structs::NS_STYLE_ALIGN_SPACE_EVENLY as u8;
|
const SPACE_EVENLY = 16;
|
||||||
|
|
||||||
// Additional flags stored in the upper bits:
|
// Additional flags stored in the upper bits:
|
||||||
/// 'legacy' (mutually exclusive w. SAFE & UNSAFE)
|
/// 'legacy' (mutually exclusive w. SAFE & UNSAFE)
|
||||||
const LEGACY = structs::NS_STYLE_ALIGN_LEGACY as u8;
|
const LEGACY = 1 << 5;
|
||||||
/// 'safe'
|
/// 'safe'
|
||||||
const SAFE = structs::NS_STYLE_ALIGN_SAFE as u8;
|
const SAFE = 1 << 6;
|
||||||
/// 'unsafe' (mutually exclusive w. SAFE)
|
/// 'unsafe' (mutually exclusive w. SAFE)
|
||||||
const UNSAFE = structs::NS_STYLE_ALIGN_UNSAFE as u8;
|
const UNSAFE = 1 << 7;
|
||||||
|
|
||||||
/// Mask for the additional flags above.
|
/// Mask for the additional flags above.
|
||||||
const FLAG_BITS = structs::NS_STYLE_ALIGN_FLAG_BITS as u8;
|
const FLAG_BITS = 0b11100000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +144,7 @@ pub enum AxisDirection {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(C)]
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
pub struct ContentDistribution {
|
pub struct ContentDistribution {
|
||||||
primary: AlignFlags,
|
primary: AlignFlags,
|
||||||
|
@ -270,6 +269,7 @@ impl ContentDistribution {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct AlignContent(pub ContentDistribution);
|
pub struct AlignContent(pub ContentDistribution);
|
||||||
|
|
||||||
impl Parse for AlignContent {
|
impl Parse for AlignContent {
|
||||||
|
@ -292,20 +292,6 @@ impl SpecifiedValueInfo for AlignContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl From<u16> for AlignContent {
|
|
||||||
fn from(bits: u16) -> Self {
|
|
||||||
AlignContent(ContentDistribution::from_bits(bits))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl From<AlignContent> for u16 {
|
|
||||||
fn from(v: AlignContent) -> u16 {
|
|
||||||
v.0.as_bits()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Value for the `justify-content` property.
|
/// Value for the `justify-content` property.
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-align/#propdef-justify-content>
|
/// <https://drafts.csswg.org/css-align/#propdef-justify-content>
|
||||||
|
@ -321,6 +307,7 @@ impl From<AlignContent> for u16 {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct JustifyContent(pub ContentDistribution);
|
pub struct JustifyContent(pub ContentDistribution);
|
||||||
|
|
||||||
impl Parse for JustifyContent {
|
impl Parse for JustifyContent {
|
||||||
|
@ -370,6 +357,7 @@ impl From<JustifyContent> for u16 {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct SelfAlignment(pub AlignFlags);
|
pub struct SelfAlignment(pub AlignFlags);
|
||||||
|
|
||||||
impl SelfAlignment {
|
impl SelfAlignment {
|
||||||
|
@ -441,6 +429,7 @@ impl SelfAlignment {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct AlignSelf(pub SelfAlignment);
|
pub struct AlignSelf(pub SelfAlignment);
|
||||||
|
|
||||||
impl Parse for AlignSelf {
|
impl Parse for AlignSelf {
|
||||||
|
@ -463,18 +452,6 @@ impl SpecifiedValueInfo for AlignSelf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for AlignSelf {
|
|
||||||
fn from(bits: u8) -> Self {
|
|
||||||
AlignSelf(SelfAlignment(AlignFlags::from_bits_truncate(bits)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AlignSelf> for u8 {
|
|
||||||
fn from(align: AlignSelf) -> u8 {
|
|
||||||
(align.0).0.bits()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The specified value of the justify-self property.
|
/// The specified value of the justify-self property.
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-align/#propdef-justify-self>
|
/// <https://drafts.csswg.org/css-align/#propdef-justify-self>
|
||||||
|
@ -490,6 +467,7 @@ impl From<AlignSelf> for u8 {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct JustifySelf(pub SelfAlignment);
|
pub struct JustifySelf(pub SelfAlignment);
|
||||||
|
|
||||||
impl Parse for JustifySelf {
|
impl Parse for JustifySelf {
|
||||||
|
@ -512,18 +490,6 @@ impl SpecifiedValueInfo for JustifySelf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for JustifySelf {
|
|
||||||
fn from(bits: u8) -> Self {
|
|
||||||
JustifySelf(SelfAlignment(AlignFlags::from_bits_truncate(bits)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<JustifySelf> for u8 {
|
|
||||||
fn from(justify: JustifySelf) -> u8 {
|
|
||||||
(justify.0).0.bits()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Value of the `align-items` property
|
/// Value of the `align-items` property
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-align/#propdef-align-items>
|
/// <https://drafts.csswg.org/css-align/#propdef-align-items>
|
||||||
|
@ -539,6 +505,7 @@ impl From<JustifySelf> for u8 {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct AlignItems(pub AlignFlags);
|
pub struct AlignItems(pub AlignFlags);
|
||||||
|
|
||||||
impl AlignItems {
|
impl AlignItems {
|
||||||
|
@ -590,6 +557,7 @@ impl SpecifiedValueInfo for AlignItems {
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-align/#justify-items-property>
|
/// <https://drafts.csswg.org/css-align/#justify-items-property>
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct JustifyItems(pub AlignFlags);
|
pub struct JustifyItems(pub AlignFlags);
|
||||||
|
|
||||||
impl JustifyItems {
|
impl JustifyItems {
|
||||||
|
|
|
@ -163,7 +163,8 @@ impl Angle {
|
||||||
/// https://github.com/w3c/fxtf-drafts/issues/228
|
/// https://github.com/w3c/fxtf-drafts/issues/228
|
||||||
///
|
///
|
||||||
/// See also: https://github.com/w3c/csswg-drafts/issues/1162.
|
/// See also: https://github.com/w3c/csswg-drafts/issues/1162.
|
||||||
enum AllowUnitlessZeroAngle {
|
#[allow(missing_docs)]
|
||||||
|
pub enum AllowUnitlessZeroAngle {
|
||||||
Yes,
|
Yes,
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
@ -203,12 +204,14 @@ impl Angle {
|
||||||
Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
|
Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_internal<'i, 't>(
|
pub(super) fn parse_internal<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
allow_unitless_zero: AllowUnitlessZeroAngle,
|
allow_unitless_zero: AllowUnitlessZeroAngle,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
let location = input.current_source_location();
|
||||||
let t = input.next()?;
|
let t = input.next()?;
|
||||||
|
let allow_unitless_zero = matches!(allow_unitless_zero, AllowUnitlessZeroAngle::Yes);
|
||||||
match *t {
|
match *t {
|
||||||
Token::Dimension {
|
Token::Dimension {
|
||||||
value, ref unit, ..
|
value, ref unit, ..
|
||||||
|
@ -221,16 +224,11 @@ impl Angle {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Token::Number { value, .. } if value == 0. => match allow_unitless_zero {
|
Token::Function(ref name) => {
|
||||||
AllowUnitlessZeroAngle::Yes => Ok(Angle::zero()),
|
let function = CalcNode::math_function(name, location)?;
|
||||||
AllowUnitlessZeroAngle::No => {
|
CalcNode::parse_angle(context, input, function)
|
||||||
let t = t.clone();
|
|
||||||
Err(input.new_unexpected_token_error(t))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
|
||||||
return input.parse_nested_block(|i| CalcNode::parse_angle(context, i));
|
|
||||||
},
|
},
|
||||||
|
Token::Number { value, .. } if value == 0. && allow_unitless_zero => Ok(Angle::zero()),
|
||||||
ref t => {
|
ref t => {
|
||||||
let t = t.clone();
|
let t = t.clone();
|
||||||
Err(input.new_unexpected_token_error(t))
|
Err(input.new_unexpected_token_error(t))
|
||||||
|
|
|
@ -61,12 +61,9 @@ pub enum DisplayInside {
|
||||||
None = 0,
|
None = 0,
|
||||||
#[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
|
||||||
Contents,
|
Contents,
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
Flow,
|
||||||
Block,
|
|
||||||
FlowRoot,
|
FlowRoot,
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||||
Inline,
|
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
Flex,
|
Flex,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Grid,
|
Grid,
|
||||||
|
@ -101,8 +98,6 @@ pub enum DisplayInside {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozBox,
|
MozBox,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozInlineBox,
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
MozGrid,
|
MozGrid,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozGridGroup,
|
MozGridGroup,
|
||||||
|
@ -111,10 +106,7 @@ pub enum DisplayInside {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozDeck,
|
MozDeck,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozGroupbox,
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
MozPopup,
|
MozPopup,
|
||||||
Flow, // only used for parsing, not computed value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -147,14 +139,8 @@ impl Display {
|
||||||
pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
|
pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
|
||||||
#[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
|
||||||
pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
|
pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Inline);
|
|
||||||
#[cfg(any(feature = "servo-layout-2020"))]
|
|
||||||
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
|
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
|
||||||
pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
|
pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Block);
|
|
||||||
#[cfg(any(feature = "servo-layout-2020"))]
|
|
||||||
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
|
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
|
pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
|
||||||
|
@ -171,7 +157,7 @@ impl Display {
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||||
pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
|
pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||||
pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Block);
|
pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Flow);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
|
pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -231,9 +217,9 @@ impl Display {
|
||||||
|
|
||||||
/// XUL boxes.
|
/// XUL boxes.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const MozBox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozBox);
|
pub const MozBox: Self = Self::new(DisplayOutside::Block, DisplayInside::MozBox);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const MozInlineBox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozInlineBox);
|
pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
|
pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -243,8 +229,6 @@ impl Display {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
|
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox);
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
|
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
|
||||||
|
|
||||||
/// Make a raw display value from <display-outside> and <display-inside> values.
|
/// Make a raw display value from <display-outside> and <display-inside> values.
|
||||||
|
@ -256,18 +240,8 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a display enum value from <display-outside> and <display-inside> values.
|
/// Make a display enum value from <display-outside> and <display-inside> values.
|
||||||
/// We store `flow` as a synthetic `block` or `inline` inside-value to simplify
|
|
||||||
/// our layout code.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
|
fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
|
||||||
let inside = match inside {
|
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
DisplayInside::Flow => match outside {
|
|
||||||
DisplayOutside::Inline => DisplayInside::Inline,
|
|
||||||
_ => DisplayInside::Block,
|
|
||||||
},
|
|
||||||
_ => inside,
|
|
||||||
};
|
|
||||||
let v = Self::new(outside, inside);
|
let v = Self::new(outside, inside);
|
||||||
if !list_item {
|
if !list_item {
|
||||||
return v;
|
return v;
|
||||||
|
@ -290,6 +264,12 @@ impl Display {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this is `display: inline` (or `inline list-item`).
|
||||||
|
#[inline]
|
||||||
|
pub fn is_inline_flow(&self) -> bool {
|
||||||
|
self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether this `display` value is some kind of list-item.
|
/// Returns whether this `display` value is some kind of list-item.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn is_list_item(&self) -> bool {
|
pub const fn is_list_item(&self) -> bool {
|
||||||
|
@ -381,19 +361,13 @@ impl Display {
|
||||||
match self.outside() {
|
match self.outside() {
|
||||||
DisplayOutside::Inline => {
|
DisplayOutside::Inline => {
|
||||||
let inside = match self.inside() {
|
let inside = match self.inside() {
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
// `inline-block` blockifies to `block` rather than
|
||||||
DisplayInside::Inline | DisplayInside::FlowRoot => DisplayInside::Block,
|
// `flow-root`, for legacy reasons.
|
||||||
#[cfg(feature = "servo-layout-2020")]
|
|
||||||
DisplayInside::FlowRoot => DisplayInside::Flow,
|
DisplayInside::FlowRoot => DisplayInside::Flow,
|
||||||
inside => inside,
|
inside => inside,
|
||||||
};
|
};
|
||||||
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
|
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
DisplayOutside::XUL => match self.inside() {
|
|
||||||
DisplayInside::MozInlineBox | DisplayInside::MozBox => Display::MozBox,
|
|
||||||
_ => Display::Block,
|
|
||||||
},
|
|
||||||
DisplayOutside::Block | DisplayOutside::None => *self,
|
DisplayOutside::Block | DisplayOutside::None => *self,
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||||
_ => Display::Block,
|
_ => Display::Block,
|
||||||
|
@ -407,16 +381,13 @@ impl Display {
|
||||||
match self.outside() {
|
match self.outside() {
|
||||||
DisplayOutside::Block => {
|
DisplayOutside::Block => {
|
||||||
let inside = match self.inside() {
|
let inside = match self.inside() {
|
||||||
DisplayInside::Block => DisplayInside::FlowRoot,
|
// `display: block` inlinifies to `display: inline-block`,
|
||||||
|
// rather than `inline`, for legacy reasons.
|
||||||
|
DisplayInside::Flow => DisplayInside::FlowRoot,
|
||||||
inside => inside,
|
inside => inside,
|
||||||
};
|
};
|
||||||
Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
|
Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
DisplayOutside::XUL => match self.inside() {
|
|
||||||
DisplayInside::MozBox => Display::MozInlineBox,
|
|
||||||
_ => *self,
|
|
||||||
},
|
|
||||||
_ => *self,
|
_ => *self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,18 +414,8 @@ impl ToCss for Display {
|
||||||
where
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
debug_assert_ne!(
|
|
||||||
self.inside(),
|
|
||||||
DisplayInside::Flow,
|
|
||||||
"`flow` fears in `display` computed value"
|
|
||||||
);
|
|
||||||
let outside = self.outside();
|
let outside = self.outside();
|
||||||
let inside = match self.inside() {
|
let inside = self.inside();
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
DisplayInside::Block | DisplayInside::Inline => DisplayInside::Flow,
|
|
||||||
inside => inside,
|
|
||||||
};
|
|
||||||
match *self {
|
match *self {
|
||||||
Display::Block | Display::Inline => outside.to_css(dest),
|
Display::Block | Display::Inline => outside.to_css(dest),
|
||||||
Display::InlineBlock => dest.write_str("inline-block"),
|
Display::InlineBlock => dest.write_str("inline-block"),
|
||||||
|
@ -657,8 +618,6 @@ impl Parse for Display {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
|
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
"-moz-groupbox" if moz_display_values_enabled(context) => Display::MozGroupbox,
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
|
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,61 @@ use crate::parser::ParserContext;
|
||||||
use crate::values::computed;
|
use crate::values::computed;
|
||||||
use crate::values::specified::length::ViewportPercentageLength;
|
use crate::values::specified::length::ViewportPercentageLength;
|
||||||
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
||||||
use crate::values::specified::{Angle, Time};
|
use crate::values::specified::{self, Angle, Time};
|
||||||
use crate::values::{CSSFloat, CSSInteger};
|
use crate::values::{CSSFloat, CSSInteger};
|
||||||
use cssparser::{AngleOrNumber, NumberOrPercentage, Parser, Token};
|
use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
use std::{cmp, mem};
|
||||||
use style_traits::values::specified::AllowedNumericType;
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
/// The name of the mathematical function that we're parsing.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum MathFunction {
|
||||||
|
/// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc
|
||||||
|
Calc,
|
||||||
|
/// `min()`: https://drafts.csswg.org/css-values-4/#funcdef-min
|
||||||
|
Min,
|
||||||
|
/// `max()`: https://drafts.csswg.org/css-values-4/#funcdef-max
|
||||||
|
Max,
|
||||||
|
/// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp
|
||||||
|
Clamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This determines the order in which we serialize members of a calc()
|
||||||
|
/// sum.
|
||||||
|
///
|
||||||
|
/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
enum SortKey {
|
||||||
|
Number,
|
||||||
|
Percentage,
|
||||||
|
Ch,
|
||||||
|
Deg,
|
||||||
|
Em,
|
||||||
|
Ex,
|
||||||
|
Px,
|
||||||
|
Rem,
|
||||||
|
Sec,
|
||||||
|
Vh,
|
||||||
|
Vmax,
|
||||||
|
Vmin,
|
||||||
|
Vw,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether we're a `min` or `max` function.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum MinMaxOp {
|
||||||
|
/// `min()`
|
||||||
|
Min,
|
||||||
|
/// `max()`
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
/// A node inside a `Calc` expression's AST.
|
/// A node inside a `Calc` expression's AST.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum CalcNode {
|
pub enum CalcNode {
|
||||||
/// `<length>`
|
/// `<length>`
|
||||||
Length(NoCalcLength),
|
Length(NoCalcLength),
|
||||||
|
@ -30,14 +76,20 @@ pub enum CalcNode {
|
||||||
Percentage(CSSFloat),
|
Percentage(CSSFloat),
|
||||||
/// `<number>`
|
/// `<number>`
|
||||||
Number(CSSFloat),
|
Number(CSSFloat),
|
||||||
/// An expression of the form `x + y`
|
/// An expression of the form `x + y + ...`. Subtraction is represented by
|
||||||
Sum(Box<CalcNode>, Box<CalcNode>),
|
/// the negated expression of the right hand side.
|
||||||
/// An expression of the form `x - y`
|
Sum(Box<[CalcNode]>),
|
||||||
Sub(Box<CalcNode>, Box<CalcNode>),
|
/// A `min()` / `max()` function.
|
||||||
/// An expression of the form `x * y`
|
MinMax(Box<[CalcNode]>, MinMaxOp),
|
||||||
Mul(Box<CalcNode>, Box<CalcNode>),
|
/// A `clamp()` function.
|
||||||
/// An expression of the form `x / y`
|
Clamp {
|
||||||
Div(Box<CalcNode>, Box<CalcNode>),
|
/// The minimum value.
|
||||||
|
min: Box<CalcNode>,
|
||||||
|
/// The central value.
|
||||||
|
center: Box<CalcNode>,
|
||||||
|
/// The maximum value.
|
||||||
|
max: Box<CalcNode>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An expected unit we intend to parse within a `calc()` expression.
|
/// An expected unit we intend to parse within a `calc()` expression.
|
||||||
|
@ -150,7 +202,362 @@ impl ToCss for CalcLengthPercentage {
|
||||||
|
|
||||||
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
||||||
|
|
||||||
|
macro_rules! impl_generic_to_type {
|
||||||
|
($self:ident, $self_variant:ident, $to_self:ident, $to_float:ident, $from_float:path) => {{
|
||||||
|
if let Self::$self_variant(ref v) = *$self {
|
||||||
|
return Ok(v.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match *$self {
|
||||||
|
Self::Sum(ref expressions) => {
|
||||||
|
let mut sum = 0.;
|
||||||
|
for sub in &**expressions {
|
||||||
|
sum += sub.$to_self()?.$to_float();
|
||||||
|
}
|
||||||
|
$from_float(sum)
|
||||||
|
},
|
||||||
|
Self::Clamp {
|
||||||
|
ref min,
|
||||||
|
ref center,
|
||||||
|
ref max,
|
||||||
|
} => {
|
||||||
|
let min = min.$to_self()?;
|
||||||
|
let center = center.$to_self()?;
|
||||||
|
let max = max.$to_self()?;
|
||||||
|
|
||||||
|
// Equivalent to cmp::max(min, cmp::min(center, max))
|
||||||
|
//
|
||||||
|
// But preserving units when appropriate.
|
||||||
|
let center_float = center.$to_float();
|
||||||
|
let min_float = min.$to_float();
|
||||||
|
let max_float = max.$to_float();
|
||||||
|
|
||||||
|
let mut result = center;
|
||||||
|
let mut result_float = center_float;
|
||||||
|
|
||||||
|
if result_float > max_float {
|
||||||
|
result = max;
|
||||||
|
result_float = max_float;
|
||||||
|
}
|
||||||
|
|
||||||
|
if result_float < min_float {
|
||||||
|
min
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::MinMax(ref nodes, op) => {
|
||||||
|
let mut result = nodes[0].$to_self()?;
|
||||||
|
let mut result_float = result.$to_float();
|
||||||
|
for node in nodes.iter().skip(1) {
|
||||||
|
let candidate = node.$to_self()?;
|
||||||
|
let candidate_float = candidate.$to_float();
|
||||||
|
let candidate_wins = match op {
|
||||||
|
MinMaxOp::Min => candidate_float < result_float,
|
||||||
|
MinMaxOp::Max => candidate_float > result_float,
|
||||||
|
};
|
||||||
|
if candidate_wins {
|
||||||
|
result = candidate;
|
||||||
|
result_float = candidate_float;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
},
|
||||||
|
Self::Length(..) |
|
||||||
|
Self::Angle(..) |
|
||||||
|
Self::Time(..) |
|
||||||
|
Self::Percentage(..) |
|
||||||
|
Self::Number(..) => return Err(()),
|
||||||
|
})
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for CalcNode {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
use self::CalcNode::*;
|
||||||
|
match (self, other) {
|
||||||
|
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
|
||||||
|
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
|
||||||
|
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CalcNode {
|
impl CalcNode {
|
||||||
|
fn negate(&mut self) {
|
||||||
|
self.mul_by(-1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul_by(&mut self, scalar: f32) {
|
||||||
|
match *self {
|
||||||
|
Self::Length(ref mut l) => {
|
||||||
|
// FIXME: For consistency this should probably convert absolute
|
||||||
|
// lengths into pixels.
|
||||||
|
*l = *l * scalar;
|
||||||
|
},
|
||||||
|
Self::Number(ref mut n) => {
|
||||||
|
*n *= scalar;
|
||||||
|
},
|
||||||
|
Self::Angle(ref mut a) => {
|
||||||
|
*a = Angle::from_calc(a.degrees() * scalar);
|
||||||
|
},
|
||||||
|
Self::Time(ref mut t) => {
|
||||||
|
*t = Time::from_calc(t.seconds() * scalar);
|
||||||
|
},
|
||||||
|
Self::Percentage(ref mut p) => {
|
||||||
|
*p *= scalar;
|
||||||
|
},
|
||||||
|
// Multiplication is distributive across this.
|
||||||
|
Self::Sum(ref mut children) => {
|
||||||
|
for node in &mut **children {
|
||||||
|
node.mul_by(scalar);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// This one is a bit trickier.
|
||||||
|
Self::MinMax(ref mut children, ref mut op) => {
|
||||||
|
for node in &mut **children {
|
||||||
|
node.mul_by(scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For negatives we need to invert the operation.
|
||||||
|
if scalar < 0. {
|
||||||
|
*op = match *op {
|
||||||
|
MinMaxOp::Min => MinMaxOp::Max,
|
||||||
|
MinMaxOp::Max => MinMaxOp::Min,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Multiplication is distributive across these.
|
||||||
|
Self::Clamp {
|
||||||
|
ref mut min,
|
||||||
|
ref mut center,
|
||||||
|
ref mut max,
|
||||||
|
} => {
|
||||||
|
min.mul_by(scalar);
|
||||||
|
center.mul_by(scalar);
|
||||||
|
max.mul_by(scalar);
|
||||||
|
// For negatives we need to swap min / max.
|
||||||
|
if scalar < 0. {
|
||||||
|
mem::swap(min, max);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_node_sort_key(&self) -> SortKey {
|
||||||
|
match *self {
|
||||||
|
Self::Number(..) => SortKey::Number,
|
||||||
|
Self::Percentage(..) => SortKey::Percentage,
|
||||||
|
Self::Time(..) => SortKey::Sec,
|
||||||
|
Self::Angle(..) => SortKey::Deg,
|
||||||
|
Self::Length(ref l) => match *l {
|
||||||
|
NoCalcLength::Absolute(..) => SortKey::Px,
|
||||||
|
NoCalcLength::FontRelative(ref relative) => match *relative {
|
||||||
|
FontRelativeLength::Ch(..) => SortKey::Ch,
|
||||||
|
FontRelativeLength::Em(..) => SortKey::Em,
|
||||||
|
FontRelativeLength::Ex(..) => SortKey::Ex,
|
||||||
|
FontRelativeLength::Rem(..) => SortKey::Rem,
|
||||||
|
},
|
||||||
|
NoCalcLength::ViewportPercentage(ref vp) => match *vp {
|
||||||
|
ViewportPercentageLength::Vh(..) => SortKey::Vh,
|
||||||
|
ViewportPercentageLength::Vw(..) => SortKey::Vw,
|
||||||
|
ViewportPercentageLength::Vmax(..) => SortKey::Vmax,
|
||||||
|
ViewportPercentageLength::Vmin(..) => SortKey::Vmin,
|
||||||
|
},
|
||||||
|
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
||||||
|
},
|
||||||
|
Self::Sum(..) | Self::MinMax(..) | Self::Clamp { .. } => SortKey::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to merge one sum to another, that is, perform `x` + `y`.
|
||||||
|
///
|
||||||
|
/// Only handles leaf nodes, it's the caller's responsibility to simplify
|
||||||
|
/// them before calling this if needed.
|
||||||
|
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
|
||||||
|
use self::CalcNode::*;
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(&mut Number(ref mut one), &Number(ref other)) |
|
||||||
|
(&mut Percentage(ref mut one), &Percentage(ref other)) => {
|
||||||
|
*one += *other;
|
||||||
|
},
|
||||||
|
(&mut Angle(ref mut one), &Angle(ref other)) => {
|
||||||
|
*one = specified::Angle::from_calc(one.degrees() + other.degrees());
|
||||||
|
},
|
||||||
|
(&mut Time(ref mut one), &Time(ref other)) => {
|
||||||
|
*one = specified::Time::from_calc(one.seconds() + other.seconds());
|
||||||
|
},
|
||||||
|
(&mut Length(ref mut one), &Length(ref other)) => {
|
||||||
|
*one = one.try_sum(other)?;
|
||||||
|
},
|
||||||
|
_ => return Err(()),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simplifies and sorts the calculation. This is only needed if it's going
|
||||||
|
/// to be preserved after parsing (so, for `<length-percentage>`). Otherwise
|
||||||
|
/// we can just evaluate it and we'll come up with a simplified value
|
||||||
|
/// anyways.
|
||||||
|
fn simplify_and_sort_children(&mut self) {
|
||||||
|
macro_rules! replace_self_with {
|
||||||
|
($slot:expr) => {{
|
||||||
|
let result = mem::replace($slot, Self::Number(0.));
|
||||||
|
mem::replace(self, result);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
match *self {
|
||||||
|
Self::Clamp {
|
||||||
|
ref mut min,
|
||||||
|
ref mut center,
|
||||||
|
ref mut max,
|
||||||
|
} => {
|
||||||
|
min.simplify_and_sort_children();
|
||||||
|
center.simplify_and_sort_children();
|
||||||
|
max.simplify_and_sort_children();
|
||||||
|
|
||||||
|
// NOTE: clamp() is max(min, min(center, max))
|
||||||
|
let min_cmp_center = match min.partial_cmp(¢er) {
|
||||||
|
Some(o) => o,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// So if we can prove that min is more than center, then we won,
|
||||||
|
// as that's what we should always return.
|
||||||
|
if matches!(min_cmp_center, cmp::Ordering::Greater) {
|
||||||
|
return replace_self_with!(&mut **min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise try with max.
|
||||||
|
let max_cmp_center = match max.partial_cmp(¢er) {
|
||||||
|
Some(o) => o,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches!(max_cmp_center, cmp::Ordering::Less) {
|
||||||
|
// max is less than center, so we need to return effectively
|
||||||
|
// `max(min, max)`.
|
||||||
|
let max_cmp_min = match max.partial_cmp(&min) {
|
||||||
|
Some(o) => o,
|
||||||
|
None => {
|
||||||
|
debug_assert!(
|
||||||
|
false,
|
||||||
|
"We compared center with min and max, how are \
|
||||||
|
min / max not comparable with each other?"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches!(max_cmp_min, cmp::Ordering::Less) {
|
||||||
|
return replace_self_with!(&mut **min);
|
||||||
|
}
|
||||||
|
|
||||||
|
return replace_self_with!(&mut **max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we're the center node.
|
||||||
|
return replace_self_with!(&mut **center);
|
||||||
|
},
|
||||||
|
Self::MinMax(ref mut children, op) => {
|
||||||
|
for child in &mut **children {
|
||||||
|
child.simplify_and_sort_children();
|
||||||
|
}
|
||||||
|
|
||||||
|
let winning_order = match op {
|
||||||
|
MinMaxOp::Min => cmp::Ordering::Less,
|
||||||
|
MinMaxOp::Max => cmp::Ordering::Greater,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = 0;
|
||||||
|
for i in 1..children.len() {
|
||||||
|
let o = match children[i].partial_cmp(&children[result]) {
|
||||||
|
// We can't compare all the children, so we can't
|
||||||
|
// know which one will actually win. Bail out and
|
||||||
|
// keep ourselves as a min / max function.
|
||||||
|
//
|
||||||
|
// TODO: Maybe we could simplify compatible children,
|
||||||
|
// see https://github.com/w3c/csswg-drafts/issues/4756
|
||||||
|
None => return,
|
||||||
|
Some(o) => o,
|
||||||
|
};
|
||||||
|
|
||||||
|
if o == winning_order {
|
||||||
|
result = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_self_with!(&mut children[result]);
|
||||||
|
},
|
||||||
|
Self::Sum(ref mut children_slot) => {
|
||||||
|
let mut sums_to_merge = SmallVec::<[_; 3]>::new();
|
||||||
|
let mut extra_kids = 0;
|
||||||
|
for (i, child) in children_slot.iter_mut().enumerate() {
|
||||||
|
child.simplify_and_sort_children();
|
||||||
|
if let Self::Sum(ref mut children) = *child {
|
||||||
|
extra_kids += children.len();
|
||||||
|
sums_to_merge.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we only have one kid, we've already simplified it, and it
|
||||||
|
// doesn't really matter whether it's a sum already or not, so
|
||||||
|
// lift it up and continue.
|
||||||
|
if children_slot.len() == 1 {
|
||||||
|
return replace_self_with!(&mut children_slot[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut children = mem::replace(children_slot, Box::new([])).into_vec();
|
||||||
|
|
||||||
|
if !sums_to_merge.is_empty() {
|
||||||
|
children.reserve(extra_kids - sums_to_merge.len());
|
||||||
|
// Merge all our nested sums, in reverse order so that the
|
||||||
|
// list indices are not invalidated.
|
||||||
|
for i in sums_to_merge.drain(..).rev() {
|
||||||
|
let kid_children = match children.swap_remove(i) {
|
||||||
|
Self::Sum(c) => c,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// This would be nicer with
|
||||||
|
// https://github.com/rust-lang/rust/issues/59878 fixed.
|
||||||
|
children.extend(kid_children.into_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(children.len() >= 2, "Should still have multiple kids!");
|
||||||
|
|
||||||
|
// Sort by spec order.
|
||||||
|
children.sort_unstable_by_key(|c| c.calc_node_sort_key());
|
||||||
|
|
||||||
|
// NOTE: if the function returns true, by the docs of dedup_by,
|
||||||
|
// a is removed.
|
||||||
|
children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());
|
||||||
|
|
||||||
|
if children.len() == 1 {
|
||||||
|
// If only one children remains, lift it up, and carry on.
|
||||||
|
replace_self_with!(&mut children[0]);
|
||||||
|
} else {
|
||||||
|
// Else put our simplified children back.
|
||||||
|
mem::replace(children_slot, children.into_boxed_slice());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Length(ref mut len) => {
|
||||||
|
if let NoCalcLength::Absolute(ref mut absolute_length) = *len {
|
||||||
|
*absolute_length = AbsoluteLength::Px(absolute_length.to_px());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Self::Percentage(..) | Self::Angle(..) | Self::Time(..) | Self::Number(..) => {
|
||||||
|
// These are leaves already, nothing to do.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to parse a single element in the expression, that is, a
|
/// Tries to parse a single element in the expression, that is, a
|
||||||
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
|
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
|
||||||
/// `expected_unit`.
|
/// `expected_unit`.
|
||||||
|
@ -203,11 +610,12 @@ impl CalcNode {
|
||||||
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
|
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
|
||||||
Ok(CalcNode::Percentage(unit_value))
|
Ok(CalcNode::Percentage(unit_value))
|
||||||
},
|
},
|
||||||
(&Token::ParenthesisBlock, _) => {
|
(&Token::ParenthesisBlock, _) => input.parse_nested_block(|input| {
|
||||||
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
|
CalcNode::parse_argument(context, input, expected_unit)
|
||||||
},
|
}),
|
||||||
(&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {
|
(&Token::Function(ref name), _) => {
|
||||||
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
|
let function = CalcNode::math_function(name, location)?;
|
||||||
|
CalcNode::parse(context, input, function, expected_unit)
|
||||||
},
|
},
|
||||||
(t, _) => Err(location.new_unexpected_token_error(t.clone())),
|
(t, _) => Err(location.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
|
@ -219,9 +627,58 @@ impl CalcNode {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
expected_unit: CalcUnit,
|
expected_unit: CalcUnit,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut root = Self::parse_product(context, input, expected_unit)?;
|
// TODO: Do something different based on the function name. In
|
||||||
|
// particular, for non-calc function we need to take a list of
|
||||||
|
// comma-separated arguments and such.
|
||||||
|
input.parse_nested_block(|input| {
|
||||||
|
match function {
|
||||||
|
MathFunction::Calc => Self::parse_argument(context, input, expected_unit),
|
||||||
|
MathFunction::Clamp => {
|
||||||
|
let min = Self::parse_argument(context, input, expected_unit)?;
|
||||||
|
input.expect_comma()?;
|
||||||
|
let center = Self::parse_argument(context, input, expected_unit)?;
|
||||||
|
input.expect_comma()?;
|
||||||
|
let max = Self::parse_argument(context, input, expected_unit)?;
|
||||||
|
Ok(Self::Clamp {
|
||||||
|
min: Box::new(min),
|
||||||
|
center: Box::new(center),
|
||||||
|
max: Box::new(max),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
MathFunction::Min | MathFunction::Max => {
|
||||||
|
// TODO(emilio): The common case for parse_comma_separated
|
||||||
|
// is just one element, but for min / max is two, really...
|
||||||
|
//
|
||||||
|
// Consider adding an API to cssparser to specify the
|
||||||
|
// initial vector capacity?
|
||||||
|
let arguments = input
|
||||||
|
.parse_comma_separated(|input| {
|
||||||
|
Self::parse_argument(context, input, expected_unit)
|
||||||
|
})?
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
|
let op = match function {
|
||||||
|
MathFunction::Min => MinMaxOp::Min,
|
||||||
|
MathFunction::Max => MinMaxOp::Max,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self::MinMax(arguments, op))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_argument<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
expected_unit: CalcUnit,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
let mut sum = SmallVec::<[CalcNode; 1]>::new();
|
||||||
|
sum.push(Self::parse_product(context, input, expected_unit)?);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
|
@ -232,14 +689,12 @@ impl CalcNode {
|
||||||
}
|
}
|
||||||
match *input.next()? {
|
match *input.next()? {
|
||||||
Token::Delim('+') => {
|
Token::Delim('+') => {
|
||||||
let rhs = Self::parse_product(context, input, expected_unit)?;
|
sum.push(Self::parse_product(context, input, expected_unit)?);
|
||||||
let new_root = CalcNode::Sum(Box::new(root), Box::new(rhs));
|
|
||||||
root = new_root;
|
|
||||||
},
|
},
|
||||||
Token::Delim('-') => {
|
Token::Delim('-') => {
|
||||||
let rhs = Self::parse_product(context, input, expected_unit)?;
|
let mut rhs = Self::parse_product(context, input, expected_unit)?;
|
||||||
let new_root = CalcNode::Sub(Box::new(root), Box::new(rhs));
|
rhs.negate();
|
||||||
root = new_root;
|
sum.push(rhs);
|
||||||
},
|
},
|
||||||
ref t => {
|
ref t => {
|
||||||
let t = t.clone();
|
let t = t.clone();
|
||||||
|
@ -254,7 +709,11 @@ impl CalcNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(root)
|
Ok(if sum.len() == 1 {
|
||||||
|
sum.drain(..).next().unwrap()
|
||||||
|
} else {
|
||||||
|
Self::Sum(sum.into_boxed_slice())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a top-level `calc` expression, and all the products that may
|
/// Parse a top-level `calc` expression, and all the products that may
|
||||||
|
@ -271,20 +730,38 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
expected_unit: CalcUnit,
|
expected_unit: CalcUnit,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut root = Self::parse_one(context, input, expected_unit)?;
|
let mut node = Self::parse_one(context, input, expected_unit)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
match input.next() {
|
match input.next() {
|
||||||
Ok(&Token::Delim('*')) => {
|
Ok(&Token::Delim('*')) => {
|
||||||
let rhs = Self::parse_one(context, input, expected_unit)?;
|
let rhs = Self::parse_one(context, input, expected_unit)?;
|
||||||
let new_root = CalcNode::Mul(Box::new(root), Box::new(rhs));
|
if let Ok(rhs) = rhs.to_number() {
|
||||||
root = new_root;
|
node.mul_by(rhs);
|
||||||
|
} else if let Ok(number) = node.to_number() {
|
||||||
|
node = rhs;
|
||||||
|
node.mul_by(number);
|
||||||
|
} else {
|
||||||
|
// One of the two parts of the multiplication has to be
|
||||||
|
// a number, at least until we implement unit math.
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Ok(&Token::Delim('/')) => {
|
Ok(&Token::Delim('/')) => {
|
||||||
let rhs = Self::parse_one(context, input, expected_unit)?;
|
let rhs = Self::parse_one(context, input, expected_unit)?;
|
||||||
let new_root = CalcNode::Div(Box::new(root), Box::new(rhs));
|
// Dividing by units is not ok.
|
||||||
root = new_root;
|
//
|
||||||
|
// TODO(emilio): Eventually it should be.
|
||||||
|
let number = match rhs.to_number() {
|
||||||
|
Ok(n) if n != 0. => n,
|
||||||
|
_ => {
|
||||||
|
return Err(
|
||||||
|
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
node.mul_by(1. / number);
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
input.reset(&start);
|
input.reset(&start);
|
||||||
|
@ -293,55 +770,24 @@ impl CalcNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(root)
|
Ok(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to simplify this expression into a `<length>` or `<percentage`>
|
/// Tries to simplify this expression into a `<length>` or `<percentage`>
|
||||||
/// value.
|
/// value.
|
||||||
fn to_length_or_percentage(
|
fn to_length_or_percentage(
|
||||||
&self,
|
&mut self,
|
||||||
clamping_mode: AllowedNumericType,
|
clamping_mode: AllowedNumericType,
|
||||||
) -> Result<CalcLengthPercentage, ()> {
|
) -> Result<CalcLengthPercentage, ()> {
|
||||||
let mut ret = CalcLengthPercentage {
|
let mut ret = CalcLengthPercentage {
|
||||||
clamping_mode: clamping_mode,
|
clamping_mode,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
self.simplify_and_sort_children();
|
||||||
self.add_length_or_percentage_to(&mut ret, 1.0)?;
|
self.add_length_or_percentage_to(&mut ret, 1.0)?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to simplify this expression into a `<percentage>` value.
|
|
||||||
fn to_percentage(&self) -> Result<CSSFloat, ()> {
|
|
||||||
Ok(match *self {
|
|
||||||
CalcNode::Percentage(percentage) => percentage,
|
|
||||||
CalcNode::Sub(ref a, ref b) => a.to_percentage()? - b.to_percentage()?,
|
|
||||||
CalcNode::Sum(ref a, ref b) => a.to_percentage()? + b.to_percentage()?,
|
|
||||||
CalcNode::Mul(ref a, ref b) => match a.to_percentage() {
|
|
||||||
Ok(lhs) => {
|
|
||||||
let rhs = b.to_number()?;
|
|
||||||
lhs * rhs
|
|
||||||
},
|
|
||||||
Err(..) => {
|
|
||||||
let lhs = a.to_number()?;
|
|
||||||
let rhs = b.to_percentage()?;
|
|
||||||
lhs * rhs
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CalcNode::Div(ref a, ref b) => {
|
|
||||||
let lhs = a.to_percentage()?;
|
|
||||||
let rhs = b.to_number()?;
|
|
||||||
if rhs == 0. {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
lhs / rhs
|
|
||||||
},
|
|
||||||
CalcNode::Number(..) |
|
|
||||||
CalcNode::Length(..) |
|
|
||||||
CalcNode::Angle(..) |
|
|
||||||
CalcNode::Time(..) => return Err(()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Puts this `<length>` or `<percentage>` into `ret`, or error.
|
/// Puts this `<length>` or `<percentage>` into `ret`, or error.
|
||||||
///
|
///
|
||||||
/// `factor` is the sign or multiplicative factor to account for the sign
|
/// `factor` is the sign or multiplicative factor to account for the sign
|
||||||
|
@ -394,29 +840,14 @@ impl CalcNode {
|
||||||
},
|
},
|
||||||
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
|
||||||
},
|
},
|
||||||
CalcNode::Sub(ref a, ref b) => {
|
CalcNode::Sum(ref children) => {
|
||||||
a.add_length_or_percentage_to(ret, factor)?;
|
for child in &**children {
|
||||||
b.add_length_or_percentage_to(ret, factor * -1.0)?;
|
child.add_length_or_percentage_to(ret, factor)?;
|
||||||
},
|
|
||||||
CalcNode::Sum(ref a, ref b) => {
|
|
||||||
a.add_length_or_percentage_to(ret, factor)?;
|
|
||||||
b.add_length_or_percentage_to(ret, factor)?;
|
|
||||||
},
|
|
||||||
CalcNode::Mul(ref a, ref b) => match b.to_number() {
|
|
||||||
Ok(rhs) => {
|
|
||||||
a.add_length_or_percentage_to(ret, factor * rhs)?;
|
|
||||||
},
|
|
||||||
Err(..) => {
|
|
||||||
let lhs = a.to_number()?;
|
|
||||||
b.add_length_or_percentage_to(ret, factor * lhs)?;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CalcNode::Div(ref a, ref b) => {
|
|
||||||
let new_factor = b.to_number()?;
|
|
||||||
if new_factor == 0. {
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
a.add_length_or_percentage_to(ret, factor / new_factor)?;
|
},
|
||||||
|
CalcNode::MinMax(..) | CalcNode::Clamp { .. } => {
|
||||||
|
// FIXME(emilio): Implement min/max/clamp for length-percentage.
|
||||||
|
return Err(());
|
||||||
},
|
},
|
||||||
CalcNode::Angle(..) | CalcNode::Time(..) | CalcNode::Number(..) => return Err(()),
|
CalcNode::Angle(..) | CalcNode::Time(..) | CalcNode::Number(..) => return Err(()),
|
||||||
}
|
}
|
||||||
|
@ -426,103 +857,55 @@ impl CalcNode {
|
||||||
|
|
||||||
/// Tries to simplify this expression into a `<time>` value.
|
/// Tries to simplify this expression into a `<time>` value.
|
||||||
fn to_time(&self) -> Result<Time, ()> {
|
fn to_time(&self) -> Result<Time, ()> {
|
||||||
Ok(match *self {
|
impl_generic_to_type!(self, Time, to_time, seconds, Time::from_calc)
|
||||||
CalcNode::Time(ref time) => time.clone(),
|
|
||||||
CalcNode::Sub(ref a, ref b) => {
|
|
||||||
let lhs = a.to_time()?;
|
|
||||||
let rhs = b.to_time()?;
|
|
||||||
Time::from_calc(lhs.seconds() - rhs.seconds())
|
|
||||||
},
|
|
||||||
CalcNode::Sum(ref a, ref b) => {
|
|
||||||
let lhs = a.to_time()?;
|
|
||||||
let rhs = b.to_time()?;
|
|
||||||
Time::from_calc(lhs.seconds() + rhs.seconds())
|
|
||||||
},
|
|
||||||
CalcNode::Mul(ref a, ref b) => match b.to_number() {
|
|
||||||
Ok(rhs) => {
|
|
||||||
let lhs = a.to_time()?;
|
|
||||||
Time::from_calc(lhs.seconds() * rhs)
|
|
||||||
},
|
|
||||||
Err(()) => {
|
|
||||||
let lhs = a.to_number()?;
|
|
||||||
let rhs = b.to_time()?;
|
|
||||||
Time::from_calc(lhs * rhs.seconds())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CalcNode::Div(ref a, ref b) => {
|
|
||||||
let lhs = a.to_time()?;
|
|
||||||
let rhs = b.to_number()?;
|
|
||||||
if rhs == 0. {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
Time::from_calc(lhs.seconds() / rhs)
|
|
||||||
},
|
|
||||||
CalcNode::Number(..) |
|
|
||||||
CalcNode::Length(..) |
|
|
||||||
CalcNode::Percentage(..) |
|
|
||||||
CalcNode::Angle(..) => return Err(()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to simplify this expression into an `Angle` value.
|
/// Tries to simplify this expression into an `Angle` value.
|
||||||
fn to_angle(&self) -> Result<Angle, ()> {
|
fn to_angle(&self) -> Result<Angle, ()> {
|
||||||
Ok(match *self {
|
impl_generic_to_type!(self, Angle, to_angle, degrees, Angle::from_calc)
|
||||||
CalcNode::Angle(ref angle) => angle.clone(),
|
|
||||||
CalcNode::Sub(ref a, ref b) => {
|
|
||||||
let lhs = a.to_angle()?;
|
|
||||||
let rhs = b.to_angle()?;
|
|
||||||
Angle::from_calc(lhs.degrees() - rhs.degrees())
|
|
||||||
},
|
|
||||||
CalcNode::Sum(ref a, ref b) => {
|
|
||||||
let lhs = a.to_angle()?;
|
|
||||||
let rhs = b.to_angle()?;
|
|
||||||
Angle::from_calc(lhs.degrees() + rhs.degrees())
|
|
||||||
},
|
|
||||||
CalcNode::Mul(ref a, ref b) => match a.to_angle() {
|
|
||||||
Ok(lhs) => {
|
|
||||||
let rhs = b.to_number()?;
|
|
||||||
Angle::from_calc(lhs.degrees() * rhs)
|
|
||||||
},
|
|
||||||
Err(..) => {
|
|
||||||
let lhs = a.to_number()?;
|
|
||||||
let rhs = b.to_angle()?;
|
|
||||||
Angle::from_calc(lhs * rhs.degrees())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CalcNode::Div(ref a, ref b) => {
|
|
||||||
let lhs = a.to_angle()?;
|
|
||||||
let rhs = b.to_number()?;
|
|
||||||
if rhs == 0. {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
Angle::from_calc(lhs.degrees() / rhs)
|
|
||||||
},
|
|
||||||
CalcNode::Number(..) |
|
|
||||||
CalcNode::Length(..) |
|
|
||||||
CalcNode::Percentage(..) |
|
|
||||||
CalcNode::Time(..) => return Err(()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to simplify this expression into a `<number>` value.
|
/// Tries to simplify this expression into a `<number>` value.
|
||||||
fn to_number(&self) -> Result<CSSFloat, ()> {
|
fn to_number(&self) -> Result<CSSFloat, ()> {
|
||||||
Ok(match *self {
|
impl_generic_to_type!(self, Number, to_number, clone, From::from)
|
||||||
CalcNode::Number(n) => n,
|
}
|
||||||
CalcNode::Sum(ref a, ref b) => a.to_number()? + b.to_number()?,
|
|
||||||
CalcNode::Sub(ref a, ref b) => a.to_number()? - b.to_number()?,
|
/// Tries to simplify this expression into a `<percentage>` value.
|
||||||
CalcNode::Mul(ref a, ref b) => a.to_number()? * b.to_number()?,
|
fn to_percentage(&self) -> Result<CSSFloat, ()> {
|
||||||
CalcNode::Div(ref a, ref b) => {
|
impl_generic_to_type!(self, Percentage, to_percentage, clone, From::from)
|
||||||
let lhs = a.to_number()?;
|
}
|
||||||
let rhs = b.to_number()?;
|
|
||||||
if rhs == 0. {
|
/// Given a function name, and the location from where the token came from,
|
||||||
return Err(());
|
/// return a mathematical function corresponding to that name or an error.
|
||||||
}
|
#[inline]
|
||||||
lhs / rhs
|
pub fn math_function<'i>(
|
||||||
},
|
name: &CowRcStr<'i>,
|
||||||
CalcNode::Length(..) |
|
location: cssparser::SourceLocation,
|
||||||
CalcNode::Percentage(..) |
|
) -> Result<MathFunction, ParseError<'i>> {
|
||||||
CalcNode::Angle(..) |
|
// TODO(emilio): Unify below when the pref for math functions is gone.
|
||||||
CalcNode::Time(..) => return Err(()),
|
if name.eq_ignore_ascii_case("calc") {
|
||||||
|
return Ok(MathFunction::Calc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn comparison_functions_enabled() -> bool {
|
||||||
|
static_prefs::pref!("layout.css.comparison-functions.enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
fn comparison_functions_enabled() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !comparison_functions_enabled() {
|
||||||
|
return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match_ignore_ascii_case! { &*name,
|
||||||
|
"min" => MathFunction::Min,
|
||||||
|
"max" => MathFunction::Max,
|
||||||
|
"clamp" => MathFunction::Clamp,
|
||||||
|
_ => return Err(location.new_unexpected_token_error(Token::Function(name.clone()))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,8 +913,9 @@ impl CalcNode {
|
||||||
pub fn parse_integer<'i, 't>(
|
pub fn parse_integer<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<CSSInteger, ParseError<'i>> {
|
) -> Result<CSSInteger, ParseError<'i>> {
|
||||||
Self::parse_number(context, input).map(|n| n.round() as CSSInteger)
|
Self::parse_number(context, input, function).map(|n| n.round() as CSSInteger)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience parsing function for `<length> | <percentage>`.
|
/// Convenience parsing function for `<length> | <percentage>`.
|
||||||
|
@ -539,8 +923,9 @@ impl CalcNode {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
clamping_mode: AllowedNumericType,
|
clamping_mode: AllowedNumericType,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::LengthPercentage)?
|
Self::parse(context, input, function, CalcUnit::LengthPercentage)?
|
||||||
.to_length_or_percentage(clamping_mode)
|
.to_length_or_percentage(clamping_mode)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -549,8 +934,9 @@ impl CalcNode {
|
||||||
pub fn parse_percentage<'i, 't>(
|
pub fn parse_percentage<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<CSSFloat, ParseError<'i>> {
|
) -> Result<CSSFloat, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::Percentage)?
|
Self::parse(context, input, function, CalcUnit::Percentage)?
|
||||||
.to_percentage()
|
.to_percentage()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -560,8 +946,9 @@ impl CalcNode {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
clamping_mode: AllowedNumericType,
|
clamping_mode: AllowedNumericType,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::Length)?
|
Self::parse(context, input, function, CalcUnit::Length)?
|
||||||
.to_length_or_percentage(clamping_mode)
|
.to_length_or_percentage(clamping_mode)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -570,8 +957,9 @@ impl CalcNode {
|
||||||
pub fn parse_number<'i, 't>(
|
pub fn parse_number<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<CSSFloat, ParseError<'i>> {
|
) -> Result<CSSFloat, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::Number)?
|
Self::parse(context, input, function, CalcUnit::Number)?
|
||||||
.to_number()
|
.to_number()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -580,8 +968,9 @@ impl CalcNode {
|
||||||
pub fn parse_angle<'i, 't>(
|
pub fn parse_angle<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<Angle, ParseError<'i>> {
|
) -> Result<Angle, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::Angle)?
|
Self::parse(context, input, function, CalcUnit::Angle)?
|
||||||
.to_angle()
|
.to_angle()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -590,8 +979,9 @@ impl CalcNode {
|
||||||
pub fn parse_time<'i, 't>(
|
pub fn parse_time<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<Time, ParseError<'i>> {
|
) -> Result<Time, ParseError<'i>> {
|
||||||
Self::parse(context, input, CalcUnit::Time)?
|
Self::parse(context, input, function, CalcUnit::Time)?
|
||||||
.to_time()
|
.to_time()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -600,8 +990,9 @@ impl CalcNode {
|
||||||
pub fn parse_number_or_percentage<'i, 't>(
|
pub fn parse_number_or_percentage<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
||||||
let node = Self::parse(context, input, CalcUnit::Percentage)?;
|
let node = Self::parse(context, input, function, CalcUnit::Percentage)?;
|
||||||
|
|
||||||
if let Ok(value) = node.to_number() {
|
if let Ok(value) = node.to_number() {
|
||||||
return Ok(NumberOrPercentage::Number { value });
|
return Ok(NumberOrPercentage::Number { value });
|
||||||
|
@ -617,8 +1008,9 @@ impl CalcNode {
|
||||||
pub fn parse_angle_or_number<'i, 't>(
|
pub fn parse_angle_or_number<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
function: MathFunction,
|
||||||
) -> Result<AngleOrNumber, ParseError<'i>> {
|
) -> Result<AngleOrNumber, ParseError<'i>> {
|
||||||
let node = Self::parse(context, input, CalcUnit::Angle)?;
|
let node = Self::parse(context, input, function, CalcUnit::Angle)?;
|
||||||
|
|
||||||
if let Ok(angle) = node.to_angle() {
|
if let Ok(angle) = node.to_angle() {
|
||||||
let degrees = angle.degrees();
|
let degrees = angle.degrees();
|
||||||
|
|
|
@ -298,8 +298,9 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
|
||||||
Ok(AngleOrNumber::Angle { degrees })
|
Ok(AngleOrNumber::Angle { degrees })
|
||||||
},
|
},
|
||||||
Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
|
Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i))
|
let function = CalcNode::math_function(name, location)?;
|
||||||
|
CalcNode::parse_angle_or_number(self.0, input, function)
|
||||||
},
|
},
|
||||||
t => return Err(location.new_unexpected_token_error(t)),
|
t => return Err(location.new_unexpected_token_error(t)),
|
||||||
}
|
}
|
||||||
|
@ -323,15 +324,16 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
|
||||||
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
|
|
||||||
match input.next()?.clone() {
|
match *input.next()? {
|
||||||
Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
|
Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
|
||||||
Token::Percentage { unit_value, .. } => {
|
Token::Percentage { unit_value, .. } => {
|
||||||
Ok(NumberOrPercentage::Percentage { unit_value })
|
Ok(NumberOrPercentage::Percentage { unit_value })
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i))
|
let function = CalcNode::math_function(name, location)?;
|
||||||
|
CalcNode::parse_number_or_percentage(self.0, input, function)
|
||||||
},
|
},
|
||||||
t => return Err(location.new_unexpected_token_error(t)),
|
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@ use crate::computed_values::list_style_type::T as ListStyleType;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::values::generics::counters as generics;
|
use crate::values::generics::counters as generics;
|
||||||
use crate::values::generics::counters::CounterPair;
|
use crate::values::generics::counters::CounterPair;
|
||||||
use crate::values::generics::counters::GenericCounterIncrement;
|
|
||||||
use crate::values::generics::counters::GenericCounterSetOrReset;
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::values::generics::CounterStyle;
|
use crate::values::generics::CounterStyle;
|
||||||
use crate::values::specified::url::SpecifiedImageUrl;
|
use crate::values::specified::url::SpecifiedImageUrl;
|
||||||
|
@ -23,7 +21,7 @@ use selectors::parser::SelectorParseErrorKind;
|
||||||
use style_traits::{ParseError, StyleParseErrorKind};
|
use style_traits::{ParseError, StyleParseErrorKind};
|
||||||
|
|
||||||
/// A specified value for the `counter-increment` property.
|
/// A specified value for the `counter-increment` property.
|
||||||
pub type CounterIncrement = GenericCounterIncrement<Integer>;
|
pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
|
||||||
|
|
||||||
impl Parse for CounterIncrement {
|
impl Parse for CounterIncrement {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
|
@ -35,7 +33,7 @@ impl Parse for CounterIncrement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specified value for the `counter-set` and `counter-reset` properties.
|
/// A specified value for the `counter-set` and `counter-reset` properties.
|
||||||
pub type CounterSetOrReset = GenericCounterSetOrReset<Integer>;
|
pub type CounterSetOrReset = generics::GenericCounterSetOrReset<Integer>;
|
||||||
|
|
||||||
impl Parse for CounterSetOrReset {
|
impl Parse for CounterSetOrReset {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
|
@ -84,10 +82,10 @@ fn parse_counters<'i, 't>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The specified value for the `content` property.
|
/// The specified value for the `content` property.
|
||||||
pub type Content = generics::Content<SpecifiedImageUrl>;
|
pub type Content = generics::GenericContent<SpecifiedImageUrl>;
|
||||||
|
|
||||||
/// The specified value for a content item in the `content` property.
|
/// The specified value for a content item in the `content` property.
|
||||||
pub type ContentItem = generics::ContentItem<SpecifiedImageUrl>;
|
pub type ContentItem = generics::GenericContentItem<SpecifiedImageUrl>;
|
||||||
|
|
||||||
impl Content {
|
impl Content {
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -115,6 +113,7 @@ impl Parse for Content {
|
||||||
// normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
|
// normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
|
||||||
// no-close-quote ]+
|
// no-close-quote ]+
|
||||||
// TODO: <uri>, attr(<identifier>)
|
// TODO: <uri>, attr(<identifier>)
|
||||||
|
#[cfg_attr(feature = "servo", allow(unused_mut))]
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
@ -131,17 +130,9 @@ impl Parse for Content {
|
||||||
{
|
{
|
||||||
return Ok(generics::Content::None);
|
return Ok(generics::Content::None);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
{
|
|
||||||
if input
|
|
||||||
.try(|input| input.expect_ident_matching("-moz-alt-content"))
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
return Ok(generics::Content::MozAltContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut content = vec![];
|
let mut content = vec![];
|
||||||
|
let mut has_alt_content = false;
|
||||||
loop {
|
loop {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
{
|
{
|
||||||
|
@ -153,7 +144,7 @@ impl Parse for Content {
|
||||||
match input.next() {
|
match input.next() {
|
||||||
Ok(&Token::QuotedString(ref value)) => {
|
Ok(&Token::QuotedString(ref value)) => {
|
||||||
content.push(generics::ContentItem::String(
|
content.push(generics::ContentItem::String(
|
||||||
value.as_ref().to_owned().into_boxed_str(),
|
value.as_ref().to_owned().into(),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
Ok(&Token::Function(ref name)) => {
|
Ok(&Token::Function(ref name)) => {
|
||||||
|
@ -168,7 +159,7 @@ impl Parse for Content {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
|
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str();
|
let separator = input.expect_string()?.as_ref().to_owned().into();
|
||||||
let style = Content::parse_counter_style(context, input);
|
let style = Content::parse_counter_style(context, input);
|
||||||
Ok(generics::ContentItem::Counters(name, separator, style))
|
Ok(generics::ContentItem::Counters(name, separator, style))
|
||||||
}),
|
}),
|
||||||
|
@ -191,6 +182,11 @@ impl Parse for Content {
|
||||||
"close-quote" => generics::ContentItem::CloseQuote,
|
"close-quote" => generics::ContentItem::CloseQuote,
|
||||||
"no-open-quote" => generics::ContentItem::NoOpenQuote,
|
"no-open-quote" => generics::ContentItem::NoOpenQuote,
|
||||||
"no-close-quote" => generics::ContentItem::NoCloseQuote,
|
"no-close-quote" => generics::ContentItem::NoCloseQuote,
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
"-moz-alt-content" => {
|
||||||
|
has_alt_content = true;
|
||||||
|
generics::ContentItem::MozAltContent
|
||||||
|
},
|
||||||
_ =>{
|
_ =>{
|
||||||
let ident = ident.clone();
|
let ident = ident.clone();
|
||||||
return Err(input.new_custom_error(
|
return Err(input.new_custom_error(
|
||||||
|
@ -206,9 +202,10 @@ impl Parse for Content {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if content.is_empty() {
|
// We don't allow to parse `-moz-alt-content in multiple positions.
|
||||||
|
if content.is_empty() || (has_alt_content && content.len() != 1) {
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
}
|
}
|
||||||
Ok(generics::Content::Items(content.into_boxed_slice()))
|
Ok(generics::Content::Items(content.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -574,11 +574,11 @@ impl KeywordInfo {
|
||||||
|
|
||||||
/// Given a parent keyword info (self), apply an additional factor/offset to
|
/// Given a parent keyword info (self), apply an additional factor/offset to
|
||||||
/// it.
|
/// it.
|
||||||
pub fn compose(self, factor: f32, offset: CSSPixelLength) -> Self {
|
fn compose(self, factor: f32) -> Self {
|
||||||
KeywordInfo {
|
KeywordInfo {
|
||||||
kw: self.kw,
|
kw: self.kw,
|
||||||
factor: self.factor * factor,
|
factor: self.factor * factor,
|
||||||
offset: self.offset * factor + offset,
|
offset: self.offset * factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -614,12 +614,6 @@ pub enum FontSize {
|
||||||
System(SystemFont),
|
System(SystemFont),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LengthPercentage> for FontSize {
|
|
||||||
fn from(other: LengthPercentage) -> Self {
|
|
||||||
FontSize::Length(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specifies a prioritized list of font family names or generic family names.
|
/// Specifies a prioritized list of font family names or generic family names.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
|
#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
|
||||||
#[cfg_attr(feature = "servo", derive(Hash))]
|
#[cfg_attr(feature = "servo", derive(Hash))]
|
||||||
|
@ -901,7 +895,7 @@ impl FontSize {
|
||||||
.get_parent_font()
|
.get_parent_font()
|
||||||
.clone_font_size()
|
.clone_font_size()
|
||||||
.keyword_info
|
.keyword_info
|
||||||
.map(|i| i.compose(factor, CSSPixelLength::new(0.)))
|
.map(|i| i.compose(factor))
|
||||||
};
|
};
|
||||||
let mut info = None;
|
let mut info = None;
|
||||||
let size = match *self {
|
let size = match *self {
|
||||||
|
@ -927,42 +921,8 @@ impl FontSize {
|
||||||
base_size.resolve(context) * pc.0
|
base_size.resolve(context) * pc.0
|
||||||
},
|
},
|
||||||
FontSize::Length(LengthPercentage::Calc(ref calc)) => {
|
FontSize::Length(LengthPercentage::Calc(ref calc)) => {
|
||||||
let parent = context.style().get_parent_font().clone_font_size();
|
|
||||||
// if we contain em/% units and the parent was keyword derived, this is too
|
|
||||||
// Extract the ratio/offset and compose it
|
|
||||||
if (calc.em.is_some() || calc.percentage.is_some()) && parent.keyword_info.is_some()
|
|
||||||
{
|
|
||||||
let ratio = calc.em.unwrap_or(0.) + calc.percentage.map_or(0., |pc| pc.0);
|
|
||||||
// Compute it, but shave off the font-relative part (em, %).
|
|
||||||
//
|
|
||||||
// This will mean that other font-relative units like ex and
|
|
||||||
// ch will be computed against the old parent font even when
|
|
||||||
// the font changes.
|
|
||||||
//
|
|
||||||
// There's no particular "right answer" for what to do here,
|
|
||||||
// Gecko recascades as if the font had changed, we instead
|
|
||||||
// track the changes and reapply, which means that we carry
|
|
||||||
// over old computed ex/ch values whilst Gecko recomputes
|
|
||||||
// new ones.
|
|
||||||
//
|
|
||||||
// This is enough of an edge case to not really matter.
|
|
||||||
let abs = calc
|
|
||||||
.to_computed_value_zoomed(
|
|
||||||
context,
|
|
||||||
FontBaseSize::InheritedStyleButStripEmUnits,
|
|
||||||
)
|
|
||||||
.unclamped_length();
|
|
||||||
|
|
||||||
info = parent.keyword_info.map(|i| i.compose(ratio, abs));
|
|
||||||
}
|
|
||||||
let calc = calc.to_computed_value_zoomed(context, base_size);
|
let calc = calc.to_computed_value_zoomed(context, base_size);
|
||||||
// FIXME(emilio): we _could_ use clamp_to_non_negative()
|
calc.resolve(base_size.resolve(context))
|
||||||
// everywhere, without affecting behavior in theory, since the
|
|
||||||
// others should reject negatives during parsing. But SMIL
|
|
||||||
// allows parsing negatives, and relies on us _not_ doing that
|
|
||||||
// clamping. That's so bonkers :(
|
|
||||||
calc.percentage_relative_to(base_size.resolve(context))
|
|
||||||
.clamp_to_non_negative()
|
|
||||||
},
|
},
|
||||||
FontSize::Keyword(i) => {
|
FontSize::Keyword(i) => {
|
||||||
// As a specified keyword, this is keyword derived
|
// As a specified keyword, this is keyword derived
|
||||||
|
|
|
@ -23,7 +23,7 @@ fn parse_pixel_or_percent<'i, 't>(
|
||||||
value, ref unit, ..
|
value, ref unit, ..
|
||||||
} => {
|
} => {
|
||||||
match_ignore_ascii_case! { unit,
|
match_ignore_ascii_case! { unit,
|
||||||
"px" => Ok(LengthPercentage::new(Length::new(value), None)),
|
"px" => Ok(LengthPercentage::new_length(Length::new(value))),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
//! [length]: https://drafts.csswg.org/css-values/#lengths
|
//! [length]: https://drafts.csswg.org/css-values/#lengths
|
||||||
|
|
||||||
use super::{AllowQuirks, Number, Percentage, ToComputedValue};
|
use super::{AllowQuirks, Number, Percentage, ToComputedValue};
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
|
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::properties::computed_value_flags::ComputedValueFlags;
|
|
||||||
use crate::values::computed::{self, CSSPixelLength, Context};
|
use crate::values::computed::{self, CSSPixelLength, Context};
|
||||||
use crate::values::generics::length as generics;
|
use crate::values::generics::length as generics;
|
||||||
use crate::values::generics::length::{
|
use crate::values::generics::length::{
|
||||||
|
@ -71,10 +71,6 @@ pub enum FontBaseSize {
|
||||||
CurrentStyle,
|
CurrentStyle,
|
||||||
/// Use the inherited font-size.
|
/// Use the inherited font-size.
|
||||||
InheritedStyle,
|
InheritedStyle,
|
||||||
/// Use the inherited font-size, but strip em units.
|
|
||||||
///
|
|
||||||
/// FIXME(emilio): This is very complex, and should go away.
|
|
||||||
InheritedStyleButStripEmUnits,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontBaseSize {
|
impl FontBaseSize {
|
||||||
|
@ -82,7 +78,7 @@ impl FontBaseSize {
|
||||||
pub fn resolve(&self, context: &Context) -> computed::Length {
|
pub fn resolve(&self, context: &Context) -> computed::Length {
|
||||||
match *self {
|
match *self {
|
||||||
FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(),
|
FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(),
|
||||||
FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => {
|
FontBaseSize::InheritedStyle => {
|
||||||
context.style().get_parent_font().clone_font_size().size()
|
context.style().get_parent_font().clone_font_size().size()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -100,6 +96,29 @@ impl FontRelativeLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
|
||||||
|
use self::FontRelativeLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match (self, other) {
|
||||||
|
(&Em(one), &Em(other)) => Em(one + other),
|
||||||
|
(&Ex(one), &Ex(other)) => Ex(one + other),
|
||||||
|
(&Ch(one), &Ch(other)) => Ch(one + other),
|
||||||
|
(&Rem(one), &Rem(other)) => Rem(one + other),
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Em(..) | Ex(..) | Ch(..) | Rem(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot to handle unit in try_sum()")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the font-relative length.
|
/// Computes the font-relative length.
|
||||||
pub fn to_computed_value(
|
pub fn to_computed_value(
|
||||||
&self,
|
&self,
|
||||||
|
@ -144,11 +163,7 @@ impl FontRelativeLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if base_size == FontBaseSize::InheritedStyleButStripEmUnits {
|
(reference_font_size, length)
|
||||||
(Zero::zero(), length)
|
|
||||||
} else {
|
|
||||||
(reference_font_size, length)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
FontRelativeLength::Ex(length) => {
|
FontRelativeLength::Ex(length) => {
|
||||||
if context.for_non_inherited_property.is_some() {
|
if context.for_non_inherited_property.is_some() {
|
||||||
|
@ -214,7 +229,7 @@ impl FontRelativeLength {
|
||||||
// element, the rem units refer to the property's initial
|
// element, the rem units refer to the property's initial
|
||||||
// value.
|
// value.
|
||||||
//
|
//
|
||||||
let reference_size = if context.is_root_element || context.in_media_query {
|
let reference_size = if context.builder.is_root_element || context.in_media_query {
|
||||||
reference_font_size
|
reference_font_size
|
||||||
} else {
|
} else {
|
||||||
computed::Length::new(context.device().root_font_size().to_f32_px())
|
computed::Length::new(context.device().root_font_size().to_f32_px())
|
||||||
|
@ -255,6 +270,29 @@ impl ViewportPercentageLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
|
||||||
|
use self::ViewportPercentageLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match (self, other) {
|
||||||
|
(&Vw(one), &Vw(other)) => Vw(one + other),
|
||||||
|
(&Vh(one), &Vh(other)) => Vh(one + other),
|
||||||
|
(&Vmin(one), &Vmin(other)) => Vmin(one + other),
|
||||||
|
(&Vmax(one), &Vmax(other)) => Vmax(one + other),
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Vw(..) | Vh(..) | Vmin(..) | Vmax(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot to handle unit in try_sum()")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the given viewport-relative length for the given viewport size.
|
/// Computes the given viewport-relative length for the given viewport size.
|
||||||
pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength {
|
pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> CSSPixelLength {
|
||||||
let (factor, length) = match *self {
|
let (factor, length) = match *self {
|
||||||
|
@ -362,6 +400,12 @@ impl ToComputedValue for AbsoluteLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for AbsoluteLength {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
self.to_px().partial_cmp(&other.to_px())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Mul<CSSFloat> for AbsoluteLength {
|
impl Mul<CSSFloat> for AbsoluteLength {
|
||||||
type Output = AbsoluteLength;
|
type Output = AbsoluteLength;
|
||||||
|
|
||||||
|
@ -476,6 +520,37 @@ impl NoCalcLength {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to sume two lengths if compatible into the left hand side.
|
||||||
|
pub(crate) fn try_sum(&self, other: &Self) -> Result<Self, ()> {
|
||||||
|
use self::NoCalcLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match (self, other) {
|
||||||
|
(&Absolute(ref one), &Absolute(ref other)) => Absolute(*one + *other),
|
||||||
|
(&FontRelative(ref one), &FontRelative(ref other)) => FontRelative(one.try_sum(other)?),
|
||||||
|
(&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
|
||||||
|
ViewportPercentage(one.try_sum(other)?)
|
||||||
|
},
|
||||||
|
(&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
|
||||||
|
ServoCharacterWidth(CharacterWidth(one.0 + other.0))
|
||||||
|
},
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Absolute(..) |
|
||||||
|
FontRelative(..) |
|
||||||
|
ViewportPercentage(..) |
|
||||||
|
ServoCharacterWidth(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot to handle unit in try_sum()")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a px value without context.
|
/// Get a px value without context.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
|
||||||
|
@ -494,6 +569,38 @@ impl NoCalcLength {
|
||||||
|
|
||||||
impl SpecifiedValueInfo for NoCalcLength {}
|
impl SpecifiedValueInfo for NoCalcLength {}
|
||||||
|
|
||||||
|
impl PartialOrd for NoCalcLength {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
use self::NoCalcLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
|
||||||
|
(&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
|
||||||
|
(&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
|
||||||
|
one.partial_cmp(other)
|
||||||
|
},
|
||||||
|
(&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
|
||||||
|
one.0.partial_cmp(&other.0)
|
||||||
|
},
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Absolute(..) |
|
||||||
|
FontRelative(..) |
|
||||||
|
ViewportPercentage(..) |
|
||||||
|
ServoCharacterWidth(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot an arm in partial_cmp?")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Zero for NoCalcLength {
|
impl Zero for NoCalcLength {
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
NoCalcLength::Absolute(AbsoluteLength::Px(0.))
|
NoCalcLength::Absolute(AbsoluteLength::Px(0.))
|
||||||
|
@ -542,6 +649,31 @@ impl Mul<CSSFloat> for Length {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FontRelativeLength {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
use self::FontRelativeLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Em(..) | Ex(..) | Ch(..) | Rem(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot an arm in partial_cmp?")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Mul<CSSFloat> for FontRelativeLength {
|
impl Mul<CSSFloat> for FontRelativeLength {
|
||||||
type Output = FontRelativeLength;
|
type Output = FontRelativeLength;
|
||||||
|
|
||||||
|
@ -570,6 +702,31 @@ impl Mul<CSSFloat> for ViewportPercentageLength {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for ViewportPercentageLength {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
use self::ViewportPercentageLength::*;
|
||||||
|
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
|
||||||
|
(&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
|
||||||
|
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
|
||||||
|
// able to figure it own on its own so we help.
|
||||||
|
_ => unsafe {
|
||||||
|
match *self {
|
||||||
|
Vw(..) | Vh(..) | Vmin(..) | Vmax(..) => {},
|
||||||
|
}
|
||||||
|
debug_unreachable!("Forgot an arm in partial_cmp?")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Length {
|
impl Length {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_internal<'i, 't>(
|
fn parse_internal<'i, 't>(
|
||||||
|
@ -599,11 +756,11 @@ impl Length {
|
||||||
value,
|
value,
|
||||||
))))
|
))))
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input
|
Token::Function(ref name) => {
|
||||||
.parse_nested_block(|input| {
|
let function = CalcNode::math_function(name, location)?;
|
||||||
CalcNode::parse_length(context, input, num_context)
|
let calc = CalcNode::parse_length(context, input, num_context, function)?;
|
||||||
.map(|calc| Length::Calc(Box::new(calc)))
|
Ok(Length::Calc(Box::new(calc)))
|
||||||
}),
|
},
|
||||||
ref token => return Err(location.new_unexpected_token_error(token.clone())),
|
ref token => return Err(location.new_unexpected_token_error(token.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -822,10 +979,10 @@ impl LengthPercentage {
|
||||||
return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
|
return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
let calc = input.parse_nested_block(|i| {
|
let function = CalcNode::math_function(name, location)?;
|
||||||
CalcNode::parse_length_or_percentage(context, i, num_context)
|
let calc =
|
||||||
})?;
|
CalcNode::parse_length_or_percentage(context, input, num_context, function)?;
|
||||||
Ok(LengthPercentage::Calc(Box::new(calc)))
|
Ok(LengthPercentage::Calc(Box::new(calc)))
|
||||||
},
|
},
|
||||||
_ => return Err(location.new_unexpected_token_error(token.clone())),
|
_ => return Err(location.new_unexpected_token_error(token.clone())),
|
||||||
|
|
|
@ -31,7 +31,7 @@ use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKin
|
||||||
pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
|
pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
|
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
|
||||||
pub use self::angle::Angle;
|
pub use self::angle::{AllowUnitlessZeroAngle, Angle};
|
||||||
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
||||||
pub use self::basic_shape::FillRule;
|
pub use self::basic_shape::FillRule;
|
||||||
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||||
|
@ -130,6 +130,47 @@ pub mod transform;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod url;
|
pub mod url;
|
||||||
|
|
||||||
|
/// <angle> | <percentage>
|
||||||
|
/// https://drafts.csswg.org/css-values/#typedef-angle-percentage
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
||||||
|
pub enum AngleOrPercentage {
|
||||||
|
Percentage(Percentage),
|
||||||
|
Angle(Angle),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AngleOrPercentage {
|
||||||
|
fn parse_internal<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
allow_unitless_zero: AllowUnitlessZeroAngle,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
if let Ok(per) = input.try(|i| Percentage::parse(context, i)) {
|
||||||
|
return Ok(AngleOrPercentage::Percentage(per));
|
||||||
|
}
|
||||||
|
|
||||||
|
Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allow unitless angles, used for conic-gradients as specified by the spec.
|
||||||
|
/// https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle
|
||||||
|
pub fn parse_with_unitless<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for AngleOrPercentage {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a `<number>` value, with a given clamping mode.
|
/// Parse a `<number>` value, with a given clamping mode.
|
||||||
fn parse_number_with_clamping_mode<'i, 't>(
|
fn parse_number_with_clamping_mode<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
|
@ -144,8 +185,9 @@ fn parse_number_with_clamping_mode<'i, 't>(
|
||||||
calc_clamping_mode: None,
|
calc_clamping_mode: None,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?;
|
let function = CalcNode::math_function(name, location)?;
|
||||||
|
let result = CalcNode::parse_number(context, input, function)?;
|
||||||
Ok(Number {
|
Ok(Number {
|
||||||
value: result.min(f32::MAX).max(f32::MIN),
|
value: result.min(f32::MAX).max(f32::MIN),
|
||||||
calc_clamping_mode: Some(clamping_mode),
|
calc_clamping_mode: Some(clamping_mode),
|
||||||
|
@ -543,8 +585,9 @@ impl Parse for Integer {
|
||||||
Token::Number {
|
Token::Number {
|
||||||
int_value: Some(v), ..
|
int_value: Some(v), ..
|
||||||
} => Ok(Integer::new(v)),
|
} => Ok(Integer::new(v)),
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?;
|
let function = CalcNode::math_function(name, location)?;
|
||||||
|
let result = CalcNode::parse_integer(context, input, function)?;
|
||||||
Ok(Integer::from_calc(result))
|
Ok(Integer::from_calc(result))
|
||||||
},
|
},
|
||||||
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
||||||
|
@ -559,16 +602,16 @@ impl Integer {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
min: i32,
|
min: i32,
|
||||||
) -> Result<Integer, ParseError<'i>> {
|
) -> Result<Integer, ParseError<'i>> {
|
||||||
match Integer::parse(context, input) {
|
let value = Integer::parse(context, input)?;
|
||||||
// FIXME(emilio): The spec asks us to avoid rejecting it at parse
|
// FIXME(emilio): The spec asks us to avoid rejecting it at parse
|
||||||
// time except until computed value time.
|
// time except until computed value time.
|
||||||
//
|
//
|
||||||
// It's not totally clear it's worth it though, and no other browser
|
// It's not totally clear it's worth it though, and no other browser
|
||||||
// does this.
|
// does this.
|
||||||
Ok(value) if value.value() >= min => Ok(value),
|
if value.value() < min {
|
||||||
Ok(_value) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
}
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a non-negative integer.
|
/// Parse a non-negative integer.
|
||||||
|
@ -767,9 +810,12 @@ impl AllowQuirks {
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
#[css(function)]
|
#[css(function)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Attr {
|
pub struct Attr {
|
||||||
/// Optional namespace prefix and URL.
|
/// Optional namespace prefix.
|
||||||
pub namespace: Option<(Prefix, Namespace)>,
|
pub namespace_prefix: Prefix,
|
||||||
|
/// Optional namespace URL.
|
||||||
|
pub namespace_url: Namespace,
|
||||||
/// Attribute name
|
/// Attribute name
|
||||||
pub attribute: Atom,
|
pub attribute: Atom,
|
||||||
}
|
}
|
||||||
|
@ -814,7 +860,7 @@ impl Attr {
|
||||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix_and_ns = if let Some(ns) = first {
|
let (namespace_prefix, namespace_url) = if let Some(ns) = first {
|
||||||
let prefix = Prefix::from(ns.as_ref());
|
let prefix = Prefix::from(ns.as_ref());
|
||||||
let ns = match get_namespace_for_prefix(&prefix, context) {
|
let ns = match get_namespace_for_prefix(&prefix, context) {
|
||||||
Some(ns) => ns,
|
Some(ns) => ns,
|
||||||
|
@ -823,17 +869,18 @@ impl Attr {
|
||||||
.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Some((prefix, ns))
|
(prefix, ns)
|
||||||
} else {
|
} else {
|
||||||
None
|
(Prefix::default(), Namespace::default())
|
||||||
};
|
};
|
||||||
return Ok(Attr {
|
return Ok(Attr {
|
||||||
namespace: prefix_and_ns,
|
namespace_prefix,
|
||||||
|
namespace_url,
|
||||||
attribute: Atom::from(second_token.as_ref()),
|
attribute: Atom::from(second_token.as_ref()),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// In the case of attr(foobar ) we don't want to error out
|
// In the case of attr(foobar ) we don't want to error out
|
||||||
// because of the trailing whitespace
|
// because of the trailing whitespace.
|
||||||
Token::WhiteSpace(..) => {},
|
Token::WhiteSpace(..) => {},
|
||||||
ref t => return Err(input.new_unexpected_token_error(t.clone())),
|
ref t => return Err(input.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
|
@ -841,7 +888,8 @@ impl Attr {
|
||||||
|
|
||||||
if let Some(first) = first {
|
if let Some(first) = first {
|
||||||
Ok(Attr {
|
Ok(Attr {
|
||||||
namespace: None,
|
namespace_prefix: Prefix::default(),
|
||||||
|
namespace_url: Namespace::default(),
|
||||||
attribute: Atom::from(first.as_ref()),
|
attribute: Atom::from(first.as_ref()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -856,8 +904,8 @@ impl ToCss for Attr {
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
dest.write_str("attr(")?;
|
dest.write_str("attr(")?;
|
||||||
if let Some((ref prefix, ref _url)) = self.namespace {
|
if !self.namespace_prefix.is_empty() {
|
||||||
serialize_atom_identifier(prefix, dest)?;
|
serialize_atom_identifier(&self.namespace_prefix, dest)?;
|
||||||
dest.write_str("|")?;
|
dest.write_str("|")?;
|
||||||
}
|
}
|
||||||
serialize_atom_identifier(&self.attribute, dest)?;
|
serialize_atom_identifier(&self.attribute, dest)?;
|
||||||
|
|
|
@ -117,14 +117,14 @@ impl Percentage {
|
||||||
{
|
{
|
||||||
Ok(Percentage::new(unit_value))
|
Ok(Percentage::new(unit_value))
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
let result =
|
let function = CalcNode::math_function(name, location)?;
|
||||||
input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
|
let value = CalcNode::parse_percentage(context, input, function)?;
|
||||||
|
|
||||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||||
// the clamping mode... I guess we could disallow it...
|
// the clamping mode... I guess we could disallow it...
|
||||||
Ok(Percentage {
|
Ok(Percentage {
|
||||||
value: result,
|
value,
|
||||||
calc_clamping_mode: Some(num_context),
|
calc_clamping_mode: Some(num_context),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,6 +22,7 @@ use cssparser::Parser;
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
use style_traits::values::specified::AllowedNumericType;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
/// The specified value of a CSS `<position>`
|
/// The specified value of a CSS `<position>`
|
||||||
|
@ -297,7 +298,7 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
|
||||||
let p = Percentage(1. - length.percentage());
|
let p = Percentage(1. - length.percentage());
|
||||||
let l = -length.unclamped_length();
|
let l = -length.unclamped_length();
|
||||||
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
// We represent `<end-side> <length>` as `calc(100% - <length>)`.
|
||||||
ComputedLengthPercentage::with_clamping_mode(l, Some(p), length.clamping_mode)
|
ComputedLengthPercentage::new_calc(l, Some(p), AllowedNumericType::All)
|
||||||
},
|
},
|
||||||
PositionComponent::Side(_, Some(ref length)) |
|
PositionComponent::Side(_, Some(ref length)) |
|
||||||
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
PositionComponent::Length(ref length) => length.to_computed_value(context),
|
||||||
|
|
|
@ -21,8 +21,10 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
|
@ -40,7 +42,6 @@ impl SVGPathData {
|
||||||
/// Get the array of PathCommand.
|
/// Get the array of PathCommand.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn commands(&self) -> &[PathCommand] {
|
pub fn commands(&self) -> &[PathCommand] {
|
||||||
debug_assert!(!self.0.is_empty());
|
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +91,6 @@ impl Parse for SVGPathData {
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
let path_string = input.expect_string()?.as_ref();
|
let path_string = input.expect_string()?.as_ref();
|
||||||
if path_string.is_empty() {
|
|
||||||
// Treat an empty string as invalid, so we will not set it.
|
|
||||||
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the svg path string as multiple sub-paths.
|
// Parse the svg path string as multiple sub-paths.
|
||||||
let mut path_parser = PathParser::new(path_string);
|
let mut path_parser = PathParser::new(path_string);
|
||||||
|
@ -156,8 +153,10 @@ impl ComputeSquaredDistance for SVGPathData {
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
|
@ -483,8 +482,10 @@ impl ToCss for PathCommand {
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
|
@ -511,8 +512,10 @@ impl IsAbsolute {
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
Copy,
|
Copy,
|
||||||
Debug,
|
Debug,
|
||||||
|
Deserialize,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToCss,
|
ToCss,
|
||||||
|
@ -530,7 +533,9 @@ impl CoordPair {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The EllipticalArc flag type.
|
/// The EllipticalArc flag type.
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
|
#[derive(
|
||||||
|
Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, SpecifiedValueInfo, ToShmem,
|
||||||
|
)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ArcFlag(bool);
|
pub struct ArcFlag(bool);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ impl ToComputedValue for LineHeight {
|
||||||
let computed_calc =
|
let computed_calc =
|
||||||
calc.to_computed_value_zoomed(context, FontBaseSize::CurrentStyle);
|
calc.to_computed_value_zoomed(context, FontBaseSize::CurrentStyle);
|
||||||
let base = context.style().get_font().clone_font_size().size();
|
let base = context.style().get_font().clone_font_size().size();
|
||||||
computed_calc.percentage_relative_to(base)
|
computed_calc.resolve(base)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
GenericLineHeight::Length(result.into())
|
GenericLineHeight::Length(result.into())
|
||||||
|
@ -596,7 +596,7 @@ impl ToComputedValue for TextAlign {
|
||||||
// In that case, the default behavior here will set it to left,
|
// In that case, the default behavior here will set it to left,
|
||||||
// but we want to set it to right -- instead set it to the default (`start`),
|
// but we want to set it to right -- instead set it to the default (`start`),
|
||||||
// which will do the right thing in this case (but not the general case)
|
// which will do the right thing in this case (but not the general case)
|
||||||
if _context.is_root_element {
|
if _context.builder.is_root_element {
|
||||||
return TextAlignKeyword::Start;
|
return TextAlignKeyword::Start;
|
||||||
}
|
}
|
||||||
let parent = _context
|
let parent = _context
|
||||||
|
@ -1029,8 +1029,8 @@ pub enum TextDecorationSkipInk {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements type for `text-underline-offset` and `text-decoration-thickness` properties
|
/// Implements type for `text-decoration-thickness` property
|
||||||
pub type TextDecorationLength = GenericTextDecorationLength<Length>;
|
pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
|
||||||
|
|
||||||
impl TextDecorationLength {
|
impl TextDecorationLength {
|
||||||
/// `Auto` value.
|
/// `Auto` value.
|
||||||
|
@ -1048,21 +1048,23 @@ impl TextDecorationLength {
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
|
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||||
#[value_info(other_values = "auto,under,left,right")]
|
#[value_info(other_values = "auto,from-font,under,left,right")]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
/// Specified keyword values for the text-underline-position property.
|
/// Specified keyword values for the text-underline-position property.
|
||||||
/// (Non-exclusive, but not all combinations are allowed: only `under` may occur
|
/// (Non-exclusive, but not all combinations are allowed: the spec grammar gives
|
||||||
/// together with either `left` or `right`.)
|
/// `auto | [ from-font | under ] || [ left | right ]`.)
|
||||||
/// https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property
|
/// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property
|
||||||
pub struct TextUnderlinePosition: u8 {
|
pub struct TextUnderlinePosition: u8 {
|
||||||
/// Use automatic positioning below the alphabetic baseline.
|
/// Use automatic positioning below the alphabetic baseline.
|
||||||
const AUTO = 0;
|
const AUTO = 0;
|
||||||
|
/// Use underline position from the first available font.
|
||||||
|
const FROM_FONT = 1 << 0;
|
||||||
/// Below the glyph box.
|
/// Below the glyph box.
|
||||||
const UNDER = 1 << 0;
|
const UNDER = 1 << 1;
|
||||||
/// In vertical mode, place to the left of the text.
|
/// In vertical mode, place to the left of the text.
|
||||||
const LEFT = 1 << 1;
|
const LEFT = 1 << 2;
|
||||||
/// In vertical mode, place to the right of the text.
|
/// In vertical mode, place to the right of the text.
|
||||||
const RIGHT = 1 << 2;
|
const RIGHT = 1 << 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1085,7 +1087,12 @@ impl Parse for TextUnderlinePosition {
|
||||||
"auto" if result.is_empty() => {
|
"auto" if result.is_empty() => {
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
},
|
},
|
||||||
"under" if !result.intersects(TextUnderlinePosition::UNDER) => {
|
"from-font" if !result.intersects(TextUnderlinePosition::FROM_FONT |
|
||||||
|
TextUnderlinePosition::UNDER) => {
|
||||||
|
result.insert(TextUnderlinePosition::FROM_FONT);
|
||||||
|
},
|
||||||
|
"under" if !result.intersects(TextUnderlinePosition::FROM_FONT |
|
||||||
|
TextUnderlinePosition::UNDER) => {
|
||||||
result.insert(TextUnderlinePosition::UNDER);
|
result.insert(TextUnderlinePosition::UNDER);
|
||||||
},
|
},
|
||||||
"left" if !result.intersects(TextUnderlinePosition::LEFT |
|
"left" if !result.intersects(TextUnderlinePosition::LEFT |
|
||||||
|
@ -1131,6 +1138,7 @@ impl ToCss for TextUnderlinePosition {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybe_write!(FROM_FONT => "from-font");
|
||||||
maybe_write!(UNDER => "under");
|
maybe_write!(UNDER => "under");
|
||||||
maybe_write!(LEFT => "left");
|
maybe_write!(LEFT => "left");
|
||||||
maybe_write!(RIGHT => "right");
|
maybe_write!(RIGHT => "right");
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl Time {
|
||||||
/// Returns a `Time` value from a CSS `calc()` expression.
|
/// Returns a `Time` value from a CSS `calc()` expression.
|
||||||
pub fn from_calc(seconds: CSSFloat) -> Self {
|
pub fn from_calc(seconds: CSSFloat) -> Self {
|
||||||
Time {
|
Time {
|
||||||
seconds: seconds,
|
seconds,
|
||||||
unit: TimeUnit::Second,
|
unit: TimeUnit::Second,
|
||||||
was_calc: true,
|
was_calc: true,
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,20 @@ impl Time {
|
||||||
Time::parse_dimension(value, unit, /* from_calc = */ false)
|
Time::parse_dimension(value, unit, /* from_calc = */ false)
|
||||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
},
|
},
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
Token::Function(ref name) => {
|
||||||
match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
|
let function = CalcNode::math_function(name, location)?;
|
||||||
Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time),
|
let time = CalcNode::parse_time(context, input, function)?;
|
||||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
|
||||||
|
// FIXME(emilio): Rejecting calc() at parse time is wrong,
|
||||||
|
// was_calc should probably be replaced by calc_clamping_mode or
|
||||||
|
// something like we do for numbers, or we should do the
|
||||||
|
// clamping here instead (simpler, but technically incorrect,
|
||||||
|
// though still more correct than this!).
|
||||||
|
if !clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) {
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(time)
|
||||||
},
|
},
|
||||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ use synstructure::{Structure, VariantInfo};
|
||||||
|
|
||||||
pub fn derive(mut input: DeriveInput) -> TokenStream {
|
pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
|
let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
|
||||||
|
let input_attrs = cg::parse_input_attrs::<AnimateInputAttrs>(&input);
|
||||||
|
|
||||||
let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
|
let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
|
||||||
let mut where_clause = input.generics.where_clause.take();
|
let mut where_clause = input.generics.where_clause.take();
|
||||||
for param in input.generics.type_params() {
|
for param in input.generics.type_params() {
|
||||||
|
@ -21,39 +23,32 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (mut match_body, append_error_clause) = {
|
let (mut match_body, needs_catchall_branch) = {
|
||||||
let s = Structure::new(&input);
|
let s = Structure::new(&input);
|
||||||
let mut append_error_clause = s.variants().len() > 1;
|
let needs_catchall_branch = s.variants().len() > 1;
|
||||||
|
|
||||||
let match_body = s.variants().iter().fold(quote!(), |body, variant| {
|
let match_body = s.variants().iter().fold(quote!(), |body, variant| {
|
||||||
let arm = match derive_variant_arm(variant, &mut where_clause) {
|
let arm = derive_variant_arm(variant, &mut where_clause);
|
||||||
Ok(arm) => arm,
|
|
||||||
Err(()) => {
|
|
||||||
append_error_clause = true;
|
|
||||||
return body;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
quote! { #body #arm }
|
quote! { #body #arm }
|
||||||
});
|
});
|
||||||
(match_body, append_error_clause)
|
(match_body, needs_catchall_branch)
|
||||||
};
|
};
|
||||||
|
|
||||||
input.generics.where_clause = where_clause;
|
input.generics.where_clause = where_clause;
|
||||||
|
|
||||||
if append_error_clause {
|
if needs_catchall_branch {
|
||||||
let input_attrs = cg::parse_input_attrs::<AnimateInputAttrs>(&input);
|
// This ideally shouldn't be needed, but see
|
||||||
if let Some(fallback) = input_attrs.fallback {
|
// https://github.com/rust-lang/rust/issues/68867
|
||||||
match_body.append_all(quote! {
|
match_body.append_all(quote! { _ => unsafe { debug_unreachable!() } });
|
||||||
(this, other) => #fallback(this, other, procedure)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
match_body.append_all(quote! { _ => Err(()) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = &input.ident;
|
let name = &input.ident;
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
|
let fallback = match input_attrs.fallback {
|
||||||
|
Some(fallback) => quote! { #fallback(self, other, procedure) },
|
||||||
|
None => quote! { Err(()) },
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause {
|
impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause {
|
||||||
#[allow(unused_variables, unused_imports)]
|
#[allow(unused_variables, unused_imports)]
|
||||||
|
@ -63,6 +58,9 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
other: &Self,
|
other: &Self,
|
||||||
procedure: crate::values::animated::Procedure,
|
procedure: crate::values::animated::Procedure,
|
||||||
) -> Result<Self, ()> {
|
) -> Result<Self, ()> {
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return #fallback;
|
||||||
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
#match_body
|
#match_body
|
||||||
}
|
}
|
||||||
|
@ -74,13 +72,17 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
fn derive_variant_arm(
|
fn derive_variant_arm(
|
||||||
variant: &VariantInfo,
|
variant: &VariantInfo,
|
||||||
where_clause: &mut Option<WhereClause>,
|
where_clause: &mut Option<WhereClause>,
|
||||||
) -> Result<TokenStream, ()> {
|
) -> TokenStream {
|
||||||
let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
|
let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
|
||||||
if variant_attrs.error {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
|
let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
|
||||||
let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
|
let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
|
||||||
|
|
||||||
|
if variant_attrs.error {
|
||||||
|
return quote! {
|
||||||
|
(&#this_pattern, &#other_pattern) => Err(()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let (result_value, result_info) = cg::value(&variant, "result");
|
let (result_value, result_info) = cg::value(&variant, "result");
|
||||||
let mut computations = quote!();
|
let mut computations = quote!();
|
||||||
let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
|
let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
|
||||||
|
@ -107,12 +109,13 @@ fn derive_variant_arm(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Ok(quote! {
|
|
||||||
|
quote! {
|
||||||
(&#this_pattern, &#other_pattern) => {
|
(&#this_pattern, &#other_pattern) => {
|
||||||
#computations
|
#computations
|
||||||
Ok(#result_value)
|
Ok(#result_value)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[darling(attributes(animate), default)]
|
#[darling(attributes(animate), default)]
|
||||||
|
|
|
@ -6,11 +6,12 @@ use crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantA
|
||||||
use derive_common::cg;
|
use derive_common::cg;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::TokenStreamExt;
|
use quote::TokenStreamExt;
|
||||||
use syn::{DeriveInput, Path};
|
use syn::{DeriveInput, Path, WhereClause};
|
||||||
use synstructure;
|
use synstructure;
|
||||||
|
|
||||||
pub fn derive(mut input: DeriveInput) -> TokenStream {
|
pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
|
let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
|
||||||
|
let input_attrs = cg::parse_input_attrs::<DistanceInputAttrs>(&input);
|
||||||
let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
|
let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
|
||||||
let mut where_clause = input.generics.where_clause.take();
|
let mut where_clause = input.generics.where_clause.take();
|
||||||
for param in input.generics.type_params() {
|
for param in input.generics.type_params() {
|
||||||
|
@ -22,76 +23,31 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut match_body, append_error_clause) = {
|
let (mut match_body, needs_catchall_branch) = {
|
||||||
let s = synstructure::Structure::new(&input);
|
let s = synstructure::Structure::new(&input);
|
||||||
let mut append_error_clause = s.variants().len() > 1;
|
let needs_catchall_branch = s.variants().len() > 1;
|
||||||
|
|
||||||
let match_body = s.variants().iter().fold(quote!(), |body, variant| {
|
let match_body = s.variants().iter().fold(quote!(), |body, variant| {
|
||||||
let attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
|
let arm = derive_variant_arm(variant, &mut where_clause);
|
||||||
if attrs.error {
|
quote! { #body #arm }
|
||||||
append_error_clause = true;
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
|
|
||||||
let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
|
|
||||||
let sum = if this_info.is_empty() {
|
|
||||||
quote! { crate::values::distance::SquaredDistance::from_sqrt(0.) }
|
|
||||||
} else {
|
|
||||||
let mut sum = quote!();
|
|
||||||
sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {
|
|
||||||
let field_attrs = cg::parse_field_attrs::<DistanceFieldAttrs>(&this.ast());
|
|
||||||
if field_attrs.field_bound {
|
|
||||||
let ty = &this.ast().ty;
|
|
||||||
cg::add_predicate(
|
|
||||||
&mut where_clause,
|
|
||||||
parse_quote!(#ty: crate::values::distance::ComputeSquaredDistance),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let animation_field_attrs =
|
|
||||||
cg::parse_field_attrs::<AnimationFieldAttrs>(&this.ast());
|
|
||||||
|
|
||||||
if animation_field_attrs.constant {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
if #this != #other {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
crate::values::distance::SquaredDistance::from_sqrt(0.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
crate::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), quote!(+));
|
|
||||||
sum
|
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
#body
|
|
||||||
(&#this_pattern, &#other_pattern) => {
|
|
||||||
Ok(#sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
(match_body, append_error_clause)
|
(match_body, needs_catchall_branch)
|
||||||
};
|
};
|
||||||
|
|
||||||
input.generics.where_clause = where_clause;
|
input.generics.where_clause = where_clause;
|
||||||
|
|
||||||
if append_error_clause {
|
if needs_catchall_branch {
|
||||||
let input_attrs = cg::parse_input_attrs::<DistanceInputAttrs>(&input);
|
// This ideally shouldn't be needed, but see:
|
||||||
if let Some(fallback) = input_attrs.fallback {
|
// https://github.com/rust-lang/rust/issues/68867
|
||||||
match_body.append_all(quote! {
|
match_body.append_all(quote! { _ => unsafe { debug_unreachable!() } });
|
||||||
(this, other) => #fallback(this, other)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
match_body.append_all(quote! { _ => Err(()) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fallback = match input_attrs.fallback {
|
||||||
|
Some(fallback) => quote! { #fallback(self, other) },
|
||||||
|
None => quote! { Err(()) },
|
||||||
|
};
|
||||||
|
|
||||||
let name = &input.ident;
|
let name = &input.ident;
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
|
@ -103,6 +59,9 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
&self,
|
&self,
|
||||||
other: &Self,
|
other: &Self,
|
||||||
) -> Result<crate::values::distance::SquaredDistance, ()> {
|
) -> Result<crate::values::distance::SquaredDistance, ()> {
|
||||||
|
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||||
|
return #fallback;
|
||||||
|
}
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
#match_body
|
#match_body
|
||||||
}
|
}
|
||||||
|
@ -111,6 +70,60 @@ pub fn derive(mut input: DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn derive_variant_arm(
|
||||||
|
variant: &synstructure::VariantInfo,
|
||||||
|
mut where_clause: &mut Option<WhereClause>,
|
||||||
|
) -> TokenStream {
|
||||||
|
let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
|
||||||
|
let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
|
||||||
|
let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
|
||||||
|
|
||||||
|
if variant_attrs.error {
|
||||||
|
return quote! {
|
||||||
|
(&#this_pattern, &#other_pattern) => Err(()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum = if this_info.is_empty() {
|
||||||
|
quote! { crate::values::distance::SquaredDistance::from_sqrt(0.) }
|
||||||
|
} else {
|
||||||
|
let mut sum = quote!();
|
||||||
|
sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {
|
||||||
|
let field_attrs = cg::parse_field_attrs::<DistanceFieldAttrs>(&this.ast());
|
||||||
|
if field_attrs.field_bound {
|
||||||
|
let ty = &this.ast().ty;
|
||||||
|
cg::add_predicate(
|
||||||
|
&mut where_clause,
|
||||||
|
parse_quote!(#ty: crate::values::distance::ComputeSquaredDistance),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let animation_field_attrs =
|
||||||
|
cg::parse_field_attrs::<AnimationFieldAttrs>(&this.ast());
|
||||||
|
|
||||||
|
if animation_field_attrs.constant {
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
if #this != #other {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
crate::values::distance::SquaredDistance::from_sqrt(0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
crate::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), quote!(+));
|
||||||
|
sum
|
||||||
|
};
|
||||||
|
|
||||||
|
return quote! {
|
||||||
|
(&#this_pattern, &#other_pattern) => Ok(#sum),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[darling(attributes(distance), default)]
|
#[darling(attributes(distance), default)]
|
||||||
#[derive(Default, FromDeriveInput)]
|
#[derive(Default, FromDeriveInput)]
|
||||||
struct DistanceInputAttrs {
|
struct DistanceInputAttrs {
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
//! A thin atomically-reference-counted slice.
|
//! A thin atomically-reference-counted slice.
|
||||||
|
|
||||||
|
use serde::de::{Deserialize, Deserializer};
|
||||||
|
use serde::ser::{Serialize, Serializer};
|
||||||
use servo_arc::ThinArc;
|
use servo_arc::ThinArc;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
@ -60,6 +62,25 @@ impl<T> Default for ArcSlice<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> Serialize for ArcSlice<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.deref().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice<T> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let r = Vec::deserialize(deserializer)?;
|
||||||
|
Ok(ArcSlice::from_iter(r.into_iter()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> ArcSlice<T> {
|
impl<T> ArcSlice<T> {
|
||||||
/// Creates an Arc for a slice using the given iterator to generate the
|
/// Creates an Arc for a slice using the given iterator to generate the
|
||||||
/// slice.
|
/// slice.
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
//! A replacement for `Box<[T]>` that cbindgen can understand.
|
//! A replacement for `Box<[T]>` that cbindgen can understand.
|
||||||
|
|
||||||
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||||
|
use serde::de::{Deserialize, Deserializer};
|
||||||
|
use serde::ser::{Serialize, Serializer};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
@ -171,3 +173,22 @@ impl<T> iter::FromIterator<T> for OwnedSlice<T> {
|
||||||
Vec::from_iter(iter).into()
|
Vec::from_iter(iter).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> Serialize for OwnedSlice<T> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.deref().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, T: Deserialize<'de>> Deserialize<'de> for OwnedSlice<T> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let r = Box::<[T]>::deserialize(deserializer)?;
|
||||||
|
Ok(r.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,34 @@ impl DerefMut for OwnedStr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OwnedStr {
|
||||||
|
/// Convert the OwnedStr into a boxed str.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_box(self) -> Box<str> {
|
||||||
|
self.into_string().into_boxed_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the OwnedStr into a `String`.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
unsafe { String::from_utf8_unchecked(self.0.into_vec()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OwnedStr> for String {
|
||||||
|
#[inline]
|
||||||
|
fn from(b: OwnedStr) -> Self {
|
||||||
|
b.into_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OwnedStr> for Box<str> {
|
||||||
|
#[inline]
|
||||||
|
fn from(b: OwnedStr) -> Self {
|
||||||
|
b.into_box()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Box<str>> for OwnedStr {
|
impl From<Box<str>> for OwnedStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(b: Box<str>) -> Self {
|
fn from(b: Box<str>) -> Self {
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use cssparser::{Parser, ParserInput};
|
use cssparser::{Parser, ParserInput};
|
||||||
|
use euclid::{Scale, Size2D};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::custom_properties::{
|
use style::custom_properties::{
|
||||||
CssEnvironment, CustomPropertiesBuilder, CustomPropertiesMap, Name, SpecifiedValue,
|
CustomPropertiesBuilder, CustomPropertiesMap, Name, SpecifiedValue,
|
||||||
};
|
};
|
||||||
|
use style::media_queries::{Device, MediaType};
|
||||||
use style::properties::{CustomDeclaration, CustomDeclarationValue};
|
use style::properties::{CustomDeclaration, CustomDeclarationValue};
|
||||||
use style::stylesheets::Origin;
|
use style::stylesheets::Origin;
|
||||||
use test::{self, Bencher};
|
use test::{self, Bencher};
|
||||||
|
@ -26,8 +28,12 @@ fn cascade(
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let env = CssEnvironment;
|
let device = Device::new(
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited, &env);
|
MediaType::screen(),
|
||||||
|
Size2D::new(800., 600.),
|
||||||
|
Scale::new(1.0),
|
||||||
|
);
|
||||||
|
let mut builder = CustomPropertiesBuilder::new(inherited, &device);
|
||||||
|
|
||||||
for declaration in &declarations {
|
for declaration in &declarations {
|
||||||
builder.cascade(declaration, Origin::Author);
|
builder.cascade(declaration, Origin::Author);
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use cssparser::{Parser, ParserInput};
|
use cssparser::{Parser, ParserInput};
|
||||||
use euclid::Scale;
|
use euclid::{Scale, Size2D};
|
||||||
use euclid::Size2D;
|
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_config::set_pref;
|
use servo_config::set_pref;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
|
|
@ -14,3 +14,6 @@
|
||||||
[Property transform-box does not inherit]
|
[Property transform-box does not inherit]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[Property transform-style has initial value auto]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
[calc-serialization-002.html]
|
|
||||||
[testing calc(1em + 1.27cm + 13% + 3em)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[testing calc(25.4q + 1vh + 12%)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue