Skip to content

script: Rewrite the layout DOM wrappers#44114

Merged
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:rewrite-layout-wrappers
Apr 12, 2026
Merged

script: Rewrite the layout DOM wrappers#44114
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:rewrite-layout-wrappers

Conversation

@mrobinson
Copy link
Copy Markdown
Member

@mrobinson mrobinson commented Apr 11, 2026

This change reworks the layout DOM wrappers so that they are simpler and
easier to reason about. The main changes here:

Combine layout wrappers into one interface:

  • LayoutNode/ThreadSafeLayoutNode is combined into LayoutNode:
    The idea here is that LayoutNode is always thread-safe when used in
    layout as long as no unsafe calls are used. These interfaces
    only expose what is necessary for layout.
  • LayoutElement/ThreadSafeLayoutElement is combined into
    LayoutElement: See above.

Expose two new interfaces to be used only with stylo and
selectors:

DangerousStyleNode and DangerousStyleElement. stylo
and selectors have a different way of ensuring safety that is
incompatible with Servo's layout (access all of the DOM tree anywhere,
but ensure that writing only happens from a single-thread). These types
only implement things like TElement, TNode and are not intended to
be used by layout at all.

All traits and implementations are moved to files that are named after
the struct or trait inside them, in order to better understand what one
is looking at.

The main goals here are:

  • Make it easier to reason about the safe use of the DOM APIs.
  • Remove the interdependencies between the stylo and selectors
    interface implementations and the layout interface. This helps
    with the first point as well and makes it simpler to know where
    a method is implemented.
  • Reduce the amount of code.
  • Make it possible to eliminate TrustedNodeAddress in the future.
  • Document and bring the method naming up to modern Rust conventions.

This is a lot of code changes, but is very well tested by the WPT tests.
Unfortunately, it is difficult to make a change like this iteratively.
In addition, this new design comes with new documentation at
servo/book#225.

Testing: This should not change behavior so should be covered by existing
WPT tests.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Apr 11, 2026
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Apr 11, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Apr 11, 2026
@github-actions
Copy link
Copy Markdown

🔨 Triggering try run (#24280322148) for Linux (WPT)

@github-actions
Copy link
Copy Markdown

Test results for linux-wpt from try job (#24280322148):

Flaky unexpected result (37)
  • OK /IndexedDB/idbdatabase_deleteObjectStore.any.html (#43823)
    • PASS [expected FAIL] subtest: Deleted object store's name should be removed from database's list. Attempting to use a deleted IDBObjectStore should throw an InvalidStateError
  • OK /_mozilla/mozilla/getBoundingClientRect.html (#39668)
    • FAIL [expected PASS] subtest: getBoundingClientRect 1

      assert_equals: expected 62 but got 60.35
      

  • CRASH [expected OK] /_webgl/conformance/ogles/GL/swizzlers/swizzlers_089_to_096.html
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #45

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #47

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #49

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #51

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • PASS [expected FAIL] subtest: WebGL test #53
    • PASS [expected FAIL] subtest: WebGL test #55
    • PASS [expected FAIL] subtest: WebGL test #57
    • PASS [expected FAIL] subtest: WebGL test #59
    • FAIL [expected PASS] subtest: WebGL test #61

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #63

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • And 18 more unexpected results...
  • CRASH [expected ERROR] /_webgl/conformance2/misc/uninitialized-test-2.html (#41656)
  • CRASH [expected OK] /content-security-policy/meta/sandbox-iframe.html (#43478)
  • FAIL [expected PASS] /css/css-backgrounds/background-size-042.html
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • PASS [expected FAIL] subtest: Delete layer invalidates @font-face
  • OK /css/css-fonts/matching/font-unicode-PUA.html
    • PASS [expected FAIL] subtest: PUA character U+F000 is rendered with a non-generic font.
  • FAIL [expected PASS] /css/css-ui/appearance-button-001.html
  • FAIL [expected PASS] /css/css-ui/appearance-radio-001.html
  • FAIL [expected PASS] /css/css-ui/webkit-appearance-menulist-button-001.html
  • CRASH [expected OK] /fetch/api/cors/cors-redirect.any.worker.html
  • CRASH [expected OK] /fetch/api/policies/referrer-origin-worker.html
  • OK [expected ERROR] /fetch/fetch-later/quota/cross-origin-iframe/accumulated-oversized-payload.https.window.html
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/accumulated-oversized-payload.https.window.html (#41705)
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • FAIL [expected PASS] subtest: content sec-fetch-site - HTTPS downgrade-upgrade

      assert_unreached: Reached unreachable code
      

  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • PASS [expected FAIL] subtest: Cross-origin navigation started from unload handler must be ignored
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html (#44099)
    • FAIL [expected TIMEOUT] subtest: Replace before load, triggered by submitButton.click()

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement=" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&code=%0A%20%20%20%20const%20form%20%3D%20document.createElement(%22form%22)%3B%0A%20%20%20%20form.action%20%3D%20%22%2Fcommon%2Fblank.html%22%3B%0A%0A%20%20%20%20const%20input%20%3D%20document.createElement(%22input%22)%3B%0A%20%20%20%20input.type%20%3D%20%22hidden%22%3B%0A%20%20%20%20input.name%20%3D%20%22thereplacement%22%3B%0A%20%20%20%20form.append(input)%3B%0A%0A%20%20%20%20const%20button%20%3D%20document.createElement(%22button%22)%3B%0A%20%20%20%20button.type%20%3D%20%22submit%22%3B%0A%20%20%20%20form.append(button)%3B%0A%0A%20%20%20%20document.currentScript.before(form)%3B%0A%20%20%20%20button.click()%3B%0A%20%20"
      

  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • FAIL [expected PASS] subtest: opener of discarded auxiliary browsing context

      assert_object_equals: property "get" expected function "function opener() {
          [native code]
      }" got function "function opener() {
          [native code]
      }"
      

  • CRASH [expected OK] /html/canvas/element/canvas-host/2d.canvas.host.size.attributes.setAttribute.whitespace.html
  • TIMEOUT /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • PASS [expected TIMEOUT] subtest: Speed-ups should not change the pitch when preservesPitch=true
    • PASS [expected NOTRUN] subtest: Slow-downs should not change the pitch when preservesPitch=true
    • TIMEOUT [expected NOTRUN] subtest: Speed-ups should change the pitch when preservesPitch=false

      Test timed out
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • OK /html/semantics/forms/form-submission-0/jsurl-form-submit.tentative.html (#36489)
    • PASS [expected FAIL] subtest: Verifies that form submissions scheduled inside javascript: urls take precedence over the javascript: url's return value.
  • CRASH [expected ERROR] /html/semantics/forms/the-select-element/customizable-select/select-appearance-button-after-span.html
  • OK /html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html (#44058)
    • PASS [expected FAIL] subtest: Parse errors in different files should be reported depending on different roots
  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html
    • TIMEOUT [expected PASS] subtest: Post-navigation state reset.

      Test timed out
      

  • ERROR [expected OK] /intersection-observer/v2/animated-opacity.html (#40827)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domInteractive > Original domInteractive
  • OK [expected CRASH] /resource-timing/render-blocking-status-link.html (#41664)
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • PASS [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.
    • PASS [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • OK [expected TIMEOUT] /webdriver/tests/classic/perform_actions/navigation.py
  • TIMEOUT [expected OK] /xhr/access-control-basic-denied.htm
Stable unexpected results that are known to be intermittent (20)
  • OK /_mozilla/css/offset_properties_inline.html (#40543)
    • FAIL [expected PASS] subtest: offsetTop

      assert_equals: offsetTop of #inline-1 should be 0. expected 0 but got -1
      

    • FAIL [expected PASS] subtest: offsetLeft

      assert_equals: offsetLeft of #inline-2 should be 40. expected 40 but got 25
      

  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • OK /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = arraybuffer
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 1
    • PASS [expected FAIL] subtest: @font-face override update with appended sheet 2
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong)
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted sans-serif (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted cursive (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted fantasy (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(kai) (drawing text in a canvas)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)

      assert_equals: quoted generic(khmer-mul) matches  @font-face rule expected 125 but got 40
      

    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
  • OK /dom/nodes/moveBefore/iframe-document-preserve.window.html (#43152)
    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove self

      assert_equals: iframe does not fire a second load event expected 1 but got 0
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • FAIL [expected PASS] subtest: sec-fetch-user

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK /fetch/metadata/window-open.https.sub.html (#40339)
    • FAIL [expected PASS] subtest: Same-site window, forced, reloaded

      The operation is insecure.
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/005.html (#27062)
    • PASS [expected FAIL] subtest: Link with onclick navigation and href navigation
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • PASS [expected FAIL] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
      

  • ERROR [expected OK] /html/canvas/offscreen/text/2d.text.measure.getActualBoundingBox.tentative.html (#43710)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • PASS [expected FAIL] subtest: Cross-Origin video should get upgraded even if CORS is set
  • OK /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (iframe)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (xmlhttprequest)
  • OK /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)
  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • FAIL [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      assert_true: about:blank window opened by 3P iframe does not inherit 3P iframe's StorageKey expected true got false
      

@github-actions
Copy link
Copy Markdown

✨ Try run (#24280322148) succeeded.

@mrobinson mrobinson force-pushed the rewrite-layout-wrappers branch 5 times, most recently from f78eb7a to 61335da Compare April 11, 2026 15:58
Copy link
Copy Markdown
Member

@simonwuelker simonwuelker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great improvement! I have some nits, mostly about comments but all in all this change LGTM. It was also significantly easier to review than I feared, the new file structure is significantly less chaotic (:

Also, thanks for adding all the documentation!

fn as_node(&self) -> Self::ConcreteLayoutNode;

/// Returns access to a version of this LayoutElement that can be used by stylo
/// and selectors. This is dangerous as it allows more access to ancestors nodes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- /// and selectors. This is dangerous as it allows more access to ancestors nodes
+ /// and selectors. This is dangerous as it allows more access to ancestor nodes

Comment on lines +117 to +118
/// Returns whether this node is a body element of an html element root
/// in an HTML element document.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: a Document is not an element (or a HTMLElement)

Suggestion:

- /// Returns whether this node is a body element of an html element root
- /// in an HTML element document.
+ /// Returns whether this node is a `<body>` element of an `<html>` element root
+ /// in an HTML document.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread components/shared/layout/layout_node.rs Outdated
Comment on lines +113 to +118
/// # Safety
///
/// This method is unsafe because it modifies the given node during
/// layout. Callers should ensure that no other layout thread is
/// attempting to read or modify the opaque layout data of this node.
fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment states that this method is unsafe, but it isn`t.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the safety documentation here. As an aside, I am going back and forth about whether all of the methods that mutate the node should be unsafe. Half of them were before, but it's really unclear if it buys us anything. it doesn't make it easier to see if some code is violating the interface anywhere else.

Comment thread components/shared/layout/layout_node.rs Outdated
fn flat_tree_children(&self) -> LayoutIterator<Self::ChildIterator>;

/// Returns an iterator over this node's children in the DOM. This
/// *does not* take into account shadow tree children and slottables.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hyper-nit about shadow-dom terminology:

- /// *does not* take into account shadow tree children and slottables.
+ /// *does not* take shadow roots and assigned slottables into acccount.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads much better.

Comment thread components/shared/layout/layout_node.rs Outdated
/// For that use [`Self::flat_tree_children`].
fn dom_children(&self) -> LayoutIterator<Self::ChildIterator>;

/// Returns a LayoutElement if this is an element in an HTML namespace, None otherwise.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- /// Returns a LayoutElement if this is an element in an HTML namespace, None otherwise.
+ /// Returns a LayoutElement if this is an element in the HTML namespace, None otherwise.

Comment thread components/shared/layout/lib.rs Outdated
pub enabled: bool,
}

pub type SharedSelection = std::sync::Arc<AtomicRefCell<ScriptSelection>>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Use Arc< directly

}

if ThreadSafeLayoutElement::is_root(&element) {
if LayoutElement::is_root(&element) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we call is_root on element directly?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes. This was necessary due to the previous design, but can be simplified now. from the previous design. Thanks.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Apr 11, 2026
This change reworks the layout DOM wrappers so that they are simpler and
easier to reason about. The main changes here:

**Combine layout wrappers into one interface:**

 - `LayoutNode`/`ThreadSafeLayoutNode` is combined into `LayoutNode`:
   The idea here is that `LayoutNode` is always thread-safe when used in
   layout as long as no `unsafe` calls are used. These interfaces
   only expose what is necessary for layout.
 - `LayoutElement`/`ThreadSafeLayoutElement` is combined into
   `LayoutElement`: See above.

**Expose two new interfaces to be used *only* with `stylo` and
`selectors`:**

`DangerousStyleNode` and `DangerousStyleElement`. `stylo`
and `selectors` have a different way of ensuring safety that is
incompatible with Servo's layout (access all of the DOM tree anywhere,
but ensure that writing only happens from a single-thread). These types
only implement things like `TElement`, `TNode` and are not intended to
be used by layout at all.

All traits and implementations are moved to files that are named after
the struct or trait inside them, in order to better understand what one
is looking at.

The main goals here are:

 - Make it easier to reason about the safe use of the DOM APIs.
 - Remove the interdependencies between the `stylo` and `selectors`
   interface implementations and the layout interface. This helps
   with the first point as well and makes it simpler to know where
   a method is implemented.
 - Reduce the amount of code.
 - Make it possible to eliminate `TrustedNodeAddress` in the future.
 - Document and bring the method naming up to modern Rust conventions.

This is a lot of code changes, but is very well tested by the WPT tests.
Unfortunately, it is difficult to make a change like this iteratively.
In addition, this new design comes with new documentation at
servo/book#225.

Signed-off-by: Martin Robinson <mrobinson@fastmail.fm>
@mrobinson mrobinson force-pushed the rewrite-layout-wrappers branch from 61335da to 428796e Compare April 12, 2026 04:12
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Apr 12, 2026
Copy link
Copy Markdown
Member Author

@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for the review @simonwuelker. I know this PR was a bit of a monster, so I highly appreciate it!

}

if ThreadSafeLayoutElement::is_root(&element) {
if LayoutElement::is_root(&element) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes. This was necessary due to the previous design, but can be simplified now. from the previous design. Thanks.

Comment on lines +117 to +118
/// Returns whether this node is a body element of an html element root
/// in an HTML element document.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment thread components/shared/layout/layout_node.rs Outdated
Comment on lines +113 to +118
/// # Safety
///
/// This method is unsafe because it modifies the given node during
/// layout. Callers should ensure that no other layout thread is
/// attempting to read or modify the opaque layout data of this node.
fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the safety documentation here. As an aside, I am going back and forth about whether all of the methods that mutate the node should be unsafe. Half of them were before, but it's really unclear if it buys us anything. it doesn't make it easier to see if some code is violating the interface anywhere else.

Comment thread components/shared/layout/layout_node.rs Outdated
fn flat_tree_children(&self) -> LayoutIterator<Self::ChildIterator>;

/// Returns an iterator over this node's children in the DOM. This
/// *does not* take into account shadow tree children and slottables.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads much better.

@mrobinson mrobinson enabled auto-merge April 12, 2026 04:21
@mrobinson mrobinson added this pull request to the merge queue Apr 12, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Apr 12, 2026
Merged via the queue into servo:main with commit 45972e0 Apr 12, 2026
33 checks passed
@mrobinson mrobinson deleted the rewrite-layout-wrappers branch April 12, 2026 04:53
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-awaiting-review There is new code that needs to be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants