Bug 345512 - Implement pattern attribute for input elements. f=ajvincent r=sicking,jorendorff sr=smaug a2.0=blocking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 18 Aug 2010 21:26:22 +0200
changeset 50911 283ec5de5ee27444e985821e961b763e4def4f65
parent 50910 03367932ad4148aeb44e3b07eb22d5becf4b5e82
child 50912 55ef0e0529bc14c66810a6ef2851923c97199358
push idunknown
push userunknown
push dateunknown
reviewerssicking, jorendorff, smaug
bugs345512
milestone2.0b5pre
Bug 345512 - Implement pattern attribute for input elements. f=ajvincent r=sicking,jorendorff sr=smaug a2.0=blocking
content/base/src/nsGkAtomList.h
content/html/content/src/nsConstraintValidation.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/test/Makefile.in
content/html/content/test/test_bug345512.html
dom/interfaces/html/nsIDOMHTMLInputElement.idl
dom/locales/en-US/chrome/dom/dom.properties
js/src/js.msg
js/src/jsapi.cpp
js/src/jsapi.h
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -720,16 +720,17 @@ GK_ATOM(pageincrement, "pageincrement")
 GK_ATOM(pagex, "pagex")
 GK_ATOM(pagey, "pagey")
 GK_ATOM(palettename, "palettename")
 GK_ATOM(panel, "panel")
 GK_ATOM(param, "param")
 GK_ATOM(parameter, "parameter")
 GK_ATOM(parent, "parent")
 GK_ATOM(parsetype, "parsetype")
+GK_ATOM(pattern, "pattern")
 GK_ATOM(patternSeparator, "pattern-separator")
 GK_ATOM(perMille, "per-mille")
 GK_ATOM(percent, "percent")
 GK_ATOM(persist, "persist")
 GK_ATOM(phase, "phase")
 GK_ATOM(ping, "ping")
 #ifdef MOZ_MEDIA
 GK_ATOM(pixelratio, "pixelratio")
@@ -1206,17 +1207,16 @@ GK_ATOM(onSVGZoom, "onSVGZoom")
 GK_ATOM(onzoom, "onzoom")
 GK_ATOM(opacity, "opacity")
 GK_ATOM(_operator, "operator")
 GK_ATOM(out, "out")
 GK_ATOM(over, "over")
 GK_ATOM(pad, "pad")
 GK_ATOM(path, "path")
 GK_ATOM(pathLength, "pathLength")
-GK_ATOM(pattern, "pattern")
 GK_ATOM(patternContentUnits, "patternContentUnits")
 GK_ATOM(patternTransform, "patternTransform")
 GK_ATOM(patternUnits, "patternUnits")
 GK_ATOM(pc, "pc")
 GK_ATOM(pointer_events, "pointer-events")
 GK_ATOM(points, "points")
 GK_ATOM(pointsAtX, "pointsAtX")
 GK_ATOM(pointsAtY, "pointsAtY")
--- a/content/html/content/src/nsConstraintValidation.cpp
+++ b/content/html/content/src/nsConstraintValidation.cpp
@@ -78,16 +78,18 @@ nsConstraintValidation::GetValidationMes
     if (!mCustomValidity.IsEmpty()) {
       aValidationMessage.Assign(mCustomValidity);
     } else if (IsTooLong()) {
       GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_TOO_LONG);
     } else if (IsValueMissing()) {
       GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_VALUE_MISSING);
     } else if (HasTypeMismatch()) {
       GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_TYPE_MISMATCH);
+    } else if (HasPatternMismatch()) {
+      GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_PATTERN_MISMATCH);
     } else {
       // TODO: The other messages have not been written
       // because related constraint validation are not implemented yet.
       // We should not be here.
       return NS_ERROR_UNEXPECTED;
     }
   } else {
     aValidationMessage.Truncate();
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -104,19 +104,26 @@
 // input type=image
 #include "nsImageLoadingContent.h"
 #include "nsIDOMWindowInternal.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsHTMLFormElement.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
 
 #include "nsTextEditRules.h"
 
+// JS headers are needed for the pattern attribute.
+#include "jsapi.h"    // for js_SaveAndClearRegExpStatics 
+                      // and js_RestoreRegExpStatics
+#include "jsregexp.h" // for js::AutoValueRooter
+#include "jscntxt.h"
+
 #include "nsHTMLInputElement.h"
 
 // XXX align=left, hspace, vspace, border? other nav4 attrs
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
 
 // First bits are needed for the control type.
@@ -547,16 +554,17 @@ NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInpu
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Required, required)
 NS_IMPL_URI_ATTR(nsHTMLInputElement, Src, src)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLInputElement, TabIndex, tabindex, 0)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, UseMap, usemap)
 //NS_IMPL_STRING_ATTR(nsHTMLInputElement, Value, value)
 //NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Size, size, 0)
+NS_IMPL_STRING_ATTR(nsHTMLInputElement, Pattern, pattern)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Placeholder, placeholder)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Type, type,
                                 kInputDefaultType->tag)
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetIndeterminate(PRBool* aValue)
 {
   *aValue = GET_BOOLBIT(mBitField, BF_INDETERMINATE);
@@ -3097,16 +3105,22 @@ nsHTMLInputElement::DoesRequiredApply() 
       return PR_TRUE;
 #else // DEBUG
     default:
       return PR_TRUE;
 #endif // DEBUG
   }
 }
 
+PRBool
+nsHTMLInputElement::DoesPatternApply() const
+{
+  return IsSingleLineTextControl(PR_FALSE);
+}
+
 // nsConstraintValidation
 
 PRBool
 nsHTMLInputElement::IsTooLong()
 {
   if (!GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
     return PR_FALSE;
   }
@@ -3197,16 +3211,40 @@ nsHTMLInputElement::HasTypeMismatch()
     return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nsnull,
                                            nsnull, getter_AddRefs(uri)));
   }
 
   return PR_FALSE;
 }
 
 PRBool
+nsHTMLInputElement::HasPatternMismatch()
+{
+  nsAutoString pattern;
+  if (!DoesPatternApply() || !GetAttr(kNameSpaceID_None, nsGkAtoms::pattern,
+                                      pattern)) {
+    return PR_FALSE;
+  }
+
+  nsAutoString value;
+  NS_ENSURE_SUCCESS(GetValue(value), PR_FALSE);
+
+  if (value.IsEmpty()) {
+    return PR_FALSE;
+  }
+
+  nsIDocument* doc = GetOwnerDoc();
+  if (!doc) {
+    return PR_FALSE;
+  }
+
+  return !IsPatternMatching(value, pattern, doc);
+}
+
+PRBool
 nsHTMLInputElement::IsBarredFromConstraintValidation()
 {
   return mType == NS_FORM_INPUT_HIDDEN ||
          mType == NS_FORM_INPUT_BUTTON ||
          mType == NS_FORM_INPUT_RESET ||
          HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
 }
 
@@ -3273,16 +3311,34 @@ nsHTMLInputElement::GetValidationMessage
       } else {
         return NS_ERROR_UNEXPECTED;
       }
       rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                               key.get(), message);
       aValidationMessage = message;
       break;
     }
+    case VALIDATION_MESSAGE_PATTERN_MISMATCH:
+    {
+      nsXPIDLString message;
+      nsAutoString title;
+      GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
+      if (title.IsEmpty()) {
+        rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+                                                "ElementSuffersFromPatternMismatch",
+                                                message);
+      } else {
+        const PRUnichar* params[] = { title.get() };
+        rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+                                                   "ElementSuffersFromPatternMismatchWithTitle",
+                                                   params, 1, message);
+      }
+      aValidationMessage = message;
+      break;
+    }
     default:
       rv = nsConstraintValidation::GetValidationMessage(aValidationMessage, aType);
   }
 
   return rv;
 }
 
 //static
@@ -3358,16 +3414,57 @@ nsHTMLInputElement::IsValidEmailAddress(
       // The domain characters have to be in this list to be valid.
       return PR_FALSE;
     }
   }
 
   return dotFound;
 }
 
+//static
+PRBool
+nsHTMLInputElement::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
+                                      nsIDocument* aDocument)
+{
+  NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
+  NS_ENSURE_TRUE(aDocument->GetScriptGlobalObject(), PR_TRUE);
+
+  JSContext* ctx = (JSContext*) aDocument->GetScriptGlobalObject()->
+                                  GetContext()->GetNativeContext();
+  NS_ENSURE_TRUE(ctx, PR_TRUE);
+
+  JSAutoRequest ar(ctx);
+
+  // The pattern has to match the entire value.
+  aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
+  aPattern.Append(NS_LITERAL_STRING(")$"));
+
+  JSObject* re = JS_NewUCRegExpObject(ctx, reinterpret_cast<jschar*>
+                                             (aPattern.BeginWriting()),
+                                      aPattern.Length(), 0);
+  NS_ENSURE_TRUE(re, PR_TRUE);
+
+  js::AutoObjectRooter re_root(ctx, re);
+  js::AutoStringRooter tvr(ctx);
+  js::RegExpStatics statics(ctx);
+  jsval rval = JSVAL_NULL;
+  size_t idx = 0;
+  JSBool res;
+
+  js_SaveAndClearRegExpStatics(ctx, &statics, &tvr);
+
+  res = JS_ExecuteRegExp(ctx, re, reinterpret_cast<jschar*>
+                                    (aValue.BeginWriting()),
+                         aValue.Length(), &idx, JS_TRUE, &rval);
+
+  js_RestoreRegExpStatics(ctx, &statics);
+
+  return res == JS_FALSE || rval != JSVAL_NULL;
+}
+
 //
 // Visitor classes
 //
 //
 // CLASS nsRadioVisitor
 //
 // (this is the superclass of the others)
 //
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -210,16 +210,17 @@ public:
   void MaybeLoadImage();
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   // nsConstraintValidation
   PRBool   IsTooLong();
   PRBool   IsValueMissing();
   PRBool   HasTypeMismatch();
+  PRBool   HasPatternMismatch();
   PRBool   IsBarredFromConstraintValidation();
   nsresult GetValidationMessage(nsAString& aValidationMessage,
                                 ValidationMessageType aType);
 
 protected:
   // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
   // by the nsITextControlElement version.
   using nsGenericHTMLFormElement::IsSingleLineTextControl;
@@ -264,16 +265,33 @@ protected:
    * This is following the HTML5 specification:
    * http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address-list
    *
    * @param aValue  the email address list to check.
    * @result        whether the given string is a valid email address list.
    */
   static PRBool IsValidEmailAddressList(const nsAString& aValue);
 
+  /**
+   * This helper method returns true if the aPattern pattern matches aValue.
+   * aPattern should not contain leading and trailing slashes (/).
+   * The pattern has to match the entire value not just a subset.
+   * aDocument must be a valid pointer (not null).
+   *
+   * This is following the HTML5 specification:
+   * http://dev.w3.org/html5/spec/forms.html#attr-input-pattern
+   *
+   * @param aValue    the string to check.
+   * @param aPattern  the string defining the pattern.
+   * @param aDocument the owner document of the element.
+   * @result          whether the given string is matches the pattern.
+   */
+  static PRBool IsPatternMatching(nsAString& aValue, nsAString& aPattern,
+                                  nsIDocument* aDocument);
+
   // Helper method
   nsresult SetValueInternal(const nsAString& aValue,
                             PRBool aUserInput,
                             PRBool aSetValueChanged);
 
   void ClearFileNames() {
     nsTArray<nsString> fileNames;
     SetFileNames(fileNames);
@@ -405,16 +423,21 @@ protected:
    */
   PRBool DoesReadOnlyApply() const;
 
   /**
    * Returns if the required attribute applies for the current type.
    */
   PRBool DoesRequiredApply() const;
 
+  /**
+   * Returns if the pattern attribute applies for the current type.
+   */
+  PRBool DoesPatternApply() const;
+
   void FreeData();
   nsTextEditorState *GetEditorState() const;
 
   /**
    * Manages the internal data storage across type changes.
    */
   void HandleTypeChange(PRUint8 aNewType);
 
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -192,12 +192,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug549475.html \
 		test_bug585508.html \
 		test_bug345624-1.html \
 		test_bug345624-2.html \
 		test_bug561640.html \
 		test_bug345822.html \
 		test_bug555559.html \
 		test_bug344615.html \
+		test_bug345512.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug345512.html
@@ -0,0 +1,300 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345512
+-->
+<head>
+  <title>Test for Bug 345512</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=345512">Mozilla Bug 345512</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <input id='i' pattern="tulip" oninvalid="invalidEventHandler(event);">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345512 **/
+
+var gInvalid = false;
+
+function invalidEventHandler(e)
+{
+  is(e.type, "invalid", "Invalid event type should be invalid");
+  gInvalid = true;
+}
+
+function checkPatternAttribute(element)
+{
+  ok('pattern' in element, "Element should have the pattern attribute");
+
+  is(element.pattern, "tulip",
+    "pattern IDL attribute value should be 'tulip'");
+  is(element.getAttribute('pattern'), "tulip",
+    "pattern content attribute value should be 'tulip'");
+
+  element.pattern = "foo";
+  is(element.pattern, "foo",
+    "pattern IDL attribute value should be 'foo'");
+  is(element.getAttribute('pattern'), "foo",
+    "pattern content attribute value should be 'foo'");
+
+  element.removeAttribute('pattern');
+  ok(!element.pattern,
+    "Element pattern attribute shouldn't be specified");
+  is(element.getAttribute('pattern'), null,
+    "Element pattern attribute shouldn't be specified");
+
+  element.setAttribute("pattern", "bar");
+  is(element.pattern, "bar",
+    "pattern IDL attribute value should be 'bar'");
+  is(element.getAttribute('pattern'), "bar",
+    "pattern content attribute value should be 'bar'");
+}
+
+function completeValidityCheck(element, alwaysValid)
+{
+  if (element.type == 'file') {
+    // Need privileges to set a filename with .value.
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  }
+
+  // Check when pattern matches.
+  if (element.type == 'email') {
+    element.pattern = ".*@bar.com";
+    element.value = "foo@bar.com";
+  } else if (element.type == 'url') {
+    element.pattern = "http://.*\\.com$";
+    element.value = "http://mozilla.com";
+  } else {
+    element.pattern = "foo";
+    element.value = "foo";
+  }
+
+  checkValidPattern(element, true);
+
+  // Check when pattern does not match.
+
+  if (element.type == 'email') {
+    element.pattern = ".*@bar.com";
+    element.value = "foo@foo.com";
+  } else if (element.type == 'url') {
+    element.pattern = "http://.*\\.com$";
+    element.value = "http://mozilla.org";
+  } else {
+    element.pattern = "foo";
+    element.value = "bar";
+  }
+
+  if (!alwaysValid) {
+    checkInvalidPattern(element, true);
+  } else {
+    checkValidPattern(element, true);
+  }
+}
+
+function checkValidPattern(element, completeCheck)
+{
+  if (completeCheck) {
+    gInvalid = false;
+
+    ok(!element.validity.patternMismatch,
+       "Element should not suffer from pattern mismatch");
+    ok(element.validity.valid, "Element should be valid");
+    ok(element.checkValidity(), "Element should be valid");
+    ok(!gInvalid, "Invalid event shouldn't have been thrown");
+    is(element.validationMessage, '',
+       "Validation message should be the empty string");
+  } else {
+    ok(!element.validity.patternMismatch,
+       "Element should not suffer from pattern mismatch");
+  }
+}
+
+function checkInvalidPattern(element, completeCheck)
+{
+  if (completeCheck) {
+    gInvalid = false;
+
+    ok(element.validity.patternMismatch,
+       "Element should suffer from pattern mismatch");
+    ok(!element.validity.valid, "Element should not be valid");
+    ok(!element.checkValidity(), "Element should not be valid");
+    ok(gInvalid, "Invalid event should have been thrown");
+    is(element.validationMessage,
+       "The field value is not in the requested format.",
+       "Validation message is not valid");
+  } else {
+    ok(element.validity.patternMismatch,
+       "Element should suffer from pattern mismatch");
+  }
+}
+
+function checkPatternValidity(element)
+{
+  element.pattern = "foo";
+
+  element.value = '';
+  checkValidPattern(element);
+
+  element.value = "foo";
+  checkValidPattern(element);
+
+  element.value = "bar";
+  checkInvalidPattern(element);
+
+  element.value = "foobar";
+  checkInvalidPattern(element);
+
+  element.value = "foofoo";
+  checkInvalidPattern(element);
+
+  element.pattern = "foo\"bar";
+  element.value = "foo\"bar";
+  checkValidPattern(element);
+
+  element.value = 'foo"bar';
+  checkValidPattern(element);
+
+  element.pattern = "foo'bar";
+  element.value = "foo\'bar";
+  checkValidPattern(element);
+
+  element.pattern = "foo\\(bar";
+  element.value = "foo(bar";
+  checkValidPattern(element);
+
+  element.value = "foo";
+  checkInvalidPattern(element);
+
+  element.pattern = "foo\\)bar";
+  element.value = "foo)bar";
+  checkValidPattern(element);
+
+  element.value = "foo";
+  checkInvalidPattern(element);
+
+  // We need '\\\\' because '\\' will produce '\\' and we want to escape the '\'
+  // for the regexp.
+  element.pattern = "foo\\\\bar";
+  element.value = "foo\\bar";
+  checkValidPattern(element);
+
+  // The same way, we want to escape the ' in the pattern.
+  element.pattern = "foo\\'bar";
+  element.value = "foo'bar";
+  checkValidPattern(element);
+
+  // Check for 'i' flag disabled. Should be case sensitive.
+  element.value = "Foo";
+  checkInvalidPattern(element);
+
+  // We can't check for the 'g' flag because we only test, we don't execute.
+  // We can't check for the 'm' flag because .value shouldn't contain line breaks.
+
+  // We should check the pattern attribute do not pollute |RegExp.lastParen|.
+  is(RegExp.lastParen, "", "RegExp.lastParen should be the empty string");
+
+  element.pattern = "(foo)";
+  element.value = "foo";
+  checkValidPattern(element);
+  is(RegExp.lastParen, "", "RegExp.lastParen should be the empty string");
+
+  // That may sound weird but the empty string is a valid pattern value.
+  element.pattern = "";
+  element.value = "";
+  checkValidPattern(element);
+
+  element.value = "foo";
+  checkInvalidPattern(element);
+
+  // Checking some complex patterns. As we are using js regexp mechanism, these
+  // tests doesn't aim to test the regexp mechanism.
+  element.pattern = "\\d{2}\\s\\d{2}\\s\\d{4}"
+  element.value = "01 01 2010"
+  checkValidPattern(element);
+
+  element.value = "01/01/2010"
+  checkInvalidPattern(element);
+
+  element.pattern = "[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z_+])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9}";
+  element.value = "foo@bar.com";
+  checkValidPattern(element);
+
+  element.value = "...@bar.com";
+  checkInvalidPattern(element);
+
+  element.pattern = "^(?:\\w{3,})$";
+  element.value = "foo";
+  checkValidPattern(element);
+
+  element.value = "f";
+  checkInvalidPattern(element);
+
+  // If @title is specified, it should be added in the validation message.
+  if (element.type == 'email') {
+    element.pattern = "foo@bar.com"
+    element.value = "bar@foo.com";
+  } else if (element.type == 'url') {
+    element.pattern = "http://mozilla.com";
+    element.value = "http://mozilla.org";
+  } else {
+    element.pattern = "foo";
+    element.value = "bar";
+  }
+  element.title = "This is an explanation of the regexp.";
+  is(element.validationMessage,
+     "The field value is not in the requested format. " + element.title,
+     "Validation message is not valid");
+  element.title = "";
+  is(element.validationMessage,
+     "The field value is not in the requested format.",
+     "Validation message is not valid");
+
+  element.pattern = "foo";
+  if (element.type == 'email') {
+    element.value = "bar@foo.com";
+  } else if (element.type == 'url') {
+    element.value = "http://mozilla.org";
+  } else {
+    element.value = "bar";
+  }
+  checkInvalidPattern(element);
+
+  element.removeAttribute('pattern');
+  checkValidPattern(element, true);
+}
+
+var input = document.getElementById('i');
+
+// All input types should have the pattern attribute.
+checkPatternAttribute(input);
+
+// |validTypes| are the types which accept @pattern
+// and |invalidTypes| are the ones which do not accept it.
+var validTypes = Array('text', 'password', 'search', 'telephone', 'email', 'url');
+var invalidTypes = Array('hidden', 'checkbox', 'radio', 'file', 'submit',
+                         'image', 'reset', 'button');
+// TODO: 'datetime', 'date', 'month', 'week', 'time', 'datetime-local',
+//       'number', 'range', ande 'color' do not accept the @pattern too but are
+//       not implemented yet.
+
+for each (type in validTypes) {
+  input.type = type;
+  completeValidityCheck(input, false);
+  checkPatternValidity(input);
+}
+
+for each (type in invalidTypes) {
+  input.type = type;
+  completeValidityCheck(input, true);
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/html/nsIDOMHTMLInputElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLInputElement.idl
@@ -46,17 +46,17 @@ interface nsIDOMValidityState;
  /**
   * The nsIDOMHTMLInputElement interface is the interface to a [X]HTML
   * input element.
   *
   * For more information on this interface please see
   * http://www.w3.org/TR/DOM-Level-2-HTML/
   */
  
-[scriptable, uuid(ed3f8a4a-9894-4120-a7e6-4a18f3d8effd)]
+[scriptable, uuid(6f283298-168e-4787-bfe7-f207be2438df)]
 interface nsIDOMHTMLInputElement : nsIDOMHTMLElement
 {
            attribute DOMString             accept;
            attribute DOMString             alt;
 
            attribute boolean               autofocus;
            attribute boolean               defaultChecked;
            attribute boolean               checked;
@@ -67,16 +67,17 @@ interface nsIDOMHTMLInputElement : nsIDO
 
            attribute boolean               indeterminate;
 
            attribute long                  maxLength;
 
            attribute boolean               multiple;
            attribute DOMString             name;
 
+           attribute DOMString             pattern;
            attribute DOMString             placeholder;
            attribute boolean               readOnly;
            attribute boolean               required;
 
            attribute DOMString             accessKey;
            attribute DOMString             align;
 
            attribute unsigned long         size;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -65,9 +65,11 @@ SpeculationFailed=An unbalanced tree was
 DocumentWriteIgnored=A call to document.write() from an asynchronously-loaded external script was ignored.
 ElementSuffersFromBeingTooLong=The text is too long. It is %S characters long and the limit is %S.
 TextElementSuffersFromBeingMissing=This field is mandatory, you have to fill it.
 CheckboxElementSuffersFromBeingMissing=This checkbox is mandatory, you have to check it.
 RadioElementSuffersFromBeingMissing=You have to select one of these options.
 FileElementSuffersFromBeingMissing=You have to select a file.
 ElementSuffersFromInvalidEmail=The entered email address is not valid.
 ElementSuffersFromInvalidURL=The entered URL is not valid.
+ElementSuffersFromPatternMismatch=The field value is not in the requested format.
+ElementSuffersFromPatternMismatchWithTitle=The field value is not in the requested format. %S
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -325,8 +325,9 @@ MSG_DEF(JSMSG_DEFINE_ARRAY_LENGTH_UNSUPP
 MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,  244, 0, JSEXN_ERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,   246, 0, JSEXN_ERR, "invalid arguments")
 MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION,   247, 0, JSEXN_ERR, "call to Function() blocked by CSP")
 MSG_DEF(JSMSG_BAD_GET_SET_FIELD,      248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
 MSG_DEF(JSMSG_BAD_PROXY_FIX,          249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler")
 MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument")
+MSG_DEF(JSMSG_NOT_EXPECTED_TYPE,      251, 3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5443,17 +5443,37 @@ JS_ClearRegExpStatics(JSContext *cx)
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpRoots(JSContext *cx)
 {
     /* No locking required, cx is thread-private and input must be live. */
     cx->regExpStatics.clear();
 }
 
-/* TODO: compile, execute, get/set other statics... */
+JS_PUBLIC_API(JSBool)
+JS_ExecuteRegExp(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
+                 size_t *indexp, JSBool test, jsval *rval)
+{
+    CHECK_REQUEST(cx);
+
+    RegExp *re = RegExp::extractFrom(obj);
+    if (!re) {
+      return JS_FALSE;
+    }
+
+    JSString *str = js_NewStringCopyN(cx, chars, length);
+    if (!str) {
+        return JS_FALSE;
+    }
+    AutoValueRooter v(cx, StringValue(str));
+
+    return re->execute(cx, str, indexp, test, Valueify(rval));
+}
+
+/* TODO: compile, get/set other statics... */
 
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
 {
     cx->localeCallbacks = callbacks;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2920,17 +2920,21 @@ extern JS_PUBLIC_API(void)
 JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline);
 
 extern JS_PUBLIC_API(void)
 JS_ClearRegExpStatics(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_ClearRegExpRoots(JSContext *cx);
 
-/* TODO: compile, exec, get/set other statics... */
+extern JS_PUBLIC_API(JSBool)
+JS_ExecuteRegExp(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
+                 size_t *indexp, JSBool test, jsval *rval);
+
+/* TODO: compile, get/set other statics... */
 
 /************************************************************************/
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsExceptionPending(JSContext *cx);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetPendingException(JSContext *cx, jsval *vp);