Skip to content

layout: Preserve boxes in rebuilt subtrees underneath independent formatting contexts#42582

Merged
Loirooriol merged 1 commit intoservo:mainfrom
mrobinson:try-to-reuse-boxes-in-subtrees
Feb 12, 2026
Merged

layout: Preserve boxes in rebuilt subtrees underneath independent formatting contexts#42582
Loirooriol merged 1 commit intoservo:mainfrom
mrobinson:try-to-reuse-boxes-in-subtrees

Conversation

@mrobinson
Copy link
Copy Markdown
Member

When a box needs to be rebuilt due to a style change, it previously
would rebuild the entire box subtree, including all descendants. This is
probably unnecessary when crossing independent formatting context
boundaries. This change limits box tree damage pushed downward when
entering certain types of independent formatting contexts. The types can
be expanded later, but this is just a first conservative step. This is
necessary in order to start preserving fragments in these subtrees.

Testing: This should not change observable layout behavior. We do not really
have tests to verify these kind of optimizations.

…matting contexts

When a box needs to be rebuilt due to a style change, it previously
would rebuild the entire box subtree, including all descendants. This is
probably unnecessary when crossing independent formatting context
boundaries. This change limits box tree damage pushed downward when
entering certain types of independent formatting contexts. The types can
be expanded later, but this is just a first conservative step. This is
necessary in order to start preserving fragments in these subtrees.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Feb 12, 2026
@Loirooriol Loirooriol added the T-linux-wpt Do a try run of the WPT label Feb 12, 2026
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Feb 12, 2026
@github-actions
Copy link
Copy Markdown

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

@github-actions
Copy link
Copy Markdown

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

Flaky unexpected result (33)
  • OK /FileAPI/url/url-with-fetch.any.worker.html (#21517)
    • FAIL [expected PASS] subtest: Revoke blob URL after calling fetch, fetch should succeed

      promise_test: Unhandled rejection with value: object "TypeError: Network error: Blob URL store error: InvalidFileID"
      

  • ERROR [expected OK] /IndexedDB/cursor-overloads.any.html (#42437)
  • OK [expected ERROR] /IndexedDB/cursor-overloads.any.worker.html (#42324)
  • OK /IndexedDB/transaction-deactivation-timing.any.html (#38772)
    • PASS [expected FAIL] subtest: New transactions are deactivated before next task
    • PASS [expected FAIL] subtest: New transactions from microtask are deactivated before next task
  • 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
      

  • OK /_mozilla/mozilla/getBoundingClientRect.html (#39668)
    • FAIL [expected PASS] subtest: getBoundingClientRect 1

      assert_equals: expected 62 but got 60.35
      

  • TIMEOUT [expected OK] /credential-management/credentialscontainer-frame-basics.https.html (#39430)
    • TIMEOUT [expected FAIL] subtest: navigator.credentials should be undefined in documents generated from data: URLs.

      Test timed out
      

  • FAIL [expected PASS] /css/css-backgrounds/border-image-repeat-space-9.html
  • OK /css/css-fonts/generic-family-keywords-002.html (#40929)
    • FAIL [expected PASS] subtest: font-family: -webkit-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-sans-serif treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-cursive treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-fantasy treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-monospace treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-system-ui treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • FAIL [expected PASS] subtest: font-family: -webkit-math treated as &lt;font-family&gt;, not &lt;generic-name&gt;

      assert_equals: expected 30 but got 50
      

    • PASS [expected FAIL] subtest: font-family: -webkit-generic(fangsong) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(kai) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • PASS [expected FAIL] subtest: font-family: -webkit-generic(khmer-mul) treated as &lt;font-family&gt;, not &lt;generic-name&gt;
    • And 12 more unexpected results...
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • 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 monospace (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 math (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(fangsong) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(khmer-mul) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
  • TIMEOUT [expected OK] /fetch/api/redirect/redirect-keepalive.https.any.html (#32153)
    • TIMEOUT [expected PASS] subtest: [keepalive][iframe][load] mixed content redirect; setting up

      Test timed out
      

  • TIMEOUT /fetch/metadata/generated/css-images.https.sub.tentative.html (#42229)
    • PASS [expected FAIL] subtest: content sec-fetch-site - Same-Origin -&gt; Same Origin
  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

      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.
      

  • ERROR [expected OK] /focus/focus-event-after-switching-iframes.sub.html (#40368)
  • 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.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • OK /html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-disabled-by-feature-policy.https.sub.html (#41221)
    • TIMEOUT [expected FAIL] subtest: Feature-Policy header: autoplay "none" disallows same-origin iframes.

      Test timed out
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/src_object_blob.html (#40340)
    • PASS [expected TIMEOUT] subtest: HTMLMediaElement.srcObject blob
  • 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.
  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • FAIL [expected PASS] subtest: print() during unload

      assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
      

  • OK /paint-timing/fcp-only/input-text.html (#42439)
    • FAIL [expected PASS] subtest: Text from a form control triggers First Contentful Paint.

      assert_equals: First contentful paint marked too early.  expected 0 but got 1
      

  • OK /paint-timing/first-image-child.html (#42455)
    • FAIL [expected PASS] subtest: Child iframe ignores paint-timing events fired from parent image rendering.

      assert_equals: expected "0" but got "1 paint first-paint"
      

  • PASS [expected FAIL] /png/apng/acTL-plays-one.html (#41218)
  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

      assert_equals: Number of entries does not match the expected value. expected 3 but got 0
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • TIMEOUT [expected PASS] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via anchor with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in enforcing mode.
  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • TIMEOUT [expected FAIL] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
  • OK /webdriver/tests/classic/take_screenshot/iframe.py
    • ERROR [expected PASS] subtest: test_always_captures_top_browsing_context

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

    • ERROR [expected PASS] subtest: test_source_origin[same_origin]

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

    • ERROR [expected PASS] subtest: test_source_origin[cross_origin]

      setup error: ConnectionRefusedError: [Errno 111] Connection refused
      

  • OK /webxr/xrSession_features_deviceSupport.https.html (#24357)
    • FAIL [expected PASS] subtest: Immersive XRSession requests with no supported device should reject

      assert_unreached: Should have rejected: undefined Reached unreachable code
      

  • CRASH [expected OK] /workers/WorkerNavigator_onLine.htm
  • ERROR [expected OK] /workers/baseurl/alpha/sharedworker-in-worker.html (#21315)
Stable unexpected results that are known to be intermittent (21)
  • 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
      

  • CRASH [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • 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 6 more unexpected results...
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "38.3166666666667px"
      

  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(kai)

      assert_equals: quoted generic(kai) matches  @font-face rule expected 30 but got 50
      

    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(nastaliq)

      assert_equals: quoted generic(nastaliq) matches  @font-face rule expected 30 but got 50
      

  • ERROR [expected OK] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • FAIL [expected PASS] subtest: sec-fetch-mode

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

  • 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
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination

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

  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • PASS [expected TIMEOUT] subtest: background-image sec-fetch-mode - Not sent to non-trustworthy same-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • FAIL [expected PASS] subtest: Same-Document Referrer from Refresh

      assert_equals: original page loads expected "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section#section"
      

  • 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])
      

  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node &lt;div autofocus=""&gt;&lt;/div&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus including no focusable descendants should be skipped

      assert_equals: expected Element node &lt;input autofocus=""&gt;&lt;/input&gt; but got Element node &lt;body&gt;&lt;div autofocus=""&gt;&lt;/div&gt;&lt;input autofocus=""&gt;&lt;/body&gt;
      

    • FAIL [expected NOTRUN] subtest: Area element should support autofocus

      promise_test: Unhandled rejection with value: object "TypeError: can't access property "appendChild", w.document.querySelector(...) is null"
      

  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • FAIL [expected TIMEOUT] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      assert_array_equals: animationFrame lengths differ, expected array ["autofocus", "scroll", "animationFrame"] length 3, got ["animationFrame"] length 1
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domComplete &gt; Original domComplete

      assert_true: Reload domComplete &gt; Original domComplete expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload domInteractive &gt; Original domInteractive

      assert_true: Reload domInteractive &gt; Original domInteractive expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventEnd &gt; Original loadEventEnd

      assert_true: Reload loadEventEnd &gt; Original loadEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventStart &gt; Original loadEventStart

      assert_true: Reload loadEventStart &gt; Original loadEventStart expected true got false
      

  • OK /paint-timing/fcp-only/fcp-with-rtl.html (#42357)
    • FAIL [expected PASS] subtest: FCP should fire when coordinates are negative, if within document scrollable area

      assert_equals: First contentful paint marked too early.  expected 0 but got 1
      

  • OK /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#40418)
    • PASS [expected FAIL] subtest: touch two-finger pan on 'touch-action: pan-x pan-y'
  • OK [expected CRASH] /resource-timing/render-blocking-status-link.html (#41664)
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission 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 form-submission 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!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ a default policy making the URL invalid in enforcing mode.

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

  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)

@github-actions
Copy link
Copy Markdown

✨ Try run (#21952571340) succeeded.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Feb 12, 2026
@Loirooriol Loirooriol added this pull request to the merge queue Feb 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 Feb 12, 2026
Merged via the queue into servo:main with commit 260eb86 Feb 12, 2026
71 checks passed
@Loirooriol Loirooriol deleted the try-to-reuse-boxes-in-subtrees branch February 12, 2026 16:55
@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 Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants