Bug 288392, DOMSubtreeModified event, r=peterv, sr=jst

This commit is contained in:
Olli.Pettay@helsinki.fi 2007-03-24 05:18:02 -07:00
Родитель 3e985ddd5d
Коммит bd6edd6ac3
11 изменённых файлов: 174 добавлений и 4 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -510,6 +510,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;
virtual nsIContent *GetChildAt(PRUint32 aIndex) 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);
}