Implement DOMTokenList.supports API

This commit is contained in:
Vincent Ricard 2020-09-20 23:05:41 +02:00
parent 3f7697690a
commit 2a4dd894de
20 changed files with 147 additions and 72 deletions

View file

@ -20,21 +20,35 @@ pub struct DOMTokenList {
reflector_: Reflector, reflector_: Reflector,
element: Dom<Element>, element: Dom<Element>,
local_name: LocalName, local_name: LocalName,
supported_tokens: Option<Vec<Atom>>,
} }
impl DOMTokenList { impl DOMTokenList {
pub fn new_inherited(element: &Element, local_name: LocalName) -> DOMTokenList { pub fn new_inherited(
element: &Element,
local_name: LocalName,
supported_tokens: Option<Vec<Atom>>,
) -> DOMTokenList {
DOMTokenList { DOMTokenList {
reflector_: Reflector::new(), reflector_: Reflector::new(),
element: Dom::from_ref(element), element: Dom::from_ref(element),
local_name: local_name, local_name: local_name,
supported_tokens: supported_tokens,
} }
} }
pub fn new(element: &Element, local_name: &LocalName) -> DomRoot<DOMTokenList> { pub fn new(
element: &Element,
local_name: &LocalName,
supported_tokens: Option<Vec<Atom>>,
) -> DomRoot<DOMTokenList> {
let window = window_from_node(element); let window = window_from_node(element);
reflect_dom_object( reflect_dom_object(
Box::new(DOMTokenList::new_inherited(element, local_name.clone())), Box::new(DOMTokenList::new_inherited(
element,
local_name.clone(),
supported_tokens,
)),
&*window, &*window,
) )
} }
@ -61,6 +75,25 @@ impl DOMTokenList {
self.element self.element
.set_atomic_tokenlist_attribute(&self.local_name, atoms) .set_atomic_tokenlist_attribute(&self.local_name, atoms)
} }
// https://dom.spec.whatwg.org/#concept-domtokenlist-validation
fn validation_steps(&self, token: &str) -> Fallible<bool> {
match &self.supported_tokens {
None => Err(Error::Type(
"This attribute has no supported tokens".to_owned(),
)),
Some(supported_tokens) => {
let token = Atom::from(token).to_ascii_lowercase();
if supported_tokens
.iter()
.any(|supported_token| *supported_token == token)
{
return Ok(true);
}
Ok(false)
},
}
}
} }
// https://dom.spec.whatwg.org/#domtokenlist // https://dom.spec.whatwg.org/#domtokenlist
@ -197,6 +230,11 @@ impl DOMTokenListMethods for DOMTokenList {
Ok(result) Ok(result)
} }
// https://dom.spec.whatwg.org/#dom-domtokenlist-supports
fn Supports(&self, token: DOMString) -> Fallible<bool> {
self.validation_steps(&token)
}
// check-tidy: no specs after this line // check-tidy: no specs after this line
fn IndexedGetter(&self, index: u32) -> Option<DOMString> { fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
self.Item(index) self.Item(index)

View file

@ -1953,7 +1953,7 @@ impl ElementMethods for Element {
// https://dom.spec.whatwg.org/#dom-element-classlist // https://dom.spec.whatwg.org/#dom-element-classlist
fn ClassList(&self) -> DomRoot<DOMTokenList> { fn ClassList(&self) -> DomRoot<DOMTokenList> {
self.class_list self.class_list
.or_init(|| DOMTokenList::new(self, &local_name!("class"))) .or_init(|| DOMTokenList::new(self, &local_name!("class"), None))
} }
// https://dom.spec.whatwg.org/#dom-element-attributes // https://dom.spec.whatwg.org/#dom-element-attributes

View file

@ -32,6 +32,7 @@ use html5ever::{LocalName, Prefix};
use net_traits::request::Referrer; use net_traits::request::Referrer;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin}; use script_traits::{HistoryEntryReplacement, LoadData, LoadOrigin};
use servo_atoms::Atom;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::default::Default; use std::default::Default;
use style::attr::AttrValue; use style::attr::AttrValue;
@ -138,8 +139,17 @@ impl HTMLAnchorElementMethods for HTMLAnchorElement {
// https://html.spec.whatwg.org/multipage/#dom-a-rellist // https://html.spec.whatwg.org/multipage/#dom-a-rellist
fn RelList(&self) -> DomRoot<DOMTokenList> { fn RelList(&self) -> DomRoot<DOMTokenList> {
self.rel_list self.rel_list.or_init(|| {
.or_init(|| DOMTokenList::new(self.upcast(), &local_name!("rel"))) DOMTokenList::new(
self.upcast(),
&local_name!("rel"),
Some(vec![
Atom::from("noopener"),
Atom::from("noreferrer"),
Atom::from("opener"),
]),
)
})
} }
// https://html.spec.whatwg.org/multipage/#dom-a-coords // https://html.spec.whatwg.org/multipage/#dom-a-coords

View file

@ -19,6 +19,7 @@ use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Point2D; use euclid::default::Point2D;
use html5ever::{LocalName, Prefix}; use html5ever::{LocalName, Prefix};
use servo_atoms::Atom;
use std::default::Default; use std::default::Default;
use std::f32; use std::f32;
use std::str; use std::str;
@ -303,10 +304,28 @@ impl HTMLAreaElementMethods for HTMLAreaElement {
// https://html.spec.whatwg.org/multipage/#attr-hyperlink-target // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
make_setter!(SetTarget, "target"); make_setter!(SetTarget, "target");
// https://html.spec.whatwg.org/multipage/#dom-a-rel
make_getter!(Rel, "rel");
// https://html.spec.whatwg.org/multipage/#dom-a-rel
fn SetRel(&self, rel: DOMString) {
self.upcast::<Element>()
.set_tokenlist_attribute(&local_name!("rel"), rel);
}
// https://html.spec.whatwg.org/multipage/#dom-area-rellist // https://html.spec.whatwg.org/multipage/#dom-area-rellist
fn RelList(&self) -> DomRoot<DOMTokenList> { fn RelList(&self) -> DomRoot<DOMTokenList> {
self.rel_list self.rel_list.or_init(|| {
.or_init(|| DOMTokenList::new(self.upcast(), &local_name!("rel"))) DOMTokenList::new(
self.upcast(),
&local_name!("rel"),
Some(vec![
Atom::from("noopener"),
Atom::from("noreferrer"),
Atom::from("opener"),
]),
)
})
} }
} }

View file

@ -450,8 +450,17 @@ impl HTMLFormElementMethods for HTMLFormElement {
// https://html.spec.whatwg.org/multipage/#dom-a-rellist // https://html.spec.whatwg.org/multipage/#dom-a-rellist
fn RelList(&self) -> DomRoot<DOMTokenList> { fn RelList(&self) -> DomRoot<DOMTokenList> {
self.rel_list self.rel_list.or_init(|| {
.or_init(|| DOMTokenList::new(self.upcast(), &local_name!("rel"))) DOMTokenList::new(
self.upcast(),
&local_name!("rel"),
Some(vec![
Atom::from("noopener"),
Atom::from("noreferrer"),
Atom::from("opener"),
]),
)
})
} }
// https://html.spec.whatwg.org/multipage/#the-form-element:supported-property-names // https://html.spec.whatwg.org/multipage/#the-form-element:supported-property-names

View file

@ -38,6 +38,7 @@ use script_traits::{
LoadOrigin, UpdatePipelineIdReason, WindowSizeData, LoadOrigin, UpdatePipelineIdReason, WindowSizeData,
}; };
use script_traits::{NewLayoutInfo, ScriptMsg}; use script_traits::{NewLayoutInfo, ScriptMsg};
use servo_atoms::Atom;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::cell::Cell; use std::cell::Cell;
use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::attr::{AttrValue, LengthOrPercentageOrAuto};
@ -530,8 +531,20 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
// https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox
fn Sandbox(&self) -> DomRoot<DOMTokenList> { fn Sandbox(&self) -> DomRoot<DOMTokenList> {
self.sandbox self.sandbox.or_init(|| {
.or_init(|| DOMTokenList::new(self.upcast::<Element>(), &local_name!("sandbox"))) DOMTokenList::new(
self.upcast::<Element>(),
&local_name!("sandbox"),
Some(vec![
Atom::from("allow-same-origin"),
Atom::from("allow-forms"),
Atom::from("allow-pointer-lock"),
Atom::from("allow-popups"),
Atom::from("allow-scripts"),
Atom::from("allow-top-navigation"),
]),
)
})
} }
// https://html.spec.whatwg.org/multipage/#dom-iframe-contentwindow // https://html.spec.whatwg.org/multipage/#dom-iframe-contentwindow

View file

@ -30,6 +30,7 @@ use embedder_traits::EmbedderMsg;
use html5ever::{LocalName, Prefix}; use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy; use net_traits::ReferrerPolicy;
use servo_arc::Arc; use servo_arc::Arc;
use servo_atoms::Atom;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::Cell; use std::cell::Cell;
use std::default::Default; use std::default::Default;
@ -428,8 +429,29 @@ impl HTMLLinkElementMethods for HTMLLinkElement {
// https://html.spec.whatwg.org/multipage/#dom-link-rellist // https://html.spec.whatwg.org/multipage/#dom-link-rellist
fn RelList(&self) -> DomRoot<DOMTokenList> { fn RelList(&self) -> DomRoot<DOMTokenList> {
self.rel_list self.rel_list.or_init(|| {
.or_init(|| DOMTokenList::new(self.upcast(), &local_name!("rel"))) DOMTokenList::new(
self.upcast(),
&local_name!("rel"),
Some(vec![
Atom::from("alternate"),
Atom::from("apple-touch-icon"),
Atom::from("apple-touch-icon-precomposed"),
Atom::from("canonical"),
Atom::from("dns-prefetch"),
Atom::from("icon"),
Atom::from("import"),
Atom::from("manifest"),
Atom::from("modulepreload"),
Atom::from("next"),
Atom::from("preconnect"),
Atom::from("prefetch"),
Atom::from("preload"),
Atom::from("prerender"),
Atom::from("stylesheet"),
]),
)
})
} }
// https://html.spec.whatwg.org/multipage/#dom-link-charset // https://html.spec.whatwg.org/multipage/#dom-link-charset

View file

@ -20,6 +20,8 @@ interface DOMTokenList {
boolean toggle(DOMString token, optional boolean force); boolean toggle(DOMString token, optional boolean force);
[CEReactions, Throws] [CEReactions, Throws]
boolean replace(DOMString token, DOMString newToken); boolean replace(DOMString token, DOMString newToken);
[Pure, Throws]
boolean supports(DOMString token);
[CEReactions, Pure] [CEReactions, Pure]
stringifier attribute DOMString value; stringifier attribute DOMString value;

View file

@ -19,8 +19,8 @@ interface HTMLAreaElement : HTMLElement {
// attribute DOMString download; // attribute DOMString download;
// [CEReactions] // [CEReactions]
// attribute USVString ping; // attribute USVString ping;
// [CEReactions] [CEReactions]
// attribute DOMString rel; attribute DOMString rel;
[SameObject, PutForwards=value] readonly attribute DOMTokenList relList; [SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
// hreflang and type are not reflected // hreflang and type are not reflected
}; };

View file

@ -17,12 +17,6 @@
[alt on HTMLAreaElement must enqueue an attributeChanged reaction when adding a new attribute] [alt on HTMLAreaElement must enqueue an attributeChanged reaction when adding a new attribute]
expected: FAIL expected: FAIL
[rel on HTMLAreaElement must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL
[rel on HTMLAreaElement must enqueue an attributeChanged reaction when adding a new attribute]
expected: FAIL
[ping on HTMLAreaElement must enqueue an attributeChanged reaction when replacing an existing attribute] [ping on HTMLAreaElement must enqueue an attributeChanged reaction when replacing an existing attribute]
expected: FAIL expected: FAIL

View file

@ -68,9 +68,6 @@
[Event interface: new CustomEvent("foo") must inherit property "composed" with the proper type] [Event interface: new CustomEvent("foo") must inherit property "composed" with the proper type]
expected: FAIL expected: FAIL
[DOMTokenList interface: document.body.classList must inherit property "supports(DOMString)" with the proper type]
expected: FAIL
[StaticRange interface: existence and properties of interface prototype object's "constructor" property] [StaticRange interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL expected: FAIL
@ -179,9 +176,6 @@
[AbstractRange interface: existence and properties of interface prototype object's "constructor" property] [AbstractRange interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL expected: FAIL
[DOMTokenList interface: operation supports(DOMString)]
expected: FAIL
[EventTarget interface: calling dispatchEvent(Event) on new AbortController().signal with too few arguments must throw TypeError] [EventTarget interface: calling dispatchEvent(Event) on new AbortController().signal with too few arguments must throw TypeError]
expected: FAIL expected: FAIL
@ -212,9 +206,6 @@
[Event interface: document.createEvent("Event") must inherit property "composedPath()" with the proper type] [Event interface: document.createEvent("Event") must inherit property "composedPath()" with the proper type]
expected: FAIL expected: FAIL
[DOMTokenList interface: calling supports(DOMString) on document.body.classList with too few arguments must throw TypeError]
expected: FAIL
[StaticRange interface object length] [StaticRange interface object length]
expected: FAIL expected: FAIL

View file

@ -8,7 +8,3 @@
[<link rel='prefetch' href='https://web-platform.test:8443/...'>] [<link rel='prefetch' href='https://web-platform.test:8443/...'>]
expected: TIMEOUT expected: TIMEOUT
[Browser supports prefetch.]
expected: FAIL

View file

@ -24,9 +24,6 @@
[<link rel='preload' as='track' href='https://web-platform.test:8443/...'>] [<link rel='preload' as='track' href='https://web-platform.test:8443/...'>]
expected: TIMEOUT expected: TIMEOUT
[Browser supports preload.]
expected: FAIL
[<link rel='preload' as='fetch' href='https://www.not-web-platform.test:8443/...'>] [<link rel='preload' as='fetch' href='https://www.not-web-platform.test:8443/...'>]
expected: TIMEOUT expected: TIMEOUT

View file

@ -36,3 +36,12 @@
[Http upgrade fetch() api] [Http upgrade fetch() api]
expected: NOTRUN expected: NOTRUN
[Http upgrade preload]
expected: TIMEOUT
[Http upgrade stylesheet]
expected: NOTRUN
[Http upgrade track]
expected: NOTRUN

View file

@ -9,3 +9,12 @@
[Https downgrade embed] [Https downgrade embed]
expected: TIMEOUT expected: TIMEOUT
[Https downgrade preload]
expected: TIMEOUT
[Https downgrade stylesheet]
expected: NOTRUN
[Https downgrade track]
expected: NOTRUN

View file

@ -2550,9 +2550,6 @@
[HTMLMeterElement interface: attribute optimum] [HTMLMeterElement interface: attribute optimum]
expected: FAIL expected: FAIL
[HTMLAreaElement interface: document.createElement("area") must inherit property "rel" with the proper type]
expected: FAIL
[HTMLInputElement interface: createInput("password") must inherit property "useMap" with the proper type] [HTMLInputElement interface: createInput("password") must inherit property "useMap" with the proper type]
expected: FAIL expected: FAIL
@ -2646,9 +2643,6 @@
[HTMLFrameElement interface: attribute frameBorder] [HTMLFrameElement interface: attribute frameBorder]
expected: FAIL expected: FAIL
[HTMLAreaElement interface: attribute rel]
expected: FAIL
[HTMLImageElement interface: new Image() must inherit property "decode()" with the proper type] [HTMLImageElement interface: new Image() must inherit property "decode()" with the proper type]
expected: FAIL expected: FAIL

View file

@ -1,4 +0,0 @@
[link-rel-manifest.html]
[link element supports a rel value of "manifest".]
expected: FAIL

View file

@ -1,5 +0,0 @@
[link-rellist.html]
type: testharness
[link.relList: non-string contains]
expected: FAIL

View file

@ -1,7 +1,4 @@
[sandbox-ascii-case-insensitive.html] [sandbox-ascii-case-insensitive.html]
[iframe 'sandbox' ASCII case insensitive, allow-same-orİgin]
expected: FAIL
[iframe 'sandbox' ASCII case insensitive, allow-ſcripts] [iframe 'sandbox' ASCII case insensitive, allow-ſcripts]
expected: FAIL expected: FAIL

View file

@ -1,16 +0,0 @@
[rellist-feature-detection.html]
[Make sure that relList based feature detection is working]
expected: FAIL
[Make sure that relList based feature detection is working for <a>]
expected: FAIL
[Make sure that relList based feature detection is working for <form>]
expected: FAIL
[Make sure that relList based feature detection is working for <area>]
expected: FAIL
[Make sure that relList based feature detection is working for <link>]
expected: FAIL