Bug 835055 - Make sure that a script can't use an unsanitized value while the user is typing in a field. r=smaug
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 31 Jan 2013 23:05:48 +0000
changeset 130406 566e7485b29a069593bbcaf53c0cf59e6b8d517a
parent 130405 d4b0027c75e9b6266d149513ba93015af581bbc5
child 130407 ecdbffdf275adaf214ae7c8e2cc91ba20ccfd181
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs835055
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 835055 - Make sure that a script can't use an unsanitized value while the user is typing in a field. r=smaug
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/test/forms/Makefile.in
content/html/content/test/forms/test_input_number_value.html
content/html/content/test/forms/test_input_typing_sanitization.html
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -1002,18 +1002,18 @@ nsHTMLInputElement::SetWidth(uint32_t aW
   return nsGenericHTMLElement::SetUnsignedIntAttr(nsGkAtoms::width, aWidth);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetValue(nsAString& aValue)
 {
   nsresult rv = GetValueInternal(aValue);
 
-  // Don't return non-sanitized value for number inputs.
-  if (mType == NS_FORM_INPUT_NUMBER) {
+  // Don't return non-sanitized value for types that are experimental on mobile.
+  if (IsExperimentalMobileType(mType)) {
     SanitizeValue(aValue);
   }
 
   return rv;
 }
 
 nsresult
 nsHTMLInputElement::GetValueInternal(nsAString& aValue) const
@@ -2477,18 +2477,20 @@ nsHTMLInputElement::PreHandleEvent(nsEve
     aVisitor.mEvent->mFlags.mNoContentDispatch = false;
   }
 
   // We must cache type because mType may change during JS event (bug 2369)
   aVisitor.mItemFlags |= mType;
 
   // Fire onchange (if necessary), before we do the blur, bug 357684.
   if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
-    // In number inputs we can't allow the user to set an invalid value.
-    if (mType == NS_FORM_INPUT_NUMBER) {
+    // Experimental mobile types rely on the system UI to prevent users to not
+    // set invalid values but we have to be extra-careful. Especially if the
+    // option has been enabled on desktop.
+    if (IsExperimentalMobileType(mType)) {
       nsAutoString aValue;
       GetValueInternal(aValue);
       SetValueInternal(aValue, false, false);
     }
     FireChangeEventIfNeeded();
   }
 
   return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
@@ -2768,17 +2770,17 @@ nsHTMLInputElement::PostHandleEvent(nsEv
            *     not submit, period.
            */
 
           if (aVisitor.mEvent->message == NS_KEY_PRESS &&
               (keyEvent->keyCode == NS_VK_RETURN ||
                keyEvent->keyCode == NS_VK_ENTER) &&
                (IsSingleLineTextControl(false, mType) ||
                 IsExperimentalMobileType(mType))) {
-            FireChangeEventIfNeeded();   
+            FireChangeEventIfNeeded();
             rv = MaybeSubmitForm(aVisitor.mPresContext);
             NS_ENSURE_SUCCESS(rv, rv);
           }
 
         } break; // NS_KEY_PRESS || NS_KEY_UP
 
         case NS_MOUSE_BUTTON_DOWN:
         case NS_MOUSE_BUTTON_UP:
--- a/content/html/content/test/forms/Makefile.in
+++ b/content/html/content/test/forms/Makefile.in
@@ -43,16 +43,16 @@ MOCHITEST_FILES = \
 		test_meter_element.html \
 		test_meter_pseudo-classes.html \
 		test_max_attribute.html \
 		test_min_attribute.html \
 		test_step_attribute.html \
 		test_stepup_stepdown.html \
 		test_valueasnumber_attribute.html \
 		test_experimental_forms_pref.html \
-		test_input_number_value.html \
+		test_input_typing_sanitization.html \
 		test_input_sanitization.html \
 		test_valueasdate_attribute.html \
 		test_input_file_b2g_disabled.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
rename from content/html/content/test/forms/test_input_number_value.html
rename to content/html/content/test/forms/test_input_typing_sanitization.html
--- a/content/html/content/test/forms/test_input_number_value.html
+++ b/content/html/content/test/forms/test_input_typing_sanitization.html
@@ -15,121 +15,212 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <iframe name="submit_frame" style="visibility: hidden;"></iframe>
 <div id="content">
   <form id='f' target="submit_frame" action="foo">
     <input name=i id="i" step='any' >
   </form>
 </div>
 <pre id="test">
-<script type="application/javascript">
+<script type="application/javascript;version=1.7">
+
+/*
+ * This test checks that when a user types in some input types, it will not be
+ * in a state where the value will be un-sanitized and usable (by a script).
+ */
 
 var input = document.getElementById('i');
 var form = document.getElementById('f');
 var submitFrame = document.getElementsByTagName('iframe')[0];
 var testData = [];
-var validData =
-[
-  "42",
-  "-42", // should work for negative values
-  "42.1234",
-  "123.12345678912345",  // double precision
-  "1e2", // e should be usable
-  "2e1",
-  "1e-1", // value after e can be negative
-  "1E2", // E can be used instead of e
-];
-var invalidData =
-[
-  "e",
-  "e2",
-  "1e0.1",
-  "foo",
-  "42,13", // comma can't be used as a decimal separator
-];
+var gValidData = [];
+var gInvalidData = [];
 
 function submitForm() {
   form.submit();
 }
 
 function sendKeyEventToSubmitForm() {
   sendKey("return");
 }
 
+function urlify(aStr) {
+  return aStr.replace(':', '%3A', 'g');
+}
+
 function checkValueSubmittedIsValid()
 {
   is(frames['submit_frame'].location.href,
      'http://mochi.test:8888/tests/content/html/content/test/forms/foo?i='
-     + validData[valueIndex++],
+     + urlify(gValidData[valueIndex++]),
      "The submitted value should not have been sanitized");
 
   input.value = "";
 
-  if (valueIndex >= validData.length) {
+  if (valueIndex >= gValidData.length) {
     valueIndex = 0;
     submitFrame.onload = checkValueSubmittedIsInvalid;
-    testData = invalidData;
+    testData = gInvalidData;
   }
-  submitNextValue();
+  testSubmissions();
 }
 
 function checkValueSubmittedIsInvalid()
 {
   is(frames['submit_frame'].location.href,
-        'http://mochi.test:8888/tests/content/html/content/test/forms/foo?i=',
-        "The submitted value should have been sanitized");
+     'http://mochi.test:8888/tests/content/html/content/test/forms/foo?i=',
+     "The submitted value should have been sanitized");
 
   valueIndex++;
   input.value = "";
 
-  if (valueIndex >= invalidData.length) {
+  if (valueIndex >= gInvalidData.length) {
     if (submitMethod == sendKeyEventToSubmitForm) {
-      SimpleTest.finish();
+      try {
+        testRunner.next();
+      } catch (e) {
+        if (e.toString() == '[object StopIteration]') {
+          SimpleTest.finish();
+        } else {
+          throw StopIteration;
+        }
+      }
       return;
     }
     valueIndex = 0;
     submitMethod = sendKeyEventToSubmitForm;
     submitFrame.onload = checkValueSubmittedIsValid;
-    testData = validData;
+    testData = gValidData;
   }
-  submitNextValue();
+  testSubmissions();
 }
 
-function submitNextValue() {
+function testSubmissions() {
   SpecialPowers.focus(input);
   sendString(testData[valueIndex]);
   submitMethod();
 }
 
 var valueIndex = 0;
 var submitMethod = submitForm;
 
 SimpleTest.waitForExplicitFinish();
-addLoadEvent(function () {
-  SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]}, function() {
-    input.type = "number";
 
-    for (data of validData) {
+function runTest()
+{
+  var data = [
+    {
+      type: 'number',
+      validData: [
+        "42",
+        "-42", // should work for negative values
+        "42.1234",
+        "123.12345678912345",  // double precision
+        "1e2", // e should be usable
+        "2e1",
+        "1e-1", // value after e can be negative
+        "1E2", // E can be used instead of e
+      ],
+      invalidData: [
+        "e",
+        "e2",
+        "1e0.1",
+        "foo",
+        "42,13", // comma can't be used as a decimal separator
+      ]
+    },
+    {
+      type: 'date',
+      validData: [
+        '0001-01-01',
+        '2012-12-21',
+        '2013-01-28',
+        '100000-01-01',
+      ],
+      invalidData: [
+        '1-01-01',
+        'a',
+        '-',
+        '2012-01',
+        '2013-01-1',
+        '1011-23-21',
+        '1000-12-99',
+      ]
+    },
+    {
+      type: 'time',
+      validData: [
+        '00:00',
+        '09:09:00',
+        '08:23:23.1',
+        '21:43:56.12',
+        '23:12:45.100',
+      ],
+      invalidData: [
+        '00:',
+        '00:00:',
+        '25:00',
+        '-00:00',
+        '00:00:00.',
+        '00:60',
+        '10:58:99',
+        ':19:10',
+        '23:08:09.1012',
+      ]
+    },
+    { type: 'week', todo: true },
+    { type: 'month', todo: true },
+    { type: 'datetime', todo: true },
+    { type: 'datetime-local', todo: true },
+    { type: 'color', todo: true },
+  ];
+
+  for (test of data) {
+    if (test.todo) {
+      input.type = test.type;
+      todo_is(input.type, test.type, test.type + " is not implemented");
+      continue;
+    }
+
+    input.type = test.type;
+    gValidData = test.validData;
+    gInvalidData = test.invalidData;
+
+    for (data of gValidData) {
       input.value = "";
       SpecialPowers.focus(input);
       sendString(data);
       input.blur();
       is(input.value, data, "valid user input should not be sanitized");
     }
 
-    for (data of invalidData) {
+    for (data of gInvalidData) {
       input.value = "";
       SpecialPowers.focus(input);
       sendString(data);
       input.blur();
       is(input.value, "", "invalid user input should be sanitized");
     }
 
-    testData = validData;
+    input.value = '';
+
+    testData = gValidData;
+    valueIndex = 0;
     submitFrame.onload = checkValueSubmittedIsValid;
-    submitNextValue();
-  });
+    testSubmissions();
+    yield;
+  }
+}
+
+var testRunner = runTest();
+
+addLoadEvent(function () {
+  SpecialPowers.pushPrefEnv({'set': [["dom.experimental_forms", true]]},
+    function() {
+      testRunner.next();
+    }
+  );
 });
 
 </script>
 </pre>
 </body>
 </html>