--- enabled: true touches: [crates/layout/src/bin/logical_properties_check.rs] depends-on: [] --- # `logical-properties`: measure flow-relative box properties against the three browsers The Web Features map (`docs/web-features-map.json`, feature `logical-properties`) lists it as `unknown` (its 266 keys are claimed only through parent rules) with 92 WPT test files under `css/css-logical`. Stylo maps a logical property (`margin-inline-start`, `padding-block`, `inline-size`, `inset-inline`, `border-block-width`) onto the physical property for the box's `writing-mode` and `direction` at cascade time, and the accessors in `crates/dom/src/computed.rs` read only the physical ones (`width`, `margin_left`, `padding_top`, `left`, `border_left_width`). So the engine should already lay these out. Nothing in the repository measures it, and the direction-dependent mapping is the part a mistake would hide in: `margin-inline-start` is the left margin under `direction: ltr` and the right one under `rtl`. This spec adds the measurement and nothing else. If the engine agrees with the browsers, no engine code changes; if it does not, the disagreements are the finding and fixing them is a separate spec, since a fix would edit `computed.rs` or `fragment.rs`, which the queued generated-function specs edit and this spec's `touches` therefore leaves alone. **Scope.** In: block-level, non-replaced boxes without text, in normal flow or absolutely positioned, under `writing-mode: horizontal-tb` and both `direction: ltr` and `rtl` (`crates/layout/src/width.rs` reads `direction`; the engine lays out no other writing mode, so vertical modes would be a change to the layout substrate, not to this feature). The longhands `margin-{inline,block}-{start,end}`, `padding-{inline,block}-{start,end}`, `border-{inline,block}-{start,end}-width` (with a `solid` style so the width applies), `inset-{inline,block}-{start,end}`, `inline-size` and `block-size`; the two-value shorthands `margin-inline`, `margin-block`, `padding-inline`, `padding-block`, `inset-inline`, `inset-block`; declarations that mix logical and physical properties for the same side, where the later one wins. Out: `min-*`/`max-*-size` (the accessors for the block axis do not exist yet), `border-*-color` and `border-*-style` (paint only), `float` and `clear` with `inline-start`/`inline-end`, `text-align: start`, `overflow-inline`, `caption-side`, `resize`, `contain-intrinsic-*` and the `border-*-radius` corner mappings. The 92 WPT files cover all of those, so the count says how large the feature is, not how much of it this spec exercises. Say so in the binary's module doc. **Build.** One file, `crates/layout/src/bin/logical_properties_check.rs`, with the same shape as `crates/layout/src/bin/fragment_check.rs`; `crates/layout/Cargo.toml` needs no edit because `src/bin/*.rs` is discovered. - A `const CASES: &[(&str, &str, &str)]` of (id, category, html) with literal documents, since `crates/harness/src/cases.rs` `Elem` is `pub(crate)`. Every document opens with the same zeroing rule the recipe corpus's `RESET` uses. At least three cases per group above under each of `ltr` and `rtl`, and at least 60 in all, including a case per logical property whose physical twin is on the opposite side under `rtl`. - The engine side is `fragment_check.rs` `engine_dump` copied, not shared. - The browser side is `harness::oracle::Oracle::start` and `Oracle::dump`. A case counts only when every browser dump that came back agrees with the others under `harness::compare::compare`; a case whose browsers disagree, or that a browser failed to dump, is dropped and listed with the reason. It passes when the engine agrees with every dump that counted. - A `#[cfg(test)]` module checks that ids are unique, categories are non-empty, and each document parses with `dom::Document::parse` and yields a box tree; browsers are not needed for it. Add one direct test that `computed::margin_left` and `computed::margin_right` swap under `rtl` for `margin-inline-start: 7px`. - The binary exits non-zero when fewer than 30 cases were compared, and zero when some disagree with the engine; those cases are the result. **Where the result goes.** The binary's module doc records the counts (cases written, dropped with reasons, compared, passed, per group and direction) and, for each failing case, its id and the cause found in Stylo's output or in `computed.rs`. No other file records it. The binary prints that record in the exact form the module doc holds it, and the module doc's copy is pasted from a real run's output, never typed or recomputed by hand, because a hand-typed table ended the first two runs of this job on a count that did not add up. A `#[cfg(test)]` test reads the module doc (`include_str!` of the source file) and fails when the recorded numbers disagree with each other: the per-group and per-direction passed and compared cells must sum to the summary's totals, compared minus passed must equal the number of failing ids listed, and every listed id must be in `CASES`. This test needs no browser, so the `test` stage catches a wrong record before review does. No `docs/feature-map.json` rule is added: a rule for a check-only result could only be `hand-written`, which the map counts as `partial`, and that file is edited by the queued generated-function specs. Heavy steps go to a build lane and never run on the development box; if no lane answers, report that instead of running locally: 1. `bench/on-lane.sh --lane-only "$PWD" cargo test -p layout --bin logical_properties_check` 2. `bench/on-lane.sh --lane-only "$PWD" cargo run --release -p layout --bin logical_properties_check` The model-first steps the generated-function specs carry (ground-truth cache, generation loop, model batches through `bench/batch-lane.sh`) do not apply: this spec adds no generated function, since Stylo, not engine code, does the mapping. **Gates.** `cargo test --workspace` passes. `fragment_check --random` stays 227/227: no engine source changes.