From 0781ce3df52bf3b6e41ae48f160d7bec6376b1b4 Mon Sep 17 00:00:00 2001 From: "bzbarsky@mit.edu" Date: Mon, 6 Aug 2007 19:07:24 -0700 Subject: [PATCH] Keep track of both the first submit in elements and the first submit not in elements, to speed up some operations. Bug 352980, r+sr+a=jst --- .../html/content/src/nsHTMLFormElement.cpp | 226 ++++++++---------- 1 file changed, 98 insertions(+), 128 deletions(-) diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp index 6e4857a2962..7ca85a29e9d 100644 --- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -296,44 +296,6 @@ protected: nsresult DoResolveName(const nsAString& aName, PRBool aFlushContent, nsISupports** aReturn); - /** - * Reset the default submit element after the previous default submit control - * is removed. - * - * Note that this, in the worst case, needs to run through all the form's - * controls. - * - * @param aNotify If true, send nsIDocumentObserver notifications on the - * new and previous default elements. - * @param aPrevDefaultInElements If true, the previous default submit element - * was in the elements list. If false, it was - * in the not-in-elements list. - * @param aPrevDefaultIndex The index of the previous default submit control - * in the list determined by aPrevDefaultInElements. - */ - void ResetDefaultSubmitElement(PRBool aNotify, - PRBool aPrevDefaultInElements, - PRUint32 aPrevDefaultIndex); - - /** - * Locates the default submit control. This is defined as the first element - * in document order which can submit a form. Returns null if there are no - * submit controls. - * - * Note that this, in the worst case, needs to run through all the form's - * controls. - * - * @param aPrevDefaultInElements If true, the previous default submit element - * was in the elements list. If false, it was - * in the not-in-elements list. - * @param aPrevDefaultIndex The index of the previous default submit control - * in the list determined by aPrevDefaultInElements. - * @return The default submit control for the form, or null if one does - * not exist. - */ - nsIFormControl* FindDefaultSubmit(PRBool aPrevDefaultInElements, - PRUint32 aPrevDefaultIndex) const; - // // Data members // @@ -368,6 +330,12 @@ protected: /** The default submit element -- WEAK */ nsIFormControl* mDefaultSubmitElement; + /** The first submit element in mElements -- WEAK */ + nsIFormControl* mFirstSubmitInElements; + + /** The first submit element in mNotInElements -- WEAK */ + nsIFormControl* mFirstSubmitNotInElements; + protected: /** Detection of first form to notify observers */ static PRBool gFirstFormSubmitted; @@ -522,7 +490,9 @@ nsHTMLFormElement::nsHTMLFormElement(nsINodeInfo *aNodeInfo) mSubmitInitiatedFromUserInput(PR_FALSE), mPendingSubmission(nsnull), mSubmittingRequest(nsnull), - mDefaultSubmitElement(nsnull) + mDefaultSubmitElement(nsnull), + mFirstSubmitInElements(nsnull), + mFirstSubmitNotInElements(nsnull) { } @@ -1312,30 +1282,49 @@ nsHTMLFormElement::AddElement(nsIFormControl* aChild, // Default submit element handling if (aChild->IsSubmitControl()) { - // The new child is the default submit if there was not previously - // a default submit element, or if the new child is before the old - // default submit element. - // To speed up parsing, the special case of a form that already - // has a default submit that's in the same list as the new child - // is ignored, since the new child then cannot be the default - // submit element. - nsIFormControl* oldControl = mDefaultSubmitElement; - if (!mDefaultSubmitElement || - ((!lastElement || - ShouldBeInElements(mDefaultSubmitElement) != childInElements) && - CompareFormControlPosition(aChild, mDefaultSubmitElement, this) < 0)) { - mDefaultSubmitElement = aChild; + // Update mDefaultSubmitElement, mFirstSubmitInElements, + // mFirstSubmitNotInElements. + + nsIFormControl** firstSubmitSlot = + childInElements ? &mFirstSubmitInElements : &mFirstSubmitNotInElements; + + // The new child is the new first submit in its list if the firstSubmitSlot + // is currently empty or if the child is before what's currently in the + // slot. Note that if we already have a control in firstSubmitSlot and + // we're appending this element can't possibly replace what's currently in + // the slot. Also note that aChild can't become the mDefaultSubmitElement + // unless it replaces what's in the slot. If it _does_ replace what's in + // the slot, it becomes the default submit if either the default submit is + // what's in the slot or the child is earlier than the default submit. + nsIFormControl* oldDefaultSubmit = mDefaultSubmitElement; + if (!*firstSubmitSlot || + (!lastElement && + CompareFormControlPosition(aChild, *firstSubmitSlot, this) < 0)) { + NS_ASSERTION(*firstSubmitSlot == mDefaultSubmitElement || + mDefaultSubmitElement, + "How can we have a null mDefaultSubmitElement but a " + "first-submit slot in one of the lists?"); + if (*firstSubmitSlot == mDefaultSubmitElement || + CompareFormControlPosition(aChild, + mDefaultSubmitElement, this) < 0) { + mDefaultSubmitElement = aChild; + } + *firstSubmitSlot = aChild; } + NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements || + mDefaultSubmitElement == mFirstSubmitNotInElements, + "What happened here?"); // Notify that the state of the previous default submit element has changed // if the element which is the default submit element has changed. The new // default submit element is responsible for its own ContentStatesChanged // call. - if (aNotify && oldControl != mDefaultSubmitElement) { + if (aNotify && oldDefaultSubmit && + oldDefaultSubmit != mDefaultSubmitElement) { nsIDocument* document = GetCurrentDoc(); if (document) { MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE); - nsCOMPtr oldElement(do_QueryInterface(oldControl)); + nsCOMPtr oldElement(do_QueryInterface(oldDefaultSubmit)); document->ContentStatesChanged(oldElement, nsnull, NS_EVENT_STATE_DEFAULT); } @@ -1380,86 +1369,63 @@ nsHTMLFormElement::RemoveElement(nsIFormControl* aChild, controls.RemoveElementAt(index); + // Update our mFirstSubmit* values. + nsIFormControl** firstSubmitSlot = + childInElements ? &mFirstSubmitInElements : &mFirstSubmitNotInElements; + if (aChild == *firstSubmitSlot) { + *firstSubmitSlot = nsnull; + + // We are removing the first submit in this list, find the new first submit + PRUint32 length = controls.Length(); + for (PRUint32 i = index; i < length; ++i) { + nsIFormControl* currentControl = controls[i]; + if (currentControl->IsSubmitControl()) { + *firstSubmitSlot = currentControl; + break; + } + } + } + if (aChild == mDefaultSubmitElement) { - // We are removing the default submit, find the new default - ResetDefaultSubmitElement(aNotify, childInElements, index ); + // Need to reset mDefaultSubmitElement + if (!mFirstSubmitNotInElements) { + mDefaultSubmitElement = mFirstSubmitInElements; + } else if (!mFirstSubmitInElements) { + mDefaultSubmitElement = mFirstSubmitNotInElements; + } else { + NS_ASSERTION(mFirstSubmitInElements != mFirstSubmitNotInElements, + "How did that happen?"); + // Have both; use the earlier one + mDefaultSubmitElement = + CompareFormControlPosition(mFirstSubmitInElements, + mFirstSubmitNotInElements, this) < 0 ? + mFirstSubmitInElements : mFirstSubmitNotInElements; + } + + NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements || + mDefaultSubmitElement == mFirstSubmitNotInElements, + "What happened here?"); + + // Notify about change. Note that we don't notify on the old default + // submit (which is being removed) because it's either being removed from + // the DOM or changing attributes in a way that makes it responsible for + // sending its own notifications. + if (aNotify && mDefaultSubmitElement) { + NS_ASSERTION(mDefaultSubmitElement != aChild, + "Notifying but elements haven't changed."); + nsIDocument* document = GetCurrentDoc(); + if (document) { + MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE); + nsCOMPtr newElement(do_QueryInterface(mDefaultSubmitElement)); + document->ContentStatesChanged(newElement, nsnull, + NS_EVENT_STATE_DEFAULT); + } + } } return rv; } -void -nsHTMLFormElement::ResetDefaultSubmitElement(PRBool aNotify, - PRBool aPrevDefaultInElements, - PRUint32 aPrevDefaultIndex) -{ - nsIFormControl* oldDefaultSubmit = mDefaultSubmitElement; - mDefaultSubmitElement = FindDefaultSubmit(aPrevDefaultInElements, - aPrevDefaultIndex); - - // Inform about change. Note that we don't notify on the old default submit - // (which is being removed) because it's either being removed from the DOM or - // changing attributes in a way that makes it responsible for sending its own - // notifications. - if (aNotify && (oldDefaultSubmit || mDefaultSubmitElement)) { - NS_ASSERTION(mDefaultSubmitElement != oldDefaultSubmit, - "Notifying but elements haven't changed."); - nsIDocument* document = GetCurrentDoc(); - if (document) { - MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE); - nsCOMPtr newElement(do_QueryInterface(mDefaultSubmitElement)); - document->ContentStatesChanged(newElement, nsnull, - NS_EVENT_STATE_DEFAULT); - } - } - } - -nsIFormControl* -nsHTMLFormElement::FindDefaultSubmit(PRBool aPrevDefaultInElements, - PRUint32 aPrevDefaultIndex) const -{ - nsIFormControl* defaultSubmit = nsnull; - - // Find the first submit control in the elements list. This list is - // in document order. - PRUint32 length = mControls->mElements.Length(); - - // If the previous default submit element was in the elements - // list, begin the search of the elements list at that index. Given - // that the list is sorted, the new default submit cannot be before - // the index of the old child as it already would've been the default submit. - PRUint32 i = aPrevDefaultInElements ? aPrevDefaultIndex : 0; - for (; i < length; ++i) { - nsIFormControl* currentControl = mControls->mElements[i]; - - if (currentControl->IsSubmitControl()) { - defaultSubmit = currentControl; - break; - } - } - - // elements are submit controls but are not in the - // elements list. Search the not-in-elements list to find any submit control - // that is ordered in the document before any submit element found already. - // See the comment above for when not to start the search at the first - // element. - length = mControls->mNotInElements.Length(); - i = !aPrevDefaultInElements ? aPrevDefaultIndex : 0; - for (; i < length; ++i) { - nsIFormControl* currControl = mControls->mNotInElements[i]; - - if (currControl->IsSubmitControl()) { - if (!defaultSubmit || - CompareFormControlPosition(currControl, defaultSubmit, - NS_CONST_CAST(nsHTMLFormElement*, this)) < 0) { - defaultSubmit = currControl; - } - break; - } - } - return defaultSubmit; -} - NS_IMETHODIMP nsHTMLFormElement::RemoveElementFromTable(nsIFormControl* aElement, const nsAString& aName) @@ -1632,6 +1598,10 @@ nsHTMLFormElement::GetSortedControls(nsTArray& aControls) const NS_IMETHODIMP_(nsIFormControl*) nsHTMLFormElement::GetDefaultSubmitElement() const { + NS_PRECONDITION(mDefaultSubmitElement == mFirstSubmitInElements || + mDefaultSubmitElement == mFirstSubmitNotInElements, + "What happened here?"); + return mDefaultSubmitElement; }