From 6bc000abf5fbc5080c3742ed2b209ebd7679b91d Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 17 Jun 2013 13:07:04 -0400 Subject: [PATCH] Bug 879319 - implement past names map in HTMLFormElement, r=bz --- content/html/content/src/HTMLImageElement.cpp | 9 +- .../html/content/src/nsGenericHTMLElement.cpp | 15 ++- .../html/content/src/nsHTMLFormElement.cpp | 64 +++++++++++-- content/html/content/src/nsHTMLFormElement.h | 23 ++++- content/html/content/test/Makefile.in | 1 + content/html/content/test/test_bug879319.html | 91 +++++++++++++++++++ 6 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 content/html/content/test/test_bug879319.html 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 6e8be1616349..cecf18b47f15 100644 --- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -358,6 +358,7 @@ MOCHITEST_FILES = \ wakelock.ogv \ test_bug869040.html \ test_bug870787.html \ + test_bug879319.html \ allowMedia.sjs \ $(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 + +

+ +
+ +
+ + + + +
+
+
+ +