author | Jessica Jong <jjong@mozilla.com> |
Wed, 10 May 2017 17:03:33 +0800 | |
changeset 358472 | 6a2dedb044054624ee630622e5f5632efdd2b4a6 |
parent 358471 | 830cc1aae18f2d023ba97ee4bb4a5d6ef8a31aa5 |
child 358473 | d0bb5397ddefb40c9fbbddd5428784c6ba32e6d7 |
push id | 31827 |
push user | cbook@mozilla.com |
push date | Tue, 16 May 2017 10:34:19 +0000 |
treeherder | mozilla-central@49365d675cbb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1363258 |
milestone | 55.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1811,128 +1811,26 @@ HTMLInputElement::StringToDecimal(const if (!IsASCII(aValue)) { return Decimal::nan(); } NS_LossyConvertUTF16toASCII asciiString(aValue); std::string stdString = asciiString.get(); 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 { Decimal decimalValue; nsAutoString stringValue; GetNonFileValueInternal(stringValue); - return !ConvertStringToNumber(stringValue, decimalValue) ? Decimal::nan() - : decimalValue; + return !mInputType->ConvertStringToNumber(stringValue, decimalValue) ? + Decimal::nan() : decimalValue; } void HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType, ErrorResult& aRv) { // check security. Note that setting the value to the empty string is always // OK and gives pages a way to clear a file input if necessary. @@ -2365,17 +2263,17 @@ HTMLInputElement::GetMinimum() const if (!HasAttr(kNameSpaceID_None, nsGkAtoms::min)) { return defaultMinimum; } nsAutoString minStr; GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr); Decimal min; - return ConvertStringToNumber(minStr, min) ? min : defaultMinimum; + return mInputType->ConvertStringToNumber(minStr, min) ? min : defaultMinimum; } Decimal HTMLInputElement::GetMaximum() const { MOZ_ASSERT(DoesValueAsNumberApply(), "GetMaximum() should only be used for types that allow .valueAsNumber"); @@ -2386,41 +2284,41 @@ HTMLInputElement::GetMaximum() const if (!HasAttr(kNameSpaceID_None, nsGkAtoms::max)) { return defaultMaximum; } nsAutoString maxStr; GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr); Decimal max; - return ConvertStringToNumber(maxStr, max) ? max : defaultMaximum; + return mInputType->ConvertStringToNumber(maxStr, max) ? max : defaultMaximum; } Decimal HTMLInputElement::GetStepBase() const { MOZ_ASSERT(IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_RANGE, "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(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; } if (mType == NS_FORM_INPUT_WEEK) { return kDefaultStepBaseWeek; } return kDefaultStepBase; @@ -5339,17 +5237,17 @@ HTMLInputElement::SanitizeValue(nsAStrin aValue.StripCRLF(); aValue = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(aValue); } break; case NS_FORM_INPUT_NUMBER: { Decimal value; - bool ok = ConvertStringToNumber(aValue, value); + bool ok = mInputType->ConvertStringToNumber(aValue, value); if (!ok) { aValue.Truncate(); } } break; case NS_FORM_INPUT_RANGE: { Decimal minimum = GetMinimum(); @@ -5358,17 +5256,17 @@ HTMLInputElement::SanitizeValue(nsAStrin "type=range should have a default maximum/minimum"); // We use this to avoid modifying the string unnecessarily, since that // may introduce rounding. This is set to true only if the value we // parse out from aValue needs to be sanitized. 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. value = maximum <= minimum ? minimum : minimum + (maximum - minimum)/Decimal(2); } else if (value < minimum || maximum < minimum) { needSanitization = true; value = minimum; } else if (value > maximum) {
--- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -1162,27 +1162,16 @@ protected: * Returns the radio group container if the element has one, null otherwise. * The radio group container will be the form owner if there is one. * The current document otherwise. * @return the radio group container if the element has one, null otherwise. */ 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 * 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
--- a/dom/html/input/DateTimeInputTypes.cpp +++ b/dom/html/input/DateTimeInputTypes.cpp @@ -1,18 +1,25 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #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 { return !mInputElement->IsDisabled() && !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); } bool @@ -77,8 +84,114 @@ DateTimeInputTypeBase::HasStepMismatch(b mozilla::Decimal step = mInputElement->GetStep(); if (step == kStepAny) { return false; } // 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; +}
--- a/dom/html/input/DateTimeInputTypes.h +++ b/dom/html/input/DateTimeInputTypes.h @@ -20,92 +20,118 @@ public: bool HasStepMismatch(bool aUseZeroIfValueNaN) const override; protected: explicit DateTimeInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement) : InputType(aInputElement) {} 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 class DateInputType : public DateTimeInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) DateInputType(aInputElement); } + bool ConvertStringToNumber(nsAString& aValue, + mozilla::Decimal& aResultValue) const override; + private: explicit DateInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement) {} }; // input type=time class TimeInputType : public DateTimeInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) TimeInputType(aInputElement); } + bool ConvertStringToNumber(nsAString& aValue, + mozilla::Decimal& aResultValue) const override; + private: explicit TimeInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement) {} }; // input type=week class WeekInputType : public DateTimeInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) WeekInputType(aInputElement); } + bool ConvertStringToNumber(nsAString& aValue, + mozilla::Decimal& aResultValue) const override; + private: explicit WeekInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement) {} }; // input type=month class MonthInputType : public DateTimeInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) MonthInputType(aInputElement); } + bool ConvertStringToNumber(nsAString& aValue, + mozilla::Decimal& aResultValue) const override; + private: explicit MonthInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement) {} }; // input type=datetime-local class DateTimeLocalInputType : public DateTimeInputTypeBase { public: static InputType* Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory) { return new (aMemory) DateTimeLocalInputType(aInputElement); } + bool ConvertStringToNumber(nsAString& aValue, + mozilla::Decimal& aResultValue) const override; + private: explicit DateTimeLocalInputType(mozilla::dom::HTMLInputElement* aInputElement) : DateTimeInputTypeBase(aInputElement) {} }; #endif /* DateTimeInputTypes_h__ */
--- a/dom/html/input/InputType.cpp +++ b/dom/html/input/InputType.cpp @@ -200,8 +200,74 @@ InputType::HasBadInput() const return false; } nsresult 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); +}
--- a/dom/html/input/InputType.h +++ b/dom/html/input/InputType.h @@ -57,16 +57,28 @@ public: virtual bool HasPatternMismatch() const; virtual bool IsRangeOverflow() const; virtual bool IsRangeUnderflow() const; virtual bool HasStepMismatch(bool aUseZeroIfValueNaN) const; virtual bool HasBadInput() const; 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) {} /** * Get the mutable state of the element. * When the element isn't mutable (immutable), the value or checkedness @@ -105,16 +117,90 @@ protected: */ mozilla::Decimal GetStepBase() const; /** * Get the primary frame for the input element. */ 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; }; // Custom deleter for UniquePtr<InputType> to avoid freeing memory pre-allocated // for InputType, but we still need to call the destructor explictly. struct DoNotDelete { void operator()(::InputType* p) { p->~InputType(); } }; #endif /* InputType_h__ */
--- a/dom/html/input/NumericInputTypes.cpp +++ b/dom/html/input/NumericInputTypes.cpp @@ -66,16 +66,27 @@ NumericInputTypeBase::HasStepMismatch(bo if (step == kStepAny) { return false; } // Value has to be an integral multiple of step. 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 NumberInputType::IsValueMissing() const { if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { return false; }
--- a/dom/html/input/NumericInputTypes.h +++ b/dom/html/input/NumericInputTypes.h @@ -13,16 +13,19 @@ class NumericInputTypeBase : public ::In { public: ~NumericInputTypeBase() override {} bool IsRangeOverflow() const override; 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) {} }; // input type=number class NumberInputType : public NumericInputTypeBase