diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index bb02119c406..86ee03d7ddd 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -3667,6 +3667,8 @@ class CGSpecializedMethod(CGAbstractExternMethod): @staticmethod def makeNativeName(descriptor, method): + if method.underlyingAttr: + return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr) name = method.identifier.name nativeName = descriptor.binaryNameFor(name) if nativeName == name: @@ -5762,7 +5764,7 @@ class CGInterfaceTrait(CGThing): for m in descriptor.interface.members: if (m.isMethod() and not m.isStatic() and not m.isMaplikeOrSetlikeOrIterableMethod() and - (not m.isIdentifierLess() or m.isStringifier()) and + (not m.isIdentifierLess() or (m.isStringifier() and not m.underlyingAttr)) and not m.isDefaultToJSON()): name = CGSpecializedMethod.makeNativeName(descriptor, m) infallible = 'infallible' in descriptor.getExtendedAttributes(m) @@ -6172,10 +6174,6 @@ class CGDescriptor(CGThing): cgThings.append(CGSpecializedMethod(descriptor, m)) cgThings.append(CGMemberJITInfo(descriptor, m)) elif m.isAttr(): - if m.stringifier: - raise TypeError("Stringifier attributes not supported yet. " - "See https://github.com/servo/servo/issues/7590\n" - "%s" % m.location) if m.getExtendedAttribute("Unscopable"): assert not m.isStatic() unscopableNames.append(m.identifier.name) diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index 215d9c1212f..b2e56c9deaf 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -760,6 +760,8 @@ class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMix # specified, it already has a nonempty exposure global names set. if len(m._exposureGlobalNames) == 0: m._exposureGlobalNames.update(self._exposureGlobalNames) + if m.isAttr() and m.stringifier: + m.expand(self.members) # resolve() will modify self.members, so we need to iterate # over a copy of the member list here. @@ -1855,6 +1857,9 @@ class IDLDictionary(IDLObjectWithScope): self._finished = False self.members = list(members) self._partialDictionaries = [] + self._extendedAttrDict = {} + self.needsConversionToJS = False + self.needsConversionFromJS = False IDLObjectWithScope.__init__(self, location, parentScope, name) @@ -1988,11 +1993,34 @@ class IDLDictionary(IDLObjectWithScope): self.identifier.name, [member.location] + locations) + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) + def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError("There are no extended attributes that are " - "allowed on dictionaries", - [attrs[0].location, self.location]) + for attr in attrs: + identifier = attr.identifier() + + if (identifier == "GenerateInitFromJSON" or + identifier == "GenerateInit"): + if not attr.noArguments(): + raise WebIDLError("[%s] must not have arguments" % identifier, + [attr.location]) + self.needsConversionFromJS = True + elif (identifier == "GenerateConversionToJS" or + identifier == "GenerateToJSON"): + if not attr.noArguments(): + raise WebIDLError("[%s] must not have arguments" % identifier, + [attr.location]) + # ToJSON methods require to-JS conversion, because we + # implement ToJSON by converting to a JS object and + # then using JSON.stringify. + self.needsConversionToJS = True + else: + raise WebIDLError("[%s] extended attribute not allowed on " + "dictionaries" % identifier, + [attr.location]) + + self._extendedAttrDict[identifier] = True def _getDependentObjects(self): deps = set(self.members) @@ -2004,6 +2032,7 @@ class IDLDictionary(IDLObjectWithScope): assert self.identifier.name == partial.identifier.name self._partialDictionaries.append(partial) + class IDLEnum(IDLObjectWithIdentifier): def __init__(self, location, parentScope, name, values): assert isinstance(parentScope, IDLScope) @@ -4622,6 +4651,40 @@ class IDLAttribute(IDLInterfaceMember): def _getDependentObjects(self): return set([self.type]) + def expand(self, members): + assert self.stringifier + if not self.type.isDOMString() and not self.type.isUSVString(): + raise WebIDLError("The type of a stringifer attribute must be " + "either DOMString or USVString", + [self.location]) + identifier = IDLUnresolvedIdentifier(self.location, "__stringifier", + allowDoubleUnderscore=True) + method = IDLMethod(self.location, + identifier, + returnType=self.type, arguments=[], + stringifier=True, underlyingAttr=self) + allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"] + # Safe to ignore these as they are only meaningful for attributes + attributeOnlyExtAttrs = [ + "CEReactions", + "CrossOriginWritable", + "SetterThrows", + ] + for (key, value) in self._extendedAttrDict.items(): + if key in allowedExtAttrs: + if value is not True: + raise WebIDLError("[%s] with a value is currently " + "unsupported in stringifier attributes, " + "please file a bug to add support" % key, + [self.location]) + method.addExtendedAttributes([IDLExtendedAttribute(self.location, (key,))]) + elif not key in attributeOnlyExtAttrs: + raise WebIDLError("[%s] is currently unsupported in " + "stringifier attributes, please file a bug " + "to add support" % key, + [self.location]) + members.append(method) + class IDLArgument(IDLObjectWithIdentifier): def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False, allowTypeAttributes=False): @@ -4867,7 +4930,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): static=False, getter=False, setter=False, deleter=False, specialType=NamedOrIndexed.Neither, legacycaller=False, stringifier=False, - maplikeOrSetlikeOrIterable=None): + maplikeOrSetlikeOrIterable=None, + underlyingAttr=None): # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Method) @@ -4894,6 +4958,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase) self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable self._htmlConstructor = False + self.underlyingAttr = underlyingAttr self._specialType = specialType self._unforgeable = False self.dependsOn = "Everything" @@ -4933,7 +4998,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert len(self._overloads) == 1 overload = self._overloads[0] assert len(overload.arguments) == 0 - assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] + if not self.underlyingAttr: + assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] def isStatic(self): return self._static diff --git a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py index 14c2c5226fc..deabdc5ec81 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py @@ -44,3 +44,103 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'") + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + stringifier attribute DOMString foo; + }; + """) + results = parser.finish() + harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute), + "Stringifier attribute should be an attribute") + stringifier = results[0].members[1] + harness.ok(isinstance(stringifier, WebIDL.IDLMethod), + "Stringifier attribute should insert a method") + harness.ok(stringifier.isStringifier(), + "Inserted method should be a stringifier") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier {}; + interface mixin TestStringifierMixin { + stringifier attribute DOMString foo; + }; + TestStringifier includes TestStringifierMixin; + """) + results = parser.finish() + harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute), + "Stringifier attribute should be an attribute") + stringifier = results[0].members[1] + harness.ok(isinstance(stringifier, WebIDL.IDLMethod), + "Stringifier attribute should insert a method") + harness.ok(stringifier.isStringifier(), + "Inserted method should be a stringifier") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + stringifier attribute USVString foo; + }; + """) + results = parser.finish() + stringifier = results[0].members[1] + harness.ok(stringifier.signatures()[0][0].isUSVString(), + "Stringifier attributes should allow USVString") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + [Throws, NeedsSubjectPrincipal] + stringifier attribute USVString foo; + }; + """) + results = parser.finish() + stringifier = results[0].members[1] + harness.ok(stringifier.getExtendedAttribute("Throws"), + "Stringifier attributes should support [Throws]") + harness.ok(stringifier.getExtendedAttribute("NeedsSubjectPrincipal"), + "Stringifier attributes should support [NeedsSubjectPrincipal]") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier attribute ByteString foo; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow ByteString") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier; + stringifier attribute DOMString foo; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow a 'stringifier;' and a stringifier attribute") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier attribute DOMString foo; + stringifier attribute DOMString bar; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow multiple stringifier attributes") diff --git a/components/script/dom/domtokenlist.rs b/components/script/dom/domtokenlist.rs index cb45edf186b..d6c5ac1537f 100644 --- a/components/script/dom/domtokenlist.rs +++ b/components/script/dom/domtokenlist.rs @@ -177,11 +177,6 @@ impl DOMTokenListMethods for DOMTokenList { Ok(()) } - // https://dom.spec.whatwg.org/#concept-dtl-serialize - fn Stringifier(&self) -> DOMString { - self.element.get_string_attribute(&self.local_name) - } - // check-tidy: no specs after this line fn IndexedGetter(&self, index: u32) -> Option { self.Item(index) diff --git a/components/script/dom/htmlanchorelement.rs b/components/script/dom/htmlanchorelement.rs index de082a9807e..f90764d8a0a 100644 --- a/components/script/dom/htmlanchorelement.rs +++ b/components/script/dom/htmlanchorelement.rs @@ -526,11 +526,6 @@ impl HTMLAnchorElementMethods for HTMLAnchorElement { // Step 5. self.update_href(url); } - - // https://html.spec.whatwg.org/multipage/#dom-hyperlink-href - fn Stringifier(&self) -> DOMString { - DOMString::from(self.Href().0) - } } impl Activatable for HTMLAnchorElement { diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 65d39a15755..df7f475b2d8 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -9,7 +9,7 @@ use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; use crate::dom::urlhelper::UrlHelper; use crate::dom::window::Window; @@ -246,11 +246,6 @@ impl LocationMethods for Location { Ok(()) } - // https://html.spec.whatwg.org/multipage/#dom-location-href - fn Stringifier(&self) -> Fallible { - Ok(DOMString::from(self.GetHref()?.0)) - } - // https://html.spec.whatwg.org/multipage/#dom-location-search fn GetSearch(&self) -> Fallible { self.check_same_origin_domain()?; diff --git a/components/script/dom/url.rs b/components/script/dom/url.rs index b31333b7ac2..9952739243b 100644 --- a/components/script/dom/url.rs +++ b/components/script/dom/url.rs @@ -270,11 +270,6 @@ impl URLMethods for URL { .or_init(|| URLSearchParams::new(&self.global(), Some(self))) } - // https://url.spec.whatwg.org/#dom-url-href - fn Stringifier(&self) -> DOMString { - DOMString::from(self.Href().0) - } - // https://url.spec.whatwg.org/#dom-url-username fn Username(&self) -> USVString { UrlHelper::Username(&self.url.borrow()) diff --git a/components/script/dom/webidls/DOMTokenList.webidl b/components/script/dom/webidls/DOMTokenList.webidl index c4b1ff776bc..ed4a541f730 100644 --- a/components/script/dom/webidls/DOMTokenList.webidl +++ b/components/script/dom/webidls/DOMTokenList.webidl @@ -22,8 +22,7 @@ interface DOMTokenList { void replace(DOMString token, DOMString newToken); [CEReactions, Pure] - attribute DOMString value; + stringifier attribute DOMString value; - stringifier; iterable; }; diff --git a/components/script/dom/webidls/HTMLHyperlinkElementUtils.webidl b/components/script/dom/webidls/HTMLHyperlinkElementUtils.webidl index 7c4750a58f6..2f0f0ae68c0 100644 --- a/components/script/dom/webidls/HTMLHyperlinkElementUtils.webidl +++ b/components/script/dom/webidls/HTMLHyperlinkElementUtils.webidl @@ -4,10 +4,8 @@ // https://html.spec.whatwg.org/multipage/#htmlhyperlinkelementutils interface mixin HTMLHyperlinkElementUtils { - // [CEReactions] - // stringifier attribute USVString href; [CEReactions] - attribute USVString href; + stringifier attribute USVString href; readonly attribute USVString origin; [CEReactions] attribute USVString protocol; @@ -27,9 +25,4 @@ interface mixin HTMLHyperlinkElementUtils { attribute USVString search; [CEReactions] attribute USVString hash; - - // Adding a separate stringifier method until - // https://github.com/servo/servo/issues/7590 adds attribute stringifier - // support. - stringifier; }; diff --git a/components/script/dom/webidls/Location.webidl b/components/script/dom/webidls/Location.webidl index 8dd69f2c36a..4120fc2731d 100644 --- a/components/script/dom/webidls/Location.webidl +++ b/components/script/dom/webidls/Location.webidl @@ -4,7 +4,7 @@ // https://html.spec.whatwg.org/multipage/#location [Exposed=Window, Unforgeable] interface Location { - /*stringifier*/ [Throws] attribute USVString href; + [Throws] stringifier attribute USVString href; [Throws] readonly attribute USVString origin; [Throws] attribute USVString protocol; [Throws] attribute USVString host; @@ -19,9 +19,4 @@ [Throws] void reload(); //[SameObject] readonly attribute USVString[] ancestorOrigins; - - // This is only doing as well as gecko right now. - // https://github.com/servo/servo/issues/7590 is on file for - // adding attribute stringifier support. - [Throws] stringifier; }; diff --git a/components/script/dom/webidls/MediaList.webidl b/components/script/dom/webidls/MediaList.webidl index 0851fb5c220..b2ba10f0d7a 100644 --- a/components/script/dom/webidls/MediaList.webidl +++ b/components/script/dom/webidls/MediaList.webidl @@ -3,10 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://drafts.csswg.org/cssom/#the-medialist-interface -// [LegacyArrayClass] [Exposed=Window] interface MediaList { - /* stringifier */ attribute [TreatNullAs=EmptyString] DOMString mediaText; + stringifier attribute [TreatNullAs=EmptyString] DOMString mediaText; readonly attribute unsigned long length; getter DOMString? item(unsigned long index); void appendMedium(DOMString medium); diff --git a/components/script/dom/webidls/URL.webidl b/components/script/dom/webidls/URL.webidl index d51f675ec0a..47a1ef53129 100644 --- a/components/script/dom/webidls/URL.webidl +++ b/components/script/dom/webidls/URL.webidl @@ -8,7 +8,7 @@ interface URL { [Throws] constructor(USVString url, optional USVString base); [SetterThrows] - /*stringifier*/ attribute USVString href; + stringifier attribute USVString href; readonly attribute USVString origin; attribute USVString protocol; attribute USVString username; @@ -27,9 +27,4 @@ interface URL { static void revokeObjectURL(DOMString url); USVString toJSON(); - - // This is only doing as well as gecko right now. - // https://github.com/servo/servo/issues/7590 is on file for - // adding attribute stringifier support. - stringifier; }; diff --git a/components/script/dom/webidls/WorkerLocation.webidl b/components/script/dom/webidls/WorkerLocation.webidl index faaf10932bc..8985da7e6e9 100644 --- a/components/script/dom/webidls/WorkerLocation.webidl +++ b/components/script/dom/webidls/WorkerLocation.webidl @@ -5,7 +5,7 @@ // https://html.spec.whatwg.org/multipage/#worker-locations [Exposed=Worker] interface WorkerLocation { - /*stringifier*/ readonly attribute USVString href; + stringifier readonly attribute USVString href; readonly attribute USVString origin; readonly attribute USVString protocol; readonly attribute USVString host; @@ -14,9 +14,4 @@ interface WorkerLocation { readonly attribute USVString pathname; readonly attribute USVString search; readonly attribute USVString hash; - - // This is only doing as well as gecko right now. - // https://github.com/servo/servo/issues/7590 is on file for - // adding attribute stringifier support. - stringifier; }; diff --git a/components/script/dom/workerlocation.rs b/components/script/dom/workerlocation.rs index 483cb69c084..d96aea8011c 100644 --- a/components/script/dom/workerlocation.rs +++ b/components/script/dom/workerlocation.rs @@ -6,7 +6,7 @@ use crate::dom::bindings::codegen::Bindings::WorkerLocationBinding; use crate::dom::bindings::codegen::Bindings::WorkerLocationBinding::WorkerLocationMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; -use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::bindings::str::USVString; use crate::dom::urlhelper::UrlHelper; use crate::dom::workerglobalscope::WorkerGlobalScope; use dom_struct::dom_struct; @@ -87,9 +87,4 @@ impl WorkerLocationMethods for WorkerLocation { fn Search(&self) -> USVString { UrlHelper::Search(&self.url) } - - // https://html.spec.whatwg.org/multipage/#dom-workerlocation-href - fn Stringifier(&self) -> DOMString { - DOMString::from(self.Href().0) - } } diff --git a/tests/wpt/metadata/css/cssom/MediaList.html.ini b/tests/wpt/metadata/css/cssom/MediaList.html.ini deleted file mode 100644 index f3ee37b050a..00000000000 --- a/tests/wpt/metadata/css/cssom/MediaList.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[MediaList.html] - type: testharness - [MediaList] - expected: FAIL - - [CSSOM - MediaList interface] - expected: FAIL - diff --git a/tests/wpt/metadata/css/cssom/idlharness.html.ini b/tests/wpt/metadata/css/cssom/idlharness.html.ini index fd06e75911e..4c18dc71370 100644 --- a/tests/wpt/metadata/css/cssom/idlharness.html.ini +++ b/tests/wpt/metadata/css/cssom/idlharness.html.ini @@ -95,9 +95,6 @@ [Stringification of sheet.cssRules[2\].cssRules[0\]] expected: FAIL - [MediaList interface: stringifier] - expected: FAIL - [CSSImportRule interface: attribute media] expected: FAIL