Bug 551670 - "type attribute for button elements should be 'submit' when type is invalid or missing" [r=smaug, sr=sicking]
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 15 Apr 2010 07:03:00 -0400
changeset 41437 15cece8f636985cd50755cbd3982a6303d6d56b6
parent 41436 87ceb85b85df5267e5b059e3cfd05c88dd2bdc94
child 41438 5c4ecf53b751423ce46dd22bee9ca47047d189e9
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, sicking
bugs551670
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 551670 - "type attribute for button elements should be 'submit' when type is invalid or missing" [r=smaug, sr=sicking]
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLBRElement.cpp
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLFontElement.cpp
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLHRElement.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLLIElement.cpp
content/html/content/src/nsHTMLLegendElement.cpp
content/html/content/src/nsHTMLOListElement.cpp
content/html/content/src/nsHTMLSharedElement.cpp
content/html/content/src/nsHTMLTableCaptionElement.cpp
content/html/content/src/nsHTMLTableCellElement.cpp
content/html/content/src/nsHTMLTableElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/test_bug551670.html
layout/reftests/css-default/reftest.list
layout/reftests/css-default/submit-button/default-submit-button-1.html
layout/reftests/css-default/submit-button/default-submit-button-2.html
layout/reftests/css-default/submit-button/default-submit-button-3.html
layout/reftests/css-default/submit-button/default-submit-button-4.html
layout/reftests/css-default/submit-button/default-submit-button-5.html
layout/reftests/css-default/submit-button/default-submit-button-6.html
layout/reftests/css-default/submit-button/default-submit-button-7.html
layout/reftests/css-default/submit-button/default-submit-button-ref.html
layout/reftests/css-default/submit-button/default-submit-button-style.css
layout/reftests/css-default/submit-button/reftest.list
layout/reftests/reftest.list
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -383,34 +383,17 @@ nsAttrValue::ToString(nsAString& aResult
     {
       NS_NOTREACHED("color attribute without string data");
       aResult.Truncate();
       break;
     }
 #endif
     case eEnum:
     {
-      PRInt16 val = GetEnumValue();
-      PRUint32 allEnumBits =
-        cont ? cont->mEnumValue : static_cast<PRUint32>(GetIntInternal());
-      const EnumTable* table = sEnumTableArray->
-        ElementAt(allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
-      while (table->tag) {
-        if (table->value == val) {
-          aResult.AssignASCII(table->tag);
-          if (allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
-            ToUpperCase(aResult);
-          }
-          return;
-        }
-        table++;
-      }
-
-      NS_NOTREACHED("couldn't find value in EnumTable");
-
+      GetEnumString(aResult, PR_FALSE);
       break;
     }
     case ePercent:
     {
       nsAutoString intStr;
       intStr.AppendInt(cont ? cont->mPercent : GetIntInternal());
       aResult = intStr + NS_LITERAL_STRING("%");
 
@@ -465,16 +448,42 @@ nsAttrValue::GetColorValue(nscolor& aCol
     NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr");
     return PR_FALSE;
   }
 
   aColor = GetMiscContainer()->mColor;
   return PR_TRUE;
 }
 
+void
+nsAttrValue::GetEnumString(nsAString& aResult, PRBool aRealTag) const
+{
+  NS_PRECONDITION(Type() == eEnum, "wrong type");
+
+  PRUint32 allEnumBits =
+    (BaseType() == eIntegerBase) ? static_cast<PRUint32>(GetIntInternal())
+                                   : GetMiscContainer()->mEnumValue;
+  PRInt16 val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS;
+  const EnumTable* table = sEnumTableArray->
+    ElementAt(allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
+
+  while (table->tag) {
+    if (table->value == val) {
+      aResult.AssignASCII(table->tag);
+      if (!aRealTag && allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
+        ToUpperCase(aResult);
+      }
+      return;
+    }
+    table++;
+  }
+
+  NS_NOTREACHED("couldn't find value in EnumTable");
+}
+
 PRInt32
 nsAttrValue::GetAtomCount() const
 {
   ValueType type = Type();
 
   if (type == eAtom) {
     return 1;
   }
@@ -953,56 +962,68 @@ nsAttrValue::SetIntValueAndType(PRInt32 
     }
   } else {
     NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!");
     mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType;
   }
 }
 
 PRBool
+nsAttrValue::GetEnumTableIndex(const EnumTable* aTable, PRInt16& aResult)
+{
+  PRInt16 index = sEnumTableArray->IndexOf(aTable);
+  if (index < 0) {
+    index = sEnumTableArray->Length();
+    NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
+        "too many enum tables");
+    if (!sEnumTableArray->AppendElement(aTable)) {
+      return PR_FALSE;
+    }
+  }
+
+  aResult = index;
+
+  return PR_TRUE;
+}
+
+PRBool
 nsAttrValue::ParseEnumValue(const nsAString& aValue,
                             const EnumTable* aTable,
                             PRBool aCaseSensitive)
 {
   ResetIfSet();
-
-  while (aTable->tag) {
-    if (aCaseSensitive ? aValue.EqualsASCII(aTable->tag) :
-                         aValue.LowerCaseEqualsASCII(aTable->tag)) {
+  const EnumTable* tableEntry = aTable;
 
-      // Find index of EnumTable
-      PRInt16 index = sEnumTableArray->IndexOf(aTable);
-      if (index < 0) {
-        index = sEnumTableArray->Length();
-        NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
-                     "too many enum tables");
-        if (!sEnumTableArray->AppendElement(aTable)) {
-          return PR_FALSE;
-        }
+  while (tableEntry->tag) {
+    if (aCaseSensitive ? aValue.EqualsASCII(tableEntry->tag) :
+                         aValue.LowerCaseEqualsASCII(tableEntry->tag)) {
+      PRInt16 index;
+      if (!GetEnumTableIndex(aTable, index)) {
+        return PR_FALSE;
       }
 
-      PRInt32 value = (aTable->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) +
+      PRInt32 value = (tableEntry->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) +
                       index;
 
-      PRBool equals = aCaseSensitive || aValue.EqualsASCII(aTable->tag);
+      PRBool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry->tag);
       if (!equals) {
         nsAutoString tag;
-        tag.AssignASCII(aTable->tag);
+        tag.AssignASCII(tableEntry->tag);
         ToUpperCase(tag);
         if ((equals = tag.Equals(aValue))) {
           value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER;
         }
       }
       SetIntValueAndType(value, eEnum, equals ? nsnull : &aValue);
-      NS_ASSERTION(GetEnumValue() == aTable->value,
+      NS_ASSERTION(GetEnumValue() == tableEntry->value,
                    "failed to store enum properly");
 
       return PR_TRUE;
     }
-    aTable++;
+    tableEntry++;
   }
 
   return PR_FALSE;
 }
 
 PRBool
 nsAttrValue::ParseSpecialIntValue(const nsAString& aString,
                                   PRBool aCanBePercent)
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -149,16 +149,24 @@ public:
   inline float GetPercentValue() const;
   inline nsCOMArray<nsIAtom>* GetAtomArrayValue() const;
   inline nsICSSStyleRule* GetCSSStyleRuleValue() const;
 #ifdef MOZ_SVG
   inline nsISVGValue* GetSVGValue() const;
 #endif
   inline float GetFloatValue() const;
 
+  /**
+   * Returns the string corresponding to the stored enum value.
+   *
+   * @param aResult   the string representing the enum tag
+   * @param aRealTag  wheter we want to have the real tag or the saved one
+   */
+  void GetEnumString(nsAString& aResult, PRBool aRealTag) const;
+
   // Methods to get access to atoms we may have
   // Returns the number of atoms we have; 0 if we have none.  It's OK
   // to call this without checking the type first; it handles that.
   PRInt32 GetAtomCount() const;
   // Returns the atom at aIndex (0-based).  Do not call this with
   // aIndex >= GetAtomCount().
   nsIAtom* AtomAt(PRInt32 aIndex) const;
 
@@ -194,22 +202,22 @@ public:
     PRInt16 value;
   };
 
   /**
    * Parse into an enum value.
    *
    * @param aValue the string to find the value for
    * @param aTable the enumeration to map with
-   * @param aResult the enum mapping [OUT]
+   * @param aCaseSensitive specify if the parsing has to be case sensitive
    * @return whether the enum value was found or not
    */
   PRBool ParseEnumValue(const nsAString& aValue,
                         const EnumTable* aTable,
-                        PRBool aCaseSensitive = PR_FALSE);
+                        PRBool aCaseSensitive);
 
   /**
    * Parse a string into an integer. Can optionally parse percent (n%).
    * This method explicitly sets a lower bound of zero on the element,
    * whether it be percent or raw integer.
    *
    * @param aString the string to parse
    * @param aCanBePercent PR_TRUE if it can be a percent value (%)
@@ -301,16 +309,27 @@ private:
       nsISVGValue* mSVGValue;
 #endif
       float mFloatValue;
     };
   };
 
   inline ValueBaseType BaseType() const;
 
+  /**
+   * Get the index of an EnumTable in the sEnumTableArray.
+   * If the EnumTable is not in the sEnumTableArray, it is added.
+   * If there is no more space in sEnumTableArray, it returns PR_FALSE.
+   *
+   * @param aTable   the EnumTable to get the index of.
+   * @param aResult  the index of the EnumTable.
+   * @return         whether the index has been found or inserted.
+   */
+  PRBool GetEnumTableIndex(const EnumTable* aTable, PRInt16& aResult);
+
   inline void SetPtrValueAndType(void* aValue, ValueBaseType aType);
   void SetIntValueAndType(PRInt32 aValue, ValueType aType,
                           const nsAString* aStringValue);
   void SetColorValue(nscolor aColor, const nsAString& aString);
   void SetMiscAtomOrString(const nsAString* aValue);
   void ResetMiscAtomOrString();
   inline void ResetIfSet();
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -1157,17 +1157,17 @@ nsGenericHTMLElement::IsNodeOfType(PRUin
 PRBool
 nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
                                      nsIAtom* aAttribute,
                                      const nsAString& aValue,
                                      nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::dir) {
-      return aResult.ParseEnumValue(aValue, kDirTable);
+      return aResult.ParseEnumValue(aValue, kDirTable, PR_FALSE);
     }
   
     if (aAttribute == nsGkAtoms::tabindex) {
       return aResult.ParseIntWithBounds(aValue, -32768, 32767);
     }
 
     if (aAttribute == nsGkAtoms::name && !aValue.IsEmpty()) {
       // Store name as an atom.  name="" means that the element has no name,
@@ -1406,17 +1406,17 @@ static const nsAttrValue::EnumTable kTab
   { "baseline",NS_STYLE_VERTICAL_ALIGN_BASELINE },
   { 0 }
 };
 
 PRBool
 nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
                                       nsAttrValue& aResult)
 {
-  return aResult.ParseEnumValue(aString, kAlignTable);
+  return aResult.ParseEnumValue(aString, kAlignTable, PR_FALSE);
 }
 
 //----------------------------------------
 
 // Vanilla table as defined by the html4 spec...
 static const nsAttrValue::EnumTable kTableHAlignTable[] = {
   { "left",   NS_STYLE_TEXT_ALIGN_LEFT },
   { "right",  NS_STYLE_TEXT_ALIGN_RIGHT },
@@ -1439,19 +1439,19 @@ static const nsAttrValue::EnumTable kCom
   { 0 }
 };
 
 PRBool
 nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
                                             nsAttrValue& aResult) const
 {
   if (InNavQuirksMode(GetOwnerDoc())) {
-    return aResult.ParseEnumValue(aString, kCompatTableHAlignTable);
+    return aResult.ParseEnumValue(aString, kCompatTableHAlignTable, PR_FALSE);
   }
-  return aResult.ParseEnumValue(aString, kTableHAlignTable);
+  return aResult.ParseEnumValue(aString, kTableHAlignTable, PR_FALSE);
 }
 
 //----------------------------------------
 
 // These tables are used for TD,TH,TR, etc (but not TABLE)
 static const nsAttrValue::EnumTable kTableCellHAlignTable[] = {
   { "left",   NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
   { "right",  NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
@@ -1477,35 +1477,35 @@ static const nsAttrValue::EnumTable kCom
   { 0 }
 };
 
 PRBool
 nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
                                                 nsAttrValue& aResult) const
 {
   if (InNavQuirksMode(GetOwnerDoc())) {
-    return aResult.ParseEnumValue(aString, kCompatTableCellHAlignTable);
+    return aResult.ParseEnumValue(aString, kCompatTableCellHAlignTable, PR_FALSE);
   }
-  return aResult.ParseEnumValue(aString, kTableCellHAlignTable);
+  return aResult.ParseEnumValue(aString, kTableCellHAlignTable, PR_FALSE);
 }
 
 //----------------------------------------
 
 PRBool
 nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
                                             nsAttrValue& aResult)
 {
-  return aResult.ParseEnumValue(aString, kTableVAlignTable);
+  return aResult.ParseEnumValue(aString, kTableVAlignTable, PR_FALSE);
 }
 
-PRBool
+PRBool 
 nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
                                          nsAttrValue& aResult) const
 {
-  return aResult.ParseEnumValue(aString, kDivAlignTable);
+  return aResult.ParseEnumValue(aString, kDivAlignTable, PR_FALSE);
 }
 
 PRBool
 nsGenericHTMLElement::ParseImageAttribute(nsIAtom* aAttribute,
                                           const nsAString& aString,
                                           nsAttrValue& aResult)
 {
   if ((aAttribute == nsGkAtoms::width) ||
@@ -1519,24 +1519,24 @@ nsGenericHTMLElement::ParseImageAttribut
   }
   return PR_FALSE;
 }
 
 PRBool
 nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
                                             nsAttrValue& aResult)
 {
-  return aResult.ParseEnumValue(aString, kFrameborderTable);
+  return aResult.ParseEnumValue(aString, kFrameborderTable, PR_FALSE);
 }
 
 PRBool
 nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
                                           nsAttrValue& aResult)
 {
-  return aResult.ParseEnumValue(aString, kScrollingTable);
+  return aResult.ParseEnumValue(aString, kScrollingTable, PR_FALSE);
 }
 
 /**
  * Handle attributes common to all html elements
  */
 void
 nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
                                               nsRuleData* aData)
@@ -2134,16 +2134,34 @@ nsGenericHTMLElement::GetURIListAttr(nsI
         break;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
+nsGenericHTMLElement::GetEnumAttr(nsIAtom* aAttr,
+                                  const char* aDefault,
+                                  nsAString& aResult)
+{
+  const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
+
+  aResult.Truncate();
+
+  if (attrVal && attrVal->Type() == nsAttrValue::eEnum) {
+    attrVal->GetEnumString(aResult, PR_TRUE);
+  } else {
+    AppendASCIItoUTF16(nsDependentCString(aDefault), aResult);
+  }
+
+  return NS_OK;
+}
+
+nsresult
 nsGenericHTMLElement::GetContentEditable(nsAString& aContentEditable)
 {
   ContentEditableTristate value = GetContentEditableValue();
 
   if (value == eTrue) {
     aContentEditable.AssignLiteral("true");
   }
   else if (value == eFalse) {
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -658,16 +658,29 @@ protected:
    * attributes in null namespace.
    *
    * @param aAttr    name of attribute.
    * @param aResult  result value [out]
    */
   NS_HIDDEN_(nsresult) GetURIListAttr(nsIAtom* aAttr, nsAString& aResult);
 
   /**
+   * Helper method for NS_IMPL_ENUM_ATTR_DEFAULT_VALUE.
+   * Gets the enum value string of an attribute and using a default value if
+   * the attribute is missing or the string is an invalid enum value.
+   *
+   * @param aType     the name of the attribute.
+   * @param aDefault  the default value if the attribute is missing or invalid.
+   * @param aResult   string corresponding to the value [out].
+   */
+  NS_HIDDEN_(nsresult) GetEnumAttr(nsIAtom* aAttr,
+                                   const char* aDefault,
+                                   nsAString& aResult);
+
+  /**
    * Locates the nsIEditor associated with this node.  In general this is
    * equivalent to GetEditorInternal(), but for designmode or contenteditable,
    * this may need to get an editor that's not actually on this element's
    * associated TextControlFrame.  This is used by the spellchecking routines
    * to get the editor affected by changing the spellcheck attribute on this
    * node.
    */
   virtual already_AddRefed<nsIEditor> GetAssociatedEditor();
@@ -1059,16 +1072,33 @@ NS_NewHTML##_elementName##Element(nsINod
   {                                                                       \
     if (aValue < 0) {                                                     \
       return NS_ERROR_DOM_INDEX_SIZE_ERR;                                 \
     }                                                                     \
     return SetIntAttr(nsGkAtoms::_atom, aValue);                          \
   }
 
 /**
+ * A macro to implement the getter and setter for a given content
+ * property that needs to set an enumerated string. The method
+ * uses a specific GetEnumAttr and the generic SetAttrHelper methods.
+ */
+#define NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(_class, _method, _atom, _default) \
+  NS_IMETHODIMP                                                           \
+  _class::Get##_method(nsAString& aValue)                                 \
+  {                                                                       \
+    return GetEnumAttr(nsGkAtoms::_atom, _default, aValue);               \
+  }                                                                       \
+  NS_IMETHODIMP                                                           \
+  _class::Set##_method(const nsAString& aValue)                           \
+  {                                                                       \
+    return SetAttrHelper(nsGkAtoms::_atom, aValue);                       \
+  }
+
+/**
  * QueryInterface() implementation helper macros
  */
 
 #define NS_HTML_CONTENT_INTERFACE_TABLE_AMBIGUOUS_BEGIN(_class, _base)        \
   NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class)                            \
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNode, _base)             \
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMElement, _base)          \
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMHTMLElement, _base)
--- a/content/html/content/src/nsHTMLBRElement.cpp
+++ b/content/html/content/src/nsHTMLBRElement.cpp
@@ -115,17 +115,17 @@ static const nsAttrValue::EnumTable kCle
 
 PRBool
 nsHTMLBRElement::ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
   if (aAttribute == nsGkAtoms::clear && aNamespaceID == kNameSpaceID_None) {
-    return aResult.ParseEnumValue(aValue, kClearTable);
+    return aResult.ParseEnumValue(aValue, kClearTable, PR_FALSE);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 static void
 MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -61,16 +61,26 @@
 #include "nsPresState.h"
 #include "nsLayoutErrors.h"
 #include "nsFocusManager.h"
 #include "nsHTMLFormElement.h"
 
 #define NS_IN_SUBMIT_CLICK      (1 << 0)
 #define NS_OUTER_ACTIVATE_EVENT (1 << 1)
 
+static const nsAttrValue::EnumTable kButtonTypeTable[] = {
+  { "button", NS_FORM_BUTTON_BUTTON },
+  { "reset", NS_FORM_BUTTON_RESET },
+  { "submit", NS_FORM_BUTTON_SUBMIT },
+  { 0 }
+};
+
+// Default type is 'submit'.
+static const nsAttrValue::EnumTable* kButtonDefaultType = &kButtonTypeTable[2];
+
 class nsHTMLButtonElement : public nsGenericHTMLFormElement,
                             public nsIDOMHTMLButtonElement,
                             public nsIDOMNSHTMLButtonElement
 {
 public:
   nsHTMLButtonElement(nsINodeInfo *aNodeInfo);
   virtual ~nsHTMLButtonElement();
 
@@ -105,16 +115,21 @@ public:
   NS_IMETHOD SaveState();
   PRBool RestoreState(nsPresState* aState);
 
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                  const nsAString* aValue, PRBool aNotify);
+  /**
+   * Called when an attribute has just been changed
+   */
+  nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                        const nsAString* aValue, PRBool aNotify);
   
   // nsIContent overrides...
   virtual PRBool IsHTMLFocusable(PRBool *aIsFocusable, PRInt32 *aTabIndex);
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
@@ -139,17 +154,17 @@ private:
 // Construction, destruction
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Button)
 
 
 nsHTMLButtonElement::nsHTMLButtonElement(nsINodeInfo *aNodeInfo)
   : nsGenericHTMLFormElement(aNodeInfo),
-    mType(NS_FORM_BUTTON_SUBMIT),  // default
+    mType(kButtonDefaultType->value),
     mHandlingClick(PR_FALSE),
     mDisabledChanged(PR_FALSE),
     mInInternalActivate(PR_FALSE)
 {
 }
 
 nsHTMLButtonElement::~nsHTMLButtonElement()
 {
@@ -186,17 +201,18 @@ nsHTMLButtonElement::GetForm(nsIDOMHTMLF
   return nsGenericHTMLFormElement::GetForm(aForm);
 }
 
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, AccessKey, accesskey)
 NS_IMPL_BOOL_ATTR(nsHTMLButtonElement, Disabled, disabled)
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Name, name)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, TabIndex, tabindex, 0)
 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Value, value)
-NS_IMPL_STRING_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, Type, type, "submit")
+NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, Type, type,
+                                kButtonDefaultType->tag)
 
 NS_IMETHODIMP
 nsHTMLButtonElement::Blur()
 {
   return nsGenericHTMLElement::Blur();
 }
 
 NS_IMETHODIMP
@@ -250,37 +266,33 @@ nsHTMLButtonElement::IsHTMLFocusable(PRB
     *aTabIndex = -1;
   }
 
   *aIsFocusable = !HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
 
   return PR_FALSE;
 }
 
-static const nsAttrValue::EnumTable kButtonTypeTable[] = {
-  { "button", NS_FORM_BUTTON_BUTTON },
-  { "reset", NS_FORM_BUTTON_RESET },
-  { "submit", NS_FORM_BUTTON_SUBMIT },
-  { 0 }
-};
-
 PRBool
 nsHTMLButtonElement::ParseAttribute(PRInt32 aNamespaceID,
                                     nsIAtom* aAttribute,
                                     const nsAString& aValue,
                                     nsAttrValue& aResult)
 {
   if (aAttribute == nsGkAtoms::type && kNameSpaceID_None == aNamespaceID) {
     // XXX ARG!! This is major evilness. ParseAttribute
     // shouldn't set members. Override SetAttr instead
-    PRBool res = aResult.ParseEnumValue(aValue, kButtonTypeTable);
-    if (res) {
+    PRBool success = aResult.ParseEnumValue(aValue, kButtonTypeTable, PR_FALSE);
+    if (success) {
       mType = aResult.GetEnumValue();
+    } else {
+      mType = kButtonDefaultType->value;
     }
-    return res;
+
+    return success;
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 nsresult
 nsHTMLButtonElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
@@ -569,16 +581,29 @@ nsHTMLButtonElement::BeforeSetAttr(PRInt
       aNameSpaceID == kNameSpaceID_None) {
     mDisabledChanged = PR_TRUE;
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
+nsresult
+nsHTMLButtonElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                                  const nsAString* aValue, PRBool aNotify)
+{
+  if (!aValue && aNameSpaceID == kNameSpaceID_None &&
+    aName == nsGkAtoms::type) {
+    mType = kButtonDefaultType->value;
+  }
+
+  return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
+                                                aValue, aNotify);
+}
+
 NS_IMETHODIMP
 nsHTMLButtonElement::SaveState()
 {
   if (!mDisabledChanged) {
     return NS_OK;
   }
   
   nsPresState *state = nsnull;
--- a/content/html/content/src/nsHTMLFontElement.cpp
+++ b/content/html/content/src/nsHTMLFontElement.cpp
@@ -146,17 +146,17 @@ nsHTMLFontElement::ParseAttribute(PRInt3
                                   nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::size) {
       nsAutoString tmp(aValue);
       tmp.CompressWhitespace(PR_TRUE, PR_TRUE);
       PRUnichar ch = tmp.IsEmpty() ? 0 : tmp.First();
       if ((ch == '+' || ch == '-') &&
-          aResult.ParseEnumValue(aValue, kRelFontSizeTable)) {
+          aResult.ParseEnumValue(aValue, kRelFontSizeTable, PR_FALSE)) {
         return PR_TRUE;
       }
 
       return aResult.ParseIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::pointSize ||
         aAttribute == nsGkAtoms::fontWeight) {
       return aResult.ParseIntValue(aValue);
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -431,20 +431,20 @@ static const nsAttrValue::EnumTable kFor
 PRBool
 nsHTMLFormElement::ParseAttribute(PRInt32 aNamespaceID,
                                   nsIAtom* aAttribute,
                                   const nsAString& aValue,
                                   nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::method) {
-      return aResult.ParseEnumValue(aValue, kFormMethodTable);
+      return aResult.ParseEnumValue(aValue, kFormMethodTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::enctype) {
-      return aResult.ParseEnumValue(aValue, kFormEnctypeTable);
+      return aResult.ParseEnumValue(aValue, kFormEnctypeTable, PR_FALSE);
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 nsresult
--- a/content/html/content/src/nsHTMLHRElement.cpp
+++ b/content/html/content/src/nsHTMLHRElement.cpp
@@ -134,17 +134,17 @@ nsHTMLHRElement::ParseAttribute(PRInt32 
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::width) {
       return aResult.ParseSpecialIntValue(aValue, PR_TRUE);
     }
     if (aAttribute == nsGkAtoms::size) {
       return aResult.ParseIntWithBounds(aValue, 1, 1000);
     }
     if (aAttribute == nsGkAtoms::align) {
-      return aResult.ParseEnumValue(aValue, kAlignTable);
+      return aResult.ParseEnumValue(aValue, kAlignTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::color) {
       return aResult.ParseColor(aValue, GetOwnerDoc());
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -148,16 +148,33 @@ static NS_DEFINE_CID(kLookAndFeelCID, NS
   NS_ORIGINAL_INDETERMINATE_VALUE))
 
 static const char kWhitespace[] = "\n\r\t\b";
 
 // whether textfields should be selected once focused:
 //  -1: no, 1: yes, 0: uninitialized
 static PRInt32 gSelectTextFieldOnFocus;
 
+static const nsAttrValue::EnumTable kInputTypeTable[] = {
+  { "button", NS_FORM_INPUT_BUTTON },
+  { "checkbox", NS_FORM_INPUT_CHECKBOX },
+  { "file", NS_FORM_INPUT_FILE },
+  { "hidden", NS_FORM_INPUT_HIDDEN },
+  { "reset", NS_FORM_INPUT_RESET },
+  { "image", NS_FORM_INPUT_IMAGE },
+  { "password", NS_FORM_INPUT_PASSWORD },
+  { "radio", NS_FORM_INPUT_RADIO },
+  { "submit", NS_FORM_INPUT_SUBMIT },
+  { "text", NS_FORM_INPUT_TEXT },
+  { 0 }
+};
+
+// Default type is 'text'.
+static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[9];
+
 #define NS_INPUT_ELEMENT_STATE_IID                 \
 { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */       \
   0xdc3b3d14,                                      \
   0x23e2,                                          \
   0x4479,                                          \
   {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
 }
 
@@ -473,17 +490,17 @@ static nsresult FireEventForAccessibilit
 // construction, destruction
 //
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
 
 nsHTMLInputElement::nsHTMLInputElement(nsINodeInfo *aNodeInfo,
                                        PRBool aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
-    mType(NS_FORM_INPUT_TEXT), // default value
+    mType(kInputDefaultType->value),
     mBitField(0),
     mValue(nsnull)
 {
   SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser);
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
 {
@@ -671,17 +688,17 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
       MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
 
       UpdateEditableState();
 
       if (!aValue) {
         // We're now a text input.  Note that we have to handle this manually,
         // since removing an attribute (which is what happened, since aValue is
         // null) doesn't call ParseAttribute.
-        mType = NS_FORM_INPUT_TEXT;
+        mType = kInputDefaultType->value;
       }
     
       // If we are changing type from File/Text/Passwd to other input types
       // we need save the mValue into value attribute
       if (mValue &&
           mType != NS_FORM_INPUT_TEXT &&
           mType != NS_FORM_INPUT_PASSWORD &&
           mType != NS_FORM_INPUT_FILE) {
@@ -768,18 +785,19 @@ NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Mu
 NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
 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_DEFAULT_VALUE(nsHTMLInputElement, Type, type, "text")
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Placeholder, placeholder)
+NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Type, type,
+                                kInputDefaultType->tag)
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetDefaultValue(nsAString& aValue)
 {
   GetAttrHelper(nsGkAtoms::value, aValue);
 
   if (mType != NS_FORM_INPUT_HIDDEN) {
     // Bug 114997: trim \n, etc. for non-hidden inputs
@@ -2204,46 +2222,32 @@ nsHTMLInputElement::UnbindFromTree(PRBoo
   // have a form
   if (!mForm && mType == NS_FORM_INPUT_RADIO) {
     WillRemoveFromRadioGroup();
   }
 
   nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
 }
 
-static const nsAttrValue::EnumTable kInputTypeTable[] = {
-  { "button", NS_FORM_INPUT_BUTTON },
-  { "checkbox", NS_FORM_INPUT_CHECKBOX },
-  { "file", NS_FORM_INPUT_FILE },
-  { "hidden", NS_FORM_INPUT_HIDDEN },
-  { "reset", NS_FORM_INPUT_RESET },
-  { "image", NS_FORM_INPUT_IMAGE },
-  { "password", NS_FORM_INPUT_PASSWORD },
-  { "radio", NS_FORM_INPUT_RADIO },
-  { "submit", NS_FORM_INPUT_SUBMIT },
-  { "text", NS_FORM_INPUT_TEXT },
-  { 0 }
-};
-
 PRBool
 nsHTMLInputElement::ParseAttribute(PRInt32 aNamespaceID,
                                    nsIAtom* aAttribute,
                                    const nsAString& aValue,
                                    nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::type) {
       // XXX ARG!! This is major evilness. ParseAttribute
       // shouldn't set members. Override SetAttr instead
       PRInt32 newType;
-      PRBool success;
-      if ((success = aResult.ParseEnumValue(aValue, kInputTypeTable))) {
+      PRBool success = aResult.ParseEnumValue(aValue, kInputTypeTable, PR_FALSE);
+      if (success) {
         newType = aResult.GetEnumValue();
       } else {
-        newType = NS_FORM_INPUT_TEXT;
+        newType = kInputDefaultType->value;
       }
 
       if (newType != mType) {
         // Make sure to do the check for newType being NS_FORM_INPUT_FILE and
         // the corresponding SetValueInternal() call _before_ we set mType.
         // That way the logic in SetValueInternal() will work right (that logic
         // makes assumptions about our frame based on mType, but we won't have
         // had time to recreate frames yet -- that happens later in the
@@ -2286,44 +2290,16 @@ nsHTMLInputElement::ParseAttribute(PRInt
       return PR_TRUE;
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
-NS_IMETHODIMP
-nsHTMLInputElement::GetType(nsAString& aValue)
-{
-  const nsAttrValue::EnumTable *table = kInputTypeTable;
-
-  while (table->tag) {
-    if (mType == table->value) {
-      CopyUTF8toUTF16(table->tag, aValue);
-
-      return NS_OK;
-    }
-
-    ++table;
-  }
-
-  NS_ERROR("Shouldn't get here!");
-
-  aValue.Truncate();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsHTMLInputElement::SetType(const nsAString& aValue)
-{
-  return SetAttrHelper(nsGkAtoms::type, aValue);
-}
-
 static void
 MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                       nsRuleData* aData)
 {
   const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type);
   if (value && value->Type() == nsAttrValue::eEnum &&
       value->GetEnumValue() == NS_FORM_INPUT_IMAGE) {
     nsGenericHTMLFormElement::MapImageBorderAttributeInto(aAttributes, aData);
--- a/content/html/content/src/nsHTMLLIElement.cpp
+++ b/content/html/content/src/nsHTMLLIElement.cpp
@@ -130,17 +130,17 @@ nsHTMLLIElement::ParseAttribute(PRInt32 
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::type) {
       return aResult.ParseEnumValue(aValue, kOrderedListItemTypeTable,
                                     PR_TRUE) ||
-             aResult.ParseEnumValue(aValue, kUnorderedListItemTypeTable);
+             aResult.ParseEnumValue(aValue, kUnorderedListItemTypeTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::value) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
--- a/content/html/content/src/nsHTMLLegendElement.cpp
+++ b/content/html/content/src/nsHTMLLegendElement.cpp
@@ -164,17 +164,17 @@ static const nsAttrValue::EnumTable kAli
 
 PRBool
 nsHTMLLegendElement::ParseAttribute(PRInt32 aNamespaceID,
                                     nsIAtom* aAttribute,
                                     const nsAString& aValue,
                                     nsAttrValue& aResult)
 {
   if (aAttribute == nsGkAtoms::align && aNamespaceID == kNameSpaceID_None) {
-    return aResult.ParseEnumValue(aValue, kAlignTable);
+    return aResult.ParseEnumValue(aValue, kAlignTable, PR_FALSE);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 nsChangeHint
 nsHTMLLegendElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
--- a/content/html/content/src/nsHTMLOListElement.cpp
+++ b/content/html/content/src/nsHTMLOListElement.cpp
@@ -159,17 +159,17 @@ nsHTMLSharedListElement::ParseAttribute(
                                         nsIAtom* aAttribute,
                                         const nsAString& aValue,
                                         nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (mNodeInfo->Equals(nsGkAtoms::ol) ||
         mNodeInfo->Equals(nsGkAtoms::ul)) {
       if (aAttribute == nsGkAtoms::type) {
-        return aResult.ParseEnumValue(aValue, kListTypeTable) ||
+        return aResult.ParseEnumValue(aValue, kListTypeTable, PR_FALSE) ||
                aResult.ParseEnumValue(aValue, kOldListTypeTable, PR_TRUE);
       }
       if (aAttribute == nsGkAtoms::start) {
         return aResult.ParseIntValue(aValue);
       }
     }
   }
 
--- a/content/html/content/src/nsHTMLSharedElement.cpp
+++ b/content/html/content/src/nsHTMLSharedElement.cpp
@@ -258,17 +258,17 @@ nsHTMLSharedElement::ParseAttribute(PRIn
       if (aAttribute == nsGkAtoms::width ||
           aAttribute == nsGkAtoms::height) {
         return aResult.ParseSpecialIntValue(aValue, PR_TRUE);
       }
     }
     else if (mNodeInfo->Equals(nsGkAtoms::dir) ||
              mNodeInfo->Equals(nsGkAtoms::menu)) {
       if (aAttribute == nsGkAtoms::type) {
-        return aResult.ParseEnumValue(aValue, kListTypeTable);
+        return aResult.ParseEnumValue(aValue, kListTypeTable, PR_FALSE);
       }
       if (aAttribute == nsGkAtoms::start) {
         return aResult.ParseIntWithBounds(aValue, 1);
       }
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
--- a/content/html/content/src/nsHTMLTableCaptionElement.cpp
+++ b/content/html/content/src/nsHTMLTableCaptionElement.cpp
@@ -119,17 +119,17 @@ static const nsAttrValue::EnumTable kCap
 
 PRBool
 nsHTMLTableCaptionElement::ParseAttribute(PRInt32 aNamespaceID,
                                           nsIAtom* aAttribute,
                                           const nsAString& aValue,
                                           nsAttrValue& aResult)
 {
   if (aAttribute == nsGkAtoms::align && aNamespaceID == kNameSpaceID_None) {
-    return aResult.ParseEnumValue(aValue, kCaptionAlignTable);
+    return aResult.ParseEnumValue(aValue, kCaptionAlignTable, PR_FALSE);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 static 
 void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData)
--- a/content/html/content/src/nsHTMLTableCellElement.cpp
+++ b/content/html/content/src/nsHTMLTableCellElement.cpp
@@ -311,17 +311,17 @@ nsHTMLTableCellElement::ParseAttribute(P
     }
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableCellHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue, GetOwnerDoc());
     }
     if (aAttribute == nsGkAtoms::scope) {
-      return aResult.ParseEnumValue(aValue, kCellScopeTable);
+      return aResult.ParseEnumValue(aValue, kCellScopeTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::valign) {
       return ParseTableVAlignValue(aValue, aResult);
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -1008,23 +1008,23 @@ nsHTMLTableElement::ParseAttribute(PRInt
     if (aAttribute == nsGkAtoms::align) {
       return ParseTableHAlignValue(aValue, aResult);
     }
     if (aAttribute == nsGkAtoms::bgcolor ||
         aAttribute == nsGkAtoms::bordercolor) {
       return aResult.ParseColor(aValue, GetOwnerDoc());
     }
     if (aAttribute == nsGkAtoms::frame) {
-      return aResult.ParseEnumValue(aValue, kFrameTable);
+      return aResult.ParseEnumValue(aValue, kFrameTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::layout) {
-      return aResult.ParseEnumValue(aValue, kLayoutTable);
+      return aResult.ParseEnumValue(aValue, kLayoutTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::rules) {
-      return aResult.ParseEnumValue(aValue, kRulesTable);
+      return aResult.ParseEnumValue(aValue, kRulesTable, PR_FALSE);
     }
     if (aAttribute == nsGkAtoms::hspace ||
         aAttribute == nsGkAtoms::vspace) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -154,12 +154,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug529859.html \
 		test_bug535043.html \
 		test_bug547850.html \
 		test_bug457800.html \
 		test_bug536891.html \
 		test_bug536895.html \
 		test_bug458037.xhtml \
 		test_bug559284.html \
+        test_bug551670.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_bug551670.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551670
+-->
+<head>
+  <title>Test for Bug 551670</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=551670">Mozilla Bug 551670</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <button type='badtype' id='b1'></button>
+  <button id='b2'></button>
+  <input type='badtype' id='i1'>
+  <input id='i2'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 551670 **/
+
+function checkType(e1, e2, defaultType, otherType, wrongType)
+{
+  // When accessing to the type attribute from the IDL,
+  // it should reflect the state of the button.
+
+  defaultType = defaultType.toLowerCase();
+  otherType = otherType.toLowerCase();
+
+  is(e1.type, defaultType,
+    "When the initial type attribute value is not valid, the state should be '" +
+    defaultType + "'");
+  is(e2.type, defaultType,
+    "When the type attribute is not set, the state should be '" +
+    defaultType + "'");
+
+  e1.type = otherType;
+  e1.setAttribute('type', '');
+  is(e1.type, defaultType,
+    "When type attribute is set to an empty string, the state should be '" +
+    defaultType + "'");
+
+  e1.type = otherType;
+  e1.type = '';
+  is(e1.type, defaultType,
+    "When type attribute is set to an empty string, the state should be '" +
+    defaultType + "'");
+
+  e1.type = otherType;
+  e1.setAttribute('type', wrongType);
+  is(e1.type, defaultType,
+    "When type attribute is set to an invalid value, the state should be '" +
+    defaultType + "'");
+  is(e1.getAttribute('type'), wrongType,
+    "Type attribute content should not be changed");
+
+  e1.type = otherType;
+  e1.type = wrongType;
+  is(e1.type, defaultType,
+    "When type attribute is set to an invalid value, the state should be '" +
+    defaultType + "'");
+  is(e1.getAttribute('type'), wrongType,
+    "Type attribute content should not be changed");
+
+  e1.type = otherType.toUpperCase();
+  is(e1.type, otherType, "Type attribute should be case insensitive");
+
+  e1.removeAttribute('type');
+  is(e1.type, defaultType,
+    "When type attribute is set to an empty string, the state should be '" +
+    defaultType + "'");
+}
+
+var wrongType = 'this-is-probably-a-wrong-type';
+
+// button types
+checkType(document.getElementById('b1'), document.getElementById('b2'), 'submit', 'button', wrongType);
+checkType(document.getElementById('b1'), document.getElementById('b2'), 'submit', 'reset', wrongType);
+
+// input types
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'button', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'checkbox', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'file', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'hidden', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'reset', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'image', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'password', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'radio', wrongType);
+checkType(document.getElementById('i1'), document.getElementById('i2'), 'text', 'submit', wrongType);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/reftest.list
@@ -0,0 +1,2 @@
+# submit button default tests
+include submit-button/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: when there is only one submit button, it is the default -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <body>
+    <form>
+      <button type='submit'>submit</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-2.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: when there is only one submit button and some other buttons,
+    there is a default -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <body>
+    <form>
+      <button type='submit'>submit</button>
+      <button type='reset'>text</button>
+      <button type='reset'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-3.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <!-- Test: when there more thon one submit button and some other buttons,
+    the first submit button is the default -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <body>
+    <form>
+      <button type='submit'>submit</button>
+      <button type='reset'>text</button>
+      <button type='submit'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-4.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when there is a submit button which is changed,
+    there is no default -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <script type="text/javascript">
+    function onLoadHandler()
+    {
+      document.getElementById('b1').type = "reset";
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="onLoadHandler();">
+    <form>
+      <button type='submit' id='b1'>submit</button>
+      <button type='reset'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-5.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when there is no submit button and one is added,
+    there is a default submit button -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <script type="text/javascript">
+    function onLoadHandler()
+    {
+      document.getElementById('b1').type = "submit";
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="onLoadHandler();">
+    <form>
+      <button type='submit' id='b1'>submit</button>
+      <button type='reset'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-6.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when there is no submit button and one is added,
+    there is a default submit button -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <script type="text/javascript">
+    function onLoadHandler()
+    {
+      document.getElementById('b1').type = "bad-value";
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="onLoadHandler();">
+    <form>
+      <button type='submit' id='b1'>submit</button>
+      <button type='reset'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-7.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when there is no submit button and one is added,
+    there is a default submit button -->
+  <link rel='stylesheet' type='text/css' href='default-submit-button-style.css'>
+  <script type="text/javascript">
+    function onLoadHandler()
+    {
+      document.getElementById('b1').removeAttribute('type');
+      document.documentElement.className = '';
+    }
+  </script>
+
+  <body onload="onLoadHandler();">
+    <form>
+      <button type='submit' id='b1'>submit</button>
+      <button type='reset'>text</button>
+    </form>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <button type="submit">submit</button>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/default-submit-button-style.css
@@ -0,0 +1,2 @@
+button { visibility: hidden; }
+button:default { visibility: visible; }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-default/submit-button/reftest.list
@@ -0,0 +1,7 @@
+== default-submit-button-1.html default-submit-button-ref.html
+== default-submit-button-2.html default-submit-button-ref.html
+== default-submit-button-3.html default-submit-button-ref.html
+== default-submit-button-4.html about:blank
+== default-submit-button-5.html default-submit-button-ref.html
+== default-submit-button-6.html default-submit-button-ref.html
+== default-submit-button-7.html default-submit-button-ref.html
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -36,16 +36,19 @@ include box-properties/reftest.list
 include box-shadow/reftest.list
 
 # bugs/
 include bugs/reftest.list
 
 # canvas 2D
 include canvas/reftest.list
 
+# css default pseudo class tests
+include css-default/reftest.list
+
 # css @import tests
 include css-import/reftest.list
 
 # css character encoding tests
 include css-charset/reftest.list
 
 # css gradients
 include css-gradients/reftest.list