Skip to content

Commit

Permalink
Implement full long reflection
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
domenic committed Jan 21, 2024
1 parent ac815ff commit c1d7005
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 45 deletions.
29 changes: 0 additions & 29 deletions lib/jsdom/living/nodes/HTMLInputElement-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -748,35 +748,6 @@ class HTMLInputElementImpl extends HTMLElementImpl {
return null;
}

// Reflected IDL attribute does not care about whether the content attribute applies.
get maxLength() {
if (!this.hasAttributeNS(null, "maxlength")) {
return 524288; // stole this from chrome
}
return parseInt(this.getAttributeNS(null, "maxlength"));
}

set maxLength(value) {
if (value < 0) {
throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]);
}
this.setAttributeNS(null, "maxlength", String(value));
}

get minLength() {
if (!this.hasAttributeNS(null, "minlength")) {
return 0;
}
return parseInt(this.getAttributeNS(null, "minlength"));
}

set minLength(value) {
if (value < 0) {
throw DOMException.create(this._globalObject, ["The index is not in the allowed range.", "IndexSizeError"]);
}
this.setAttributeNS(null, "minlength", String(value));
}

get size() {
if (!this.hasAttributeNS(null, "size")) {
return 20;
Expand Down
4 changes: 2 additions & 2 deletions lib/jsdom/living/nodes/HTMLInputElement.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ interface HTMLInputElement : HTMLElement {
[CEReactions, Reflect] attribute DOMString inputMode;
readonly attribute HTMLElement? list;
[CEReactions, Reflect] attribute DOMString max;
[CEReactions] attribute long maxLength;
[CEReactions, ReflectNonNegative] attribute long maxLength;
[CEReactions, Reflect] attribute DOMString min;
[CEReactions] attribute long minLength;
[CEReactions, ReflectNonNegative] attribute long minLength;
[CEReactions, Reflect] attribute boolean multiple;
[CEReactions, Reflect] attribute DOMString name;
[CEReactions, Reflect] attribute DOMString pattern;
Expand Down
4 changes: 2 additions & 2 deletions lib/jsdom/living/nodes/HTMLTextAreaElement.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ interface HTMLTextAreaElement : HTMLElement {
[CEReactions, Reflect] attribute boolean disabled;
readonly attribute HTMLFormElement? form;
[CEReactions, Reflect] attribute DOMString inputMode;
[CEReactions, Reflect] attribute long maxLength; // TODO limited to only non-negative numbers
[CEReactions, Reflect] attribute long minLength; // TODO limited to only non-negative numbers
[CEReactions, ReflectNonNegative] attribute long maxLength;
[CEReactions, ReflectNonNegative] attribute long minLength;
[CEReactions, Reflect] attribute DOMString name;
[CEReactions, Reflect] attribute DOMString placeholder;
[CEReactions, Reflect] attribute boolean readOnly;
Expand Down
57 changes: 49 additions & 8 deletions scripts/webidl/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,16 @@ const transformer = new Webidl2js({
},
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes
processReflect(idl, implObj) {
const reflectAttr = idl.extAttrs.find(attr => attr.name === "Reflect");
const attrName = (reflectAttr && reflectAttr.rhs && JSON.parse(reflectAttr.rhs.value)) || idl.name.toLowerCase();
const reflectAttr = idl.extAttrs.find(
attr => attr.name === "Reflect" || attr.name === "ReflectURL" || attr.name === "ReflectNonNegative"
);
const attrName = reflectAttr?.rhs ? JSON.parse(reflectAttr.rhs.value) : idl.name.toLowerCase();

if (idl.extAttrs.find(attr => attr.name === "ReflectURL")) {
// TODO: [ReflectDefault] is only used for `long` right now; also use it for `unsigned long` and `double`.
const reflectDefaultAttr = idl.extAttrs.find(attr => attr.name === "ReflectDefault");
const reflectDefault = reflectDefaultAttr?.rhs ? JSON.parse(reflectDefaultAttr.rhs.value) : undefined;

if (reflectAttr.name === "ReflectURL") {
// Allow DOMString also due to https://github.com/whatwg/html/issues/5241.
if (!isSimpleIDLType(idl.idlType, "USVString") && !isSimpleIDLType(idl.idlType, "DOMString")) {
throw new Error("[ReflectURL] specified on non-USV/DOMString attribute");
Expand All @@ -68,6 +74,13 @@ const transformer = new Webidl2js({
};
}

if (reflectAttr.name === "ReflectNonNegative") {
if (!isSimpleIDLType(idl.idlType, "long")) {
throw new Error("[ReflectNonNegative] specified on non-long attribute");
}
// We'll actually do the processing in the long case, later.
}

if (isSimpleIDLType(idl.idlType, "DOMString") || isSimpleIDLType(idl.idlType, "USVString")) {
if (idl.idlType.nullable) {
// Nonstandard; see https://github.com/whatwg/html/issues/10037. This passes the WPTs though.
Expand Down Expand Up @@ -113,18 +126,46 @@ const transformer = new Webidl2js({
}

if (isSimpleIDLType(idl.idlType, "long")) {
const parseInteger = this.addImport("../helpers/strings", "parseInteger");
const parser = this.addImport(
"../helpers/strings",
reflectAttr.name === "ReflectNonNegative" ? "parseNonNegativeInteger" : "parseInteger"
);

let defaultValue;
if (reflectDefault !== undefined) {
defaultValue = reflectDefault;
} else if (reflectAttr.name === "ReflectNonNegative") {
defaultValue = -1;
} else {
defaultValue = 0;
}

let setterPrefix = "";
if (reflectAttr.name === "ReflectNonNegative") {
const createDOMException = this.addImport("./DOMException", "create");
setterPrefix = `
if (V < 0) {
throw ${createDOMException}(
globalObject,
[\`The negative value \${V} cannot be set for the ${idl.name} property.\`, "IndexSizeError"]
);
}
`;
}

return {
get: `
let value = ${implObj}._reflectGetTheContentAttribute("${attrName}");
if (value === null) {
return 0;
if (value !== null) {
value = ${parser}(value);
if (value !== null && conversions.long(value) === value) {
return value;
}
}
value = ${parseInteger}(value);
return value !== null && conversions.long(value) === value ? value : 0;
return ${defaultValue};
`,
set: `
${setterPrefix}
${implObj}._reflectSetTheContentAttribute("${attrName}", String(V));
`
};
Expand Down
4 changes: 0 additions & 4 deletions test/web-platform-tests/to-run.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1190,8 +1190,6 @@ input-type-button.html: [fail, Depends on offsetWidth]
input-type-change-value.html: [fail, Unknown]
input-type-checkbox-switch.tentative.window.html: [fail, Unknown]
input-untrusted-key-event.html: [fail-slow, Not implemented]
maxlength.html: [fail, Reflection not implemented correctly]
minlength.html: [fail, Reflection not implemented correctly]
pattern_attribute_v_flag.html: [fail, Unknown]
radio-disconnected-group-owner.html: [fail, Unknown]
range-2.html: [fail, step attribute not yet implemented]
Expand Down Expand Up @@ -1242,8 +1240,6 @@ show-picker-cross-origin-iframe.tentative.html: [timeout, Unknown]

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

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

Expand Down

0 comments on commit c1d7005

Please sign in to comment.