Bug 1345767 - Part 6: Factor IsRangeOverflow/Underflow() and HasStepMismatch() out of HTMLInputElement. r=smaug
authorJessica Jong <jjong@mozilla.com>
Thu, 04 May 2017 15:33:38 +0800
changeset 356878 c8deb87d938b95382cbc00a7261f74c1e6a6308a
parent 356877 05dc4f814accd4f36eaeb30df5e77f6209edac13
child 356879 b532001b111626c145ada4ce9472eee67c39d793
push id31775
push userihsiao@mozilla.com
push dateMon, 08 May 2017 03:10:38 +0000
treeherdermozilla-central@22aaf8bad4df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1345767
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 1345767 - Part 6: Factor IsRangeOverflow/Underflow() and HasStepMismatch() out of HTMLInputElement. r=smaug MozReview-Commit-ID: 4jmjPK1Wxhz
dom/html/HTMLInputElement.cpp
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
@@ -122,24 +122,16 @@
 #include "js/Date.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
 
 // XXX align=left, hspace, vspace, border? other nav4 attrs
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 
-// This must come outside of any namespace, or else it won't overload with the
-// double based version in nsMathUtils.h
-inline mozilla::Decimal
-NS_floorModulo(mozilla::Decimal x, mozilla::Decimal y)
-{
-  return (x - y * (x / y).floor());
-}
-
 namespace mozilla {
 namespace dom {
 
 // First bits are needed for the control type.
 #define NS_OUTER_ACTIVATE_EVENT   (1 << 9)
 #define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
 #define NS_NO_CONTENT_DISPATCH    (1 << 11)
 #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
@@ -1468,69 +1460,41 @@ HTMLInputElement::AfterSetAttr(int32_t a
     } else if (aName == nsGkAtoms::minlength) {
       UpdateTooShortValidityState();
     } else if (aName == nsGkAtoms::pattern && mDoneCreating) {
       UpdatePatternMismatchValidityState();
     } else if (aName == nsGkAtoms::multiple) {
       UpdateTypeMismatchValidityState();
     } else if (aName == nsGkAtoms::max) {
       UpdateHasRange();
-      if (mType == NS_FORM_INPUT_RANGE) {
-        // The value may need to change when @max changes since the value may
-        // have been invalid and can now change to a valid value, or vice
-        // versa. For example, consider:
-        // <input type=range value=-1 max=1 step=3>. The valid range is 0 to 1
-        // while the nearest valid steps are -1 and 2 (the max value having
-        // prevented there being a valid step in range). Changing @max to/from
-        // 1 and a number greater than on equal to 3 should change whether we
-        // have a step mismatch or not.
-        // The value may also need to change between a value that results in
-        // a step mismatch and a value that results in overflow. For example,
-        // if @max in the example above were to change from 1 to -1.
-        nsAutoString value;
-        GetNonFileValueInternal(value);
-        nsresult rv =
-          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      // Validity state must be updated *after* the SetValueInternal call above
-      // or else the following assert will not be valid.
+      nsresult rv = mInputType->MinMaxStepAttrChanged();
+      NS_ENSURE_SUCCESS(rv, rv);
+      // Validity state must be updated *after* the UpdateValueDueToAttrChange
+      // call above or else the following assert will not be valid.
       // We don't assert the state of underflow during creation since
       // DoneCreatingElement sanitizes.
       UpdateRangeOverflowValidityState();
       MOZ_ASSERT(!mDoneCreating ||
                  mType != NS_FORM_INPUT_RANGE ||
                  !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                  "HTML5 spec does not allow underflow for type=range");
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
-      if (mType == NS_FORM_INPUT_RANGE) {
-        // See @max comment
-        nsAutoString value;
-        GetNonFileValueInternal(value);
-        nsresult rv =
-          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
+      nsresult rv = mInputType->MinMaxStepAttrChanged();
+      NS_ENSURE_SUCCESS(rv, rv);
       // See corresponding @max comment
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
       MOZ_ASSERT(!mDoneCreating ||
                  mType != NS_FORM_INPUT_RANGE ||
                  !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                  "HTML5 spec does not allow underflow for type=range");
     } else if (aName == nsGkAtoms::step) {
-      if (mType == NS_FORM_INPUT_RANGE) {
-        // See @max comment
-        nsAutoString value;
-        GetNonFileValueInternal(value);
-        nsresult rv =
-          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
+      nsresult rv = mInputType->MinMaxStepAttrChanged();
+      NS_ENSURE_SUCCESS(rv, rv);
       // See corresponding @max comment
       UpdateStepMismatchValidityState();
       MOZ_ASSERT(!mDoneCreating ||
                  mType != NS_FORM_INPUT_RANGE ||
                  !GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                  "HTML5 spec does not allow underflow for type=range");
     } else if (aName == nsGkAtoms::dir &&
                aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
@@ -7543,77 +7507,29 @@ bool
 HTMLInputElement::HasPatternMismatch() const
 {
   return mInputType->HasPatternMismatch();
 }
 
 bool
 HTMLInputElement::IsRangeOverflow() const
 {
-  if (!DoesMinMaxApply()) {
-    return false;
-  }
-
-  Decimal maximum = GetMaximum();
-  if (maximum.isNaN()) {
-    return false;
-  }
-
-  Decimal value = GetValueAsDecimal();
-  if (value.isNaN()) {
-    return false;
-  }
-
-  return value > maximum;
+  return mInputType->IsRangeOverflow();
 }
 
 bool
 HTMLInputElement::IsRangeUnderflow() const
 {
-  if (!DoesMinMaxApply()) {
-    return false;
-  }
-
-  Decimal minimum = GetMinimum();
-  if (minimum.isNaN()) {
-    return false;
-  }
-
-  Decimal value = GetValueAsDecimal();
-  if (value.isNaN()) {
-    return false;
-  }
-
-  return value < minimum;
+  return mInputType->IsRangeUnderflow();
 }
 
 bool
 HTMLInputElement::HasStepMismatch(bool aUseZeroIfValueNaN) const
 {
-  if (!DoesStepApply()) {
-    return false;
-  }
-
-  Decimal value = GetValueAsDecimal();
-  if (value.isNaN()) {
-    if (aUseZeroIfValueNaN) {
-      value = Decimal(0);
-    } else {
-      // The element can't suffer from step mismatch if it's value isn't a number.
-      return false;
-    }
-  }
-
-  Decimal step = GetStep();
-  if (step == kStepAny) {
-    return false;
-  }
-
-  // Value has to be an integral multiple of step.
-  return NS_floorModulo(value - GetStepBase(), step) != Decimal(0);
+  return mInputType->HasStepMismatch(aUseZeroIfValueNaN);
 }
 
 /**
  * Takes aEmail and attempts to convert everything after the first "@"
  * character (if anything) to punycode before returning the complete result via
  * the aEncodedEmail out-param. The aIndexOfAt out-param is set to the index of
  * the "@" character.
  *
--- a/dom/html/input/DateTimeInputTypes.cpp
+++ b/dom/html/input/DateTimeInputTypes.cpp
@@ -23,8 +23,62 @@ DateTimeInputTypeBase::IsValueMissing() 
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
 }
+
+bool
+DateTimeInputTypeBase::IsRangeOverflow() const
+{
+  mozilla::Decimal maximum = mInputElement->GetMaximum();
+  if (maximum.isNaN()) {
+    return false;
+  }
+
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    return false;
+  }
+
+  return value > maximum;
+}
+
+bool
+DateTimeInputTypeBase::IsRangeUnderflow() const
+{
+  mozilla::Decimal minimum = mInputElement->GetMinimum();
+  if (minimum.isNaN()) {
+    return false;
+  }
+
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    return false;
+  }
+
+  return value < minimum;
+}
+
+bool
+DateTimeInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const
+{
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    if (aUseZeroIfValueNaN) {
+      value = mozilla::Decimal(0);
+    } else {
+      // The element can't suffer from step mismatch if it's value isn't a number.
+      return false;
+    }
+  }
+
+  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);
+}
--- a/dom/html/input/DateTimeInputTypes.h
+++ b/dom/html/input/DateTimeInputTypes.h
@@ -10,16 +10,19 @@
 #include "InputType.h"
 
 class DateTimeInputTypeBase : public ::InputType
 {
 public:
   ~DateTimeInputTypeBase() override {}
 
   bool IsValueMissing() const override;
+  bool IsRangeOverflow() const override;
+  bool IsRangeUnderflow() const override;
+  bool HasStepMismatch(bool aUseZeroIfValueNaN) const override;
 
 protected:
   explicit DateTimeInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement)
     : InputType(aInputElement)
   {}
 
   bool IsMutable() const override;
 };
--- a/dom/html/input/InputType.cpp
+++ b/dom/html/input/InputType.cpp
@@ -11,16 +11,17 @@
 #include "CheckableInputTypes.h"
 #include "ColorInputType.h"
 #include "DateTimeInputTypes.h"
 #include "FileInputType.h"
 #include "HiddenInputType.h"
 #include "NumericInputTypes.h"
 #include "SingleLineTextInputTypes.h"
 
+const mozilla::Decimal InputType::kStepAny = mozilla::Decimal(0);
 
 /* static */ mozilla::UniquePtr<InputType, DoNotDelete>
 InputType::Create(mozilla::dom::HTMLInputElement* aInputElement, uint8_t aType,
                   void* aMemory)
 {
   mozilla::UniquePtr<InputType, DoNotDelete> inputType;
   switch(aType) {
     // Single line text
@@ -115,16 +116,28 @@ InputType::IsValueEmpty() const
 }
 
 void
 InputType::GetNonFileValueInternal(nsAString& aValue) const
 {
   return mInputElement->GetNonFileValueInternal(aValue);
 }
 
+nsresult
+InputType::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
+{
+  return mInputElement->SetValueInternal(aValue, aFlags);
+}
+
+mozilla::Decimal
+InputType::GetStepBase() const
+{
+  return mInputElement->GetStepBase();
+}
+
 void
 InputType::DropReference()
 {
   // Drop our (non ref-counted) reference.
   mInputElement = nullptr;
 }
 
 bool
@@ -151,8 +164,32 @@ InputType::HasTypeMismatch() const
   return false;
 }
 
 bool
 InputType::HasPatternMismatch() const
 {
   return false;
 }
+
+bool
+InputType::IsRangeOverflow() const
+{
+  return false;
+}
+
+bool
+InputType::IsRangeUnderflow() const
+{
+  return false;
+}
+
+bool
+InputType::HasStepMismatch(bool aUseZeroIfValueNaN) const
+{
+  return false;
+}
+
+nsresult
+InputType::MinMaxStepAttrChanged()
+{
+  return NS_OK;
+}
--- a/dom/html/input/InputType.h
+++ b/dom/html/input/InputType.h
@@ -3,18 +3,28 @@
 /* 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/. */
 
 #ifndef InputType_h__
 #define InputType_h__
 
 #include <stdint.h>
+#include "mozilla/Decimal.h"
 #include "mozilla/UniquePtr.h"
 #include "nsString.h"
+#include "nsError.h"
+
+// This must come outside of any namespace, or else it won't overload with the
+// double based version in nsMathUtils.h
+inline mozilla::Decimal
+NS_floorModulo(mozilla::Decimal x, mozilla::Decimal y)
+{
+  return (x - y * (x / y).floor());
+}
 
 namespace mozilla {
 namespace dom {
 class HTMLInputElement;
 } // namespace dom
 } // namespace mozilla
 
 struct DoNotDelete;
@@ -26,26 +36,34 @@ class InputType
 {
 public:
   static mozilla::UniquePtr<InputType, DoNotDelete>
   Create(mozilla::dom::HTMLInputElement* aInputElement, uint8_t aType,
          void* aMemory);
 
   virtual ~InputType() {}
 
+  // Float value returned by GetStep() when the step attribute is set to 'any'.
+  static const mozilla::Decimal kStepAny;
+
   /**
    * Drop the reference to the input element.
    */
   void DropReference();
 
   virtual bool IsTooLong() const;
   virtual bool IsTooShort() const;
   virtual bool IsValueMissing() const;
   virtual bool HasTypeMismatch() const;
   virtual bool HasPatternMismatch() const;
+  virtual bool IsRangeOverflow() const;
+  virtual bool IsRangeUnderflow() const;
+  virtual bool HasStepMismatch(bool aUseZeroIfValueNaN) const;
+
+  virtual nsresult MinMaxStepAttrChanged();
 
 protected:
   explicit InputType(mozilla::dom::HTMLInputElement* aInputElement)
     : mInputElement(aInputElement)
   {}
 
   /**
    * Get the mutable state of the element.
@@ -64,16 +82,32 @@ protected:
    * @return whether the input element's current value is the empty string.
    */
   bool IsValueEmpty() const;
 
   // A getter for callers that know we're not dealing with a file input, so they
   // don't have to think about the caller type.
   void GetNonFileValueInternal(nsAString& aValue) const;
 
+  /**
+   * Setting the input element's value.
+   *
+   * @param aValue      String to set.
+   * @param aFlags      See nsTextEditorState::SetValueFlags.
+   */
+  nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
+
+  /**
+   * Return the base used to compute if a value matches step.
+   * Basically, it's the min attribute if present and a default value otherwise.
+   *
+   * @return The step base.
+   */
+  mozilla::Decimal GetStepBase() 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
@@ -2,29 +2,105 @@
 /* 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 "NumericInputTypes.h"
 
 #include "mozilla/dom/HTMLInputElement.h"
+#include "nsTextEditorState.h"
 
 bool
 NumberInputType::IsMutable() const
 {
   return !mInputElement->IsDisabled() &&
          !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
 }
 
 bool
+NumericInputTypeBase::IsRangeOverflow() const
+{
+  mozilla::Decimal maximum = mInputElement->GetMaximum();
+  if (maximum.isNaN()) {
+    return false;
+  }
+
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    return false;
+  }
+
+  return value > maximum;
+}
+
+bool
+NumericInputTypeBase::IsRangeUnderflow() const
+{
+  mozilla::Decimal minimum = mInputElement->GetMinimum();
+  if (minimum.isNaN()) {
+    return false;
+  }
+
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    return false;
+  }
+
+  return value < minimum;
+}
+
+bool
+NumericInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const
+{
+  mozilla::Decimal value = mInputElement->GetValueAsDecimal();
+  if (value.isNaN()) {
+    if (aUseZeroIfValueNaN) {
+      value = mozilla::Decimal(0);
+    } else {
+      // The element can't suffer from step mismatch if it's value isn't a number.
+      return false;
+    }
+  }
+
+  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=numer */
+
+bool
 NumberInputType::IsValueMissing() const
 {
   if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
 }
+
+/* input type=range */
+nsresult
+RangeInputType::MinMaxStepAttrChanged()
+{
+  // The value may need to change when @min/max/step changes since the value may
+  // have been invalid and can now change to a valid value, or vice versa. For
+  // example, consider: <input type=range value=-1 max=1 step=3>. The valid
+  // range is 0 to 1 while the nearest valid steps are -1 and 2 (the max value
+  // having prevented there being a valid step in range). Changing @max to/from
+  // 1 and a number greater than on equal to 3 should change whether we have a
+  // step mismatch or not.
+  // The value may also need to change between a value that results in a step
+  // mismatch and a value that results in overflow. For example, if @max in the
+  // example above were to change from 1 to -1.
+  nsAutoString value;
+  GetNonFileValueInternal(value);
+  return SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
+}
--- a/dom/html/input/NumericInputTypes.h
+++ b/dom/html/input/NumericInputTypes.h
@@ -9,16 +9,20 @@
 
 #include "InputType.h"
 
 class NumericInputTypeBase : public ::InputType
 {
 public:
   ~NumericInputTypeBase() override {}
 
+  bool IsRangeOverflow() const override;
+  bool IsRangeUnderflow() const override;
+  bool HasStepMismatch(bool aUseZeroIfValueNaN) const override;
+
 protected:
   explicit NumericInputTypeBase(mozilla::dom::HTMLInputElement* aInputElement)
     : InputType(aInputElement)
   {}
 };
 
 // input type=number
 class NumberInputType : public NumericInputTypeBase
@@ -45,15 +49,17 @@ class RangeInputType : public NumericInp
 {
 public:
   static InputType*
   Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory)
   {
     return new (aMemory) RangeInputType(aInputElement);
   }
 
+  nsresult MinMaxStepAttrChanged() override;
+
 private:
   explicit RangeInputType(mozilla::dom::HTMLInputElement* aInputElement)
     : NumericInputTypeBase(aInputElement)
   {}
 };
 
 #endif /* NumericInputTypes_h__ */