Skip to content

Resolve video URLs against the page and embed YouTube/Vimeo only with a video ID - #5889

Draft
Eugene (OEvgeny) wants to merge 5 commits into
mainfrom
OEvgeny/fix-video-relative-url
Draft

Eugene (OEvgeny) wants to merge 5 commits into
mainfrom
OEvgeny/fix-video-relative-url

Conversation

@OEvgeny

@OEvgeny Eugene (OEvgeny) commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Video attachments, video cards, and animation cards with a video showed an empty bubble or a broken embed when the video URL was relative, protocol-relative, could not be parsed, or was a YouTube or Vimeo URL without a video ID.

Changelog Entry

Fixed

  • Fixed video attachments, video cards, and animation cards with a video showing nothing or a broken embed when the video URL is relative (such as /videos/intro.mp4), protocol-relative, cannot be parsed, or is a YouTube or Vimeo URL without a video ID: the video URL is now resolved against the page, the same as images and audio, and YouTube and Vimeo videos are only embedded when the URL has a video ID, in PR #5889, by @OEvgeny

Description

VideoContent parses the video URL to detect YouTube and Vimeo links. It called new URL(src) without a base URL, which throws for relative URLs, protocol-relative URLs (such as //www.youtube.com/watch?v=...), and URLs that cannot be parsed. The error boundary of the attachment caught the error, so the video, or the whole video card or animation card including its title and buttons, was replaced by an empty bubble. Images and audio with the same URLs render, because they pass the URL to the browser as-is.

Also, YouTube and Vimeo URLs without a video ID were embedded anyway. https://www.youtube.com/@Microsoft has no v parameter, so the embed ID was null: development builds failed prop validation and showed an empty bubble, and production builds embed /embed/null. https://youtu.be/ and https://vimeo.com/ were embedded with an empty ID, and the Vimeo player shows an error page.

The URL is now resolved against document.baseURI, the same as the browser does for <video src>. If it still cannot be parsed, it is passed to <video> as-is. YouTube and Vimeo videos are only embedded when the URL has a video ID.

Design

  • Resolving against the page makes the YouTube and Vimeo detection match what the browser loads. Protocol-relative YouTube and Vimeo links are now embedded, and other relative URLs play in <video>, the same as images and audio.
  • A URL that cannot be parsed (such as https://) now renders a <video> that fails to load, instead of an empty bubble.
  • A YouTube or Vimeo URL without a video ID is played in <video>, the same as any other URL that cannot be embedded (such as https://example.com/page). The player cannot play it, the same as today for other URLs that are not videos, instead of an empty bubble or an embed without a video.
  • The <a href> parsing workaround for browsers without URL is removed: Internet Explorer is not supported since 4.16.0, and other components, such as file attachments and card actions, already use URL without a workaround.
  • Other places that parse URLs are unchanged, because they already handle relative URLs on purpose:
    • File attachments do not offer a download for relative URLs. This allowlist is intentional (Add allowed protocol list for file attachment #4143) and covered by attachment/forbiddenProtocol.html.
    • Card actions (openUrl, playVideo, and others) only allow absolute URLs with an allowed scheme.
    • Markdown links, link definitions, and localized strings already handle URLs that cannot be parsed.
  • Follow-up: YouTube video URLs that keep the video ID in the path, such as /shorts/<id>, /embed/<id>, or /live/<id>, are not embedded, the same as before. They are now played in <video>, instead of failing prop validation in development builds, or embedding /embed/null in production builds.
  • The test uses __tests__/assets/dark-green-video.webm, a uniform dark green video (2 seconds, no audio track). A player that cannot play is gray, close to the dark gray video that the focus indicator tests use, so with dark green the snapshots tell a loaded video from a failed one.
  • The snapshots cover the video attachment, the video card, and the animation card with a relative URL, and the video attachment with a URL that cannot be parsed. The players of YouTube and Vimeo URLs without a video ID load from the network, so the test asserts their state (a <video> with the URL, no <iframe>) instead of taking snapshots.

Specific Changes

  • Attachment/VideoContent.tsx: parse the URL with new URL(src, document.baseURI) inside onErrorResumeNext(), embed YouTube and Vimeo only when the URL has a video ID, and otherwise render <video>; removed parseURL() and its <a href> workaround
  • Added __tests__/html2/attachment/video.url.html: a video attachment, a video card, and an animation card with a relative URL each show a <video> that loads, and the cards keep their title; video attachments with a URL that cannot be parsed, or with a YouTube or Vimeo URL without a video ID, show a <video> and nothing is embedded
    • Snapshots of the video attachment, the video card, the video attachment with a URL that cannot be parsed, and the animation card
  • Added __tests__/assets/dark-green-video.webm (1.2 KB, 2 seconds, solid dark green), byte-identical to the file in Let keyboard users reach the native controls of audio and video players in an activity #5891, so the two PRs do not conflict on it
  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

@OEvgeny

Eugene (OEvgeny) commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

TODO: Can we have snaps for this, so we don't break it? Also, dark-green video could work better in this case.

@OEvgeny

Copy link
Copy Markdown
Collaborator Author

Done: the test now snapshots the video attachment, video card, and animation card with a relative URL, plus the player for an unparsable URL, using a dark green video (a player that cannot play is gray, so a loaded video stands out); the YouTube/Vimeo fallbacks load from the network, so they are asserted instead.

Eugene Olonov added 5 commits October 17, 2026 18:41
`VideoContent` parsed the URL with `new URL(src)` to detect YouTube and
Vimeo links. Relative URLs (such as `/videos/intro.mp4`), protocol-relative
URLs, and URLs that cannot be parsed made it throw while rendering, so the
video attachment, or the whole video card or animation card, was replaced
by an empty bubble.

Resolve the URL against `document.baseURI`, the same as `<video src>` does,
and fall back to `<video>` if it still cannot be parsed. Remove the
`<a href>` parsing workaround for browsers without `URL`, which are no
longer supported since Internet Explorer support ended in 4.16.0.

Add the 2-second dark gray WebM test asset (same file as in #5887).
`VideoContent` embedded every YouTube and Vimeo URL, even without a video
ID: `https://www.youtube.com/@Microsoft` has no `v` parameter, so the
embed ID was `null` (prop validation failed in development builds and the
bubble was empty), and `https://youtu.be/` or `https://vimeo.com/` were
embedded with an empty ID.

Without a video ID, play the URL in `<video>`, the same as any other URL
that cannot be embedded.

Rename the test to `video.url.html`, as it now covers more than relative
URLs.
… green video in tests

- Snapshot the video attachment, the video card, and the animation card with a relative URL, and the video attachment with a URL that cannot be parsed.
- Use a uniform dark green test video, so a loaded video is distinguishable from a player that cannot play (gray).
- YouTube and Vimeo URLs without a video ID load from the network, so their state is asserted instead of their look.
#5887 lays out the video of animation cards as a block, which removes the
line gap below the video. The card is 4 pixels shorter.
@OEvgeny
Eugene (OEvgeny) force-pushed the OEvgeny/fix-video-relative-url branch from db0c765 to 873c215 Compare October 18, 2026 01:52

This branch has not been deployed

No deployments
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.

1 participant