diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js
index febf1fd..41cb1fc 100644
--- a/crates/obscura-js/js/bootstrap.js
+++ b/crates/obscura-js/js/bootstrap.js
@@ -5116,25 +5116,42 @@ globalThis.HTMLImageElement = HTMLImageElement;
_markNative(HTMLImageElement);
_markNative(HTMLImageElement.prototype.decode);
-// Media elements need canPlayType for codec detection fingerprinting.
-// Values match Chrome 145 on Linux x86_64 without proprietary codecs.
+// Report only capabilities backed by a real decoder. Poster rendering is an
+// image operation and does not make any audio/video container playable.
class HTMLMediaElement extends Element {
- canPlayType(type) {
- if (!type || typeof type !== 'string') return '';
- const mime = type.split(';')[0].trim().toLowerCase();
- if (mime === 'video/mp4' || mime === 'video/webm' || mime === 'video/ogg') return 'probably';
- if (mime === 'video/x-matroska') return 'maybe';
- if (mime === 'audio/ogg' || mime === 'audio/webm' || mime === 'audio/wav' ||
- mime === 'audio/mpeg') return 'probably';
- if (mime === 'audio/mp4' || mime === 'audio/x-m4a' || mime === 'audio/aac') return 'maybe';
- return '';
- }
+ static NETWORK_EMPTY = 0;
+ static NETWORK_IDLE = 1;
+ static NETWORK_LOADING = 2;
+ static NETWORK_NO_SOURCE = 3;
+ static HAVE_NOTHING = 0;
+ static HAVE_METADATA = 1;
+ static HAVE_CURRENT_DATA = 2;
+ static HAVE_FUTURE_DATA = 3;
+ static HAVE_ENOUGH_DATA = 4;
+ canPlayType(_type) { return ''; }
load() {}
- play() { return Promise.resolve(); }
+ play() {
+ return Promise.reject(new DOMException(
+ "The element has no supported sources.",
+ "NotSupportedError",
+ ));
+ }
pause() {}
+ get NETWORK_EMPTY() { return HTMLMediaElement.NETWORK_EMPTY; }
+ get NETWORK_IDLE() { return HTMLMediaElement.NETWORK_IDLE; }
+ get NETWORK_LOADING() { return HTMLMediaElement.NETWORK_LOADING; }
+ get NETWORK_NO_SOURCE() { return HTMLMediaElement.NETWORK_NO_SOURCE; }
+ get HAVE_NOTHING() { return HTMLMediaElement.HAVE_NOTHING; }
+ get HAVE_METADATA() { return HTMLMediaElement.HAVE_METADATA; }
+ get HAVE_CURRENT_DATA() { return HTMLMediaElement.HAVE_CURRENT_DATA; }
+ get HAVE_FUTURE_DATA() { return HTMLMediaElement.HAVE_FUTURE_DATA; }
+ get HAVE_ENOUGH_DATA() { return HTMLMediaElement.HAVE_ENOUGH_DATA; }
get paused() { return true; }
get ended() { return false; }
- get readyState() { return 0; }
+ get networkState() { return HTMLMediaElement.NETWORK_EMPTY; }
+ get readyState() { return HTMLMediaElement.HAVE_NOTHING; }
+ get error() { return null; }
+ get seeking() { return false; }
get currentTime() { return 0; }
set currentTime(v) {}
get duration() { return NaN; }
@@ -5142,8 +5159,14 @@ class HTMLMediaElement extends Element {
set volume(v) {}
get muted() { return false; }
set muted(v) {}
- get src() { return this.getAttribute('src') || ''; }
+ get src() {
+ const raw = this.getAttribute("src");
+ if (!raw) return "";
+ try { return new URL(raw, this.baseURI || globalThis.location?.href || "about:blank").href; }
+ catch (_error) { return raw; }
+ }
set src(v) { this.setAttribute('src', v); }
+ get currentSrc() { return ""; }
get textTracks() {
return TextTrackList.from(
Array.from(this.querySelectorAll("track")).map((element) => element.track)
@@ -5157,7 +5180,17 @@ _markNative(HTMLMediaElement.prototype.canPlayType);
_markNative(HTMLMediaElement.prototype.play);
_markNative(HTMLMediaElement.prototype.load);
_markNative(HTMLMediaElement.prototype.pause);
-class HTMLVideoElement extends HTMLMediaElement {}
+class HTMLVideoElement extends HTMLMediaElement {
+ get poster() {
+ const raw = this.getAttribute("poster");
+ if (!raw) return "";
+ try { return new URL(raw, this.baseURI || globalThis.location?.href || "about:blank").href; }
+ catch (_error) { return raw; }
+ }
+ set poster(value) { this.setAttribute("poster", value); }
+ get videoWidth() { return 0; }
+ get videoHeight() { return 0; }
+}
class HTMLAudioElement extends HTMLMediaElement {}
class HTMLTrackElement extends Element {
static NONE = 0;
diff --git a/crates/obscura-js/src/runtime.rs b/crates/obscura-js/src/runtime.rs
index f0bb847..47fbb50 100644
--- a/crates/obscura-js/src/runtime.rs
+++ b/crates/obscura-js/src/runtime.rs
@@ -667,9 +667,9 @@ impl ObscuraJsRuntime {
}
/// Return the exact responsive candidates selected for live `
`
- /// elements without loading them. The browser layer can then fetch them
- /// concurrently through the page-owned transport before synchronous layout
- /// or paint observes the cache.
+ /// elements and `