Bug 1345767 - Part 6: Factor IsRangeOverflow/Underflow() and HasStepMismatch() out of HTMLInputElement. r?smaug
MozReview-Commit-ID: 4jmjPK1Wxhz
--- 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)) {
@@ -7522,77 +7486,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__ */