Skip to content

Commit

Permalink
Use null as "error" value for parseFloatingPointNumber
Browse files Browse the repository at this point in the history
This is more consistent with the rest of jsdom.

Also make sure there is no Unicode whitespace before the number appears.
  • Loading branch information
TimothyGu authored and domenic committed Feb 1, 2020
1 parent ad43220 commit a4c6144
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 45 deletions.
4 changes: 2 additions & 2 deletions lib/jsdom/living/helpers/form-controls.js
Expand Up @@ -192,7 +192,7 @@ exports.sanitizeValueByType = (input, val) => {
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
// TODO: using parseFloatingPointNumber in addition to isValidFloatingPointNumber to pass number.html WPT.
// Possible spec bug.
if (!isValidFloatingPointNumber(val) || isNaN(parseFloatingPointNumber(val))) {
if (!isValidFloatingPointNumber(val) || parseFloatingPointNumber(val) === null) {
val = "";
}
break;
Expand All @@ -201,7 +201,7 @@ exports.sanitizeValueByType = (input, val) => {
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
// TODO: using parseFloatingPointNumber in addition to isValidFloatingPointNumber to pass number.html WPT.
// Possible spec bug.
if (!isValidFloatingPointNumber(val) || isNaN(parseFloatingPointNumber(val))) {
if (!isValidFloatingPointNumber(val) || parseFloatingPointNumber(val) === null) {
const minimum = input._minimum;
const maximum = input._maximum;
const defaultValue = maximum < minimum ? minimum : (minimum + maximum) / 2;
Expand Down
20 changes: 10 additions & 10 deletions lib/jsdom/living/helpers/number-and-date-inputs.js
Expand Up @@ -37,23 +37,23 @@ exports.convertStringToNumberByType = {
date(input) {
const date = parseDateString(input);
if (date === null) {
return NaN;
return null;
}
return getUTCMs(date.year, date.month, date.day);
},
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-string-number
month(input) {
const date = parseMonthString(input);
if (date === null) {
return NaN;
return null;
}
return (date.year - 1970) * 12 + (date.month - 1);
},
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-value-string-number
week(input) {
const date = parseWeekString(input);
if (date === null) {
return NaN;
return null;
}
const dateObj = new Date(getUTCMs(date.year));
// An HTML week starts on Monday, while 0 represents Sunday. Account for such.
Expand All @@ -64,15 +64,15 @@ exports.convertStringToNumberByType = {
time(input) {
const time = parseTimeString(input);
if (time === null) {
return NaN;
return null;
}
return ((time.hour * 60 + time.minute) * 60 + time.second) * 1000 + time.millisecond;
},
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-value-string-number
"datetime-local"(input) {
const dateAndTime = parseLocalDateAndTimeString(input);
if (dateAndTime === null) {
return NaN;
return null;
}
const { date: { year, month, day }, time: { hour, minute, second, millisecond } } = dateAndTime;
// Doesn't quite matter whether or not UTC is used, since the offset from 1970-01-01 local time is returned.
Expand All @@ -87,13 +87,13 @@ exports.convertStringToNumberByType = {
exports.convertStringToDateByType = {
date(input) {
const parsedInput = exports.convertStringToNumberByType.date(input);
return isNaN(parsedInput) ? NaN : new Date(parsedInput);
return parsedInput === null ? null : new Date(parsedInput);
},
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-string-number
month(input) {
const parsedMonthString = parseMonthString(input);
if (parsedMonthString === null) {
return NaN;
return null;
}

const date = new Date(0);
Expand All @@ -103,15 +103,15 @@ exports.convertStringToDateByType = {
},
week(input) {
const parsedInput = exports.convertStringToNumberByType.week(input);
return isNaN(parsedInput) ? NaN : new Date(parsedInput);
return parsedInput === null ? null : new Date(parsedInput);
},
time(input) {
const parsedInput = exports.convertStringToNumberByType.time(input);
return isNaN(parsedInput) ? NaN : new Date(parsedInput);
return parsedInput === null ? null : new Date(parsedInput);
},
"datetime-local"(input) {
const parsedInput = exports.convertStringToNumberByType["datetime-local"](input);
return isNaN(parsedInput) ? NaN : new Date(parsedInput);
return parsedInput === null ? null : new Date(parsedInput);
}
};

Expand Down
11 changes: 10 additions & 1 deletion lib/jsdom/living/helpers/strings.js
@@ -1,5 +1,6 @@
"use strict";

// https://infra.spec.whatwg.org/#ascii-whitespace
const asciiWhitespaceRe = /^[\t\n\f\r ]$/;
exports.asciiWhitespaceRe = asciiWhitespaceRe;

Expand Down Expand Up @@ -53,9 +54,17 @@ const floatingPointNumRe = /^-?(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?$/;
exports.isValidFloatingPointNumber = str => floatingPointNumRe.test(str);

// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
// Error is represented as null.
exports.parseFloatingPointNumber = str => {
// The implementation here is slightly different from the spec's. We need to use parseFloat() in order to retain
// accuracy, but parseFloat() trims Unicode whitespace in addition to just ASCII ones, so we make sure that the
// trimmed prefix contains only ASCII whitespace ourselves.
const numWhitespace = str.length - str.trimStart().length;
if (/[^\t\n\f\r ]/.test(str.slice(0, numWhitespace))) {
return null;
}
const parsed = parseFloat(str);
return isFinite(parsed) ? parsed : NaN;
return isFinite(parsed) ? parsed : null;
};

// https://infra.spec.whatwg.org/#split-on-ascii-whitespace
Expand Down
16 changes: 8 additions & 8 deletions lib/jsdom/living/nodes/HTMLInputElement-impl.js
Expand Up @@ -477,7 +477,8 @@ class HTMLInputElementImpl extends HTMLElementImpl {
return NaN;
}

return this._parsedValue;
const parsedValue = this._parsedValue;
return typeof parsedValue === "number" ? parsedValue : NaN;
}

set valueAsNumber(v) {
Expand Down Expand Up @@ -803,7 +804,7 @@ class HTMLInputElementImpl extends HTMLElementImpl {
const attr = this._getAttributeIfApplies("min");
if (attr !== null && this._convertStringToNumber !== undefined) {
const parsed = this._convertStringToNumber(attr);
if (!isNaN(parsed)) {
if (parsed !== null) {
min = parsed;
}
}
Expand All @@ -815,7 +816,7 @@ class HTMLInputElementImpl extends HTMLElementImpl {
const attr = this._getAttributeIfApplies("max");
if (attr !== null && this._convertStringToNumber !== undefined) {
const parsed = this._convertStringToNumber(attr);
if (!isNaN(parsed)) {
if (parsed !== null) {
max = parsed;
}
}
Expand Down Expand Up @@ -847,8 +848,7 @@ class HTMLInputElementImpl extends HTMLElementImpl {
if (this._convertStringToNumber === undefined) {
return false;
}
const parsedValue = this._parsedValue;
return typeof parsedValue === "number" && !isNaN(parsedValue);
return typeof this._parsedValue === "number";
}

// https://html.spec.whatwg.org/multipage/input.html#concept-input-step
Expand All @@ -861,7 +861,7 @@ class HTMLInputElementImpl extends HTMLElementImpl {
return null;
}
const parsedStep = parseFloatingPointNumber(attr);
if (isNaN(parsedStep) || parsedStep <= 0) {
if (parsedStep === null || parsedStep <= 0) {
return this._defaultStep * this._stepScaleFactor;
}
return parsedStep * this._stepScaleFactor;
Expand Down Expand Up @@ -895,13 +895,13 @@ class HTMLInputElementImpl extends HTMLElementImpl {
get _stepBase() {
if (this._hasAttributeAndApplies("min")) {
const min = this._convertStringToNumber(this.getAttributeNS(null, "min"));
if (!isNaN(min)) {
if (min !== null) {
return min;
}
}
if (this.hasAttributeNS(null, "value")) {
const value = this._convertStringToNumber(this.getAttributeNS(null, "value"));
if (!isNaN(value)) {
if (value !== null) {
return value;
}
}
Expand Down
23 changes: 11 additions & 12 deletions lib/jsdom/living/nodes/HTMLMeterElement-impl.js
Expand Up @@ -13,14 +13,13 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-minimum
get _minimumValue() {
const min = this.getAttributeNS(null, "min");
if (min === null) {
return 0;
}
const parsed = parseFloatingPointNumber(min);
if (Number.isNaN(parsed)) {
return 0;
if (min !== null) {
const parsed = parseFloatingPointNumber(min);
if (parsed !== null) {
return parsed;
}
}
return parsed;
return 0;
}

// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-maximum
Expand All @@ -30,7 +29,7 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
const max = this.getAttributeNS(null, "max");
if (max !== null) {
const parsed = parseFloatingPointNumber(max);
if (!Number.isNaN(parsed)) {
if (parsed !== null) {
candidate = parsed;
}
}
Expand All @@ -46,7 +45,7 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
const value = this.getAttributeNS(null, "value");
if (value !== null) {
const parsed = parseFloatingPointNumber(value);
if (!Number.isNaN(parsed)) {
if (parsed !== null) {
candidate = parsed;
}
}
Expand All @@ -68,7 +67,7 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
const low = this.getAttributeNS(null, "low");
if (low !== null) {
const parsed = parseFloatingPointNumber(low);
if (!Number.isNaN(parsed)) {
if (parsed !== null) {
candidate = parsed;
}
}
Expand All @@ -89,7 +88,7 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
const high = this.getAttributeNS(null, "high");
if (high !== null) {
const parsed = parseFloatingPointNumber(high);
if (!Number.isNaN(parsed)) {
if (parsed !== null) {
candidate = parsed;
}
}
Expand All @@ -111,7 +110,7 @@ class HTMLMeterElementImpl extends HTMLElementImpl {
const optimum = this.getAttributeNS(null, "optimum");
if (optimum !== null) {
const parsed = parseFloatingPointNumber(optimum);
if (!Number.isNaN(parsed)) {
if (parsed !== null) {
candidate = parsed;
}
}
Expand Down
44 changes: 32 additions & 12 deletions lib/jsdom/living/nodes/HTMLProgressElement-impl.js
Expand Up @@ -10,38 +10,58 @@ class HTMLProgressElementImpl extends HTMLElementImpl {
this._labels = null;
}

get value() {
const parsedValue = parseFloatingPointNumber(this.getAttributeNS(null, "value"));
get _isDeterminate() {
return this.hasAttributeNS(null, "value");
}

if (!isNaN(parsedValue) && parsedValue > 0) {
return parsedValue > this.max ? this.max : parsedValue;
// https://html.spec.whatwg.org/multipage/form-elements.html#concept-progress-value
get _value() {
const valueAttr = this.getAttributeNS(null, "value");
const parsedValue = parseFloatingPointNumber(valueAttr);
if (parsedValue !== null && parsedValue > 0) {
return parsedValue;
}
return 0;
}

// https://html.spec.whatwg.org/multipage/form-elements.html#concept-progress-current-value
get _currentValue() {
const value = this._value;
return value > this.max ? this.max : value;
}

get value() {
if (this._isDeterminate) {
return this._currentValue;
}
return 0;
}
set value(value) {
this.setAttributeNS(null, "value", value);
}

get max() {
const parsedMax = parseFloatingPointNumber(this.getAttributeNS(null, "max"));

if (!isNaN(parsedMax) && parsedMax > 0) {
return parsedMax;
const max = this.getAttributeNS(null, "max");
if (max !== null) {
const parsedMax = parseFloatingPointNumber(max);
if (parsedMax !== null && parsedMax > 0) {
return parsedMax;
}
}

return 1.0;
}
set max(value) {
this.setAttributeNS(null, "max", value);
if (value > 0) {
this.setAttributeNS(null, "max", value);
}
}

get position() {
if (!this.hasAttributeNS(null, "value")) {
if (!this._isDeterminate) {
return -1;
}

return this.value / this.max;
return this._currentValue / this.max;
}

get labels() {
Expand Down

0 comments on commit a4c6144

Please sign in to comment.