Skip to content

Commit c1d7005

Browse files
committed
Implement full long reflection
This makes maxLength and minLength have the precisely correct semantics for HTMLInputElement and HTMLTextAreaElement. And the code is now fully generated, instead of hand-coded.
1 parent ac815ff commit c1d7005

5 files changed

Lines changed: 53 additions & 45 deletions

File tree

lib/jsdom/living/nodes/HTMLInputElement-impl.js

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -748,35 +748,6 @@ class HTMLInputElementImpl extends HTMLElementImpl {
748748
return null;
749749
}
750750

751-
// Reflected IDL attribute does not care about whether the content attribute applies.
752-
get maxLength() {
753-
if (!this.hasAttributeNS(null, "maxlength")) {
754-
return 524288; // stole this from chrome
755-
}
756-
return parseInt(this.getAttributeNS(null, "maxlength"));
757-
}
758-
759-
set maxLength(value) {
760-
if (value < 0) {
761-
throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]);
762-
}
763-
this.setAttributeNS(null, "maxlength", String(value));
764-
}
765-
766-
get minLength() {
767-
if (!this.hasAttributeNS(null, "minlength")) {
768-
return 0;
769-
}
770-
return parseInt(this.getAttributeNS(null, "minlength"));
771-
}
772-
773-
set minLength(value) {
774-
if (value < 0) {
775-
throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]);
776-
}
777-
this.setAttributeNS(null, "minlength", String(value));
778-
}
779-
780751
get size() {
781752
if (!this.hasAttributeNS(null, "size")) {
782753
return 20;

lib/jsdom/living/nodes/HTMLInputElement.webidl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ interface HTMLInputElement : HTMLElement {
2222
[CEReactions, Reflect] attribute DOMString inputMode;
2323
readonly attribute HTMLElement? list;
2424
[CEReactions, Reflect] attribute DOMString max;
25-
[CEReactions] attribute long maxLength;
25+
[CEReactions, ReflectNonNegative] attribute long maxLength;
2626
[CEReactions, Reflect] attribute DOMString min;
27-
[CEReactions] attribute long minLength;
27+
[CEReactions, ReflectNonNegative] attribute long minLength;
2828
[CEReactions, Reflect] attribute boolean multiple;
2929
[CEReactions, Reflect] attribute DOMString name;
3030
[CEReactions, Reflect] attribute DOMString pattern;

lib/jsdom/living/nodes/HTMLTextAreaElement.webidl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ interface HTMLTextAreaElement : HTMLElement {
99
[CEReactions, Reflect] attribute boolean disabled;
1010
readonly attribute HTMLFormElement? form;
1111
[CEReactions, Reflect] attribute DOMString inputMode;
12-
[CEReactions, Reflect] attribute long maxLength; // TODO limited to only non-negative numbers
13-
[CEReactions, Reflect] attribute long minLength; // TODO limited to only non-negative numbers
12+
[CEReactions, ReflectNonNegative] attribute long maxLength;
13+
[CEReactions, ReflectNonNegative] attribute long minLength;
1414
[CEReactions, Reflect] attribute DOMString name;
1515
[CEReactions, Reflect] attribute DOMString placeholder;
1616
[CEReactions, Reflect] attribute boolean readOnly;

scripts/webidl/convert.js

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,16 @@ const transformer = new Webidl2js({
3939
},
4040
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes
4141
processReflect(idl, implObj) {
42-
const reflectAttr = idl.extAttrs.find(attr => attr.name === "Reflect");
43-
const attrName = (reflectAttr && reflectAttr.rhs && JSON.parse(reflectAttr.rhs.value)) || idl.name.toLowerCase();
42+
const reflectAttr = idl.extAttrs.find(
43+
attr => attr.name === "Reflect" || attr.name === "ReflectURL" || attr.name === "ReflectNonNegative"
44+
);
45+
const attrName = reflectAttr?.rhs ? JSON.parse(reflectAttr.rhs.value) : idl.name.toLowerCase();
4446

45-
if (idl.extAttrs.find(attr => attr.name === "ReflectURL")) {
47+
// TODO: [ReflectDefault] is only used for `long` right now; also use it for `unsigned long` and `double`.
48+
const reflectDefaultAttr = idl.extAttrs.find(attr => attr.name === "ReflectDefault");
49+
const reflectDefault = reflectDefaultAttr?.rhs ? JSON.parse(reflectDefaultAttr.rhs.value) : undefined;
50+
51+
if (reflectAttr.name === "ReflectURL") {
4652
// Allow DOMString also due to https://github.com/whatwg/html/issues/5241.
4753
if (!isSimpleIDLType(idl.idlType, "USVString") && !isSimpleIDLType(idl.idlType, "DOMString")) {
4854
throw new Error("[ReflectURL] specified on non-USV/DOMString attribute");
@@ -68,6 +74,13 @@ const transformer = new Webidl2js({
6874
};
6975
}
7076

77+
if (reflectAttr.name === "ReflectNonNegative") {
78+
if (!isSimpleIDLType(idl.idlType, "long")) {
79+
throw new Error("[ReflectNonNegative] specified on non-long attribute");
80+
}
81+
// We'll actually do the processing in the long case, later.
82+
}
83+
7184
if (isSimpleIDLType(idl.idlType, "DOMString") || isSimpleIDLType(idl.idlType, "USVString")) {
7285
if (idl.idlType.nullable) {
7386
// Nonstandard; see https://github.com/whatwg/html/issues/10037. This passes the WPTs though.
@@ -113,18 +126,46 @@ const transformer = new Webidl2js({
113126
}
114127

115128
if (isSimpleIDLType(idl.idlType, "long")) {
116-
const parseInteger = this.addImport("../helpers/strings", "parseInteger");
129+
const parser = this.addImport(
130+
"../helpers/strings",
131+
reflectAttr.name === "ReflectNonNegative" ? "parseNonNegativeInteger" : "parseInteger"
132+
);
133+
134+
let defaultValue;
135+
if (reflectDefault !== undefined) {
136+
defaultValue = reflectDefault;
137+
} else if (reflectAttr.name === "ReflectNonNegative") {
138+
defaultValue = -1;
139+
} else {
140+
defaultValue = 0;
141+
}
142+
143+
let setterPrefix = "";
144+
if (reflectAttr.name === "ReflectNonNegative") {
145+
const createDOMException = this.addImport("./DOMException", "create");
146+
setterPrefix = `
147+
if (V < 0) {
148+
throw ${createDOMException}(
149+
globalObject,
150+
[\`The negative value \${V} cannot be set for the ${idl.name} property.\`, "IndexSizeError"]
151+
);
152+
}
153+
`;
154+
}
117155

118156
return {
119157
get: `
120158
let value = ${implObj}._reflectGetTheContentAttribute("${attrName}");
121-
if (value === null) {
122-
return 0;
159+
if (value !== null) {
160+
value = ${parser}(value);
161+
if (value !== null && conversions.long(value) === value) {
162+
return value;
163+
}
123164
}
124-
value = ${parseInteger}(value);
125-
return value !== null && conversions.long(value) === value ? value : 0;
165+
return ${defaultValue};
126166
`,
127167
set: `
168+
${setterPrefix}
128169
${implObj}._reflectSetTheContentAttribute("${attrName}", String(V));
129170
`
130171
};

test/web-platform-tests/to-run.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,8 +1190,6 @@ input-type-button.html: [fail, Depends on offsetWidth]
11901190
input-type-change-value.html: [fail, Unknown]
11911191
input-type-checkbox-switch.tentative.window.html: [fail, Unknown]
11921192
input-untrusted-key-event.html: [fail-slow, Not implemented]
1193-
maxlength.html: [fail, Reflection not implemented correctly]
1194-
minlength.html: [fail, Reflection not implemented correctly]
11951193
pattern_attribute_v_flag.html: [fail, Unknown]
11961194
radio-disconnected-group-owner.html: [fail, Unknown]
11971195
range-2.html: [fail, step attribute not yet implemented]
@@ -1242,8 +1240,6 @@ show-picker-cross-origin-iframe.tentative.html: [timeout, Unknown]
12421240

12431241
DIR: html/semantics/forms/the-textarea-element
12441242

1245-
textarea-maxlength.html: [fail, Unknown]
1246-
textarea-minlength.html: [fail, Unknown]
12471243
textarea-placeholder-lineheight.html: [fail, getBoundingClientRect() is not implemented]
12481244
wrap-enumerated-ascii-case-insensitive.html: [timeout, Unknown]
12491245

0 commit comments

Comments
 (0)