author | Mounir Lamouri <mounir.lamouri@gmail.com> |
Wed, 24 Nov 2010 00:50:53 +0100 | |
changeset 58147 | 253651b1e9c2f5baf3e2bb423994f51ec502a2e6 |
parent 58146 | 5be85a60b133680a48ead8085bb9f7cca9c2e016 |
child 58148 | a3fabf61118eb10deff968dd22cc9aa7a8399e4a |
push id | 17175 |
push user | mlamouri@mozilla.com |
push date | Wed, 24 Nov 2010 10:15:50 +0000 |
treeherder | mozilla-central@7f5cd850578e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz, bsmedberg |
bugs | 605124 |
milestone | 2.0b8pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -1701,30 +1701,19 @@ nsHTMLFormElement::CheckValidFormSubmiss // Do not check form validity if there is no observer for // NS_INVALIDFORMSUBMIT_SUBJECT. if (NS_SUCCEEDED(rv) && hasObserver) { nsCOMPtr<nsIMutableArray> invalidElements = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); if (!CheckFormValidity(invalidElements.get())) { - nsCOMPtr<nsISupports> inst; - nsCOMPtr<nsIFormSubmitObserver> observer; - PRBool more = PR_TRUE; - while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) { - theEnum->GetNext(getter_AddRefs(inst)); - observer = do_QueryInterface(inst); - - if (observer) { - observer->NotifyInvalidSubmit(this, - static_cast<nsIArray*>(invalidElements)); - } - } - // For the first invalid submission, we should update element states. + // We have to do that _before_ calling the observers so we are sure they + // will not interfere (like focusing the element). if (!mEverTriedInvalidSubmit) { mEverTriedInvalidSubmit = true; nsIDocument* doc = GetCurrentDoc(); if (doc) { /* * We are going to call ContentStatesChanged assuming elements want to * be notified because we can't know. @@ -1745,16 +1734,29 @@ nsHTMLFormElement::CheckValidFormSubmiss for (PRUint32 i = 0, length = mControls->mNotInElements.Length(); i < length; ++i) { doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull, NS_EVENT_STATE_MOZ_UI_INVALID); } } } + nsCOMPtr<nsISupports> inst; + nsCOMPtr<nsIFormSubmitObserver> observer; + PRBool more = PR_TRUE; + while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) { + theEnum->GetNext(getter_AddRefs(inst)); + observer = do_QueryInterface(inst); + + if (observer) { + observer->NotifyInvalidSubmit(this, + static_cast<nsIArray*>(invalidElements)); + } + } + // The form is invalid. Observers have been alerted. Do not submit. return false; } } else { NS_WARNING("There is no observer for \"invalidformsubmit\". \ One should be implemented!"); }
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -110,17 +110,16 @@ #include "nsIPopupWindowManager.h" #include "nsGlobalWindow.h" // input type=image #include "nsImageLoadingContent.h" #include "nsIDOMWindowInternal.h" #include "mozAutoDocUpdate.h" -#include "nsHTMLFormElement.h" #include "nsContentCreatorFunctions.h" #include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsTextEditRules.h" // JS headers are needed for the pattern attribute. #include "jsapi.h" @@ -621,16 +620,17 @@ nsHTMLInputElement::nsHTMLInputElement(a FromParser aFromParser) : nsGenericHTMLFormElement(aNodeInfo), mType(kInputDefaultType->value), mBitField(0) { SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser); SET_BOOLBIT(mBitField, BF_INHIBIT_RESTORATION, aFromParser & mozilla::dom::FROM_PARSER_FRAGMENT); + SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE); mInputData.mState = new nsTextEditorState(this); NS_ADDREF(mInputData.mState); if (!gUploadLastDir) nsHTMLInputElement::InitUploadLastDir(); } nsHTMLInputElement::~nsHTMLInputElement() @@ -2108,26 +2108,43 @@ SelectTextFieldOnFocus() nsresult nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { if (!aVisitor.mPresContext) { return NS_OK; } - if (PlaceholderApplies() && - HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) && - // TODO: checking if the value is empty could be a good idea but we do not + if (aVisitor.mEvent->message == NS_FOCUS_CONTENT || + aVisitor.mEvent->message == NS_BLUR_CONTENT) { + nsEventStates states; + + if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) { + // If the invalid UI is shown, we should show it while focusing (and + // update). Otherwise, we should not. + SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, + !IsValid() && ShouldShowInvalidUI()); + // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID given that + // the state should not change. + } else { // NS_BLUR_CONTENT + SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE); + states |= NS_EVENT_STATE_MOZ_UI_INVALID; + } + + if (PlaceholderApplies() && + HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) { + // TODO: checking if the value is empty could be a good idea but we do not // have a simple way to do that, see bug 585100 - (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT)) { + states |= NS_EVENT_STATE_MOZ_PLACEHOLDER; + } + nsIDocument* doc = GetCurrentDoc(); if (doc) { MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); - doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER); + doc->ContentStatesChanged(this, nsnull, states); } } // ignore the activate event fired by the "Browse..." button // (file input controls fire their own) (bug 500885) if (mType == NS_FORM_INPUT_FILE) { nsCOMPtr<nsIContent> maybeButton = do_QueryInterface(aVisitor.mEvent->originalTarget); @@ -3305,30 +3322,19 @@ nsHTMLInputElement::IntrinsicState() con state |= NS_EVENT_STATE_OPTIONAL; } if (IsCandidateForConstraintValidation()) { if (IsValid()) { state |= NS_EVENT_STATE_VALID; } else { state |= NS_EVENT_STATE_INVALID; - ValueModeType valueMode = GetValueMode(); - // If the element is suffering from VALIDITY_STATE_CUSTOM_ERROR, - // NS_EVENT_STATE_MOZ_UI_INVALID always apply. - // Otherwise, NS_EVENT_STATE_MOZ_UI_INVALID applies if the element's value - // has been modified. - // For VALUE_MODE_DEFAULT case, value being modified has no sense. - // NS_EVENT_STATE_MOZ_UI_INVALID always applies if the form submission has - // been tried while invalid. - if ((mForm && mForm->HasEverTriedInvalidSubmit()) || - (valueMode == VALUE_MODE_DEFAULT || - GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) || - (valueMode == VALUE_MODE_DEFAULT_ON && GetCheckedChanged()) || - ((valueMode == VALUE_MODE_FILENAME || valueMode == VALUE_MODE_VALUE) && - GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)))) { + + if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) && + ShouldShowInvalidUI()) { state |= NS_EVENT_STATE_MOZ_UI_INVALID; } } } if (PlaceholderApplies() && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) && !nsContentUtils::IsFocusedContent((nsIContent*)(this))) { // TODO: we really need a GetValue(...) const method, see bug 585097
--- a/content/html/content/src/nsHTMLInputElement.h +++ b/content/html/content/src/nsHTMLInputElement.h @@ -40,37 +40,38 @@ #define nsHTMLInputElement_h__ #include "nsGenericHTMLElement.h" #include "nsImageLoadingContent.h" #include "nsIDOMHTMLInputElement.h" #include "nsITextControlElement.h" #include "nsIPhonetic.h" #include "nsIDOMNSEditableElement.h" - #include "nsTextEditorState.h" #include "nsCOMPtr.h" #include "nsIConstraintValidation.h" #include "nsDOMFile.h" +#include "nsHTMLFormElement.h" // for ShouldShowInvalidUI() // // Accessors for mBitField // #define BF_DISABLED_CHANGED 0 #define BF_HANDLING_CLICK 1 #define BF_VALUE_CHANGED 2 #define BF_CHECKED_CHANGED 3 #define BF_CHECKED 4 #define BF_HANDLING_SELECT_EVENT 5 #define BF_SHOULD_INIT_CHECKED 6 #define BF_PARSER_CREATING 7 #define BF_IN_INTERNAL_ACTIVATE 8 #define BF_CHECKED_IS_TOGGLED 9 #define BF_INDETERMINATE 10 #define BF_INHIBIT_RESTORATION 11 +#define BF_CAN_SHOW_INVALID_UI 12 #define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \ ? PR_TRUE : PR_FALSE) #define SET_BOOLBIT(bitfield, field, b) ((b) \ ? ((bitfield) |= (0x01 << (field))) \ : ((bitfield) &= ~(0x01 << (field)))) class nsDOMFileList; @@ -528,16 +529,54 @@ protected: /** * Set the current default value to the value of the input element. * @note You should not call this method if GetValueMode() doesn't return * VALUE_MODE_VALUE. */ nsresult SetDefaultValueAsValue(); + /** + * Return if an invalid element should have a specific UI for being invalid + * (with :-moz-ui-invalid pseudo-class. + * + * @return Whether the invalid elemnet should have a UI for being invalid. + * @note The caller has to be sure the element is invalid before calling. + */ + bool ShouldShowInvalidUI() const { + NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the " + "element is valid!"); + + /** + * Always show the invalid UI if: + * - the form has already tried to be submitted but was invalid; + * - the element is suffering from a custom error; + * - the element has had its value changed + * + * Otherwise, show the invalid UI if the element's value has been changed. + */ + if ((mForm && mForm->HasEverTriedInvalidSubmit()) || + GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) { + return true; + } + + switch (GetValueMode()) { + case VALUE_MODE_DEFAULT: + return true; + case VALUE_MODE_DEFAULT_ON: + return GetCheckedChanged(); + case VALUE_MODE_VALUE: + case VALUE_MODE_FILENAME: + return GET_BOOLBIT(mBitField, BF_VALUE_CHANGED); + default: + NS_NOTREACHED("We should not be there: there are no other modes."); + return false; + } + } + nsCOMPtr<nsIControllers> mControllers; /** * The type of this input (<input type=...>) as an integer. * @see nsIFormControl.h (specifically NS_FORM_INPUT_*) */ PRUint8 mType; /**
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp +++ b/content/html/content/src/nsHTMLTextAreaElement.cpp @@ -233,19 +233,22 @@ protected: PRPackedBool mHandlingSelect; /** Whether or not we are done adding children (always PR_TRUE if not created by a parser */ PRPackedBool mDoneAddingChildren; /** Whether state restoration should be inhibited in DoneAddingChildren. */ PRPackedBool mInhibitStateRestoration; /** Whether our disabled state has changed from the default **/ PRPackedBool mDisabledChanged; + /** Whether we should make :-moz-ui-invalid apply on the element. **/ + PRPackedBool mCanShowInvalidUI; + /** The state of the text editor (selection controller and the editor) **/ nsRefPtr<nsTextEditorState> mState; - + NS_IMETHOD SelectAll(nsPresContext* aPresContext); /** * Get the value, whether it is from the content or the frame. * @param aValue the value [out] * @param aIgnoreWrap whether to ignore the wrap attribute when getting the * value. If this is true, linebreaks will not be inserted even if * wrap=hard. */ @@ -266,16 +269,39 @@ protected: * parent; we should only respond to the change if aContent is non-anonymous. */ void ContentChanged(nsIContent* aContent); virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName, const nsAString* aValue, PRBool aNotify); /** + * Return if an invalid element should have a specific UI for being invalid + * (with :-moz-ui-invalid pseudo-class. + * + * @return Whether the invalid elemnet should have a UI for being invalid. + * @note The caller has to be sure the element is invalid before calling. + */ + bool ShouldShowInvalidUI() const { + NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the " + "element is valid!"); + + /** + * Always show the invalid UI if: + * - the form has already tried to be submitted but was invalid; + * - the element is suffering from a custom error; + * + * Otherwise, show the invalid UI if the element's value has been changed. + */ + + return (mForm && mForm->HasEverTriedInvalidSubmit()) || + mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR); + } + + /** * Get the mutable state of the element. */ PRBool IsMutable() const; }; NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea) @@ -283,16 +309,17 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER nsHTMLTextAreaElement::nsHTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo, FromParser aFromParser) : nsGenericHTMLFormElement(aNodeInfo), mValueChanged(PR_FALSE), mHandlingSelect(PR_FALSE), mDoneAddingChildren(!aFromParser), mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)), mDisabledChanged(PR_FALSE), + mCanShowInvalidUI(PR_TRUE), mState(new nsTextEditorState(this)) { AddMutationObserver(this); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement, @@ -725,25 +752,41 @@ nsHTMLTextAreaElement::PreHandleEvent(ns nsresult nsHTMLTextAreaElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { if (aVisitor.mEvent->message == NS_FORM_SELECTED) { mHandlingSelect = PR_FALSE; } - if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) && + if (aVisitor.mEvent->message == NS_FOCUS_CONTENT || + aVisitor.mEvent->message == NS_BLUR_CONTENT) { + nsEventStates states; + + if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) { + // If the invalid UI is shown, we should show it while focusing (and + // update). Otherwise, we should not. + mCanShowInvalidUI = !IsValid() && ShouldShowInvalidUI(); + // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID given that + // the state should not change. + } else { // NS_BLUR_CONTENT + mCanShowInvalidUI = PR_TRUE; + states |= NS_EVENT_STATE_MOZ_UI_INVALID; + } + + if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) { // TODO: checking if the value is empty could be a good idea but we do not // have a simple way to do that, see bug 585100 - (aVisitor.mEvent->message == NS_FOCUS_CONTENT || - aVisitor.mEvent->message == NS_BLUR_CONTENT)) { + states |= NS_EVENT_STATE_MOZ_PLACEHOLDER; + } + nsIDocument* doc = GetCurrentDoc(); if (doc) { MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); - doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER); + doc->ContentStatesChanged(this, nsnull, states); } } // Reset the flag for other content besides this text field aVisitor.mEvent->flags |= (aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH) ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE; return NS_OK; @@ -1017,18 +1060,17 @@ nsHTMLTextAreaElement::IntrinsicState() state |= NS_EVENT_STATE_VALID; } else { state |= NS_EVENT_STATE_INVALID; // NS_EVENT_STATE_MOZ_UI_INVALID always apply if the element suffers from // VALIDITY_STATE_CUSTOM_ERROR. // Otherwise, it applies if the value has been modified. // NS_EVENT_STATE_MOZ_UI_INVALID always applies if the form submission has // been tried while invalid. - if ((mForm && mForm->HasEverTriedInvalidSubmit()) || - (mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR))) { + if (mCanShowInvalidUI && ShouldShowInvalidUI()) { state |= NS_EVENT_STATE_MOZ_UI_INVALID; } } } if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) && !nsContentUtils::IsFocusedContent((nsIContent*)(this))) { nsAutoString value;
--- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -238,13 +238,14 @@ include $(topsrcdir)/config/rules.mk test_bug606817.html \ test_bug297761.html \ file_bug297761.html \ test_bug607145.html \ test_bug601061.html \ test_bug596511.html \ reflect.js \ test_bug613113.html \ - test_bug605124.html \ + test_bug605124-1.html \ + test_bug605124-2.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
rename from content/html/content/test/test_bug605124.html rename to content/html/content/test/test_bug605124-1.html --- a/content/html/content/test/test_bug605124.html +++ b/content/html/content/test/test_bug605124-1.html @@ -7,17 +7,17 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 605124</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=605124">Mozilla Bug 605124</a> <p id="display"></p> -<div id="content" style="display: none"> +<div id="content"> <form> <textarea required></textarea> <input required> <button type='submit'></button> </form> <table> <form>
new file mode 100644 --- /dev/null +++ b/content/html/content/test/test_bug605124-2.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=605124 +--> +<head> + <title>Test for Bug 605124</title> + <script type="application/javascript" src="/MochiKit/packed.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=605124">Mozilla Bug 605124</a> +<p id="display"></p> +<div id="content"> + <input required> + <textarea required></textarea> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 605124 **/ + +function checkPseudoClass(aElement, aExpected) +{ + is(aElement.mozMatchesSelector(":-moz-ui-invalid"), aExpected, + "mozMatchesSelector(':-moz-ui-invalid') should return " + aExpected + " for " + aElement); +} + +function checkElement(aElement) +{ + checkPseudoClass(aElement, false); + + // Focusing while :-moz-ui-invalid doesn't apply, + // the pseudo-class should not apply while typing. + aElement.focus(); + checkPseudoClass(aElement, false); + // with keys + synthesizeKey('f', {}); + checkPseudoClass(aElement, false); + synthesizeKey('VK_BACK_SPACE', {}); + checkPseudoClass(aElement, false); + // with .value + aElement.value = 'f'; + checkPseudoClass(aElement, false); + aElement.value = ''; + checkPseudoClass(aElement, false); + + aElement.blur(); + checkPseudoClass(aElement, true); + + // Focusing while :-moz-ui-invalid applies, + // the pseudo-class should apply while typing if appropriate. + aElement.focus(); + checkPseudoClass(aElement, true); + // with keys + synthesizeKey('f', {}); + checkPseudoClass(aElement, false); + synthesizeKey('VK_BACK_SPACE', {}); + checkPseudoClass(aElement, true); + // with .value + aElement.value = 'f'; + checkPseudoClass(aElement, false); + aElement.value = ''; + checkPseudoClass(aElement, true); +} + +checkElement(document.getElementsByTagName('input')[0]); +checkElement(document.getElementsByTagName('textarea')[0]); + +</script> +</pre> +</body> +</html>