mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
267e9f48e0
@ -1552,6 +1552,15 @@ public:
|
||||
*/
|
||||
static ViewportInfo GetViewportInfo(nsIDocument* aDocument);
|
||||
|
||||
// Call EnterMicroTask when you're entering JS execution.
|
||||
// Usually the best way to do this is to use nsAutoMicroTask.
|
||||
static void EnterMicroTask() { ++sMicroTaskLevel; }
|
||||
static void LeaveMicroTask();
|
||||
|
||||
static bool IsInMicroTask() { return sMicroTaskLevel != 0; }
|
||||
static PRUint32 MicroTaskLevel() { return sMicroTaskLevel; }
|
||||
static void SetMicroTaskLevel(PRUint32 aLevel) { sMicroTaskLevel = aLevel; }
|
||||
|
||||
/* Process viewport META data. This gives us information for the scale
|
||||
* and zoom of a page on mobile devices. We stick the information in
|
||||
* the document header and use it later on after rendering.
|
||||
@ -1956,6 +1965,13 @@ public:
|
||||
static nsresult Atob(const nsAString& aAsciiString,
|
||||
nsAString& aBinaryData);
|
||||
|
||||
/** If aJSArray is a Javascript array, this method iterates over its
|
||||
* elements and appends values to aRetVal as nsIAtoms.
|
||||
* @throw NS_ERROR_ILLEGAL_VALUE if aJSArray isn't a JS array.
|
||||
*/
|
||||
static nsresult JSArrayToAtomArray(JSContext* aCx, const JS::Value& aJSArray,
|
||||
nsCOMArray<nsIAtom>& aRetVal);
|
||||
|
||||
/**
|
||||
* Returns whether the input element passed in parameter has the autocomplete
|
||||
* functionnality enabled. It is taking into account the form owner.
|
||||
@ -2082,6 +2098,7 @@ private:
|
||||
#ifdef DEBUG
|
||||
static PRUint32 sDOMNodeRemovedSuppressCount;
|
||||
#endif
|
||||
static PRUint32 sMicroTaskLevel;
|
||||
// Not an nsCOMArray because removing elements from those is slower
|
||||
static nsTArray< nsCOMPtr<nsIRunnable> >* sBlockedScriptRunners;
|
||||
static PRUint32 sRunnersCountAtFirstBlocker;
|
||||
@ -2184,6 +2201,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class NS_STACK_CLASS nsAutoMicroTask
|
||||
{
|
||||
public:
|
||||
nsAutoMicroTask()
|
||||
{
|
||||
nsContentUtils::EnterMicroTask();
|
||||
}
|
||||
~nsAutoMicroTask()
|
||||
{
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
}
|
||||
};
|
||||
|
||||
#define NS_INTERFACE_MAP_ENTRY_TEAROFF(_interface, _allocator) \
|
||||
if (aIID.Equals(NS_GET_IID(_interface))) { \
|
||||
foundInterface = static_cast<_interface *>(_allocator); \
|
||||
|
@ -125,8 +125,8 @@ class Element;
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0x283ec27d, 0x5b23, 0x49b2, \
|
||||
{ 0x94, 0xd9, 0x9, 0xb5, 0xdb, 0x45, 0x30, 0x73 } }
|
||||
{ 0x8e51e6d9, 0x914d, 0x46ba, \
|
||||
{ 0xb3, 0x11, 0x2f, 0x27, 0x3d, 0xe6, 0x0d, 0x19 } }
|
||||
|
||||
|
||||
// Flag for AddStyleSheet().
|
||||
@ -1642,6 +1642,30 @@ public:
|
||||
// declaration of nsINode::SizeOfIncludingThis.
|
||||
virtual void DocSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const;
|
||||
|
||||
PRBool MayHaveDOMMutationObservers()
|
||||
{
|
||||
return mMayHaveDOMMutationObservers;
|
||||
}
|
||||
|
||||
void SetMayHaveDOMMutationObservers()
|
||||
{
|
||||
mMayHaveDOMMutationObservers = true;
|
||||
}
|
||||
|
||||
bool IsInSyncOperation()
|
||||
{
|
||||
return mInSyncOperationCount != 0;
|
||||
}
|
||||
|
||||
void SetIsInSyncOperation(bool aSync)
|
||||
{
|
||||
if (aSync) {
|
||||
++mInSyncOperationCount;
|
||||
} else {
|
||||
--mInSyncOperationCount;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PRUint64 mWarnedAbout;
|
||||
|
||||
@ -1813,6 +1837,9 @@ protected:
|
||||
// True if a style flush might not be a no-op
|
||||
bool mNeedStyleFlush;
|
||||
|
||||
// True if a DOMMutationObserver is perhaps attached to a node in the document.
|
||||
bool mMayHaveDOMMutationObservers;
|
||||
|
||||
// The document's script global object, the object from which the
|
||||
// document can get its script context and scope. This is the
|
||||
// *inner* window object.
|
||||
@ -1908,6 +1935,8 @@ protected:
|
||||
nsCOMPtr<nsIVariant> mStateObjectCached;
|
||||
|
||||
PRUint8 mDefaultElementType;
|
||||
|
||||
PRUint32 mInSyncOperationCount;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
|
||||
@ -1954,6 +1983,16 @@ private:
|
||||
nsCOMPtr<nsIDocument> mSubtreeOwner;
|
||||
};
|
||||
|
||||
class NS_STACK_CLASS nsAutoSyncOperation
|
||||
{
|
||||
public:
|
||||
nsAutoSyncOperation(nsIDocument* aDocument);
|
||||
~nsAutoSyncOperation();
|
||||
private:
|
||||
nsCOMArray<nsIDocument> mDocuments;
|
||||
PRUint32 mMicroTaskLevel;
|
||||
};
|
||||
|
||||
// XXX These belong somewhere else
|
||||
nsresult
|
||||
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult);
|
||||
|
@ -1327,6 +1327,8 @@ private:
|
||||
NodeHasExplicitBaseURI,
|
||||
// Set if the element has some style states locked
|
||||
ElementHasLockedStyleStates,
|
||||
// Set if the node may have DOMMutationObserver attached to it.
|
||||
NodeMayHaveDOMMutationObserver,
|
||||
// Guard value
|
||||
BooleanFlagCount
|
||||
};
|
||||
@ -1383,7 +1385,10 @@ public:
|
||||
void SetIsPurpleRoot(bool aValue)
|
||||
{ SetBoolFlag(NodeIsPurpleRoot, aValue); }
|
||||
bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); }
|
||||
|
||||
bool MayHaveDOMMutationObserver()
|
||||
{ return GetBoolFlag(NodeMayHaveDOMMutationObserver); }
|
||||
void SetMayHaveDOMMutationObserver()
|
||||
{ SetBoolFlag(NodeMayHaveDOMMutationObserver, true); }
|
||||
bool HasListenerManager() { return HasFlag(NODE_HAS_LISTENERMANAGER); }
|
||||
protected:
|
||||
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
|
||||
@ -1421,6 +1426,12 @@ public:
|
||||
// Optimized way to get classinfo.
|
||||
virtual nsXPCClassInfo* GetClassInfo() = 0;
|
||||
|
||||
// Makes nsINode object to keep aObject alive.
|
||||
void BindObject(nsISupports* aObject);
|
||||
// After calling UnbindObject nsINode object doesn't keep
|
||||
// aObject alive anymore.
|
||||
void UnbindObject(nsISupports* aObject);
|
||||
|
||||
/**
|
||||
* Returns the length of this node, as specified at
|
||||
* <http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node-length>
|
||||
|
@ -155,6 +155,7 @@ CPPSRCS = \
|
||||
ThirdPartyUtil.cpp \
|
||||
nsEventSource.cpp \
|
||||
FileIOObject.cpp \
|
||||
nsDOMMutationObserver.cpp \
|
||||
$(NULL)
|
||||
|
||||
# Are we targeting x86-32 or x86-64? If so, we want to include SSE2 code for
|
||||
|
@ -208,6 +208,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsIDOMDocumentType.h"
|
||||
@ -271,6 +272,7 @@ PRUint32 nsContentUtils::sScriptBlockerCount = 0;
|
||||
#ifdef DEBUG
|
||||
PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
|
||||
#endif
|
||||
PRUint32 nsContentUtils::sMicroTaskLevel = 0;
|
||||
nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull;
|
||||
PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
|
||||
nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
|
||||
@ -4197,6 +4199,7 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
||||
// mutations.
|
||||
mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
|
||||
UPDATE_CONTENT_MODEL, true);
|
||||
nsAutoMutationBatch mb;
|
||||
|
||||
PRUint32 childCount = aContent->GetChildCount();
|
||||
|
||||
@ -4221,10 +4224,12 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
||||
}
|
||||
}
|
||||
else {
|
||||
mb.Init(aContent, true, false);
|
||||
for (PRUint32 i = 0; i < childCount; ++i) {
|
||||
aContent->RemoveChildAt(0, true);
|
||||
}
|
||||
}
|
||||
mb.RemovalDone();
|
||||
|
||||
if (aValue.IsEmpty()) {
|
||||
return NS_OK;
|
||||
@ -4237,7 +4242,9 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
||||
|
||||
textContent->SetText(aValue, true);
|
||||
|
||||
return aContent->AppendChildTo(textContent, true);
|
||||
rv = aContent->AppendChildTo(textContent, true);
|
||||
mb.NodesAdded();
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void AppendNodeTextContentsRecurse(nsINode* aNode, nsAString& aResult)
|
||||
@ -4790,6 +4797,14 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::LeaveMicroTask()
|
||||
{
|
||||
if (--sMicroTaskLevel == 0) {
|
||||
nsDOMMutationObserver::HandleMutations();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for nsContentUtils::ProcessViewportInfo.
|
||||
*
|
||||
@ -6609,3 +6624,46 @@ nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::JSArrayToAtomArray(JSContext* aCx, const JS::Value& aJSArray,
|
||||
nsCOMArray<nsIAtom>& aRetVal)
|
||||
{
|
||||
JSAutoRequest ar(aCx);
|
||||
if (!aJSArray.isObject()) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
JSObject* obj = &aJSArray.toObject();
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(aCx, obj)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
PRUint32 length;
|
||||
if (!JS_IsArrayObject(aCx, obj) || !JS_GetArrayLength(aCx, obj, &length)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
JSString* str = nsnull;
|
||||
JS::Anchor<JSString *> deleteProtector(str);
|
||||
for (PRUint32 i = 0; i < length; ++i) {
|
||||
jsval v;
|
||||
if (!JS_GetElement(aCx, obj, i, &v) ||
|
||||
!(str = JS_ValueToString(aCx, v))) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsDependentJSString depStr;
|
||||
if (!depStr.init(aCx, str)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> a = do_GetAtom(depStr);
|
||||
if (!a) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
aRetVal.AppendObject(a);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
865
content/base/src/nsDOMMutationObserver.cpp
Normal file
865
content/base/src/nsDOMMutationObserver.cpp
Normal file
@ -0,0 +1,865 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsIXPCScriptable.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
#include "nsTextFragment.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "DictionaryHelpers.h"
|
||||
|
||||
nsCOMArray<nsIDOMMozMutationObserver>*
|
||||
nsDOMMutationObserver::sScheduledMutationObservers = nsnull;
|
||||
|
||||
nsIDOMMozMutationObserver* nsDOMMutationObserver::sCurrentObserver = nsnull;
|
||||
|
||||
PRUint32 nsDOMMutationObserver::sMutationLevel = 0;
|
||||
PRUint64 nsDOMMutationObserver::sCount = 0;
|
||||
|
||||
nsAutoTArray<nsCOMArray<nsIDOMMozMutationObserver>, 4>*
|
||||
nsDOMMutationObserver::sCurrentlyHandlingObservers = nsnull;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationRecord)
|
||||
|
||||
DOMCI_DATA(MutationRecord, nsDOMMutationRecord)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMMutationRecord)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MutationRecord)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationRecord)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPreviousSibling)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNextSibling)
|
||||
tmp->mAddedNodes = nsnull;
|
||||
tmp->mRemovedNodes = nsnull;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationRecord)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPreviousSibling)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNextSibling)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAddedNodes");
|
||||
cb.NoteXPCOMChild(static_cast<nsIDOMNodeList*>(tmp->mAddedNodes));
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRemovedNodes");
|
||||
cb.NoteXPCOMChild(static_cast<nsIDOMNodeList*>(tmp->mRemovedNodes));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetType(nsAString& aType)
|
||||
{
|
||||
aType = mType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetTarget(nsIDOMNode** aTarget)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> n = do_QueryInterface(mTarget);
|
||||
n.forget(aTarget);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetAddedNodes(nsIDOMNodeList** aAddedNodes)
|
||||
{
|
||||
if (!mAddedNodes && mTarget) {
|
||||
mAddedNodes = new nsSimpleContentList(mTarget);
|
||||
}
|
||||
NS_IF_ADDREF(*aAddedNodes = mAddedNodes);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetRemovedNodes(nsIDOMNodeList** aRemovedNodes)
|
||||
{
|
||||
if (!mRemovedNodes && mTarget) {
|
||||
mRemovedNodes = new nsSimpleContentList(mTarget);
|
||||
}
|
||||
NS_IF_ADDREF(*aRemovedNodes = mRemovedNodes);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetPreviousSibling(nsIDOMNode** aPreviousSibling)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> n = do_QueryInterface(mPreviousSibling);
|
||||
*aPreviousSibling = n.forget().get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetNextSibling(nsIDOMNode** aNextSibling)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> n = do_QueryInterface(mNextSibling);
|
||||
*aNextSibling = n.forget().get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetAttributeName(nsAString& aAttrName)
|
||||
{
|
||||
aAttrName = mAttrName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetAttributeNamespace(nsAString& aAttrNamespace)
|
||||
{
|
||||
aAttrNamespace = mAttrNamespace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationRecord::GetOldValue(nsAString& aPrevValue)
|
||||
{
|
||||
aPrevValue = mPrevValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Observer
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsMutationReceiver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMutationReceiver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMutationReceiver)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMutationReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsMutationReceiver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsMutationReceiver)
|
||||
tmp->Disconnect();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsMutationReceiver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
void
|
||||
nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
|
||||
mozilla::dom::Element* aElement,
|
||||
PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
PRInt32 aModType)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!ObservesAttr(aElement, aNameSpaceID, aAttribute) ||
|
||||
aElement->IsInNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(NS_LITERAL_STRING("attributes"));
|
||||
|
||||
NS_ASSERTION(!m->mTarget || m->mTarget == aElement,
|
||||
"Wrong target!");
|
||||
NS_ASSERTION(m->mAttrName.IsVoid() ||
|
||||
m->mAttrName.Equals(nsDependentAtomString(aAttribute)),
|
||||
"Wrong attribute!");
|
||||
if (!m->mTarget) {
|
||||
m->mTarget = aElement;
|
||||
m->mAttrName = nsAtomString(aAttribute);
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
m->mAttrNamespace.SetIsVoid(true);
|
||||
} else {
|
||||
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID,
|
||||
m->mAttrNamespace);
|
||||
}
|
||||
}
|
||||
|
||||
if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
|
||||
if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
|
||||
m->mPrevValue.SetIsVoid(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
|
||||
nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!CharacterData() || !(Subtree() || aContent == Target()) ||
|
||||
aContent->IsInNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(NS_LITERAL_STRING("characterData"));
|
||||
|
||||
NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
|
||||
"Wrong target!");
|
||||
|
||||
if (!m->mTarget) {
|
||||
m->mTarget = aContent;
|
||||
}
|
||||
if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) {
|
||||
aContent->GetText()->AppendTo(m->mPrevValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
PRInt32 aNewIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aFirstNewContent->IsInNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsAutoMutationBatch::IsBatching()) {
|
||||
if (parent == nsAutoMutationBatch::GetBatchTarget()) {
|
||||
nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(NS_LITERAL_STRING("childList"));
|
||||
NS_ASSERTION(!m->mTarget || m->mTarget == parent,
|
||||
"Wrong target!");
|
||||
if (m->mTarget) {
|
||||
// Already handled case.
|
||||
return;
|
||||
}
|
||||
m->mTarget = parent;
|
||||
m->mAddedNodes = new nsSimpleContentList(parent);
|
||||
|
||||
nsINode* n = aFirstNewContent;
|
||||
while (n) {
|
||||
m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n));
|
||||
n = n->GetNextSibling();
|
||||
}
|
||||
m->mPreviousSibling = aFirstNewContent->GetPreviousSibling();
|
||||
}
|
||||
|
||||
void
|
||||
nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aChild->IsInNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nsAutoMutationBatch::IsBatching()) {
|
||||
if (parent == nsAutoMutationBatch::GetBatchTarget()) {
|
||||
nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(NS_LITERAL_STRING("childList"));
|
||||
if (m->mTarget) {
|
||||
// Already handled case.
|
||||
return;
|
||||
}
|
||||
m->mTarget = parent;
|
||||
m->mAddedNodes = new nsSimpleContentList(parent);
|
||||
m->mAddedNodes->AppendElement(aChild);
|
||||
m->mPreviousSibling = aChild->GetPreviousSibling();
|
||||
m->mNextSibling = aChild->GetNextSibling();
|
||||
}
|
||||
|
||||
void
|
||||
nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer,
|
||||
nsIContent* aPreviousSibling)
|
||||
{
|
||||
if (aChild->IsInNativeAnonymousSubtree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
if (nsAutoMutationBatch::IsBatching()) {
|
||||
if (nsAutoMutationBatch::IsRemovalDone()) {
|
||||
// This can happen for example if HTML parser parses to
|
||||
// context node, but needs to move elements around.
|
||||
return;
|
||||
}
|
||||
if (nsAutoMutationBatch::GetBatchTarget() != parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (wantsChildList || Subtree()) {
|
||||
nsAutoMutationBatch::NodeRemoved(aChild);
|
||||
nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Subtree()) {
|
||||
// Try to avoid creating transient observer if the node
|
||||
// already has an observer observing the same set of nodes.
|
||||
nsMutationReceiver* orig = GetParent() ? GetParent() : this;
|
||||
if (Observer()->GetReceiverFor(aChild, false) != orig) {
|
||||
bool transientExists = false;
|
||||
nsCOMArray<nsMutationReceiver>* transientReceivers = nsnull;
|
||||
Observer()->mTransientReceivers.Get(aChild, &transientReceivers);
|
||||
if (!transientReceivers) {
|
||||
transientReceivers = new nsCOMArray<nsMutationReceiver>();
|
||||
Observer()->mTransientReceivers.Put(aChild, transientReceivers);
|
||||
} else {
|
||||
for (PRInt32 i = 0; i < transientReceivers->Count(); ++i) {
|
||||
nsMutationReceiver* r = transientReceivers->ObjectAt(i);
|
||||
if (r->GetParent() == orig) {
|
||||
transientExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!transientExists) {
|
||||
// Make sure the elements which are removed from the
|
||||
// subtree are kept in the same observation set.
|
||||
transientReceivers->AppendObject(new nsMutationReceiver(aChild, orig));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ChildList() && (Subtree() || parent == Target())) {
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(NS_LITERAL_STRING("childList"));
|
||||
if (m->mTarget) {
|
||||
// Already handled case.
|
||||
return;
|
||||
}
|
||||
m->mTarget = parent;
|
||||
m->mRemovedNodes = new nsSimpleContentList(parent);
|
||||
m->mRemovedNodes->AppendElement(aChild);
|
||||
m->mPreviousSibling = aPreviousSibling;
|
||||
m->mNextSibling = parent->GetChildAt(aIndexInContainer);
|
||||
}
|
||||
// We need to schedule always, so that after microtask mTransientReceivers
|
||||
// can be cleared correctly.
|
||||
Observer()->ScheduleForRun();
|
||||
}
|
||||
|
||||
// Observer
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver)
|
||||
|
||||
DOMCI_DATA(MozMutationObserver, nsDOMMutationObserver)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMMozMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMutationObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
|
||||
for (PRInt32 i = 0; i < tmp->mReceivers.Count(); ++i) {
|
||||
tmp->mReceivers[i]->Disconnect();
|
||||
}
|
||||
tmp->mReceivers.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingMutations)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCallback)
|
||||
// No need to handle mTransientReceivers
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mReceivers)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingMutations)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCallback)
|
||||
// No need to handle mTransientReceivers
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
nsMutationReceiver*
|
||||
nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate)
|
||||
{
|
||||
if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
for (PRInt32 i = 0; i < mReceivers.Count(); ++i) {
|
||||
if (mReceivers[i]->Target() == aNode) {
|
||||
return mReceivers[i];
|
||||
}
|
||||
}
|
||||
if (!aMayCreate) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsMutationReceiver* r = new nsMutationReceiver(aNode, this);
|
||||
mReceivers.AppendObject(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode,
|
||||
nsTArray<nsMutationReceiver*>&
|
||||
aReceivers)
|
||||
{
|
||||
nsINode* n = aNode;
|
||||
while (n) {
|
||||
if (n->MayHaveDOMMutationObserver()) {
|
||||
nsMutationReceiver* r = GetReceiverFor(n, false);
|
||||
if (r && r->Subtree() && !aReceivers.Contains(r)) {
|
||||
aReceivers.AppendElement(r);
|
||||
// If we've found all the receivers the observer has,
|
||||
// no need to search for more.
|
||||
if (mReceivers.Count() == PRInt32(aReceivers.Length())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
nsCOMArray<nsMutationReceiver>* transientReceivers = nsnull;
|
||||
if (mTransientReceivers.Get(n, &transientReceivers) && transientReceivers) {
|
||||
for (PRInt32 i = 0; i < transientReceivers->Count(); ++i) {
|
||||
nsMutationReceiver* r = transientReceivers->ObjectAt(i);
|
||||
nsMutationReceiver* parent = r->GetParent();
|
||||
if (r->Subtree() && parent && !aReceivers.Contains(parent)) {
|
||||
aReceivers.AppendElement(parent);
|
||||
}
|
||||
}
|
||||
if (mReceivers.Count() == PRInt32(aReceivers.Length())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
n = n->GetNodeParent();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::ScheduleForRun()
|
||||
{
|
||||
nsDOMMutationObserver::AddCurrentlyHandlingObserver(this);
|
||||
|
||||
if (mWaitingForRun) {
|
||||
return;
|
||||
}
|
||||
mWaitingForRun = true;
|
||||
RescheduleForRun();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::RescheduleForRun()
|
||||
{
|
||||
if (!sScheduledMutationObservers) {
|
||||
sScheduledMutationObservers = new nsCOMArray<nsIDOMMozMutationObserver>;
|
||||
}
|
||||
|
||||
bool didInsert = false;
|
||||
for (PRInt32 i = 0; i < sScheduledMutationObservers->Count(); ++i) {
|
||||
if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i])
|
||||
->mId > mId) {
|
||||
sScheduledMutationObservers->InsertObjectAt(this, i);
|
||||
didInsert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!didInsert) {
|
||||
sScheduledMutationObservers->AppendObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationObserver::Observe(nsIDOMNode* aTarget,
|
||||
const JS::Value& aOptions,
|
||||
JSContext* aCx)
|
||||
{
|
||||
nsCOMPtr<nsINode> target = do_QueryInterface(aTarget);
|
||||
NS_ENSURE_STATE(target);
|
||||
|
||||
mozilla::dom::MutationObserverInit d;
|
||||
nsresult rv = d.Init(aCx, &aOptions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(d.childList || d.attributes || d.characterData,
|
||||
NS_ERROR_DOM_SYNTAX_ERR);
|
||||
NS_ENSURE_TRUE(!d.attributeOldValue || d.attributes,
|
||||
NS_ERROR_DOM_SYNTAX_ERR);
|
||||
NS_ENSURE_TRUE(!d.characterDataOldValue || d.characterData,
|
||||
NS_ERROR_DOM_SYNTAX_ERR);
|
||||
|
||||
nsCOMArray<nsIAtom> filters;
|
||||
bool allAttrs = true;
|
||||
if (!d.attributeFilter.isUndefined()) {
|
||||
allAttrs = false;
|
||||
rv = nsContentUtils::JSArrayToAtomArray(aCx, d.attributeFilter, filters);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(filters.Count() == 0 || d.attributes,
|
||||
NS_ERROR_DOM_SYNTAX_ERR);
|
||||
}
|
||||
|
||||
nsMutationReceiver* r = GetReceiverFor(target, true);
|
||||
r->SetChildList(d.childList);
|
||||
r->SetAttributes(d.attributes);
|
||||
r->SetCharacterData(d.characterData);
|
||||
r->SetSubtree(d.subtree);
|
||||
r->SetAttributeOldValue(d.attributeOldValue);
|
||||
r->SetCharacterDataOldValue(d.characterDataOldValue);
|
||||
r->SetAttributeFilter(filters);
|
||||
r->SetAllAttributes(allAttrs);
|
||||
r->RemoveClones();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationObserver::Disconnect()
|
||||
{
|
||||
for (PRInt32 i = 0; i < mReceivers.Count(); ++i) {
|
||||
mReceivers[i]->Disconnect();
|
||||
}
|
||||
mReceivers.Clear();
|
||||
for (PRUint32 i = 0; i < mCurrentMutations.Length(); ++i) {
|
||||
nsDOMMutationRecord* r = mCurrentMutations[i];
|
||||
}
|
||||
mCurrentMutations.Clear();
|
||||
mPendingMutations.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMutationObserver::Initialize(nsISupports* aOwner, JSContext* cx,
|
||||
JSObject* obj, PRUint32 argc, jsval* argv)
|
||||
{
|
||||
mOwner = do_QueryInterface(aOwner);
|
||||
if (!mOwner) {
|
||||
NS_WARNING("Unexpected nsIJSNativeInitializer owner");
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
|
||||
NS_ENSURE_STATE(sgo);
|
||||
mScriptContext = sgo->GetContext();
|
||||
NS_ENSURE_STATE(mScriptContext);
|
||||
|
||||
NS_ENSURE_STATE(argc >= 1);
|
||||
NS_ENSURE_STATE(!JSVAL_IS_PRIMITIVE(argv[0]));
|
||||
|
||||
nsCOMPtr<nsISupports> tmp;
|
||||
nsContentUtils::XPConnect()->WrapJS(cx, JSVAL_TO_OBJECT(argv[0]),
|
||||
NS_GET_IID(nsIMutationObserverCallback),
|
||||
getter_AddRefs(tmp));
|
||||
mCallback = do_QueryInterface(tmp);
|
||||
NS_ENSURE_STATE(mCallback);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
TransientReceiverTraverser(nsISupports* aKey,
|
||||
nsCOMArray<nsMutationReceiver>* aArray,
|
||||
void* aUserArg)
|
||||
{
|
||||
PRInt32 count = aArray->Count();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
nsMutationReceiver* r = aArray->ObjectAt(i);
|
||||
nsMutationReceiver* p = r->GetParent();
|
||||
if (p) {
|
||||
p->RemoveClones();
|
||||
}
|
||||
r->Disconnect();
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::HandleMutation()
|
||||
{
|
||||
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
|
||||
NS_ASSERTION(mCurrentMutations.Length() == 0,
|
||||
"Still generating MutationRecords?");
|
||||
|
||||
mWaitingForRun = false;
|
||||
|
||||
for (PRInt32 i = 0; i < mReceivers.Count(); ++i) {
|
||||
mReceivers[i]->RemoveClones();
|
||||
}
|
||||
mTransientReceivers.Clear();
|
||||
|
||||
nsPIDOMWindow* outer = mOwner->GetOuterWindow();
|
||||
if (!mPendingMutations.Count() || !outer ||
|
||||
outer->GetCurrentInnerWindow() != mOwner) {
|
||||
mPendingMutations.Clear();
|
||||
return;
|
||||
}
|
||||
nsCxPusher pusher;
|
||||
nsCOMPtr<nsIDOMEventTarget> et = do_QueryInterface(mOwner);
|
||||
if (!mCallback || !pusher.Push(et)) {
|
||||
mPendingMutations.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
PRInt32 len = mPendingMutations.Count();
|
||||
nsTArray<nsIDOMMutationRecord*> mods(len);
|
||||
for (PRInt32 i = 0; i < len; ++i) {
|
||||
mods.AppendElement(mPendingMutations[i]);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> mutations =
|
||||
do_CreateInstance("@mozilla.org/variant;1");
|
||||
mutations->SetAsArray(nsIDataType::VTYPE_INTERFACE,
|
||||
&NS_GET_IID(nsIDOMMutationRecord),
|
||||
mods.Length(),
|
||||
const_cast<void*>(
|
||||
static_cast<const void*>(mods.Elements())));
|
||||
mPendingMutations.Clear();
|
||||
nsAutoMicroTask mt;
|
||||
sCurrentObserver = this; // For 'this' handling.
|
||||
mCallback->HandleMutations(mutations, this);
|
||||
sCurrentObserver = nsnull;
|
||||
}
|
||||
|
||||
class AsyncMutationHandler : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsDOMMutationObserver::HandleMutations();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::HandleMutationsInternal()
|
||||
{
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
|
||||
return;
|
||||
}
|
||||
static nsRefPtr<nsDOMMutationObserver> sCurrentObserver;
|
||||
if (sCurrentObserver && !sCurrentObserver->Suppressed()) {
|
||||
// In normal cases sScheduledMutationObservers will be handled
|
||||
// after previous mutations are handled. But in case some
|
||||
// callback calls a sync API, which spins the eventloop, we need to still
|
||||
// process other mutations happening during that sync call.
|
||||
// This does *not* catch all cases, but should work for stuff running
|
||||
// in separate tabs.
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIDOMMozMutationObserver>* suppressedObservers = nsnull;
|
||||
|
||||
while (sScheduledMutationObservers) {
|
||||
nsCOMArray<nsIDOMMozMutationObserver>* observers = sScheduledMutationObservers;
|
||||
sScheduledMutationObservers = nsnull;
|
||||
for (PRInt32 i = 0; i < observers->Count(); ++i) {
|
||||
sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]);
|
||||
if (!sCurrentObserver->Suppressed()) {
|
||||
sCurrentObserver->HandleMutation();
|
||||
} else {
|
||||
if (!suppressedObservers) {
|
||||
suppressedObservers = new nsCOMArray<nsIDOMMozMutationObserver>;
|
||||
}
|
||||
if (suppressedObservers->IndexOf(sCurrentObserver) < 0) {
|
||||
suppressedObservers->AppendObject(sCurrentObserver);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete observers;
|
||||
}
|
||||
|
||||
if (suppressedObservers) {
|
||||
for (PRInt32 i = 0; i < suppressedObservers->Count(); ++i) {
|
||||
static_cast<nsDOMMutationObserver*>(suppressedObservers->ObjectAt(i))->
|
||||
RescheduleForRun();
|
||||
}
|
||||
delete suppressedObservers;
|
||||
suppressedObservers = nsnull;
|
||||
}
|
||||
sCurrentObserver = nsnull;
|
||||
}
|
||||
|
||||
nsDOMMutationRecord*
|
||||
nsDOMMutationObserver::CurrentRecord(const nsAString& aType)
|
||||
{
|
||||
NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
|
||||
|
||||
while (mCurrentMutations.Length() < sMutationLevel) {
|
||||
mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nsnull));
|
||||
}
|
||||
|
||||
PRUint32 last = sMutationLevel - 1;
|
||||
if (!mCurrentMutations[last]) {
|
||||
nsDOMMutationRecord* r = new nsDOMMutationRecord(aType);
|
||||
mCurrentMutations[last] = r;
|
||||
mPendingMutations.AppendObject(r);
|
||||
ScheduleForRun();
|
||||
}
|
||||
|
||||
NS_ASSERTION(mCurrentMutations[last]->mType.Equals(aType),
|
||||
"Unexpected MutationRecord type!");
|
||||
|
||||
return mCurrentMutations[last];
|
||||
}
|
||||
|
||||
nsDOMMutationObserver::~nsDOMMutationObserver()
|
||||
{
|
||||
for (PRInt32 i = 0; i < mReceivers.Count(); ++i) {
|
||||
mReceivers[i]->RemoveClones();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::EnterMutationHandling()
|
||||
{
|
||||
++sMutationLevel;
|
||||
}
|
||||
|
||||
// Leave the current mutation level (there can be several levels if in case
|
||||
// of nested calls to the nsIMutationObserver methods).
|
||||
// The most recent mutation record is removed from mCurrentMutations, so
|
||||
// that is doesn't get modified anymore by receivers.
|
||||
void
|
||||
nsDOMMutationObserver::LeaveMutationHandling()
|
||||
{
|
||||
if (sCurrentlyHandlingObservers &&
|
||||
sCurrentlyHandlingObservers->Length() == sMutationLevel) {
|
||||
nsCOMArray<nsIDOMMozMutationObserver>& obs =
|
||||
sCurrentlyHandlingObservers->ElementAt(sMutationLevel - 1);
|
||||
for (PRInt32 i = 0; i < obs.Count(); ++i) {
|
||||
nsDOMMutationObserver* o =
|
||||
static_cast<nsDOMMutationObserver*>(obs[i]);
|
||||
if (o->mCurrentMutations.Length() == sMutationLevel) {
|
||||
// It is already in pending mutations.
|
||||
nsDOMMutationRecord* r = o->mCurrentMutations[sMutationLevel - 1];
|
||||
o->mCurrentMutations.RemoveElementAt(sMutationLevel - 1);
|
||||
}
|
||||
}
|
||||
sCurrentlyHandlingObservers->RemoveElementAt(sMutationLevel - 1);
|
||||
}
|
||||
--sMutationLevel;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver)
|
||||
{
|
||||
NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
|
||||
|
||||
if (!sCurrentlyHandlingObservers) {
|
||||
sCurrentlyHandlingObservers =
|
||||
new nsAutoTArray<nsCOMArray<nsIDOMMozMutationObserver>, 4>;
|
||||
}
|
||||
|
||||
while (sCurrentlyHandlingObservers->Length() < sMutationLevel) {
|
||||
sCurrentlyHandlingObservers->InsertElementAt(
|
||||
sCurrentlyHandlingObservers->Length());
|
||||
}
|
||||
|
||||
PRUint32 last = sMutationLevel - 1;
|
||||
if (sCurrentlyHandlingObservers->ElementAt(last).IndexOf(aObserver) < 0) {
|
||||
sCurrentlyHandlingObservers->ElementAt(last).AppendObject(aObserver);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::Shutdown()
|
||||
{
|
||||
delete sCurrentlyHandlingObservers;
|
||||
sCurrentlyHandlingObservers = nsnull;
|
||||
delete sScheduledMutationObservers;
|
||||
sScheduledMutationObservers = nsnull;
|
||||
}
|
||||
|
||||
nsAutoMutationBatch*
|
||||
nsAutoMutationBatch::sCurrentBatch = nsnull;
|
||||
|
||||
void
|
||||
nsAutoMutationBatch::Done()
|
||||
{
|
||||
if (sCurrentBatch != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
sCurrentBatch = mPreviousBatch;
|
||||
if (mObservers.IsEmpty()) {
|
||||
nsDOMMutationObserver::LeaveMutationHandling();
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
PRUint32 len = mObservers.Length();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
nsDOMMutationObserver* ob = mObservers[i].mObserver;
|
||||
bool wantsChildList = mObservers[i].mWantsChildList;
|
||||
|
||||
nsRefPtr<nsSimpleContentList> removedList;
|
||||
if (wantsChildList) {
|
||||
removedList = new nsSimpleContentList(mBatchTarget);
|
||||
}
|
||||
|
||||
nsTArray<nsMutationReceiver*> allObservers;
|
||||
ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers);
|
||||
|
||||
PRInt32 j = mFromFirstToLast ? 0 : mRemovedNodes.Length() - 1;
|
||||
PRInt32 end = mFromFirstToLast ? mRemovedNodes.Length() : -1;
|
||||
for (; j != end; mFromFirstToLast ? ++j : --j) {
|
||||
nsCOMPtr<nsIContent> removed = mRemovedNodes[j];
|
||||
if (removedList) {
|
||||
removedList->AppendElement(removed);
|
||||
}
|
||||
|
||||
if (allObservers.Length()) {
|
||||
nsCOMArray<nsMutationReceiver>* transientReceivers = nsnull;
|
||||
ob->mTransientReceivers.Get(removed, &transientReceivers);
|
||||
if (!transientReceivers) {
|
||||
transientReceivers = new nsCOMArray<nsMutationReceiver>();
|
||||
ob->mTransientReceivers.Put(removed, transientReceivers);
|
||||
}
|
||||
for (PRUint32 k = 0; k < allObservers.Length(); ++k) {
|
||||
nsMutationReceiver* r = allObservers[k];
|
||||
nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r;
|
||||
if (ob->GetReceiverFor(removed, false) != orig) {
|
||||
// Make sure the elements which are removed from the
|
||||
// subtree are kept in the same observation set.
|
||||
transientReceivers->AppendObject(new nsMutationReceiver(removed, orig));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wantsChildList && (mRemovedNodes.Length() || mAddedNodes.Length())) {
|
||||
nsRefPtr<nsSimpleContentList> addedList =
|
||||
new nsSimpleContentList(mBatchTarget);
|
||||
for (PRUint32 i = 0; i < mAddedNodes.Length(); ++i) {
|
||||
addedList->AppendElement(mAddedNodes[i]);
|
||||
}
|
||||
nsDOMMutationRecord* m =
|
||||
new nsDOMMutationRecord(NS_LITERAL_STRING("childList"));
|
||||
ob->mPendingMutations.AppendObject(m);
|
||||
m->mTarget = mBatchTarget;
|
||||
m->mRemovedNodes = removedList;
|
||||
m->mAddedNodes = addedList;
|
||||
m->mPreviousSibling = mPrevSibling;
|
||||
m->mNextSibling = mNextSibling;
|
||||
ob->ScheduleForRun();
|
||||
}
|
||||
}
|
||||
nsDOMMutationObserver::LeaveMutationHandling();
|
||||
}
|
533
content/base/src/nsDOMMutationObserver.h
Normal file
533
content/base/src/nsDOMMutationObserver.h
Normal file
@ -0,0 +1,533 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMMutationObserver_h
|
||||
#define nsDOMMutationObserver_h
|
||||
|
||||
#include "nsIDOMMutationObserver.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIJSNativeInitializer.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsStubMutationObserver.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIVariant.h"
|
||||
#include "nsContentList.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsNodeUtils.h"
|
||||
|
||||
class nsDOMMutationObserver;
|
||||
|
||||
class nsDOMMutationRecord : public nsIDOMMutationRecord
|
||||
{
|
||||
public:
|
||||
nsDOMMutationRecord(const nsAString& aType) : mType(aType)
|
||||
{
|
||||
mAttrName.SetIsVoid(PR_TRUE);
|
||||
mAttrNamespace.SetIsVoid(PR_TRUE);
|
||||
mPrevValue.SetIsVoid(PR_TRUE);
|
||||
}
|
||||
virtual ~nsDOMMutationRecord() {}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMMutationRecord)
|
||||
NS_DECL_NSIDOMMUTATIONRECORD
|
||||
|
||||
nsCOMPtr<nsINode> mTarget;
|
||||
nsString mType;
|
||||
nsString mAttrName;
|
||||
nsString mAttrNamespace;
|
||||
nsString mPrevValue;
|
||||
nsRefPtr<nsSimpleContentList> mAddedNodes;
|
||||
nsRefPtr<nsSimpleContentList> mRemovedNodes;
|
||||
nsCOMPtr<nsINode> mPreviousSibling;
|
||||
nsCOMPtr<nsINode> mNextSibling;
|
||||
};
|
||||
|
||||
// Base class just prevents direct access to
|
||||
// members to make sure we go through getters/setters.
|
||||
class nsMutationReceiverBase : public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
virtual ~nsMutationReceiverBase() { }
|
||||
|
||||
nsDOMMutationObserver* Observer();
|
||||
nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
|
||||
nsINode* RegisterTarget() { return mRegisterTarget; }
|
||||
|
||||
bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
|
||||
void SetSubtree(bool aSubtree)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mSubtree = aSubtree;
|
||||
}
|
||||
|
||||
bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
|
||||
void SetChildList(bool aChildList)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mChildList = aChildList;
|
||||
}
|
||||
|
||||
bool CharacterData()
|
||||
{
|
||||
return mParent ? mParent->CharacterData() : mCharacterData;
|
||||
}
|
||||
void SetCharacterData(bool aCharacterData)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mCharacterData = aCharacterData;
|
||||
}
|
||||
|
||||
bool CharacterDataOldValue()
|
||||
{
|
||||
return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
|
||||
}
|
||||
void SetCharacterDataOldValue(bool aOldValue)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mCharacterDataOldValue = aOldValue;
|
||||
}
|
||||
|
||||
bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
|
||||
void SetAttributes(bool aAttributes)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mAttributes = aAttributes;
|
||||
}
|
||||
|
||||
bool AllAttributes()
|
||||
{
|
||||
return mParent ? mParent->AllAttributes()
|
||||
: mAllAttributes;
|
||||
}
|
||||
void SetAllAttributes(bool aAll)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mAllAttributes = aAll;
|
||||
}
|
||||
|
||||
bool AttributeOldValue() {
|
||||
return mParent ? mParent->AttributeOldValue()
|
||||
: mAttributeOldValue;
|
||||
}
|
||||
void SetAttributeOldValue(bool aOldValue)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mAttributeOldValue = aOldValue;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
|
||||
void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
|
||||
{
|
||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
||||
mAttributeFilter.Clear();
|
||||
mAttributeFilter.AppendObjects(aFilter);
|
||||
}
|
||||
|
||||
void AddClone(nsMutationReceiverBase* aClone)
|
||||
{
|
||||
mTransientReceivers.AppendObject(aClone);
|
||||
}
|
||||
|
||||
void RemoveClone(nsMutationReceiverBase* aClone)
|
||||
{
|
||||
mTransientReceivers.RemoveObject(aClone);
|
||||
}
|
||||
|
||||
protected:
|
||||
nsMutationReceiverBase(nsINode* aTarget, nsIDOMMozMutationObserver* aObserver)
|
||||
: mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
|
||||
{
|
||||
mRegisterTarget->AddMutationObserver(this);
|
||||
mRegisterTarget->SetMayHaveDOMMutationObserver();
|
||||
mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
|
||||
}
|
||||
|
||||
nsMutationReceiverBase(nsINode* aRegisterTarget,
|
||||
nsMutationReceiverBase* aParent)
|
||||
: mObserver(nsnull), mParent(aParent), mRegisterTarget(aRegisterTarget)
|
||||
{
|
||||
NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
|
||||
mRegisterTarget->AddMutationObserver(this);
|
||||
mRegisterTarget->SetMayHaveDOMMutationObserver();
|
||||
mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
|
||||
}
|
||||
|
||||
bool ObservesAttr(mozilla::dom::Element* aElement,
|
||||
PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttr)
|
||||
{
|
||||
if (mParent) {
|
||||
return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
|
||||
}
|
||||
if (!Attributes() || (!Subtree() && aElement != Target())) {
|
||||
return false;
|
||||
}
|
||||
if (AllAttributes()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aNameSpaceID != kNameSpaceID_None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMArray<nsIAtom>& filters = AttributeFilter();
|
||||
for (PRInt32 i = 0; i < filters.Count(); ++i) {
|
||||
if (filters[i] == aAttr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The target for the MutationObserver.observe() method.
|
||||
nsINode* mTarget;
|
||||
nsIDOMMozMutationObserver* mObserver;
|
||||
nsRefPtr<nsMutationReceiverBase> mParent; // Cleared after microtask.
|
||||
// The node to which Gecko-internal nsIMutationObserver was registered to.
|
||||
// This is different than mTarget when dealing with transient observers.
|
||||
nsINode* mRegisterTarget;
|
||||
nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
|
||||
|
||||
private:
|
||||
bool mSubtree;
|
||||
bool mChildList;
|
||||
bool mCharacterData;
|
||||
bool mCharacterDataOldValue;
|
||||
bool mAttributes;
|
||||
bool mAllAttributes;
|
||||
bool mAttributeOldValue;
|
||||
nsCOMArray<nsIAtom> mAttributeFilter;
|
||||
};
|
||||
|
||||
|
||||
#define NS_MUTATION_OBSERVER_IID \
|
||||
{ 0xe628f313, 0x8129, 0x4f90, \
|
||||
{ 0x8e, 0xc3, 0x85, 0xe8, 0x28, 0x22, 0xe7, 0xab } }
|
||||
|
||||
class nsMutationReceiver : public nsMutationReceiverBase
|
||||
{
|
||||
public:
|
||||
nsMutationReceiver(nsINode* aTarget, nsIDOMMozMutationObserver* aObserver)
|
||||
: nsMutationReceiverBase(aTarget, aObserver)
|
||||
{
|
||||
mTarget->BindObject(aObserver);
|
||||
}
|
||||
|
||||
nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
|
||||
: nsMutationReceiverBase(aRegisterTarget, aParent)
|
||||
{
|
||||
NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
|
||||
"Shouldn't create deep observer hierarchies!");
|
||||
aParent->AddClone(this);
|
||||
}
|
||||
|
||||
virtual ~nsMutationReceiver() { Disconnect(); }
|
||||
|
||||
nsMutationReceiver* GetParent()
|
||||
{
|
||||
return static_cast<nsMutationReceiver*>(mParent.get());
|
||||
}
|
||||
|
||||
void RemoveClones()
|
||||
{
|
||||
for (PRInt32 i = 0; i < mTransientReceivers.Count(); ++i) {
|
||||
nsMutationReceiver* r =
|
||||
static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
|
||||
r->Disconnect();
|
||||
}
|
||||
mTransientReceivers.Clear();
|
||||
}
|
||||
|
||||
void DisconnectTransientReceivers()
|
||||
{
|
||||
if (mRegisterTarget) {
|
||||
mRegisterTarget->RemoveMutationObserver(this);
|
||||
mRegisterTarget = nsnull;
|
||||
}
|
||||
|
||||
mParent = nsnull;
|
||||
}
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
if (mRegisterTarget) {
|
||||
mRegisterTarget->RemoveMutationObserver(this);
|
||||
mRegisterTarget = nsnull;
|
||||
}
|
||||
if (mTarget && mObserver) {
|
||||
mTarget->UnbindObject(mObserver);
|
||||
}
|
||||
|
||||
mParent = nsnull;
|
||||
mTarget = nsnull;
|
||||
mObserver = nsnull;
|
||||
RemoveClones();
|
||||
}
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMUTATION_OBSERVER_IID)
|
||||
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsMutationReceiver)
|
||||
|
||||
virtual void AttributeWillChange(nsIDocument* aDocument,
|
||||
mozilla::dom::Element* aElement,
|
||||
PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
PRInt32 aModType);
|
||||
virtual void CharacterDataWillChange(nsIDocument *aDocument,
|
||||
nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo);
|
||||
virtual void ContentAppended(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent,
|
||||
PRInt32 aNewIndexInContainer);
|
||||
virtual void ContentInserted(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer);
|
||||
virtual void ContentRemoved(nsIDocument *aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer,
|
||||
nsIContent* aPreviousSibling);
|
||||
|
||||
virtual void NodeWillBeDestroyed(const nsINode *aNode)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsMutationReceiver, NS_MUTATION_OBSERVER_IID)
|
||||
|
||||
class nsDOMMutationObserver : public nsIDOMMozMutationObserver,
|
||||
public nsIJSNativeInitializer
|
||||
{
|
||||
public:
|
||||
nsDOMMutationObserver() : mWaitingForRun(false), mId(++sCount)
|
||||
{
|
||||
mTransientReceivers.Init();
|
||||
}
|
||||
virtual ~nsDOMMutationObserver();
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMMutationObserver,
|
||||
nsIDOMMozMutationObserver)
|
||||
NS_DECL_NSIDOMMOZMUTATIONOBSERVER
|
||||
|
||||
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
|
||||
PRUint32 argc, jsval* argv);
|
||||
|
||||
void HandleMutation();
|
||||
|
||||
// static methods
|
||||
static void HandleMutations()
|
||||
{
|
||||
if (sScheduledMutationObservers) {
|
||||
HandleMutationsInternal();
|
||||
}
|
||||
}
|
||||
|
||||
static void EnterMutationHandling();
|
||||
static void LeaveMutationHandling();
|
||||
|
||||
static nsIDOMMozMutationObserver* CurrentObserver()
|
||||
{
|
||||
return sCurrentObserver;
|
||||
}
|
||||
|
||||
static void Shutdown();
|
||||
protected:
|
||||
friend class nsMutationReceiver;
|
||||
friend class nsAutoMutationBatch;
|
||||
nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate);
|
||||
void GetAllSubtreeObserversFor(nsINode* aNode,
|
||||
nsTArray<nsMutationReceiver*>& aObservers);
|
||||
void ScheduleForRun();
|
||||
void RescheduleForRun();
|
||||
|
||||
nsDOMMutationRecord* CurrentRecord(const nsAString& aType);
|
||||
bool HasCurrentRecord(const nsAString& aType);
|
||||
|
||||
bool Suppressed()
|
||||
{
|
||||
if (mOwner) {
|
||||
nsCOMPtr<nsIDocument> d = do_QueryInterface(mOwner->GetExtantDocument());
|
||||
return d && d->IsInSyncOperation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void HandleMutationsInternal();
|
||||
|
||||
static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
|
||||
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsPIDOMWindow> mOwner;
|
||||
|
||||
nsCOMArray<nsMutationReceiver> mReceivers;
|
||||
nsClassHashtable<nsISupportsHashKey,
|
||||
nsCOMArray<nsMutationReceiver> > mTransientReceivers;
|
||||
// MutationRecords which are being constructed.
|
||||
nsAutoTArray<nsDOMMutationRecord*, 4> mCurrentMutations;
|
||||
// MutationRecords which will be handed to the callback at the end of
|
||||
// the microtask.
|
||||
nsCOMArray<nsDOMMutationRecord> mPendingMutations;
|
||||
nsCOMPtr<nsIMutationObserverCallback> mCallback;
|
||||
|
||||
bool mWaitingForRun;
|
||||
|
||||
PRUint64 mId;
|
||||
|
||||
static PRUint64 sCount;
|
||||
static nsCOMArray<nsIDOMMozMutationObserver>* sScheduledMutationObservers;
|
||||
static nsIDOMMozMutationObserver* sCurrentObserver;
|
||||
|
||||
static PRUint32 sMutationLevel;
|
||||
static nsAutoTArray<nsCOMArray<nsIDOMMozMutationObserver>, 4>*
|
||||
sCurrentlyHandlingObservers;
|
||||
};
|
||||
|
||||
class nsAutoMutationBatch
|
||||
{
|
||||
public:
|
||||
nsAutoMutationBatch()
|
||||
: mPreviousBatch(nsnull), mBatchTarget(nsnull), mRemovalDone(false),
|
||||
mFromFirstToLast(false), mAllowNestedBatches(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
|
||||
bool aAllowNestedBatches)
|
||||
: mPreviousBatch(nsnull), mBatchTarget(nsnull), mRemovalDone(false),
|
||||
mFromFirstToLast(false), mAllowNestedBatches(false)
|
||||
{
|
||||
Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
|
||||
}
|
||||
|
||||
void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
|
||||
{
|
||||
if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
|
||||
if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
|
||||
return;
|
||||
}
|
||||
mBatchTarget = aTarget;
|
||||
mFromFirstToLast = aFromFirstToLast;
|
||||
mAllowNestedBatches = aAllowNestedBatches;
|
||||
mPreviousBatch = sCurrentBatch;
|
||||
sCurrentBatch = this;
|
||||
nsDOMMutationObserver::EnterMutationHandling();
|
||||
}
|
||||
}
|
||||
|
||||
void RemovalDone() { mRemovalDone = true; }
|
||||
static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
|
||||
|
||||
void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
|
||||
void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
|
||||
|
||||
void Done();
|
||||
|
||||
~nsAutoMutationBatch() { NodesAdded(); }
|
||||
|
||||
static bool IsBatching()
|
||||
{
|
||||
return !!sCurrentBatch;
|
||||
}
|
||||
|
||||
static nsAutoMutationBatch* GetCurrentBatch()
|
||||
{
|
||||
return sCurrentBatch;
|
||||
}
|
||||
|
||||
static void UpdateObserver(nsDOMMutationObserver* aObserver,
|
||||
bool aWantsChildList)
|
||||
{
|
||||
PRUint32 l = sCurrentBatch->mObservers.Length();
|
||||
for (PRUint32 i = 0; i < l; ++i) {
|
||||
if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
|
||||
if (aWantsChildList) {
|
||||
sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
|
||||
bo->mObserver = aObserver;
|
||||
bo->mWantsChildList = aWantsChildList;
|
||||
}
|
||||
|
||||
|
||||
static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
|
||||
|
||||
// Mutation receivers notify the batch about removed child nodes.
|
||||
static void NodeRemoved(nsIContent* aChild)
|
||||
{
|
||||
if (IsBatching() && !sCurrentBatch->mRemovalDone) {
|
||||
PRUint32 len = sCurrentBatch->mRemovedNodes.Length();
|
||||
if (!len ||
|
||||
sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
|
||||
sCurrentBatch->mRemovedNodes.AppendElement(aChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called after new child nodes have been added to the batch target.
|
||||
void NodesAdded()
|
||||
{
|
||||
if (sCurrentBatch != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent* c =
|
||||
mPrevSibling ? mPrevSibling->GetNextSibling() :
|
||||
mBatchTarget->GetFirstChild();
|
||||
for (; c != mNextSibling; c = c->GetNextSibling()) {
|
||||
mAddedNodes.AppendElement(c);
|
||||
}
|
||||
Done();
|
||||
}
|
||||
|
||||
private:
|
||||
struct BatchObserver
|
||||
{
|
||||
nsDOMMutationObserver* mObserver;
|
||||
bool mWantsChildList;
|
||||
};
|
||||
|
||||
static nsAutoMutationBatch* sCurrentBatch;
|
||||
nsAutoMutationBatch* mPreviousBatch;
|
||||
nsAutoTArray<BatchObserver, 2> mObservers;
|
||||
nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
|
||||
nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
|
||||
nsINode* mBatchTarget;
|
||||
bool mRemovalDone;
|
||||
bool mFromFirstToLast;
|
||||
bool mAllowNestedBatches;
|
||||
nsCOMPtr<nsINode> mPrevSibling;
|
||||
nsCOMPtr<nsINode> mNextSibling;
|
||||
};
|
||||
|
||||
inline
|
||||
nsDOMMutationObserver*
|
||||
nsMutationReceiverBase::Observer()
|
||||
{
|
||||
return mParent ?
|
||||
mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
|
||||
}
|
||||
|
||||
#define NS_DOMMUTATIONOBSERVER_CID \
|
||||
{ /* b66b9490-52f7-4f2a-b998-dbb1d59bc13e */ \
|
||||
0xb66b9490, 0x52f7, 0x4f2a, \
|
||||
{ 0xb9, 0x98, 0xdb, 0xb1, 0xd5, 0x9b, 0xc1, 0x3e } }
|
||||
|
||||
#define NS_DOMMUTATIONOBSERVER_CONTRACTID \
|
||||
"@mozilla.org/dommutationobserver;1"
|
||||
|
||||
#endif
|
@ -9269,3 +9269,42 @@ nsDocument::DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
|
||||
// is worthwhile:
|
||||
// - many!
|
||||
}
|
||||
|
||||
bool
|
||||
MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
|
||||
{
|
||||
nsCOMArray<nsIDocument>* documents =
|
||||
static_cast<nsCOMArray<nsIDocument>*>(aData);
|
||||
if (aDoc) {
|
||||
aDoc->SetIsInSyncOperation(true);
|
||||
documents->AppendObject(aDoc);
|
||||
aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
|
||||
{
|
||||
mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
|
||||
nsContentUtils::SetMicroTaskLevel(0);
|
||||
if (aDoc) {
|
||||
nsPIDOMWindow* win = aDoc->GetWindow();
|
||||
if (win) {
|
||||
nsCOMPtr<nsIDOMWindow> topWindow;
|
||||
win->GetTop(getter_AddRefs(topWindow));
|
||||
nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
|
||||
if (top) {
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(top->GetExtantDocument());
|
||||
MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoSyncOperation::~nsAutoSyncOperation()
|
||||
{
|
||||
for (PRInt32 i = 0; i < mDocuments.Count(); ++i) {
|
||||
mDocuments[i]->SetIsInSyncOperation(false);
|
||||
}
|
||||
nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@
|
||||
|
||||
#include "nsCSSParser.h"
|
||||
#include "prprf.h"
|
||||
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsSVGFeatures.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsCycleCollector.h"
|
||||
@ -1261,6 +1261,13 @@ nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
|
||||
|
||||
if (tmp->HasProperties()) {
|
||||
nsNodeUtils::TraverseUserData(tmp, cb);
|
||||
nsCOMArray<nsISupports>* objects =
|
||||
static_cast<nsCOMArray<nsISupports>*>(tmp->GetProperty(nsGkAtoms::keepobjectsalive));
|
||||
if (objects) {
|
||||
for (PRInt32 i = 0; i < objects->Count(); ++i) {
|
||||
cb.NoteXPCOMChild(objects->ObjectAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
|
||||
@ -1290,6 +1297,7 @@ nsINode::Unlink(nsINode *tmp)
|
||||
|
||||
if (tmp->HasProperties()) {
|
||||
nsNodeUtils::UnlinkUserData(tmp);
|
||||
tmp->DeleteProperty(nsGkAtoms::keepobjectsalive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4270,8 +4278,10 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
|
||||
}
|
||||
|
||||
nsAutoMutationBatch mb;
|
||||
// If we're replacing
|
||||
if (aReplace) {
|
||||
mb.Init(this, true, true);
|
||||
RemoveChildAt(insPos, true);
|
||||
}
|
||||
|
||||
@ -4292,7 +4302,13 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
nsAutoMutationBatch mb(oldParent, true, true);
|
||||
oldParent->RemoveChildAt(removeIndex, true);
|
||||
if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
|
||||
mb.RemovalDone();
|
||||
mb.SetPrevSibling(oldParent->GetChildAt(removeIndex - 1));
|
||||
mb.SetNextSibling(oldParent->GetChildAt(removeIndex));
|
||||
}
|
||||
|
||||
// Adjust insert index if the node we ripped out was a sibling
|
||||
// of the node we're inserting before
|
||||
@ -4337,8 +4353,21 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
}
|
||||
|
||||
// Remove the children from the fragment.
|
||||
for (PRUint32 i = count; i > 0;) {
|
||||
newContent->RemoveChildAt(--i, true);
|
||||
{
|
||||
nsAutoMutationBatch mb(newContent, false, true);
|
||||
for (PRUint32 i = count; i > 0;) {
|
||||
newContent->RemoveChildAt(--i, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!aReplace) {
|
||||
mb.Init(this, true, true);
|
||||
}
|
||||
nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch();
|
||||
if (mutationBatch) {
|
||||
mutationBatch->RemovalDone();
|
||||
mutationBatch->SetPrevSibling(GetChildAt(insPos - 1));
|
||||
mutationBatch->SetNextSibling(GetChildAt(insPos));
|
||||
}
|
||||
|
||||
bool appending =
|
||||
@ -4363,10 +4392,17 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
}
|
||||
}
|
||||
|
||||
if (mutationBatch && !appending) {
|
||||
mutationBatch->NodesAdded();
|
||||
}
|
||||
|
||||
// Notify and fire mutation events when appending
|
||||
if (appending) {
|
||||
nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
|
||||
firstInsertedContent, firstInsPos);
|
||||
if (mutationBatch) {
|
||||
mutationBatch->NodesAdded();
|
||||
}
|
||||
// Optimize for the case when there are no listeners
|
||||
if (nsContentUtils::
|
||||
HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
|
||||
@ -4382,6 +4418,11 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
// wrapper is not the wrapper for their ownerDocument (XUL elements,
|
||||
// form controls, ...). Also applies in the fragment code above.
|
||||
|
||||
if (nsAutoMutationBatch::GetCurrentBatch() == &mb) {
|
||||
mb.RemovalDone();
|
||||
mb.SetPrevSibling(GetChildAt(insPos - 1));
|
||||
mb.SetNextSibling(GetChildAt(insPos));
|
||||
}
|
||||
res = InsertChildAt(newContent, insPos, true);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
@ -5193,7 +5234,7 @@ nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID,
|
||||
}
|
||||
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
|
||||
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
|
||||
return true;
|
||||
return !OwnerDoc()->MayHaveDOMMutationObservers();
|
||||
}
|
||||
modification = true;
|
||||
}
|
||||
@ -6055,6 +6096,37 @@ nsGenericElement::GetLinkTarget(nsAString& aTarget)
|
||||
aTarget.Truncate();
|
||||
}
|
||||
|
||||
static void
|
||||
nsCOMArrayDeleter(void* aObject, nsIAtom* aPropertyName,
|
||||
void* aPropertyValue, void* aData)
|
||||
{
|
||||
nsCOMArray<nsISupports>* objects =
|
||||
static_cast<nsCOMArray<nsISupports>*>(aPropertyValue);
|
||||
delete objects;
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::BindObject(nsISupports* aObject)
|
||||
{
|
||||
nsCOMArray<nsISupports>* objects =
|
||||
static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
|
||||
if (!objects) {
|
||||
objects = new nsCOMArray<nsISupports>();
|
||||
SetProperty(nsGkAtoms::keepobjectsalive, objects, nsCOMArrayDeleter, true);
|
||||
}
|
||||
objects->AppendObject(aObject);
|
||||
}
|
||||
|
||||
void
|
||||
nsINode::UnbindObject(nsISupports* aObject)
|
||||
{
|
||||
nsCOMArray<nsISupports>* objects =
|
||||
static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive));
|
||||
if (objects) {
|
||||
objects->RemoveObject(aObject);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The aPresContext pointer is NOT addrefed.
|
||||
// *aSelectorList might be null even if NS_OK is returned; this
|
||||
// happens when all the selectors were pseudo-element selectors.
|
||||
|
@ -491,6 +491,7 @@ GK_ATOM(itemtype, "itemtype")
|
||||
GK_ATOM(kbd, "kbd")
|
||||
GK_ATOM(noautofocus, "noautofocus")
|
||||
GK_ATOM(keepcurrentinview, "keepcurrentinview")
|
||||
GK_ATOM(keepobjectsalive, "keepobjectsalive")
|
||||
GK_ATOM(key, "key")
|
||||
GK_ATOM(keycode, "keycode")
|
||||
GK_ATOM(keydown, "keydown")
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "jsgc.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsObjectLoadingContent.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -75,8 +76,12 @@ using namespace mozilla::dom;
|
||||
// If you change how this macro behave please update AttributeChildRemoved.
|
||||
#define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \
|
||||
PR_BEGIN_MACRO \
|
||||
bool needsEnterLeave = doc->MayHaveDOMMutationObservers(); \
|
||||
if (needsEnterLeave) { \
|
||||
nsDOMMutationObserver::EnterMutationHandling(); \
|
||||
} \
|
||||
nsINode* node = content_; \
|
||||
NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \
|
||||
NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \
|
||||
if (doc) { \
|
||||
static_cast<nsIMutationObserver*>(doc->BindingManager())-> \
|
||||
func_ params_; \
|
||||
@ -92,6 +97,9 @@ using namespace mozilla::dom;
|
||||
} \
|
||||
node = node->GetNodeParent(); \
|
||||
} while (node); \
|
||||
if (needsEnterLeave) { \
|
||||
nsDOMMutationObserver::LeaveMutationHandling(); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
void
|
||||
@ -569,8 +577,12 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
// nsImageLoadingContent needs to know when its document changes
|
||||
if (oldDoc != newDoc) {
|
||||
nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aNode));
|
||||
if (imageContent)
|
||||
if (imageContent) {
|
||||
imageContent->NotifyOwnerDocumentChanged(oldDoc);
|
||||
}
|
||||
if (oldDoc->MayHaveDOMMutationObservers()) {
|
||||
newDoc->SetMayHaveDOMMutationObservers();
|
||||
}
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
|
@ -3132,13 +3132,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
}
|
||||
|
||||
ChangeState(XML_HTTP_REQUEST_SENT);
|
||||
// Note, calling ChangeState may have cleared
|
||||
// XML_HTTP_REQUEST_SYNCLOOPING flag.
|
||||
nsIThread *thread = NS_GetCurrentThread();
|
||||
while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
|
||||
if (!NS_ProcessNextEvent(thread)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
break;
|
||||
|
||||
{
|
||||
nsAutoSyncOperation sync(suspendedDoc);
|
||||
// Note, calling ChangeState may have cleared
|
||||
// XML_HTTP_REQUEST_SYNCLOOPING flag.
|
||||
nsIThread *thread = NS_GetCurrentThread();
|
||||
while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
|
||||
if (!NS_ProcessNextEvent(thread)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,6 +285,7 @@ _TEST_FILES1 = \
|
||||
file_XHRDocURI.text \
|
||||
file_XHRDocURI.text^headers^ \
|
||||
test_DOMException.html \
|
||||
test_mutationobservers.html \
|
||||
$(NULL)
|
||||
|
||||
_TEST_FILES2 = \
|
||||
|
467
content/base/test/test_mutationobservers.html
Normal file
467
content/base/test/test_mutationobservers.html
Normal file
@ -0,0 +1,467 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=641821
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 641821</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body onload="runTest()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641821">Mozilla Bug 641821</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 641821 **/
|
||||
|
||||
var div = document.createElement("div");
|
||||
|
||||
var M;
|
||||
if ("MozMutationObserver" in window) {
|
||||
M = window.MozMutationObserver;
|
||||
} else if ("WebKitMutationObserver" in window) {
|
||||
M = window.WebKitMutationObserver;
|
||||
} else {
|
||||
M = window.MutationObserver;
|
||||
}
|
||||
|
||||
function log(str) {
|
||||
var d = document.createElement("div");
|
||||
d.textContent = str;
|
||||
if (str.indexOf("PASSED") >= 0) {
|
||||
d.setAttribute("style", "color: green;");
|
||||
} else {
|
||||
d.setAttribute("style", "color: red;");
|
||||
}
|
||||
document.getElementById("log").appendChild(d);
|
||||
}
|
||||
|
||||
// Some helper functions so that this test runs also outside mochitest.
|
||||
if (!("ok" in window)) {
|
||||
window.ok = function(val, str) {
|
||||
log(str + (val ? " PASSED\n" : " FAILED\n"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!("is" in window)) {
|
||||
window.is = function(val, refVal, str) {
|
||||
log(str + (val == refVal? " PASSED " : " FAILED ") +
|
||||
(val != refVal ? "expected " + refVal + " got " + val + "\n" : "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!("isnot" in window)) {
|
||||
window.isnot = function(val, refVal, str) {
|
||||
log(str + (val != refVal? " PASSED " : " FAILED ") +
|
||||
(val == refVal ? "Didn't expect " + refVal + "\n" : "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!("SimpleTest" in window)) {
|
||||
window.SimpleTest =
|
||||
{
|
||||
finish: function() {
|
||||
document.getElementById("log").appendChild(document.createTextNode("DONE"));
|
||||
},
|
||||
waitForExplicitFinish: function() {}
|
||||
}
|
||||
}
|
||||
|
||||
function then(thenFn) {
|
||||
setTimeout(function() {
|
||||
if (thenFn) {
|
||||
setTimeout(thenFn, 0);
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var m;
|
||||
var m2;
|
||||
var m3;
|
||||
var m4;
|
||||
|
||||
// Checks basic parameter validation and normal 'this' handling.
|
||||
// Tests also basic attribute handling.
|
||||
function runTest() {
|
||||
m = new M(function(){});
|
||||
ok(m, "MutationObserver supported");
|
||||
|
||||
var e = null;
|
||||
try {
|
||||
m.observe(document, {});
|
||||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
m.observe(document, { childList: true, attributeOldValue: true });
|
||||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
m.observe(document, { childList: true, attributeFilter: ["foo"] });
|
||||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
m.observe(document, { childList: true, characterDataOldValue: true });
|
||||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
m.observe(document);
|
||||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
|
||||
m = new M(function(records, observer) {
|
||||
is(observer, m, "2nd parameter should be the mutation observer");
|
||||
is(observer, this, "2nd parameter should be 'this'");
|
||||
is(records.length, 1, "Should have one record.");
|
||||
is(records[0].type, "attributes", "Should have got attributes record");
|
||||
is(records[0].target, div, "Should have got div as target");
|
||||
is(records[0].attributeName, "foo", "Should have got record about foo attribute");
|
||||
observer.disconnect();
|
||||
then(testThisBind);
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { attributes: true});
|
||||
div.setAttribute("foo", "bar");
|
||||
}
|
||||
|
||||
// 'this' handling when fn.bind() is used.
|
||||
function testThisBind() {
|
||||
var child = div.appendChild(document.createElement("div"));
|
||||
var gchild = child.appendChild(document.createElement("div"));
|
||||
m = new M((function(records, observer) {
|
||||
is(observer, m, "2nd parameter should be the mutation observer");
|
||||
isnot(observer, this, "2nd parameter should be 'this'");
|
||||
is(records.length, 3, "Should have one record.");
|
||||
is(records[0].type, "attributes", "Should have got attributes record");
|
||||
is(records[0].target, div, "Should have got div as target");
|
||||
is(records[0].attributeName, "foo", "Should have got record about foo attribute");
|
||||
is(records[0].oldValue, "bar", "oldValue should be bar");
|
||||
is(records[1].type, "attributes", "Should have got attributes record");
|
||||
is(records[1].target, div, "Should have got div as target");
|
||||
is(records[1].attributeName, "foo", "Should have got record about foo attribute");
|
||||
is(records[1].oldValue, "bar2", "oldValue should be bar2");
|
||||
is(records[2].type, "attributes", "Should have got attributes record");
|
||||
is(records[2].target, gchild, "Should have got div as target");
|
||||
is(records[2].attributeName, "foo", "Should have got record about foo attribute");
|
||||
is(records[2].oldValue, null, "oldValue should be bar2");
|
||||
observer.disconnect();
|
||||
then(testCharacterData);
|
||||
m = null;
|
||||
}).bind(window));
|
||||
m.observe(div, { attributes: true, attributeOldValue: true, subtree: true });
|
||||
div.setAttribute("foo", "bar2");
|
||||
div.removeAttribute("foo");
|
||||
div.removeChild(child);
|
||||
child.removeChild(gchild);
|
||||
div.appendChild(gchild);
|
||||
div.removeChild(gchild);
|
||||
gchild.setAttribute("foo", "bar");
|
||||
}
|
||||
|
||||
function testCharacterData() {
|
||||
m = new M(function(records, observer) {
|
||||
is(records[0].type, "characterData", "Should have got characterData");
|
||||
is(records[0].oldValue, null, "Shouldn't have got oldData");
|
||||
observer.disconnect();
|
||||
m = null;
|
||||
});
|
||||
m2 = new M(function(records, observer) {
|
||||
is(records[0].type, "characterData", "Should have got characterData");
|
||||
is(records[0].oldValue, "foo", "Should have got oldData");
|
||||
observer.disconnect();
|
||||
m2 = null;
|
||||
});
|
||||
m3 = new M(function(records, observer) {
|
||||
ok(false, "This should not be called!");
|
||||
observer.disconnect();
|
||||
m3 = null;
|
||||
});
|
||||
m4 = new M(function(records, observer) {
|
||||
is(records[0].oldValue, null, "Shouldn't have got oldData");
|
||||
observer.disconnect();
|
||||
m3.disconnect();
|
||||
m3 = null;
|
||||
then(testChildList);
|
||||
m4 = null;
|
||||
});
|
||||
|
||||
div.appendChild(document.createTextNode("foo"));
|
||||
m.observe(div, { characterData: true, subtree: true });
|
||||
m2.observe(div, { characterData: true, characterDataOldValue: true, subtree: true});
|
||||
// If observing the same node twice, only the latter option should apply.
|
||||
m3.observe(div, { characterData: true, subtree: true });
|
||||
m3.observe(div, { characterData: true, subtree: false });
|
||||
m4.observe(div.firstChild, { characterData: true, subtree: false });
|
||||
div.firstChild.data = "bar";
|
||||
}
|
||||
|
||||
function testChildList() {
|
||||
var fc = div.firstChild;
|
||||
m = new M(function(records, observer) {
|
||||
is(records[0].type, "childList", "Should have got childList");
|
||||
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
||||
is(records[0].removedNodes[0], fc, "Should have removed a text node");
|
||||
observer.disconnect();
|
||||
then(testChildList2);
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { childList: true});
|
||||
div.removeChild(div.firstChild);
|
||||
}
|
||||
|
||||
function testChildList2() {
|
||||
div.innerHTML = "<span>1</span><span>2</span>";
|
||||
m = new M(function(records, observer) {
|
||||
is(records[0].type, "childList", "Should have got childList");
|
||||
is(records[0].removedNodes.length, 2, "Should have got removedNodes");
|
||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
||||
observer.disconnect();
|
||||
then(testChildList3);
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { childList: true });
|
||||
div.innerHTML = "<span><span>foo</span></span>";
|
||||
}
|
||||
|
||||
function testChildList3() {
|
||||
m = new M(function(records, observer) {
|
||||
is(records[0].type, "childList", "Should have got childList");
|
||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
||||
observer.disconnect();
|
||||
then(testChildList4);
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { childList: true });
|
||||
div.textContent = "hello";
|
||||
}
|
||||
|
||||
function testChildList4() {
|
||||
div.textContent = null;
|
||||
var df = document.createDocumentFragment();
|
||||
var t1 = df.appendChild(document.createTextNode("Hello "));
|
||||
var t2 = df.appendChild(document.createTextNode("world!"));
|
||||
var s1 = div.appendChild(document.createElement("span"));
|
||||
s1.textContent = "foo";
|
||||
var s2 = div.appendChild(document.createElement("span"));
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 3, "Should have got one record for removing nodes from document fragment and one record for adding them to div");
|
||||
is(records[0].removedNodes.length, 2, "Should have got removedNodes");
|
||||
is(records[0].removedNodes[0], t1, "Should be the 1st textnode");
|
||||
is(records[0].removedNodes[1], t2, "Should be the 2nd textnode");
|
||||
is(records[1].addedNodes.length, 2, "Should have got addedNodes");
|
||||
is(records[1].addedNodes[0], t1, "Should be the 1st textnode");
|
||||
is(records[1].addedNodes[1], t2, "Should be the 2nd textnode");
|
||||
is(records[1].previousSibling, s1, "Should have previousSibling");
|
||||
is(records[1].nextSibling, s2, "Should have nextSibling");
|
||||
is(records[2].type, "characterData", "3rd record should be characterData");
|
||||
is(records[2].target, t1, "target should be the textnode");
|
||||
is(records[2].oldValue, "Hello ", "oldValue was 'Hello '");
|
||||
observer.disconnect();
|
||||
then(testChildList5);
|
||||
m = null;
|
||||
});
|
||||
m.observe(df, { childList: true, characterData: true, characterDataOldValue: true, subtree: true });
|
||||
m.observe(div, { childList: true });
|
||||
div.insertBefore(df, s2);
|
||||
s1.firstChild.data = "bar"; // This should *not* create a record.
|
||||
t1.data = "Hello the whole "; // This should create a record.
|
||||
}
|
||||
|
||||
function testChildList5() {
|
||||
div.textContent = null;
|
||||
var c1 = div.appendChild(document.createElement("div"));
|
||||
var c2 = document.createElement("div");
|
||||
var div2 = document.createElement("div");
|
||||
var c3 = div2.appendChild(document.createElement("div"));
|
||||
var c4 = document.createElement("div");
|
||||
var c5 = document.createElement("div");
|
||||
var df = document.createDocumentFragment();
|
||||
var dfc1 = df.appendChild(document.createElement("div"));
|
||||
var dfc2 = df.appendChild(document.createElement("div"));
|
||||
var dfc3 = df.appendChild(document.createElement("div"));
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 6 , "");
|
||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
||||
is(records[0].removedNodes[0], c1, "");
|
||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
||||
is(records[0].addedNodes[0], c2, "");
|
||||
is(records[0].previousSibling, null, "");
|
||||
is(records[0].nextSibling, null, "");
|
||||
is(records[1].removedNodes.length, 1, "Should have got removedNodes");
|
||||
is(records[1].removedNodes[0], c3, "");
|
||||
is(records[1].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
||||
is(records[1].previousSibling, null, "");
|
||||
is(records[1].nextSibling, null, "");
|
||||
is(records[2].removedNodes.length, 1, "Should have got removedNodes");
|
||||
is(records[2].removedNodes[0], c2, "");
|
||||
is(records[2].addedNodes.length, 1, "Should have got addedNodes");
|
||||
is(records[2].addedNodes[0], c3, "");
|
||||
is(records[2].previousSibling, null, "");
|
||||
is(records[2].nextSibling, null, "");
|
||||
// Check document fragment handling
|
||||
is(records[5].removedNodes.length, 1, "");
|
||||
is(records[5].removedNodes[0], c4, "");
|
||||
is(records[5].addedNodes.length, 3, "");
|
||||
is(records[5].addedNodes[0], dfc1, "");
|
||||
is(records[5].addedNodes[1], dfc2, "");
|
||||
is(records[5].addedNodes[2], dfc3, "");
|
||||
is(records[5].previousSibling, c3, "");
|
||||
is(records[5].nextSibling, c5, "");
|
||||
observer.disconnect();
|
||||
then(testAdoptNode);
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { childList: true, subtree: true });
|
||||
m.observe(div2, { childList: true, subtree: true });
|
||||
div.replaceChild(c2, c1);
|
||||
div.replaceChild(c3, c2);
|
||||
div.appendChild(c4);
|
||||
div.appendChild(c5);
|
||||
div.replaceChild(df, c4);
|
||||
}
|
||||
|
||||
function testAdoptNode() {
|
||||
var d1 = document.implementation.createHTMLDocument(null);
|
||||
var d2 = document.implementation.createHTMLDocument(null);
|
||||
var addedNode;
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 3, "Should have 2 records");
|
||||
is(records[0].target.ownerDocument, d1, "ownerDocument should be the initial document")
|
||||
is(records[1].target.ownerDocument, d2, "ownerDocument should be the new document");
|
||||
is(records[2].type, "attributes", "Should have got attribute mutation")
|
||||
is(records[2].attributeName, "foo", "Should have got foo attribute mutation")
|
||||
observer.disconnect();
|
||||
then(testOuterHTML);
|
||||
m = null;
|
||||
});
|
||||
m.observe(d1, { childList: true, subtree: true, attributes: true });
|
||||
d2.body.appendChild(d1.body);
|
||||
addedNode = d2.body.lastChild.appendChild(d2.createElement("div"));
|
||||
addedNode.setAttribute("foo", "bar");
|
||||
}
|
||||
|
||||
function testOuterHTML() {
|
||||
var doc = document.implementation.createHTMLDocument(null);
|
||||
var d1 = doc.body.appendChild(document.createElement("div"));
|
||||
var d2 = doc.body.appendChild(document.createElement("div"));
|
||||
var d3 = doc.body.appendChild(document.createElement("div"));
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 3, "Should have 1 record");
|
||||
is(records[0].removedNodes.length, 1, "Should have 1 removed nodes");
|
||||
is(records[0].addedNodes.length, 2, "Should have 2 added nodes");
|
||||
is(records[0].previousSibling, null, "");
|
||||
is(records[0].nextSibling, d2, "");
|
||||
is(records[1].removedNodes.length, 1, "Should have 1 removed nodes");
|
||||
is(records[1].addedNodes.length, 2, "Should have 2 added nodes");
|
||||
is(records[1].previousSibling, records[0].addedNodes[1], "");
|
||||
is(records[1].nextSibling, d3, "");
|
||||
is(records[2].removedNodes.length, 1, "Should have 1 removed nodes");
|
||||
is(records[2].addedNodes.length, 2, "Should have 2 added nodes");
|
||||
is(records[2].previousSibling, records[1].addedNodes[1], "");
|
||||
is(records[2].nextSibling, null, "");
|
||||
observer.disconnect();
|
||||
then(testInsertAdjacentHTML);
|
||||
m = null;
|
||||
});
|
||||
m.observe(doc, { childList: true, subtree: true });
|
||||
d1.outerHTML = "<div>1</div><div>1</div>";
|
||||
d2.outerHTML = "<div>2</div><div>2</div>";
|
||||
d3.outerHTML = "<div>3</div><div>3</div>";
|
||||
}
|
||||
|
||||
function testInsertAdjacentHTML() {
|
||||
var doc = document.implementation.createHTMLDocument(null);
|
||||
var d1 = doc.body.appendChild(document.createElement("div"));
|
||||
var d2 = doc.body.appendChild(document.createElement("div"));
|
||||
var d3 = doc.body.appendChild(document.createElement("div"));
|
||||
var d4 = doc.body.appendChild(document.createElement("div"));
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 4, "");
|
||||
is(records[0].target, doc.body, "");
|
||||
is(records[0].previousSibling, null, "");
|
||||
is(records[0].nextSibling, d1, "");
|
||||
is(records[1].target, d2, "");
|
||||
is(records[1].previousSibling, null, "");
|
||||
is(records[1].nextSibling, null, "");
|
||||
is(records[2].target, d3, "");
|
||||
is(records[2].previousSibling, null, "");
|
||||
is(records[2].nextSibling, null, "");
|
||||
is(records[3].target, doc.body, "");
|
||||
is(records[3].previousSibling, d4, "");
|
||||
is(records[3].nextSibling, null, "");
|
||||
observer.disconnect();
|
||||
then(testSyncXHR);
|
||||
m = null;
|
||||
});
|
||||
m.observe(doc, { childList: true, subtree: true });
|
||||
d1.insertAdjacentHTML("beforebegin", "<div></div><div></div>");
|
||||
d2.insertAdjacentHTML("afterbegin", "<div></div><div></div>");
|
||||
d3.insertAdjacentHTML("beforeend", "<div></div><div></div>");
|
||||
d4.insertAdjacentHTML("afterend", "<div></div><div></div>");
|
||||
}
|
||||
|
||||
|
||||
var callbackHandled = false;
|
||||
|
||||
function testSyncXHR() {
|
||||
div.textContent = null;
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 1, "");
|
||||
is(records[0].addedNodes.length, 1, "");
|
||||
callbackHandled = true;
|
||||
observer.disconnect();
|
||||
m = null;
|
||||
});
|
||||
m.observe(div, { childList: true, subtree: true });
|
||||
div.innerHTML = "<div>hello</div>";
|
||||
var x = new XMLHttpRequest();
|
||||
x.open("GET", window.location, false);
|
||||
x.send();
|
||||
ok(!callbackHandled, "Shouldn't have called the mutation callback!");
|
||||
setTimeout(testSyncXHR2, 0);
|
||||
}
|
||||
|
||||
function testSyncXHR2() {
|
||||
ok(callbackHandled, "Should have called the mutation callback!");
|
||||
then();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="log">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -792,6 +792,7 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
nsAutoMicroTask mt;
|
||||
// nsIDOMEvent::currentTarget is set in nsEventDispatcher.
|
||||
result = aListener->HandleEvent(aDOMEvent);
|
||||
}
|
||||
|
@ -120,7 +120,7 @@
|
||||
#include "nsHTMLMenuElement.h"
|
||||
#include "nsAsyncDOMEvent.h"
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
|
||||
@ -763,9 +763,11 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
|
||||
|
||||
// Remove childnodes.
|
||||
PRUint32 childCount = GetChildCount();
|
||||
nsAutoMutationBatch mb(this, true, false);
|
||||
for (PRUint32 i = 0; i < childCount; ++i) {
|
||||
RemoveChildAt(0, true);
|
||||
}
|
||||
mb.RemovalDone();
|
||||
|
||||
nsAutoScriptLoaderDisabler sld(doc);
|
||||
|
||||
@ -779,6 +781,7 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
|
||||
doc->GetCompatibilityMode() ==
|
||||
eCompatibility_NavQuirks,
|
||||
true);
|
||||
mb.NodesAdded();
|
||||
// HTML5 parser has notified, but not fired mutation events.
|
||||
FireMutationEventsForDirectParsing(doc, this, oldChildCount);
|
||||
} else {
|
||||
@ -794,6 +797,7 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
|
||||
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
|
||||
|
||||
static_cast<nsINode*>(this)->AppendChild(fragment, &rv);
|
||||
mb.NodesAdded();
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,6 +840,7 @@ nsGenericHTMLElement::SetOuterHTML(const nsAString& aOuterHTML)
|
||||
OwnerDoc()->GetCompatibilityMode() ==
|
||||
eCompatibility_NavQuirks,
|
||||
true);
|
||||
nsAutoMutationBatch mb(parent, true, false);
|
||||
parent->ReplaceChild(fragment, this, &rv);
|
||||
return rv;
|
||||
}
|
||||
@ -861,6 +866,7 @@ nsGenericHTMLElement::SetOuterHTML(const nsAString& aOuterHTML)
|
||||
getter_AddRefs(df));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
|
||||
nsAutoMutationBatch mb(parent, true, false);
|
||||
parent->ReplaceChild(fragment, this, &rv);
|
||||
return rv;
|
||||
}
|
||||
@ -910,7 +916,7 @@ nsGenericHTMLElement::InsertAdjacentHTML(const nsAString& aPosition,
|
||||
|
||||
nsresult rv;
|
||||
// Parse directly into destination if possible
|
||||
if (doc->IsHTML() &&
|
||||
if (doc->IsHTML() && !OwnerDoc()->MayHaveDOMMutationObservers() &&
|
||||
(position == eBeforeEnd ||
|
||||
(position == eAfterEnd && !GetNextSibling()) ||
|
||||
(position == eAfterBegin && !GetFirstChild()))) {
|
||||
@ -949,6 +955,7 @@ nsGenericHTMLElement::InsertAdjacentHTML(const nsAString& aPosition,
|
||||
// listeners on the fragment that comes from the parser.
|
||||
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
|
||||
|
||||
nsAutoMutationBatch mb(destination, true, false);
|
||||
switch (position) {
|
||||
case eBeforeBegin:
|
||||
destination->InsertBefore(fragment, this, &rv);
|
||||
|
@ -371,7 +371,10 @@ AsyncClickHandler::Run()
|
||||
|
||||
// Open dialog
|
||||
PRInt16 mode;
|
||||
rv = filePicker->Show(&mode);
|
||||
{
|
||||
nsAutoSyncOperation sync(doc);
|
||||
rv = filePicker->Show(&mode);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (mode == nsIFilePicker::returnCancel) {
|
||||
return NS_OK;
|
||||
|
@ -125,6 +125,8 @@ nsXBLProtoImplField::InstallField(nsIScriptContext* aContext,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
// EvaluateStringWithValue and JS_DefineUCProperty can both trigger GC, so
|
||||
// protect |result| here.
|
||||
nsresult rv;
|
||||
|
@ -324,7 +324,9 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
|
||||
if (!context) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
JSContext* cx = context->GetNativeContext();
|
||||
|
||||
JSObject* globalObject = global->GetGlobalJSObject();
|
||||
|
@ -480,6 +480,7 @@ nsXMLDocument::Load(const nsAString& aUrl, bool *aReturn)
|
||||
if (!mAsync) {
|
||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||
|
||||
nsAutoSyncOperation sync(this);
|
||||
mLoopingForSyncLoad = true;
|
||||
while (mLoopingForSyncLoad) {
|
||||
if (!NS_ProcessNextEvent(thread))
|
||||
|
@ -66,6 +66,7 @@ txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader,
|
||||
// Raw pointer, we want the resulting txXPathNode to hold a reference to
|
||||
// the document.
|
||||
nsIDOMDocument* theDocument = nsnull;
|
||||
nsAutoSyncOperation sync(loaderDocument);
|
||||
rv = nsSyncLoadService::LoadDocument(documentURI,
|
||||
loaderDocument->NodePrincipal(),
|
||||
loadGroup, true, &theDocument);
|
||||
|
@ -685,6 +685,12 @@ txSyncCompileObserver::loadURI(const nsAString& aUri,
|
||||
|
||||
// This is probably called by js, a loadGroup for the channel doesn't
|
||||
// make sense.
|
||||
nsCOMPtr<nsINode> source;
|
||||
if (mProcessor) {
|
||||
source =
|
||||
do_QueryInterface(mProcessor->GetSourceContentModel());
|
||||
}
|
||||
nsAutoSyncOperation sync(source ? source->OwnerDoc() : nsnull);
|
||||
nsCOMPtr<nsIDOMDocument> document;
|
||||
rv = nsSyncLoadService::LoadDocument(uri, referrerPrincipal, nsnull,
|
||||
false, getter_AddRefs(document));
|
||||
|
@ -503,6 +503,7 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
|
||||
|
||||
#include "nsDOMTouchEvent.h"
|
||||
#include "nsIDOMCustomEvent.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "dombindings.h"
|
||||
@ -1617,7 +1618,10 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(CustomEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(MozMutationObserver, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(MutationRecord, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(MozSettingsEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
@ -1666,6 +1670,7 @@ static const nsContractIDMapData kConstructorMap[] =
|
||||
NS_DEFINE_CONSTRUCTOR_DATA(XSLTProcessor,
|
||||
"@mozilla.org/document-transformer;1?type=xslt")
|
||||
NS_DEFINE_CONSTRUCTOR_DATA(EventSource, NS_EVENTSOURCE_CONTRACTID)
|
||||
NS_DEFINE_CONSTRUCTOR_DATA(MozMutationObserver, NS_DOMMUTATIONOBSERVER_CONTRACTID)
|
||||
};
|
||||
|
||||
#define NS_DEFINE_EVENT_CTOR(_class) \
|
||||
@ -2404,6 +2409,12 @@ nsDOMClassInfo::Init()
|
||||
sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener),
|
||||
elt, getter_AddRefs(old));
|
||||
|
||||
nsCOMPtr<nsIXPCFunctionThisTranslator> mctl = new nsMutationCallbackThisTranslator();
|
||||
NS_ENSURE_TRUE(elt, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIMutationObserverCallback),
|
||||
mctl, getter_AddRefs(old));
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> sm =
|
||||
do_GetService("@mozilla.org/scriptsecuritymanager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -4370,6 +4381,14 @@ nsDOMClassInfo::Init()
|
||||
DOM_CLASSINFO_EVENT_MAP_ENTRIES
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(MozMutationObserver, nsIDOMMozMutationObserver)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozMutationObserver)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(MutationRecord, nsIDOMMutationRecord)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMutationRecord)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(MozSettingsEvent, nsIDOMMozSettingsEvent)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSettingsEvent)
|
||||
DOM_CLASSINFO_EVENT_MAP_ENTRIES
|
||||
@ -10657,6 +10676,28 @@ nsEventListenerThisTranslator::TranslateThis(nsISupports *aInitialThis,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsMutationCallbackThisTranslator)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsMutationCallbackThisTranslator)
|
||||
NS_IMPL_RELEASE(nsMutationCallbackThisTranslator)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMutationCallbackThisTranslator::TranslateThis(nsISupports *aInitialThis,
|
||||
nsIInterfaceInfo *aInterfaceInfo,
|
||||
PRUint16 aMethodIndex,
|
||||
bool *aHideFirstParamFromJS,
|
||||
nsIID * *aIIDOfResult,
|
||||
nsISupports **_retval)
|
||||
{
|
||||
*aHideFirstParamFromJS = false;
|
||||
*aIIDOfResult = nsnull;
|
||||
NS_IF_ADDREF(*_retval = nsDOMMutationObserver::CurrentObserver());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMConstructorSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
JSObject *globalObj, JSObject **parentObj)
|
||||
|
@ -1482,6 +1482,24 @@ public:
|
||||
NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
|
||||
};
|
||||
|
||||
class nsMutationCallbackThisTranslator : public nsIXPCFunctionThisTranslator
|
||||
{
|
||||
public:
|
||||
nsMutationCallbackThisTranslator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsMutationCallbackThisTranslator()
|
||||
{
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIXPCFunctionThisTranslator
|
||||
NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
|
||||
};
|
||||
|
||||
class nsDOMConstructorSH : public nsDOMGenericSH
|
||||
{
|
||||
protected:
|
||||
|
@ -532,6 +532,10 @@ DOMCI_CLASS(MozCSSKeyframesRule)
|
||||
|
||||
DOMCI_CLASS(MediaQueryList)
|
||||
DOMCI_CLASS(CustomEvent)
|
||||
|
||||
DOMCI_CLASS(MozMutationObserver)
|
||||
DOMCI_CLASS(MutationRecord)
|
||||
|
||||
DOMCI_CLASS(MozSettingsEvent)
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
|
@ -4819,6 +4819,9 @@ nsGlobalWindow::Alert(const nsAString& aString)
|
||||
if (promptBag)
|
||||
promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), allowTabModal);
|
||||
|
||||
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
|
||||
GetCurrentInnerWindowInternal()->mDoc :
|
||||
nsnull);
|
||||
if (shouldEnableDisableDialog) {
|
||||
bool disallowDialog = false;
|
||||
nsXPIDLString label;
|
||||
@ -4885,6 +4888,9 @@ nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
|
||||
if (promptBag)
|
||||
promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), allowTabModal);
|
||||
|
||||
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
|
||||
GetCurrentInnerWindowInternal()->mDoc :
|
||||
nsnull);
|
||||
if (shouldEnableDisableDialog) {
|
||||
bool disallowDialog = false;
|
||||
nsXPIDLString label;
|
||||
@ -4964,6 +4970,9 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
|
||||
"ScriptDialogLabel", label);
|
||||
}
|
||||
|
||||
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
|
||||
GetCurrentInnerWindowInternal()->mDoc :
|
||||
nsnull);
|
||||
bool ok;
|
||||
rv = prompt->Prompt(title.get(), fixedMessage.get(),
|
||||
&inoutValue, label.get(), &disallowDialog, &ok);
|
||||
@ -5230,6 +5239,9 @@ nsGlobalWindow::Print()
|
||||
nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
|
||||
if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
|
||||
getter_AddRefs(webBrowserPrint)))) {
|
||||
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
|
||||
GetCurrentInnerWindowInternal()->mDoc :
|
||||
nsnull);
|
||||
|
||||
nsCOMPtr<nsIPrintSettingsService> printSettingsService =
|
||||
do_GetService("@mozilla.org/gfx/printsettings-service;1");
|
||||
|
@ -1187,6 +1187,8 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
// Safety first: get an object representing the script's principals, i.e.,
|
||||
// the entities who signed this script, or the fully-qualified-domain-name
|
||||
// or "codebase" from which it was loaded.
|
||||
@ -1381,6 +1383,8 @@ nsJSContext::EvaluateString(const nsAString& aScript,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
if (!aScopeObject) {
|
||||
aScopeObject = JS_GetGlobalObject(mContext);
|
||||
}
|
||||
@ -1557,6 +1561,8 @@ nsJSContext::ExecuteScript(JSScript* aScriptObject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
if (!aScopeObject) {
|
||||
aScopeObject = JS_GetGlobalObject(mContext);
|
||||
}
|
||||
@ -1815,6 +1821,7 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, JSObject* aScope,
|
||||
#endif
|
||||
SAMPLE_LABEL("JS", "CallEventHandler");
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
JSAutoRequest ar(mContext);
|
||||
JSObject* target = nsnull;
|
||||
nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
|
||||
|
@ -72,6 +72,7 @@ XPIDLSRCS = \
|
||||
nsIDOMDOMTokenList.idl \
|
||||
nsIDOMDOMSettableTokenList.idl \
|
||||
nsIInlineEventHandlers.idl \
|
||||
nsIDOMMutationObserver.idl \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
71
dom/interfaces/core/nsIDOMMutationObserver.idl
Normal file
71
dom/interfaces/core/nsIDOMMutationObserver.idl
Normal file
@ -0,0 +1,71 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
interface nsIVariant;
|
||||
|
||||
[scriptable, uuid(2fa4716f-405a-449b-954b-ae570c170364)]
|
||||
interface nsIDOMMutationRecord : nsISupports
|
||||
{
|
||||
/**
|
||||
* type is on of childList, attribute, characterData.
|
||||
*/
|
||||
readonly attribute DOMString type;
|
||||
|
||||
/**
|
||||
* Target of the change.
|
||||
* If an attribute is changed, target is the element,
|
||||
* if an element is added or removed, target is the node
|
||||
* which was added or removed.
|
||||
* If text is changed, target is the CharacterData node which was changed.
|
||||
*/
|
||||
readonly attribute nsIDOMNode target;
|
||||
|
||||
readonly attribute nsIDOMNodeList addedNodes;
|
||||
readonly attribute nsIDOMNodeList removedNodes;
|
||||
|
||||
readonly attribute nsIDOMNode previousSibling;
|
||||
readonly attribute nsIDOMNode nextSibling;
|
||||
|
||||
/**
|
||||
* The name of the attribute which was changed, or null.
|
||||
*/
|
||||
readonly attribute DOMString attributeName;
|
||||
readonly attribute DOMString attributeNamespace;
|
||||
|
||||
/*
|
||||
* The previous value of the attribute or CharacterData node, or null.
|
||||
*
|
||||
* If a new attribute is added, or attribute values aren't reported,
|
||||
* prevValue is null.
|
||||
*/
|
||||
readonly attribute DOMString oldValue;
|
||||
};
|
||||
|
||||
dictionary MutationObserverInit
|
||||
{
|
||||
boolean childList;
|
||||
boolean attributes;
|
||||
boolean characterData;
|
||||
boolean subtree;
|
||||
boolean attributeOldValue;
|
||||
boolean characterDataOldValue;
|
||||
jsval attributeFilter; // DOMString[]
|
||||
};
|
||||
|
||||
//[Constructor(in nsIMutationCallback aDoneCallback)]
|
||||
[scriptable, builtinclass, uuid(daeba265-9aa7-45ab-8de2-b6b039c13ced)]
|
||||
interface nsIDOMMozMutationObserver : nsISupports
|
||||
{
|
||||
[implicit_jscontext]
|
||||
void observe(in nsIDOMNode aTarget, in jsval aOptions);
|
||||
void disconnect();
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(fb539590-b088-4d07-96ff-2cefbc90a198)]
|
||||
interface nsIMutationObserverCallback : nsISupports
|
||||
{
|
||||
void handleMutations(in nsIVariant aRecords, in nsIDOMMozMutationObserver aObserver);
|
||||
};
|
@ -229,7 +229,8 @@ nsDOMGeoPositionError::NotifyCallback(nsIDOMGeoPositionErrorCallback* aCallback)
|
||||
nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
|
||||
if (!stack || NS_FAILED(stack->Push(nsnull)))
|
||||
return;
|
||||
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
aCallback->HandleEvent(this);
|
||||
|
||||
// remove the stack
|
||||
@ -459,7 +460,8 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
|
||||
nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
|
||||
if (!stack || NS_FAILED(stack->Push(nsnull)))
|
||||
return; // silently fail
|
||||
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
mCallback->HandleEvent(aPosition);
|
||||
|
||||
// remove the stack
|
||||
|
@ -12,6 +12,7 @@ dictionaries = [
|
||||
[ 'IDBIndexParameters', 'nsIIDBObjectStore.idl' ],
|
||||
[ 'StorageEventInit', 'nsIDOMStorageEvent.idl' ],
|
||||
[ 'BlobPropertyBag', 'nsIDOMFile.idl' ],
|
||||
[ 'MutationObserverInit', 'nsIDOMMutationObserver.idl' ],
|
||||
[ 'SettingsEventInit', 'nsIDOMSettingsManager.idl' ]
|
||||
]
|
||||
|
||||
|
@ -177,6 +177,7 @@ members = [
|
||||
'nsIDOMDOMTokenList.*',
|
||||
'nsIDOMDOMSettableTokenList.*',
|
||||
'nsIDOMXULDocument.getBoxObjectFor',
|
||||
'nsIDOMMutationRecord.*',
|
||||
|
||||
# dom/interfaces/css
|
||||
'nsIDOMElementCSSInlineStyle.*',
|
||||
@ -509,6 +510,8 @@ irregularFilenames = {
|
||||
'nsIDOMTouch': 'nsIDOMTouchEvent',
|
||||
'nsIDOMTouchList': 'nsIDOMTouchEvent',
|
||||
|
||||
'nsIDOMMutationRecord': 'nsIDOMMutationObserver',
|
||||
|
||||
'nsITelephoneCallback': 'nsITelephone',
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "mozilla/dom/bindings/Utils.h"
|
||||
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -2335,6 +2336,7 @@ nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread,
|
||||
// Call cycle collector occasionally.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsJSContext::MaybePokeCC();
|
||||
nsDOMMutationObserver::HandleMutations();
|
||||
|
||||
return Pop(nsnull);
|
||||
}
|
||||
|
@ -1209,6 +1209,7 @@ DocumentViewerImpl::PermitUnload(bool aCallerClosesWindow, bool *aPermitUnload)
|
||||
(nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) |
|
||||
(nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_1));
|
||||
|
||||
nsAutoSyncOperation sync(mDocument);
|
||||
rv = prompt->ConfirmEx(title, message, buttonFlags,
|
||||
leaveLabel, stayLabel, nsnull, nsnull,
|
||||
&dummy, &buttonPressed);
|
||||
|
@ -386,6 +386,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
|
||||
|
||||
PRInt64 eventTime = mMostRecentRefreshEpochTime / PR_USEC_PER_MSEC;
|
||||
for (PRUint32 i = 0; i < frameRequestCallbacks.Length(); ++i) {
|
||||
nsAutoMicroTask mt;
|
||||
frameRequestCallbacks[i]->Sample(eventTime);
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,7 @@ using mozilla::dom::gonk::SystemWorkerManager;
|
||||
#include "AudioManager.h"
|
||||
using mozilla::dom::gonk::AudioManager;
|
||||
#endif
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
// Editor stuff
|
||||
#include "nsEditorCID.h"
|
||||
@ -287,6 +288,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
|
||||
SystemWorkerManager::FactoryCreate)
|
||||
#endif
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMMutationObserver)
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(AudioManager)
|
||||
@ -782,6 +784,7 @@ NS_DEFINE_NAMED_CID(NS_NULLPRINCIPAL_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SECURITYNAMESET_CID);
|
||||
NS_DEFINE_NAMED_CID(THIRDPARTYUTIL_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_STRUCTUREDCLONECONTAINER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMMUTATIONOBSERVER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DEVICE_SENSORS_CID);
|
||||
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
@ -1058,6 +1061,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
||||
#endif
|
||||
{ &kTHIRDPARTYUTIL_CID, false, NULL, ThirdPartyUtilConstructor },
|
||||
{ &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor },
|
||||
{ &kNS_DOMMUTATIONOBSERVER_CID, false, NULL, nsDOMMutationObserverConstructor },
|
||||
{ &kSMS_SERVICE_CID, false, NULL, nsISmsServiceConstructor },
|
||||
{ &kSMS_DATABASE_SERVICE_CID, false, NULL, nsISmsDatabaseServiceConstructor },
|
||||
{ &kSMS_REQUEST_MANAGER_CID, false, NULL, SmsRequestManagerConstructor },
|
||||
@ -1191,6 +1195,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
#endif
|
||||
{ THIRDPARTYUTIL_CONTRACTID, &kTHIRDPARTYUTIL_CID },
|
||||
{ NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
|
||||
{ NS_DOMMUTATIONOBSERVER_CONTRACTID, &kNS_DOMMUTATIONOBSERVER_CID },
|
||||
{ SMS_SERVICE_CONTRACTID, &kSMS_SERVICE_CID },
|
||||
{ SMS_DATABASE_SERVICE_CONTRACTID, &kSMS_DATABASE_SERVICE_CID },
|
||||
{ SMS_REQUEST_MANAGER_CONTRACTID, &kSMS_REQUEST_MANAGER_CID },
|
||||
|
@ -120,7 +120,7 @@
|
||||
#include "nsContentSink.h"
|
||||
#include "nsFrameMessageManager.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsHyphenationManager.h"
|
||||
#include "nsEditorSpellCheck.h"
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
@ -362,4 +362,5 @@ nsLayoutStatics::Shutdown()
|
||||
|
||||
nsHyphenationManager::Shutdown();
|
||||
nsEditorSpellCheck::ShutDown();
|
||||
nsDOMMutationObserver::Shutdown();
|
||||
}
|
||||
|
@ -184,10 +184,9 @@ ifdef NSS_DISABLE_DBM
|
||||
DEFAULT_GMAKE_FLAGS += NSS_DISABLE_DBM=1
|
||||
endif
|
||||
ABS_topsrcdir := $(call core_abspath,$(topsrcdir))
|
||||
# Hack to force NSS build system to use "normal" object directories
|
||||
DEFAULT_GMAKE_FLAGS += BUILD='$(MOZ_BUILD_ROOT)/security/$$(subst $(shell cd $(topsrcdir); pwd)/security/,,$$(CURDIR))'
|
||||
DEFAULT_GMAKE_FLAGS += BUILD_TREE='$$(BUILD)' OBJDIR='$$(BUILD)' DEPENDENCIES='$$(BUILD)/.deps' SINGLE_SHLIB_DIR='$$(BUILD)'
|
||||
DEFAULT_GMAKE_FLAGS += SOURCE_XP_DIR=$(ABS_DIST)
|
||||
ifneq ($(ABS_topsrcdir),$(MOZ_BUILD_ROOT))
|
||||
DEFAULT_GMAKE_FLAGS += BUILD_TREE=$(MOZ_BUILD_ROOT)
|
||||
endif
|
||||
ifndef MOZ_DEBUG
|
||||
DEFAULT_GMAKE_FLAGS += BUILD_OPT=1 OPT_CODE_SIZE=1
|
||||
endif
|
||||
@ -286,6 +285,10 @@ ifdef MOZ_CFLAGS_NSS
|
||||
DEFAULT_GMAKE_FLAGS += XCFLAGS="$(CFLAGS)"
|
||||
endif
|
||||
|
||||
SUBMAKEFILES = boot/Makefile ssl/Makefile pki/Makefile locales/Makefile
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
ifdef MOZ_NSS_PATCH
|
||||
# If we're applying a patch, we'll copy the NSS source to the objdir
|
||||
# and build it from there.
|
||||
@ -307,81 +310,118 @@ else
|
||||
NSS_SRCDIR = $(topsrcdir)
|
||||
endif
|
||||
|
||||
NSS_DIRS =
|
||||
ifndef NSS_DISABLE_DBM
|
||||
NSS_DIRS += dbm
|
||||
endif
|
||||
NSS_DIRS += \
|
||||
nss/lib \
|
||||
nss/cmd/lib \
|
||||
nss/cmd/shlibsign \
|
||||
$(NULL)
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
NSS_DIRS += \
|
||||
nss/cmd/certutil \
|
||||
nss/cmd/pk12util \
|
||||
nss/cmd/modutil \
|
||||
$(NULL)
|
||||
dependclean export packages chrome::
|
||||
$(MAKE) -C boot $@
|
||||
$(MAKE) -C ssl $@
|
||||
$(MAKE) -C locales $@
|
||||
ifdef MOZ_XUL
|
||||
$(MAKE) -C pki $@
|
||||
endif
|
||||
|
||||
libs::
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
define build_rules
|
||||
libs::
|
||||
$$(NSSMAKE) -C $$(NSS_SRCDIR)/security/$(1) $$(DEFAULT_GMAKE_FLAGS)
|
||||
|
||||
clean clobber clobber_all realclean distclean depend::
|
||||
$$(NSSMAKE) -C $$(NSS_SRCDIR)/security/$(1) $$(DEFAULT_GMAKE_FLAGS) clean
|
||||
endef
|
||||
$(foreach dir,$(NSS_DIRS),$(eval $(call build_rules,$(dir))))
|
||||
|
||||
NSS_LIBS = \
|
||||
$(LOADABLE_ROOT_MODULE) \
|
||||
$(SOFTOKEN3_LIB) \
|
||||
$(NSSDBM3_LIB) \
|
||||
$(NSS3_LIB) \
|
||||
$(NSSUTIL3_LIB) \
|
||||
$(SSL3_LIB) \
|
||||
$(SMIME3_LIB) \
|
||||
$(FREEBL_LIB) \
|
||||
$(FREEBL_32INT_LIB) \
|
||||
$(FREEBL_32FPU_LIB) \
|
||||
$(FREEBL_32INT64_LIB) \
|
||||
$(FREEBL_64INT_LIB) \
|
||||
$(FREEBL_64FPU_LIB) \
|
||||
$(NULL)
|
||||
|
||||
define install_rules
|
||||
libs::
|
||||
ifeq ($(OS_ARCH)_$(1), SunOS_$(SOFTOKEN3_LIB))
|
||||
# has to use copy mode on Solaris, see #665509
|
||||
$$(NSINSTALL) -t -m 755 $$(DIST)/lib/$(1) $$(DIST)/bin
|
||||
else
|
||||
$$(INSTALL) -m 755 $$(DIST)/lib/$(1) $$(DIST)/bin
|
||||
ifndef NSS_DISABLE_DBM
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/dbm $(DEFAULT_GMAKE_FLAGS)
|
||||
endif
|
||||
|
||||
install::
|
||||
$$(SYSINSTALL) -m 755 $$(DIST)/lib/$(1) $$(DESTDIR)$$(mozappdir)
|
||||
endef
|
||||
$(foreach lib,$(NSS_LIBS),$(eval $(call install_rules,$(lib))))
|
||||
|
||||
libs::
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/lib $(DEFAULT_GMAKE_FLAGS)
|
||||
ifdef ENABLE_TESTS
|
||||
# Need certutil binary for mochitest certificates generation
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS)
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/certutil $(DEFAULT_GMAKE_FLAGS)
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/pk12util $(DEFAULT_GMAKE_FLAGS)
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/modutil $(DEFAULT_GMAKE_FLAGS)
|
||||
endif
|
||||
ifndef ENABLE_TESTS # Just avoid secondary compile
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS)
|
||||
endif
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/shlibsign $(DEFAULT_GMAKE_FLAGS)
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(LOADABLE_ROOT_MODULE) $(DIST)/bin
|
||||
ifeq ($(OS_ARCH), SunOS)
|
||||
# has to use copy mode on Solaris, see #665509
|
||||
$(NSINSTALL) -t -m 755 $(DIST)/lib/$(SOFTOKEN3_LIB) $(DIST)/bin
|
||||
else
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(SOFTOKEN3_LIB) $(DIST)/bin
|
||||
endif
|
||||
ifndef NSS_DISABLE_DBM
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(NSSDBM3_LIB) $(DIST)/bin
|
||||
endif
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(NSS3_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(NSSUTIL3_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(SSL3_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(SMIME3_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(SDK_LIBS) $(DIST)/sdk/lib
|
||||
ifdef HAVE_FREEBL_LIBS
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_LIB) $(DIST)/bin
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_32INT_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_32FPU_LIB) $(DIST)/bin
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32INT64
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_32INT64_LIB) $(DIST)/bin
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_64
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_64INT_LIB) $(DIST)/bin
|
||||
$(INSTALL) -m 755 $(DIST)/lib/$(FREEBL_64FPU_LIB) $(DIST)/bin
|
||||
endif
|
||||
endif
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
# NSS installs headers to dist/public and we want them in dist/include
|
||||
$(NSINSTALL) -D $(DIST)/include/nss
|
||||
(cd $(DIST)/public/nss && tar $(TAR_CREATE_FLAGS) - .) | \
|
||||
(cd $(DIST)/include && tar -xf -)
|
||||
|
||||
endif # MOZ_NATIVE_NSS
|
||||
|
||||
DIRS = \
|
||||
boot \
|
||||
ssl \
|
||||
locales \
|
||||
$(NULL)
|
||||
|
||||
endif
|
||||
$(MAKE) -C boot $@
|
||||
$(MAKE) -C ssl $@
|
||||
$(MAKE) -C locales $@
|
||||
ifdef MOZ_XUL
|
||||
DIRS += pki
|
||||
$(MAKE) -C pki $@
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
check::
|
||||
$(MAKE) -C ssl $@
|
||||
|
||||
install::
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(LOADABLE_ROOT_MODULE) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(SOFTOKEN3_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(NSSDBM3_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(NSS3_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(NSSUTIL3_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(SSL3_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(SMIME3_LIB) $(DESTDIR)$(mozappdir)
|
||||
ifdef HAVE_FREEBL_LIBS
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_LIB) $(DESTDIR)$(mozappdir)
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_32INT_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_32FPU_LIB) $(DESTDIR)$(mozappdir)
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32INT64
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_32INT64_LIB) $(DESTDIR)$(mozappdir)
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_64
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_64INT_LIB) $(DESTDIR)$(mozappdir)
|
||||
$(SYSINSTALL) -m 755 $(DIST)/lib/$(FREEBL_64FPU_LIB) $(DESTDIR)$(mozappdir)
|
||||
endif
|
||||
endif
|
||||
$(MAKE) -C boot $@
|
||||
$(MAKE) -C ssl $@
|
||||
$(MAKE) -C locales $@
|
||||
ifdef MOZ_XUL
|
||||
$(MAKE) -C pki $@
|
||||
endif
|
||||
|
||||
clean clobber clobber_all realclean distclean depend::
|
||||
$(MAKE) -C boot $@
|
||||
$(MAKE) -C ssl $@
|
||||
$(MAKE) -C locales $@
|
||||
ifdef MOZ_XUL
|
||||
$(MAKE) -C pki $@
|
||||
endif
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/coreconf $(DEFAULT_GMAKE_FLAGS) clean
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/lib $(DEFAULT_GMAKE_FLAGS) clean
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS) clean
|
||||
$(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/shlibsign $(DEFAULT_GMAKE_FLAGS) clean
|
||||
endif
|
||||
|
Loading…
Reference in New Issue
Block a user