fix: retain only valid cascade winners
This commit is contained in:
parent
c79eb796b4
commit
13559a41f0
@ -54,6 +54,7 @@ Do not pass both `:stylesheet` and `:rules`. `:rules` accepts the rule list retu
|
|||||||
- `(ecss-computed-style-custom-properties STYLE)` returns a deterministic custom-property plist.
|
- `(ecss-computed-style-custom-properties STYLE)` returns a deterministic custom-property plist.
|
||||||
- `(ecss-computed-style-active-properties STYLE)` returns property IDs supplied by valid declarations or active inheritance, preserving explicit nil versus absence. An invalid winner falling back to the initial value is not presented as an active declaration.
|
- `(ecss-computed-style-active-properties STYLE)` returns property IDs supplied by valid declarations or active inheritance, preserving explicit nil versus absence. An invalid winner falling back to the initial value is not presented as an active declaration.
|
||||||
- `(ecss-computed-style-specified-properties STYLE)` returns longhand property IDs that have cascade winners, independent of whether provenance was requested.
|
- `(ecss-computed-style-specified-properties STYLE)` returns longhand property IDs that have cascade winners, independent of whether provenance was requested.
|
||||||
|
- `(ecss-computed-style-precedence-winner STYLE PROPERTIES)` returns the property ID whose already-selected declaration winner has the highest cascade precedence. It never reruns cascade or requires public provenance.
|
||||||
- `(ecss-computed-style-copy-with-values STYLE VALUES &key active-properties specified-properties)` returns an immutable-style copy for a consumer that has already proved selector/cascade facts unchanged and only needs to replace computed values.
|
- `(ecss-computed-style-copy-with-values STYLE VALUES &key active-properties specified-properties)` returns an immutable-style copy for a consumer that has already proved selector/cascade facts unchanged and only needs to replace computed values.
|
||||||
- `(ecss-computed-style-provenance STYLE)` returns winner, origin, importance, layer, specificity, scope/source/declaration order, validity, and fallback facts.
|
- `(ecss-computed-style-provenance STYLE)` returns winner, origin, importance, layer, specificity, scope/source/declaration order, validity, and fallback facts.
|
||||||
- `(ecss-computed-style-diagnostics STYLE)` returns deterministic diagnostics such as custom-property cycles.
|
- `(ecss-computed-style-diagnostics STYLE)` returns deterministic diagnostics such as custom-property cycles.
|
||||||
|
|||||||
@ -54,6 +54,7 @@ ORIGIN 是 `ua`、`user`、`author`、`animation` 或 `transition`。Inline decl
|
|||||||
- `(ecss-computed-style-custom-properties STYLE)` 返回 deterministic custom-property plist。
|
- `(ecss-computed-style-custom-properties STYLE)` 返回 deterministic custom-property plist。
|
||||||
- `(ecss-computed-style-active-properties STYLE)` 返回由有效 declaration 或 active inheritance 实际提供的 property ids,保留 explicit nil 与 absence 的区别;invalid winner 回退到 initial 时不伪装成 active declaration。
|
- `(ecss-computed-style-active-properties STYLE)` 返回由有效 declaration 或 active inheritance 实际提供的 property ids,保留 explicit nil 与 absence 的区别;invalid winner 回退到 initial 时不伪装成 active declaration。
|
||||||
- `(ecss-computed-style-specified-properties STYLE)` 返回存在 cascade winner 的长手 property ids,独立于是否请求 provenance。
|
- `(ecss-computed-style-specified-properties STYLE)` 返回存在 cascade winner 的长手 property ids,独立于是否请求 provenance。
|
||||||
|
- `(ecss-computed-style-precedence-winner STYLE PROPERTIES)` 返回这些 canonical property 已选 winner 中 cascade 优先级最高的 property ID;不会重跑 cascade,也不要求公开 provenance。
|
||||||
- `(ecss-computed-style-copy-with-values STYLE VALUES &key active-properties specified-properties)` 在 selector/cascade 事实已证明不变、只需替换 computed values 时返回新的 immutable-style copy。
|
- `(ecss-computed-style-copy-with-values STYLE VALUES &key active-properties specified-properties)` 在 selector/cascade 事实已证明不变、只需替换 computed values 时返回新的 immutable-style copy。
|
||||||
- `(ecss-computed-style-provenance STYLE)` 返回 winner、origin、important、layer、specificity、scope/source/declaration order、valid/fallback 等事实。
|
- `(ecss-computed-style-provenance STYLE)` 返回 winner、origin、important、layer、specificity、scope/source/declaration order、valid/fallback 等事实。
|
||||||
- `(ecss-computed-style-diagnostics STYLE)` 返回 custom-property cycle 等 deterministic diagnostics。
|
- `(ecss-computed-style-diagnostics STYLE)` 返回 custom-property cycle 等 deterministic diagnostics。
|
||||||
|
|||||||
@ -55,7 +55,7 @@
|
|||||||
(:conc-name ecss--computed-style-))
|
(:conc-name ecss--computed-style-))
|
||||||
"Computed declarations, custom properties, metadata, and diagnostics."
|
"Computed declarations, custom properties, metadata, and diagnostics."
|
||||||
schemas values custom-properties active-properties specified-properties
|
schemas values custom-properties active-properties specified-properties
|
||||||
provenance diagnostics)
|
winners provenance diagnostics)
|
||||||
|
|
||||||
(cl-defstruct (ecss-computed-property-fact
|
(cl-defstruct (ecss-computed-property-fact
|
||||||
(:constructor ecss--make-computed-property-fact))
|
(:constructor ecss--make-computed-property-fact))
|
||||||
@ -1061,7 +1061,7 @@ handle explicit sources, and DIAGNOSTICS records variable failures."
|
|||||||
PARENT-STYLE and CUSTOM-TABLES supply inherited values. SUBJECT and RESOLVER
|
PARENT-STYLE and CUSTOM-TABLES supply inherited values. SUBJECT and RESOLVER
|
||||||
handle explicit sources. DIAGNOSTICS records failures; PROVENANCE-P retains
|
handle explicit sources. DIAGNOSTICS records failures; PROVENANCE-P retains
|
||||||
facts."
|
facts."
|
||||||
(let (values active specified provenance)
|
(let (values active specified winners provenance)
|
||||||
(dolist (property (ecss--schema-set-order schemas))
|
(dolist (property (ecss--schema-set-order schemas))
|
||||||
(let ((schema (gethash property (ecss--schema-set-table schemas))))
|
(let ((schema (gethash property (ecss--schema-set-table schemas))))
|
||||||
(unless (ecss-property-schema-shorthand schema)
|
(unless (ecss-property-schema-shorthand schema)
|
||||||
@ -1076,13 +1076,15 @@ facts."
|
|||||||
(ecss--parent-property-active-p parent-style property)))
|
(ecss--parent-property-active-p parent-style property)))
|
||||||
(push property active))
|
(push property active))
|
||||||
(when winner
|
(when winner
|
||||||
(push property specified))
|
(push property specified)
|
||||||
|
(unless invalid
|
||||||
|
(setq winners (plist-put winners property winner))))
|
||||||
(when provenance-p
|
(when provenance-p
|
||||||
(setq provenance
|
(setq provenance
|
||||||
(plist-put provenance property
|
(plist-put provenance property
|
||||||
(ecss--property-provenance
|
(ecss--property-provenance
|
||||||
winner invalid source))))))))
|
winner invalid source))))))))
|
||||||
(list values (nreverse active) (nreverse specified) provenance)))
|
(list values (nreverse active) (nreverse specified) winners provenance)))
|
||||||
|
|
||||||
(defun ecss--validate-rule-declarations (schemas rule)
|
(defun ecss--validate-rule-declarations (schemas rule)
|
||||||
"Validate canonical declarations in RULE against SCHEMAS."
|
"Validate canonical declarations in RULE against SCHEMAS."
|
||||||
@ -1143,7 +1145,7 @@ non-nil, is the only function allowed to evaluate caller-owned value sources;
|
|||||||
(custom (ecss--resolved-custom-properties
|
(custom (ecss--resolved-custom-properties
|
||||||
(car raw-facts) diagnostics)))
|
(car raw-facts) diagnostics)))
|
||||||
(let ((custom-tables (ecss--custom-value-tables custom)))
|
(let ((custom-tables (ecss--custom-value-tables custom)))
|
||||||
(pcase-let ((`(,values ,active ,specified ,property-facts)
|
(pcase-let ((`(,values ,active ,specified ,winners ,property-facts)
|
||||||
(ecss--compute-property-values
|
(ecss--compute-property-values
|
||||||
schemas table parent-style custom-tables subject value-resolver
|
schemas table parent-style custom-tables subject value-resolver
|
||||||
diagnostics provenance)))
|
diagnostics provenance)))
|
||||||
@ -1151,6 +1153,7 @@ non-nil, is the only function allowed to evaluate caller-owned value sources;
|
|||||||
:schemas schemas :values values :custom-properties custom
|
:schemas schemas :values values :custom-properties custom
|
||||||
:active-properties active
|
:active-properties active
|
||||||
:specified-properties specified
|
:specified-properties specified
|
||||||
|
:winners winners
|
||||||
:provenance (and provenance
|
:provenance (and provenance
|
||||||
(append (cadr raw-facts) property-facts))
|
(append (cadr raw-facts) property-facts))
|
||||||
:diagnostics (nreverse (car diagnostics))))))))
|
:diagnostics (nreverse (car diagnostics))))))))
|
||||||
@ -1207,6 +1210,27 @@ the public boundary."
|
|||||||
(ecss--copy-boundary-data
|
(ecss--copy-boundary-data
|
||||||
(ecss--computed-style-specified-properties style)))
|
(ecss--computed-style-specified-properties style)))
|
||||||
|
|
||||||
|
(defun ecss-computed-style-precedence-winner (style properties)
|
||||||
|
"Return the highest-precedence declared property in PROPERTIES for STYLE.
|
||||||
|
PROPERTIES may use canonical IDs or aliases from STYLE's schema set. Nil is
|
||||||
|
returned when none has a cascade winner. The comparison reuses ECSS's exact
|
||||||
|
cascade ordering and does not require public provenance materialization."
|
||||||
|
(setq style (ecss--computed-style-check style))
|
||||||
|
(let ((schemas (ecss--computed-style-schemas style))
|
||||||
|
(winners (ecss--computed-style-winners style))
|
||||||
|
winner-property winner)
|
||||||
|
(dolist (property properties winner-property)
|
||||||
|
(let* ((canonical
|
||||||
|
(or (ecss-schema-set-canonical-id schemas property)
|
||||||
|
(signal 'ecss-invalid-declaration
|
||||||
|
(list :unknown-property property))))
|
||||||
|
(candidate (plist-get winners canonical)))
|
||||||
|
(when (and candidate
|
||||||
|
(or (null winner)
|
||||||
|
(ecss--candidate-higher-p candidate winner)))
|
||||||
|
(setq winner-property canonical
|
||||||
|
winner candidate))))))
|
||||||
|
|
||||||
(defun ecss-computed-style-provenance (style)
|
(defun ecss-computed-style-provenance (style)
|
||||||
"Return defensive winner provenance from computed STYLE."
|
"Return defensive winner provenance from computed STYLE."
|
||||||
(ecss--computed-style-check style)
|
(ecss--computed-style-check style)
|
||||||
|
|||||||
@ -751,6 +751,55 @@
|
|||||||
(should (equal (ecss-computed-style-specified-properties declared)
|
(should (equal (ecss-computed-style-specified-properties declared)
|
||||||
'(app/color app/width app/payload)))))
|
'(app/color app/width app/payload)))))
|
||||||
|
|
||||||
|
(ert-deftest ecss-cascade-test-cross-property-projection-reuses-cascade-order ()
|
||||||
|
"A consumer projection should choose among property winners without recascade."
|
||||||
|
(let* ((schemas (ecss-cascade-test--schemas))
|
||||||
|
(stylesheet (ecss-stylesheet-create))
|
||||||
|
(subject (ecss-cascade-test--subject
|
||||||
|
:id "hero" :classes '("target"))))
|
||||||
|
(ecss-stylesheet-add-rule
|
||||||
|
stylesheet schemas ".target" '(app/padding-left 1))
|
||||||
|
(ecss-stylesheet-add-rule
|
||||||
|
stylesheet schemas "#hero" '(app/width 2))
|
||||||
|
(let* ((ruled (ecss-compute-style
|
||||||
|
schemas subject :stylesheet stylesheet))
|
||||||
|
(inline (ecss-compute-style
|
||||||
|
schemas subject :stylesheet stylesheet
|
||||||
|
:declarations '(app/padding-left 3)))
|
||||||
|
(copy (ecss-computed-style-copy-with-values
|
||||||
|
inline (ecss-computed-style-values inline))))
|
||||||
|
(should (eq (ecss-computed-style-precedence-winner
|
||||||
|
ruled '(app/padding-left app/width))
|
||||||
|
'app/width))
|
||||||
|
(should (eq (ecss-computed-style-precedence-winner
|
||||||
|
inline '(app/padding-left app/width))
|
||||||
|
'app/padding-left))
|
||||||
|
(should (eq (ecss-computed-style-precedence-winner
|
||||||
|
copy '(app/padding-left app/width))
|
||||||
|
'app/padding-left))
|
||||||
|
(should-not (ecss-computed-style-provenance inline)))))
|
||||||
|
|
||||||
|
(ert-deftest ecss-cascade-test-cross-property-projection-skips-invalid-winner ()
|
||||||
|
"An invalid declaration must not override a valid cross-property fact."
|
||||||
|
(let* ((schemas (ecss-cascade-test--schemas))
|
||||||
|
(stylesheet (ecss-stylesheet-create))
|
||||||
|
(subject (ecss-cascade-test--subject
|
||||||
|
:id "hero" :classes '("target"))))
|
||||||
|
(ecss-stylesheet-add-rule
|
||||||
|
stylesheet schemas ".target" '(app/padding-left 5))
|
||||||
|
(ecss-stylesheet-add-rule
|
||||||
|
stylesheet schemas "#hero" '(app/width "invalid"))
|
||||||
|
(let ((style (ecss-compute-style
|
||||||
|
schemas subject :stylesheet stylesheet)))
|
||||||
|
(should (eq (ecss-computed-style-precedence-winner
|
||||||
|
style '(app/padding-left app/width))
|
||||||
|
'app/padding-left))
|
||||||
|
(should (memq 'app/width
|
||||||
|
(ecss-computed-style-specified-properties style)))
|
||||||
|
(should-not (memq 'app/width
|
||||||
|
(ecss-computed-style-active-properties style)))
|
||||||
|
(should (= (ecss-computed-style-value style 'app/width) 0)))))
|
||||||
|
|
||||||
(ert-deftest ecss-cascade-test-provenance-getter-copies-selector-data ()
|
(ert-deftest ecss-cascade-test-provenance-getter-copies-selector-data ()
|
||||||
(let ((schemas (ecss-cascade-test--schemas))
|
(let ((schemas (ecss-cascade-test--schemas))
|
||||||
(stylesheet (ecss-stylesheet-create))
|
(stylesheet (ecss-stylesheet-create))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user