Bug 1842027 - Make <input type=number> localization faster. r=masayuki
Using the fast unlocalized parser first exposes a subtle difference between the ICU parser (which uses `double`), and Decimal::fromString (which has a larger range). Check for iee754 finiteness explicitly, matching our previous behavior, and the expectation of this subtest: https://searchfox.org/mozilla-central/rev/a3852ea8db25c759bc8b108aeec870d66c95452c/testing/web-platform/tests/html/semantics/forms/the-input-element/number.html#33 That check matches this Blink code: https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/html/parser/html_parser_idioms.cc;l=103;drc=0d1bbe8de137aaae7c956a33249eb840a0191627 This patch avoids a bunch of number->string->number conversions in the common case where the value comes from the value attribute and thus parses fine without localization shenanigans. Differential Revision: https://phabricator.services.mozilla.com/D183254
This commit is contained in:
@@ -1586,18 +1586,25 @@ Decimal HTMLInputElement::StringToDecimal(const nsAString& aValue) {
|
||||
}
|
||||
NS_LossyConvertUTF16toASCII asciiString(aValue);
|
||||
std::string stdString(asciiString.get(), asciiString.Length());
|
||||
return Decimal::fromString(stdString);
|
||||
auto decimal = Decimal::fromString(stdString);
|
||||
if (!decimal.isFinite()) {
|
||||
return Decimal::nan();
|
||||
}
|
||||
// Numbers are considered finite IEEE 754 Double-precision floating point
|
||||
// values, but decimal supports a bigger range.
|
||||
static const Decimal maxDouble =
|
||||
Decimal::fromDouble(std::numeric_limits<double>::max());
|
||||
if (decimal < -maxDouble || decimal > maxDouble) {
|
||||
return Decimal::nan();
|
||||
}
|
||||
return decimal;
|
||||
}
|
||||
|
||||
Decimal HTMLInputElement::GetValueAsDecimal() const {
|
||||
Decimal decimalValue;
|
||||
nsAutoString stringValue;
|
||||
|
||||
GetNonFileValueInternal(stringValue);
|
||||
|
||||
return !mInputType->ConvertStringToNumber(stringValue, decimalValue)
|
||||
? Decimal::nan()
|
||||
: decimalValue;
|
||||
Decimal result = mInputType->ConvertStringToNumber(stringValue).mResult;
|
||||
return result.isFinite() ? result : Decimal::nan();
|
||||
}
|
||||
|
||||
void HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
|
||||
@@ -1872,8 +1879,8 @@ Decimal HTMLInputElement::GetMinimum() const {
|
||||
nsAutoString minStr;
|
||||
GetAttr(nsGkAtoms::min, minStr);
|
||||
|
||||
Decimal min;
|
||||
return mInputType->ConvertStringToNumber(minStr, min) ? min : defaultMinimum;
|
||||
Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
|
||||
return min.isFinite() ? min : defaultMinimum;
|
||||
}
|
||||
|
||||
Decimal HTMLInputElement::GetMaximum() const {
|
||||
@@ -1892,8 +1899,8 @@ Decimal HTMLInputElement::GetMaximum() const {
|
||||
nsAutoString maxStr;
|
||||
GetAttr(nsGkAtoms::max, maxStr);
|
||||
|
||||
Decimal max;
|
||||
return mInputType->ConvertStringToNumber(maxStr, max) ? max : defaultMaximum;
|
||||
Decimal max = mInputType->ConvertStringToNumber(maxStr).mResult;
|
||||
return max.isFinite() ? max : defaultMaximum;
|
||||
}
|
||||
|
||||
Decimal HTMLInputElement::GetStepBase() const {
|
||||
@@ -1901,22 +1908,23 @@ Decimal HTMLInputElement::GetStepBase() const {
|
||||
mType == FormControlType::InputNumber ||
|
||||
mType == FormControlType::InputRange,
|
||||
"Check that kDefaultStepBase is correct for this new type");
|
||||
|
||||
Decimal stepBase;
|
||||
|
||||
// Do NOT use GetMinimum here - the spec says to use "the min content
|
||||
// attribute", not "the minimum".
|
||||
nsAutoString minStr;
|
||||
if (GetAttr(nsGkAtoms::min, minStr) &&
|
||||
mInputType->ConvertStringToNumber(minStr, stepBase)) {
|
||||
return stepBase;
|
||||
if (GetAttr(nsGkAtoms::min, minStr)) {
|
||||
Decimal min = mInputType->ConvertStringToNumber(minStr).mResult;
|
||||
if (min.isFinite()) {
|
||||
return min;
|
||||
}
|
||||
}
|
||||
|
||||
// If @min is not a double, we should use @value.
|
||||
nsAutoString valueStr;
|
||||
if (GetAttr(nsGkAtoms::value, valueStr) &&
|
||||
mInputType->ConvertStringToNumber(valueStr, stepBase)) {
|
||||
return stepBase;
|
||||
if (GetAttr(nsGkAtoms::value, valueStr)) {
|
||||
Decimal value = mInputType->ConvertStringToNumber(valueStr).mResult;
|
||||
if (value.isFinite()) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == FormControlType::InputWeek) {
|
||||
@@ -4595,44 +4603,42 @@ void HTMLInputElement::SanitizeValue(nsAString& aValue,
|
||||
return;
|
||||
}
|
||||
|
||||
Decimal value;
|
||||
bool ok = mInputType->ConvertStringToNumber(aValue, value);
|
||||
if (!ok) {
|
||||
InputType::StringToNumberResult result =
|
||||
mInputType->ConvertStringToNumber(aValue);
|
||||
if (!result.mResult.isFinite()) {
|
||||
aValue.Truncate();
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString sanitizedValue;
|
||||
if (aForGetter == ForValueGetter::Yes) {
|
||||
// If the default non-localized algorithm parses the value, then we're
|
||||
// done, don't un-localize it, to avoid precision loss, and to preserve
|
||||
// scientific notation as well for example.
|
||||
//
|
||||
// FIXME(emilio, bug 1622808): Localization should ideally be more
|
||||
// input-preserving.
|
||||
if (StringToDecimal(aValue).isFinite()) {
|
||||
if (!result.mLocalized) {
|
||||
return;
|
||||
}
|
||||
// For the <input type=number> value getter, we return the unlocalized
|
||||
// value if it doesn't parse as StringToDecimal, for compat with other
|
||||
// browsers.
|
||||
char buf[32];
|
||||
DebugOnly<bool> ok = value.toString(buf, ArrayLength(buf));
|
||||
sanitizedValue.AssignASCII(buf);
|
||||
DebugOnly<bool> ok = result.mResult.toString(buf, ArrayLength(buf));
|
||||
aValue.AssignASCII(buf);
|
||||
MOZ_ASSERT(ok, "buf not big enough");
|
||||
} else {
|
||||
mInputType->ConvertNumberToString(value, sanitizedValue);
|
||||
// Otherwise we localize as needed, but if both the localized and
|
||||
// unlocalized version parse, we just use the unlocalized one, to
|
||||
// preserve the input as much as possible.
|
||||
// Otherwise this SanitizeValue call is for display. We localize as
|
||||
// needed, but if both the localized and unlocalized version parse with
|
||||
// the generic parser, we just use the unlocalized one, to preserve the
|
||||
// input as much as possible.
|
||||
//
|
||||
// FIXME(emilio, bug 1622808): Localization should ideally be more
|
||||
// input-preserving.
|
||||
if (StringToDecimal(sanitizedValue).isFinite()) {
|
||||
nsAutoString localizedValue;
|
||||
mInputType->ConvertNumberToString(result.mResult, localizedValue);
|
||||
if (StringToDecimal(localizedValue).isFinite()) {
|
||||
return;
|
||||
}
|
||||
aValue.Assign(localizedValue);
|
||||
}
|
||||
aValue.Assign(sanitizedValue);
|
||||
} break;
|
||||
case FormControlType::InputRange: {
|
||||
Decimal minimum = GetMinimum();
|
||||
@@ -4645,9 +4651,8 @@ void HTMLInputElement::SanitizeValue(nsAString& aValue,
|
||||
// parse out from aValue needs to be sanitized.
|
||||
bool needSanitization = false;
|
||||
|
||||
Decimal value;
|
||||
bool ok = mInputType->ConvertStringToNumber(aValue, value);
|
||||
if (!ok) {
|
||||
Decimal value = mInputType->ConvertStringToNumber(aValue).mResult;
|
||||
if (!value.isFinite()) {
|
||||
needSanitization = true;
|
||||
// Set value to midway between minimum and maximum.
|
||||
value = maximum <= minimum ? minimum
|
||||
@@ -6756,13 +6761,16 @@ int32_t HTMLInputElement::GetWrapCols() {
|
||||
int32_t HTMLInputElement::GetRows() { return DEFAULT_ROWS; }
|
||||
|
||||
void HTMLInputElement::GetDefaultValueFromContent(nsAString& aValue) {
|
||||
if (GetEditorState()) {
|
||||
GetDefaultValue(aValue);
|
||||
// This is called by the frame to show the value.
|
||||
// We have to sanitize it when needed.
|
||||
if (mDoneCreating) {
|
||||
SanitizeValue(aValue);
|
||||
}
|
||||
if (!GetEditorState()) {
|
||||
return;
|
||||
}
|
||||
GetDefaultValue(aValue);
|
||||
// This is called by the frame to show the value.
|
||||
// We have to sanitize it when needed.
|
||||
// FIXME: This is also used from GetNonFileValueInternal where we might not
|
||||
// need to sanitize some of the time.
|
||||
if (mDoneCreating) {
|
||||
SanitizeValue(aValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user