Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen
2012-12-28 12:57:30 -05:00
24 changed files with 1434 additions and 181 deletions

View File

@@ -282,14 +282,12 @@ function getJSON(element) {
// Until the input type=date/datetime/time have been implemented
// let's return their real type even if the platform returns 'text'
// Related to Bug 769352 - Implement <input type=date>
// Related to Bug 777279 - Implement <input type=time>
let attributeType = element.getAttribute("type") || "";
if (attributeType) {
var typeLowerCase = attributeType.toLowerCase();
switch (typeLowerCase) {
case "date":
case "time":
case "datetime":
case "datetime-local":

View File

@@ -1899,6 +1899,7 @@ nsEventStateManager::FireContextClick()
type == NS_FORM_INPUT_PASSWORD ||
type == NS_FORM_INPUT_FILE ||
type == NS_FORM_INPUT_NUMBER ||
type == NS_FORM_INPUT_DATE ||
type == NS_FORM_TEXTAREA);
}
else if (tag == nsGkAtoms::applet ||

View File

@@ -48,6 +48,7 @@ enum ButtonElementTypes {
enum InputElementTypes {
NS_FORM_INPUT_BUTTON = NS_FORM_INPUT_ELEMENT + 1,
NS_FORM_INPUT_CHECKBOX,
NS_FORM_INPUT_DATE,
NS_FORM_INPUT_EMAIL,
NS_FORM_INPUT_FILE,
NS_FORM_INPUT_HIDDEN,
@@ -232,6 +233,8 @@ nsIFormControl::IsSingleLineTextControl(bool aExcludePassword, uint32_t aType)
aType == NS_FORM_INPUT_URL ||
// TODO: this is temporary until bug 635240 is fixed.
aType == NS_FORM_INPUT_NUMBER ||
// TODO: this is temporary until bug 773205 is fixed.
aType == NS_FORM_INPUT_DATE ||
(!aExcludePassword && aType == NS_FORM_INPUT_PASSWORD);
}

View File

@@ -184,6 +184,7 @@ ShouldBeInElements(nsIFormControl* aFormControl)
case NS_FORM_INPUT_TEL :
case NS_FORM_INPUT_URL :
case NS_FORM_INPUT_NUMBER :
case NS_FORM_INPUT_DATE :
case NS_FORM_SELECT :
case NS_FORM_TEXTAREA :
case NS_FORM_FIELDSET :

View File

@@ -88,11 +88,15 @@
#include "mozilla/LookAndFeel.h"
#include "mozilla/Util.h" // DebugOnly
#include "mozilla/Preferences.h"
#include "mozilla/MathAlgorithms.h"
#include "nsIIDNService.h"
#include <limits>
// input type=date
#include "jsapi.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -117,6 +121,7 @@ UploadLastDir* nsHTMLInputElement::gUploadLastDir;
static const nsAttrValue::EnumTable kInputTypeTable[] = {
{ "button", NS_FORM_INPUT_BUTTON },
{ "checkbox", NS_FORM_INPUT_CHECKBOX },
{ "date", NS_FORM_INPUT_DATE },
{ "email", NS_FORM_INPUT_EMAIL },
{ "file", NS_FORM_INPUT_FILE },
{ "hidden", NS_FORM_INPUT_HIDDEN },
@@ -134,7 +139,7 @@ static const nsAttrValue::EnumTable kInputTypeTable[] = {
};
// Default type is 'text'.
static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[13];
static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[14];
static const uint8_t NS_INPUT_AUTOCOMPLETE_OFF = 0;
static const uint8_t NS_INPUT_AUTOCOMPLETE_ON = 1;
@@ -172,6 +177,8 @@ static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
// Default inputmode value is "auto".
static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTable[0];
const double nsHTMLInputElement::kStepScaleFactorDate = 86400000;
const double nsHTMLInputElement::kStepScaleFactorNumber = 1;
const double nsHTMLInputElement::kDefaultStepBase = 0;
const double nsHTMLInputElement::kStepAny = 0;
@@ -696,6 +703,7 @@ nsHTMLInputElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
case NS_FORM_INPUT_TEL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
if (mValueChanged) {
// We don't have our default value anymore. Set our value on
// the clone.
@@ -1055,17 +1063,72 @@ nsHTMLInputElement::IsValueEmpty() const
return value.IsEmpty();
}
bool
nsHTMLInputElement::ConvertStringToNumber(nsAString& aValue,
double& aResultValue) const
{
switch (mType) {
case NS_FORM_INPUT_NUMBER:
{
nsresult ec;
aResultValue = PromiseFlatString(aValue).ToDouble(&ec);
if (NS_FAILED(ec)) {
return false;
}
break;
}
case NS_FORM_INPUT_DATE:
{
JSContext* ctx = nsContentUtils::GetContextFromDocument(OwnerDoc());
if (!ctx) {
return false;
}
uint32_t year, month, day;
if (!GetValueAsDate(aValue, year, month, day)) {
return false;
}
JSObject* date = JS_NewDateObjectMsec(ctx, 0);
jsval rval;
jsval fullYear[3];
fullYear[0].setInt32(year);
fullYear[1].setInt32(month-1);
fullYear[2].setInt32(day);
if (!JS::Call(ctx, date, "setUTCFullYear", 3, fullYear, &rval)) {
return false;
}
jsval timestamp;
if (!JS::Call(ctx, date, "getTime", 0, nullptr, &timestamp)) {
return false;
}
if (!timestamp.isNumber()) {
return false;
}
aResultValue = timestamp.toNumber();
}
break;
default:
return false;
}
return true;
}
double
nsHTMLInputElement::GetValueAsDouble() const
{
double doubleValue;
nsAutoString stringValue;
nsresult ec;
GetValueInternal(stringValue);
doubleValue = stringValue.ToDouble(&ec);
return NS_SUCCEEDED(ec) ? doubleValue : MOZ_DOUBLE_NaN();
return !ConvertStringToNumber(stringValue, doubleValue) ? MOZ_DOUBLE_NaN()
: doubleValue;
}
NS_IMETHODIMP
@@ -1140,11 +1203,121 @@ nsHTMLInputElement::GetList(nsIDOMHTMLElement** aValue)
void
nsHTMLInputElement::SetValue(double aValue)
{
MOZ_ASSERT(!MOZ_DOUBLE_IS_INFINITE(aValue), "aValue must not be Infinity!");
if (MOZ_DOUBLE_IS_NaN(aValue)) {
SetValue(EmptyString());
return;
}
nsAutoString value;
value.AppendFloat(aValue);
ConvertNumberToString(aValue, value);
SetValue(value);
}
bool
nsHTMLInputElement::ConvertNumberToString(double aValue,
nsAString& aResultString) const
{
MOZ_ASSERT(mType == NS_FORM_INPUT_DATE || mType == NS_FORM_INPUT_NUMBER,
"ConvertNumberToString is only implemented for type='{number,date}'");
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(aValue) && !MOZ_DOUBLE_IS_INFINITE(aValue),
"aValue must be a valid non-Infinite number.");
aResultString.Truncate();
switch (mType) {
case NS_FORM_INPUT_NUMBER:
aResultString.AppendFloat(aValue);
return true;
case NS_FORM_INPUT_DATE:
{
JSContext* ctx = nsContentUtils::GetContextFromDocument(OwnerDoc());
if (!ctx) {
return false;
}
// The specs require |aValue| to be truncated.
aValue = floor(aValue);
JSObject* date = JS_NewDateObjectMsec(ctx, aValue);
if (!date) {
return false;
}
jsval year, month, day;
if (!JS::Call(ctx, date, "getUTCFullYear", 0, nullptr, &year) ||
!JS::Call(ctx, date, "getUTCMonth", 0, nullptr, &month) ||
!JS::Call(ctx, date, "getUTCDate", 0, nullptr, &day)) {
return false;
}
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0f", year.toNumber(),
month.toNumber() + 1, day.toNumber());
return true;
}
default:
MOZ_NOT_REACHED();
return false;
}
}
NS_IMETHODIMP
nsHTMLInputElement::GetValueAsDate(JSContext* aCtx, jsval* aDate)
{
if (mType != NS_FORM_INPUT_DATE) {
aDate->setNull();
return NS_OK;
}
uint32_t year, month, day;
nsAutoString value;
GetValueInternal(value);
if (!GetValueAsDate(value, year, month, day)) {
aDate->setNull();
return NS_OK;
}
JSObject* date = JS_NewDateObjectMsec(aCtx, 0);
jsval rval;
jsval fullYear[3];
fullYear[0].setInt32(year);
fullYear[1].setInt32(month-1);
fullYear[2].setInt32(day);
if(!JS::Call(aCtx, date, "setUTCFullYear", 3, fullYear, &rval)) {
aDate->setNull();
return NS_OK;
}
aDate->setObjectOrNull(date);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::SetValueAsDate(JSContext* aCtx, const jsval& aDate)
{
if (mType != NS_FORM_INPUT_DATE) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (!aDate.isObject() || !JS_ObjectIsDate(aCtx, &aDate.toObject())) {
SetValue(EmptyString());
return NS_OK;
}
JSObject& date = aDate.toObject();
jsval timestamp;
bool ret = JS::Call(aCtx, &date, "getTime", 0, nullptr, &timestamp);
if (!ret || !timestamp.isNumber() || MOZ_DOUBLE_IS_NaN(timestamp.toNumber())) {
SetValue(EmptyString());
return NS_OK;
}
SetValue(timestamp.toNumber());
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetValueAsNumber(double* aValueAsNumber)
{
@@ -1156,6 +1329,12 @@ nsHTMLInputElement::GetValueAsNumber(double* aValueAsNumber)
NS_IMETHODIMP
nsHTMLInputElement::SetValueAsNumber(double aValueAsNumber)
{
// TODO: return TypeError when HTMLInputElement is converted to WebIDL, see
// bug 825197.
if (MOZ_DOUBLE_IS_INFINITE(aValueAsNumber)) {
return NS_ERROR_INVALID_ARG;
}
if (!DoesValueAsNumberApply()) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
@@ -1167,8 +1346,8 @@ nsHTMLInputElement::SetValueAsNumber(double aValueAsNumber)
double
nsHTMLInputElement::GetMinAsDouble() const
{
// Should only be used for <input type='number'> for the moment.
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
// Should only be used for <input type='number'/'date'> for the moment.
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE);
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::min)) {
return MOZ_DOUBLE_NaN();
@@ -1177,16 +1356,15 @@ nsHTMLInputElement::GetMinAsDouble() const
nsAutoString minStr;
GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
nsresult ec;
double min = minStr.ToDouble(&ec);
return NS_SUCCEEDED(ec) ? min : MOZ_DOUBLE_NaN();
double min;
return ConvertStringToNumber(minStr, min) ? min : MOZ_DOUBLE_NaN();
}
double
nsHTMLInputElement::GetMaxAsDouble() const
{
// Should only be used for <input type='number'> for the moment.
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER);
// Should only be used for <input type='number'/'date'> for the moment.
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE);
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::max)) {
return MOZ_DOUBLE_NaN();
@@ -1195,9 +1373,8 @@ nsHTMLInputElement::GetMaxAsDouble() const
nsAutoString maxStr;
GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr);
nsresult ec;
double max = maxStr.ToDouble(&ec);
return NS_SUCCEEDED(ec) ? max : MOZ_DOUBLE_NaN();
double max;
return ConvertStringToNumber(maxStr, max) ? max : MOZ_DOUBLE_NaN();
}
double
@@ -1267,6 +1444,21 @@ nsHTMLInputElement::ApplyStep(int32_t aStep)
value += aStep * step;
// For date inputs, the value can hold a string that is not a day. We do not
// want to round it, as it might result in a step mismatch. Instead we want to
// clamp to the next valid value.
if (mType == NS_FORM_INPUT_DATE &&
NS_floorModulo(value - GetStepBase(), GetStepScaleFactor()) != 0) {
double validStep = EuclidLCM<uint64_t>(static_cast<uint64_t>(step),
static_cast<uint64_t>(GetStepScaleFactor()));
if (aStep > 0) {
value -= NS_floorModulo(value - GetStepBase(), validStep);
value += validStep;
} else if (aStep < 0) {
value -= NS_floorModulo(value - GetStepBase(), validStep);
}
}
// When stepUp() is called and the value is below min, we should clamp on
// min unless stepUp() moves us higher than min.
if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW) && aStep > 0 &&
@@ -1373,8 +1565,8 @@ nsHTMLInputElement::MozSetFileNameArray(const PRUnichar **aFileNames, uint32_t a
NS_IMETHODIMP
nsHTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
{
// TODO: temporary until bug 635240 is fixed.
if (mType == NS_FORM_INPUT_NUMBER) {
// TODO: temporary until bug 635240 and 773205 are fixed.
if (mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE) {
*aResult = false;
return NS_OK;
}
@@ -2460,7 +2652,8 @@ nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
(keyEvent->keyCode == NS_VK_RETURN ||
keyEvent->keyCode == NS_VK_ENTER) &&
(IsSingleLineTextControl(false, mType) ||
mType == NS_FORM_INPUT_NUMBER)) {
mType == NS_FORM_INPUT_NUMBER ||
mType == NS_FORM_INPUT_DATE)) {
FireChangeEventIfNeeded();
rv = MaybeSubmitForm(aVisitor.mPresContext);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2754,9 +2947,152 @@ nsHTMLInputElement::SanitizeValue(nsAString& aValue)
}
}
break;
case NS_FORM_INPUT_DATE:
{
if (!aValue.IsEmpty() && !IsValidDate(aValue)) {
aValue.Truncate();
}
}
break;
}
}
bool
nsHTMLInputElement::IsValidDate(nsAString& aValue) const
{
uint32_t year, month, day;
return GetValueAsDate(aValue, year, month, day);
}
bool
nsHTMLInputElement::GetValueAsDate(nsAString& aValue,
uint32_t& aYear,
uint32_t& aMonth,
uint32_t& aDay) const
{
/*
* Parse the year, month, day values out a date string formatted as 'yyy-mm-dd'.
* -The year must be 4 or more digits long, and year > 0
* -The month must be exactly 2 digits long, and 01 <= month <= 12
* -The day must be exactly 2 digit long, and 01 <= day <= maxday
* Where maxday is the number of days in the month 'month' and year 'year'
*/
if (aValue.IsEmpty()) {
return false;
}
int32_t fieldMaxSize = 0;
int32_t fieldMinSize = 4;
enum {
YEAR, MONTH, DAY, NONE
} field;
int32_t fieldSize = 0;
nsresult ec;
field = YEAR;
for (uint32_t offset = 0; offset < aValue.Length(); ++offset) {
// Test if the fied size is superior to its maximum size.
if (fieldMaxSize && fieldSize > fieldMaxSize) {
return false;
}
// Illegal char.
if (aValue[offset] != '-' && !NS_IsAsciiDigit(aValue[offset])) {
return false;
}
// There are more characters in this field.
if (aValue[offset] != '-' && offset != aValue.Length()-1) {
fieldSize++;
continue;
}
// Parse the field.
if (fieldSize < fieldMinSize) {
return false;
}
switch(field) {
case YEAR:
aYear = PromiseFlatString(StringHead(aValue, offset)).ToInteger(&ec);
NS_ENSURE_SUCCESS(ec, false);
if (aYear <= 0) {
return false;
}
// The field after year is month, which have a fixed size of 2 char.
field = MONTH;
fieldMaxSize = 2;
fieldMinSize = 2;
break;
case MONTH:
aMonth = PromiseFlatString(Substring(aValue,
offset-fieldSize,
offset)).ToInteger(&ec);
NS_ENSURE_SUCCESS(ec, false);
if (aMonth < 1 || aMonth > 12) {
return false;
}
// The next field is the last one, we won't parse a '-',
// so the field size will be one char smaller.
field = DAY;
fieldMinSize = 1;
fieldMaxSize = 1;
break;
case DAY:
aDay = PromiseFlatString(Substring(aValue,
offset-fieldSize,
offset + 1)).ToInteger(&ec);
NS_ENSURE_SUCCESS(ec, false);
if (aDay < 1 || aDay > NumberOfDaysInMonth(aMonth, aYear)) {
return false;
}
field = NONE;
break;
default:
return false;
}
fieldSize = 0;
}
return field == NONE;
}
uint32_t
nsHTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const
{
/*
* Returns the number of days in a month.
* Months that are |longMonths| always have 31 days.
* Months that are not |longMonths| have 30 days except February (month 2).
* February has 29 days during leap years which are years that are divisible by 400.
* or divisible by 100 and 4. February has 28 days otherwise.
*/
static const bool longMonths[] = { true, false, true, false, true, false,
true, true, false, true, false, true };
MOZ_ASSERT(aMonth <= 12 && aMonth > 0);
if (longMonths[aMonth-1]) {
return 31;
}
if (aMonth != 2) {
return 30;
}
return (aYear % 400 == 0 || (aYear % 100 != 0 && aYear % 4 == 0))
? 29 : 28;
}
bool
nsHTMLInputElement::ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
@@ -2771,8 +3107,9 @@ nsHTMLInputElement::ParseAttribute(int32_t aNamespaceID,
bool success = aResult.ParseEnumValue(aValue, kInputTypeTable, false);
if (success) {
newType = aResult.GetEnumValue();
if (newType == NS_FORM_INPUT_NUMBER &&
!Preferences::GetBool("dom.experimental_forms", false)) {
if ((newType == NS_FORM_INPUT_NUMBER ||
newType == NS_FORM_INPUT_DATE) &&
!Preferences::GetBool("dom.experimental_forms", false)) {
newType = kInputDefaultType->value;
aResult.SetTo(newType, &aValue);
}
@@ -3397,6 +3734,7 @@ nsHTMLInputElement::SaveState()
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_HIDDEN:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
{
if (mValueChanged) {
inputState = new nsHTMLInputElementState();
@@ -3581,6 +3919,7 @@ nsHTMLInputElement::RestoreState(nsPresState* aState)
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_HIDDEN:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
{
SetValueInternal(inputState->GetValue(), false, true);
break;
@@ -3805,6 +4144,7 @@ nsHTMLInputElement::GetValueMode() const
case NS_FORM_INPUT_EMAIL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
return VALUE_MODE_VALUE;
default:
NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()");
@@ -3849,6 +4189,7 @@ nsHTMLInputElement::DoesReadOnlyApply() const
case NS_FORM_INPUT_EMAIL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
return true;
default:
NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()");
@@ -3885,6 +4226,7 @@ nsHTMLInputElement::DoesRequiredApply() const
case NS_FORM_INPUT_EMAIL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
return true;
default:
NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
@@ -3896,11 +4238,21 @@ nsHTMLInputElement::DoesRequiredApply() const
}
}
bool
nsHTMLInputElement::PlaceholderApplies() const
{
if (mType == NS_FORM_INPUT_DATE) {
return false;
}
return IsSingleLineTextControl(false);
}
bool
nsHTMLInputElement::DoesPatternApply() const
{
// TODO: temporary until bug 635240 is fixed.
if (mType == NS_FORM_INPUT_NUMBER) {
if (mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE) {
return false;
}
@@ -3913,6 +4265,7 @@ nsHTMLInputElement::DoesMinMaxApply() const
switch (mType)
{
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
// TODO:
// case NS_FORM_INPUT_RANGE:
// All date/time types.
@@ -3946,11 +4299,10 @@ nsHTMLInputElement::DoesMinMaxApply() const
double
nsHTMLInputElement::GetStep() const
{
NS_ASSERTION(mType == NS_FORM_INPUT_NUMBER,
"We can't be there if type!=number!");
MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER || mType == NS_FORM_INPUT_DATE,
"We can't be there if type!=number or date!");
// NOTE: should be defaultStep * defaultStepScaleFactor,
// which is 1 for type=number.
// NOTE: should be defaultStep, which is 1 for type=number and date.
double step = 1;
if (HasAttr(kNameSpaceID_None, nsGkAtoms::step)) {
@@ -3963,17 +4315,16 @@ nsHTMLInputElement::GetStep() const
}
nsresult ec;
// NOTE: should be multiplied by defaultStepScaleFactor,
// which is 1 for type=number.
step = stepStr.ToDouble(&ec);
if (NS_FAILED(ec) || step <= 0) {
// NOTE: we should use defaultStep * defaultStepScaleFactor,
// which is 1 for type=number.
// NOTE: we should use defaultStep, which is 1 for type=number and date.
step = 1;
}
}
return step;
// TODO: This multiplication can lead to inexact results, we should use a
// type that supports a better precision than double. Bug 783607.
return step * GetStepScaleFactor();
}
// nsIConstraintValidation
@@ -4164,6 +4515,15 @@ nsHTMLInputElement::HasStepMismatch() const
return false;
}
if (mType == NS_FORM_INPUT_DATE) {
// The multiplication by the stepScaleFactor for date can easily lead
// to precision loss, since in most use cases this value should be
// an integer (millisecond precision), we can get rid of the precision
// loss by rounding step. This will however lead to erroneous results
// when step was intented to have a precision superior to a millisecond.
step = NS_round(step);
}
// Value has to be an integral multiple of step.
return NS_floorModulo(value - GetStepBase(), step) != 0;
}
@@ -4386,11 +4746,18 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
{
nsXPIDLString message;
double max = GetMaxAsDouble();
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(max));
nsAutoString maxStr;
maxStr.AppendFloat(max);
if (mType == NS_FORM_INPUT_NUMBER) {
//We want to show the value as parsed when it's a number
double max = GetMaxAsDouble();
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(max));
maxStr.AppendFloat(max);
} else if (mType == NS_FORM_INPUT_DATE) {
GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr);
} else {
NS_NOTREACHED("Unexpected input type");
}
const PRUnichar* params[] = { maxStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -4403,11 +4770,17 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
{
nsXPIDLString message;
double min = GetMinAsDouble();
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(min));
nsAutoString minStr;
minStr.AppendFloat(min);
if (mType == NS_FORM_INPUT_NUMBER) {
double min = GetMinAsDouble();
MOZ_ASSERT(!MOZ_DOUBLE_IS_NaN(min));
minStr.AppendFloat(min);
} else if (mType == NS_FORM_INPUT_DATE) {
GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
} else {
NS_NOTREACHED("Unexpected input type");
}
const PRUnichar* params[] = { minStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -4426,6 +4799,16 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
double step = GetStep();
MOZ_ASSERT(step != kStepAny);
// In case this is a date and the step is not an integer, we don't want to
// display the dates corresponding to the truncated timestamps of valueLow
// and valueHigh because they might suffer from a step mismatch as well.
// Instead we want the timestamps to correspond to a rounded day. That is,
// we want a multiple of the step scale factor (1 day) as well as of step.
if (mType == NS_FORM_INPUT_DATE) {
step = EuclidLCM<uint64_t>(static_cast<uint64_t>(step),
static_cast<uint64_t>(GetStepScaleFactor()));
}
double stepBase = GetStepBase();
double valueLow = value - NS_floorModulo(value - stepBase, step);
@@ -4435,8 +4818,8 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
if (MOZ_DOUBLE_IS_NaN(max) || valueHigh <= max) {
nsAutoString valueLowStr, valueHighStr;
valueLowStr.AppendFloat(valueLow);
valueHighStr.AppendFloat(valueHigh);
ConvertNumberToString(valueLow, valueLowStr);
ConvertNumberToString(valueHigh, valueHighStr);
const PRUnichar* params[] = { valueLowStr.get(), valueHighStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -4444,7 +4827,7 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
params, message);
} else {
nsAutoString valueLowStr;
valueLowStr.AppendFloat(valueLow);
ConvertNumberToString(valueLow, valueLowStr);
const PRUnichar* params[] = { valueLowStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -4861,6 +5244,22 @@ nsHTMLInputElement::GetFilterFromAccept()
return filter;
}
double
nsHTMLInputElement::GetStepScaleFactor() const
{
MOZ_ASSERT(DoesStepApply());
switch (mType) {
case NS_FORM_INPUT_DATE:
return kStepScaleFactorDate;
case NS_FORM_INPUT_NUMBER:
return kStepScaleFactorNumber;
default:
MOZ_NOT_REACHED();
return MOZ_DOUBLE_NaN();
}
}
void
nsHTMLInputElement::UpdateValidityUIBits(bool aIsFocused)
{
@@ -4883,7 +5282,7 @@ nsHTMLInputElement::UpdateHasRange()
{
mHasRange = false;
if (mType != NS_FORM_INPUT_NUMBER) {
if (mType != NS_FORM_INPUT_NUMBER && mType != NS_FORM_INPUT_DATE) {
return;
}

View File

@@ -502,7 +502,7 @@ protected:
/**
* Returns whether the placeholder attribute applies for the current type.
*/
bool PlaceholderApplies() const { return IsSingleLineTextControl(false, mType); }
bool PlaceholderApplies() const;
/**
* Set the current default value to the value of the input element.
@@ -560,6 +560,54 @@ protected:
*/
double GetValueAsDouble() const;
/**
* Convert a string to a 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 timestamp as a double.
* @result whether the parsing was successful.
*/
bool ConvertStringToNumber(nsAString& aValue, double& aResultValue) const;
/**
* Convert a double 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 double to be converted
* @param aResultString [out] the string representing the double
* @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
* as expected by the type.
*/
bool ConvertNumberToString(double aValue, nsAString& aResultString) const;
/**
* Parse a date string of the form yyyy-mm-dd
* @param the string to be parsed.
* @return whether the string is a valid date.
* Note : this function does not consider the empty string as valid.
*/
bool IsValidDate(nsAString& aValue) const;
/**
* Parse a date string of the form yyyy-mm-dd
* @param the string to be parsed.
* @return the date in aYear, aMonth, aDay.
* @return whether the parsing was successful.
*/
bool GetValueAsDate(nsAString& aValue,
uint32_t& aYear,
uint32_t& aMonth,
uint32_t& aDay) const;
/**
* This methods returns the number of days in a given month, for a given year.
*/
uint32_t NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const;
/**
* Sets the value of the element to the string representation of the double.
*
@@ -584,6 +632,13 @@ protected:
*/
double GetMaxAsDouble() const;
/**
* Get the step scale value for the current type.
* See:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/common-input-element-attributes.html#concept-input-step-scale
*/
double GetStepScaleFactor() const;
/**
* Returns the current step value.
* Returns kStepAny if the current step is "any" string.
@@ -653,6 +708,10 @@ protected:
*/
nsString mFocusedValue;
// Step scale factor values, for input types that have one.
static const double kStepScaleFactorDate;
static const double kStepScaleFactorNumber;
// Default step base value when a type do not have specific one.
static const double kDefaultStepBase;
// Float alue returned by GetStep() when the step attribute is set to 'any'.

View File

@@ -159,7 +159,6 @@ MOCHITEST_FILES = \
test_bug567938-4.html \
test_bug569955.html \
test_bug573969.html \
test_bug549475.html \
test_bug585508.html \
test_bug561640.html \
test_bug566064.html \

View File

@@ -49,6 +49,8 @@ MOCHITEST_FILES = \
test_valueasnumber_attribute.html \
test_experimental_forms_pref.html \
test_input_number_value.html \
test_input_sanitization.html \
test_valueasdate_attribute.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@@ -201,10 +201,10 @@ reflectLimitedEnumerated({
attribute: "type",
validValues: [ "hidden", "text", "search", "tel", "url", "email", "password",
"checkbox", "radio", "file", "submit", "image", "reset",
"button", "number" ],
"button", "date", "number" ],
invalidValues: [ "this-is-probably-a-wrong-type", "", "tulip" ],
defaultValue: "text",
unsupportedValues: [ "datetime", "date", "month", "week", "time",
unsupportedValues: [ "datetime", "month", "week", "time",
"datetime-local", "range", "color" ]
});
@@ -218,8 +218,8 @@ reflectString({
// .value doesn't reflect a content attribute.
// .valueAsDate
todo("valueAsDate" in document.createElement("input"),
"valueAsDate isn't implemented yet");
is("valueAsDate" in document.createElement("input"), true,
"valueAsDate should be available");
// Deeper check will be done with bug 763305.
is('valueAsNumber' in document.createElement("input"), true,

View File

@@ -25,13 +25,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549475
var inputTypes =
[
"text", "password", "search", "tel", "hidden", "checkbox", "radio",
"submit", "image", "reset", "button", "email", "url", "number",
"submit", "image", "reset", "button", "email", "url", "number", "date",
];
var todoTypes =
[
"range", "color",
"date", "month", "week", "time", "datetime", "datetime-local",
"month", "week", "time", "datetime", "datetime-local",
];
var valueModeValue =
@@ -40,40 +40,114 @@ var valueModeValue =
"month", "week", "time", "datetime-local", "number", "range", "color",
];
function checkDateSanitizing(element) {
var invalidData =
[
"1234",
"1234-",
"12345",
"1234-01",
"1234-012",
"1234-01-",
"12-12",
"999-01-01",
"1234-56-78-91",
"1234-567-78",
"1234--7-78",
"abcd-12-12",
"thisinotadate",
"2012-13-01",
"1234-12-42",
" 2012-13-01",
" 123-01-01",
"2012- 3-01",
"12- 10- 01",
" 12-0-1",
"2012-3-001",
"2012-12-00",
"2012-12-1r",
"2012-11-31",
"2011-02-29",
"2100-02-29",
"a2000-01-01",
"2000a-01-0'",
"20aa00-01-01",
"2000a2000-01-01",
"2000-1-1",
"2000-1-01",
"2000-01-1",
"2000-01-01 ",
"2000- 01-01",
"-1970-01-01",
"0000-00-00",
"0001-00-00",
"0000-01-01",
"1234-12 12",
"1234 12-12",
"1234 12 12",
];
var validData =
[
"1970-01-01",
"1234-12-12",
"1234567890-01-02",
"2012-12-31",
"2012-02-29",
"2000-02-29",
];
for (data of validData) {
element.value = data;
is(element.value, data, "valid date should not be sanitized");
}
for (data of invalidData) {
element.value = data;
is(element.value, "", "invalid date should be sanitized");
}
}
function sanitizeValue(aType, aValue)
{
switch (aType) {
case "text":
case "password":
case "search":
case "tel":
return aValue.replace(/[\n\r]/g, "");
case "url":
case "email":
return aValue.replace(/[\n\r]/g, "").replace(/^\s+|\s+$/g, "");
case "date":
case "month":
case "week":
case "time":
case "datetime":
case "datetime-local":
// TODO: write the sanitize algorithm.
return "";
case "number":
return (parseFloat(aValue) + "" === aValue) ? aValue : "";
case "range":
// TODO: write the sanitize algorithm.
return "";
case "color":
// TODO: write the sanitize algorithm.
return "";
default:
return aValue;
}
switch (aType) {
case "text":
case "password":
case "search":
case "tel":
return aValue.replace(/[\n\r]/g, "");
case "url":
case "email":
return aValue.replace(/[\n\r]/g, "").replace(/^\s+|\s+$/g, "");
case "date":
return "";
case "month":
case "week":
case "time":
case "datetime":
case "datetime-local":
// TODO: write the sanitize algorithm.
return "";
case "number":
return (parseFloat(aValue) + "" === aValue) ? aValue : "";
case "range":
// TODO: write the sanitize algorithm.
return "";
case "color":
// TODO: write the sanitize algorithm.
return "";
default:
return aValue;
}
}
function checkSanitizing(element)
{
if (element.type == 'date') {
checkDateSanitizing(element);
return;
}
var testData =
[
// For text, password, search, tel, email:

View File

@@ -28,7 +28,7 @@ var types = [
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true, true ],
[ 'date', true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true, true ],
@@ -90,7 +90,12 @@ for (var data of types) {
checkValidity(input, true, apply, false);
input.max = '2';
if (input.type == 'date') {
input.max = '2012-06-27';
} else {
input.max = '2';
}
checkValidity(input, true, apply, apply);
if (input.type == 'url') {
@@ -118,6 +123,36 @@ for (var data of types) {
checkValidity(input, true, apply, apply);
file.remove(false);
} else if (input.type == 'date') {
input.value = '2012-06-26';
checkValidity(input, true, apply, apply);
input.value = '2012-06-27';
checkValidity(input, true, apply, apply);
input.value = 'foo';
checkValidity(input, true, apply, apply);
input.value = '2012-06-28';
checkValidity(input, false, apply, apply);
input.max = '2012-06-30';
checkValidity(input, true, apply, apply);
input.value = '2012-07-05';
checkValidity(input, false, apply, apply);
input.value = '1000-01-01';
checkValidity(input, true, apply, apply);
input.value = '20120-01-01';
checkValidity(input, false, apply, apply);
input.max = '0050-01-01';
checkValidity(input, false, apply, apply);
input.value = '0049-01-01';
checkValidity(input, true, apply, apply);
} else {
input.value = '1';
checkValidity(input, true, apply, apply);

View File

@@ -28,7 +28,7 @@ var types = [
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true, true ],
[ 'date', true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true, true ],
@@ -90,7 +90,12 @@ for (var data of types) {
checkValidity(input, true, apply, false);
input.min = '0';
if (input.type == 'date') {
input.min = '2012-06-27';
} else {
input.min = '0';
}
checkValidity(input, true, apply, apply);
if (input.type == 'url') {
@@ -118,6 +123,36 @@ for (var data of types) {
checkValidity(input, true, apply, apply);
file.remove(false);
} else if (input.type == 'date') {
input.value = '2012-06-28';
checkValidity(input, true, apply, apply);
input.value = '2012-06-27';
checkValidity(input, true, apply, apply);
input.value = 'foo';
checkValidity(input, true, apply, apply);
input.value = '2012-06-26';
checkValidity(input, false, apply, apply);
input.min = '2012-02-29';
checkValidity(input, true, apply, apply);
input.value = '2012-02-28';
checkValidity(input, false, apply, apply);
input.value = '1000-01-01';
checkValidity(input, false, apply, apply);
input.value = '20120-01-01';
checkValidity(input, true, apply, apply);
input.min = '0050-01-01';
checkValidity(input, true, apply, apply);
input.value = '0049-01-01';
checkValidity(input, false, apply, apply);
} else {
input.value = '1';
checkValidity(input, true, apply, apply);

View File

@@ -48,6 +48,7 @@ var gInputTestData = [
['email', true],
['url', true],
['number', false],
['date', false],
];
/**
@@ -58,7 +59,6 @@ var gInputTodoData = [
/* type expected result */
['range', false],
['color', false],
['date', false],
['datetime', false],
['month', false],
['week', false],

View File

@@ -261,8 +261,8 @@ var input = document.getElementById('i');
// and |invalidTypes| are the ones which do not accept it.
var validTypes = Array('text', 'password', 'search', 'tel', 'email', 'url');
var barredTypes = Array('hidden', 'reset', 'button', 'submit', 'image');
var invalidTypes = Array('checkbox', 'radio', 'file', 'number');
// TODO: 'datetime', 'date', 'month', 'week', 'time', 'datetime-local',
var invalidTypes = Array('checkbox', 'radio', 'file', 'number', 'date');
// TODO: 'datetime', 'month', 'week', 'time', 'datetime-local',
// 'range', and 'color' do not accept the @pattern too but are not
// implemented yet.

View File

@@ -164,6 +164,8 @@ function checkInputRequiredValidity(type)
element.value = 'http://mozilla.org/';
} else if (element.type == 'number') {
element.value = '42';
} else if (element.type == 'date') {
element.value = '2010-10-10';
} else {
element.value = 'foo';
}
@@ -352,6 +354,9 @@ function checkInputRequiredValidityForFile()
checkNotSufferingFromBeingMissing(element);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkTextareaRequiredValidity();
// The require attribute behavior depend of the input type.
@@ -370,10 +375,10 @@ for (type of typeRequireNotApply) {
}
// Now, checking for all types which accept the required attribute.
// TODO: check 'datetime', 'date', 'month', 'week', 'time' and 'datetime-local'
// TODO: check 'datetime', 'month', 'week', 'time' and 'datetime-local'
// when they will be implemented.
var typeRequireApply = ["text", "password", "search", "tel", "email", "url",
"number"];
"number", "date"];
for (type of typeRequireApply) {
checkInputRequiredValidity(type);
@@ -383,6 +388,9 @@ checkInputRequiredValidityForCheckbox();
checkInputRequiredValidityForRadio();
checkInputRequiredValidityForFile();
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@@ -28,7 +28,7 @@ var types = [
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true, true ],
[ 'date', true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true, true ],
@@ -115,6 +115,132 @@ for (var data of types) {
checkValidity(input, true, apply);
file.remove(false);
} else if (input.type == 'date') {
// For date, the step is calulated on the timestamp since 1970-01-01
// which mean that for all dates prior to the epoch, this timestamp is < 0
// and the behavior might differ, therefore we have to test for these cases.
// When step is 1 every date is valid
input.value = '2012-07-05';
checkValidity(input, true, apply);
input.step = 'foo';
input.value = '1970-01-01';
checkValidity(input, true, apply);
input.step = '-1';
input.value = '1969-12-12';
checkValidity(input, true, apply);
input.removeAttribute('step');
input.value = '1500-01-01';
checkValidity(input, true, apply);
input.step = 'any';
checkValidity(input, true, apply);
input.step = 'aNy';
checkValidity(input, true, apply);
input.step = 'AnY';
checkValidity(input, true, apply);
input.step = 'ANY';
checkValidity(input, true, apply);
// When min is set to a valid date, there is a step base.
input.min = '2008-02-28';
input.step = '2';
input.value = '2008-03-01';
checkValidity(input, true, apply);
input.value = '2008-02-29';
checkValidity(input, false, apply, { low: "2008-02-28", high: "2008-03-01" });
input.min = '2008-02-27';
input.value = '2008-02-28';
checkValidity(input, false, apply, { low: "2008-02-27", high: "2008-02-29" });
input.min = '2009-02-27';
input.value = '2009-02-28';
checkValidity(input, false, apply, { low: "2009-02-27", high: "2009-03-01" });
input.min = '2009-02-01';
input.step = '1.1';
input.value = '2009-02-02';
checkValidity(input, false, apply, { low: "2009-02-01", high: "2009-02-12" });
// Without any step attribute the date is valid
input.removeAttribute('step');
checkValidity(input, true, apply);
input.min = '1950-01-01';
input.step = '366';
input.value = '1951-01-01';
checkValidity(input, false, apply, { low: "1950-01-01", high: "1951-01-02" });
input.min = '1951-01-01';
input.step = '365';
input.value = '1952-01-01';
checkValidity(input, true, apply);
input.step = '0.9';
input.value = '1951-01-02';
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-10" });
input.value = '1951-01-10'
checkValidity(input, true, apply);
input.step = '0.5';
input.value = '1951-01-02';
checkValidity(input, true, apply);
input.step = '1.5';
input.value = '1951-01-03';
checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-04" });
input.value = '1951-01-08';
checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-10" });
input.step = '3000';
input.min= '1968-01-01';
input.value = '1968-05-12';
checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
input.value = '1971-01-01';
checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
input.value = '1991-01-01';
checkValidity(input, false, apply, { low: "1984-06-05", high: "1992-08-22" });
input.value = '1984-06-05';
checkValidity(input, true, apply);
input.value = '1992-08-22';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1991-01-01';
input.value = '1991-01-01';
checkValidity(input, true, apply);
input.value = '1991-01-02';
checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-12" });
input.value = '1991-01-12';
checkValidity(input, true, apply);
input.step = '1.1';
input.min = '1969-12-20';
input.value = '1969-12-20';
checkValidity(input, true, apply);
input.value = '1969-12-21';
checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-31" });
input.value = '1969-12-31';
checkValidity(input, true, apply);
} else {
// When step=0, the allowed step is 1.
input.value = '1.2';

View File

@@ -47,6 +47,7 @@ function checkAvailability()
["reset", false],
["button", false],
["number", true],
["date", true],
// The next types have not been implemented but will fallback to "text"
// which has the same value.
["color", false],
@@ -55,7 +56,6 @@ function checkAvailability()
var todoList =
[
["datetime", true],
["date", true],
["month", true],
["week", true],
["time", true],
@@ -107,12 +107,13 @@ function checkAvailability()
}
}
function checkStepDownForNumber()
function checkStepDown()
{
// This testData is very similar to the one in checkStepUpForNumber
// with some changes relative to stepDown.
// This testData is very similar to the one in checkStepUp with some changes
// relative to stepDown.
var testData = [
/* Initial value | step | min | max | stepDown arg | final value | exception */
{ type: 'number', data: [
// Regular case.
[ '1', null, null, null, null, '0', false ],
// Argument testing.
@@ -178,54 +179,124 @@ function checkStepDownForNumber()
[ '0', 'aNy', null, null, 1, null, true ],
// With @value = step base.
[ '1', '2', null, null, null, '-1', false ],
]},
{ type: 'date', data: [
// Regular case.
[ '2012-07-09', null, null, null, null, '2012-07-08', false ],
// Argument testing.
[ '2012-07-09', null, null, null, 1, '2012-07-08', false ],
[ '2012-07-09', null, null, null, 5, '2012-07-04', false ],
[ '2012-07-09', null, null, null, -1, '2012-07-10', false ],
[ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
// Month/Year wrapping.
[ '2012-08-01', null, null, null, 1, '2012-07-31', false ],
[ '1969-01-02', null, null, null, 4, '1968-12-29', false ],
[ '1969-01-01', null, null, null, -365, '1970-01-01', false ],
[ '2012-02-29', null, null, null, -1, '2012-03-01', false ],
// Float values are rounded to integer (1.1 -> 1).
[ '2012-01-02', null, null, null, 1.1, '2012-01-01', false ],
[ '2012-01-02', null, null, null, 1.9, '2012-01-01', false ],
// With step values.
[ '2012-01-03', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-02', '0.5', null, null, null, '2012-01-01', false ],
[ '2012-01-01', '2', null, null, null, '2011-12-30', false ],
[ '2012-01-02', '0.25',null, null, 4, '2012-01-01', false ],
[ '2012-01-15', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
[ '2012-01-12', '1.1', '2012-01-01', null, 2, '2012-01-01', false ],
[ '2012-01-23', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
[ '2012-01-23', '1.1', '2012-01-01', null, 11, '2012-01-01', false ],
[ '1968-01-12', '1.1', '1968-01-01', null, 8, '1968-01-01', false ],
// step = 0 isn't allowed (-> step = 1).
[ '2012-01-02', '0', null, null, null, '2012-01-01', false ],
// step < 0 isn't allowed (-> step = 1).
[ '2012-01-02', '-1', null, null, null, '2012-01-01', false ],
// step = NaN isn't allowed (-> step = 1).
[ '2012-01-02', 'foo', null, null, null, '2012-01-01', false ],
// Min values testing.
[ '2012-01-03', '1', 'foo', null, 2, '2012-01-01', false ],
[ '2012-01-02', '1', '2012-01-01', null, null, '2012-01-01', false ],
[ '2012-01-01', '1', '2012-01-01', null, null, '2012-01-01', false ],
[ '2012-01-01', '1', '2012-01-10', null, 1, '2012-01-01', false ],
[ '2012-01-05', '3', '2012-01-01', null, null, '2012-01-04', false ],
[ '1969-01-01', '5', '1969-01-01', '1969-01-02', null, '1969-01-01', false ],
// Max values testing.
[ '2012-01-02', '1', null, 'foo', null, '2012-01-01', false ],
[ '2012-01-02', null, null, '2012-01-05', null, '2012-01-01', false ],
[ '2012-01-03', null, null, '2012-01-03', null, '2012-01-02', false ],
[ '2012-01-07', null, null, '2012-01-04', 4, '2012-01-03', false ],
[ '2012-01-07', '2', null, '2012-01-04', 3, '2012-01-01', false ],
// Step mismatch.
[ '2012-01-04', '2', '2012-01-01', null, null, '2012-01-03', false ],
[ '2012-01-06', '2', '2012-01-01', null, 2, '2012-01-03', false ],
[ '2012-01-05', '2', '2012-01-04', '2012-01-08', null, '2012-01-04', false ],
[ '1970-01-04', '2', null, null, null, '1970-01-03', false ],
[ '1970-01-09', '3', null, null, null, '1970-01-07', false ],
// Clamping.
[ '2012-05-01', null, null, '2012-01-05', null, '2012-01-05', false ],
[ '1970-01-05', '2', '1970-01-02', '1970-01-05', null, '1970-01-04', false ],
[ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-01', false ],
[ '1970-01-07', '5', '1969-12-27', '1970-01-06', 2, '1970-01-01', false ],
[ '1970-03-08', '3', '1970-02-01', '1970-02-07', 15, '1970-02-01', false ],
[ '1970-01-10', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '2012-01-01', 'any', null, null, 1, null, true ],
[ '2012-01-01', 'ANY', null, null, 1, null, true ],
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
];
for (var data of testData) {
var element = document.createElement("input");
element.type = 'number';
for (var test of testData) {
for (var data of test.data) {
var element = document.createElement("input");
element.type = test.type;
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepDown(data[4]);
} else {
element.stepDown();
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepDown(data[4]);
} else {
element.stepDown();
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
}
}
}
}
function checkStepUpForNumber()
function checkStepUp()
{
// This testData is very similar to the one in checkStepDownForNumber
// with some changes relative to stepUp.
// This testData is very similar to the one in checkStepDown with some changes
// relative to stepUp.
var testData = [
/* Initial value | step | min | max | stepUp arg | final value | exception */
{ type: 'number', data: [
// Regular case.
[ '1', null, null, null, null, '2', false ],
// Argument testing.
@@ -288,44 +359,116 @@ function checkStepUpForNumber()
[ '0', 'aNy', null, null, 1, null, true ],
// With @value = step base.
[ '1', '2', null, null, null, '3', false ],
]},
{ type: 'date', data: [
// Regular case.
[ '2012-07-09', null, null, null, null, '2012-07-10', false ],
// Argument testing.
[ '2012-07-09', null, null, null, 1, '2012-07-10', false ],
[ '2012-07-09', null, null, null, 9, '2012-07-18', false ],
[ '2012-07-09', null, null, null, -1, '2012-07-08', false ],
[ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
// Month/Year wrapping.
[ '2012-07-31', null, null, null, 1, '2012-08-01', false ],
[ '1968-12-29', null, null, null, 4, '1969-01-02', false ],
[ '1970-01-01', null, null, null, -365, '1969-01-01', false ],
[ '2012-03-01', null, null, null, -1, '2012-02-29', false ],
// Float values are rounded to integer (1.1 -> 1).
[ '2012-01-01', null, null, null, 1.1, '2012-01-02', false ],
[ '2012-01-01', null, null, null, 1.9, '2012-01-02', false ],
// With step values.
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
[ '2012-01-01', '2', null, null, null, '2012-01-03', false ],
[ '2012-01-01', '0.25', null, null, 4, '2012-01-02', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 1, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 2, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 10, '2012-01-12', false ],
[ '2012-01-01', '1.1', '2012-01-01', null, 11, '2012-01-23', false ],
[ '1968-01-01', '1.1', '1968-01-01', null, 8, '1968-01-12', false ],
// step = 0 isn't allowed (-> step = 1).
[ '2012-01-01', '0', null, null, null, '2012-01-02', false ],
// step < 0 isn't allowed (-> step = 1).
[ '2012-01-01', '-1', null, null, null, '2012-01-02', false ],
// step = NaN isn't allowed (-> step = 1).
[ '2012-01-01', 'foo', null, null, null, '2012-01-02', false ],
// Min values testing.
[ '2012-01-01', '1', 'foo', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2011-12-01', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-02', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-01', null, null, '2012-01-02', false ],
[ '2012-01-01', null, '2012-01-04', null, 4, '2012-01-05', false ],
[ '2012-01-01', '2', '2012-01-04', null, 3, '2012-01-06', false ],
// Max values testing.
[ '2012-01-01', '1', null, 'foo', 2, '2012-01-03', false ],
[ '2012-01-01', '1', null, '2012-01-10', 1, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-01', null, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-02', null, '2012-01-02', false ],
[ '2012-01-02', null, null, '2012-01-02', null, '2012-01-02', false ],
[ '1969-01-02', '5', '1969-01-01', '1969-01-02', null, '1969-01-02', false ],
// Step mismatch.
[ '2012-01-02', '2', '2012-01-01', null, null, '2012-01-03', false ],
[ '2012-01-02', '2', '2012-01-01', null, 2, '2012-01-05', false ],
[ '2012-01-05', '2', '2012-01-01', '2012-01-06', null, '2012-01-05', false ],
[ '1970-01-02', '2', null, null, null, '1970-01-03', false ],
[ '1970-01-05', '3', null, null, null, '1970-01-07', false ],
[ '1970-01-03', '3', null, null, null, '1970-01-04', false ],
[ '1970-01-03', '3', '1970-01-02', null, null, '1970-01-05', false ],
// Clamping.
[ '2012-01-01', null, '2012-01-31', null, null, '2012-01-31', false ],
[ '1970-01-02', '2', '1970-01-01', '1970-01-04', null, '1970-01-03', false ],
[ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-07', false ],
[ '1969-12-28', '5', '1969-12-29', '1970-01-06', 3, '1970-01-03', false ],
[ '1970-01-01', '3', '1970-02-01', '1970-02-07', 15, '1970-02-07', false ],
[ '1970-01-01', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
// value = "" (NaN).
[ '', null, null, null, null, '', false ],
// With step = 'any'.
[ '2012-01-01', 'any', null, null, 1, null, true ],
[ '2012-01-01', 'ANY', null, null, 1, null, true ],
[ '2012-01-01', 'AnY', null, null, 1, null, true ],
[ '2012-01-01', 'aNy', null, null, 1, null, true ],
]},
];
for (var data of testData) {
var element = document.createElement("input");
element.type = 'number';
for (var test of testData) {
for (var data of test.data) {
var element = document.createElement("input");
element.type = test.type;
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepUp(data[4]);
} else {
element.stepUp();
if (data[0] != null) {
element.setAttribute('value', data[0]);
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
if (data[1] != null) {
element.step = data[1];
}
if (data[2] != null) {
element.min = data[2];
}
if (data[3] != null) {
element.max = data[3];
}
var exceptionCaught = false;
try {
if (data[4] != null) {
element.stepUp(data[4]);
} else {
element.stepUp();
}
is(element.value, data[5], "The value should be " + data[5]);
} catch (e) {
exceptionCaught = true;
is(element.value, data[0], e.name + "The value should not have changed");
is(e.name, 'InvalidStateError',
"It should be a InvalidStateError exception.");
} finally {
is(exceptionCaught, data[6], "exception status should be " + data[6]);
}
}
}
}
@@ -335,8 +478,9 @@ SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function(
checkPresence();
checkAvailability();
checkStepDownForNumber();
checkStepUpForNumber();
checkStepDown();
checkStepUp();
SimpleTest.finish();
});

View File

@@ -0,0 +1,206 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=769370
-->
<head>
<title>Test for input.valueAsDate</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=769370">Mozilla Bug 769370</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 769370**/
/**
* This test is checking .valueAsDate.
*/
var element = document.createElement("input");
function checkAvailability()
{
var testData =
[
["text", false],
["password", false],
["search", false],
["telephone", false],
["email", false],
["url", false],
["hidden", false],
["checkbox", false],
["radio", false],
["file", false],
["submit", false],
["image", false],
["reset", false],
["button", false],
["number", false],
["date", true],
// The next types have not been implemented but will fallback to "text"
// which has the same value.
["range", false],
["color", false],
];
var todoList =
[
["datetime", true],
["month", true],
["week", true],
["time", true],
["datetime-local", true],
];
for (data of testData) {
var exceptionCatched = false;
element.type = data[0];
try {
element.valueAsDate;
} catch (e) {
exceptionCatched = true;
}
is(exceptionCatched, false,
"valueAsDate shouldn't throw exception on getting");
exceptionCatched = false;
try {
element.valueAsDate = new Date();
} catch (e) {
exceptionCatched = true;
}
is(exceptionCatched, !data[1], "valueAsDate for " + data[0] +
" availability is not correct");
}
for (data of todoList) {
var exceptionCatched = false;
element.type = data[0];
try {
element.valueAsDate;
} catch (e) {
exceptionCatched = true;
}
is(exceptionCatched, false,
"valueAsDate shouldn't throw exception on getting");
exceptionCatched = false;
try {
element.valueAsDate= 42;
} catch (e) {
exceptionCatched = true;
}
todo_is(exceptionCatched, !data[1],
"valueAsDate for " + data[0] + " availability is not correct");
}
}
function checkGet()
{
var validData =
[
[ "2012-07-12", 1342051200000 ],
[ "1970-01-01", 0 ],
[ "1970-01-02", 86400000 ],
[ "1969-12-31", -86400000 ],
[ "0311-01-31", -52350451200000 ],
[ "275760-09-13", 8640000000000000 ],
[ "0001-01-01", -62135596800000 ],
[ "2012-02-29", 1330473600000 ],
[ "2011-02-28", 1298851200000 ],
];
var invalidData =
[
[ "invaliddate" ],
[ "-001-12-31" ],
[ "901-12-31" ],
[ "1901-13-31" ],
[ "1901-12-32" ],
[ "1901-00-12" ],
[ "1901-01-00" ],
[ "1900-02-29" ],
[ "0000-01-01" ],
[ "" ],
// This date is valid for the input element, but is out of
// the date object range. In this case, on getting valueAsDate,
// a Date object will be created, but it will have a NaN internal value,
// and will return the string "Invalid Date".
[ "275760-09-14", true ],
];
element.type = "date";
for (data of validData) {
element.value = data[0];
is(element.valueAsDate.valueOf(), data[1],
"valueAsDate should return the " +
"valid date object representing this date");
}
for (data of invalidData) {
element.value = data[0];
is(element.valueAsDate, data[1] ? "Invalid Date" : null,
"valueAsDate should return null " +
"when the element value is not a valid date");
}
}
function checkSet()
{
var testData =
[
[ 1342051200000, "2012-07-12" ],
[ 0, "1970-01-01" ],
// Maximum valid date (limited by the ecma date object range).
[ 8640000000000000, "275760-09-13" ],
// Minimum valid date (limited by the input element minimum valid value).
[ -62135596800000 , "0001-01-01" ],
[ 1330473600000, "2012-02-29" ],
[ 1298851200000, "2011-02-28" ],
// "Values must be truncated to valid dates"
[ 42.1234, "1970-01-01" ],
[ 123.123456789123, "1970-01-01" ],
[ 1e-1, "1970-01-01" ],
[ 1298851200010, "2011-02-28" ],
[ -1, "1969-12-31" ],
[ -86400000, "1969-12-31" ],
[ 86400000, "1970-01-02" ],
// Negative years, this is out of range for the input element,
// the corresponding date string is the empty string
[ -62135596800001, "" ],
// Invalid dates.
[ NaN, "" ],
];
element.type = "date";
for (data of testData) {
element.valueAsDate = new Date(data[0]);
is(element.value, data[1], "valueAsDate should set the value to "
+ data[1]);
}
element.valueAsDate = null;
is(element.value, "", "valueAsDate should set the value to the empty string");
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkAvailability();
checkGet();
checkSet();
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@@ -4,7 +4,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=636737
-->
<head>
<title>Test for Bug 636737</title>
<title>Test for Bug input.valueAsNumber</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
@@ -43,6 +43,7 @@ function checkAvailability()
["reset", false],
["button", false],
["number", true],
["date", true],
// The next types have not been implemented but will fallback to "text"
// which has the same value.
["color", false],
@@ -51,7 +52,6 @@ function checkAvailability()
var todoList =
[
["datetime", true],
["date", true],
["month", true],
["week", true],
["time", true],
@@ -103,7 +103,7 @@ function checkAvailability()
}
}
function checkGet()
function checkNumberGet()
{
var testData =
[
@@ -126,6 +126,8 @@ function checkGet()
element.type = "number";
for (data of testData) {
element.value = data[0];
// Given that NaN != NaN, we have to use null when the expected value is NaN.
if (data[1] != null) {
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
"floating point representation of the value");
@@ -136,7 +138,7 @@ function checkGet()
}
}
function checkSet()
function checkNumberSet()
{
var testData =
[
@@ -148,29 +150,156 @@ function checkSet()
[2e1, "20"],
[1e-1, "0.1"], // value after e can be negative
[1E2, "100"], // E can be used instead of e
["", null], // the empty string is not a number
["foo", null],
// Setting a string will set NaN.
["foo", ""],
// "" is converted to 0.
["", 0],
[42, "42"], // Keep this here, it is used by the next test.
// Setting Infinity should throw and not change the current value.
[Infinity, 42, true],
[-Infinity, 42, true],
// Setting NaN should change the value to the empty string.
[NaN, ""],
];
element.type = "number";
for (data of testData) {
element.valueAsNumber = data[0];
if (data[1] != null) {
var caught = false;
try {
element.valueAsNumber = data[0];
is(element.value, data[1],
"valueAsNumber should be able to set the value");
} catch (e) {
caught = true;
}
if (data[2]) {
ok(caught, "valueAsNumber should have thrown");
is(element.value, data[1], "value should not have changed");
} else {
element.valueAsNumber = testData[0];
isnot(element.value, data[1],
"valueAsNumber should not set the value if it's not a number");
ok(!caught, "valueAsNumber should not have thrown");
}
}
}
function checkDateGet()
{
var validData =
[
[ "2012-07-12", 1342051200000 ],
[ "1970-01-01", 0 ],
// We are supposed to support at least until this date.
// (corresponding to the date object maximal value)
[ "275760-09-13", 8640000000000000 ],
// Minimum valid date (limited by the input element minimum valid value)
[ "0001-01-01", -62135596800000 ],
[ "2012-02-29", 1330473600000 ],
[ "2011-02-28", 1298851200000 ],
];
var invalidData =
[
"invaliddate",
"",
"275760-09-14",
"999-12-31",
"-001-12-31",
"0000-01-01",
"2011-02-29",
"1901-13-31",
"1901-12-32",
"1901-00-12",
"1901-01-00",
"1900-02-29",
];
element.type = "date";
for (data of validData) {
element.value = data[0];
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
"timestamp representing this date");
}
for (data of invalidData) {
element.value = data;
ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
"when the element value is not a valid date");
}
}
function checkDateSet()
{
var testData =
[
[ 1342051200000, "2012-07-12" ],
[ 0, "1970-01-01" ],
// Maximum valid date (limited by the ecma date object range).
[ 8640000000000000, "275760-09-13" ],
// Minimum valid date (limited by the input element minimum valid value)
[ -62135596800000, "0001-01-01" ],
[ 1330473600000, "2012-02-29" ],
[ 1298851200000, "2011-02-28" ],
// "Values must be truncated to valid dates"
[ 42.1234, "1970-01-01" ],
[ 123.123456789123, "1970-01-01" ],
[ 1e2, "1970-01-01" ],
[ 1E9, "1970-01-12" ],
[ 1e-1, "1970-01-01" ],
[ 2e10, "1970-08-20" ],
[ 1298851200010, "2011-02-28" ],
[ -1, "1969-12-31" ],
[ -86400000, "1969-12-31" ],
[ 86400000, "1970-01-02" ],
// Invalid numbers.
// Those are implicitly converted to numbers
[ "", "1970-01-01" ],
[ true, "1970-01-01" ],
[ false, "1970-01-01" ],
[ null, "1970-01-01" ],
// Those are converted to NaN, the corresponding date string is the empty string
[ "invaliddatenumber", "" ],
[ NaN, "" ],
[ undefined, "" ],
// Out of range, the corresponding date string is the empty string
[ -62135596800001, "" ],
// Infinity will keep the current value and throw (so we need to set a current value).
[ 1298851200010, "2011-02-28" ],
[ Infinity, "2011-02-28", true ],
[ -Infinity, "2011-02-28", true ],
];
element.type = "date";
for (data of testData) {
var caught = false;
try {
element.valueAsNumber = data[0];
is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
} catch(e) {
caught = true;
}
if (data[2]) {
ok(caught, "valueAsNumber should have trhown");
is(element.value, data[1], "the value should not have changed");
} else {
ok(!caught, "valueAsNumber should not have thrown");
}
}
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
checkAvailability();
checkGet();
checkSet();
// <input type='number'> test
checkNumberGet();
checkNumberSet();
// <input type='date'> test
checkDateGet();
checkDateSet();
SimpleTest.finish();
});

View File

@@ -35,13 +35,17 @@ var testData = [
[ "search", true ],
[ "password", true ],
[ "number", true ],
[ "date", true ],
// 'file' is treated separatly.
];
var todoTypes = [
"datetime", "date", "month", "week", "time", "datetime-local", "range", "color"
"datetime", "month", "week", "time", "datetime-local", "range", "color"
];
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
var length = testData.length;
for (var i=0; i<length; ++i) {
for (var j=0; j<length; ++j) {
@@ -49,8 +53,13 @@ for (var i=0; i<length; ++i) {
e.type = testData[i][0];
var expectedValue;
if (testData[i][0] == 'number' || testData[j][0] == 'number') {
if ((testData[i][0] == 'number' && testData[j][0] == 'date') ||
(testData[i][0] == 'date' && testData[j][0] == 'number')) {
expectedValue = '';
} else if (testData[i][0] == 'number' || testData[j][0] == 'number') {
expectedValue = '42';
} else if (testData[i][0] == 'date' || testData[j][0] == 'date') {
expectedValue = '2012-12-21';
} else {
expectedValue = "foo";
}
@@ -87,6 +96,9 @@ for (var type of todoTypes) {
todo_is(e.type, type, type + " type isn't supported yet");
}
SimpleTest.finish();
});
</script>
</pre>
</body>

View File

@@ -69,6 +69,7 @@ interface nsIDOMHTMLInputElement : nsIDOMHTMLElement
attribute DOMString defaultValue;
attribute DOMString value;
attribute double valueAsNumber;
[implicit_jscontext] attribute jsval valueAsDate;
[optional_argc] void stepDown([optional] in long n);
[optional_argc] void stepUp([optional] in long n);

View File

@@ -3264,6 +3264,7 @@ nsWebBrowserPersist::CloneNodeWithFixedUpAttributes(
case NS_FORM_INPUT_TEL:
case NS_FORM_INPUT_URL:
case NS_FORM_INPUT_NUMBER:
case NS_FORM_INPUT_DATE:
nodeAsInput->GetValue(valueStr);
// Avoid superfluous value="" serialization
if (valueStr.IsEmpty())

View File

@@ -3432,6 +3432,8 @@ nsCSSFrameConstructor::FindInputData(Element* aElement,
SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
// TODO: this is temporary until a frame is written: bug 635240.
SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewTextControlFrame),
// TODO: this is temporary until a frame is written: bug 773205.
SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame),
{ NS_FORM_INPUT_SUBMIT,
FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
nsCSSAnonBoxes::buttonContent) },

View File

@@ -92,6 +92,12 @@ Form History test: form field autocomplete
<button type="submit">Submit</button>
</form>
<!-- form with input type='date' -->
<form id="form14" onsubmit="return false;">
<input type="date" name="field11">
<button type="submit">Submit</button>
</form>
</div>
<pre id="test">
@@ -134,10 +140,11 @@ fh.addEntry("field7", "value");
fh.addEntry("field8", "value");
fh.addEntry("field9", "value");
fh.addEntry("field10", "42");
fh.addEntry("field11", "2010-10-10");
fh.addEntry("searchbar-history", "blacklist test");
// All these non-implemeted types might need autocomplete tests in the future.
var todoTypes = [ "datetime", "date", "month", "week", "time", "datetime-local",
var todoTypes = [ "datetime", "month", "week", "time", "datetime-local",
"range", "color" ];
var todoInput = document.createElement("input");
for (var type of todoTypes) {
@@ -730,6 +737,17 @@ function runTest(testNum) {
doKey("return");
checkForm("42");
input = $_(14, "field11");
restoreForm();
doKey("down");
break;
case 405:
checkMenuEntries(["2010-10-10"]);
doKey("down");
doKey("return");
checkForm("2010-10-10");
// Go to test 500.
fh.addEntry("field1", "value1");
input = $_(1, "field1");