Ident(String), Function(String), AtKeyword(String), Hash(String, HashType),
String(String), BadString, Url(String), BadUrl,
Delim(char), Number(f64, NumType), Percentage(f64), Dimension(f64, String),
Whitespace, Cdo, Cdc, Colon, Semicolon, Comma,
SquareBracketOpen, SquareBracketClose,
ParenOpen, ParenClose,
CurlyBracketOpen, CurlyBracketClose, Eof
Parent: #19
Goal
Implement a CSS parser following CSS Syntax Module Level 3, a selector matching engine, the cascade algorithm, and expand
ComputedStyleto cover ~40 properties needed for layout. Also create the user-agent stylesheet.Prerequisites
None for parsing — can be developed in parallel with Step 1 (HTML tokenizer).
ie-dom needed for selector matching tests.
File Changes
crates/ie-css/src/tokenizer.rs— new file, CSS tokenizercrates/ie-css/src/parser.rs— rewrite, full CSS parsercrates/ie-css/src/selector.rs— major expansioncrates/ie-css/src/specificity.rs— new filecrates/ie-css/src/cascade.rs— new filecrates/ie-css/src/style.rs— major expansion (~40 properties)crates/ie-css/src/values.rs— new file, extended value typescrates/ie-css/src/inherited.rs— new file, property inheritance classificationcrates/ie-css/data/ua.css— new file, user-agent stylesheetcrates/ie-css/src/lib.rs— update module declarationsImplementation
CSS Tokenizer (
tokenizer.rs)impl Iterator<Item = CssToken>CssTokenenum:HashType: Id vs UnrestrictedNumType: Integer vs NumberCSS Parser (
parser.rs)Stylesheet { rules: Vec<Rule> }@import url("...")— record URL for fetching@media (condition) { ... }— parse but defer evaluation to Phase 3property: value;pairsValuetypesmargin,padding,border,background,font,flex!importantflag on declarationsstyleattributes (declaration block without selectors)Selectors (
selector.rs)Selectoras a list ofCompoundSelectorconnected byCombinator:::before,::after,::first-line,::first-letter(parse but don't generate content in Phase 2)parse_selector(input: &str) -> Result<Selector>parse_selector_list(input: &str) -> Result<Vec<Selector>>Specificity (
specificity.rs)Specificity(u32, u32, u32)— (a, b, c) per CSS spec:fn specificity(selector: &Selector) -> Specificityimpl Ord for Specificity— comparison for cascadeCascade (
cascade.rs)fn cascade(rules: &[(Selector, Vec<Declaration>, Origin)], node: NodeId, doc: &Document) -> PropertyMap:!importantoverriding non-important regardless of specificity)Originenum:UserAgent,Author,AuthorImportant,UserAgentImportantPropertyMap:HashMap<PropertyId, CascadedValue>— the raw cascaded values before inheritance/computationExtended ComputedStyle (
style.rs)Expand from current ~5 properties to ~40:
Box model:
display: block, inline, inline-block, flex, grid, none, contentswidth,height,min-width,max-width,min-height,max-heightmargin-top/right/bottom/leftpadding-top/right/bottom/leftborder-top/right/bottom/left-widthborder-top/right/bottom/left-styleborder-top/right/bottom/left-colorbox-sizing: content-box, border-boxTypography:
font-family: list of family namesfont-size: absolute (px) after computationfont-weight: numeric (100-900)font-style: normal, italic, obliqueline-height: normal or number/lengthtext-align: left, right, center, justifytext-decoration: none, underline, overline, line-throughcolor: RGBAwhite-space: normal, nowrap, pre, pre-wrap, pre-linePositioning:
position: static, relative, absolute, fixed, stickytop,right,bottom,leftz-index: auto or integerFlexbox:
flex-direction,flex-wrap,justify-content,align-items,align-selfflex-grow,flex-shrink,flex-basisgap(row-gap, column-gap)Visual:
background-coloroverflow,overflow-x,overflow-yvisibility: visible, hidden, collapseopacity: 0.0 to 1.0Extended Values (
values.rs)px,em,rem,%,vw,vh,vmin,vmax,ch,ex#rgb,#rrggbb,#rgba,#rrggbbaa,rgb(),rgba(),hsl(),hsla(), named colors (CSS Color Level 4 named colors list)autokeyword (for margin, width, height)inherit,initial,unsetkeywordscalc(): basic arithmetic (add, subtract, multiply, divide) with mixed units where validPropertyIdenum: one variant per CSS property (for efficient map keys)Property inheritance classification (
inherited.rs)fn is_inherited(property: PropertyId) -> boolcolor,font-*,line-height,text-align,text-decoration,visibility,white-space,cursordisplay,width,height,margin-*,padding-*,border-*,position,top/right/bottom/left,background-*,overflow,opacity,flex-*,z-indexfn initial_value(property: PropertyId) -> ValueUser-Agent Stylesheet (
data/ua.css)Stylesheetinclude_str!or constTests
CSS tokenizer tests
div { color: red; }→ correct token sequence"hello\"world"→ single String tokenurl(https://example.com)→ Url token16px→ Dimension(16.0, "px")50%→ Percentage(50.0)rgb(→ Function("rgb")CSS parser tests
p { color: red; }→ one rule, one selector, one declarationmargin: 10px 20px;→ four individual margin properties!importanth1, h2, h3 { ... }→ three selectors@importat-ruleSelector tests
div > .class#id[attr=val]:hover::before→ correct compound selector structure#id→ (1,0,0),.class→ (0,1,0),div→ (0,0,1)#id .class div→ (1,1,1):nth-child(2n+1)→ NthExpr { a: 2, b: 1 }:not(.foo)→ Not containing class selectorCascade tests
!importantoverrides non-important regardless of specificityProperty and value tests
color: #ff0000→ Color { r: 255, g: 0, b: 0, a: 255 }color: rgb(255, 0, 0)→ samemargin: 10px→ all four margins set to 10pxfont-size: 1.5em→ Em(1.5)red,blue,transparent, etc.calc(100% - 20px)parsed correctlyUA stylesheet tests
<div>hasdisplay: block<span>hasdisplay: inline<head>hasdisplay: none<h1>has larger font-size than<p>Acceptance Criteria
cargo test -p ie-css— all tests passcargo clippy -p ie-css -- -D warnings— no warnings