mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 00:01:34 +00:00
fix(dom): expose reflected token list attributes
This commit is contained in:
@@ -88,11 +88,7 @@ impl NativeDomBridge {
|
||||
runtime_ptr: *mut JsContextHost,
|
||||
handle: DomHandle,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
self.wrap_bridge_handle(
|
||||
scope,
|
||||
runtime_ptr,
|
||||
BridgeHandle::ClassList(handle, DomTokenListKind::Class),
|
||||
)
|
||||
self.wrap_dom_token_list(scope, runtime_ptr, handle, DomTokenListKind::Class)
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_part_list<'s, 'i>(
|
||||
@@ -101,24 +97,17 @@ impl NativeDomBridge {
|
||||
runtime_ptr: *mut JsContextHost,
|
||||
handle: DomHandle,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
self.wrap_bridge_handle(
|
||||
scope,
|
||||
runtime_ptr,
|
||||
BridgeHandle::ClassList(handle, DomTokenListKind::Part),
|
||||
)
|
||||
self.wrap_dom_token_list(scope, runtime_ptr, handle, DomTokenListKind::Part)
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_rel_list<'s, 'i>(
|
||||
pub(in crate::native_bridge) fn wrap_dom_token_list<'s, 'i>(
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, 'i>,
|
||||
runtime_ptr: *mut JsContextHost,
|
||||
handle: DomHandle,
|
||||
kind: DomTokenListKind,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
self.wrap_bridge_handle(
|
||||
scope,
|
||||
runtime_ptr,
|
||||
BridgeHandle::ClassList(handle, DomTokenListKind::Rel),
|
||||
)
|
||||
self.wrap_bridge_handle(scope, runtime_ptr, BridgeHandle::ClassList(handle, kind))
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_dataset<'s, 'i>(
|
||||
|
||||
@@ -171,7 +171,9 @@ pub(crate) use canvas::{
|
||||
pub(crate) use class_list::install_dom_token_list_prototype_bindings;
|
||||
pub(super) use class_list::{
|
||||
build_dom_token_list_wrapper_template, html_rel_list_getter_function,
|
||||
html_rel_list_setter_function,
|
||||
html_rel_list_setter_function, iframe_sandbox_getter_function, iframe_sandbox_setter_function,
|
||||
link_sizes_getter_function, link_sizes_setter_function, output_html_for_getter_function,
|
||||
output_html_for_setter_function, svg_rel_list_setter_function,
|
||||
};
|
||||
pub(super) use content::{
|
||||
node_direct_text_content, node_get_html_callback, node_inner_html_getter_function,
|
||||
@@ -461,10 +463,9 @@ pub(super) use global_attributes::{
|
||||
node_hidden_setter_function, node_inert_getter_function, node_inert_setter_function,
|
||||
node_input_mode_getter_function, node_input_mode_setter_function,
|
||||
node_is_content_editable_getter_function, node_lang_getter_function, node_lang_setter_function,
|
||||
node_sandbox_getter_function, node_sandbox_setter_function, node_spellcheck_getter_function,
|
||||
node_spellcheck_setter_function, node_tab_index_getter_function,
|
||||
node_tab_index_setter_function, node_title_getter_function, node_title_setter_function,
|
||||
node_translate_getter_function, node_translate_setter_function,
|
||||
node_spellcheck_getter_function, node_spellcheck_setter_function,
|
||||
node_tab_index_getter_function, node_tab_index_setter_function, node_title_getter_function,
|
||||
node_title_setter_function, node_translate_getter_function, node_translate_setter_function,
|
||||
node_writing_suggestions_getter_function, node_writing_suggestions_setter_function,
|
||||
null_to_empty_dom_string_reflection_getter_function,
|
||||
null_to_empty_dom_string_reflection_setter_function, object_archive_getter_function,
|
||||
@@ -4112,8 +4113,8 @@ struct HtmlIFrameElementPrototypeDeclaration {
|
||||
referrer_policy: (),
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = node_sandbox_getter_function,
|
||||
setter = node_sandbox_setter_function
|
||||
getter = iframe_sandbox_getter_function,
|
||||
setter = iframe_sandbox_setter_function
|
||||
)]
|
||||
sandbox: (),
|
||||
#[webapi(
|
||||
@@ -4252,6 +4253,12 @@ struct HtmlLinkElementUrlPrototypeDeclaration {
|
||||
setter_data = DomStringReflection::LinkHreflang
|
||||
)]
|
||||
hreflang: (),
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = link_sizes_getter_function,
|
||||
setter = link_sizes_setter_function
|
||||
)]
|
||||
sizes: (),
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = html_charset_getter_function,
|
||||
@@ -5086,6 +5093,12 @@ struct HtmlInputElementValuePrototypeDeclaration {
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::HTMLOutputElement, enumerable)]
|
||||
struct HtmlOutputElementValuePrototypeDeclaration {
|
||||
#[webapi(
|
||||
accessor_property = "htmlFor",
|
||||
getter = output_html_for_getter_function,
|
||||
setter = output_html_for_setter_function
|
||||
)]
|
||||
html_for: (),
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = output_default_value_getter_function,
|
||||
@@ -5102,6 +5115,17 @@ struct HtmlOutputElementValuePrototypeDeclaration {
|
||||
type_: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::SVGAElement, enumerable)]
|
||||
struct SvgAElementRelListPrototypeDeclaration {
|
||||
#[webapi(
|
||||
accessor_property = "relList",
|
||||
getter = html_rel_list_getter_function,
|
||||
setter = svg_rel_list_setter_function
|
||||
)]
|
||||
rel_list: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::HTMLMeterElement, enumerable)]
|
||||
struct HtmlMeterElementPrototypeDeclaration {
|
||||
@@ -7374,6 +7398,7 @@ pub(crate) fn install_element_template_bindings<'s>(
|
||||
"HTMLScriptElement" => install!(HtmlScriptElementPrototypeDeclaration),
|
||||
"SVGScriptElement" => install!(SvgScriptElementPrototypeDeclaration),
|
||||
"SVGImageElement" => install!(SvgImageElementPrototypeDeclaration),
|
||||
"SVGAElement" => install!(SvgAElementRelListPrototypeDeclaration),
|
||||
"HTMLStyleElement" => install!(HtmlStyleElementPrototypeDeclaration),
|
||||
"SVGStyleElement" => install!(SvgStyleElementPrototypeDeclaration),
|
||||
"HTMLTableElement" => install!(HtmlTableElementPrototypeDeclaration),
|
||||
|
||||
@@ -15,7 +15,9 @@ mod template;
|
||||
mod tokens;
|
||||
|
||||
pub(in crate::native_bridge) use self::accessors::{
|
||||
html_rel_list_getter_function, html_rel_list_setter_function,
|
||||
html_rel_list_getter_function, html_rel_list_setter_function, iframe_sandbox_getter_function,
|
||||
iframe_sandbox_setter_function, link_sizes_getter_function, link_sizes_setter_function,
|
||||
output_html_for_getter_function, output_html_for_setter_function, svg_rel_list_setter_function,
|
||||
};
|
||||
pub(in crate::native_bridge) use self::template::build_dom_token_list_wrapper_template;
|
||||
pub(crate) use self::template::install_dom_token_list_prototype_bindings;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use super::*;
|
||||
|
||||
pub(in crate::native_bridge) fn html_rel_list_getter_function<'s>(
|
||||
fn token_list_getter<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s, v8::Value>,
|
||||
kind: DomTokenListKind,
|
||||
) {
|
||||
let Ok((runtime_ptr, handle)) =
|
||||
node_runtime_and_handle_from_object_or_detached(scope, args.this())
|
||||
@@ -16,30 +17,41 @@ pub(in crate::native_bridge) fn html_rel_list_getter_function<'s>(
|
||||
return;
|
||||
}
|
||||
let runtime = unsafe { &mut *runtime_ptr };
|
||||
match runtime
|
||||
.native_bridge_mut()
|
||||
.wrap_rel_list(scope, runtime_ptr, handle)
|
||||
{
|
||||
Some(rel_list) => rv.set(rel_list.into()),
|
||||
let token_list =
|
||||
runtime
|
||||
.native_bridge_mut()
|
||||
.wrap_dom_token_list(scope, runtime_ptr, handle, kind);
|
||||
match token_list {
|
||||
Some(token_list) => rv.set(token_list.into()),
|
||||
None => rv.set_null(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_html_rel_list_for_receiver<'s>(
|
||||
fn set_token_list_for_receiver<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
receiver: v8::Local<'s, v8::Object>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
owner: &'static str,
|
||||
property: &'static str,
|
||||
attribute: &'static str,
|
||||
) {
|
||||
let Ok((runtime_ptr, handle)) =
|
||||
node_runtime_and_handle_from_object_or_detached(scope, receiver)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(value) = property_dom_string_value(scope, value, owner, "relList") else {
|
||||
let Some(value) = property_dom_string_value(scope, value, owner, property) else {
|
||||
return;
|
||||
};
|
||||
set_reflected_attribute(scope, runtime_ptr, handle, "rel", &value);
|
||||
set_reflected_attribute(scope, runtime_ptr, handle, attribute, &value);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn html_rel_list_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
token_list_getter(scope, args, rv, DomTokenListKind::Rel);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn html_rel_list_setter_function<'s>(
|
||||
@@ -50,7 +62,102 @@ pub(in crate::native_bridge) fn html_rel_list_setter_function<'s>(
|
||||
if let Some(interface) =
|
||||
super::super::reflection::ElementReflectionInterface::from_callback_data(scope, args.data())
|
||||
{
|
||||
set_html_rel_list_for_receiver(scope, args.this(), args.get(0), interface.name());
|
||||
set_token_list_for_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
args.get(0),
|
||||
interface.name(),
|
||||
"relList",
|
||||
"rel",
|
||||
);
|
||||
}
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn svg_rel_list_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
set_token_list_for_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
args.get(0),
|
||||
"SVGAElement",
|
||||
"relList",
|
||||
"rel",
|
||||
);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn output_html_for_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
token_list_getter(scope, args, rv, DomTokenListKind::HtmlFor);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn output_html_for_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
set_token_list_for_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
args.get(0),
|
||||
"HTMLOutputElement",
|
||||
"htmlFor",
|
||||
"for",
|
||||
);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn iframe_sandbox_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
token_list_getter(scope, args, rv, DomTokenListKind::Sandbox);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn iframe_sandbox_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
set_token_list_for_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
args.get(0),
|
||||
"HTMLIFrameElement",
|
||||
"sandbox",
|
||||
"sandbox",
|
||||
);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn link_sizes_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
token_list_getter(scope, args, rv, DomTokenListKind::Sizes);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn link_sizes_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
set_token_list_for_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
args.get(0),
|
||||
"HTMLLinkElement",
|
||||
"sizes",
|
||||
"sizes",
|
||||
);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
@@ -72,12 +72,34 @@ const LINK_REL_LIST_SUPPORTED_TOKENS: &[&str] = &[
|
||||
|
||||
const NAVIGATION_REL_LIST_SUPPORTED_TOKENS: &[&str] = &["noreferrer", "noopener", "opener"];
|
||||
|
||||
const SANDBOX_SUPPORTED_TOKENS: &[&str] = &[
|
||||
"allow-downloads",
|
||||
"allow-forms",
|
||||
"allow-modals",
|
||||
"allow-orientation-lock",
|
||||
"allow-pointer-lock",
|
||||
"allow-popups",
|
||||
"allow-popups-to-escape-sandbox",
|
||||
"allow-presentation",
|
||||
"allow-same-origin",
|
||||
"allow-scripts",
|
||||
"allow-storage-access-by-user-activation",
|
||||
"allow-top-navigation",
|
||||
"allow-top-navigation-by-user-activation",
|
||||
"allow-top-navigation-to-custom-protocols",
|
||||
];
|
||||
|
||||
fn rel_list_supports_token(runtime: &JsContextHost, handle: DomHandle, token: &str) -> bool {
|
||||
let supported_tokens = if runtime.dom_host().is_html_element_named(handle, "link") {
|
||||
LINK_REL_LIST_SUPPORTED_TOKENS
|
||||
} else if ["a", "area", "form"]
|
||||
.into_iter()
|
||||
.any(|name| runtime.dom_host().is_html_element_named(handle, name))
|
||||
|| runtime
|
||||
.dom_host()
|
||||
.node(handle)
|
||||
.and_then(|node| node.as_element())
|
||||
.is_some_and(|element| element.is_svg_element("a"))
|
||||
{
|
||||
NAVIGATION_REL_LIST_SUPPORTED_TOKENS
|
||||
} else {
|
||||
@@ -88,6 +110,12 @@ fn rel_list_supports_token(runtime: &JsContextHost, handle: DomHandle, token: &s
|
||||
.any(|supported| token.eq_ignore_ascii_case(supported))
|
||||
}
|
||||
|
||||
fn sandbox_supports_token(token: &str) -> bool {
|
||||
SANDBOX_SUPPORTED_TOKENS
|
||||
.iter()
|
||||
.any(|supported| token.eq_ignore_ascii_case(supported))
|
||||
}
|
||||
|
||||
pub(super) fn class_list_item_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -314,11 +342,18 @@ pub(super) fn class_list_supports_callback<'s>(
|
||||
return;
|
||||
};
|
||||
match kind {
|
||||
DomTokenListKind::Class | DomTokenListKind::Part => {
|
||||
DomTokenListKind::Class
|
||||
| DomTokenListKind::Part
|
||||
| DomTokenListKind::HtmlFor
|
||||
| DomTokenListKind::Sizes => {
|
||||
throw_type_error(scope, "DOMTokenList has no supported tokens.");
|
||||
return;
|
||||
}
|
||||
DomTokenListKind::Rel => {}
|
||||
DomTokenListKind::Sandbox => {
|
||||
rv.set_bool(sandbox_supports_token(&parsed.token));
|
||||
return;
|
||||
}
|
||||
}
|
||||
rv.set_bool(rel_list_supports_token(
|
||||
unsafe { &*runtime_ptr },
|
||||
|
||||
@@ -40,5 +40,8 @@ pub(super) fn token_list_attribute_name(kind: DomTokenListKind) -> &'static str
|
||||
DomTokenListKind::Class => "class",
|
||||
DomTokenListKind::Part => "part",
|
||||
DomTokenListKind::Rel => "rel",
|
||||
DomTokenListKind::HtmlFor => "for",
|
||||
DomTokenListKind::Sandbox => "sandbox",
|
||||
DomTokenListKind::Sizes => "sizes",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,30 +1030,6 @@ pub(in crate::native_bridge) fn html_color_getter_function<'s>(
|
||||
attribute_property_getter_from_object_or_detached(scope, args.this(), "color", rv);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn node_sandbox_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
attribute_property_getter_from_object_or_detached(scope, args.this(), "sandbox", rv);
|
||||
}
|
||||
|
||||
pub(in crate::native_bridge) fn node_sandbox_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
set_dom_string_attribute_property_on_object(
|
||||
scope,
|
||||
args.this(),
|
||||
"sandbox",
|
||||
args.get(0),
|
||||
"HTMLIFrameElement",
|
||||
"sandbox",
|
||||
);
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
fn set_dom_string_treat_null_as_empty_on_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
|
||||
@@ -150,6 +150,9 @@ pub(super) enum DomTokenListKind {
|
||||
Class,
|
||||
Part,
|
||||
Rel,
|
||||
HtmlFor,
|
||||
Sandbox,
|
||||
Sizes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
||||
+6
-2
@@ -1034,10 +1034,14 @@ fn detached_resource_template_accessors_use_owner_prototypes() {
|
||||
}
|
||||
assert(iframe.csp === "", "iframe csp default");
|
||||
iframe.csp = 123456;
|
||||
const sandbox = iframe.sandbox;
|
||||
assert(Object.prototype.toString.call(sandbox) === "[object DOMTokenList]", "iframe sandbox type");
|
||||
assert(sandbox === iframe.sandbox, "iframe sandbox SameObject");
|
||||
iframe.sandbox = "allow-scripts";
|
||||
iframe.allowFullscreen = true;
|
||||
assert(iframe.csp === "123456" && iframe.getAttribute("csp") === "123456", "iframe csp");
|
||||
assert(iframe.sandbox === "allow-scripts" && iframe.getAttribute("sandbox") === "allow-scripts", "iframe sandbox");
|
||||
assert(sandbox.value === "allow-scripts" && iframe.getAttribute("sandbox") === "allow-scripts", "iframe sandbox");
|
||||
assert(sandbox.supports("ALLOW-SCRIPTS"), "iframe sandbox supported token");
|
||||
assert(iframe.allowFullscreen === true && iframe.getAttribute("allowfullscreen") === "", "iframe allowFullscreen");
|
||||
for (const name of ["csp", "sandbox", "allowFullscreen"]) {
|
||||
assert(!own(iframe, name), `iframe.${name} should stay inherited after set`);
|
||||
@@ -1046,7 +1050,7 @@ fn detached_resource_template_accessors_use_owner_prototypes() {
|
||||
}
|
||||
iframe.setAttribute("csp", "default-src 'self'");
|
||||
assert(iframe.csp === "default-src 'self'", "iframe csp after delete");
|
||||
assert(iframe.sandbox === "allow-scripts", "iframe sandbox after delete");
|
||||
assert(iframe.sandbox === sandbox && sandbox.value === "allow-scripts", "iframe sandbox after delete");
|
||||
assert(iframe.allowFullscreen === true, "iframe allowFullscreen after delete");
|
||||
}
|
||||
|
||||
|
||||
+98
@@ -2732,3 +2732,101 @@ fn element_heading_reflections_drive_flat_tree_heading_matching() {
|
||||
r#"{"owner":[true,false,true,true],"initial":[0,false,true],"parentOffset":["3",true],"reset":[true,true],"clamped":["20",8,true],"slotted":true,"modalState":[false,true,true],"badGetter":"throw:TypeError","badSetter":"throw:TypeError"}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reflected_dom_token_list_attributes_are_live_same_object_and_owner_scoped() {
|
||||
let mut vm = new_storage_test_vm("https://reflected-token-lists.test/");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const assert = (condition, message) => {
|
||||
if (!condition) throw new Error(message);
|
||||
};
|
||||
const svg = "http://www.w3.org/2000/svg";
|
||||
const detached = document.implementation.createHTMLDocument("");
|
||||
const descriptors = [
|
||||
[HTMLIFrameElement.prototype, "sandbox"],
|
||||
[HTMLLinkElement.prototype, "sizes"],
|
||||
[HTMLOutputElement.prototype, "htmlFor"],
|
||||
[SVGAElement.prototype, "relList"]
|
||||
];
|
||||
for (const [prototype, name] of descriptors) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
||||
assert(!!descriptor, `${prototype.constructor.name}.${name} descriptor`);
|
||||
assert(typeof descriptor.get === "function", `${name} getter`);
|
||||
assert(typeof descriptor.set === "function", `${name} PutForwards setter`);
|
||||
assert(descriptor.enumerable && descriptor.configurable, `${name} descriptor flags`);
|
||||
}
|
||||
|
||||
const noSupportedTokens = list => {
|
||||
try {
|
||||
list.supports("anything");
|
||||
return false;
|
||||
} catch (error) {
|
||||
return error && error.name === "TypeError";
|
||||
}
|
||||
};
|
||||
const exercise = ownerDocument => {
|
||||
const iframe = ownerDocument.createElement("iframe");
|
||||
const sandbox = iframe.sandbox;
|
||||
assert(Object.prototype.toString.call(sandbox) === "[object DOMTokenList]", "sandbox type");
|
||||
assert(sandbox === iframe.sandbox, "sandbox SameObject");
|
||||
iframe.sandbox = "allow-scripts allow-forms allow-scripts";
|
||||
assert(sandbox.length === 2 && sandbox.contains("allow-forms"), "sandbox tokens");
|
||||
assert(iframe.getAttribute("sandbox") === "allow-scripts allow-forms allow-scripts", "sandbox PutForwards");
|
||||
assert(sandbox.supports("ALLOW-SCRIPTS"), "sandbox supports ASCII case-insensitively");
|
||||
assert(sandbox.supports("allow-storage-access-by-user-activation"), "sandbox storage-access token");
|
||||
assert(!sandbox.supports("unknown"), "sandbox rejects unknown token");
|
||||
sandbox.add("allow-popups");
|
||||
assert(iframe.getAttribute("sandbox") === "allow-scripts allow-forms allow-popups", "sandbox mutation reflects");
|
||||
|
||||
const output = ownerDocument.createElement("output");
|
||||
const htmlFor = output.htmlFor;
|
||||
assert(Object.prototype.toString.call(htmlFor) === "[object DOMTokenList]", "htmlFor type");
|
||||
assert(htmlFor === output.htmlFor, "htmlFor SameObject");
|
||||
output.htmlFor = "first second first";
|
||||
htmlFor.add("third");
|
||||
assert(output.getAttribute("for") === "first second third", "htmlFor reflects for");
|
||||
assert(noSupportedTokens(htmlFor), "htmlFor has no supported tokens");
|
||||
|
||||
const link = ownerDocument.createElement("link");
|
||||
const sizes = link.sizes;
|
||||
assert(Object.prototype.toString.call(sizes) === "[object DOMTokenList]", "sizes type");
|
||||
assert(sizes === link.sizes, "sizes SameObject");
|
||||
assert(sizes !== link.relList, "link token lists have distinct identity");
|
||||
link.sizes = "16x16 32x32 16x16";
|
||||
sizes.remove("16x16");
|
||||
assert(link.getAttribute("sizes") === "32x32", "sizes mutation reflects");
|
||||
assert(noSupportedTokens(sizes), "sizes has no supported tokens");
|
||||
|
||||
const anchor = ownerDocument.createElementNS(svg, "a");
|
||||
const relList = anchor.relList;
|
||||
assert(Object.prototype.toString.call(relList) === "[object DOMTokenList]", "SVG relList type");
|
||||
assert(relList === anchor.relList, "SVG relList SameObject");
|
||||
anchor.relList = "noopener noreferrer";
|
||||
relList.add("opener");
|
||||
assert(anchor.getAttribute("rel") === "noopener noreferrer opener", "SVG relList reflects rel");
|
||||
assert(relList.supports("NOOPENER"), "SVG relList supported tokens");
|
||||
};
|
||||
|
||||
exercise(document);
|
||||
exercise(detached);
|
||||
|
||||
const div = document.createElement("div");
|
||||
for (const name of ["htmlFor", "sandbox", "sizes", "relList"]) {
|
||||
assert(div[name] === undefined, `div.${name} should be undefined`);
|
||||
}
|
||||
assert(document.createElementNS(svg, "link").sizes === undefined, "SVG link.sizes");
|
||||
assert(document.createElementNS(svg, "output").htmlFor === undefined, "SVG output.htmlFor");
|
||||
assert(document.createElementNS(svg, "iframe").sandbox === undefined, "SVG iframe.sandbox");
|
||||
assert(document.createElement("svg").relList === undefined, "HTML svg.relList");
|
||||
return "ok";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("reflected DOMTokenList attributes should evaluate");
|
||||
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user