From a3a40f6eb582885df7b55e0a03e78c20c237f83f Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Wed, 16 Aug 2006 03:20:19 +0000 Subject: [PATCH] Implement the :default CSS3 pseudo-class. Bug 302186, patch by Allan Beaufour and jpl24 , r+sr=bzbarsky --- content/events/public/nsIEventStateManager.h | 11 +- content/html/content/public/nsIForm.h | 17 ++- content/html/content/public/nsIFormControl.h | 15 +- .../html/content/src/nsGenericHTMLElement.cpp | 41 ++++-- .../html/content/src/nsGenericHTMLElement.h | 6 +- .../html/content/src/nsHTMLFormElement.cpp | 125 +++++++++++++++-- .../html/content/src/nsHTMLInputElement.cpp | 132 ++++++++++-------- .../html/content/src/nsHTMLOptionElement.cpp | 18 ++- .../html/document/src/nsHTMLContentSink.cpp | 2 +- layout/style/nsCSSPseudoClassList.h | 1 + layout/style/nsCSSRuleProcessor.cpp | 6 +- 11 files changed, 280 insertions(+), 94 deletions(-) diff --git a/content/events/public/nsIEventStateManager.h b/content/events/public/nsIEventStateManager.h index 8e274e494d67..793802074af5 100644 --- a/content/events/public/nsIEventStateManager.h +++ b/content/events/public/nsIEventStateManager.h @@ -170,18 +170,19 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIEventStateManager, NS_IEVENTSTATEMANAGER_IID) // these two are temporary (see bug 302188) #define NS_EVENT_STATE_MOZ_READONLY 0x00008000 // CSS3-UI #define NS_EVENT_STATE_MOZ_READWRITE 0x00010000 // CSS3-UI +#define NS_EVENT_STATE_DEFAULT 0x00020000 // CSS3-UI // Content could not be rendered (image/object/etc). -#define NS_EVENT_STATE_BROKEN 0x00020000 +#define NS_EVENT_STATE_BROKEN 0x00040000 // Content disabled by the user (images turned off, say) -#define NS_EVENT_STATE_USERDISABLED 0x00040000 +#define NS_EVENT_STATE_USERDISABLED 0x00080000 // Content suppressed by the user (ad blocking, etc) -#define NS_EVENT_STATE_SUPPRESSED 0x00080000 +#define NS_EVENT_STATE_SUPPRESSED 0x00100000 // Content is still loading such that there is nothing to show the // user (eg an image which hasn't started coming in yet) -#define NS_EVENT_STATE_LOADING 0x00100000 +#define NS_EVENT_STATE_LOADING 0x00200000 // Content is of a type that gecko can't handle #define NS_EVENT_STATE_TYPE_UNSUPPORTED \ - 0x00200000 + 0x00400000 #endif // nsIEventStateManager_h__ diff --git a/content/html/content/public/nsIForm.h b/content/html/content/public/nsIForm.h index 2da2b8b36cc8..2d5cc2279d9a 100644 --- a/content/html/content/public/nsIForm.h +++ b/content/html/content/public/nsIForm.h @@ -55,8 +55,8 @@ class nsIURI; // IID for the nsIFormManager interface #define NS_IFORM_IID \ -{ 0xb7e94510, 0x4c19, 0x11d2, \ - { 0x80, 0x3f, 0x0, 0x60, 0x8, 0x15, 0xa7, 0x91 } } +{ 0xab592735, 0x31d6, 0x4e28, \ + {0xaa, 0x1c, 0x77, 0x10, 0xf6, 0x01, 0xb8, 0x45} } /** @@ -74,9 +74,11 @@ public: * Add an element to end of this form's list of elements * * @param aElement the element to add + * @param aNotify If true, send nsIDocumentObserver notifications as needed. * @return NS_OK if the element was successfully added */ - NS_IMETHOD AddElement(nsIFormControl* aElement) = 0; + NS_IMETHOD AddElement(nsIFormControl* aElement, + PRBool aNotify) = 0; /** * Add an element to the lookup table mainted by the form. @@ -110,9 +112,11 @@ public: * Remove an element from this form's list of elements * * @param aElement the element to remove + * @param aNotify If true, send nsIDocumentObserver notifications as needed. * @return NS_OK if the element was successfully removed. */ - NS_IMETHOD RemoveElement(nsIFormControl* aElement) = 0; + NS_IMETHOD RemoveElement(nsIFormControl* aElement, + PRBool aNotify) = 0; /** * Remove an element from the lookup table mainted by the form. @@ -184,6 +188,11 @@ public: */ NS_IMETHOD GetActionURL(nsIURI** aActionURL) = 0; + /** + * Get the default submit element. If there's no default submit element, + * return null. + */ + NS_IMETHOD_(nsIFormControl*) GetDefaultSubmitElement() const = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIForm, NS_IFORM_IID) diff --git a/content/html/content/public/nsIFormControl.h b/content/html/content/public/nsIFormControl.h index dc089a22d2f1..716fb2bb3363 100644 --- a/content/html/content/public/nsIFormControl.h +++ b/content/html/content/public/nsIFormControl.h @@ -69,8 +69,8 @@ class nsIFormSubmission; #define NS_FORM_OBJECT 21 #define NS_IFORMCONTROL_IID \ -{ 0xfcf27549, 0xbd77, 0x455a, \ - {0x8c, 0x3e, 0xbb, 0x20, 0xc5, 0xaf, 0x7b, 0x86} } +{ 0x119c5ce8, 0xf4b0, 0x456c, \ + {0x83, 0x4d, 0xb1, 0x90, 0x3d, 0x99, 0x9f, 0xb3} } /** @@ -95,9 +95,11 @@ public: * @param aForm the form * @param aRemoveFromForm set false if you do not want this element removed * from the form. (Used by nsFormControlList::Clear()) + * @param aNofify If true, send nsIDocumentObserver notifications as needed. */ NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm, - PRBool aRemoveFromForm = PR_TRUE) = 0; + PRBool aRemoveFromForm, + PRBool aNotify) = 0; /** * Get the type of this control as an int (see NS_FORM_* above) @@ -141,6 +143,13 @@ public: virtual PRBool RestoreState(nsPresState* aState) = 0; virtual PRBool AllowDrop() = 0; + + /** + * Returns true if this is a control which submits the form when + * activated by the user. + * @return Whether this is a submit control. + */ + virtual PRBool IsSubmitControl() const = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIFormControl, NS_IFORMCONTROL_IID) diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 8cdd9ff0d67d..635fc28d13d3 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -3015,8 +3015,14 @@ nsGenericHTMLFormElement::nsGenericHTMLFormElement(nsINodeInfo *aNodeInfo) nsGenericHTMLFormElement::~nsGenericHTMLFormElement() { + // Check that this element is still not the default content + // of its parent form. + NS_ASSERTION(!mForm || mForm->GetDefaultSubmitElement() != this, + "Content being destroyed is the default content"); + // Clean up. Set the form to nsnull so it knows we went away. - SetForm(nsnull); + // Do not notify as the content is being destroyed. + SetForm(nsnull, PR_TRUE, PR_FALSE); } NS_INTERFACE_MAP_BEGIN(nsGenericHTMLFormElement) @@ -3032,7 +3038,8 @@ nsGenericHTMLFormElement::IsNodeOfType(PRUint32 aFlags) const NS_IMETHODIMP nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm, - PRBool aRemoveFromForm) + PRBool aRemoveFromForm, + PRBool aNotify) { nsAutoString nameVal, idVal; @@ -3042,7 +3049,7 @@ nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm, } if (mForm && aRemoveFromForm) { - mForm->RemoveElement(this); + mForm->RemoveElement(this, aNotify); if (!nameVal.IsEmpty()) { mForm->RemoveElementFromTable(this, nameVal); @@ -3062,7 +3069,7 @@ nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm, } if (mForm) { - mForm->AddElement(this); + mForm->AddElement(this, aNotify); if (!nameVal.IsEmpty()) { mForm->AddElementToTable(this, nameVal); @@ -3182,12 +3189,12 @@ nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) // Might need to unset mForm if (aNullParent) { // No more parent means no more form - SetForm(nsnull); + SetForm(nsnull, PR_TRUE, PR_TRUE); } else { // Recheck whether we should still have an mForm. nsCOMPtr form = FindForm(mForm); if (!form) { - SetForm(nsnull); + SetForm(nsnull, PR_TRUE, PR_TRUE); } } } @@ -3225,7 +3232,7 @@ nsGenericHTMLFormElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, mForm->RemoveElementFromTable(this, tmp); } - mForm->RemoveElement(this); + mForm->RemoveElement(this, aNotify); } } @@ -3262,7 +3269,7 @@ nsGenericHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, mForm->AddElementToTable(this, tmp); } - mForm->AddElement(this); + mForm->AddElement(this, aNotify); } // And notify on content state changes, if any @@ -3306,12 +3313,21 @@ nsGenericHTMLFormElement::CanBeDisabled() const type != NS_FORM_OBJECT; } +PRBool +nsGenericHTMLFormElement::IsSubmitControl() const +{ + PRInt32 type = GetType(); + return type == NS_FORM_INPUT_SUBMIT || + type == NS_FORM_BUTTON_SUBMIT || + type == NS_FORM_INPUT_IMAGE; +} + void nsGenericHTMLFormElement::FindAndSetForm() { nsCOMPtr form = FindForm(); if (form) { - SetForm(form); // always succeeds + SetForm(form, PR_TRUE, PR_TRUE); // always succeeds } } @@ -3332,6 +3348,13 @@ nsGenericHTMLFormElement::IntrinsicState() const state |= NS_EVENT_STATE_ENABLED; } } + + if (mForm && mForm->GetDefaultSubmitElement() == this) { + NS_ASSERTION(IsSubmitControl(), + "Default submit element that isn't a submit control."); + // We are the default submit element (:default) + state |= NS_EVENT_STATE_DEFAULT; + } return state; } diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index 8c7b94d406cf..4f2ecf2cdf52 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -788,7 +788,9 @@ public: // nsIFormControl NS_IMETHOD GetForm(nsIDOMHTMLFormElement** aForm); NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm, - PRBool aRemoveFromForm = PR_TRUE); + PRBool aRemoveFromForm, + PRBool aNotify); + NS_IMETHOD SaveState() { return NS_OK; @@ -801,6 +803,8 @@ public: { return PR_TRUE; } + + virtual PRBool IsSubmitControl() const; // nsIContent virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp index 808495bd7e82..1c9816b10c7f 100644 --- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -164,12 +164,14 @@ public: NS_DECL_NSIWEBPROGRESSLISTENER // nsIForm - NS_IMETHOD AddElement(nsIFormControl* aElement); + NS_IMETHOD AddElement(nsIFormControl* aElement, + PRBool aNotify); NS_IMETHOD AddElementToTable(nsIFormControl* aChild, const nsAString& aName); NS_IMETHOD GetElementAt(PRInt32 aIndex, nsIFormControl** aElement) const; NS_IMETHOD GetElementCount(PRUint32* aCount) const; - NS_IMETHOD RemoveElement(nsIFormControl* aElement); + NS_IMETHOD RemoveElement(nsIFormControl* aElement, + PRBool aNotify); NS_IMETHOD RemoveElementFromTable(nsIFormControl* aElement, const nsAString& aName); NS_IMETHOD ResolveName(const nsAString& aName, @@ -181,6 +183,7 @@ public: NS_IMETHOD FlushPendingSubmission(); NS_IMETHOD ForgetPendingSubmission(); NS_IMETHOD GetActionURL(nsIURI** aActionURL); + NS_IMETHOD_(nsIFormControl*) GetDefaultSubmitElement() const; // nsIRadioGroupContainer NS_IMETHOD SetCurrentRadioButton(const nsAString& aName, @@ -304,6 +307,18 @@ 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. + */ + nsresult ResetDefaultSubmitElement(PRBool aNotify); // // Data members @@ -336,6 +351,9 @@ protected: /** The web progress object we are currently listening to */ nsCOMPtr mWebProgress; + /** The default submit element -- WEAK */ + nsIFormControl* mDefaultSubmitElement; + friend class nsFormControlEnumerator; protected: @@ -494,7 +512,8 @@ nsHTMLFormElement::nsHTMLFormElement(nsINodeInfo *aNodeInfo) mSubmitPopupState(openAbused), mSubmitInitiatedFromUserInput(PR_FALSE), mPendingSubmission(nsnull), - mSubmittingRequest(nsnull) + mSubmittingRequest(nsnull), + mDefaultSubmitElement(nsnull) { } @@ -1196,8 +1215,10 @@ static PRInt32 CompareFormControlPosition(nsIFormControl *control1, nsIFormContr } NS_IMETHODIMP -nsHTMLFormElement::AddElement(nsIFormControl* aChild) +nsHTMLFormElement::AddElement(nsIFormControl* aChild, + PRBool aNotify) { + PRBool lastElement = PR_FALSE; if (ShouldBeInElements(aChild)) { PRUint32 count = ElementCount(); @@ -1216,6 +1237,7 @@ nsHTMLFormElement::AddElement(nsIFormControl* aChild) if (position >= 0 || count == 0) { // WEAK - don't addref mControls->mElements.AppendElement(aChild); + lastElement = PR_TRUE; } else { PRInt32 low = 0, mid, high; @@ -1261,6 +1283,34 @@ nsHTMLFormElement::AddElement(nsIFormControl* aChild) nsnull, NS_PASSWORDMANAGER_CATEGORY); } + + // 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 the last element in a form that already has a default submit + // element is ignored as it cannot be the default submit element. + nsIFormControl* oldControl = mDefaultSubmitElement; + if (!mDefaultSubmitElement || (!lastElement && + CompareFormControlPosition(aChild, mDefaultSubmitElement) < 0)) { + mDefaultSubmitElement = aChild; + } + + // Notify that the states of the new default submit and the previous + // default submit element have changed if the element which is the + // default submit element has changed. + if (aNotify && oldControl != mDefaultSubmitElement) { + nsIDocument* document = GetCurrentDoc(); + if (document) { + MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE); + nsCOMPtr oldElement(do_QueryInterface(oldControl)); + nsCOMPtr newElement(do_QueryInterface(mDefaultSubmitElement)); + document->ContentStatesChanged(oldElement, newElement, + NS_EVENT_STATE_DEFAULT); + } + } + } return NS_OK; } @@ -1274,14 +1324,16 @@ nsHTMLFormElement::AddElementToTable(nsIFormControl* aChild, NS_IMETHODIMP -nsHTMLFormElement::RemoveElement(nsIFormControl* aChild) +nsHTMLFormElement::RemoveElement(nsIFormControl* aChild, + PRBool aNotify) { // // Remove it from the radio group if it's a radio button // + nsresult rv = NS_OK; if (aChild->GetType() == NS_FORM_INPUT_RADIO) { nsCOMPtr radio = do_QueryInterface(aChild); - nsresult rv = radio->WillRemoveFromRadioGroup(); + rv = radio->WillRemoveFromRadioGroup(); NS_ENSURE_SUCCESS(rv, rv); } @@ -1291,9 +1343,58 @@ nsHTMLFormElement::RemoveElement(nsIFormControl* aChild) mControls->mNotInElements.RemoveElement(aChild); } - return NS_OK; + if (aChild == mDefaultSubmitElement) { + // We are removing the default submit, find the new default + rv = ResetDefaultSubmitElement(aNotify); + } + + return rv; } +nsresult +nsHTMLFormElement::ResetDefaultSubmitElement(PRBool aNotify) +{ + nsIFormControl* oldDefaultSubmit = mDefaultSubmitElement; + mDefaultSubmitElement = nsnull; + + nsCOMPtr formControls; + GetControlEnumerator(getter_AddRefs(formControls)); + + nsCOMPtr currentControlSupports; + nsCOMPtr currentControl; + + // Find default submit element + PRBool hasMoreElements; + nsresult rv; + while (NS_SUCCEEDED(rv = formControls->HasMoreElements(&hasMoreElements)) && + hasMoreElements) { + rv = formControls->GetNext(getter_AddRefs(currentControlSupports)); + NS_ENSURE_SUCCESS(rv, rv); + + currentControl = do_QueryInterface(currentControlSupports); + if (currentControl && currentControl->IsSubmitControl()) { + mDefaultSubmitElement = currentControl; + break; + } + } + + // Inform about change. + 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 oldElement(do_QueryInterface(oldDefaultSubmit)); + nsCOMPtr newElement(do_QueryInterface(mDefaultSubmitElement)); + document->ContentStatesChanged(oldElement, newElement, + NS_EVENT_STATE_DEFAULT); + } + } + + return rv; + } + NS_IMETHODIMP nsHTMLFormElement::RemoveElementFromTable(nsIFormControl* aElement, const nsAString& aName) @@ -1457,6 +1558,12 @@ nsHTMLFormElement::GetActionURL(nsIURI** aActionURL) return rv; } +NS_IMETHODIMP_(nsIFormControl*) +nsHTMLFormElement::GetDefaultSubmitElement() const +{ + return mDefaultSubmitElement; +} + NS_IMETHODIMP nsHTMLFormElement::GetEncoding(nsAString& aEncoding) { @@ -1826,7 +1933,7 @@ nsFormControlList::Clear() nsIFormControl* f = NS_STATIC_CAST(nsIFormControl *, mElements.ElementAt(i)); if (f) { - f->SetForm(nsnull, PR_FALSE); + f->SetForm(nsnull, PR_FALSE, PR_TRUE); } } mElements.Clear(); @@ -1835,7 +1942,7 @@ nsFormControlList::Clear() nsIFormControl* f = NS_STATIC_CAST(nsIFormControl*, mNotInElements.ElementAt(i)); if (f) { - f->SetForm(nsnull, PR_FALSE); + f->SetForm(nsnull, PR_FALSE, PR_TRUE); } } mNotInElements.Clear(); diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index d8ae174e022b..9379bd4030ef 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -517,18 +517,28 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, // // Checked must be set no matter what type of control it is, since // GetChecked() must reflect the new value - // - if (aName == nsHTMLAtoms::checked && - !GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) { - // Delay setting checked if the parser is creating this element - // (wait until everything is set) - if (GET_BOOLBIT(mBitField, BF_PARSER_CREATING)) { - SET_BOOLBIT(mBitField, BF_SHOULD_INIT_CHECKED, PR_TRUE); - } else { - PRBool defaultChecked; - GetDefaultChecked(&defaultChecked); - DoSetChecked(defaultChecked); - SetCheckedChanged(PR_FALSE); + if (aName == nsHTMLAtoms::checked) { + if (aNotify && + (mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX)) { + // the checked attribute being changed, no matter the current checked + // state, influences the :default state, so notify about changes + nsIDocument* document = GetCurrentDoc(); + if (document) { + MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify); + document->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_DEFAULT); + } + } + if (!GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) { + // Delay setting checked if the parser is creating this element (wait + // until everything is set) + if (GET_BOOLBIT(mBitField, BF_PARSER_CREATING)) { + SET_BOOLBIT(mBitField, BF_SHOULD_INIT_CHECKED, PR_TRUE); + } else { + PRBool defaultChecked; + GetDefaultChecked(&defaultChecked); + DoSetChecked(defaultChecked); + SetCheckedChanged(PR_FALSE); + } } } @@ -1012,53 +1022,48 @@ nsHTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext) return NS_OK; } - // Find the first submit control in elements[] - // and also check how many text controls we have in the form - nsCOMPtr submitControl; - PRInt32 numTextControlsFound = 0; + nsIPresShell* shell = aPresContext->GetPresShell(); + if (!shell) { + return NS_OK; + } - nsCOMPtr formControls; - mForm->GetControlEnumerator(getter_AddRefs(formControls)); + // Get the default submit element + nsIFormControl* submitControl = mForm->GetDefaultSubmitElement(); + if (submitControl) { + nsCOMPtr submitContent(do_QueryInterface(submitControl)); + NS_ASSERTION(submitContent, "Form control not implementing nsIContent?!"); + // Fire the button's onclick handler and let the button handle + // submitting the form. + nsMouseEvent event(PR_TRUE, NS_MOUSE_LEFT_CLICK, nsnull, + nsMouseEvent::eReal); + nsEventStatus status = nsEventStatus_eIgnore; + shell->HandleDOMEventWithTarget(submitContent, &event, &status); + } else { + PRInt32 numTextControlsFound = 0; + // No default submit, find the number of text controls. + nsCOMPtr formControls; + mForm->GetControlEnumerator(getter_AddRefs(formControls)); - nsCOMPtr currentControlSupports; - nsCOMPtr currentControl; - PRBool hasMoreElements; - nsresult rv; - while (NS_SUCCEEDED(rv = formControls->HasMoreElements(&hasMoreElements)) && - hasMoreElements) { - rv = formControls->GetNext(getter_AddRefs(currentControlSupports)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr currentControlSupports; + nsCOMPtr currentControl; + PRBool hasMoreElements; + nsresult rv; + while (NS_SUCCEEDED(rv = formControls->HasMoreElements(&hasMoreElements)) && + hasMoreElements && numTextControlsFound < 2) { + rv = formControls->GetNext(getter_AddRefs(currentControlSupports)); + NS_ENSURE_SUCCESS(rv, rv); - currentControl = do_QueryInterface(currentControlSupports); - if (currentControl) { - PRInt32 type = currentControl->GetType(); - if (!submitControl && - (type == NS_FORM_INPUT_SUBMIT || - type == NS_FORM_BUTTON_SUBMIT || - type == NS_FORM_INPUT_IMAGE)) { - submitControl = do_QueryInterface(currentControl); - // We know as soon as we find a submit control that it no - // longer matters how many text controls there are--we are - // going to fire the onClick handler. - break; - } else if (type == NS_FORM_INPUT_TEXT || - type == NS_FORM_INPUT_PASSWORD) { - numTextControlsFound++; + currentControl = do_QueryInterface(currentControlSupports); + if (currentControl) { + PRInt32 type = currentControl->GetType(); + if (type == NS_FORM_INPUT_TEXT || + type == NS_FORM_INPUT_PASSWORD) { + numTextControlsFound++; + } } } - } - NS_ENSURE_SUCCESS(rv, rv); - - nsIPresShell* shell = aPresContext->GetPresShell(); - if (shell) { - if (submitControl) { - // Fire the button's onclick handler and let the button handle - // submitting the form. - nsMouseEvent event(PR_TRUE, NS_MOUSE_LEFT_CLICK, nsnull, - nsMouseEvent::eReal); - nsEventStatus status = nsEventStatus_eIgnore; - shell->HandleDOMEventWithTarget(submitControl, &event, &status); - } else if (numTextControlsFound == 1) { + + if (numTextControlsFound == 1) { // If there's only one text control, just submit the form nsCOMPtr form = do_QueryInterface(mForm); nsFormEvent event(PR_TRUE, NS_FORM_SUBMIT); @@ -2561,13 +2566,24 @@ PRInt32 nsHTMLInputElement::IntrinsicState() const { PRInt32 state = nsGenericHTMLFormElement::IntrinsicState(); - if (GET_BOOLBIT(mBitField, BF_CHECKED) && - (mType == NS_FORM_INPUT_CHECKBOX || - mType == NS_FORM_INPUT_RADIO)) { - state |= NS_EVENT_STATE_CHECKED; + if (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO) { + // Check current checked state (:checked) + if (GET_BOOLBIT(mBitField, BF_CHECKED)) { + state |= NS_EVENT_STATE_CHECKED; + } + + // Check whether we are the default checked element (:default) + // The call is to an interface function, which makes it non-const, so we + // use a nasty hack :( + PRBool defaultState = PR_FALSE; + NS_CONST_CAST(nsHTMLInputElement*, this)->GetDefaultChecked(&defaultState); + if (defaultState) { + state |= NS_EVENT_STATE_DEFAULT; + } } else if (mType == NS_FORM_INPUT_IMAGE) { state |= nsImageLoadingContent::ImageState(); } + return state; } diff --git a/content/html/content/src/nsHTMLOptionElement.cpp b/content/html/content/src/nsHTMLOptionElement.cpp index f099e98c76e4..261af749eef8 100644 --- a/content/html/content/src/nsHTMLOptionElement.cpp +++ b/content/html/content/src/nsHTMLOptionElement.cpp @@ -427,6 +427,12 @@ nsHTMLOptionElement::IntrinsicState() const state |= NS_EVENT_STATE_CHECKED; } + // Also calling a non-const interface method (for :default) + NS_CONST_CAST(nsHTMLOptionElement*, this)->GetDefaultSelected(&selected); + if (selected) { + state |= NS_EVENT_STATE_DEFAULT; + } + PRBool disabled; GetBoolAttr(nsHTMLAtoms::disabled, &disabled); if (disabled) { @@ -463,12 +469,18 @@ nsHTMLOptionElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, const nsAString* aValue, PRBool aNotify) { if (aNotify && aNameSpaceID == kNameSpaceID_None && - aName == nsHTMLAtoms::disabled) { + (aName == nsHTMLAtoms::disabled || aName == nsHTMLAtoms::selected)) { + PRInt32 states; + if (aName == nsHTMLAtoms::disabled) { + states = NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED; + } else { + states = NS_EVENT_STATE_DEFAULT; + } + nsIDocument* document = GetCurrentDoc(); if (document) { mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, PR_TRUE); - document->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_DISABLED | - NS_EVENT_STATE_ENABLED); + document->ContentStatesChanged(this, nsnull, states); } } diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index 375f8c02a15e..a188da1968bb 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -765,7 +765,7 @@ MaybeSetForm(nsGenericHTMLElement* aContent, nsHTMLTag aNodeType, NS_ASSERTION(!form || formElement, "nsGenericHTMLElement didn't implement nsIDOMHTMLFormElement"); - formControl->SetForm(formElement); + formControl->SetForm(formElement, PR_TRUE, PR_FALSE); } static already_AddRefed diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 5a15bf01a8d8..a810fc31dec3 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -96,5 +96,6 @@ CSS_PSEUDO_CLASS(valid, ":valid") CSS_PSEUDO_CLASS(invalid, ":invalid") CSS_PSEUDO_CLASS(inRange, ":in-range") CSS_PSEUDO_CLASS(outOfRange, ":out-of-range") +CSS_PSEUDO_CLASS(defaultPseudo, ":default") CSS_PSEUDO_CLASS(mozReadOnly, ":-moz-read-only") CSS_PSEUDO_CLASS(mozReadWrite, ":-moz-read-write") diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 9081873a97e6..eae62f15f665 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1229,6 +1229,9 @@ static PRBool SelectorMatches(RuleProcessorData &data, else if (nsCSSPseudoClasses::mozTypeUnsupported == pseudoClass->mAtom) { stateToCheck = NS_EVENT_STATE_TYPE_UNSUPPORTED; } + else if (nsCSSPseudoClasses::defaultPseudo == pseudoClass->mAtom) { + stateToCheck = NS_EVENT_STATE_DEFAULT; + } else if (nsCSSPseudoClasses::required == pseudoClass->mAtom) { stateToCheck = NS_EVENT_STATE_REQUIRED; } @@ -1827,7 +1830,8 @@ PRBool IsStateSelector(nsCSSSelector& aSelector) (pseudoClass->mAtom == nsCSSPseudoClasses::inRange) || (pseudoClass->mAtom == nsCSSPseudoClasses::outOfRange) || (pseudoClass->mAtom == nsCSSPseudoClasses::mozReadOnly) || - (pseudoClass->mAtom == nsCSSPseudoClasses::mozReadWrite)) { + (pseudoClass->mAtom == nsCSSPseudoClasses::mozReadWrite) || + (pseudoClass->mAtom == nsCSSPseudoClasses::defaultPseudo)) { return PR_TRUE; } }