mirror of
https://github.com/servo/servo.git
synced 2025-09-17 18:38:22 +01:00
script: Implement CSS.registerProperty
(#38682)
The implementation is mostly equivalent to https://searchfox.org/mozilla-central/source/servo/ports/geckolib/glue.rs#9480. Testing: New web platform tests start to pass --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
3a0e8fefde
commit
43da933247
9 changed files with 185 additions and 46 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4736,6 +4736,7 @@ dependencies = [
|
|||
"base",
|
||||
"bitflags 2.9.1",
|
||||
"compositing_traits",
|
||||
"cssparser",
|
||||
"data-url",
|
||||
"embedder_traits",
|
||||
"euclid",
|
||||
|
|
|
@ -22,6 +22,7 @@ atomic_refcell = { workspace = true }
|
|||
base = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
compositing_traits = { workspace = true }
|
||||
cssparser = { workspace = true }
|
||||
data-url = { workspace = true }
|
||||
embedder_traits = { workspace = true }
|
||||
euclid = { workspace = true }
|
||||
|
|
|
@ -17,6 +17,7 @@ use base::id::{PipelineId, WebViewId};
|
|||
use bitflags::bitflags;
|
||||
use compositing_traits::CrossProcessCompositorApi;
|
||||
use compositing_traits::display_list::ScrollType;
|
||||
use cssparser::ParserInput;
|
||||
use embedder_traits::{Theme, ViewportDetails};
|
||||
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
|
||||
use euclid::{Point2D, Scale, Size2D};
|
||||
|
@ -27,9 +28,9 @@ use fxhash::FxHashMap;
|
|||
use ipc_channel::ipc::IpcSender;
|
||||
use layout_api::wrapper_traits::LayoutNode;
|
||||
use layout_api::{
|
||||
IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory, OffsetParentResponse, QueryMsg,
|
||||
ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, ReflowResult,
|
||||
TrustedNodeAddress,
|
||||
IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory, OffsetParentResponse,
|
||||
PropertyRegistration, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
|
||||
ReflowRequestRestyle, ReflowResult, RegisterPropertyError, TrustedNodeAddress,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||
|
@ -50,6 +51,7 @@ use style::animation::DocumentAnimationSet;
|
|||
use style::context::{
|
||||
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
|
||||
};
|
||||
use style::custom_properties::{SpecifiedValue, parse_name};
|
||||
use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
|
||||
use style::error_reporting::RustLogReporter;
|
||||
use style::font_metrics::FontMetrics;
|
||||
|
@ -58,6 +60,11 @@ use style::invalidation::element::restyle_hints::RestyleHint;
|
|||
use style::media_queries::{Device, MediaList, MediaType};
|
||||
use style::properties::style_structs::Font;
|
||||
use style::properties::{ComputedValues, PropertyId};
|
||||
use style::properties_and_values::registry::{
|
||||
PropertyRegistration as StyloPropertyRegistration, PropertyRegistrationData,
|
||||
};
|
||||
use style::properties_and_values::rule::{Inherits, PropertyRegistrationError, PropertyRuleName};
|
||||
use style::properties_and_values::syntax::Descriptor;
|
||||
use style::queries::values::PrefersColorScheme;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
|
||||
use style::servo::media_queries::FontMetricsProvider;
|
||||
|
@ -72,6 +79,7 @@ use style::traversal_flags::TraversalFlags;
|
|||
use style::values::computed::font::GenericFontFamily;
|
||||
use style::values::computed::{CSSPixelLength, FontSize, Length, NonNegativeLength};
|
||||
use style::values::specified::font::{KeywordInfo, QueryFontMetricsFlags};
|
||||
use style::values::{Parser, SourceLocation};
|
||||
use style::{Zero, driver};
|
||||
use style_traits::{CSSPixel, SpeculativePainter};
|
||||
use stylo_atoms::Atom;
|
||||
|
@ -510,6 +518,92 @@ impl Layout for LayoutThread {
|
|||
fn needs_new_display_list(&self) -> bool {
|
||||
self.need_new_display_list.get()
|
||||
}
|
||||
|
||||
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>
|
||||
fn register_custom_property(
|
||||
&mut self,
|
||||
property_registration: PropertyRegistration,
|
||||
) -> Result<(), RegisterPropertyError> {
|
||||
// Step 2. If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
|
||||
// If property set already contains an entry with name as its property name
|
||||
// (compared codepoint-wise), throw an InvalidModificationError and exit this algorithm.
|
||||
let Ok(name) = parse_name(&property_registration.name) else {
|
||||
return Err(RegisterPropertyError::InvalidName);
|
||||
};
|
||||
let name = Atom::from(name);
|
||||
|
||||
if self
|
||||
.stylist
|
||||
.custom_property_script_registry()
|
||||
.get(&name)
|
||||
.is_some()
|
||||
{
|
||||
return Err(RegisterPropertyError::AlreadyRegistered);
|
||||
}
|
||||
|
||||
// Step 3. Attempt to consume a syntax definition from syntax. If it returns failure,
|
||||
// throw a SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
|
||||
let syntax = Descriptor::from_str(&property_registration.syntax, false)
|
||||
.map_err(|_| RegisterPropertyError::InvalidSyntax)?;
|
||||
|
||||
// Step 4 - Parse and validate initial value
|
||||
let initial_value = match property_registration.initial_value {
|
||||
Some(value) => {
|
||||
let mut input = ParserInput::new(&value);
|
||||
let parsed = Parser::new(&mut input)
|
||||
.parse_entirely(|input| {
|
||||
input.skip_whitespace();
|
||||
SpecifiedValue::parse(input, &property_registration.url_data)
|
||||
.map(servo_arc::Arc::new)
|
||||
})
|
||||
.ok();
|
||||
if parsed.is_none() {
|
||||
return Err(RegisterPropertyError::InvalidInitialValue);
|
||||
}
|
||||
parsed
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
StyloPropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
|
||||
.map_err(|error| match error {
|
||||
PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
|
||||
RegisterPropertyError::InitialValueNotComputationallyIndependent
|
||||
},
|
||||
PropertyRegistrationError::InvalidInitialValue => {
|
||||
RegisterPropertyError::InvalidInitialValue
|
||||
},
|
||||
PropertyRegistrationError::NoInitialValue => RegisterPropertyError::NoInitialValue,
|
||||
})?;
|
||||
|
||||
// Step 5. Set inherit flag to the value of inherits.
|
||||
let inherits = if property_registration.inherits {
|
||||
Inherits::True
|
||||
} else {
|
||||
Inherits::False
|
||||
};
|
||||
|
||||
// Step 6. Let registered property be a struct with a property name of name, a syntax of
|
||||
// syntax definition, an initial value of parsed initial value, and an inherit flag of inherit flag.
|
||||
// Append registered property to property set.
|
||||
let property_registration = StyloPropertyRegistration {
|
||||
name: PropertyRuleName(name),
|
||||
data: PropertyRegistrationData {
|
||||
syntax,
|
||||
initial_value,
|
||||
inherits,
|
||||
},
|
||||
url_data: property_registration.url_data,
|
||||
source_location: SourceLocation { line: 0, column: 0 },
|
||||
};
|
||||
|
||||
self.stylist
|
||||
.custom_property_script_registry_mut()
|
||||
.register(property_registration);
|
||||
self.stylist.rebuild_initial_values_for_custom_properties();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutThread {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
use cssparser::{Parser, ParserInput, serialize_identifier};
|
||||
use dom_struct::dom_struct;
|
||||
use layout_api::{PropertyRegistration, RegisterPropertyError};
|
||||
use script_bindings::codegen::GenericBindings::CSSBinding::PropertyDefinition;
|
||||
use style::context::QuirksMode;
|
||||
use style::parser::ParserContext;
|
||||
use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration};
|
||||
|
@ -12,7 +14,7 @@ use style_traits::ParsingMode;
|
|||
|
||||
use crate::dom::bindings::codegen::Bindings::CSSBinding::CSSMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
|
||||
use crate::dom::bindings::error::Fallible;
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::reflector::Reflector;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
|
@ -81,4 +83,32 @@ impl CSSMethods<crate::DomTypeHolder> for CSS {
|
|||
fn PaintWorklet(win: &Window) -> DomRoot<Worklet> {
|
||||
win.paint_worklet()
|
||||
}
|
||||
|
||||
/// <https://drafts.css-houdini.org/css-properties-values-api/#the-registerproperty-function>
|
||||
fn RegisterProperty(window: &Window, property_definition: &PropertyDefinition) -> Fallible<()> {
|
||||
let property_registration = PropertyRegistration {
|
||||
name: property_definition.name.str().to_owned(),
|
||||
inherits: property_definition.inherits,
|
||||
url_data: UrlExtraData(window.get_url().get_arc()),
|
||||
initial_value: property_definition
|
||||
.initialValue
|
||||
.as_ref()
|
||||
.map(|value| value.str().to_owned()),
|
||||
syntax: property_definition.syntax.str().to_owned(),
|
||||
};
|
||||
|
||||
window
|
||||
.layout_mut()
|
||||
.register_custom_property(property_registration)
|
||||
.map_err(|error| match error {
|
||||
RegisterPropertyError::InvalidName |
|
||||
RegisterPropertyError::InvalidSyntax |
|
||||
RegisterPropertyError::InvalidInitialValue |
|
||||
RegisterPropertyError::NoInitialValue |
|
||||
RegisterPropertyError::InitialValueNotComputationallyIndependent => Error::Syntax,
|
||||
RegisterPropertyError::AlreadyRegistered => Error::InvalidModification,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@ partial namespace CSS {
|
|||
};
|
||||
|
||||
// https://drafts.css-houdini.org/css-properties-values-api/#the-registerproperty-function
|
||||
// dictionary PropertyDefinition {
|
||||
// required DOMString name;
|
||||
// DOMString syntax = "*";
|
||||
// required boolean inherits;
|
||||
// DOMString initialValue;
|
||||
// };
|
||||
dictionary PropertyDefinition {
|
||||
required DOMString name;
|
||||
DOMString syntax = "*";
|
||||
required boolean inherits;
|
||||
DOMString initialValue;
|
||||
};
|
||||
|
||||
// partial namespace CSS {
|
||||
// undefined registerProperty(PropertyDefinition definition);
|
||||
// };
|
||||
partial namespace CSS {
|
||||
[Throws] undefined registerProperty(PropertyDefinition definition);
|
||||
};
|
||||
|
|
|
@ -54,7 +54,7 @@ use style::media_queries::Device;
|
|||
use style::properties::PropertyId;
|
||||
use style::properties::style_structs::Font;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
||||
use style::stylesheets::Stylesheet;
|
||||
use style::stylesheets::{Stylesheet, UrlExtraData};
|
||||
use style_traits::CSSPixel;
|
||||
use webrender_api::units::{DeviceIntSize, LayoutPoint, LayoutVector2D};
|
||||
use webrender_api::{ExternalScrollId, ImageKey};
|
||||
|
@ -206,6 +206,24 @@ pub struct LayoutConfig {
|
|||
pub theme: Theme,
|
||||
}
|
||||
|
||||
pub struct PropertyRegistration {
|
||||
pub name: String,
|
||||
pub syntax: String,
|
||||
pub initial_value: Option<String>,
|
||||
pub inherits: bool,
|
||||
pub url_data: UrlExtraData,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RegisterPropertyError {
|
||||
InvalidName,
|
||||
AlreadyRegistered,
|
||||
InvalidSyntax,
|
||||
InvalidInitialValue,
|
||||
InitialValueNotComputationallyIndependent,
|
||||
NoInitialValue,
|
||||
}
|
||||
|
||||
pub trait LayoutFactory: Send + Sync {
|
||||
fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
|
||||
}
|
||||
|
@ -299,6 +317,10 @@ pub trait Layout {
|
|||
point: LayoutPoint,
|
||||
flags: ElementsFromPointFlags,
|
||||
) -> Vec<ElementsFromPointResult>;
|
||||
fn register_custom_property(
|
||||
&mut self,
|
||||
property_registration: PropertyRegistration,
|
||||
) -> Result<(), RegisterPropertyError>;
|
||||
}
|
||||
|
||||
/// This trait is part of `layout_api` because it depends on both `script_traits`
|
||||
|
|
|
@ -1,17 +1,6 @@
|
|||
[animation-important-001.html]
|
||||
expected: ERROR
|
||||
[Interpolated value is observable]
|
||||
[Standard property animations appearing via setKeyframes do not override important declarations]
|
||||
expected: FAIL
|
||||
|
||||
[Important rules override animations (::before)]
|
||||
[Custom property animations appearing via setKeyframes do not override important declarations]
|
||||
expected: FAIL
|
||||
|
||||
[Non-overriden interpolations are observable]
|
||||
expected: FAIL
|
||||
|
||||
[Important rules do not override animations on :visited as seen from JS]
|
||||
expected: FAIL
|
||||
|
||||
[Important rules override animations]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
[container-units-computational-independence.html]
|
||||
[Container relative unit cqw is not computationally independent]
|
||||
expected: FAIL
|
||||
|
||||
[Container relative unit cqh is not computationally independent]
|
||||
expected: FAIL
|
||||
|
||||
[Container relative unit cqi is not computationally independent]
|
||||
expected: FAIL
|
||||
|
||||
[Container relative unit cqb is not computationally independent]
|
||||
expected: FAIL
|
||||
|
||||
[Container relative unit cqmin is not computationally independent]
|
||||
expected: FAIL
|
||||
|
||||
[Container relative unit cqmax is not computationally independent]
|
||||
expected: FAIL
|
|
@ -1,5 +1,4 @@
|
|||
[attr-security.html]
|
||||
expected: ERROR
|
||||
[CSS Values and Units Test: attr() security limitations]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -74,3 +73,24 @@
|
|||
|
||||
['--x: image-set(var(--some-string-list))' with data-foo="https://does-not-exist.test/404.png"]
|
||||
expected: FAIL
|
||||
|
||||
['--registered-color: attr(data-foo type(<color>))' with data-foo="blue"]
|
||||
expected: FAIL
|
||||
|
||||
['--x: image-set(var(--some-other-url))' with data-foo="https://does-not-exist.test/404.png"]
|
||||
expected: FAIL
|
||||
|
||||
['--x: image-set(if(style(--true): attr(data-foo);))' with data-foo="https://does-not-exist.test/404.png"]
|
||||
expected: FAIL
|
||||
|
||||
['background-image: image-set(\n if(style(--true): url(https://does-not-exist-2.test/404.png);\n else: attr(data-foo);))' with data-foo="https://does-not-exist-2.test/404.png"]
|
||||
expected: FAIL
|
||||
|
||||
['background-image: image-set(if(style(--true): url(https://does-not-exist.test/404.png);\n style(--condition-val): url(https://does-not-exist.test/404.png);\n else: url(https://does-not-exist.test/404.png);))' with data-foo="attr(data-foo type(*))"]
|
||||
expected: FAIL
|
||||
|
||||
['--x: image-set(if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))' with data-foo="3"]
|
||||
expected: FAIL
|
||||
|
||||
['--x: image-set(if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))' with data-foo="3"]
|
||||
expected: FAIL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue