From 76f757828a4b6eb4633160e3fef281feee934962 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 May 2011 21:46:57 -0400 Subject: [PATCH] Bug 598833 part 12. Add dom::Element::UpdateState and use it in various places where elements update their own state. r=smaug,sdwilsh,mounir --- content/base/public/Element.h | 50 +++++- content/base/public/nsIContent.h | 9 +- content/base/src/Link.cpp | 38 ++-- content/base/src/nsGenConImageContent.cpp | 3 + content/base/src/nsGenericDOMDataNode.cpp | 2 +- content/base/src/nsGenericElement.cpp | 78 +++++--- content/base/src/nsGenericElement.h | 2 + content/base/src/nsImageLoadingContent.cpp | 15 +- content/base/src/nsObjectLoadingContent.cpp | 25 ++- content/base/src/nsObjectLoadingContent.h | 3 +- .../html/content/src/nsGenericHTMLElement.cpp | 100 +++++------ .../html/content/src/nsGenericHTMLElement.h | 30 ++-- .../html/content/src/nsHTMLButtonElement.cpp | 45 +++-- .../content/src/nsHTMLFieldSetElement.cpp | 5 +- .../html/content/src/nsHTMLFormElement.cpp | 128 +++++-------- .../html/content/src/nsHTMLImageElement.cpp | 5 + .../html/content/src/nsHTMLInputElement.cpp | 169 ++++-------------- content/html/content/src/nsHTMLInputElement.h | 6 +- .../html/content/src/nsHTMLObjectElement.cpp | 3 + .../content/src/nsHTMLOptGroupElement.cpp | 2 + .../html/content/src/nsHTMLOptionElement.cpp | 12 +- .../html/content/src/nsHTMLOutputElement.cpp | 36 +++- .../content/src/nsHTMLProgressElement.cpp | 2 + .../html/content/src/nsHTMLSelectElement.cpp | 96 +++------- .../html/content/src/nsHTMLSelectElement.h | 2 +- .../content/src/nsHTMLSharedObjectElement.cpp | 3 + .../content/src/nsHTMLTextAreaElement.cpp | 112 ++++-------- content/html/content/src/nsRadioVisitor.cpp | 8 +- content/html/content/test/test_bug345822.html | 2 +- content/html/document/src/nsHTMLDocument.cpp | 7 +- .../mathml/content/src/nsMathMLElement.cpp | 7 +- content/svg/content/src/nsSVGFilters.cpp | 5 + content/svg/content/src/nsSVGImageElement.cpp | 5 + content/xtf/src/nsXTFElementWrapper.cpp | 3 +- content/xul/content/src/nsXULElement.cpp | 32 ++-- content/xul/content/src/nsXULElement.h | 11 ++ layout/generic/nsVideoFrame.cpp | 6 +- 37 files changed, 487 insertions(+), 580 deletions(-) diff --git a/content/base/public/Element.h b/content/base/public/Element.h index 962737e4bd3..5a3c185c3fa 100644 --- a/content/base/public/Element.h +++ b/content/base/public/Element.h @@ -85,11 +85,16 @@ enum { namespace mozilla { namespace dom { +class Link; + class Element : public nsIContent { public: #ifdef MOZILLA_INTERNAL_API - Element(already_AddRefed aNodeInfo) : nsIContent(aNodeInfo) {} + Element(already_AddRefed aNodeInfo) : + nsIContent(aNodeInfo), + mState(NS_EVENT_STATE_MOZ_READONLY) + {} #endif // MOZILLA_INTERNAL_API /** @@ -97,7 +102,9 @@ public: * the possible bits that could be set here. */ nsEventStates State() const { - return IntrinsicState() | mState; + // mState is maintained by having whoever might have changed it + // call UpdateState() or one of the other mState mutators. + return mState; } /** @@ -111,6 +118,18 @@ public: */ virtual void RequestLinkStateUpdate(); + /** + * Ask this element to update its state. If aNotify is false, then + * state change notifications will not be dispatched; in that + * situation it is the caller's responsibility to dispatch them. + * + * In general, aNotify should only be false if we're guaranteed that + * the element can't have a frame no matter what its style is + * (e.g. if we're in the middle of adding it to the document or + * removing it from the document). + */ + void UpdateState(bool aNotify); + protected: /** * Method to get the _intrinsic_ content state of this element. This is the @@ -125,6 +144,26 @@ protected: */ void UpdateLinkState(nsEventStates aState); + /** + * Method to add state bits. This should be called from subclass + * constructors to set up our event state correctly at construction + * time and other places where we don't want to notify a state + * change. + */ + void AddStatesSilently(nsEventStates aStates) { + mState |= aStates; + } + + /** + * Method to remove state bits. This should be called from subclass + * constructors to set up our event state correctly at construction + * time and other places where we don't want to notify a state + * change. + */ + void RemoveStatesSilently(nsEventStates aStates) { + mState &= ~aStates; + } + private: // Need to allow the ESM, nsGlobalWindow, and the focus manager to // set our state @@ -132,6 +171,9 @@ private: friend class ::nsGlobalWindow; friend class ::nsFocusManager; + // Also need to allow Link to call UpdateLinkState. + friend class Link; + void NotifyStateChange(nsEventStates aStates); // Methods for the ESM to manage state bits. These will handle @@ -140,13 +182,13 @@ private: void AddStates(nsEventStates aStates) { NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES), "Should only be adding ESM-managed states here"); - mState |= aStates; + AddStatesSilently(aStates); NotifyStateChange(aStates); } void RemoveStates(nsEventStates aStates) { NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES), "Should only be removing ESM-managed states here"); - mState &= ~aStates; + RemoveStatesSilently(aStates); NotifyStateChange(aStates); } diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index 5545fd90e3b..6143a046c18 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -76,8 +76,8 @@ enum nsLinkState { // IID for the nsIContent interface #define NS_ICONTENT_IID \ -{ 0xba1c9e22, 0x4b73, 0x42ae, \ - { 0xb6, 0x45, 0xa7, 0x83, 0xd0, 0x7e, 0xee, 0x2c } } +{ 0x860ee35b, 0xe505, 0x438f, \ + { 0xa7, 0x7b, 0x65, 0xb9, 0xf5, 0x0b, 0xe5, 0x29 } } /** * A node of content in a document's content model. This interface @@ -856,9 +856,10 @@ public: /** * Should be called when the node can become editable or when it can stop * being editable (for example when its contentEditable attribute changes, - * when it is moved into an editable parent, ...). + * when it is moved into an editable parent, ...). If aNotify is true and + * the node is an element, this will notify the state change. */ - virtual void UpdateEditableState(); + virtual void UpdateEditableState(PRBool aNotify); /** * Destroy this node and its children. Ideally this shouldn't be needed diff --git a/content/base/src/Link.cpp b/content/base/src/Link.cpp index 04dce3c6a80..8357f1f2dd5 100644 --- a/content/base/src/Link.cpp +++ b/content/base/src/Link.cpp @@ -85,25 +85,18 @@ Link::SetLinkState(nsLinkState aState) NS_ASSERTION(mLinkState != aState, "Setting state to the currently set state!"); - // Remember our old link state for when we notify. - nsEventStates oldLinkState = LinkState(); - // Set our current state as appropriate. mLinkState = aState; // Per IHistory interface documentation, we are no longer registered. mRegistered = false; - // Notify the document that our visited state has changed. - Element *element = mElement; - nsIDocument *doc = element->GetCurrentDoc(); - NS_ASSERTION(doc, "Registered but we have no document?!"); - nsEventStates newLinkState = LinkState(); - NS_ASSERTION(newLinkState == NS_EVENT_STATE_VISITED || - newLinkState == NS_EVENT_STATE_UNVISITED, - "Unexpected state obtained from LinkState()!"); - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(element, oldLinkState ^ newLinkState); + NS_ABORT_IF_FALSE(LinkState() == NS_EVENT_STATE_VISITED || + LinkState() == NS_EVENT_STATE_UNVISITED, + "Unexpected state obtained from LinkState()!"); + + // Tell the element to update its visited state + mElement->UpdateState(true); } nsEventStates @@ -484,15 +477,16 @@ Link::ResetLinkState(bool aNotify) // Get rid of our cached URI. mCachedURI = nsnull; - // If aNotify is true, notify both of the visited-related states. We have - // to do that, because we might be racing with a response from history and - // hence need to make sure that we get restyled whether we were visited or - // not before. In particular, we need to make sure that our LinkState() is - // called so that we'll start a new history query as needed. - if (aNotify && doc) { - nsEventStates changedState = NS_EVENT_STATE_VISITED ^ NS_EVENT_STATE_UNVISITED; - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(element, changedState); + // We have to be very careful here: if aNotify is false we do NOT + // want to call UpdateState, because that will call into LinkState() + // and try to start off loads, etc. But ResetLinkState is called + // with aNotify false when things are in inconsistent states, so + // we'll get confused in that situation. Instead, just silently + // update the link state on mElement. + if (aNotify) { + mElement->UpdateState(aNotify); + } else { + mElement->UpdateLinkState(nsEventStates()); } } diff --git a/content/base/src/nsGenConImageContent.cpp b/content/base/src/nsGenConImageContent.cpp index ea43820ba45..c2de8523f8c 100644 --- a/content/base/src/nsGenConImageContent.cpp +++ b/content/base/src/nsGenConImageContent.cpp @@ -55,6 +55,9 @@ public: nsGenConImageContent(already_AddRefed aNodeInfo) : nsXMLElement(aNodeInfo) { + // nsImageLoadingContent starts out broken, so we start out + // suppressed to match it. + AddStatesSilently(NS_EVENT_STATE_SUPPRESSED); } nsresult Init(imgIRequest* aImageRequest) diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index 482a6b80c1b..b1c91309c3a 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -535,7 +535,7 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsNodeUtils::ParentChainChanged(this); - UpdateEditableState(); + UpdateEditableState(PR_FALSE); NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document"); NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index af75e36fe14..e20c80ad801 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -819,13 +819,59 @@ Element::UpdateLinkState(nsEventStates aState) } void -nsIContent::UpdateEditableState() +Element::UpdateState(bool aNotify) { + nsEventStates oldState = mState; + mState = IntrinsicState() | (oldState & ESM_MANAGED_STATES); + if (aNotify) { + nsEventStates changedStates = oldState ^ mState; + if (!changedStates.IsEmpty()) { + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + nsAutoScriptBlocker scriptBlocker; + doc->ContentStateChanged(this, changedStates); + } + } + } +} + +void +nsIContent::UpdateEditableState(PRBool aNotify) +{ + // Guaranteed to be non-element content + NS_ASSERTION(!IsElement(), "What happened here?"); nsIContent *parent = GetParent(); SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); } +void +nsGenericElement::UpdateEditableState(PRBool aNotify) +{ + nsIContent *parent = GetParent(); + + PRBool oldEditable = IsEditable(); + SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); + PRBool newEditable = IsEditable(); + if (oldEditable != newEditable) { + if (aNotify) { + UpdateState(aNotify); + } else { + // Avoid calling UpdateState in this very common case, because + // this gets called for pretty much every single element on + // insertion into the document and UpdateState can be slow for + // some kinds of elements even when not notifying. + if (oldEditable) { + RemoveStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); + AddStatesSilently(NS_EVENT_STATE_MOZ_READONLY); + } else { + RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY); + AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); + } + } + } +} + nsIContent* nsIContent::FindFirstNonNativeAnonymous() const { @@ -3034,7 +3080,7 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } } - UpdateEditableState(); + UpdateEditableState(PR_FALSE); // Now recurse into our kids for (nsIContent* child = GetFirstChild(); child; @@ -4668,13 +4714,6 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID, nsIDocument* document = GetCurrentDoc(); mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); - // When notifying, make sure to keep track of states whose value - // depends solely on the value of an attribute. - nsEventStates stateMask; - if (aNotify) { - stateMask = IntrinsicState(); - } - nsMutationGuard::DidMutate(); if (aNamespaceID == kNameSpaceID_None) { @@ -4706,11 +4745,9 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID, } } + UpdateState(aNotify); + if (aNotify) { - stateMask ^= IntrinsicState(); - if (document && !stateMask.IsEmpty()) { - document->ContentStateChanged(this, stateMask); - } nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType); } @@ -4904,13 +4941,6 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIDOMMutationEvent::REMOVAL); } - // When notifying, make sure to keep track of states whose value - // depends solely on the value of an attribute. - nsEventStates stateMask; - if (aNotify) { - stateMask = IntrinsicState(); - } - PRBool hasMutationListeners = aNotify && nsContentUtils::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, @@ -4950,11 +4980,9 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, } } + UpdateState(aNotify); + if (aNotify) { - stateMask ^= IntrinsicState(); - if (document && !stateMask.IsEmpty()) { - document->ContentStateChanged(this, stateMask); - } nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName, nsIDOMMutationEvent::REMOVAL); } @@ -5085,7 +5113,7 @@ nsGenericElement::List(FILE* out, PRInt32 aIndent, ListAttributes(out); - fprintf(out, " intrinsicstate=[%llx]", IntrinsicState().GetInternalValue()); + fprintf(out, " state=[%llx]", State().GetInternalValue()); fprintf(out, " flags=[%08x]", static_cast(GetFlags())); fprintf(out, " primaryframe=%p", static_cast(GetPrimaryFrame())); fprintf(out, " refcount=%d<", mRefCnt.get()); diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index 407486639e6..96c34bcc514 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -355,6 +355,8 @@ public: } // nsIContent interface methods + virtual void UpdateEditableState(PRBool aNotify); + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, PRBool aCompileEventHandlers); diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 613f987f9b0..43192cab645 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -794,8 +794,6 @@ nsImageLoadingContent::UpdateImageState(PRBool aNotify) return; } - nsEventStates oldState = ImageState(); - mLoading = mBroken = mUserDisabled = mSuppressed = PR_FALSE; // If we were blocked by server-based content policy, we claim to be @@ -818,17 +816,8 @@ nsImageLoadingContent::UpdateImageState(PRBool aNotify) } } - if (aNotify) { - nsIDocument* doc = thisContent->GetCurrentDoc(); - if (doc) { - NS_ASSERTION(thisContent->IsInDoc(), "Something is confused"); - nsEventStates changedBits = oldState ^ ImageState(); - if (!changedBits.IsEmpty()) { - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(thisContent, changedBits); - } - } - } + NS_ASSERTION(thisContent->IsElement(), "Not an element?"); + thisContent->AsElement()->UpdateState(aNotify); } void diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 3815821269c..28a7abf5d71 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -337,9 +337,7 @@ class AutoNotifier { mOldState = aContent->ObjectState(); } ~AutoNotifier() { - if (mNotify) { - mContent->NotifyStateChanged(mOldType, mOldState, PR_FALSE); - } + mContent->NotifyStateChanged(mOldType, mOldState, PR_FALSE, mNotify); } /** @@ -350,7 +348,7 @@ class AutoNotifier { void Notify() { NS_ASSERTION(mNotify, "Should not notify when notify=false"); - mContent->NotifyStateChanged(mOldType, mOldState, PR_TRUE); + mContent->NotifyStateChanged(mOldType, mOldState, PR_TRUE, PR_TRUE); mOldType = mContent->Type(); mOldState = mContent->ObjectState(); } @@ -1639,7 +1637,9 @@ nsObjectLoadingContent::UnloadContent() void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, - nsEventStates aOldState, PRBool aSync) + nsEventStates aOldState, + PRBool aSync, + PRBool aNotify) { LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx) (sync=%i)\n", this, aOldType, aOldState.GetInternalValue(), mType, @@ -1649,6 +1649,18 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, do_QueryInterface(static_cast(this)); NS_ASSERTION(thisContent, "must be a content"); + NS_ASSERTION(thisContent->IsElement(), "Not an element?"); + + // Unfortunately, we do some state changes without notifying + // (e.g. in Fallback when canceling image requests), so we have to + // manually notify object state changes. + thisContent->AsElement()->UpdateState(false); + + if (!aNotify) { + // We're done here + return; + } + nsIDocument* doc = thisContent->GetCurrentDoc(); if (!doc) { return; // Nothing to do @@ -1666,8 +1678,7 @@ nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType, doc->ContentStateChanged(thisContent, changedBits); } if (aSync) { - // Make sure that frames are actually constructed, and do it after - // EndUpdate was called. + // Make sure that frames are actually constructed immediately. doc->FlushPendingNotifications(Flush_Frames); } } else if (aOldType != mType) { diff --git a/content/base/src/nsObjectLoadingContent.h b/content/base/src/nsObjectLoadingContent.h index aa676b69910..2df93eaafe6 100644 --- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -256,9 +256,10 @@ class nsObjectLoadingContent : public nsImageLoadingContent * * @param aSync If a synchronous frame construction is required. If false, * the construction may either be sync or async. + * @param aNotify if false, only need to update the state of our element. */ void NotifyStateChanged(ObjectType aOldType, nsEventStates aOldState, - PRBool aSync); + PRBool aSync, PRBool aNotify); /** * Fires the "Plugin not found" event. This function doesn't do any checks diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 4f8aecf5ecd..8c2ef8dae0c 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -939,17 +939,16 @@ nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc) } void -nsGenericHTMLElement::UpdateEditableState() +nsGenericHTMLElement::UpdateEditableState(PRBool aNotify) { // XXX Should we do this only when in a document? ContentEditableTristate value = GetContentEditableValue(); if (value != eInherit) { - SetEditableFlag(!!value); - + DoSetEditableFlag(!!value, aNotify); return; } - nsStyledElement::UpdateEditableState(); + nsStyledElement::UpdateEditableState(aNotify); } nsresult @@ -1713,27 +1712,26 @@ nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttribu } void -nsGenericHTMLFormElement::UpdateEditableFormControlState() +nsGenericHTMLFormElement::UpdateEditableFormControlState(PRBool aNotify) { // nsCSSFrameConstructor::MaybeConstructLazily is based on the logic of this // function, so should be kept in sync with that. ContentEditableTristate value = GetContentEditableValue(); if (value != eInherit) { - SetEditableFlag(!!value); - + DoSetEditableFlag(!!value, aNotify); return; } nsIContent *parent = GetParent(); if (parent && parent->HasFlag(NODE_IS_EDITABLE)) { - SetEditableFlag(PR_TRUE); + DoSetEditableFlag(PR_TRUE, aNotify); return; } if (!IsTextControl(PR_FALSE)) { - SetEditableFlag(PR_FALSE); + DoSetEditableFlag(PR_FALSE, aNotify); return; } @@ -1741,7 +1739,7 @@ nsGenericHTMLFormElement::UpdateEditableFormControlState() PRBool roState; GetBoolAttr(nsGkAtoms::readonly, &roState); - SetEditableFlag(!roState); + DoSetEditableFlag(!roState, aNotify); } @@ -2410,6 +2408,9 @@ nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed , mForm(nsnull) , mFieldSet(nsnull) { + // We should add the NS_EVENT_STATE_ENABLED bit here as needed, but + // that depends on our type, which is not initialized yet. So we + // have to do this in subclasses. } nsGenericHTMLFormElement::~nsGenericHTMLFormElement() @@ -2564,7 +2565,7 @@ nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument, } // Set parent fieldset which should be used for the disabled state. - UpdateFieldSet(); + UpdateFieldSet(PR_FALSE); return NS_OK; } @@ -2589,6 +2590,11 @@ nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); } } + + if (!mForm) { + // Our novalidate state might have changed + UpdateState(false); + } } // We have to remove the form id observer if there was one. @@ -2601,7 +2607,7 @@ nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); // The element might not have a fieldset anymore. - UpdateFieldSet(); + UpdateFieldSet(PR_FALSE); } nsresult @@ -2622,8 +2628,6 @@ nsGenericHTMLFormElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, } if (mForm && aName == nsGkAtoms::type) { - nsIDocument* doc = GetCurrentDoc(); - GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp); if (!tmp.IsEmpty()) { @@ -2642,10 +2646,8 @@ nsGenericHTMLFormElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, // control anymore. Go ahead and notify on that change, though we might // end up readding and becoming the default control again in // AfterSetAttr. - if (doc && aNotify) { - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(this, NS_EVENT_STATE_DEFAULT); - } + // FIXME: Bug 656197 + UpdateState(aNotify); } if (aName == nsGkAtoms::form) { @@ -2679,7 +2681,6 @@ nsGenericHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, } if (mForm && aName == nsGkAtoms::type) { - nsIDocument* doc = GetDocument(); nsAutoString tmp; GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp); @@ -2700,9 +2701,7 @@ nsGenericHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, // Go ahead and notify on that change. // Note: no need to notify on CanBeDisabled(), since type attr // changes can't affect that. - if (doc && aNotify) { - doc->ContentStateChanged(this, NS_EVENT_STATE_DEFAULT); - } + UpdateState(aNotify); } if (aName == nsGkAtoms::form) { @@ -2913,19 +2912,13 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree, NS_PRECONDITION(!aBindToTree || !aFormIdElement, "aFormIdElement shouldn't be set if aBindToTree is true!"); + PRBool needStateUpdate = PR_FALSE; if (!aBindToTree) { - PRBool wasDefaultSubmit = mForm && mForm->IsDefaultSubmitElement(this); + needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this); ClearForm(PR_TRUE); - if (wasDefaultSubmit) { - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(this, NS_EVENT_STATE_DEFAULT); - } - } } - bool hadForm = mForm; + nsHTMLFormElement *oldForm = mForm; if (!mForm) { // If @form is set, we have to use that to find the form. @@ -2972,7 +2965,7 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree, SetFlags(ADDED_TO_FORM); // Notify only if we just found this mForm. - mForm->AddElement(this, true, !hadForm); + mForm->AddElement(this, true, oldForm == nsnull); if (!nameVal.IsEmpty()) { mForm->AddElementToTable(this, nameVal); @@ -2982,10 +2975,14 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree, mForm->AddElementToTable(this, idVal); } } + + if (mForm != oldForm || needStateUpdate) { + UpdateState(true); + } } void -nsGenericHTMLFormElement::UpdateFieldSet() +nsGenericHTMLFormElement::UpdateFieldSet(PRBool aNotify) { nsIContent* parent = nsnull; nsIContent* prev = nsnull; @@ -2997,11 +2994,19 @@ nsGenericHTMLFormElement::UpdateFieldSet() static_cast(parent); if (!prev || fieldset->GetFirstLegend() != prev) { + if (mFieldSet == fieldset) { + // We already have the right fieldset; + return; + } + if (mFieldSet) { static_cast(mFieldSet)->RemoveElement(this); } mFieldSet = fieldset; fieldset->AddElement(this); + + // The disabled state may have changed + FieldSetDisabledChanged(aNotify); return; } } @@ -3010,24 +3015,16 @@ nsGenericHTMLFormElement::UpdateFieldSet() // No fieldset found. if (mFieldSet) { static_cast(mFieldSet)->RemoveElement(this); + mFieldSet = nsnull; + // The disabled state may have changed + FieldSetDisabledChanged(aNotify); } - mFieldSet = nsnull; } void -nsGenericHTMLFormElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify) +nsGenericHTMLFormElement::FieldSetDisabledChanged(PRBool aNotify) { - if (!aNotify) { - return; - } - - aStates |= NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED; - - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - nsAutoScriptBlocker scriptBlocker; - doc->ContentStateChanged(this, aStates); - } + UpdateState(aNotify); } //---------------------------------------------------------------------- @@ -3556,20 +3553,13 @@ MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument) // that. For elements, we need to send a ContentStateChanged // notification. if (!aContent->IsElement()) { - aContent->UpdateEditableState(); + aContent->UpdateEditableState(PR_FALSE); return; } Element *element = aContent->AsElement(); - nsEventStates stateBefore = element->State(); - element->UpdateEditableState(); - - if (aDocument && stateBefore != element->State()) { - aDocument->ContentStateChanged(element, - NS_EVENT_STATE_MOZ_READONLY | - NS_EVENT_STATE_MOZ_READWRITE); - } + element->UpdateEditableState(PR_TRUE); for (nsIContent *child = aContent->GetFirstChild(); child; diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index 25440ed06dc..a502f66d257 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -199,7 +199,13 @@ public: // HTML element methods void Compact() { mAttrsAndChildren.Compact(); } - virtual void UpdateEditableState(); + virtual void UpdateEditableState(PRBool aNotify); + + // Helper for setting our editable flag and notifying + void DoSetEditableFlag(PRBool aEditable, bool aNotify) { + SetEditableFlag(aEditable); + UpdateState(aNotify); + } virtual PRBool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute, @@ -870,22 +876,18 @@ public: } /** - * This callback is called by a fieldest on all it's elements whenever it's - * disabled attribute is changed so the element knows it's disabled state + * This callback is called by a fieldest on all its elements whenever its + * disabled attribute is changed so the element knows its disabled state * might have changed. * - * @param aStates States for which a change should be notified. - * @note Classes redefining this method should not call ContentStatesChanged - * but they should pass aStates instead. + * @note Classes redefining this method should not do any content + * state updates themselves but should just make sure to call into + * nsGenericHTMLFormElement::FieldSetDisabledChanged. */ - virtual void FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify); + virtual void FieldSetDisabledChanged(PRBool aNotify); void FieldSetFirstLegendChanged(PRBool aNotify) { - UpdateFieldSet(); - - // The disabled state may have change because the element might not be in - // the first legend anymore. - FieldSetDisabledChanged(nsEventStates(), aNotify); + UpdateFieldSet(aNotify); } /** @@ -916,7 +918,7 @@ protected: virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, const nsAString* aValue, PRBool aNotify); - void UpdateEditableFormControlState(); + void UpdateEditableFormControlState(PRBool aNotify); /** * This method will update the form owner, using @form or looking to a parent. @@ -934,7 +936,7 @@ protected: /** * This method will update mFieldset and set it to the first fieldset parent. */ - void UpdateFieldSet(); + void UpdateFieldSet(PRBool aNotify); /** * Add a form id observer which will observe when the element with the id in diff --git a/content/html/content/src/nsHTMLButtonElement.cpp b/content/html/content/src/nsHTMLButtonElement.cpp index b4688b7267d..6205895cdc8 100644 --- a/content/html/content/src/nsHTMLButtonElement.cpp +++ b/content/html/content/src/nsHTMLButtonElement.cpp @@ -134,6 +134,12 @@ public: virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor); + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + PRBool aCompileEventHandlers); + virtual void UnbindFromTree(PRBool aDeep = PR_TRUE, + PRBool aNullParent = PR_TRUE); + virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; virtual void DoneCreatingElement(); virtual nsXPCClassInfo* GetClassInfo(); @@ -167,6 +173,9 @@ nsHTMLButtonElement::nsHTMLButtonElement(already_AddRefed aNodeInfo { //