Bug 1898887. Implement parsing of CSS 'inset-area' property. r=emilio,firefox-style-system-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D211955
This commit is contained in:
Jonathan Watt 2024-05-30 19:39:07 +00:00
parent d4a8bd78d9
commit 745b6de9a7
13 changed files with 484 additions and 8 deletions

View File

@ -98,6 +98,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"ime-mode", "ime-mode",
"-moz-inert", "-moz-inert",
"initial-letter", "initial-letter",
"inset-area",
"isolation", "isolation",
"justify-content", "justify-content",
"justify-items", "justify-items",

View File

@ -107,7 +107,7 @@ use.counter:
send_in_pings: send_in_pings:
- use-counters - use-counters
# Total of 2329 use counter metrics (excludes denominators). # Total of 2343 use counter metrics (excludes denominators).
# Total of 364 'page' use counters. # Total of 364 'page' use counters.
use.counter.page: use.counter.page:
svgsvgelement_getelementbyid: svgsvgelement_getelementbyid:
@ -14177,7 +14177,7 @@ use.counter.worker.service:
send_in_pings: send_in_pings:
- use-counters - use-counters
# Total of 50 'deprecated operations (page)' use counters. # Total of 56 'deprecated operations (page)' use counters.
use.counter.deprecated_ops.page: use.counter.deprecated_ops.page:
domsubtree_modified: domsubtree_modified:
type: counter type: counter
@ -15131,7 +15131,7 @@ use.counter.deprecated_ops.page:
send_in_pings: send_in_pings:
- use-counters - use-counters
# Total of 50 'deprecated operations (document)' use counters. # Total of 56 'deprecated operations (document)' use counters.
use.counter.deprecated_ops.doc: use.counter.deprecated_ops.doc:
domsubtree_modified: domsubtree_modified:
type: counter type: counter
@ -16085,7 +16085,7 @@ use.counter.deprecated_ops.doc:
send_in_pings: send_in_pings:
- use-counters - use-counters
# Total of 701 'CSS (page)' use counters. # Total of 702 'CSS (page)' use counters.
use.counter.css.page: use.counter.css.page:
css_align_content: css_align_content:
type: counter type: counter
@ -16903,6 +16903,23 @@ use.counter.css.page:
send_in_pings: send_in_pings:
- use-counters - use-counters
css_inset_area:
type: counter
description: >
Whether a page used the CSS property inset-area.
Compare against `use.counter.top_level_content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_isolation: css_isolation:
type: counter type: counter
description: > description: >
@ -28004,7 +28021,7 @@ use.counter.css.page:
send_in_pings: send_in_pings:
- use-counters - use-counters
# Total of 701 'CSS (document)' use counters. # Total of 702 'CSS (document)' use counters.
use.counter.css.doc: use.counter.css.doc:
css_align_content: css_align_content:
type: counter type: counter
@ -28822,6 +28839,23 @@ use.counter.css.doc:
send_in_pings: send_in_pings:
- use-counters - use-counters
css_inset_area:
type: counter
description: >
Whether a document used the CSS property inset-area.
Compare against `use.counter.content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
css_isolation: css_isolation:
type: counter type: counter
description: > description: >
@ -39922,4 +39956,3 @@ use.counter.css.doc:
expires: never expires: never
send_in_pings: send_in_pings:
- use-counters - use-counters

View File

@ -627,6 +627,7 @@ cbindgen-types = [
{ gecko = "StyleAnchorScope", servo = "crate::values::computed::position::AnchorScope" }, { gecko = "StyleAnchorScope", servo = "crate::values::computed::position::AnchorScope" },
{ gecko = "StylePositionAnchor", servo = "crate::values::computed::position::PositionAnchor" }, { gecko = "StylePositionAnchor", servo = "crate::values::computed::position::PositionAnchor" },
{ gecko = "StylePositionVisibility", servo = "crate::values::computed::position::PositionVisibility" }, { gecko = "StylePositionVisibility", servo = "crate::values::computed::position::PositionVisibility" },
{ gecko = "StyleInsetArea", servo = "crate::values::computed::position::InsetArea" },
{ gecko = "StyleFontVariantEastAsian", servo = "crate::values::computed::font::FontVariantEastAsian" }, { gecko = "StyleFontVariantEastAsian", servo = "crate::values::computed::font::FontVariantEastAsian" },
{ gecko = "StyleFontVariantLigatures", servo = "crate::values::computed::font::FontVariantLigatures" }, { gecko = "StyleFontVariantLigatures", servo = "crate::values::computed::font::FontVariantLigatures" },
{ gecko = "StyleFontVariantNumeric", servo = "crate::values::computed::font::FontVariantNumeric" }, { gecko = "StyleFontVariantNumeric", servo = "crate::values::computed::font::FontVariantNumeric" },

View File

@ -1051,6 +1051,8 @@ nsStylePosition::nsStylePosition()
mMaxHeight(StyleMaxSize::None()), mMaxHeight(StyleMaxSize::None()),
mPositionAnchor(StylePositionAnchor::Auto()), mPositionAnchor(StylePositionAnchor::Auto()),
mPositionVisibility(StylePositionVisibility::ALWAYS), mPositionVisibility(StylePositionVisibility::ALWAYS),
mInsetArea(StyleInsetArea{StyleInsetAreaKeyword::None,
StyleInsetAreaKeyword::None}),
mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())), mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
mAspectRatio(StyleAspectRatio::Auto()), mAspectRatio(StyleAspectRatio::Auto()),
mGridAutoFlow(StyleGridAutoFlow::ROW), mGridAutoFlow(StyleGridAutoFlow::ROW),
@ -1101,6 +1103,7 @@ nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
mMaxHeight(aSource.mMaxHeight), mMaxHeight(aSource.mMaxHeight),
mPositionAnchor(aSource.mPositionAnchor), mPositionAnchor(aSource.mPositionAnchor),
mPositionVisibility(aSource.mPositionVisibility), mPositionVisibility(aSource.mPositionVisibility),
mInsetArea(aSource.mInsetArea),
mFlexBasis(aSource.mFlexBasis), mFlexBasis(aSource.mFlexBasis),
mGridAutoColumns(aSource.mGridAutoColumns), mGridAutoColumns(aSource.mGridAutoColumns),
mGridAutoRows(aSource.mGridAutoRows), mGridAutoRows(aSource.mGridAutoRows),
@ -1285,7 +1288,8 @@ nsChangeHint nsStylePosition::CalcDifference(
hint |= nsChangeHint_NeutralChange; hint |= nsChangeHint_NeutralChange;
} }
if (mPositionVisibility != aNewData.mPositionVisibility) { if (mPositionVisibility != aNewData.mPositionVisibility ||
mInsetArea != aNewData.mInsetArea) {
hint |= nsChangeHint_NeutralChange; hint |= nsChangeHint_NeutralChange;
} }

View File

@ -747,6 +747,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition {
// element. // element.
mozilla::StylePositionAnchor mPositionAnchor; mozilla::StylePositionAnchor mPositionAnchor;
mozilla::StylePositionVisibility mPositionVisibility; mozilla::StylePositionVisibility mPositionVisibility;
mozilla::StyleInsetArea mInsetArea;
mozilla::StyleFlexBasis mFlexBasis; mozilla::StyleFlexBasis mFlexBasis;
StyleImplicitGridTracks mGridAutoColumns; StyleImplicitGridTracks mGridAutoColumns;

View File

@ -13338,6 +13338,85 @@ if (IsCSSPropertyPrefEnabled("layout.css.anchor-positioning.enabled")) {
], ],
}; };
gCSSProperties["inset-area"] = {
domProp: "insetArea",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: ["none"],
other_values: [
"center",
"span-all",
"left",
"right",
"span-left",
"span-right",
"x-start",
"x-end",
"span-x-start",
"span-x-end",
"x-self-start",
"x-self-end",
"span-x-self-start",
"span-x-self-end",
"top",
"bottom",
"span-top",
"span-bottom",
"y-start",
"y-end",
"span-y-start",
"span-y-end",
"y-self-start",
"y-self-end",
"span-y-self-start",
"span-y-self-end",
"block-start",
"block-end",
"span-block-start",
"span-block-end",
"inline-start",
"inline-end",
"span-inline-start",
"span-inline-end",
"self-block-start",
"self-block-end",
"span-self-block-start",
"span-self-block-end",
"self-inline-start",
"self-inline-end",
"span-self-inline-start",
"span-self-inline-end",
"start",
"end",
"span-start",
"span-end",
"self-start",
"self-end",
"span-self-start",
"span-self-end",
"center span-all",
"left center",
"span-left bottom",
"span-block-end inline-start",
"span-inline-start block-end",
"self-block-end span-self-inline-start",
"start center",
"span-start span-end",
"self-end span-self-start",
],
invalid_values: [
"auto",
"none left",
"left self-top",
"right block-end",
"top self-end",
"y-self-end x-end",
"inline-start self-block-end",
"span-self-inline-start start",
"end span-self-start",
],
};
gCSSProperties["position-anchor"] = { gCSSProperties["position-anchor"] = {
domProp: "positionAnchor", domProp: "positionAnchor",
inherited: false, inherited: false,

View File

@ -566,6 +566,8 @@ class Longhand(Property):
"ImageRendering", "ImageRendering",
"InitialLetter", "InitialLetter",
"Integer", "Integer",
"InsetArea",
"InsetAreaKeyword",
"JustifyContent", "JustifyContent",
"JustifyItems", "JustifyItems",
"JustifySelf", "JustifySelf",

View File

@ -354,6 +354,18 @@ ${helpers.predefined_type(
affects="layout", affects="layout",
)} )}
${helpers.predefined_type(
"inset-area",
"InsetArea",
"computed::InsetArea::none()",
engines="gecko",
initial_specified_value="specified::InsetArea::none()",
animation_value_type="discrete",
gecko_pref="layout.css.anchor-positioning.enabled",
spec="https://drafts.csswg.org/css-anchor-position-1/#typedef-inset-area",
affects="layout",
)}
${helpers.single_keyword( ${helpers.single_keyword(
"box-sizing", "box-sizing",
"content-box border-box", "content-box border-box",

View File

@ -92,6 +92,7 @@ pub use self::position::AnchorName;
pub use self::position::AnchorScope; pub use self::position::AnchorScope;
pub use self::position::PositionAnchor; pub use self::position::PositionAnchor;
pub use self::position::PositionVisibility; pub use self::position::PositionVisibility;
pub use self::position::{InsetArea, InsetAreaKeyword};
pub use self::position::AspectRatio; pub use self::position::AspectRatio;
pub use self::position::{ pub use self::position::{
GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex, GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,

View File

@ -14,7 +14,7 @@ use crate::values::generics::position::PositionComponent as GenericPositionCompo
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto; use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex; use crate::values::generics::position::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow}; pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow};
pub use crate::values::specified::position::{AnchorName, AnchorScope, PositionAnchor, PositionVisibility}; pub use crate::values::specified::position::{AnchorName, AnchorScope, InsetArea, InsetAreaKeyword, PositionAnchor, PositionVisibility};
use crate::Zero; use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};

View File

@ -83,6 +83,7 @@ pub use self::percentage::{NonNegativePercentage, Percentage};
pub use self::position::AspectRatio; pub use self::position::AspectRatio;
pub use self::position::AnchorName; pub use self::position::AnchorName;
pub use self::position::AnchorScope; pub use self::position::AnchorScope;
pub use self::position::{InsetArea, InsetAreaKeyword};
pub use self::position::PositionAnchor; pub use self::position::PositionAnchor;
pub use self::position::PositionVisibility; pub use self::position::PositionVisibility;
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto}; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};

View File

@ -535,6 +535,345 @@ impl PositionVisibility {
} }
} }
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
#[repr(u8)]
/// Possible values for the `inset-area` preperty's keywords.
/// https://drafts.csswg.org/css-anchor-position-1/#propdef-inset-area
pub enum InsetAreaKeyword {
None,
// Common (shared) keywords:
Center,
SpanAll,
// Horizontal keywords:
Left,
Right,
SpanLeft,
SpanRight,
XStart,
XEnd,
SpanXStart,
SpanXEnd,
XSelfStart,
XSelfEnd,
SpanXSelfStart,
SpanXSelfEnd,
// Vertical keywords:
Top,
Bottom,
SpanTop,
SpanBottom,
YStart,
YEnd,
SpanYStart,
SpanYEnd,
YSelfStart,
YSelfEnd,
SpanYSelfStart,
SpanYSelfEnd,
// Block keywords:
BlockStart,
BlockEnd,
SpanBlockStart,
SpanBlockEnd,
// Inline keywords:
InlineStart,
InlineEnd,
SpanInlineStart,
SpanInlineEnd,
// "Self" block keywords:
SelfBlockStart,
SelfBlockEnd,
SpanSelfBlockStart,
SpanSelfBlockEnd,
// "Self" inline keywords:
SelfInlineStart,
SelfInlineEnd,
SpanSelfInlineStart,
SpanSelfInlineEnd,
// Inferred axis keywords:
Start,
End,
SpanStart,
SpanEnd,
// "Self" inferred axis keywords:
SelfStart,
SelfEnd,
SpanSelfStart,
SpanSelfEnd,
}
impl Default for InsetAreaKeyword {
fn default() -> Self {
Self::None
}
}
#[allow(missing_docs)]
impl InsetAreaKeyword {
#[inline]
pub fn none() -> Self {
Self::None
}
pub fn is_none(&self) -> bool {
*self == Self::None
}
/// Is a value that's common to all compatible keyword groupings.
pub fn is_common(&self) -> bool {
*self == Self::Center || *self == Self::SpanAll
}
pub fn is_horizontal(&self) -> bool {
matches!(
self,
Self::Left |
Self::Right |
Self::SpanLeft |
Self::SpanRight |
Self::XStart |
Self::XEnd |
Self::SpanXStart |
Self::SpanXEnd |
Self::XSelfStart |
Self::XSelfEnd |
Self::SpanXSelfStart |
Self::SpanXSelfEnd
)
}
pub fn is_vertical(&self) -> bool {
matches!(
self,
Self::Top |
Self::Bottom |
Self::SpanTop |
Self::SpanBottom |
Self::YStart |
Self::YEnd |
Self::SpanYStart |
Self::SpanYEnd |
Self::YSelfStart |
Self::YSelfEnd |
Self::SpanYSelfStart |
Self::SpanYSelfEnd
)
}
pub fn is_block(&self) -> bool {
matches!(
self,
Self::BlockStart | Self::BlockEnd | Self::SpanBlockStart | Self::SpanBlockEnd
)
}
pub fn is_inline(&self) -> bool {
matches!(
self,
Self::InlineStart | Self::InlineEnd | Self::SpanInlineStart | Self::SpanInlineEnd
)
}
pub fn is_self_block(&self) -> bool {
matches!(
self,
Self::SelfBlockStart |
Self::SelfBlockEnd |
Self::SpanSelfBlockStart |
Self::SpanSelfBlockEnd
)
}
pub fn is_self_inline(&self) -> bool {
matches!(
self,
Self::SelfInlineStart |
Self::SelfInlineEnd |
Self::SpanSelfInlineStart |
Self::SpanSelfInlineEnd
)
}
pub fn is_inferred_logical(&self) -> bool {
matches!(
self,
Self::Start | Self::End | Self::SpanStart | Self::SpanEnd
)
}
pub fn is_self_inferred_logical(&self) -> bool {
matches!(
self,
Self::SelfStart | Self::SelfEnd | Self::SpanSelfStart | Self::SpanSelfEnd
)
}
}
#[inline]
fn is_compatible_pairing(first: InsetAreaKeyword, second: InsetAreaKeyword) -> bool {
if first.is_none() || second.is_none() {
// `none` is not allowed as one of the keywords when two keywords are
// provided.
return false;
}
if first.is_common() || second.is_common() {
return true;
}
if first.is_horizontal() {
return second.is_vertical();
}
if first.is_vertical() {
return second.is_horizontal();
}
if first.is_block() {
return second.is_inline();
}
if first.is_inline() {
return second.is_block();
}
if first.is_self_block() {
return second.is_self_inline();
}
if first.is_self_inline() {
return second.is_self_block();
}
if first.is_inferred_logical() {
return second.is_inferred_logical();
}
if first.is_self_inferred_logical() {
return second.is_self_inferred_logical();
}
debug_assert!(false, "Not reached");
// Return false to increase the chances of this being reported to us if we
// ever were to get here.
false
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
/// https://drafts.csswg.org/css-anchor-position-1/#propdef-inset-area
pub struct InsetArea {
/// First keyword, if any.
pub first: InsetAreaKeyword,
/// Second keyword, if any.
#[css(skip_if = "InsetAreaKeyword::is_none")]
pub second: InsetAreaKeyword,
}
#[allow(missing_docs)]
impl InsetArea {
#[inline]
pub fn none() -> Self {
Self {
first: InsetAreaKeyword::None,
second: InsetAreaKeyword::None,
}
}
#[inline]
pub fn is_none(&self) -> bool {
self.first.is_none()
}
}
impl Parse for InsetArea {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let mut first = InsetAreaKeyword::parse(input)?;
if first.is_none() {
return Ok(Self::none());
}
let location = input.current_source_location();
let second = input.try_parse(InsetAreaKeyword::parse);
if let Ok(InsetAreaKeyword::None) = second {
// `none` is only allowed as a single value
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let mut second = second.unwrap_or(InsetAreaKeyword::None);
if second.is_none() {
// Either there was no second keyword and try_parse returned a
// BasicParseErrorKind::EndOfInput, or else the second "keyword"
// was invalid. We assume the former case here, and if it's the
// latter case then our caller detects the error (try_parse will,
// have rewound, leaving an unparsed token).
return Ok(Self { first, second });
}
if !is_compatible_pairing(first, second) {
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// Normalize by applying the shortest serialization principle:
// https://drafts.csswg.org/cssom/#serializing-css-values
if first.is_inferred_logical() ||
second.is_inferred_logical() ||
first.is_self_inferred_logical() ||
second.is_self_inferred_logical() ||
(first.is_common() && second.is_common())
{
// In these cases we must not change the order of the keywords
// since their meaning is inferred from their order. However, if
// both keywords are the same, only one should be set.
if first == second {
second = InsetAreaKeyword::None;
}
} else if second == InsetAreaKeyword::SpanAll {
// Span-all is the default behavior, so specifying `span-all` is
// superfluous.
second = InsetAreaKeyword::None;
} else if first == InsetAreaKeyword::SpanAll {
// Same here, but the non-superfluous keyword must come first.
first = second;
second = InsetAreaKeyword::None;
} else if first.is_vertical() ||
second.is_horizontal() ||
first.is_inline() ||
second.is_block() ||
first.is_self_inline() ||
second.is_self_block()
{
// Canonical order is horizontal before vertical, block before inline.
std::mem::swap(&mut first, &mut second);
}
Ok(Self { first, second })
}
}
/// Represents a side, either horizontal or vertical, of a CSS position. /// Represents a side, either horizontal or vertical, of a CSS position.
pub trait Side { pub trait Side {
/// Returns the start side. /// Returns the start side.

View File

@ -105,6 +105,8 @@ include = [
"FontVariantEastAsian", "FontVariantEastAsian",
"FontVariantLigatures", "FontVariantLigatures",
"FontVariantNumeric", "FontVariantNumeric",
"InsetArea",
"InsetAreaKeyword",
"ComputedFontStretchRange", "ComputedFontStretchRange",
"ComputedFontStyleDescriptor", "ComputedFontStyleDescriptor",
"ComputedFontWeightRange", "ComputedFontWeightRange",