Skip to content

Conversation

@mrobinson
Copy link
Member

@mrobinson mrobinson commented Nov 25, 2025

This change is a major re-architecture of servoshell to support multiple
windows. Unfortunately it was not possible to do this incrementally, but
@mukilan and I did this together so we feel more confident about these
changes.

The main change here is that now the HashMap of windows that App
has can be filled with more than one ServoShellWindow.
ServoShellWindow is a wrapper around a PlatformWindow which can
either be headed, headless, or for embedded platforms. Embedded
platforms (Android and OHOS) are only expected to have a single window,
but there is no reason that more windows cannot be added.

There is still a little bit more work to be done in order to fully enable
mulitple windows, so this change is just the architectural preparation.

This change enables the embedded and desktop versions of servoshell to
start to be fully integrated so that the entire RunningAppState is
shared between them.

Testing: servoshell is the test harness so these changes are covered
by the WPT tests.

@mrobinson mrobinson changed the title servoshell: Add support for opening multiple windows servoshell: Add architectural support for opening multiple windows Nov 25, 2025
@mrobinson mrobinson force-pushed the servoshell-multiple-windows branch 2 times, most recently from 69e9047 to 84f243a Compare November 25, 2025 15:47
@jschwe
Copy link
Member

jschwe commented Nov 26, 2025

Embedded platforms (Android and OHOS) are only expected to have a single window, but there is no reason that more windows cannot be added.

Could you clarify the implications (of "expected") here? For ohos we definitly want / need multiple windows, and I would assume android would have similar use-cases for multiple windows.

@mrobinson
Copy link
Member Author

Could you clarify the implications (of "expected") here? For ohos we definitly want / need multiple windows, and I would assume android would have similar use-cases for multiple windows.

Ah, interesting. There is no reason that the embedded platforms cannot have more than one window in servoshell. We just did not plan to add interface elements to allow that -- and aren't certain what it means in fullscreen servoshell. From the perspective of Servo itself, creating multiple RenderingContexts (one per window) and running Servo in them should work just fine on any platform.

@jschwe
Copy link
Member

jschwe commented Nov 26, 2025

and aren't certain what it means in fullscreen servoshell

ohos / android don't necessiraly imply full screen. There are many device types, from normal smartphone, over foldables to tablets or even PCs. For the larger device-types (IMHO starting from foldable), having multi-window support makes sense, to support side-by-side viewing of different websites. (Split screen also works on my normal android phone as a top/bottom split, but imho it doesn't make too much sense without a foldable, since half the screen space is quite small)

From the perspective of Servo itself, creating multiple RenderingContexts (one per window) and running Servo in them should work just fine on any platform.

Thanks for clarifying! Another question - I've not been following the previous PRs, but is the servo backend using one webrender instance per rendering-context now, or just a single webrender instance?

Edit: To maybe clarify a bit more on the ohos use-case. As you know we primarily care about the webview use-case (where multi-window is obviously important), but being able to run servoshell with multiple windows would allow us to easily test multi-window functionality in servoshell upstream.

@mukilan
Copy link
Member

mukilan commented Nov 26, 2025

I've not been following the previous PRs, but is the servo backend using one webrender instance per rendering-context now, or just a single webrender instance?

Servo is using one WR instance per rendering-context. Using multiple documents (one per webview) in a single WebRender instance has issues. We've let Mozilla GFX team know about those and they are discussing internally if that is something that they can/want to support and how this fits into WR's roadmap.

@mrobinson
Copy link
Member Author

ohos / android don't necessiraly imply full screen. There are many device types, from normal smartphone, over foldables to tablets or even PCs. For the larger device-types (IMHO starting from foldable), having multi-window support makes sense, to support side-by-side viewing of different websites. (Split screen also works on my normal android phone as a top/bottom split, but imho it doesn't make too much sense without a foldable, since half the screen space is quite small)

Oh, for sure. We would need a bit more work to add multi-windows support for Android / OHOS servoshell as the interface elements aren't really there. Multi-window support will definitely be available on these platforms, but maybe not in the short-term for servoshell itself.

@mrobinson mrobinson force-pushed the servoshell-multiple-windows branch 4 times, most recently from 07dfa50 to ac557d0 Compare November 26, 2025 15:23
@mrobinson mrobinson added T-linux-wpt Do a try run of the WPT T-android Do a try run on Android T-ohos Do a try run on OpenHarmony labels Nov 26, 2025
@github-actions github-actions bot removed T-linux-wpt Do a try run of the WPT T-android Do a try run on Android T-ohos Do a try run on OpenHarmony labels Nov 26, 2025
@github-actions
Copy link

🔨 Triggering try run (#19708809664) for Linux (WPT), Android, OpenHarmony

@mrobinson mrobinson marked this pull request as ready for review November 26, 2025 15:24
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 26, 2025
@github-actions
Copy link

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

Flaky unexpected result (27)
  • OK /IndexedDB/idbfactory_open.any.html
    • FAIL [expected PASS] subtest: Calling open() with version argument 1.5 should not throw.

      assert_equals: version expected 1 but got 9007199254740991
      

  • 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
      

  • OK /_mozilla/webxr/create_session.https.html
    • FAIL [expected PASS] subtest: create_session

      can't access property "simulateDeviceConnection", navigator.xr.test is undefined
      

  • OK /_mozilla/webxr/obtain_frame.https.html
    • FAIL [expected PASS] subtest: obtain_frame

      promise_test: Unhandled rejection with value: object "TypeError: can't access property "simulateDeviceConnection", navigator.xr.test is undefined"
      

  • ERROR [expected TIMEOUT] /_mozilla/webxr/sessionavailable.https.html
  • CRASH [expected OK] /_webgl/conformance2/buffers/buffer-overflow-test.html
  • CRASH [expected OK] /credential-management/idlharness.https.window.html
  • FAIL [expected PASS] /css/css-backgrounds/background-size-041.html
  • TIMEOUT [expected FAIL] /css/css-values/attr-length-invalid-cast.html
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
    • PASS [expected FAIL] subtest: Multiple values - name content attribute is ignored
  • OK /fetch/fetch-later/permissions-policy/deferred-fetch-allowed-by-permissions-policy.https.window.html (#40478)
    • PASS [expected FAIL] subtest: Permissions policy header: "deferred-fetch=*" allows fetchLater() in the top-level document.
  • CRASH [expected OK] /fetch/metadata/generated/element-iframe.https.sub.html (#40341)
  • 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."
      

  • CRASH [expected OK] /html/browsers/browsing-the-web/history-traversal/001.html
  • 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/replace-before-load/a-click.html (#28697)
    • PASS [expected FAIL] subtest: aElement.click() before the load event must NOT replace
  • CRASH [expected OK] /html/browsers/the-window-object/open-close/open-features-tokenization-screenx-screeny.html
  • ERROR [expected OK] /html/semantics/embedded-content/media-elements/event_timeupdate_noautoplay.html (#25046)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: Basic test (formdata event)

      assert_equals: expected "basic=test\r\n" but got ""
      

    • FAIL [expected PASS] subtest: text/plain: Basic File test (normal form)

      assert_equals: expected "basic=file-test.txt\r\n" but got ""
      

    • PASS [expected FAIL] subtest: text/plain: Basic File test (formdata event)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in value (formdata event)
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: Basic File test (formdata event)
  • OK /html/syntax/speculative-parsing/expect-no-linked-resources/no-speculative-fetch.tentative.optional.html
    • FAIL [expected PASS] subtest: expect-no-linked-resources hint was ignored

      assert_equals: speculative case fetched expected "" but got "9fe6c6ec-f109-4b87-88fd-c83f2a7755e4"
      

  • CRASH [expected OK] /html/webappapis/scripting/reporterror.any.worker.html
  • CRASH [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#40418)
  • 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
      

  • OK /service-workers/service-worker/fetch-event.https.html (#36234)
    • PASS [expected FAIL] subtest: Service Worker falls back to network in fetch event with POST form
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • 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!\""
      

Stable unexpected results that are known to be intermittent (29)
  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/transaction-deactivation-timing.any.worker.html (#38808)
    • PASS [expected FAIL] subtest: New transactions are deactivated before next task
  • 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 /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • PASS [expected FAIL] subtest: WebGL test #45
    • PASS [expected FAIL] subtest: WebGL test #47
    • PASS [expected FAIL] subtest: WebGL test #49
    • PASS [expected FAIL] subtest: WebGL test #51
    • FAIL [expected PASS] subtest: WebGL test #53

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

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

      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 #57

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

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

      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 #61
    • PASS [expected FAIL] subtest: WebGL test #63
    • And 18 more unexpected results...
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)

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

  • FAIL [expected PASS] /css/css-grid/grid-lanes/tentative/alignment/row-grid-lanes-align-self-003.html (#40833)
  • FAIL [expected PASS] /css/css-grid/grid-lanes/tentative/grid-placement/row-explicit-placement-004.html (#40834)
  • FAIL [expected PASS] /css/css-grid/masonry/tentative/subgrid/column/masonry-subgrid-002f.html (#40830)
  • 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
      

  • OK /fetch/fetch-later/send-on-discard/not-send-after-abort.https.window.html (#40696)
    • FAIL [expected PASS] subtest: A discarded document does not send an already aborted fetchLater request.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

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

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

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

  • OK /fetch/metadata/generated/element-img-environment-change.https.sub.html (#30111)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same site, no attributes

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

    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes

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

    • FAIL [expected PASS] subtest: sec-fetch-site - Cross-Site -> Same-Site, no attributes

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

    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -> Cross-Site, no attributes

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

    • PASS [expected FAIL] subtest: sec-fetch-site - Same-Site -> Cross-Site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-mode - attributes: crossorigin

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

    • PASS [expected FAIL] subtest: sec-fetch-user - no attributes
  • OK /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes

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

    • FAIL [expected PASS] subtest: sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes

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

    • PASS [expected FAIL] subtest: sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - HTTPS downgrade-upgrade, no attributes

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

  • 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/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_3.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • FAIL [expected TIMEOUT] subtest: <dialog>-contained autofocus element gets focused when the dialog is shown

      assert_equals: expected "DIV" but got "BODY"
      

  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Element with tabindex should support autofocus

      assert_equals: expected "SPAN" but got "BODY"
      

    • TIMEOUT [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus

      Test timed out
      

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

      Test timed out
      

  • OK /html/semantics/embedded-content/media-elements/preserves-pitch.html (#40352)
    • FAIL [expected PASS] subtest: Speed-ups should not change the pitch when preservesPitch=true

      assert_approx_equals: The actual pitch should be close to the expected pitch. expected 440 +/- 66 but got 0
      

  • 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 /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart
    • PASS [expected FAIL] subtest: Reload domInteractive > Original domInteractive
    • PASS [expected FAIL] subtest: Reload fetchStart > Original fetchStart
  • OK /preload/preload-error.sub.html (#37177)
    • FAIL [expected PASS] subtest: 404 (fetch): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?pipe=status%28404%29&label=fetch should be loaded expected a number greater than 0 but got 0
      

  • OK /reporting/same-origin-same-site-credentials.https.sub.html (#40479)
    • FAIL [expected PASS] subtest: Reporting endpoints received credentials.

      assert_equals: No additional cookies were received expected 4 but got 5
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected FAIL] 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 FAIL] 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] /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!\""
      

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

🐰 Bencher Report

Branch40883/PR
TestbedHUAWEI Mate 60 Pro

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkDataMeasure (units) x 1e3Latencymilliseconds (ms)MemoryBytesscoreMeasure (units)
release/E2E/file:///parse_from_string.html/📈 view plot
⚠️ NO THRESHOLD
1.75 units x 1e3
release/E2E/https://www.google.com/JS/gc-heap/admin📈 view plot
⚠️ NO THRESHOLD
26,752.00
release/E2E/https://www.google.com/JS/gc-heap/decommitted📈 view plot
⚠️ NO THRESHOLD
409,600.00
release/E2E/https://www.google.com/JS/gc-heap/unused📈 view plot
⚠️ NO THRESHOLD
136,016.00
release/E2E/https://www.google.com/JS/gc-heap/used📈 view plot
⚠️ NO THRESHOLD
476,208.00
release/E2E/https://www.google.com/JS/malloc-heap📈 view plot
⚠️ NO THRESHOLD
5,053,023.00
release/E2E/https://www.google.com/JS/non-heap📈 view plot
⚠️ NO THRESHOLD
262,144.00
release/E2E/https://www.google.com/LayoutThread/box-tree📈 view plot
⚠️ NO THRESHOLD
108,744.00
release/E2E/https://www.google.com/LayoutThread/display-list📈 view plot
⚠️ NO THRESHOLD
0.00
release/E2E/https://www.google.com/LayoutThread/font-context📈 view plot
⚠️ NO THRESHOLD
9,288.00
release/E2E/https://www.google.com/LayoutThread/fragment-tree📈 view plot
⚠️ NO THRESHOLD
112.00
release/E2E/https://www.google.com/LayoutThread/stacking-context-tree📈 view plot
⚠️ NO THRESHOLD
14,080.00
release/E2E/https://www.google.com/LayoutThread/stylist📈 view plot
⚠️ NO THRESHOLD
5,504.00
release/E2E/https://www.google.com/image-cache📈 view plot
⚠️ NO THRESHOLD
2,328.00
release/E2E/https://www.google.com/resident-smaps📈 view plot
⚠️ NO THRESHOLD
373,043,200.00
release/E2E/https://www.servo.org/Load📈 view plot
⚠️ NO THRESHOLD
925.31 ms
release/E2E/https://www.servo.org/Resident📈 view plot
⚠️ NO THRESHOLD
370,846,924.00
release/E2E/https://www.servo.org/resident-smaps📈 view plot
⚠️ NO THRESHOLD
371,208,192.00
release/Speedometer/Charts-observable-plot📈 view plot
⚠️ NO THRESHOLD
637.14 ms
release/Speedometer/Charts-observable-plot/Dotted📈 view plot
⚠️ NO THRESHOLD
79.31 ms
release/Speedometer/Charts-observable-plot/Dotted/Async📈 view plot
⚠️ NO THRESHOLD
9.68 ms
release/Speedometer/Charts-observable-plot/Dotted/Sync📈 view plot
⚠️ NO THRESHOLD
69.63 ms
release/Speedometer/Charts-observable-plot/Stacked by 20📈 view plot
⚠️ NO THRESHOLD
313.76 ms
release/Speedometer/Charts-observable-plot/Stacked by 20/Async📈 view plot
⚠️ NO THRESHOLD
17.82 ms
release/Speedometer/Charts-observable-plot/Stacked by 20/Sync📈 view plot
⚠️ NO THRESHOLD
295.94 ms
release/Speedometer/Charts-observable-plot/Stacked by 6📈 view plot
⚠️ NO THRESHOLD
244.07 ms
release/Speedometer/Charts-observable-plot/Stacked by 6/Async📈 view plot
⚠️ NO THRESHOLD
9.02 ms
release/Speedometer/Charts-observable-plot/Stacked by 6/Sync📈 view plot
⚠️ NO THRESHOLD
235.04 ms
release/Speedometer/Geomean📈 view plot
⚠️ NO THRESHOLD
635.77 ms
release/Speedometer/Iteration-0-Total📈 view plot
⚠️ NO THRESHOLD
836.53 ms
release/Speedometer/Iteration-1-Total📈 view plot
⚠️ NO THRESHOLD
817.54 ms
release/Speedometer/Iteration-2-Total📈 view plot
⚠️ NO THRESHOLD
821.20 ms
release/Speedometer/Iteration-3-Total📈 view plot
⚠️ NO THRESHOLD
835.81 ms
release/Speedometer/Iteration-4-Total📈 view plot
⚠️ NO THRESHOLD
840.20 ms
release/Speedometer/Iteration-5-Total📈 view plot
⚠️ NO THRESHOLD
832.94 ms
release/Speedometer/Iteration-6-Total📈 view plot
⚠️ NO THRESHOLD
834.38 ms
release/Speedometer/Iteration-7-Total📈 view plot
⚠️ NO THRESHOLD
863.92 ms
release/Speedometer/Iteration-8-Total📈 view plot
⚠️ NO THRESHOLD
854.85 ms
release/Speedometer/Iteration-9-Total📈 view plot
⚠️ NO THRESHOLD
864.15 ms
release/Speedometer/Score📈 view plot
⚠️ NO THRESHOLD
1.58 units
release/Speedometer/TodoMVC-Angular📈 view plot
⚠️ NO THRESHOLD
871.30 ms
release/Speedometer/TodoMVC-Angular/Adding100Items📈 view plot
⚠️ NO THRESHOLD
430.05 ms
release/Speedometer/TodoMVC-Angular/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
61.66 ms
release/Speedometer/TodoMVC-Angular/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
368.39 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
283.54 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
69.12 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
214.42 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
157.70 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
6.59 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
151.11 ms
release/Speedometer/TodoMVC-JavaScript-ES5📈 view plot
⚠️ NO THRESHOLD
1,317.93 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items📈 view plot
⚠️ NO THRESHOLD
1,092.93 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
98.84 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
994.09 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
142.35 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
29.81 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
112.54 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
82.65 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
6.40 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
76.25 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack📈 view plot
⚠️ NO THRESHOLD
1,822.04 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items📈 view plot
⚠️ NO THRESHOLD
1,486.02 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
85.46 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
1,400.55 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
211.17 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
34.71 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
176.46 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
124.85 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
7.38 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
117.46 ms
release/Speedometer/TodoMVC-Preact📈 view plot
⚠️ NO THRESHOLD
171.85 ms
release/Speedometer/TodoMVC-Preact/Adding100Items📈 view plot
⚠️ NO THRESHOLD
81.04 ms
release/Speedometer/TodoMVC-Preact/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
73.86 ms
release/Speedometer/TodoMVC-Preact/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
7.18 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
77.17 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
65.16 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
12.01 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
13.63 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.75 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
4.89 ms
release/Speedometer/TodoMVC-React📈 view plot
⚠️ NO THRESHOLD
810.64 ms
release/Speedometer/TodoMVC-React-Redux📈 view plot
⚠️ NO THRESHOLD
930.37 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items📈 view plot
⚠️ NO THRESHOLD
333.45 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
66.66 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
266.79 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
386.05 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
36.91 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
349.14 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
210.86 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
7.78 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
203.08 ms
release/Speedometer/TodoMVC-React/Adding100Items📈 view plot
⚠️ NO THRESHOLD
308.12 ms
release/Speedometer/TodoMVC-React/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
69.27 ms
release/Speedometer/TodoMVC-React/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
238.84 ms
release/Speedometer/TodoMVC-React/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
332.41 ms
release/Speedometer/TodoMVC-React/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
68.96 ms
release/Speedometer/TodoMVC-React/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
263.45 ms
release/Speedometer/TodoMVC-React/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
170.11 ms
release/Speedometer/TodoMVC-React/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
7.59 ms
release/Speedometer/TodoMVC-React/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
162.52 ms
release/Speedometer/TodoMVC-Svelte📈 view plot
⚠️ NO THRESHOLD
159.98 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items📈 view plot
⚠️ NO THRESHOLD
82.27 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
65.33 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
16.94 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
57.63 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
49.44 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
8.19 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
20.07 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
16.15 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
3.92 ms
🐰 View full continuous benchmarking report in Bencher

This change is a major re-architecture of servoshell to support multiple
windows. Unfortunately it was not possible to do this incrementally, but
@mukilan and I did this together so we feel more confident about these
changes.

The main change here is that now the `HashMap` of windows that `App`
has can be filled with more than one `ServoShellWindow`.
`ServoShellWindow` is a wrapper around a `PlatformWindow` which can
either be headed, headless, or for embedded platforms. Embedded
platforms (Android and OHOS) are only expected to have a single window,
but there is no reason that more windows cannot be added.

This change enables the embedded and desktop versions of servoshell to
start to be fully integrated so that the entire `RunningAppState` is
shared between them.

Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson mrobinson force-pushed the servoshell-multiple-windows branch from ac557d0 to ea91dd1 Compare November 26, 2025 18:04
@mrobinson mrobinson added the T-ohos Do a try run on OpenHarmony label Nov 26, 2025
@github-actions github-actions bot removed the T-ohos Do a try run on OpenHarmony label Nov 26, 2025
@github-actions
Copy link

🔨 Triggering try run (#19713821795) for OpenHarmony

@github-actions
Copy link

✨ Try run (#19713821795) succeeded.

@jschwe
Copy link
Member

jschwe commented Nov 26, 2025

FYI, the one failing ohos test is also failing on main (most of the time), so the panic seems to have been fixed.

@mukilan
Copy link
Member

mukilan commented Nov 27, 2025

I've tested this on NixOS+KDE+Wayland and found an issue. When the window is closed, the app seems to "hang" and I get the following backtrace in the console:

✦ ❯ ./mach run -r
NOTE: Entering nix-shell ./shell.nix
mozilla::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed: Device or resource busy
Caught signal 11 in thread "main"
   0: servoshell::backtrace::print
   1: servoshell::crash_handler::install::handler
   2: <unknown>
   3: _ZN7mozilla6detail9MutexImplD1Ev
   4: __run_exit_handlers
   5: exit
   6: __libc_start_call_main
   7: __libc_start_main_alias_1
   8: _start

I think this happens when servo instance is shutdown before the JS runtime gets destroyed. I'll try to dig deeper.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson
Copy link
Member Author

mrobinson commented Nov 27, 2025

I've tested this on NixOS+KDE+Wayland and found an issue. When the window is closed, the app seems to "hang" and I get the following backtrace in the console:

I think I've tracked this down to the order we were letting things go when a window was removed. I've modified the code to schedule a window close instead of doing it immediately. I think this allows things to be released in the right order and I no longer see this issue on my Linux system.

@Narfinger
Copy link
Contributor

Wanted to confirm that it works currently on ohos. The only thing that doesn't work is the make new tab feature. I think it is fine to disable it for now and fix it later.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 27, 2025
@mrobinson
Copy link
Member Author

mrobinson commented Nov 27, 2025

Wanted to confirm that it works currently on ohos. The only thing that doesn't work is the make new tab feature. I think it is fine to disable it for now and fix it later.

Do you have any more details on errors that are emitted when using the new tab feature? Maybe we can fix it before landing this?

@mrobinson
Copy link
Member Author

I suspect that that if the new tab feature is broken by the ordering of WebViews in the collection, that it will be fixed by a followup change. I'll go ahead and land this.

@mrobinson mrobinson added this pull request to the merge queue Nov 27, 2025
@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 Nov 27, 2025
Merged via the queue into servo:main with commit 6d4937b Nov 27, 2025
29 checks passed
@mrobinson mrobinson deleted the servoshell-multiple-windows branch November 27, 2025 11:49
@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 Nov 27, 2025
mrobinson added a commit to mrobinson/servo that referenced this pull request Nov 28, 2025
… cleanly

This is a speculative fix for a crash mentioned by @mukilan[^1]. It will
also make moving to a more consistent shutdown model (even one driven by
dropping of the `Servo` instance) possible.

[^1]: servo#40883 (comment)

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
github-merge-queue bot pushed a commit that referenced this pull request Nov 28, 2025
… cleanly (#40933)

This is a speculative fix for a crash mentioned by @mukilan[^1]. It will
also make moving to a more consistent shutdown model (even one driven by
dropping of the `Servo` instance) possible.

[^1]: #40883 (comment)

Testing: It's difficult to test this crash because it seems to only
happen on
nixOS and we currently don't have a good way to test crashes on exit.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
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.

5 participants