<!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=549475 --> <head> <title>Test for Bug 549475</title> <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=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: actual, expected: expected, description: 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) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-date-string 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) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-time-string 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) { // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-local-date-and-time-string if (aValue.length < 16) { return ""; } var separator = aValue[10]; if (separator != "T" && separator != " ") { return ""; } var [date, time] = aValue.split(separator); if (!sanitizeDate(date)) { return ""; } if (!sanitizeTime(time)) { return ""; } // Normalize datetime-local string. // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string if (separator == " ") { aValue = date + "T" + time; } if (aValue.length == 16) { return aValue; } if (aValue.length > 19) { var milliseconds = aValue.substring(20); if (Number(milliseconds) != 0) { return aValue; } aValue = aValue.slice(0, 19); } var seconds = aValue.substring(17); if (Number(seconds) != 0) { return aValue; } aValue = aValue.slice(0, 16); return aValue; } function sanitizeValue(aType, aValue) { // http://www.whatwg.org/html/#value-sanitization-algorithm 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": // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-month-string 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": // 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-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", "1234567-01-01T12:00", "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>