mirror of
https://github.com/lexmount/moli.git
synced 2026-10-30 16:07:24 +00:00
fix(media): honor capability dictionary inheritance and defaults
This commit is contained in:
@@ -29,39 +29,35 @@ struct MediaCapabilitiesPrototypeDeclaration {
|
||||
encoding_info: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain)]
|
||||
struct MediaCapabilitiesDecodingInfoDeclaration<'scope> {
|
||||
#[webapi(data_property, enumerable)]
|
||||
supported: bool,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
smooth: bool,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaCapabilitiesInfo", snapshot)]
|
||||
struct MediaCapabilitiesInfo {
|
||||
#[webidl(required)]
|
||||
power_efficient: bool,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
key_system_access: v8::Local<'scope, v8::Value>,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
configuration: v8::Local<'scope, v8::Object>,
|
||||
#[webidl(required)]
|
||||
smooth: bool,
|
||||
#[webidl(required)]
|
||||
supported: bool,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain)]
|
||||
struct MediaCapabilitiesEncodingInfoDeclaration<'scope> {
|
||||
#[webapi(data_property, enumerable)]
|
||||
supported: bool,
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaCapabilitiesDecodingInfo", snapshot)]
|
||||
struct MediaCapabilitiesDecodingInfo<'s> {
|
||||
#[webidl(inherit)]
|
||||
info: MediaCapabilitiesInfo,
|
||||
#[webidl(required, dictionary)]
|
||||
configuration: MediaDecodingConfiguration,
|
||||
#[webidl(required, nullable, interface = web_api_interfaces::MediaKeySystemAccess)]
|
||||
key_system_access: Option<v8::Local<'s, v8::Object>>,
|
||||
}
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
smooth: bool,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
power_efficient: bool,
|
||||
|
||||
#[webapi(data_property, enumerable)]
|
||||
configuration: v8::Local<'scope, v8::Object>,
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaCapabilitiesEncodingInfo", snapshot)]
|
||||
struct MediaCapabilitiesEncodingInfo {
|
||||
#[webidl(inherit)]
|
||||
info: MediaCapabilitiesInfo,
|
||||
#[webidl(required, dictionary)]
|
||||
configuration: MediaEncodingConfiguration,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, webidl::WebIdlEnum)]
|
||||
@@ -117,97 +113,93 @@ enum MediaKeysRequirement {
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "AudioConfiguration", snapshot)]
|
||||
struct AudioConfiguration {
|
||||
// Snapshot order preserves result keys independently of input conversion.
|
||||
#[webidl(required, snapshot_order = 0)]
|
||||
#[webidl(required)]
|
||||
content_type: String,
|
||||
#[webidl(snapshot_order = 1)]
|
||||
channels: Option<String>,
|
||||
#[webidl(snapshot_order = 2)]
|
||||
bitrate: Option<u64>,
|
||||
#[webidl(snapshot_order = 3)]
|
||||
samplerate: Option<u32>,
|
||||
#[webidl(snapshot_order = 4)]
|
||||
spatial_rendering: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "VideoConfiguration", snapshot)]
|
||||
struct VideoConfiguration {
|
||||
#[webidl(required, snapshot_order = 0)]
|
||||
#[webidl(required)]
|
||||
content_type: String,
|
||||
#[webidl(required, snapshot_order = 1)]
|
||||
#[webidl(required)]
|
||||
width: u32,
|
||||
#[webidl(required, snapshot_order = 2)]
|
||||
#[webidl(required)]
|
||||
height: u32,
|
||||
#[webidl(required, snapshot_order = 3)]
|
||||
#[webidl(required)]
|
||||
bitrate: u64,
|
||||
#[webidl(required, converter = "double", snapshot_order = 4)]
|
||||
#[webidl(required, converter = "double")]
|
||||
framerate: f64,
|
||||
#[webidl(snapshot_order = 5)]
|
||||
has_alpha_channel: Option<bool>,
|
||||
#[webidl(converter = "enum", snapshot_order = 6)]
|
||||
#[webidl(converter = "enum")]
|
||||
hdr_metadata_type: Option<HdrMetadataType>,
|
||||
#[webidl(converter = "enum", snapshot_order = 7)]
|
||||
#[webidl(converter = "enum")]
|
||||
color_gamut: Option<ColorGamut>,
|
||||
#[webidl(converter = "enum", snapshot_order = 8)]
|
||||
#[webidl(converter = "enum")]
|
||||
transfer_function: Option<TransferFunction>,
|
||||
#[webidl(snapshot_order = 9)]
|
||||
scalability_mode: Option<String>,
|
||||
#[webidl(snapshot_order = 10)]
|
||||
spatial_scalability: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "KeySystemTrackConfiguration", snapshot)]
|
||||
struct KeySystemTrackConfiguration {
|
||||
#[webidl(default = "", snapshot_order = 0)]
|
||||
robustness: String,
|
||||
#[webidl(with = optional_nullable_dom_string_member, snapshot_order = 1)]
|
||||
#[webidl(snapshot_kind = webidl::SnapshotKind::Nullable(&webidl::SnapshotKind::Scalar))]
|
||||
encryption_scheme: Option<Option<String>>,
|
||||
#[webidl(converter = "raw", default = webidl::DomString16(Vec::new()))]
|
||||
robustness: webidl::DomString16,
|
||||
#[webidl(nullable, converter = "raw")]
|
||||
encryption_scheme: Option<webidl::DomString16>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaCapabilitiesKeySystemConfiguration", snapshot)]
|
||||
struct MediaCapabilitiesKeySystemConfiguration {
|
||||
#[webidl(required, snapshot_order = 0)]
|
||||
#[webidl(required)]
|
||||
key_system: String,
|
||||
#[webidl(default = "", snapshot_order = 1)]
|
||||
#[webidl(default = "")]
|
||||
init_data_type: String,
|
||||
#[webidl(converter = "enum", default = MediaKeysRequirement::Optional, snapshot_order = 2)]
|
||||
#[webidl(converter = "enum", default = MediaKeysRequirement::Optional)]
|
||||
distinctive_identifier: MediaKeysRequirement,
|
||||
#[webidl(converter = "enum", default = MediaKeysRequirement::Optional, snapshot_order = 3)]
|
||||
#[webidl(converter = "enum", default = MediaKeysRequirement::Optional)]
|
||||
persistent_state: MediaKeysRequirement,
|
||||
#[webidl(sequence, snapshot_order = 4)]
|
||||
#[webidl(sequence)]
|
||||
session_types: Option<Vec<String>>,
|
||||
#[webidl(dictionary, snapshot_order = 5)]
|
||||
#[webidl(dictionary)]
|
||||
audio: Option<KeySystemTrackConfiguration>,
|
||||
#[webidl(dictionary, snapshot_order = 6)]
|
||||
#[webidl(dictionary)]
|
||||
video: Option<KeySystemTrackConfiguration>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaConfiguration", snapshot)]
|
||||
struct MediaConfiguration {
|
||||
#[webidl(dictionary)]
|
||||
audio: Option<AudioConfiguration>,
|
||||
#[webidl(dictionary)]
|
||||
video: Option<VideoConfiguration>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaDecodingConfiguration", snapshot)]
|
||||
struct MediaDecodingConfiguration {
|
||||
#[webidl(dictionary, snapshot_order = 0)]
|
||||
audio: Option<AudioConfiguration>,
|
||||
#[webidl(name = "keySystemConfiguration", dictionary, snapshot_order = 3)]
|
||||
#[webidl(inherit)]
|
||||
media: MediaConfiguration,
|
||||
#[webidl(name = "keySystemConfiguration", dictionary)]
|
||||
key_system_configuration: Option<MediaCapabilitiesKeySystemConfiguration>,
|
||||
#[webidl(name = "type", required, converter = "enum", snapshot_order = 2)]
|
||||
#[webidl(name = "type", required, converter = "enum")]
|
||||
decoding_type: MediaDecodingType,
|
||||
#[webidl(dictionary, snapshot_order = 1)]
|
||||
video: Option<VideoConfiguration>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MediaEncodingConfiguration", snapshot)]
|
||||
struct MediaEncodingConfiguration {
|
||||
#[webidl(dictionary, snapshot_order = 0)]
|
||||
audio: Option<AudioConfiguration>,
|
||||
#[webidl(name = "type", required, converter = "enum", snapshot_order = 2)]
|
||||
#[webidl(inherit)]
|
||||
media: MediaConfiguration,
|
||||
#[webidl(name = "type", required, converter = "enum")]
|
||||
encoding_type: MediaEncodingType,
|
||||
#[webidl(dictionary, snapshot_order = 1)]
|
||||
video: Option<VideoConfiguration>,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
@@ -267,59 +259,42 @@ fn media_capabilities_decoding_info_callback<'s>(
|
||||
return;
|
||||
}
|
||||
|
||||
if configuration.key_system_configuration.is_some() {
|
||||
let (name, message) = if media_capabilities_is_worker(scope, receiver) {
|
||||
(
|
||||
"InvalidStateError",
|
||||
let supported = if configuration.key_system_configuration.is_some() {
|
||||
if media_capabilities_is_worker(scope, receiver) {
|
||||
let reason = crate::context_bootstrap::new_dom_exception_value(
|
||||
scope,
|
||||
"Encrypted media capabilities are unavailable in workers",
|
||||
)
|
||||
} else if !media_capabilities_is_secure_context(scope, receiver) {
|
||||
(
|
||||
"SecurityError",
|
||||
"Encrypted media capabilities require a secure context",
|
||||
)
|
||||
} else {
|
||||
let Some(configuration_object) =
|
||||
configuration.to_js_snapshot(scope, SnapshotPrototype::Ordinary)
|
||||
else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
let Ok(info) = MediaCapabilitiesDecodingInfoDeclaration::new(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
v8::null(scope).into(),
|
||||
configuration_object,
|
||||
)
|
||||
.bind(scope) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
set_resolved_promise(scope, &mut rv, info.into());
|
||||
"InvalidStateError",
|
||||
);
|
||||
set_rejected_promise(scope, &mut rv, reason);
|
||||
return;
|
||||
};
|
||||
let reason = crate::context_bootstrap::new_dom_exception_value(scope, message, name);
|
||||
set_rejected_promise(scope, &mut rv, reason);
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(configuration_object) =
|
||||
configuration.to_js_snapshot(scope, SnapshotPrototype::Ordinary)
|
||||
else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
}
|
||||
if !media_capabilities_is_secure_context(scope, receiver) {
|
||||
let reason = crate::context_bootstrap::new_dom_exception_value(
|
||||
scope,
|
||||
"Encrypted media capabilities require a secure context",
|
||||
"SecurityError",
|
||||
);
|
||||
set_rejected_promise(scope, &mut rv, reason);
|
||||
return;
|
||||
}
|
||||
false
|
||||
} else {
|
||||
configuration_supported(
|
||||
configuration.media.audio.as_ref(),
|
||||
configuration.media.video.as_ref(),
|
||||
)
|
||||
};
|
||||
let supported =
|
||||
configuration_supported(configuration.audio.as_ref(), configuration.video.as_ref());
|
||||
let Ok(info) = MediaCapabilitiesDecodingInfoDeclaration::new(
|
||||
supported,
|
||||
false,
|
||||
false,
|
||||
v8::null(scope).into(),
|
||||
configuration_object,
|
||||
)
|
||||
.bind(scope) else {
|
||||
let info = MediaCapabilitiesDecodingInfo {
|
||||
info: MediaCapabilitiesInfo {
|
||||
power_efficient: false,
|
||||
smooth: false,
|
||||
supported,
|
||||
},
|
||||
configuration,
|
||||
key_system_access: None,
|
||||
};
|
||||
let Some(info) = info.to_js_snapshot(scope, SnapshotPrototype::Ordinary) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
@@ -344,21 +319,19 @@ fn media_capabilities_encoding_info_callback<'s>(
|
||||
set_rejected_promise(scope, &mut rv, reason);
|
||||
return;
|
||||
}
|
||||
let Some(configuration_object) =
|
||||
configuration.to_js_snapshot(scope, SnapshotPrototype::Ordinary)
|
||||
else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
let supported = configuration_supported(
|
||||
configuration.media.audio.as_ref(),
|
||||
configuration.media.video.as_ref(),
|
||||
);
|
||||
let info = MediaCapabilitiesEncodingInfo {
|
||||
info: MediaCapabilitiesInfo {
|
||||
power_efficient: false,
|
||||
smooth: false,
|
||||
supported,
|
||||
},
|
||||
configuration,
|
||||
};
|
||||
let supported =
|
||||
configuration_supported(configuration.audio.as_ref(), configuration.video.as_ref());
|
||||
let Ok(info) = MediaCapabilitiesEncodingInfoDeclaration::new(
|
||||
supported,
|
||||
false,
|
||||
false,
|
||||
configuration_object,
|
||||
)
|
||||
.bind(scope) else {
|
||||
let Some(info) = info.to_js_snapshot(scope, SnapshotPrototype::Ordinary) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
@@ -435,30 +408,12 @@ fn media_capabilities_is_worker<'s>(
|
||||
.is_some_and(|value| value.boolean_value(scope))
|
||||
}
|
||||
|
||||
fn optional_nullable_dom_string_member<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
name: &'static str,
|
||||
) -> std::result::Result<Option<Option<String>>, webidl::WebIdlError> {
|
||||
let context = webidl::Context::member("KeySystemTrackConfiguration", name);
|
||||
let Some(value) = webidl::property_result(scope, object, name, context)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if value.is_undefined() {
|
||||
return Ok(None);
|
||||
}
|
||||
if value.is_null() {
|
||||
return Ok(Some(None));
|
||||
}
|
||||
webidl::convert::<webidl::DomString>(scope, value, context).map(|value| Some(Some(value.0)))
|
||||
}
|
||||
|
||||
fn validate_decoding_configuration(
|
||||
configuration: &MediaDecodingConfiguration,
|
||||
) -> std::result::Result<(), &'static str> {
|
||||
validate_media_configuration(
|
||||
configuration.audio.as_ref(),
|
||||
configuration.video.as_ref(),
|
||||
configuration.media.audio.as_ref(),
|
||||
configuration.media.video.as_ref(),
|
||||
configuration.decoding_type == MediaDecodingType::Webrtc,
|
||||
true,
|
||||
)?;
|
||||
@@ -466,10 +421,10 @@ fn validate_decoding_configuration(
|
||||
if configuration.decoding_type == MediaDecodingType::Webrtc {
|
||||
return Err("keySystemConfiguration is not valid for WebRTC decoding");
|
||||
}
|
||||
if key_system.audio.is_some() && configuration.audio.is_none() {
|
||||
if key_system.audio.is_some() && configuration.media.audio.is_none() {
|
||||
return Err("keySystemConfiguration.audio requires an audio configuration");
|
||||
}
|
||||
if key_system.video.is_some() && configuration.video.is_none() {
|
||||
if key_system.video.is_some() && configuration.media.video.is_none() {
|
||||
return Err("keySystemConfiguration.video requires a video configuration");
|
||||
}
|
||||
if key_system.session_types.as_ref().is_some_and(|types| {
|
||||
@@ -487,8 +442,8 @@ fn validate_encoding_configuration(
|
||||
configuration: &MediaEncodingConfiguration,
|
||||
) -> std::result::Result<(), &'static str> {
|
||||
validate_media_configuration(
|
||||
configuration.audio.as_ref(),
|
||||
configuration.video.as_ref(),
|
||||
configuration.media.audio.as_ref(),
|
||||
configuration.media.video.as_ref(),
|
||||
configuration.encoding_type == MediaEncodingType::Webrtc,
|
||||
false,
|
||||
)
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
globalThis.__mediaCapabilitiesConversionResult = 'pending';
|
||||
(async () => {
|
||||
const rows = [];
|
||||
for (const method of ['decodingInfo', 'encodingInfo']) {
|
||||
for (const scenario of ['normal', 'video-before-derived', 'invalid-video-before-type', 'invalid-audio']) {
|
||||
let reads = '';
|
||||
const videoMarker = {}, keyMarker = {}, typeMarker = {};
|
||||
const input = new Proxy({
|
||||
audio: scenario === 'invalid-audio' ? null : {contentType: 'audio/webm; codecs="opus"'},
|
||||
video: scenario === 'invalid-video-before-type' ? null : {contentType: 'video/webm; codecs="vp09.00.10.08"', width: 320, height: 240, bitrate: 200000, framerate: 24},
|
||||
type: scenario === 'invalid-video-before-type' ? undefined : method === 'decodingInfo' ? 'file' : 'webrtc'
|
||||
}, {
|
||||
get(target, name, receiver) {
|
||||
reads += name + ',';
|
||||
if (scenario === 'video-before-derived') {
|
||||
if (name === 'video') throw videoMarker;
|
||||
if (name === 'keySystemConfiguration') throw keyMarker;
|
||||
if (name === 'type') throw typeMarker;
|
||||
}
|
||||
return Reflect.get(target, name, receiver);
|
||||
}
|
||||
});
|
||||
let outcome = 'resolved', isPromise = false, synchronous = false;
|
||||
try {
|
||||
let promise;
|
||||
try { promise = navigator.mediaCapabilities[method](input); }
|
||||
catch (error) { synchronous = true; throw error; }
|
||||
isPromise = promise instanceof Promise;
|
||||
await promise;
|
||||
} catch (error) {
|
||||
outcome = error === videoMarker ? 'video' : error === keyMarker ? 'key' : error === typeMarker ? 'type' : error instanceof TypeError ? 'TypeError' : 'other';
|
||||
}
|
||||
rows.push({method, scenario, reads, outcome, isPromise, synchronous});
|
||||
}
|
||||
}
|
||||
globalThis.__mediaCapabilitiesConversionRows = JSON.stringify(rows);
|
||||
const matches = rows.every(row => {
|
||||
const reads = row.scenario === 'invalid-audio' ? 'audio,' : row.scenario !== 'normal' ? 'audio,video,' : row.method === 'decodingInfo' ? 'audio,video,keySystemConfiguration,type,' : 'audio,video,type,';
|
||||
const outcome = row.scenario === 'normal' ? 'resolved' : row.scenario === 'video-before-derived' ? 'video' : 'TypeError';
|
||||
return row.reads === reads && row.outcome === outcome && row.isPromise && !row.synchronous;
|
||||
});
|
||||
return matches ? 'ok' : JSON.stringify(rows);
|
||||
})().then(value => globalThis.__mediaCapabilitiesConversionResult = value,
|
||||
error => globalThis.__mediaCapabilitiesConversionResult = String(error));
|
||||
+11
-6
@@ -24,13 +24,14 @@ globalThis.__mediaCapabilitiesSnapshotResult = 'pending';
|
||||
};
|
||||
const promise = capabilities[method](input);
|
||||
check(promise instanceof Promise, 'query returns a Promise');
|
||||
check(reads === 'audio,bitrate,channels,contentType,samplerate,spatialRendering,type,video,', 'lexical conversion order');
|
||||
check(reads === 'audio,bitrate,channels,contentType,samplerate,spatialRendering,video,type,', 'base dictionary converts before derived members');
|
||||
for (const key of Object.keys(input)) Object.defineProperty(input, key, {get() { throw new Error('input read after conversion'); }});
|
||||
for (const key of Object.getOwnPropertyNames(child)) Object.defineProperty(child, key, {get() { throw new Error('nested input read after conversion'); }});
|
||||
const result = await promise;
|
||||
check(Object.keys(result).join() === (method === 'decodingInfo' ? 'powerEfficient,smooth,supported,configuration,keySystemAccess' : 'powerEfficient,smooth,supported,configuration'), 'result dictionary inheritance order');
|
||||
check(result.configuration.audio.channels === '2', 'converted nested result');
|
||||
check(Object.keys(result.configuration).join() === 'audio,type', 'configuration output order');
|
||||
check(Object.keys(result.configuration.audio).join() === 'contentType,channels,bitrate,samplerate,spatialRendering', 'audio output order');
|
||||
check(Object.keys(result.configuration.audio).join() === 'bitrate,channels,contentType,samplerate,spatialRendering', 'audio output order');
|
||||
dataProperties(result, window);
|
||||
const stable = Object.freeze({type: method === 'decodingInfo' ? 'file' : 'record', audio: Object.freeze(audio())});
|
||||
const first = await capabilities[method](stable);
|
||||
@@ -41,7 +42,7 @@ globalThis.__mediaCapabilitiesSnapshotResult = 'pending';
|
||||
Object.freeze(second.configuration.audio);
|
||||
check((await capabilities[method](stable)).configuration.audio.channels === '2', 'frozen prior result');
|
||||
}
|
||||
for (const scheme of [undefined, null, 'cenc']) {
|
||||
for (const scheme of [undefined, null, 'cenc', 'cbc\uD800']) {
|
||||
const track = {};
|
||||
if (scheme !== undefined) track.encryptionScheme = scheme;
|
||||
const input = {type: 'file', audio: audio(), keySystemConfiguration: {keySystem: 'org.w3.clearkey', sessionTypes: ['temporary', 'temporary'], audio: track}};
|
||||
@@ -49,14 +50,18 @@ globalThis.__mediaCapabilitiesSnapshotResult = 'pending';
|
||||
input.keySystemConfiguration.sessionTypes[0] = 'changed';
|
||||
const result = await promise;
|
||||
const key = result.configuration.keySystemConfiguration;
|
||||
check(Object.keys(result.configuration).join() === 'audio,type,keySystemConfiguration', 'decoding output order');
|
||||
check(Object.keys(result.configuration).join() === 'audio,keySystemConfiguration,type', 'decoding output order');
|
||||
check(key.distinctiveIdentifier === 'optional' && key.persistentState === 'optional' && key.initDataType === '', 'dictionary defaults');
|
||||
check(key.audio.robustness === '', 'nested dictionary default');
|
||||
check(Object.hasOwn(key.audio, 'encryptionScheme') === (scheme !== undefined), 'absent versus nullable member');
|
||||
if (scheme !== undefined) check(key.audio.encryptionScheme === scheme, 'nullable member value');
|
||||
check(Object.hasOwn(key.audio, 'encryptionScheme'), 'nullable default creates a member');
|
||||
check(key.audio.encryptionScheme === (scheme === undefined ? null : scheme), 'nullable member default and value');
|
||||
check(Object.keys(key.audio).join() === 'encryptionScheme,robustness', 'nested dictionary member order');
|
||||
check(key.sessionTypes.join() === 'temporary,temporary' && key.sessionTypes !== input.keySystemConfiguration.sessionTypes, 'copied sequence preserves duplicates');
|
||||
dataProperties(result, window);
|
||||
}
|
||||
const codeUnits = '\uD800\u0000\uDCFF';
|
||||
const unicode = await capabilities.decodingInfo({type: 'file', audio: audio(), keySystemConfiguration: {keySystem: 'org.w3.clearkey', audio: {robustness: codeUnits, encryptionScheme: codeUnits}}});
|
||||
check(unicode.configuration.keySystemConfiguration.audio.robustness === codeUnits && unicode.configuration.keySystemConfiguration.audio.encryptionScheme === codeUnits, 'DOMString code units');
|
||||
const names = ['audio', 'video', 'type', 'configuration', 'contentType', 'width', 'sessionTypes', 'keySystemConfiguration', 'encryptionScheme'];
|
||||
const marker = {};
|
||||
for (const mode of ['count', 'throw', 'readonly', 'index']) {
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn media_capabilities_inherited_members_convert_before_derived_getters() {
|
||||
let mut vm =
|
||||
new_storage_page_task_executor_test_vm("https://media-capabilities-conversion.test/");
|
||||
vm.eval(include_str!("media_capabilities_conversion.js"))
|
||||
.unwrap();
|
||||
vm.eval("0").unwrap();
|
||||
assert_eq!(
|
||||
vm.eval("__mediaCapabilitiesConversionResult").unwrap(),
|
||||
"ok"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn media_capabilities_dictionary_snapshots_preserve_conversion_and_define_own_data() {
|
||||
let mut vm =
|
||||
|
||||
Reference in New Issue
Block a user