Bug 1363258 - Part 2: Factor ConvertNumberToString out of HTMLInputElement. r=smaug

MozReview-Commit-ID: 8ZHrdvpjr0m

--HG--
extra : rebase_source : c52d219ada73a663740212c3229ecf90e5c5c2f6
This commit is contained in:
Jessica Jong 2017-05-10 17:03:46 +08:00
Родитель 83f65520f8
Коммит 937e398a92
8 изменённых файлов: 285 добавлений и 214 удалений

Просмотреть файл

@ -1943,175 +1943,11 @@ HTMLInputElement::SetValue(Decimal aValue, CallerType aCallerType)
}
nsAutoString value;
ConvertNumberToString(aValue, value);
mInputType->ConvertNumberToString(aValue, value);
IgnoredErrorResult rv;
SetValue(value, aCallerType, rv);
}
bool
HTMLInputElement::ConvertNumberToString(Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(DoesValueAsNumberApply(),
"ConvertNumberToString is only implemented for types implementing .valueAsNumber");
MOZ_ASSERT(aValue.isFinite(),
"aValue must be a valid non-Infinite number.");
aResultString.Truncate();
switch (mType) {
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_RANGE:
{
char buf[32];
bool ok = aValue.toString(buf, ArrayLength(buf));
aResultString.AssignASCII(buf);
MOZ_ASSERT(ok, "buf not big enough");
return ok;
}
case NS_FORM_INPUT_DATE:
{
// The specs (and our JS APIs) require |aValue| to be truncated.
aValue = aValue.floor();
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
if (IsNaN(year) || IsNaN(month) || IsNaN(day)) {
return false;
}
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0f", year,
month + 1, day);
return true;
}
case NS_FORM_INPUT_TIME:
{
aValue = aValue.floor();
// Per spec, we need to truncate |aValue| and we should only represent
// times inside a day [00:00, 24:00[, which means that we should do a
// modulo on |aValue| using the number of milliseconds in a day (86400000).
uint32_t value =
NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble();
uint16_t milliseconds, seconds, minutes, hours;
if (!GetTimeFromMs(value, &hours, &minutes, &seconds, &milliseconds)) {
return false;
}
if (milliseconds != 0) {
aResultString.AppendPrintf("%02d:%02d:%02d.%03d",
hours, minutes, seconds, milliseconds);
} else if (seconds != 0) {
aResultString.AppendPrintf("%02d:%02d:%02d",
hours, minutes, seconds);
} else {
aResultString.AppendPrintf("%02d:%02d", hours, minutes);
}
return true;
}
case NS_FORM_INPUT_MONTH:
{
aValue = aValue.floor();
double month = NS_floorModulo(aValue, Decimal(12)).toDouble();
month = (month < 0 ? month + 12 : month);
double year = 1970 + (aValue.toDouble() - month) / 12;
// Maximum valid month is 275760-09.
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
if (year == kMaximumYear && month > 8) {
return false;
}
aResultString.AppendPrintf("%04.0f-%02.0f", year, month + 1);
return true;
}
case NS_FORM_INPUT_WEEK:
{
aValue = aValue.floor();
// Based on ISO 8601 date.
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
// Adding 1 since day starts from 0.
double dayInYear = JS::DayWithinYear(aValue.toDouble(), year) + 1;
// Adding 1 since month starts from 0.
uint32_t isoWeekday = DayOfWeek(year, month + 1, day, true);
// Target on Wednesday since ISO 8601 states that week 1 is the week
// with the first Thursday of that year.
uint32_t week = (dayInYear - isoWeekday + 10) / 7;
if (week < 1) {
year--;
if (year < 1) {
return false;
}
week = MaximumWeekInYear(year);
} else if (week > MaximumWeekInYear(year)) {
year++;
if (year > kMaximumYear ||
(year == kMaximumYear && week > kMaximumWeekInMaximumYear)) {
return false;
}
week = 1;
}
aResultString.AppendPrintf("%04.0f-W%02d", year, week);
return true;
}
case NS_FORM_INPUT_DATETIME_LOCAL:
{
aValue = aValue.floor();
uint32_t timeValue =
NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble();
uint16_t milliseconds, seconds, minutes, hours;
if (!GetTimeFromMs(timeValue,
&hours, &minutes, &seconds, &milliseconds)) {
return false;
}
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
if (IsNaN(year) || IsNaN(month) || IsNaN(day)) {
return false;
}
if (milliseconds != 0) {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d.%03d",
year, month + 1, day, hours, minutes,
seconds, milliseconds);
} else if (seconds != 0) {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d",
year, month + 1, day, hours, minutes,
seconds);
} else {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d",
year, month + 1, day, hours, minutes);
}
return true;
}
default:
MOZ_ASSERT(false, "Unrecognized input type");
return false;
}
}
Nullable<Date>
HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
{
@ -4146,7 +3982,7 @@ HTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent)
// DispatchTrustedEvent.
// TODO: decide what we should do here - bug 851782.
nsAutoString val;
ConvertNumberToString(mRangeThumbDragStartValue, val);
mInputType->ConvertNumberToString(mRangeThumbDragStartValue, val);
// TODO: What should we do if SetValueInternal fails? (The allocation
// is small, so we should be fine here.)
SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
@ -4169,7 +4005,7 @@ HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
Decimal oldValue = GetValueAsDecimal();
nsAutoString val;
ConvertNumberToString(aValue, val);
mInputType->ConvertNumberToString(aValue, val);
// TODO: What should we do if SetValueInternal fails? (The allocation
// is small, so we should be fine here.)
SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
@ -4272,7 +4108,7 @@ HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
}
nsAutoString newVal;
ConvertNumberToString(newValue, newVal);
mInputType->ConvertNumberToString(newValue, newVal);
// TODO: What should we do if SetValueInternal fails? (The allocation
// is small, so we should be fine here.)
SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput |
@ -5419,29 +5255,6 @@ HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const
kMaximumWeekInYear : kMaximumWeekInYear - 1;
}
bool
HTMLInputElement::GetTimeFromMs(double aValue, uint16_t* aHours,
uint16_t* aMinutes, uint16_t* aSeconds,
uint16_t* aMilliseconds) const {
MOZ_ASSERT(aValue >= 0 && aValue < kMsPerDay,
"aValue must be milliseconds within a day!");
uint32_t value = floor(aValue);
*aMilliseconds = value % 1000;
value /= 1000;
*aSeconds = value % 60;
value /= 60;
*aMinutes = value % 60;
value /= 60;
*aHours = value;
return true;
}
bool
HTMLInputElement::IsValidWeek(const nsAString& aValue) const
{
@ -7771,8 +7584,8 @@ HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
if (maximum.isNaN() || valueHigh <= maximum) {
nsAutoString valueLowStr, valueHighStr;
ConvertNumberToString(valueLow, valueLowStr);
ConvertNumberToString(valueHigh, valueHighStr);
mInputType->ConvertNumberToString(valueLow, valueLowStr);
mInputType->ConvertNumberToString(valueHigh, valueHighStr);
if (valueLowStr.Equals(valueHighStr)) {
const char16_t* params[] = { valueLowStr.get() };
@ -7787,7 +7600,7 @@ HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
}
} else {
nsAutoString valueLowStr;
ConvertNumberToString(valueLow, valueLowStr);
mInputType->ConvertNumberToString(valueLow, valueLowStr);
const char16_t* params[] = { valueLowStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,

Просмотреть файл

@ -1166,19 +1166,6 @@ protected:
*/
nsIRadioGroupContainer* GetRadioGroupContainer() 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
* value if type=number.
*
* @param aValue the Decimal to be converted
* @param aResultString [out] the string representing the Decimal
* @return whether the function succeded, it will fail if the current input's
* type is not supported or the number can't be converted to a string
* as expected by the type.
*/
bool ConvertNumberToString(Decimal aValue, nsAString& aResultString) const;
/**
* Parse a color string of the form #XXXXXX where X should be hexa characters
* @param the string to be parsed.
@ -1315,13 +1302,6 @@ protected:
*/
uint32_t MaximumWeekInYear(uint32_t aYear) const;
/**
* This method converts aValue (milliseconds within a day) to hours, minutes,
* seconds and milliseconds.
*/
bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes,
uint16_t* aSeconds, uint16_t* aMilliseconds) const;
/**
* This methods returns true if it's a leap year.
*/

Просмотреть файл

@ -90,6 +90,29 @@ DateTimeInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const
return NS_floorModulo(value - GetStepBase(), step) != mozilla::Decimal(0);
}
bool
DateTimeInputTypeBase::GetTimeFromMs(double aValue, uint16_t* aHours,
uint16_t* aMinutes, uint16_t* aSeconds,
uint16_t* aMilliseconds) const {
MOZ_ASSERT(aValue >= 0 && aValue < kMsPerDay,
"aValue must be milliseconds within a day!");
uint32_t value = floor(aValue);
*aMilliseconds = value % 1000;
value /= 1000;
*aSeconds = value % 60;
value /= 60;
*aMinutes = value % 60;
value /= 60;
*aHours = value;
return true;
}
// input type=date
bool
@ -110,6 +133,29 @@ DateInputType::ConvertStringToNumber(nsAString& aValue,
return true;
}
bool
DateInputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
// The specs (and our JS APIs) require |aValue| to be truncated.
aValue = aValue.floor();
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
if (mozilla::IsNaN(year) || mozilla::IsNaN(month) || mozilla::IsNaN(day)) {
return false;
}
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0f", year, month + 1, day);
return true;
}
// input type=time
bool
@ -125,6 +171,39 @@ TimeInputType::ConvertStringToNumber(nsAString& aValue,
return true;
}
bool
TimeInputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
aValue = aValue.floor();
// Per spec, we need to truncate |aValue| and we should only represent
// times inside a day [00:00, 24:00[, which means that we should do a
// modulo on |aValue| using the number of milliseconds in a day (86400000).
uint32_t value =
NS_floorModulo(aValue, mozilla::Decimal::fromDouble(kMsPerDay)).toDouble();
uint16_t milliseconds, seconds, minutes, hours;
if (!GetTimeFromMs(value, &hours, &minutes, &seconds, &milliseconds)) {
return false;
}
if (milliseconds != 0) {
aResultString.AppendPrintf("%02d:%02d:%02d.%03d",
hours, minutes, seconds, milliseconds);
} else if (seconds != 0) {
aResultString.AppendPrintf("%02d:%02d:%02d",
hours, minutes, seconds);
} else {
aResultString.AppendPrintf("%02d:%02d", hours, minutes);
}
return true;
}
// input type=week
bool
@ -150,6 +229,48 @@ WeekInputType::ConvertStringToNumber(nsAString& aValue,
return true;
}
bool
WeekInputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
aValue = aValue.floor();
// Based on ISO 8601 date.
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
// Adding 1 since day starts from 0.
double dayInYear = JS::DayWithinYear(aValue.toDouble(), year) + 1;
// Adding 1 since month starts from 0.
uint32_t isoWeekday = DayOfWeek(year, month + 1, day, true);
// Target on Wednesday since ISO 8601 states that week 1 is the week
// with the first Thursday of that year.
uint32_t week = (dayInYear - isoWeekday + 10) / 7;
if (week < 1) {
year--;
if (year < 1) {
return false;
}
week = MaximumWeekInYear(year);
} else if (week > MaximumWeekInYear(year)) {
year++;
if (year > kMaximumYear ||
(year == kMaximumYear && week > kMaximumWeekInMaximumYear)) {
return false;
}
week = 1;
}
aResultString.AppendPrintf("%04.0f-W%02d", year, week);
return true;
}
// input type=month
bool
@ -175,6 +296,35 @@ MonthInputType::ConvertStringToNumber(nsAString& aValue,
return true;
}
bool
MonthInputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
aValue = aValue.floor();
double month = NS_floorModulo(aValue, mozilla::Decimal(12)).toDouble();
month = (month < 0 ? month + 12 : month);
double year = 1970 + (aValue.toDouble() - month) / 12;
// Maximum valid month is 275760-09.
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
if (year == kMaximumYear && month > 8) {
return false;
}
aResultString.AppendPrintf("%04.0f-%02.0f", year, month + 1);
return true;
}
// input type=datetime-local
bool
@ -195,3 +345,45 @@ DateTimeLocalInputType::ConvertStringToNumber(
aResultValue = mozilla::Decimal::fromDouble(time.toDouble());
return true;
}
bool
DateTimeLocalInputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
aValue = aValue.floor();
uint32_t timeValue =
NS_floorModulo(aValue, mozilla::Decimal::fromDouble(kMsPerDay)).toDouble();
uint16_t milliseconds, seconds, minutes, hours;
if (!GetTimeFromMs(timeValue, &hours, &minutes, &seconds, &milliseconds)) {
return false;
}
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
if (mozilla::IsNaN(year) || mozilla::IsNaN(month) || mozilla::IsNaN(day)) {
return false;
}
if (milliseconds != 0) {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d.%03d",
year, month + 1, day, hours, minutes,
seconds, milliseconds);
} else if (seconds != 0) {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d",
year, month + 1, day, hours, minutes,
seconds);
} else {
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d",
year, month + 1, day, hours, minutes);
}
return true;
}

Просмотреть файл

@ -26,6 +26,13 @@ protected:
bool IsMutable() const override;
/**
* This method converts aValue (milliseconds within a day) to hours, minutes,
* seconds and milliseconds.
*/
bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes,
uint16_t* aSeconds, uint16_t* aMilliseconds) const;
// Minimum year limited by HTML standard, year >= 1.
static const double kMinimumYear;
// Maximum year limited by ECMAScript date object range, year <= 275760.
@ -50,6 +57,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
private:
explicit DateInputType(mozilla::dom::HTMLInputElement* aInputElement)
@ -69,6 +78,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
private:
explicit TimeInputType(mozilla::dom::HTMLInputElement* aInputElement)
@ -88,6 +99,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
private:
explicit WeekInputType(mozilla::dom::HTMLInputElement* aInputElement)
@ -107,6 +120,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
private:
explicit MonthInputType(mozilla::dom::HTMLInputElement* aInputElement)
@ -126,6 +141,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
private:
explicit DateTimeLocalInputType(mozilla::dom::HTMLInputElement* aInputElement)

Просмотреть файл

@ -215,6 +215,15 @@ InputType::ConvertStringToNumber(nsAString& aValue,
return false;
}
bool
InputType::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
NS_WARNING("InputType::ConvertNumberToString called");
return false;
}
bool
InputType::ParseDate(const nsAString& aValue, uint32_t* aYear, uint32_t* aMonth,
uint32_t* aDay) const
@ -271,3 +280,18 @@ InputType::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const
// see comment in InputType::ParseDate().
return mInputElement->DaysSinceEpochFromWeek(aYear, aWeek);
}
uint32_t
InputType::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
bool isoWeek) const
{
// see comment in InputType::ParseDate().
return mInputElement->DayOfWeek(aYear, aMonth, aDay, isoWeek);
}
uint32_t
InputType::MaximumWeekInYear(uint32_t aYear) const
{
// see comment in InputType::ParseDate().
return mInputElement->MaximumWeekInYear(aYear);
}

Просмотреть файл

@ -74,6 +74,20 @@ public:
virtual bool ConvertStringToNumber(nsAString& aValue,
mozilla::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
* value if type=number.
*
* @param aValue the Decimal to be converted
* @param aResultString [out] the string representing the Decimal
* @return whether the function succeeded, it will fail if the current input's
* type is not supported or the number can't be converted to a string
* as expected by the type.
*/
virtual bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const;
protected:
explicit InputType(mozilla::dom::HTMLInputElement* aInputElement)
: mInputElement(aInputElement)
@ -196,6 +210,19 @@ protected:
*/
double DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const;
/**
* This methods returns the day of the week given a date. If @isoWeek is true,
* 7=Sunday, otherwise, 0=Sunday.
*/
uint32_t DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
bool isoWeek) const;
/**
* This methods returns the maximum number of week in a given year, the
* result is either 52 or 53.
*/
uint32_t MaximumWeekInYear(uint32_t aYear) const;
mozilla::dom::HTMLInputElement* mInputElement;
};

Просмотреть файл

@ -82,6 +82,22 @@ NumericInputTypeBase::ConvertStringToNumber(nsAString& aValue,
return true;
}
bool
NumericInputTypeBase::ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
aResultString.Truncate();
char buf[32];
bool ok = aValue.toString(buf, mozilla::ArrayLength(buf));
aResultString.AssignASCII(buf);
MOZ_ASSERT(ok, "buf not big enough");
return ok;
}
/* input type=numer */
bool

Просмотреть файл

@ -20,6 +20,8 @@ public:
bool ConvertStringToNumber(nsAString& aValue,
mozilla::Decimal& aResultValue) const override;
bool ConvertNumberToString(mozilla::Decimal aValue,
nsAString& aResultString) const override;
protected:
explicit NumericInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement)