Bug 1363258 - Part 1: Factor ConvertStringToNumber out of HTMLInputElement. r=smaug

MozReview-Commit-ID: 8DratVTlToP

--HG--
extra : rebase_source : 86427e784b91ad724976b1881846aa4a395a6c7e
This commit is contained in:
Jessica Jong 2017-05-10 17:03:33 +08:00
parent 8bde0a472a
commit 83f65520f8
8 changed files with 313 additions and 121 deletions

View File

@ -1816,108 +1816,6 @@ HTMLInputElement::StringToDecimal(const nsAString& aValue)
return Decimal::fromString(stdString);
}
bool
HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
Decimal& aResultValue) const
{
MOZ_ASSERT(DoesValueAsNumberApply(),
"ConvertStringToNumber only applies if .valueAsNumber applies");
switch (mType) {
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_RANGE:
{
aResultValue = StringToDecimal(aValue);
if (!aResultValue.isFinite()) {
return false;
}
return true;
}
case NS_FORM_INPUT_DATE:
{
uint32_t year, month, day;
if (!ParseDate(aValue, &year, &month, &day)) {
return false;
}
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
if (!time.isValid()) {
return false;
}
aResultValue = Decimal::fromDouble(time.toDouble());
return true;
}
case NS_FORM_INPUT_TIME:
uint32_t milliseconds;
if (!ParseTime(aValue, &milliseconds)) {
return false;
}
aResultValue = Decimal(int32_t(milliseconds));
return true;
case NS_FORM_INPUT_MONTH:
{
uint32_t year, month;
if (!ParseMonth(aValue, &year, &month)) {
return false;
}
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
// Maximum valid month is 275760-09.
if (year == kMaximumYear && month > kMaximumMonthInMaximumYear) {
return false;
}
int32_t months = MonthsSinceJan1970(year, month);
aResultValue = Decimal(int32_t(months));
return true;
}
case NS_FORM_INPUT_WEEK:
{
uint32_t year, week;
if (!ParseWeek(aValue, &year, &week)) {
return false;
}
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
// Maximum week is 275760-W37, the week of 275760-09-13.
if (year == kMaximumYear && week > kMaximumWeekInMaximumYear) {
return false;
}
double days = DaysSinceEpochFromWeek(year, week);
aResultValue = Decimal::fromDouble(days * kMsPerDay);
return true;
}
case NS_FORM_INPUT_DATETIME_LOCAL:
{
uint32_t year, month, day, timeInMs;
if (!ParseDateTimeLocal(aValue, &year, &month, &day, &timeInMs)) {
return false;
}
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day,
timeInMs));
if (!time.isValid()) {
return false;
}
aResultValue = Decimal::fromDouble(time.toDouble());
return true;
}
default:
MOZ_ASSERT(false, "Unrecognized input type");
return false;
}
}
Decimal
HTMLInputElement::GetValueAsDecimal() const
{
@ -1926,8 +1824,8 @@ HTMLInputElement::GetValueAsDecimal() const
GetNonFileValueInternal(stringValue);
return !ConvertStringToNumber(stringValue, decimalValue) ? Decimal::nan()
: decimalValue;
return !mInputType->ConvertStringToNumber(stringValue, decimalValue) ?
Decimal::nan() : decimalValue;
}
void
@ -2370,7 +2268,7 @@ HTMLInputElement::GetMinimum() const
GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
Decimal min;
return ConvertStringToNumber(minStr, min) ? min : defaultMinimum;
return mInputType->ConvertStringToNumber(minStr, min) ? min : defaultMinimum;
}
Decimal
@ -2391,7 +2289,7 @@ HTMLInputElement::GetMaximum() const
GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr);
Decimal max;
return ConvertStringToNumber(maxStr, max) ? max : defaultMaximum;
return mInputType->ConvertStringToNumber(maxStr, max) ? max : defaultMaximum;
}
Decimal
@ -2408,14 +2306,14 @@ HTMLInputElement::GetStepBase() const
// attribute", not "the minimum".
nsAutoString minStr;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr) &&
ConvertStringToNumber(minStr, stepBase)) {
mInputType->ConvertStringToNumber(minStr, stepBase)) {
return stepBase;
}
// If @min is not a double, we should use @value.
nsAutoString valueStr;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::value, valueStr) &&
ConvertStringToNumber(valueStr, stepBase)) {
mInputType->ConvertStringToNumber(valueStr, stepBase)) {
return stepBase;
}
@ -5344,7 +5242,7 @@ HTMLInputElement::SanitizeValue(nsAString& aValue)
case NS_FORM_INPUT_NUMBER:
{
Decimal value;
bool ok = ConvertStringToNumber(aValue, value);
bool ok = mInputType->ConvertStringToNumber(aValue, value);
if (!ok) {
aValue.Truncate();
}
@ -5363,7 +5261,7 @@ HTMLInputElement::SanitizeValue(nsAString& aValue)
bool needSanitization = false;
Decimal value;
bool ok = ConvertStringToNumber(aValue, value);
bool ok = mInputType->ConvertStringToNumber(aValue, value);
if (!ok) {
needSanitization = true;
// Set value to midway between minimum and maximum.

View File

@ -1166,17 +1166,6 @@ protected:
*/
nsIRadioGroupContainer* GetRadioGroupContainer() const;
/**
* Convert a string to a Decimal number in a type specific way,
* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#concept-input-value-string-number
* ie parse a date string to a timestamp if type=date,
* or parse a number string to its value if type=number.
* @param aValue the string to be parsed.
* @param aResultValue the number as a Decimal.
* @result whether the parsing was successful.
*/
bool ConvertStringToNumber(nsAString& aValue, Decimal& aResultValue) const;
/**
* Convert a Decimal to a string in a type specific way, ie convert a timestamp
* to a date string if type=date or append the number string representing the

View File

@ -6,8 +6,15 @@
#include "DateTimeInputTypes.h"
#include "js/Date.h"
#include "mozilla/dom/HTMLInputElement.h"
const double DateTimeInputTypeBase::kMinimumYear = 1;
const double DateTimeInputTypeBase::kMaximumYear = 275760;
const double DateTimeInputTypeBase::kMaximumMonthInMaximumYear = 9;
const double DateTimeInputTypeBase::kMaximumWeekInMaximumYear = 37;
const double DateTimeInputTypeBase::kMsPerDay = 24 * 60 * 60 * 1000;
bool
DateTimeInputTypeBase::IsMutable() const
{
@ -82,3 +89,109 @@ DateTimeInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const
// Value has to be an integral multiple of step.
return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0);
}
// input type=date
bool
DateInputType::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
uint32_t year, month, day;
if (!ParseDate(aValue, &year, &month, &day)) {
return false;
}
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
if (!time.isValid()) {
return false;
}
aResultValue = mozilla::Decimal::fromDouble(time.toDouble());
return true;
}
// input type=time
bool
TimeInputType::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
uint32_t milliseconds;
if (!ParseTime(aValue, &milliseconds)) {
return false;
}
aResultValue = mozilla::Decimal(int32_t(milliseconds));
return true;
}
// input type=week
bool
WeekInputType::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
uint32_t year, week;
if (!ParseWeek(aValue, &year, &week)) {
return false;
}
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
// Maximum week is 275760-W37, the week of 275760-09-13.
if (year == kMaximumYear && week > kMaximumWeekInMaximumYear) {
return false;
}
double days = DaysSinceEpochFromWeek(year, week);
aResultValue = mozilla::Decimal::fromDouble(days * kMsPerDay);
return true;
}
// input type=month
bool
MonthInputType::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
uint32_t year, month;
if (!ParseMonth(aValue, &year, &month)) {
return false;
}
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
// Maximum valid month is 275760-09.
if (year == kMaximumYear && month > kMaximumMonthInMaximumYear) {
return false;
}
int32_t months = MonthsSinceJan1970(year, month);
aResultValue = mozilla::Decimal(int32_t(months));
return true;
}
// input type=datetime-local
bool
DateTimeLocalInputType::ConvertStringToNumber(
nsAString& aValue, mozilla::Decimal& aResultValue) const
{
uint32_t year, month, day, timeInMs;
if (!ParseDateTimeLocal(aValue, &year, &month, &day, &timeInMs)) {
return false;
}
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day,
timeInMs));
if (!time.isValid()) {
return false;
}
aResultValue = mozilla::Decimal::fromDouble(time.toDouble());
return true;
}

View File

@ -25,6 +25,17 @@ protected:
{}
bool IsMutable() const override;
// Minimum year limited by HTML standard, year >= 1.
static const double kMinimumYear;
// Maximum year limited by ECMAScript date object range, year <= 275760.
static const double kMaximumYear;
// Maximum valid month is 275760-09.
static const double kMaximumMonthInMaximumYear;
// Maximum valid week is 275760-W37.
static const double kMaximumWeekInMaximumYear;
// Milliseconds in a day.
static const double kMsPerDay;
};
// input type=date
@ -37,6 +48,9 @@ public:
return new (aMemory) DateInputType(aInputElement);
}
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
private:
explicit DateInputType(mozilla::dom::HTMLInputElement* aInputElement)
: DateTimeInputTypeBase(aInputElement)
@ -53,6 +67,9 @@ public:
return new (aMemory) TimeInputType(aInputElement);
}
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
private:
explicit TimeInputType(mozilla::dom::HTMLInputElement* aInputElement)
: DateTimeInputTypeBase(aInputElement)
@ -69,6 +86,9 @@ public:
return new (aMemory) WeekInputType(aInputElement);
}
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
private:
explicit WeekInputType(mozilla::dom::HTMLInputElement* aInputElement)
: DateTimeInputTypeBase(aInputElement)
@ -85,6 +105,9 @@ public:
return new (aMemory) MonthInputType(aInputElement);
}
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
private:
explicit MonthInputType(mozilla::dom::HTMLInputElement* aInputElement)
: DateTimeInputTypeBase(aInputElement)
@ -101,6 +124,9 @@ public:
return new (aMemory) DateTimeLocalInputType(aInputElement);
}
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
private:
explicit DateTimeLocalInputType(mozilla::dom::HTMLInputElement* aInputElement)
: DateTimeInputTypeBase(aInputElement)

View File

@ -205,3 +205,69 @@ InputType::MinMaxStepAttrChanged()
{
return NS_OK;
}
bool
InputType::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
NS_WARNING("InputType::ConvertStringToNumber called");
return false;
}
bool
InputType::ParseDate(const nsAString& aValue, uint32_t* aYear, uint32_t* aMonth,
uint32_t* aDay) const
{
// TODO: move this function and implementation to DateTimeInpuTypeBase when
// refactoring is completed. Now we can only call HTMLInputElement::ParseDate
// from here, since the method is protected and only InputType is a friend
// class.
return mInputElement->ParseDate(aValue, aYear, aMonth, aDay);
}
bool
InputType::ParseTime(const nsAString& aValue, uint32_t* aResult) const
{
// see comment in InputType::ParseDate().
return mInputElement->ParseTime(aValue, aResult);
}
bool
InputType::ParseMonth(const nsAString& aValue, uint32_t* aYear,
uint32_t* aMonth) const
{
// see comment in InputType::ParseDate().
return mInputElement->ParseMonth(aValue, aYear, aMonth);
}
bool
InputType::ParseWeek(const nsAString& aValue, uint32_t* aYear,
uint32_t* aWeek) const
{
// see comment in InputType::ParseDate().
return mInputElement->ParseWeek(aValue, aYear, aWeek);
}
bool
InputType::ParseDateTimeLocal(const nsAString& aValue, uint32_t* aYear,
uint32_t* aMonth, uint32_t* aDay, uint32_t* aTime)
const
{
// see comment in InputType::ParseDate().
return mInputElement->ParseDateTimeLocal(aValue, aYear, aMonth, aDay, aTime);
}
int32_t
InputType::MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const
{
// see comment in InputType::ParseDate().
return mInputElement->MonthsSinceJan1970(aYear, aMonth);
}
double
InputType::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const
{
// see comment in InputType::ParseDate().
return mInputElement->DaysSinceEpochFromWeek(aYear, aWeek);
}

View File

@ -62,6 +62,18 @@ public:
virtual nsresult MinMaxStepAttrChanged();
/**
* Convert a string to a Decimal number in a type specific way,
* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#concept-input-value-string-number
* ie parse a date string to a timestamp if type=date,
* or parse a number string to its value if type=number.
* @param aValue the string to be parsed.
* @param aResultValue the number as a Decimal.
* @result whether the parsing was successful.
*/
virtual bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const;
protected:
explicit InputType(mozilla::dom::HTMLInputElement* aInputElement)
: mInputElement(aInputElement)
@ -110,6 +122,80 @@ protected:
*/
nsIFrame* GetPrimaryFrame() const;
/**
* Parse a date string of the form yyyy-mm-dd
*
* @param aValue the string to be parsed.
* @return the date in aYear, aMonth, aDay.
* @return whether the parsing was successful.
*/
bool ParseDate(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aMonth,
uint32_t* aDay) const;
/**
* Returns the time expressed in milliseconds of |aValue| being parsed as a
* time following the HTML specifications:
* https://html.spec.whatwg.org/multipage/infrastructure.html#parse-a-time-string
*
* Note: |aResult| can be null.
*
* @param aValue the string to be parsed.
* @param aResult the time expressed in milliseconds representing the time [out]
* @return whether the parsing was successful.
*/
bool ParseTime(const nsAString& aValue, uint32_t* aResult) const;
/**
* Parse a month string of the form yyyy-mm
*
* @param the string to be parsed.
* @return the year and month in aYear and aMonth.
* @return whether the parsing was successful.
*/
bool ParseMonth(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aMonth) const;
/**
* Parse a week string of the form yyyy-Www
*
* @param the string to be parsed.
* @return the year and week in aYear and aWeek.
* @return whether the parsing was successful.
*/
bool ParseWeek(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aWeek) const;
/**
* Parse a datetime-local string of the form yyyy-mm-ddThh:mm[:ss.s] or
* yyyy-mm-dd hh:mm[:ss.s], where fractions of seconds can be 1 to 3 digits.
*
* @param the string to be parsed.
* @return the date in aYear, aMonth, aDay and time expressed in milliseconds
* in aTime.
* @return whether the parsing was successful.
*/
bool ParseDateTimeLocal(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aMonth,
uint32_t* aDay,
uint32_t* aTime) const;
/**
* This methods returns the number of months between January 1970 and the
* given year and month.
*/
int32_t MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const;
/**
* This methods returns the number of days since epoch for a given year and
* week.
*/
double DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const;
mozilla::dom::HTMLInputElement* mInputElement;
};

View File

@ -71,6 +71,17 @@ NumericInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const
return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0);
}
bool
NumericInputTypeBase::ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const
{
aResultValue = mozilla::dom::HTMLInputElement::StringToDecimal(aValue);
if (!aResultValue.isFinite()) {
return false;
}
return true;
}
/* input type=numer */
bool

View File

@ -18,6 +18,9 @@ public:
bool IsRangeUnderflow() const override;
bool HasStepMismatch(bool aUseZeroIfValueNaN) const override;
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
protected:
explicit NumericInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement)
: InputType(aInputElement)