Bug 1363258 - Part 1: Factor ConvertStringToNumber out of HTMLInputElement. r=smaug
authorJessica Jong <jjong@mozilla.com>
Wed, 10 May 2017 17:03:33 +0800
changeset 358472 6a2dedb044054624ee630622e5f5632efdd2b4a6
parent 358471 830cc1aae18f2d023ba97ee4bb4a5d6ef8a31aa5
child 358473 d0bb5397ddefb40c9fbbddd5428784c6ba32e6d7
push id31827
push usercbook@mozilla.com
push dateTue, 16 May 2017 10:34:19 +0000
treeherdermozilla-central@49365d675cbb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1363258
milestone55.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
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