diff --git a/content/html/content/src/HTMLImageElement.cpp b/content/html/content/src/HTMLImageElement.cpp
index 8bff2e39b705..cc9c2fd30f0d 100644
--- a/content/html/content/src/HTMLImageElement.cpp
+++ b/content/html/content/src/HTMLImageElement.cpp
@@ -311,7 +311,8 @@ HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
GetAttr(kNameSpaceID_None, aName, tmp);
if (!tmp.IsEmpty()) {
- mForm->RemoveImageElementFromTable(this, tmp);
+ mForm->RemoveImageElementFromTable(this, tmp,
+ nsHTMLFormElement::AttributeUpdated);
}
}
@@ -694,11 +695,13 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
mForm->RemoveImageElement(this);
if (!nameVal.IsEmpty()) {
- mForm->RemoveImageElementFromTable(this, nameVal);
+ mForm->RemoveImageElementFromTable(this, nameVal,
+ nsHTMLFormElement::ElementRemoved);
}
if (!idVal.IsEmpty()) {
- mForm->RemoveImageElementFromTable(this, idVal);
+ mForm->RemoveImageElementFromTable(this, idVal,
+ nsHTMLFormElement::ElementRemoved);
}
}
diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp
index f45608528e46..958ccb0b9546 100644
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2179,11 +2179,13 @@ nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm)
mForm->RemoveElement(this, true);
if (!nameVal.IsEmpty()) {
- mForm->RemoveElementFromTable(this, nameVal);
+ mForm->RemoveElementFromTable(this, nameVal,
+ nsHTMLFormElement::ElementRemoved);
}
if (!idVal.IsEmpty()) {
- mForm->RemoveElementFromTable(this, idVal);
+ mForm->RemoveElementFromTable(this, idVal,
+ nsHTMLFormElement::ElementRemoved);
}
}
@@ -2314,7 +2316,8 @@ nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
GetAttr(kNameSpaceID_None, aName, tmp);
if (!tmp.IsEmpty()) {
- mForm->RemoveElementFromTable(this, tmp);
+ mForm->RemoveElementFromTable(this, tmp,
+ nsHTMLFormElement::AttributeUpdated);
}
}
@@ -2322,13 +2325,15 @@ nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
if (!tmp.IsEmpty()) {
- mForm->RemoveElementFromTable(this, tmp);
+ mForm->RemoveElementFromTable(this, tmp,
+ nsHTMLFormElement::AttributeUpdated);
}
GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
if (!tmp.IsEmpty()) {
- mForm->RemoveElementFromTable(this, tmp);
+ mForm->RemoveElementFromTable(this, tmp,
+ nsHTMLFormElement::AttributeUpdated);
}
mForm->RemoveElement(this, false);
diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp
index 4b9c2a173d5a..5742a14bcd1c 100644
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -247,6 +247,7 @@ nsHTMLFormElement::nsHTMLFormElement(already_AddRefed aNodeInfo)
mEverTriedInvalidSubmit(false)
{
mImageNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE);
+ mPastNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE);
}
nsHTMLFormElement::~nsHTMLFormElement()
@@ -299,6 +300,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLFormElement,
nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
tmp->mSelectedRadioButtons.EnumerateRead(ElementTraverser, &cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -1440,15 +1442,15 @@ RemoveElementFromTableInternal(
if (!aTable.Get(aName, getter_AddRefs(supports)))
return NS_OK;
+ // Single element in the hash, just remove it if it's the one
+ // we're trying to remove...
+ if (supports == aChild) {
+ aTable.Remove(aName);
+ return NS_OK;
+ }
+
nsCOMPtr content(do_QueryInterface(supports));
-
if (content) {
- // Single element in the hash, just remove it if it's the one
- // we're trying to remove...
- if (content == aChild) {
- aTable.Remove(aName);
- }
-
return NS_OK;
}
@@ -1479,10 +1481,25 @@ RemoveElementFromTableInternal(
return NS_OK;
}
+static PLDHashOperator
+RemovePastNames(const nsAString& aName,
+ nsCOMPtr& aData,
+ void* aClosure)
+{
+ return aClosure == aData ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
+}
+
nsresult
nsHTMLFormElement::RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
- const nsAString& aName)
+ const nsAString& aName,
+ RemoveElementReason aRemoveReason)
{
+ // If the element is being removed from the form, we have to remove it from
+ // the past names map.
+ if (aRemoveReason == ElementRemoved) {
+ mPastNameLookupTable.Enumerate(RemovePastNames, aElement);
+ }
+
return mControls->RemoveElementFromTable(aElement, aName);
}
@@ -1494,10 +1511,18 @@ nsHTMLFormElement::FindNamedItem(const nsAString& aName,
if (result) {
// FIXME Get the wrapper cache from DoResolveName.
*aCache = nullptr;
+ AddToPastNamesMap(aName, result);
return result.forget();
}
result = mImageNameLookupTable.GetWeak(aName);
+ if (result) {
+ *aCache = nullptr;
+ AddToPastNamesMap(aName, result);
+ return result.forget();
+ }
+
+ result = mPastNameLookupTable.GetWeak(aName);
if (result) {
*aCache = nullptr;
return result.forget();
@@ -2269,6 +2294,7 @@ nsHTMLFormElement::Clear()
}
mImageElements.Clear();
mImageNameLookupTable.Clear();
+ mPastNameLookupTable.Clear();
}
//----------------------------------------------------------------------
@@ -2705,8 +2731,28 @@ nsHTMLFormElement::RemoveImageElement(HTMLImageElement* aChild)
nsresult
nsHTMLFormElement::RemoveImageElementFromTable(HTMLImageElement* aElement,
- const nsAString& aName)
+ const nsAString& aName,
+ RemoveElementReason aRemoveReason)
{
+ // If the element is being removed from the form, we have to remove it from
+ // the past names map.
+ if (aRemoveReason == ElementRemoved) {
+ mPastNameLookupTable.Enumerate(RemovePastNames, aElement);
+ }
+
return RemoveElementFromTableInternal(mImageNameLookupTable, aElement, aName);
}
+void
+nsHTMLFormElement::AddToPastNamesMap(const nsAString& aName,
+ nsISupports* aChild)
+{
+ // If candidates contains exactly one node. Add a mapping from name to the
+ // node in candidates in the form element's past names map, replacing the
+ // previous entry with the same name, if any.
+ nsCOMPtr node = do_QueryInterface(aChild);
+ if (node) {
+ mPastNameLookupTable.Put(aName, aChild);
+ }
+}
+
diff --git a/content/html/content/src/nsHTMLFormElement.h b/content/html/content/src/nsHTMLFormElement.h
index 00ab0bbe9e78..3702959b52b0 100644
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -149,10 +149,20 @@ public:
*
* @param aElement the element to remove
* @param aName the name or id of the element to remove
+ * @param aRemoveReason describe why this element is removed. If the element
+ * is removed because it's removed from the form, it will be removed
+ * from the past names map too, otherwise it will stay in the past
+ * names map.
* @return NS_OK if the element was successfully removed.
*/
+ enum RemoveElementReason {
+ AttributeUpdated,
+ ElementRemoved
+ };
nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
- const nsAString& aName);
+ const nsAString& aName,
+ RemoveElementReason aRemoveReason);
+
/**
* Add an element to end of this form's list of elements
*
@@ -195,7 +205,8 @@ public:
* @return NS_OK if the element was successfully removed.
*/
nsresult RemoveImageElementFromTable(mozilla::dom::HTMLImageElement* aElement,
- const nsAString& aName);
+ const nsAString& aName,
+ RemoveElementReason aRemoveReason);
/**
* Add an image element to the end of this form's list of image elements
*
@@ -412,6 +423,9 @@ protected:
// Clear the mImageNameLookupTable and mImageElements.
void Clear();
+ // Insert a element into the past names map.
+ void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild);
+
public:
/**
* Flush a possible pending submission. If there was a scripted submission
@@ -480,6 +494,11 @@ protected:
nsInterfaceHashtable mImageNameLookupTable;
+ // A map from names to elements that were gotten by those names from this
+ // form in that past. See "past names map" in the HTML5 specification.
+
+ nsInterfaceHashtable mPastNameLookupTable;
+
/**
* Number of invalid and candidate for constraint validation elements in the
* form the last time UpdateValidity has been called.
diff --git a/content/html/content/test/Makefile.in b/content/html/content/test/Makefile.in
index 43eaf0de25d1..bce4a4cbe9f5 100644
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -379,6 +379,7 @@ MOCHITEST_FILES = \
wakelock.ogv \
test_bug869040.html \
test_bug870787.html \
+ test_bug879319.html \
allowMedia.sjs \
test_bug874758.html \
$(NULL)
diff --git a/content/html/content/test/test_bug879319.html b/content/html/content/test/test_bug879319.html
new file mode 100644
index 000000000000..c1687d5b08b2
--- /dev/null
+++ b/content/html/content/test/test_bug879319.html
@@ -0,0 +1,91 @@
+
+
+
+
+ Test for Bug 879319
+
+
+
+
+
+Mozilla Bug 879319
+
+
+
+
+
+
+
+
+