Skip to content

Conversation

@mrobinson
Copy link
Member

@mrobinson mrobinson commented Oct 22, 2025

This change adds a full implementation of pinch zoom, including
center-aware zooming. Before this kind of pinch zooming was only enabled
on OpenHarmony. Now all pinch zooms must come with a focal point, which
determines the center point of the zoom. This enables full pinch zoom on
Android and has OpenHarmony use the same system for pinch zoom.

Every WebView now has a PinchZoom which describes the viewport of the
pinch zoom and handles panning with proper chaining of zoom viewport
panning to scroll layer scrolling. In addition, the collection of touch
actions is simplified by storing an array and turning each into a
ScrollZoomEvent when appropriate.

Caveats:

  • We've noticed some hard to diagnose bugs with clamping the panning
    viewport, but we'll tackle those later once we figure out how to
    reliably reproduce them.
  • Keyboard scroll events currently do not properly pan the pinch zoom
    viewport. This will be handled in a followup.

Testing: There are currently no tests for this kind of touch interaction
as there's no way to read the pinch zoom from a WebView. It's processed
asynchronously. Once that API is added, we should be able to add some
simple tests, but many things are still unaccessible such as the pan
position in the pinch zoom viewport.
Fixes #4224.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Oct 22, 2025
@mrobinson mrobinson added T-linux-wpt Do a try run of the WPT T-ohos Do a try run on OpenHarmony T-android Do a try run on Android labels Oct 22, 2025
@github-actions github-actions bot removed T-linux-wpt Do a try run of the WPT T-ohos Do a try run on OpenHarmony labels Oct 22, 2025
@github-actions
Copy link

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

@github-actions github-actions bot removed the T-android Do a try run on Android label Oct 22, 2025
@github-actions
Copy link

🔨 Triggering try run (#18721882069) for Android

@github-actions
Copy link

🐰 Bencher Report

Branch40083/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.76 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
137,960.00
release/E2E/https://www.google.com/JS/gc-heap/used📈 view plot
⚠️ NO THRESHOLD
474,264.00
release/E2E/https://www.google.com/JS/malloc-heap📈 view plot
⚠️ NO THRESHOLD
5,051,739.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
107,632.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
8,616.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,104.00
release/E2E/https://www.google.com/Load📈 view plot
⚠️ NO THRESHOLD
612.31 ms
release/E2E/https://www.google.com/Resident📈 view plot
⚠️ NO THRESHOLD
183,901,388.00
release/E2E/https://www.google.com/image-cache📈 view plot
⚠️ NO THRESHOLD
35,760.00
release/E2E/https://www.google.com/resident-smaps📈 view plot
⚠️ NO THRESHOLD
186,644,070.00
release/E2E/https://www.servo.org/Load📈 view plot
⚠️ NO THRESHOLD
832.80 ms
release/E2E/https://www.servo.org/Resident📈 view plot
⚠️ NO THRESHOLD
257,444,249.00
release/E2E/https://www.servo.org/resident-smaps📈 view plot
⚠️ NO THRESHOLD
258,657,484.00
release/Speedometer/Charts-observable-plot📈 view plot
⚠️ NO THRESHOLD
892.85 ms
release/Speedometer/Charts-observable-plot/Dotted📈 view plot
⚠️ NO THRESHOLD
107.40 ms
release/Speedometer/Charts-observable-plot/Dotted/Async📈 view plot
⚠️ NO THRESHOLD
10.64 ms
release/Speedometer/Charts-observable-plot/Dotted/Sync📈 view plot
⚠️ NO THRESHOLD
96.76 ms
release/Speedometer/Charts-observable-plot/Stacked by 20📈 view plot
⚠️ NO THRESHOLD
441.28 ms
release/Speedometer/Charts-observable-plot/Stacked by 20/Async📈 view plot
⚠️ NO THRESHOLD
19.42 ms
release/Speedometer/Charts-observable-plot/Stacked by 20/Sync📈 view plot
⚠️ NO THRESHOLD
421.86 ms
release/Speedometer/Charts-observable-plot/Stacked by 6📈 view plot
⚠️ NO THRESHOLD
344.16 ms
release/Speedometer/Charts-observable-plot/Stacked by 6/Async📈 view plot
⚠️ NO THRESHOLD
10.25 ms
release/Speedometer/Charts-observable-plot/Stacked by 6/Sync📈 view plot
⚠️ NO THRESHOLD
333.91 ms
release/Speedometer/Geomean📈 view plot
⚠️ NO THRESHOLD
756.41 ms
release/Speedometer/Iteration-0-Total📈 view plot
⚠️ NO THRESHOLD
886.11 ms
release/Speedometer/Iteration-1-Total📈 view plot
⚠️ NO THRESHOLD
1,069.73 ms
release/Speedometer/Iteration-2-Total📈 view plot
⚠️ NO THRESHOLD
1,076.98 ms
release/Speedometer/Iteration-3-Total📈 view plot
⚠️ NO THRESHOLD
1,085.99 ms
release/Speedometer/Iteration-4-Total📈 view plot
⚠️ NO THRESHOLD
1,080.25 ms
release/Speedometer/Iteration-5-Total📈 view plot
⚠️ NO THRESHOLD
1,074.37 ms
release/Speedometer/Iteration-6-Total📈 view plot
⚠️ NO THRESHOLD
1,083.27 ms
release/Speedometer/Iteration-7-Total📈 view plot
⚠️ NO THRESHOLD
1,095.31 ms
release/Speedometer/Iteration-8-Total📈 view plot
⚠️ NO THRESHOLD
1,084.66 ms
release/Speedometer/Iteration-9-Total📈 view plot
⚠️ NO THRESHOLD
1,078.65 ms
release/Speedometer/Score📈 view plot
⚠️ NO THRESHOLD
1.32 units
release/Speedometer/TodoMVC-Angular📈 view plot
⚠️ NO THRESHOLD
1,130.36 ms
release/Speedometer/TodoMVC-Angular/Adding100Items📈 view plot
⚠️ NO THRESHOLD
530.05 ms
release/Speedometer/TodoMVC-Angular/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
38.93 ms
release/Speedometer/TodoMVC-Angular/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
491.12 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
375.05 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
45.19 ms
release/Speedometer/TodoMVC-Angular/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
329.86 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
225.26 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.68 ms
release/Speedometer/TodoMVC-Angular/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
216.58 ms
release/Speedometer/TodoMVC-JavaScript-ES5📈 view plot
⚠️ NO THRESHOLD
1,637.18 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items📈 view plot
⚠️ NO THRESHOLD
1,332.92 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
58.54 ms
release/Speedometer/TodoMVC-JavaScript-ES5/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
1,274.37 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
189.11 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
31.43 ms
release/Speedometer/TodoMVC-JavaScript-ES5/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
157.68 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
115.15 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.42 ms
release/Speedometer/TodoMVC-JavaScript-ES5/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
106.74 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack📈 view plot
⚠️ NO THRESHOLD
2,346.24 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items📈 view plot
⚠️ NO THRESHOLD
1,895.31 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
36.96 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
1,858.35 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
282.91 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
38.52 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
244.39 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
168.02 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.03 ms
release/Speedometer/TodoMVC-JavaScript-ES6-Webpack/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
159.99 ms
release/Speedometer/TodoMVC-Preact📈 view plot
⚠️ NO THRESHOLD
168.61 ms
release/Speedometer/TodoMVC-Preact/Adding100Items📈 view plot
⚠️ NO THRESHOLD
83.60 ms
release/Speedometer/TodoMVC-Preact/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
75.69 ms
release/Speedometer/TodoMVC-Preact/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
7.91 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
66.58 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
52.60 ms
release/Speedometer/TodoMVC-Preact/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
13.98 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
18.44 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
11.65 ms
release/Speedometer/TodoMVC-Preact/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
6.78 ms
release/Speedometer/TodoMVC-React📈 view plot
⚠️ NO THRESHOLD
967.56 ms
release/Speedometer/TodoMVC-React-Redux📈 view plot
⚠️ NO THRESHOLD
1,208.29 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items📈 view plot
⚠️ NO THRESHOLD
388.68 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
42.48 ms
release/Speedometer/TodoMVC-React-Redux/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
346.21 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
538.21 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
43.34 ms
release/Speedometer/TodoMVC-React-Redux/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
494.88 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
281.40 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.60 ms
release/Speedometer/TodoMVC-React-Redux/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
272.80 ms
release/Speedometer/TodoMVC-React/Adding100Items📈 view plot
⚠️ NO THRESHOLD
338.95 ms
release/Speedometer/TodoMVC-React/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
45.49 ms
release/Speedometer/TodoMVC-React/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
293.46 ms
release/Speedometer/TodoMVC-React/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
406.21 ms
release/Speedometer/TodoMVC-React/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
39.03 ms
release/Speedometer/TodoMVC-React/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
367.18 ms
release/Speedometer/TodoMVC-React/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
222.40 ms
release/Speedometer/TodoMVC-React/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
8.47 ms
release/Speedometer/TodoMVC-React/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
213.93 ms
release/Speedometer/TodoMVC-Svelte📈 view plot
⚠️ NO THRESHOLD
141.18 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items📈 view plot
⚠️ NO THRESHOLD
76.93 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items/Async📈 view plot
⚠️ NO THRESHOLD
58.41 ms
release/Speedometer/TodoMVC-Svelte/Adding100Items/Sync📈 view plot
⚠️ NO THRESHOLD
18.52 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems📈 view plot
⚠️ NO THRESHOLD
47.17 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
37.65 ms
release/Speedometer/TodoMVC-Svelte/CompletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
9.52 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems📈 view plot
⚠️ NO THRESHOLD
17.08 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems/Async📈 view plot
⚠️ NO THRESHOLD
12.03 ms
release/Speedometer/TodoMVC-Svelte/DeletingAllItems/Sync📈 view plot
⚠️ NO THRESHOLD
5.05 ms
🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

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

Flaky unexpected result (23)
  • 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 occurred"
      

  • 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 /_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 6 more unexpected results...
  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted serif (drawing text in a canvas)

      assert_equals: quoted serif matches  @font-face rule expected 125 but got 40
      

  • 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-site destination
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination
  • 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 /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html (#34819)
    • PASS [expected FAIL] subtest: link click
  • 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-fragment.html (#20768)
    • FAIL [expected PASS] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation

      assert_equals: expected "" but got "#fragment"
      

  • 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
  • TIMEOUT /html/browsers/history/the-history-interface/001.html (#12580)
    • FAIL [expected PASS] subtest: traversing history must also traverse hash changes

      assert_equals: (this could cause other failures later on) expected "" but got "test"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • 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 /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]
      }"
      

  • OK [expected 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"
      

    • PASS [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus
    • FAIL [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      assert_equals: expected Element node <div autofocus=""></div> but got Element node <body></body>
      

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

      assert_equals: expected Element node <input autofocus=""></input> but got Element node <body></body>
      

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

      assert_equals: expected Element node <area autofocus="" href="/common/blank.html"></area> but got Element node <body>
      <img src="/media/poster.png" usemap="#map">
      <map n...
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • FAIL [expected PASS] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe

      uncaught exception: Error: assert_unreached: The iframe into which the meta was moved must not refresh Reached unreachable code
      

  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • PASS [expected FAIL] subtest: print() during unload
  • TIMEOUT [expected OK] /preload/preload-error.sub.html (#37177)
    • NOTRUN [expected PASS] subtest: CSP-error (style): preload events
    • PASS [expected FAIL] subtest: success (fetch): main
    • PASS [expected FAIL] subtest: CORS (fetch): main
  • ERROR /service-workers/idlharness.https.any.html (#36250)
    • PASS [expected TIMEOUT] subtest: ServiceWorkerContainer interface: operation register((TrustedScriptURL or USVString), optional RegistrationOptions)
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation enable()
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation disable()
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation setHeaderValue(ByteString)
    • PASS [expected TIMEOUT] subtest: NavigationPreloadManager interface: operation getState()
  • OK /wasm/webapi/abort.any.html (#39966)
    • FAIL [expected PASS] subtest: instantiateStreaming() asynchronously racing with abort should succeed or reject with AbortError

      assert_equals: expected "AbortError" but got "CompileError"
      

  • OK [expected TIMEOUT] /webmessaging/with-ports/018.html (#24485)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, javascript:
  • OK [expected TIMEOUT] /webmessaging/without-ports/018.html (#24485)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, javascript:
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
Stable unexpected results that are known to be intermittent (18)
  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • 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/key-conversion-exceptions.any.worker.html (#39284)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected FAIL] /dom/xslt/large-cdata.html (#38029)
  • 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/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-site - Same origin
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • PASS [expected FAIL] subtest: Check execution order on load handler
    • PASS [expected FAIL] subtest: Check execution order from nested timeout
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html (#29048)
    • FAIL [expected PASS] subtest: Navigating to a different document with location.assign

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?1" but got "about:blank"
      

  • 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
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.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)
  • 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/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (normal form)

      assert_equals: expected "basic=test" but got ""
      

  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domComplete > Original domComplete

      assert_true: Reload domComplete > Original domComplete expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart

      assert_true: Reload domContentLoadedEventStart > Original domContentLoadedEventStart expected true got false
      

    • FAIL [expected PASS] subtest: Reload fetchStart > Original fetchStart

      assert_true: Reload fetchStart > Original fetchStart expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventEnd > Original loadEventEnd

      assert_true: Reload loadEventEnd > Original loadEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventStart > Original loadEventStart

      assert_true: Reload loadEventStart > Original loadEventStart expected true got false
      

  • CRASH [expected OK] /websockets/unload-a-document/001.html?wss (#40072)

@github-actions
Copy link

✨ Try run (#18721878997) succeeded.

@github-actions
Copy link

✨ Try run (#18721882069) succeeded.

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Oct 23, 2025
@atbrakhi atbrakhi added this pull request to the merge queue Oct 23, 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 Oct 23, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to a conflict with the base branch Oct 23, 2025
@servo-highfive servo-highfive added S-needs-rebase There are merge conflict errors. and removed S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. labels Oct 23, 2025
This change adds a full implementation of pinch zoom, including
center-aware zooming. Before this kind of pinch zooming was only enabled
on OpenHarmony. Now all pinch zooms must come with a focal point, which
determines the center point of the zoom. This enables full pinch zoom on
Android and has OpenHarmony use the same system for pinch zoom.

Every WebView now has a `PinchZoom` which describes the viewport of the
pinch zoom and handles panning with proper chaining of zoom viewport
panning to scroll layer scrolling.  In addition, the collection of touch
actions is simplified by storing an array and turning each into a
ScrollZoomEvent when appropriate.

Note: We've noticed some hard to diagnose bugs with clamping the panning
viewport, but we'll tackle those later once we figure out how to
reliably reproduce them.

Fixes servo#4224.

Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. and removed S-needs-rebase There are merge conflict errors. labels Oct 23, 2025
@mrobinson mrobinson enabled auto-merge October 23, 2025 13:15
@mrobinson mrobinson added this pull request to the merge queue Oct 23, 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 Oct 23, 2025
Merged via the queue into servo:main with commit 37ae099 Oct 23, 2025
33 checks passed
@mrobinson mrobinson deleted the pinch-zoom branch October 23, 2025 14:38
@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 Oct 23, 2025
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.

Pinch zoom should take the mouse position into account

3 participants