Skip to content

layout: Map document selection offsets across inline formatting context text transformations - #46864

Merged
mrobinson merged 19 commits into
servo:mainfrom
mrobinson:offsetmap
Jul 30, 2026
Merged

layout: Map document selection offsets across inline formatting context text transformations#46864
mrobinson merged 19 commits into
servo:mainfrom
mrobinson:offsetmap

Conversation

@mrobinson

Copy link
Copy Markdown
Member

This change adds an OffsetMap type which is used to map DOM text
offsets to post-text transformation offsets during inline formatting
context construction. This is necessary to properly display selections
across text that is modified due to white space collapsing or CSS
text-transform processing.

This change fixes on test case in WPT dealing with text-transform
as a side effect.

Testing: This change updates WPT tests results.

mrobinson and others added 13 commits July 28, 2026 15:03
…xt text transformations

This change adds an `OffsetMap` type which is used to map DOM text
offsets to post-text transformation offsets during inline formatting
context construction. This is necessary to properly display selections
across text that is modified due to white space collapsing or CSS
`text-transform` processing.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
It’s always the same input type, no need to make it `Box<dyn …>`

Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
This enables binary search instead of linear scan, and reverse mapping

Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
@mrobinson
mrobinson requested a review from Loirooriol as a code owner July 29, 2026 16:14
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 29, 2026
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Jul 29, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Jul 29, 2026
@github-actions

Copy link
Copy Markdown

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

@codecov-commenter

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

Instead of a semantic representation have `CharacterTransformIteration`
be a bit more generic. Also do a few minor cleanups:

- Remove
  `InlineFormattingContextBuilder::current_original_character_offset`.
  This can just be `OffsetMap::current_original_character_offset`-
- Reorder some things so that they are gruoped together:wq
  logically

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Comment on lines +35 to +36
/// The character that were produced during this iteration.
characters: [char; 3],

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.

Let’s add a comment that only the self.characters[..self.produced.0] slice should be considered.

Alternatively, replace both fields with an ArrayVec (external crate but already in our dependencies) and use its .len() wherever produced is needed

Comment on lines +43 to +47
let mut characters = ['\0'; 3];
let mut produced = 0;
for (array_entry, character) in characters.iter_mut().zip(iterator) {
*array_entry = character;
produced += 1;

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.

With an ArrayVec this would be .collect()

Comment on lines +68 to +69
produced: Utf32CodeUnits(if character.is_some() { 1 } else { 0 }),
characters: [character.unwrap_or_default(), '\0', '\0'],

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.

With ArrayVec: option.into_iter().collect()

Comment on lines 77 to 85
pub(crate) fn each_char(self, mut each: impl FnMut(char)) {
match self {
CharacterTransformIteration::OneToOne(c) => each(c),
CharacterTransformIteration::WhitespaceCharsCollapsedToOneSpace(_) => each(' '),
CharacterTransformIteration::WhitespaceCharsCollapsedToOneNewline(_) => each('\n'),
CharacterTransformIteration::WhitespaceCharsCollapsedToNothing(_) => {},
CharacterTransformIteration::ToLowercase(iter) => iter.for_each(each),
CharacterTransformIteration::ToUppercase(iter) => iter.for_each(each),
CharacterTransformIteration::ToTitlecase(iter) => iter.for_each(each),
for character in &self.characters[..self.produced.0] {
each(*character)
}
}

pub fn push_chars_to(self, string: &mut String) {
pub(crate) fn push_chars_to(self, string: &mut String) {
self.each_char(|character| string.push(character));
}

@SimonSapin SimonSapin Jul 29, 2026

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.

Now that we no longer carry multiple iterator types, let’s replace these two push-based methods with one that returns &[char] (or no method at all, if the field is ArrayVec)

} else {
output.push(iteration);
*iteration =
CharacterTransformIteration::case_mapped(to_titlecase(iteration.characters[0]));

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.

If we’re back to a single use of to_titlecase let’s inline it

@github-actions

Copy link
Copy Markdown

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

Flaky unexpected result (41)
  • OK /IndexedDB/idbtransaction-oncomplete.any.html (#42845)
    • FAIL [expected PASS] subtest: IDBTransaction - complete event

      assert_array_equals: lengths differ, expected array ["upgradeneeded", "complete", "success", "opencursor"] length 4, got ["upgradeneeded", "complete", "success"] length 3
      

  • OK [expected TIMEOUT] /_webgl/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html (#39735)
    • PASS [expected NOTRUN] subtest: Overall test
    • PASS [expected FAIL] subtest: WebGL test #0
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/no-over-optimization-on-uniform-array-07.html
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected OK] /_webgl/conformance2/sync/sync-webgl-specific.html
  • OK /cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html (#45255)
    • FAIL [expected PASS] subtest: In embedded cross-site contexts, partitioned cookies can only be set with explicit SameSite=None

      assert_equals: expected 0 but got 3
      

  • CRASH [expected OK] /css/compositing/canvas-composite-modes.html
  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • FAIL [expected PASS] subtest: Matching font-style: 'italic' should prefer 'oblique 5deg' over 'normal'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 20deg' should prefer 'oblique 0deg' over 'oblique -50deg -20deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 21deg' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

  • FAIL [expected PASS] /css/css-sizing/dynamic-available-size-iframe.html (#45763)
  • FAIL [expected PASS] /css/css-text-decor/text-underline-position-from-font-variable.html (#45615)
  • 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
      

    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove self via replaceChildren()

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

  • TIMEOUT [expected OK] /fetch/content-encoding/br/big-br-body.https.any.html
    • TIMEOUT [expected PASS] subtest: large br data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large br data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/br/big-br-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large br data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large br data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/gzip/big-gzip-body.https.any.sharedworker.html
    • TIMEOUT [expected PASS] subtest: large gzip data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large gzip data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/gzip/big-gzip-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large gzip data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large gzip data should be decompressed successfully with byte stream
  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html (#44098)
    • FAIL [expected TIMEOUT] subtest: Replace before load, triggered by formElement.requestSubmit()

      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)&amp;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%20document.currentScript.before(form)%3B%0A%20%20%20%20form.requestSubmit()%3B%0A%20%20"
      

  • 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)&amp;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"
      

  • TIMEOUT /html/browsers/history/the-history-interface/001.html (#12580)
    • PASS [expected FAIL] subtest: traversing history must also traverse hash changes
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected ERROR] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • OK /html/browsers/windows/embedded-opener-remove-frame.html (#23867)
    • PASS [expected FAIL] subtest: opener of discarded auxiliary browsing context
  • TIMEOUT /html/semantics/embedded-content/media-elements/autoplay-default-permissions-policy.https.sub.html (#46805)
    • PASS [expected TIMEOUT] subtest: Default "autoplay" feature policy ["self"] allows same-origin iframes.
  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • FAIL [expected PASS] subtest: Video should seek to time specified in media fragment syntax

      assert_equals: expected 3 but got 0
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-reload-location-reload.html (#32595)
    • FAIL [expected PASS] subtest: Reloading iframe loading='lazy' before it is loaded: location.reload

      uncaught exception: Error: assert_equals: expected "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?src" but got "about:blank"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-video-element/video_timeupdate_on_seek.html
    • PASS [expected TIMEOUT] subtest: timeupdate is emitted after a seek before the data is received: webm.
  • 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/syntax/parsing/html5lib_write_single.html?file=processing-instructions (#46206)
    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?z0123456789 data&gt;

      Test timed out
      

    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?m-0&gt;

      Test timed out
      

  • TIMEOUT [expected OK] /html/user-activation/navigation-state-reset-sameorigin.html (#45151)
    • TIMEOUT [expected FAIL] subtest: Post-navigation state reset.

      Test timed out
      

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

  • TIMEOUT [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#44693)
    • NOTRUN [expected PASS] subtest: touch two-finger pan on 'touch-action: pan-x pan-y'
    • NOTRUN [expected FAIL] subtest: touch two-finger pan on 'touch-action: pinch-zoom'
  • OK /pointerevents/compat/pointerevent_touch_target_after_pointerdown_target_removed.tentative.html (#42813)
    • FAIL [expected PASS] subtest: After a pointerdown listener removes its target, touch events should be fired on the touchstart target even though an orphan and pointer events should be fired on the parent

      assert_equals: expected "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; } ]" but got "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerdown, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; } ]"
      

    • FAIL [expected PASS] subtest: After a pointerdown listener removes its target, touchmove event should be fired on the pointerdown target

      assert_equals: expected "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchmove, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; } ]" but got "[ { type: pointerdown, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: touchstart, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchmove, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: touchend, target: Element node &lt;div id=\"pointerDownTarget\"&gt;&lt;/div&gt; }, { type: pointermove, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerdown, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; }, { type: pointerup, target: Element node &lt;div id=\"pointerDownTargetParent\"&gt;&lt;/div&gt; } ]"
      

  • OK /preload/prefetch-document.html (#37210)
    • FAIL [expected PASS] subtest: different-site document prefetch with 'as=document' should not be consumed

      assert_equals: expected 2 but got 1
      

  • OK /resource-timing/test_resource_timing.https.html (#25216)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (link)
  • OK /touch-events/single-tap-when-touchend-listener-use-sync-xhr.html (#41175)
    • PASS [expected FAIL] subtest: Click event should be fired when touchend opens synchronous XHR
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • PASS [expected TIMEOUT] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ default policy in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a frame via anchor with javascript:-urls in enforcing mode.
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?11-15 (#44834)
    • PASS [expected TIMEOUT] subtest: Navigate a window via area with javascript:-urls in report-only mode.
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?16-20 (#44835)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via area with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: Navigate a frame via area with javascript:-urls in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via area with javascript:-urls w/ default policy in report-only mode.
  • TIMEOUT /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • PASS [expected TIMEOUT] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • PASS [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • TIMEOUT [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.

      Test timed out
      

  • CRASH [expected OK] /uievents/mouse/mousemove_prevent_default_action.html
  • OK /visual-viewport/resize-event-order.html (#41981)
    • PASS [expected FAIL] subtest: Popup: DOMWindow resize fired before VisualViewport.
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: buffer-stitching-2

      assert_approx_equals: Stitched sine‑wave buffers at sample rate 43800 sample[43466] |-639.951171875 - -0.8892210125923157| = 639.0619508624077 &gt; 0.0038986 expected -0.8892210125923157 +/- 0.0038986 but got -639.951171875
      

Stable unexpected results that are known to be intermittent (21)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.

      Test timed out
      

  • 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 /beacon/beacon-basic.https.window.html (#41723)
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = arraybuffer
    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = blob
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-site - Not sent to non-trustworthy same-origin destination

      Test timed out
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored

      promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
      

  • 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/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • PASS [expected FAIL] subtest: Same-Document Referrer from Refresh
  • OK /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html (#28697)
    • FAIL [expected PASS] subtest: aElement.click() before the load event must NOT replace

      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)&amp;code=%0A%20%20%20%20const%20a%20%3D%20document.createElement(%22a%22)%3B%0A%20%20%20%20a.href%20%3D%20%22%2Fcommon%2Fblank.html%3Fthereplacement%22%3B%0A%20%20%20%20document.currentScript.before(a)%3B%0A%20%20%20%20a.click()%3B%0A%20%20"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected PASS] subtest: Non-HTMLElement should not support autofocus

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: Host element with delegatesFocus should support autofocus
    • NOTRUN [expected FAIL] subtest: Host element with delegatesFocus including no focusable descendants should be skipped
    • NOTRUN [expected FAIL] subtest: Area element should support autofocus
  • 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
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/media-elements/autoplay-disabled-by-permissions-policy.https.sub.html (#46012, #46804)
    • TIMEOUT [expected PASS] subtest: Permissions-Policy header: autoplay "none" has no effect on the top level document.

      Test timed out
      

    • TIMEOUT [expected FAIL] subtest: Permissions-Policy header: autoplay "none" disallows same-origin iframes.

      Test timed out
      

  • OK /html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html (#46014)
    • FAIL [expected PASS] subtest: seek to currentTime

      assert_greater_than: seekable ranges expected a number greater than 0 but got 0
      

  • 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
      

  • TIMEOUT /html/syntax/parsing/html5lib_write.html?file=processing-instructions (#46205)
    • TIMEOUT [expected FAIL] subtest: &lt;body&gt;&lt;?level-99&gt;

      Test timed out
      

  • OK [expected TIMEOUT] /infrastructure/testdriver/click_nested.html (#43887)
    • FAIL [expected NOTRUN] subtest: TestDriver click method with multiple windows and nested iframe

      can't access property "document", child.frames[2] is undefined
      

  • OK /pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html (#46781)
    • FAIL [expected PASS] subtest: scroll was received while shouldn't

      assert_true: scroll received while shouldn't expected true got false
      

  • OK /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (script)
  • TIMEOUT /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • FAIL [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in report-only mode.

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

  • 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 in report-only mode.
    • PASS [expected NOTRUN] 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
Copy Markdown

✨ Try run (#30469831136) succeeded.

mrobinson and others added 4 commits July 29, 2026 19:01
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
Signed-off-by: Simon Sapin <simon@igalia.com>
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jul 29, 2026
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 30, 2026
@mrobinson
mrobinson enabled auto-merge July 30, 2026 09:22
@mrobinson
mrobinson added this pull request to the merge queue Jul 30, 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 Jul 30, 2026
Merged via the queue into servo:main with commit 9ae0b2e Jul 30, 2026
36 checks passed
@mrobinson
mrobinson deleted the offsetmap branch July 30, 2026 10:30
@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 Jul 30, 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.

4 participants