fix: publish inherited native paint updates

This commit is contained in:
Kinneyzhang 2026-08-29 01:01:58 +08:00
parent 722791a01e
commit 54b3514668
6 changed files with 153 additions and 6 deletions

View File

@ -9824,6 +9824,10 @@ role, and outside-complement compatibility before publication."
(setq overrides (setq overrides
(plist-put overrides :native-removed-node-ids (plist-put overrides :native-removed-node-ids
(plist-get prepared :removed-node-ids))) (plist-get prepared :removed-node-ids)))
(setq overrides
(plist-put overrides :native-inherited-dirty-node-ids
(plist-get candidate-state
:native-inherited-dirty-node-ids)))
(setq overrides (setq overrides
(plist-put (plist-put
overrides :native-topology-stable-p overrides :native-topology-stable-p

View File

@ -32,6 +32,7 @@
(declare-function ebox--literal-root-pixel-width "ebox" (box)) (declare-function ebox--literal-root-pixel-width "ebox" (box))
(declare-function ebox-get "ebox" (box key)) (declare-function ebox-get "ebox" (box key))
(declare-function ebox-style-cascade-active-p "ebox-style" ()) (declare-function ebox-style-cascade-active-p "ebox-style" ())
(declare-function ebox-style-property "ebox-style" (name))
(declare-function ebox-tree-node-key "ebox-tree" (source-index node)) (declare-function ebox-tree-node-key "ebox-tree" (source-index node))
(declare-function ebox-tree--children-raw "ebox-tree" (node)) (declare-function ebox-tree--children-raw "ebox-tree" (node))
(declare-function ebox--node-direct-viewport-width-dependent-p (declare-function ebox--node-direct-viewport-width-dependent-p
@ -105,6 +106,17 @@
(setq node-id (and parents (gethash node-id parents)))))) (setq node-id (and parents (gethash node-id parents))))))
(nreverse result))) (nreverse result)))
(defun ebox-native-commit-inherited-dirty-node-ids (prepared)
"Return nodes whose PREPARED changes can alter descendant computed style."
(cl-loop
for entry in (plist-get prepared :dirty-set)
when (cl-some
(lambda (key)
(when-let* ((property (ebox-style-property key)))
(plist-get property :inherits)))
(plist-get entry :changed-keys))
collect (plist-get entry :node-id)))
(defun ebox-native-commit--node-position-table (state) (defun ebox-native-commit--node-position-table (state)
"Return NODE-ID to `(PARENT-ID . POSITION)' table for STATE." "Return NODE-ID to `(PARENT-ID . POSITION)' table for STATE."
(let ((nodes (plist-get state :node-table)) (let ((nodes (plist-get state :node-table))
@ -325,6 +337,8 @@ the committed text/fragment bases, compiler postorder, and projection support."
old-state candidate-state)) old-state candidate-state))
(plist-put candidate-state :native-topology-stable-p stable) (plist-put candidate-state :native-topology-stable-p stable)
(plist-put candidate-state :native-touched-node-ids layout-touched) (plist-put candidate-state :native-touched-node-ids layout-touched)
(plist-put candidate-state :native-inherited-dirty-node-ids
(ebox-native-commit-inherited-dirty-node-ids prepared))
(when context-axes-stable (when context-axes-stable
(plist-put candidate-state :viewport-dependent-node-ids-ready t) (plist-put candidate-state :viewport-dependent-node-ids-ready t)
(plist-put candidate-state :viewport-dependent-node-ids (plist-put candidate-state :viewport-dependent-node-ids

View File

@ -53,6 +53,8 @@
(declare-function ebox-layout-config-props "ebox-layout-config" (config)) (declare-function ebox-layout-config-props "ebox-layout-config" (config))
(declare-function ebox-tree-node-children "ebox-tree" (node)) (declare-function ebox-tree-node-children "ebox-tree" (node))
(declare-function ebox-buffer--font-face "ebox-buffer-backend" (style)) (declare-function ebox-buffer--font-face "ebox-buffer-backend" (style))
(declare-function ebox-buffer--text-decoration-face
"ebox-buffer-backend" (style))
(declare-function ebox--propertize-typography (declare-function ebox--propertize-typography
"ebox-buffer-backend" (string style)) "ebox-buffer-backend" (string style))
(declare-function ebox--ensure-node-id "ebox" (node)) (declare-function ebox--ensure-node-id "ebox" (node))
@ -71,6 +73,7 @@
(declare-function ebox--layout-children "ebox-tree" (node)) (declare-function ebox--layout-children "ebox-tree" (node))
(declare-function ebox-tree-layout-children "ebox-tree" (node)) (declare-function ebox-tree-layout-children "ebox-tree" (node))
(declare-function ebox-tree-node-local-source-signature "ebox-tree" (node)) (declare-function ebox-tree-node-local-source-signature "ebox-tree" (node))
(declare-function ebox-source--index-record "ebox-source" (index handle))
(declare-function ebox-buffer-side-border-face (declare-function ebox-buffer-side-border-face
"ebox-buffer-backend" (color)) "ebox-buffer-backend" (color))
(declare-function ebox--space-pixel-width "ebox-measure" ()) (declare-function ebox--space-pixel-width "ebox-measure" ())
@ -2419,7 +2422,7 @@ per call, and no call recursively visits the captured Ebox tree."
(vconcat (nreverse nodes)))) (vconcat (nreverse nodes))))
(defun ebox-native-reflow--retained-layout-signature (defun ebox-native-reflow--retained-layout-signature
(node child-revisions flex-content-min-widths) (node child-revisions flex-content-min-widths source-index)
"Return exact retained compiler signature for NODE and CHILD-REVISIONS." "Return exact retained compiler signature for NODE and CHILD-REVISIONS."
(let ((wrapper (and (eq (plist-get node :ebox-type) 'flex) (let ((wrapper (and (eq (plist-get node :ebox-type) 'flex)
(plist-get node :box)))) (plist-get node :box))))
@ -2430,6 +2433,12 @@ per call, and no call recursively visits the captured Ebox tree."
(and wrapper (plist-get wrapper :region-id))) (and wrapper (plist-get wrapper :region-id)))
(ebox-tree-node-local-source-signature node) (ebox-tree-node-local-source-signature node)
(and wrapper (ebox-tree-node-local-source-signature wrapper)) (and wrapper (ebox-tree-node-local-source-signature wrapper))
(and source-index
(when-let* ((handle (plist-get node :ebox-source-handle)))
(ebox-source--index-record source-index handle)))
(and source-index wrapper
(when-let* ((handle (plist-get wrapper :ebox-source-handle)))
(ebox-source--index-record source-index handle)))
child-revisions child-revisions
(and (eq (plist-get node :ebox-type) 'flex) (and (eq (plist-get node :ebox-type) 'flex)
(mapcar (mapcar
@ -2455,12 +2464,18 @@ per call, and no call recursively visits the captured Ebox tree."
0)) 0))
(flex-content-min-widths (flex-content-min-widths
(plist-get state :flex-content-min-widths)) (plist-get state :flex-content-min-widths))
(source-index (plist-get state :source-index))
(touched-ids (plist-get state :native-touched-node-ids)) (touched-ids (plist-get state :native-touched-node-ids))
(inherited-dirty-ids
(plist-get state :native-inherited-dirty-node-ids))
(touched-set (touched-set
(and (plist-get state :native-topology-stable-p) (and (plist-get state :native-topology-stable-p)
(plist-member state :native-touched-node-ids) (plist-member state :native-touched-node-ids)
(proper-list-p touched-ids) (proper-list-p touched-ids)
(make-hash-table :test 'equal))) (make-hash-table :test 'equal)))
(inherited-touched-set
(and touched-set inherited-dirty-ids
(make-hash-table :test 'equal)))
(retained-fast-p (retained-fast-p
(and touched-set (and touched-set
(= (hash-table-count old-cache) (length postorder)) (= (hash-table-count old-cache) (length postorder))
@ -2488,6 +2503,24 @@ per call, and no call recursively visits the captured Ebox tree."
(make-hash-table :test 'eq))) (make-hash-table :test 'eq)))
(dolist (node-id touched-ids) (dolist (node-id touched-ids)
(when touched-set (puthash node-id t touched-set))) (when touched-set (puthash node-id t touched-set)))
(when (and touched-set inherited-dirty-ids)
(let ((inherited-set (make-hash-table :test 'equal)))
(dolist (node-id inherited-dirty-ids)
(puthash node-id t inherited-set))
(cl-labels
((mark
(current inherited-p)
(let* ((node-id (plist-get current :node-id))
(inherited-p
(or inherited-p (gethash node-id inherited-set))))
(when inherited-p (puthash node-id t touched-set))
(when inherited-p
(puthash node-id t inherited-touched-set))
(dolist (child
(ebox-native-reflow--retained-layout-children
current))
(mark child inherited-p)))))
(mark node nil))))
(cl-loop (cl-loop
for template in ebox-native-reflow--compile-property-templates for template in ebox-native-reflow--compile-property-templates
for index from 0 for index from 0
@ -2521,9 +2554,12 @@ per call, and no call recursively visits the captured Ebox tree."
children)) children))
(signature (signature
(ebox-native-reflow--retained-layout-signature (ebox-native-reflow--retained-layout-signature
current child-revisions flex-content-min-widths)) current child-revisions flex-content-min-widths
source-index))
fragment entry) fragment entry)
(if (and cached (if (and cached
(not (and inherited-touched-set
(gethash node-id inherited-touched-set)))
(equal signature (plist-get cached :signature))) (equal signature (plist-get cached :signature)))
(setq fragment (plist-get cached :fragment) (setq fragment (plist-get cached :fragment)
entry cached) entry cached)

View File

@ -1458,6 +1458,14 @@ the projection roots a second time."
(ebox-tree-clear-author-style-pending root)) (ebox-tree-clear-author-style-pending root))
projection)) projection))
(defun ebox-surface--native-full-frame-p (state projection-kind)
"Return non-nil when STATE owns a complete native surface frame."
(and (eq projection-kind 'native-frame)
(plist-get state :native-topology-stable-p)
(let ((frame (plist-get state :native-render-frame)))
(and (plist-get frame :native-frame)
(not (plist-get frame :native-patch))))))
(defun ebox-surface--projection-result (defun ebox-surface--projection-result
(context projection state output &optional projection-kind) (context projection state output &optional projection-kind)
"Complete CONTEXT PROJECTION for STATE from rendered OUTPUT." "Complete CONTEXT PROJECTION for STATE from rendered OUTPUT."
@ -1468,13 +1476,14 @@ the projection roots a second time."
(owner-id (plist-get state :span-patch-owner-id)) (owner-id (plist-get state :span-patch-owner-id))
(scroll-fast-p (and (eq projection-kind 'scroll-patch) (scroll-fast-p (and (eq projection-kind 'scroll-patch)
(plist-get state :scroll-fast-window-p))) (plist-get state :scroll-fast-window-p)))
(full-surface-p
(and scroll-fast-p
(plist-get state :retained-scroll-content-p)
root-id owner-id (= root-id owner-id)))
(native-retained-p (native-retained-p
(and (eq projection-kind 'native-frame) (and (eq projection-kind 'native-frame)
(plist-get state :native-topology-stable-p))) (plist-get state :native-topology-stable-p)))
(full-surface-p
(or (and scroll-fast-p
(plist-get state :retained-scroll-content-p)
root-id owner-id (= root-id owner-id))
(ebox-surface--native-full-frame-p state projection-kind)))
(previous-owned-ranges (previous-owned-ranges
(plist-get state :previous-surface-owned-ranges)) (plist-get state :previous-surface-owned-ranges))
(node-objects (node-objects

View File

@ -4,6 +4,7 @@
(require 'ert) (require 'ert)
(require 'ebox) (require 'ebox)
(require 'ebox-native-commit) (require 'ebox-native-commit)
(require 'ebox-native-reflow)
;; These tests lock the named Elisp projection proofs. Native commit has its ;; These tests lock the named Elisp projection proofs. Native commit has its
;; own focused contract tests below; disable runtime module discovery here so ;; own focused contract tests below; disable runtime module discovery here so
@ -2091,6 +2092,71 @@ remain retained identities."
(should-not mixed-output))) (should-not mixed-output)))
(when (buffer-live-p buffer) (kill-buffer buffer))))) (when (buffer-live-p buffer) (kill-buffer buffer)))))
(ert-deftest ebox-native-inherited-dirty-domain-is-schema-owned ()
"Native invalidation derives inherited propagation from the style schema."
(should
(equal '(7)
(ebox-native-commit-inherited-dirty-node-ids
'(:dirty-set
((:node-id 7 :dirty-kind paint :changed-keys (:color)))))))
(should-not
(ebox-native-commit-inherited-dirty-node-ids
'(:dirty-set
((:node-id 7 :dirty-kind paint
:changed-keys (:background-color)))))))
(ert-deftest ebox-native-retained-compiler-refreshes-inherited-descendants ()
"An inherited parent paint change cannot reuse a stale child fragment."
(require 'ebox-native-reflow)
(let* ((input
(ebox-test-box
:color "#111111"
(ebox-test-text "Paint" :color "#111111")))
(root (ebox-test-root input))
(_ids (ebox--runtime-node-ids root))
(source-index (ebox-test-source-index input))
(session
(ebox-native-reflow--make-session
:handle 'test :generation 0 :styles nil :layout-package nil
:layout-fragment-cache (make-hash-table :test 'equal)
:layout-fragment-revision 0))
(initial-state
(list :native-node-postorder
(ebox-native-reflow--retained-layout-postorder root)
:native-topology-stable-p nil
:source-index source-index))
(initial-package
(ebox-native-reflow--compile-retained-layout-package
session initial-state root))
(next (copy-tree root))
(next-child (car (ebox-tree-node-children next))))
(setf (ebox-native-reflow-session-styles session)
(plist-get initial-package :styles)
(ebox-native-reflow-session-layout-package session)
initial-package)
;; Model a candidate computed-style projection: the child source record is
;; unchanged, while its inherited runtime color follows the parent.
(plist-put next :color "#222222")
(plist-put next-child :color "#222222")
(let* ((root-id (plist-get next :node-id))
(next-state
(list :native-node-postorder
(ebox-native-reflow--retained-layout-postorder next)
:native-topology-stable-p t
:native-touched-node-ids (list root-id)
:native-inherited-dirty-node-ids (list root-id)
:source-index source-index))
(package
(ebox-native-reflow--compile-retained-layout-package
session next-state next))
(document-root (plist-get (plist-get package :document) :root))
(style-id (plist-get document-root :content-foreground-style))
(styles (plist-get package :styles)))
(should (integerp style-id))
(should
(equal '(:foreground "#222222")
(plist-get (aref styles style-id) :face))))))
(provide 'ebox-commit-tests) (provide 'ebox-commit-tests)
;;; ebox-commit-tests.el ends here ;;; ebox-commit-tests.el ends here

View File

@ -2995,6 +2995,24 @@ participant and rollback contract without creating a real frame."
(when (buffer-live-p buffer) (when (buffer-live-p buffer)
(kill-buffer buffer)))) (kill-buffer buffer))))
(ert-deftest ebox-surface-native-full-frame-owns-full-publication-scope ()
"A complete native frame is authoritative for the whole TP surface."
(should
(ebox-surface--native-full-frame-p
'(:native-topology-stable-p t
:native-render-frame (:native-frame t :native-patch nil))
'native-frame))
(should-not
(ebox-surface--native-full-frame-p
'(:native-topology-stable-p t
:native-render-frame (:native-frame t :native-patch t))
'native-frame))
(should-not
(ebox-surface--native-full-frame-p
'(:native-topology-stable-p t
:native-render-frame (:native-frame t :native-patch nil))
'paint)))
(provide 'ebox-surface-tests) (provide 'ebox-surface-tests)
;;; ebox-surface-tests.el ends here ;;; ebox-surface-tests.el ends here