зеркало из https://github.com/mozilla/gecko-dev.git
Bug 288392, DOMSubtreeModified event, r=peterv, sr=jst
This commit is contained in:
Родитель
3e985ddd5d
Коммит
bd6edd6ac3
|
@ -90,11 +90,12 @@ template<class E> class nsCOMArray;
|
|||
class nsIDocumentObserver;
|
||||
class nsBindingManager;
|
||||
class nsIDOMNodeList;
|
||||
class mozAutoSubtreeModified;
|
||||
|
||||
// IID for the nsIDocument interface
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0x0b8acf09, 0x7877, 0x4928, \
|
||||
{ 0x8b, 0xfb, 0x6d, 0x7c, 0x6d, 0x53, 0xff, 0x32 } }
|
||||
{ 0x1b8ed19c, 0xb87d, 0x4058, \
|
||||
{ 0x92, 0x2a, 0xff, 0xbc, 0x36, 0x29, 0x3b, 0xd7 } }
|
||||
|
||||
// Flag for AddStyleSheet().
|
||||
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
||||
|
@ -846,6 +847,11 @@ public:
|
|||
*/
|
||||
virtual void FlushSkinBindings() = 0;
|
||||
|
||||
/**
|
||||
* Returns PR_TRUE if one or more mutation events are being dispatched.
|
||||
*/
|
||||
virtual PRBool MutationEventBeingDispatched() = 0;
|
||||
|
||||
protected:
|
||||
~nsIDocument()
|
||||
{
|
||||
|
@ -856,6 +862,15 @@ protected:
|
|||
// XXX Same thing applies to mBindingManager
|
||||
}
|
||||
|
||||
/**
|
||||
* These methods should be called before and after dispatching
|
||||
* a mutation event.
|
||||
* To make this easy and painless, use the mozAutoSubtreeModified helper class.
|
||||
*/
|
||||
virtual void WillDispatchMutationEvent(nsINode* aTarget) = 0;
|
||||
virtual void MutationEventDispatched(nsINode* aTarget) = 0;
|
||||
friend class mozAutoSubtreeModified;
|
||||
|
||||
nsString mDocumentTitle;
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
nsCOMPtr<nsIURI> mDocumentBaseURI;
|
||||
|
@ -952,6 +967,48 @@ private:
|
|||
mozAutoDocUpdate MOZ_AUTO_DOC_UPDATE_PASTE(_autoDocUpdater_, __LINE__) \
|
||||
(doc,type,notify)
|
||||
|
||||
/**
|
||||
* mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
|
||||
* event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
|
||||
* object is deleted.
|
||||
*/
|
||||
class mozAutoSubtreeModified
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aSubTreeOwner The document in which a subtree will be modified.
|
||||
* @param aTarget The target of the possible DOMSubtreeModified event.
|
||||
* Can be nsnull, in which case mozAutoSubtreeModified
|
||||
* is just used to batch DOM mutations.
|
||||
*/
|
||||
mozAutoSubtreeModified(nsIDocument* aSubtreeOwner, nsINode* aTarget)
|
||||
{
|
||||
UpdateTarget(aSubtreeOwner, aTarget);
|
||||
}
|
||||
|
||||
~mozAutoSubtreeModified()
|
||||
{
|
||||
UpdateTarget(nsnull, nsnull);
|
||||
}
|
||||
|
||||
void UpdateTarget(nsIDocument* aSubtreeOwner, nsINode* aTarget)
|
||||
{
|
||||
if (mSubtreeOwner) {
|
||||
mSubtreeOwner->MutationEventDispatched(mTarget);
|
||||
}
|
||||
|
||||
mTarget = aTarget;
|
||||
mSubtreeOwner = aSubtreeOwner;
|
||||
if (mSubtreeOwner) {
|
||||
mSubtreeOwner->WillDispatchMutationEvent(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsINode> mTarget;
|
||||
nsCOMPtr<nsIDocument> mSubtreeOwner;
|
||||
};
|
||||
|
||||
// XXX These belong somewhere else
|
||||
nsresult
|
||||
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult);
|
||||
|
|
|
@ -2820,6 +2820,12 @@ nsContentUtils::HasMutationListeners(nsINode* aNode,
|
|||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// To batch DOMSubtreeModified properly, all mutation events should be
|
||||
// processed if one is being processed already.
|
||||
if (doc->MutationEventBeingDispatched()) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// global object will be null for documents that don't have windows.
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
window = do_QueryInterface(doc->GetScriptGlobalObject());
|
||||
|
|
|
@ -145,6 +145,7 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
|
|||
#include "nsDateTimeFormatCID.h"
|
||||
#include "nsIDateTimeFormat.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsMutationEvent.h"
|
||||
#include "nsIDOMXPathEvaluator.h"
|
||||
#include "nsDOMCID.h"
|
||||
|
||||
|
@ -5704,6 +5705,72 @@ nsDocument::OnPageHide(PRBool aPersisted)
|
|||
mVisible = PR_FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
|
||||
{
|
||||
NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
|
||||
mSubtreeModifiedTargets.Count() == 0,
|
||||
"mSubtreeModifiedTargets not cleared after dispatching?");
|
||||
++mSubtreeModifiedDepth;
|
||||
if (aTarget) {
|
||||
mSubtreeModifiedTargets.AppendObject(aTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::MutationEventDispatched(nsINode* aTarget)
|
||||
{
|
||||
--mSubtreeModifiedDepth;
|
||||
if (mSubtreeModifiedDepth == 0) {
|
||||
PRInt32 count = mSubtreeModifiedTargets.Count();
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window;
|
||||
window = do_QueryInterface(GetScriptGlobalObject());
|
||||
if (window &&
|
||||
!window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
|
||||
mSubtreeModifiedTargets.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMArray<nsINode> realTargets;
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
nsINode* possibleTarget = mSubtreeModifiedTargets[i];
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
|
||||
if (content && content->IsAnonymousForEvents()) {
|
||||
if (realTargets.IndexOf(possibleTarget) == -1) {
|
||||
realTargets.AppendObject(possibleTarget);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
nsINode* commonAncestor = nsnull;
|
||||
PRInt32 realTargetCount = realTargets.Count();
|
||||
for (PRInt32 j = 0; j < realTargetCount; ++j) {
|
||||
commonAncestor =
|
||||
nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
|
||||
if (commonAncestor) {
|
||||
realTargets.ReplaceObjectAt(commonAncestor, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!commonAncestor) {
|
||||
realTargets.AppendObject(possibleTarget);
|
||||
}
|
||||
}
|
||||
|
||||
mSubtreeModifiedTargets.Clear();
|
||||
|
||||
PRInt32 realTargetCount = realTargets.Count();
|
||||
for (PRInt32 k = 0; k < realTargetCount; ++k) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED);
|
||||
nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PRUint32 GetURIHash(nsIURI* aURI)
|
||||
{
|
||||
nsCAutoString str;
|
||||
|
|
|
@ -509,6 +509,13 @@ public:
|
|||
|
||||
virtual void OnPageShow(PRBool aPersisted);
|
||||
virtual void OnPageHide(PRBool aPersisted);
|
||||
|
||||
virtual void WillDispatchMutationEvent(nsINode* aTarget);
|
||||
virtual void MutationEventDispatched(nsINode* aTarget);
|
||||
virtual PRBool MutationEventBeingDispatched()
|
||||
{
|
||||
return (mSubtreeModifiedDepth > 0);
|
||||
}
|
||||
|
||||
// nsINode
|
||||
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
|
||||
|
@ -827,6 +834,9 @@ private:
|
|||
// Member to store out last-selected stylesheet set.
|
||||
nsString mLastStyleSheetSet;
|
||||
|
||||
nsCOMArray<nsINode> mSubtreeModifiedTargets;
|
||||
PRUint32 mSubtreeModifiedDepth;
|
||||
|
||||
// Our update nesting level
|
||||
PRUint32 mUpdateNestLevel;
|
||||
};
|
||||
|
|
|
@ -470,6 +470,7 @@ nsGenericDOMDataNode::SetTextInternal(PRUint32 aOffset, PRUint32 aCount,
|
|||
mutation.mNewAttrValue = do_GetAtom(val);
|
||||
}
|
||||
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,9 @@ nsNode3Tearoff::GetTextContent(nsAString &aTextContent)
|
|||
NS_IMETHODIMP
|
||||
nsNode3Tearoff::SetTextContent(const nsAString &aTextContent)
|
||||
{
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(mContent->GetOwnerDoc(), nsnull);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
|
||||
NS_ASSERTION(node, "We have an nsIContent which doesn't support nsIDOMNode");
|
||||
|
||||
|
@ -1667,6 +1670,9 @@ nsGenericElement::JoinTextNodes(nsIContent* aFirst,
|
|||
nsresult
|
||||
nsGenericElement::Normalize()
|
||||
{
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
|
||||
|
||||
nsresult result = NS_OK;
|
||||
PRUint32 index, count = GetChildCount();
|
||||
|
||||
|
@ -2343,6 +2349,7 @@ nsGenericElement::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex,
|
|||
NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
|
||||
mutation.mRelatedNode = do_QueryInterface(container);
|
||||
mozAutoSubtreeModified subtree(container->GetOwnerDoc(), container);
|
||||
nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
|
||||
}
|
||||
}
|
||||
|
@ -2387,11 +2394,13 @@ nsGenericElement::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify,
|
|||
|
||||
nsMutationGuard guard;
|
||||
|
||||
mozAutoSubtreeModified subtree(nsnull, nsnull);
|
||||
if (aNotify &&
|
||||
nsContentUtils::HasMutationListeners(aKid,
|
||||
NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
|
||||
nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
|
||||
mutation.mRelatedNode = do_QueryInterface(container);
|
||||
subtree.UpdateTarget(container->GetOwnerDoc(), container);
|
||||
nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
|
||||
}
|
||||
|
||||
|
@ -3326,6 +3335,7 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
|
|||
mutation.mPrevAttrValue = do_GetAtom(aOldValue);
|
||||
}
|
||||
mutation.mAttrChange = modType;
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
|
@ -3553,6 +3563,7 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|||
mutation.mPrevAttrValue = do_GetAtom(value);
|
||||
mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
|
||||
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
|
||||
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
|
||||
}
|
||||
|
||||
|
|
|
@ -1087,6 +1087,9 @@ nsresult nsRange::DeleteContents()
|
|||
if(IsDetached())
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(mRoot ? mRoot->GetOwnerDoc(): nsnull, nsnull);
|
||||
|
||||
// Save the range end points locally to avoid interference
|
||||
// of Range gravity during our edits!
|
||||
|
||||
|
@ -1276,6 +1279,9 @@ nsresult nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn)
|
|||
if(mIsDetached)
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(mRoot ? mRoot->GetOwnerDoc(): nsnull, nsnull);
|
||||
|
||||
// XXX_kin: The spec says that nodes that are completely in the
|
||||
// XXX_kin: range should be moved into the document fragment, not
|
||||
// XXX_kin: copied. This method will have to be rewritten using
|
||||
|
|
|
@ -179,7 +179,8 @@ public:
|
|||
* Returns the mutation bits depending on which mutation listeners are
|
||||
* registered to this listener manager.
|
||||
* @note If a listener is an nsIDOMMutationListener, all possible mutation
|
||||
* event bits are returned.
|
||||
* event bits are returned. All bits are also returned if one of the
|
||||
* event listeners is registered to handle DOMSubtreeModified events.
|
||||
*/
|
||||
virtual PRUint32 MutationListenerBits() = 0;
|
||||
};
|
||||
|
|
|
@ -527,7 +527,11 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
if (window) {
|
||||
NS_ASSERTION(window->IsInnerWindow(),
|
||||
"Setting mutation listener bits on outer window?");
|
||||
window->SetMutationListeners(MutationBitForEventType(aType));
|
||||
// If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
|
||||
// mutations.
|
||||
window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
|
||||
kAllMutationBits :
|
||||
MutationBitForEventType(aType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1868,6 +1872,9 @@ nsEventListenerManager::MutationListenerBits()
|
|||
if (ls &&
|
||||
(ls->mEventType >= NS_MUTATION_START &&
|
||||
ls->mEventType <= NS_MUTATION_END)) {
|
||||
if (ls->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
|
||||
return kAllMutationBits;
|
||||
}
|
||||
bits |= MutationBitForEventType(ls->mEventType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -762,6 +762,9 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
|
|||
// scriptloader before the last EndUpdate call.
|
||||
mozAutoDocUpdate updateBatch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
|
||||
|
||||
// Remove childnodes
|
||||
nsContentUtils::SetNodeTextContent(this, EmptyString(), PR_FALSE);
|
||||
|
||||
|
|
|
@ -1498,6 +1498,7 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify)
|
|||
mutation.mPrevAttrValue = do_GetAtom(oldValue);
|
||||
mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
|
||||
|
||||
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
|
||||
nsEventDispatcher::Dispatch(NS_STATIC_CAST(nsIContent*, this),
|
||||
nsnull, &mutation);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче