Bug 1363258 - Part 1: Factor ConvertStringToNumber out of HTMLInputElement. r?smaug draft
authorJessica Jong <jjong@mozilla.com>
Wed, 10 May 2017 17:03:33 +0800
changeset 577749 987e6b1fb9ddf61fbaf39caba4a46884c8b4858e
parent 577554 e66dedabe582ba7b394aee4f89ed70fe389b3c46
child 577750 8b0b7248f7e09f71cce2fe668ca27f1cf6508040
push id58771
push userjjong@mozilla.com
push dateMon, 15 May 2017 09:24:10 +0000
reviewerssmaug
bugs1363258
milestone55.0a1
Bug 1363258 - Part 1: Factor ConvertStringToNumber out of HTMLInputElement. r?smaug MozReview-Commit-ID: 8DratVTlToP
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/html/input/DateTimeInputTypes.cpp
dom/html/input/DateTimeInputTypes.h
dom/html/input/InputType.cpp
dom/html/input/InputType.h
dom/html/input/NumericInputTypes.cpp
dom/html/input/NumericInputTypes.h
--- 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