Bug 1278192 - Implement the value sanitizing algorithm for <input type=week>. r=smaug

--HG--
extra : source : 6ee805904d2d54728be175ddb60466acbb78c242
This commit is contained in:
Jessica Jong 2016-09-08 15:39:01 -07:00
parent 89a07725ed
commit c51974390f
9 changed files with 202 additions and 95 deletions

View File

@ -217,6 +217,7 @@ const Decimal HTMLInputElement::kStepAny = Decimal(0);
const double HTMLInputElement::kMaximumYear = 275760;
const double HTMLInputElement::kMinimumYear = 1;
const double HTMLInputElement::kMaximumWeekInYear = 53;
#define NS_INPUT_ELEMENT_STATE_IID \
{ /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \
@ -5047,6 +5048,13 @@ HTMLInputElement::SanitizeValue(nsAString& aValue)
}
}
break;
case NS_FORM_INPUT_WEEK:
{
if (!aValue.IsEmpty() && !IsValidWeek(aValue)) {
aValue.Truncate();
}
}
break;
case NS_FORM_INPUT_COLOR:
{
if (IsValidSimpleColor(aValue)) {
@ -5076,6 +5084,43 @@ bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const
return true;
}
bool
HTMLInputElement::IsLeapYear(uint32_t aYear) const
{
if ((aYear % 4 == 0 && aYear % 100 != 0) || ( aYear % 400 == 0)) {
return true;
}
return false;
}
uint32_t
HTMLInputElement::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay) const
{
// Tomohiko Sakamoto algorithm.
int monthTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
aYear -= aMonth < 3;
return (aYear + aYear / 4 - aYear / 100 + aYear / 400 +
monthTable[aMonth - 1] + aDay) % 7;
}
uint32_t
HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const
{
int day = DayOfWeek(aYear, 1, 1); // January 1.
// A year starting on Thursday or a leap year starting on Wednesday has 53
// weeks. All other years have 52 weeks.
return day == 4 || (day == 3 && IsLeapYear(aYear)) ? kMaximumWeekInYear
: kMaximumWeekInYear - 1;
}
bool
HTMLInputElement::IsValidWeek(const nsAString& aValue) const
{
uint32_t year, week;
return ParseWeek(aValue, &year, &week);
}
bool
HTMLInputElement::IsValidMonth(const nsAString& aValue) const
{
@ -5123,6 +5168,34 @@ bool HTMLInputElement::ParseMonth(const nsAString& aValue,
*aMonth > 0 && *aMonth <= 12;
}
bool HTMLInputElement::ParseWeek(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aWeek) const
{
// Parse the year, month values out a string formatted as 'yyyy-Www'.
if (aValue.Length() < 8) {
return false;
}
uint32_t endOfYearOffset = aValue.Length() - 4;
if (aValue[endOfYearOffset] != '-') {
return false;
}
if (aValue[endOfYearOffset + 1] != 'W') {
return false;
}
const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
if (!ParseYear(yearStr, aYear)) {
return false;
}
return DigitSubStringToNumber(aValue, endOfYearOffset + 2, 2, aWeek) &&
*aWeek > 0 && *aWeek <= MaximumWeekInYear(*aYear);
}
bool HTMLInputElement::ParseDate(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aMonth,

View File

@ -1165,7 +1165,15 @@ protected:
bool IsValidSimpleColor(const nsAString& aValue) const;
/**
* Parse a date string of the form yyyy-mm
* Parse a week string of the form yyyy-Www
* @param the string to be parsed.
* @return whether the string is a valid week.
* Note : this function does not consider the empty string as valid.
*/
bool IsValidWeek(const nsAString& aValue) const;
/**
* Parse a month string of the form yyyy-mm
* @param the string to be parsed.
* @return whether the string is a valid month.
* Note : this function does not consider the empty string as valid.
@ -1201,6 +1209,16 @@ protected:
uint32_t* aYear,
uint32_t* aMonth) const;
/**
* Parse a week string of the form yyyy-Www
*
* @param the string to be parsed.
* @return the year and week in aYear and aWeek.
* @return whether the parsing was successful.
*/
bool ParseWeek(const nsAString& aValue,
uint32_t* aYear,
uint32_t* aWeek) const;
/**
* Parse a date string of the form yyyy-mm-dd
*
@ -1224,6 +1242,22 @@ protected:
*/
int32_t MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const;
/**
* This methods returns the day of the week given a date, note that 0 = Sunday.
*/
uint32_t DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay) const;
/**
* This methods returns the maximum number of week in a given year, the
* result is either 52 or 53.
*/
uint32_t MaximumWeekInYear(uint32_t aYear) const;
/**
* This methods returns true if it's a leap year.
*/
bool IsLeapYear(uint32_t aYear) const;
/**
* Returns whether aValue is a valid time as described by HTML specifications:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-time-string
@ -1460,6 +1494,8 @@ protected:
static const double kMaximumYear;
// Minimum year limited by HTML standard, year >= 1.
static const double kMinimumYear;
// Long years in a ISO calendar have 53 weeks in them.
static const double kMaximumWeekInYear;
/**
* The type of this input (<input type=...>) as an integer.

View File

@ -74,7 +74,7 @@ var inputTypes =
[
"text", "password", "search", "tel", "hidden", "checkbox", "radio",
"submit", "image", "reset", "button", "email", "url", "number", "date",
"time", "range", "color", "month"
"time", "range", "color", "month", "week"
];
var todoTypes =
@ -191,6 +191,36 @@ function sanitizeValue(aType, aValue)
}
return aValue;
case "week":
// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-week-string
function isLeapYear(aYear) {
return ((aYear % 4 == 0) && (aYear % 100 != 0)) || (aYear % 400 == 0);
}
function getDayofWeek(aYear, aMonth, aDay) { /* 0 = Sunday */
// Tomohiko Sakamoto algorithm.
var monthTable = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
aYear -= Number(aMonth < 3);
return (aYear + parseInt(aYear / 4) - parseInt(aYear / 100) +
parseInt(aYear / 400) + monthTable[aMonth - 1] + aDay) % 7;
}
function getMaximumWeekInYear(aYear) {
var day = getDayofWeek(aYear, 1, 1);
return day == 4 || (day == 3 && isLeapYear(aYear)) ? 53 : 52;
}
var match = /^([0-9]{4,})-W([0-9]{2})$/.exec(aValue);
if (!match) {
return "";
}
var year = Number(match[1]);
if (year === 0) {
return "";
}
var week = Number(match[2]);
if (week > 53 || month < 1) {
return "";
}
return 1 <= week && week <= getMaximumWeekInYear(year) ? aValue : "";
case "datetime":
case "datetime-local":
// TODO: write the sanitize algorithm.
@ -365,6 +395,27 @@ function checkSanitizing(element, inputTypeDescription)
"2013 - 03",
"2013 03",
"2013/03",
// For week
"1970-W01",
"1970-W53",
"1964-W53",
"1900-W10",
"2004-W53",
"2065-W53",
"2099-W53",
"2010-W53",
"2016-W30",
"1900-W3",
"2016-w30",
"2016-30",
"16-W30",
"2016-Week30",
"2000-100",
"0000-W01",
"00-W01",
"123456-W05",
"1985-W100",
"week",
];
for (value of testData) {

View File

@ -198,6 +198,24 @@ function runTest()
'2012-1',
]
},
{
type: 'week',
validData: [
'0001-W01',
'1970-W53',
'100000-W52',
'2016-W30',
],
invalidData: [
'1-W01',
'week',
'2016-30',
'2010-W80',
'2000/W30',
'1985-W00',
'1000-W'
]
},
{ type: 'datetime', todo: true },
{ type: 'datetime-local', todo: true },
];

View File

@ -48,7 +48,7 @@ var todoTypes = [
"datetime", "datetime-local"
];
var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month' ];
var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month', 'week' ];
var length = testData.length;
for (var i=0; i<length; ++i) {
@ -62,7 +62,7 @@ for (var i=0; i<length; ++i) {
// number. We need to handle that specially.
if (testData[j][0] == 'range' || testData[i][0] == 'range') {
if (testData[j][0] == 'date' || testData[j][0] == 'time' ||
testData[j][0] == 'month') {
testData[j][0] == 'month' || testData[j][0] == 'week') {
expectedValue = '';
} else if (testData[j][0] == 'color') {
expectedValue = '#000000';
@ -71,7 +71,8 @@ for (var i=0; i<length; ++i) {
}
} else if (testData[i][0] == 'color' || testData[j][0] == 'color') {
if (testData[j][0] == 'number' || testData[j][0] == 'date' ||
testData[j][0] == 'time' || testData[j][0] == 'month') {
testData[j][0] == 'time' || testData[j][0] == 'month' ||
testData[j][0] == 'week') {
expectedValue = ''
} else {
expectedValue = '#000000';
@ -87,6 +88,8 @@ for (var i=0; i<length; ++i) {
expectedValue = '21:21';
} else if (testData[i][0] == 'month' || testData[j][0] == 'month') {
expectedValue = '2013-03';
} else if (testData[i][0] == 'week' || testData[j][0] == 'week') {
expectedValue = '2016-W35';
} else {
expectedValue = "foo";
}

View File

@ -2,19 +2,3 @@
type: testharness
[[INPUT in DATETIME status\] The datetime type must be supported.]
expected: FAIL
[[INPUT in WEEK status\] The value attribute is a number(1234567)]
expected: FAIL
[[INPUT in WEEK status\] The value attribute is a Date object]
expected: FAIL
[[INPUT in WEEK status\] Invalid week string(2000-W99)]
expected: FAIL
[[INPUT in WEEK status\] invalid week string(2000-W00)]
expected: FAIL
[[INPUT in WEEK status\] invalid week string(2000-w01)]
expected: FAIL

View File

@ -3,18 +3,12 @@
[change state from hidden to datetime]
expected: FAIL
[change state from hidden to week]
expected: FAIL
[change state from text to hidden]
expected: FAIL
[change state from text to datetime]
expected: FAIL
[change state from text to week]
expected: FAIL
[change state from text to checkbox]
expected: FAIL
@ -39,9 +33,6 @@
[change state from search to datetime]
expected: FAIL
[change state from search to week]
expected: FAIL
[change state from search to checkbox]
expected: FAIL
@ -66,9 +57,6 @@
[change state from tel to datetime]
expected: FAIL
[change state from tel to week]
expected: FAIL
[change state from tel to checkbox]
expected: FAIL
@ -105,9 +93,6 @@
[change state from url to datetime]
expected: FAIL
[change state from url to week]
expected: FAIL
[change state from url to checkbox]
expected: FAIL
@ -144,9 +129,6 @@
[change state from email to datetime]
expected: FAIL
[change state from email to week]
expected: FAIL
[change state from email to checkbox]
expected: FAIL
@ -171,9 +153,6 @@
[change state from password to datetime]
expected: FAIL
[change state from password to week]
expected: FAIL
[change state from password to checkbox]
expected: FAIL
@ -195,9 +174,6 @@
[change state from datetime to hidden]
expected: FAIL
[change state from datetime to week]
expected: FAIL
[change state from datetime to checkbox]
expected: FAIL
@ -297,7 +273,22 @@
[change state from week to hidden]
expected: FAIL
[change state from week to datetime]
[change state from week to text]
expected: FAIL
[change state from week to search]
expected: FAIL
[change state from week to tel]
expected: FAIL
[change state from week to url]
expected: FAIL
[change state from week to email]
expected: FAIL
[change state from week to password]
expected: FAIL
[change state from week to checkbox]
@ -420,9 +411,6 @@
[change state from range to datetime]
expected: FAIL
[change state from range to week]
expected: FAIL
[change state from range to number]
expected: FAIL
@ -468,9 +456,6 @@
[change state from color to datetime]
expected: FAIL
[change state from color to week]
expected: FAIL
[change state from color to checkbox]
expected: FAIL
@ -492,15 +477,9 @@
[change state from checkbox to datetime]
expected: FAIL
[change state from checkbox to week]
expected: FAIL
[change state from radio to datetime]
expected: FAIL
[change state from radio to week]
expected: FAIL
[change state from file to hidden]
expected: FAIL
@ -567,24 +546,11 @@
[change state from submit to datetime]
expected: FAIL
[change state from submit to week]
expected: FAIL
[change state from image to datetime]
expected: FAIL
[change state from image to week]
expected: FAIL
[change state from reset to datetime]
expected: FAIL
[change state from reset to week]
expected: FAIL
[change state from button to datetime]
expected: FAIL
[change state from button to week]
expected: FAIL

View File

@ -12,12 +12,6 @@
[value IDL attribute of input type datetime with value attribute]
expected: FAIL
[value IDL attribute of input type week without value attribute]
expected: FAIL
[value IDL attribute of input type week with value attribute]
expected: FAIL
[value IDL attribute of input type checkbox without value attribute]
expected: FAIL

View File

@ -1,23 +1,5 @@
[week.html]
type: testharness
[2014 has 52 weeks: Value should be empty]
expected: FAIL
[Invalid value: year only]
expected: FAIL
[Invalid value: no week number]
expected: FAIL
[Invalid value: no '-' (U+002D)]
expected: FAIL
[Invalid value: yearless week]
expected: FAIL
[Invalid value: yearless week and no '-' (U+002D)]
expected: FAIL
[Value < min attribute]
expected: FAIL