Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE HTML>
<html>
<!--
-->
<head>
<title>Test for Bug 549475</title>
<script 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=549475">Mozilla Bug 549475</a>
<p id="display"></p>
<pre id="test">
<div id='content'>
<form>
</form>
</div>
<script type="application/javascript">
SimpleTest.requestLongerTimeout(2);
/**
* This files tests the 'value sanitization algorithm' for the various input
* types. Note that an input's value is affected by more than just its type's
* value sanitization algorithm; e.g. some type=range has actions that the user
* agent must perform to change the element's value to avoid underflow/overflow
* and step mismatch (when possible). We specifically avoid triggering these
* other actions here so that this test only tests the value sanitization
* algorithm for the various input types.
*
* XXXjwatt splitting out testing of the value sanitization algorithm and
* "other things" that affect .value makes it harder to know what we're testing
* and what we've missed, because what's included in the value sanitization
* algorithm and what's not is different from input type to input type. It
* seems to me it would be better to have a test (maybe one per type) focused
* on testing .value for permutations of all other inputs that can affect it.
* The value sanitization algorithm is just an internal spec concept after all.
*/
// We buffer up the results of sets of sub-tests, and avoid outputting log
// entries for them all if they all pass. Otherwise, we have an enormous amount
// of test output.
var delayedTests = [];
var anyFailedDelayedTests = false;
function delayed_is(actual, expected, description)
{
var result = actual == expected;
delayedTests.push({ actual, expected, description });
if (!result) {
anyFailedDelayedTests = true;
}
}
function flushDelayedTests(description)
{
if (anyFailedDelayedTests) {
info("Outputting individual results for \"" + description + "\" due to failures in subtests");
for (var test of delayedTests) {
is(test.actual, test.expected, test.description);
}
} else {
ok(true, description + " (" + delayedTests.length + " subtests)");
}
delayedTests = [];
anyFailedDelayedTests = false;
}
// We are excluding "file" because it's too different from the other types.
// And it has no sanitizing algorithm.
var inputTypes =
[
"text", "password", "search", "tel", "hidden", "checkbox", "radio",
"submit", "image", "reset", "button", "email", "url", "number", "date",
"time", "range", "color", "month", "week", "datetime-local"
];
var valueModeValue =
[
"text", "search", "url", "tel", "email", "password", "date", "datetime",
"month", "week", "time", "datetime-local", "number", "range", "color",
];
function sanitizeDate(aValue)
{
function getNumbersOfDaysInMonth(aMonth, aYear) {
if (aMonth === 2) {
return (aYear % 400 === 0 || (aYear % 100 != 0 && aYear % 4 === 0)) ? 29 : 28;
}
return (aMonth === 1 || aMonth === 3 || aMonth === 5 || aMonth === 7 ||
aMonth === 8 || aMonth === 10 || aMonth === 12) ? 31 : 30;
}
var match = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/.exec(aValue);
if (!match) {
return "";
}
var year = Number(match[1]);
if (year === 0) {
return "";
}
var month = Number(match[2]);
if (month > 12 || month < 1) {
return "";
}
var day = Number(match[3]);
return 1 <= day && day <= getNumbersOfDaysInMonth(month, year) ? aValue : "";
}
function sanitizeTime(aValue)
{
var match = /^([0-9]{2}):([0-9]{2})(.*)$/.exec(aValue);
if (!match) {
return "";
}
var hours = match[1];
if (hours < 0 || hours > 23) {
return "";
}
var minutes = match[2];
if (minutes < 0 || minutes > 59) {
return "";
}
var other = match[3];
if (other == "") {
return aValue;
}
match = /^:([0-9]{2})(.*)$/.exec(other);
if (!match) {
return "";
}
var seconds = match[1];
if (seconds < 0 || seconds > 59) {
return "";
}
var other = match[2];
if (other == "") {
return aValue;
}
match = /^.([0-9]{1,3})$/.exec(other);
if (!match) {
return "";
}
return aValue;
}
function sanitizeDateTimeLocal(aValue)
{
if (aValue.length < 16) {
return "";
}
var sepIndex = aValue.indexOf("T");
if (sepIndex == -1) {
sepIndex = aValue.indexOf(" ");
if (sepIndex == -1) {
return "";
}
}
var [date, time] = aValue.split(aValue[sepIndex]);
if (!sanitizeDate(date)) {
return "";
}
if (!sanitizeTime(time)) {
return "";
}
// Normalize datetime-local string.
if (aValue[sepIndex] == " ") {
aValue = date + "T" + time;
}
if ((aValue.length - sepIndex) == 6) {
return aValue;
}
if ((aValue.length - sepIndex) > 9) {
var milliseconds = aValue.substring(sepIndex + 10);
if (Number(milliseconds) != 0) {
return aValue;
}
aValue = aValue.slice(0, sepIndex + 9);
}
var seconds = aValue.substring(sepIndex + 7);
if (Number(seconds) != 0) {
return aValue;
}
aValue = aValue.slice(0, sepIndex + 6);
return aValue;
}
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(/^[\u0020\u0009\t\u000a\u000c\u000d]+|[\u0020\u0009\t\u000a\u000c\u000d]+$/g, "");
case "number":
return isNaN(Number(aValue)) ? "" : aValue;
case "range":
var defaultMinimum = 0;
var defaultMaximum = 100;
var value = Number(aValue);
if (isNaN(value)) {
return ((defaultMaximum - defaultMinimum)/2).toString(); // "50"
}
if (value < defaultMinimum) {
return defaultMinimum.toString();
}
if (value > defaultMaximum) {
return defaultMaximum.toString();
}
return aValue;
case "date":
return sanitizeDate(aValue);
case "time":
return sanitizeTime(aValue);
case "month":
var match = /^([0-9]{4,})-([0-9]{2})$/.exec(aValue);
if (!match) {
return "";
}
var year = Number(match[1]);
if (year === 0) {
return "";
}
var month = Number(match[2]);
if (month > 12 || month < 1) {
return "";
}
return aValue;
case "week":
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-local":
return sanitizeDateTimeLocal(aValue);
case "color":
return /^#[0-9A-Fa-f]{6}$/.exec(aValue) ? aValue.toLowerCase() : "#000000";
default:
return aValue;
}
}
function checkSanitizing(element, inputTypeDescription)
{
var testData =
[
// For text, password, search, tel, email:
"\n\rfoo\n\r",
"foo\n\rbar",
" foo ",
" foo\n\r bar ",
// For url:
"\r\n foobar \n\r",
"\u000B foo \u000B",
"\u000A foo \u000A",
"\u000C foo \u000C",
"\u000d foo \u000d",
"\u0020 foo \u0020",
" \u0009 foo \u0009 ",
// For number and range:
"42",
"13.37",
"1.234567898765432",
"12foo",
"1e2",
"3E42",
// For date:
"1970-01-01",
"1234-12-12",
"1234567890-01-02",
"2012-12-31",
"2012-02-29",
"2000-02-29",
"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",
// For time:
"1",
"10",
"10:",
"10:1",
"21:21",
":21:21",
"-21:21",
" 21:21",
"21-21",
"21:21:",
"21:211",
"121:211",
"21:21 ",
"00:00",
"-1:00",
"24:00",
"00:60",
"01:01",
"23:59",
"99:99",
"8:30",
"19:2",
"19:a2",
"4c:19",
"10:.1",
"1.:10",
"13:37:42",
"13:37.42",
"13:37:42 ",
"13:37:42.",
"13:37:61.",
"13:37:00",
"13:37:99",
"13:37:b5",
"13:37:-1",
"13:37:.1",
"13:37:1.",
"13:37:42.001",
"13:37:42.001",
"13:37:42.abc",
"13:37:42.00c",
"13:37:42.a23",
"13:37:42.12e",
"13:37:42.1e1",
"13:37:42.e11",
"13:37:42.1",
"13:37:42.99",
"13:37:42.0",
"13:37:42.00",
"13:37:42.000",
"13:37:42.-1",
"13:37:42.1.1",
"13:37:42.1,1",
"13:37:42.",
"foo12:12",
"13:37:42.100000000000",
// For color
"#00ff00",
"#000000",
"red",
"#0f0",
"#FFFFAA",
"FFAABB",
"fFAaBb",
"FFAAZZ",
"ABCDEF",
"#7654321",
// For month
"1970-01",
"1234-12",
"123456789-01",
"2013-13",
"0000-00",
"2015-00",
"0001-01",
"1-1",
"888-05",
"2013-3",
"2013-may",
"2000-1a",
"2013-03-13",
"december",
"abcdef",
"12",
" 2013-03",
"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 datetime-local
"1970-01-01T00:00",
"1970-01-01Z12:00",
"1970-01-01 00:00:00",
"1970-01-01T00:00:00.0",
"1970-01-01T00:00:00.00",
"1970-01-01T00:00:00.000",
"1970-01-01 00:00:00.20",
"1969-12-31 23:59",
"1969-12-31 23:59:00",
"1969-12-31 23:59:00.000",
"1969-12-31 23:59:00.30",
"123456-01-01T12:00",
"123456-01-01T12:00:00",
"123456-01-01T12:00:00.0",
"123456-01-01T12:00:00.00",
"123456-01-01T12:00:00.000",
"123456-01-01T12:00:30",
"123456-01-01T12:00:00.123",
"10000-12-31 20:00",
"10000-12-31 20:00:00",
"10000-12-31 20:00:00.0",
"10000-12-31 20:00:00.00",
"10000-12-31 20:00:00.000",
"10000-12-31 20:00:30",
"10000-12-31 20:00:00.123",
"2016-13-01T12:00",
"2016-12-32T12:00",
"2016-11-08 15:40:30.0",
"2016-11-08T15:40:30.00",
"2016-11-07T17:30:10",
"2016-12-1T12:45",
"2016-12-01T12:45:30.123456",
"2016-12-01T24:00",
"2016-12-01T12:88:30",
"2016-12-01T12:30:99",
"2016-12-01T12:30:100",
"2016-12-01",
"2016-12-01T",
"2016-Dec-01T00:00",
"12-05-2016T00:00",
"datetime-local"
];
for (value of testData) {
element.setAttribute('value', value);
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
if (type in valueModeValue) {
element.setAttribute('value', 'tulip');
element.value = value;
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
delayed_is(element.getAttribute('value'), 'tulip',
"The content value should not have been sanitized");
}
element.setAttribute('value', '');
form.reset();
element.type = 'checkbox'; // We know this type has no sanitizing algorithm.
element.setAttribute('value', value);
delayed_is(element.value, value, "The value should not have been sanitized");
element.type = type;
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
element.setAttribute('value', '');
form.reset();
element.setAttribute('value', value);
form.reset();
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
// Cleaning-up.
element.setAttribute('value', '');
form.reset();
}
flushDelayedTests(inputTypeDescription);
}
for (type of inputTypes) {
var form = document.forms[0];
var element = document.createElement("input");
element.style.display = "none";
element.type = type;
form.appendChild(element);
checkSanitizing(element, "type=" + type + ", no frame, no editor");
element.style.display = "";
checkSanitizing(element, "type=" + type + ", frame, no editor");
element.focus();
element.blur();
checkSanitizing(element, "type=" + type + ", frame, editor");
element.style.display = "none";
checkSanitizing(element, "type=" + type + ", no frame, editor");
form.removeChild(element);
}
</script>
</pre>
</body>
</html>