зеркало из https://github.com/mozilla/gecko-dev.git
Bug 333078: XPCOM cycle collector
r=brendan, sicking, bz, dbaron, bsmedberg
This commit is contained in:
Родитель
546f5b33f3
Коммит
4525ab79d9
|
@ -55,7 +55,6 @@ nsContentPolicyUtils.h \
|
|||
nsContentUtils.h \
|
||||
nsIDocument.h \
|
||||
nsIDocumentObserver.h \
|
||||
nsIDOMGCParticipant.h \
|
||||
nsIMutationObserver.h \
|
||||
nsINameSpaceManager.h \
|
||||
nsINode.h \
|
||||
|
|
|
@ -764,6 +764,20 @@ public:
|
|||
PRBool aCancelable,
|
||||
PRBool *aDefaultAction = nsnull);
|
||||
|
||||
/**
|
||||
* Used only during traversal of the XPCOM graph by the cycle
|
||||
* collector: push a pointer to the listener manager onto the
|
||||
* children deque, if it exists. Do nothing if there is no listener
|
||||
* manager.
|
||||
*
|
||||
* Crucially: does not perform any refcounting operations.
|
||||
*
|
||||
* @param aNode The node to traverse.
|
||||
* @param children The buffer to push a listener manager pointer into.
|
||||
*/
|
||||
static void TraverseListenerManager(nsINode *aNode,
|
||||
nsCycleCollectionTraversalCallback &cb);
|
||||
|
||||
/**
|
||||
* Get the eventlistener manager for aNode. If a new eventlistener manager
|
||||
* was created, aCreated is set to PR_TRUE.
|
||||
|
@ -916,7 +930,9 @@ private:
|
|||
static nsresult doReparentContentWrapper(nsIContent *aChild,
|
||||
JSContext *cx,
|
||||
JSObject *aOldGlobal,
|
||||
JSObject *aNewGlobal);
|
||||
JSObject *aNewGlobal,
|
||||
nsIDocument *aOldDocument,
|
||||
nsIDocument *aNewDocument);
|
||||
|
||||
static nsresult EnsureStringBundle(PropertiesFile aFile);
|
||||
|
||||
|
|
|
@ -785,4 +785,31 @@ public:
|
|||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIContent, NS_ICONTENT_IID)
|
||||
|
||||
// Some cycle-collecting helper macros for nsIContent subclasses
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER \
|
||||
if (tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { \
|
||||
nsContentUtils::TraverseListenerManager(tmp, cb); \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER \
|
||||
{ \
|
||||
nsISupports *preservedWrapper = nsnull; \
|
||||
if (tmp->GetOwnerDoc()) \
|
||||
preservedWrapper = tmp->GetOwnerDoc()->GetReference(tmp); \
|
||||
if (preservedWrapper) \
|
||||
cb.NoteXPCOMChild(preservedWrapper); \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER \
|
||||
if (tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { \
|
||||
nsContentUtils::RemoveListenerManager(tmp); \
|
||||
tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
|
||||
if (tmp->GetOwnerDoc()) \
|
||||
tmp->GetOwnerDoc()->RemoveReference(tmp);
|
||||
|
||||
|
||||
#endif /* nsIContent_h___ */
|
||||
|
|
|
@ -609,6 +609,10 @@ public:
|
|||
virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup,
|
||||
nsIPrincipal* aPrincipal) = 0;
|
||||
|
||||
virtual void AddReference(void *aKey, nsISupports *aReference) = 0;
|
||||
virtual nsISupports *GetReference(void *aKey) = 0;
|
||||
virtual already_AddRefed<nsISupports> RemoveReference(void *aKey) = 0;
|
||||
|
||||
/**
|
||||
* Set the container (docshell) for this document.
|
||||
*/
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#ifndef nsINode_h___
|
||||
#define nsINode_h___
|
||||
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
#include "nsEvent.h"
|
||||
#include "nsPropertyTable.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
@ -97,11 +96,11 @@ class nsNodeSupportsWeakRefTearoff;
|
|||
|
||||
// IID for the nsINode interface
|
||||
#define NS_INODE_IID \
|
||||
{ 0x98470c4b, 0xe988, 0x4abf, \
|
||||
{ 0xa4, 0x00, 0x39, 0xbd, 0x00, 0x38, 0x51, 0x4d } }
|
||||
{ 0x0d2a583d, 0x7a99, 0x426b, \
|
||||
{ 0x89, 0xfa, 0xca, 0x8d, 0x63, 0xbb, 0xd7, 0x3f } }
|
||||
|
||||
// hack to make egcs / gcc 2.95.2 happy
|
||||
class nsINode_base : public nsIDOMGCParticipant {
|
||||
class nsINode_base : public nsISupports {
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
|
||||
};
|
||||
|
|
|
@ -138,6 +138,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
|
|||
#ifdef IBMBIDI
|
||||
#include "nsIBidiKeyboard.h"
|
||||
#endif
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
// for ReportToConsole
|
||||
#include "nsIStringBundle.h"
|
||||
|
@ -726,7 +727,9 @@ nsresult
|
|||
nsContentUtils::doReparentContentWrapper(nsIContent *aNode,
|
||||
JSContext *cx,
|
||||
JSObject *aOldGlobal,
|
||||
JSObject *aNewGlobal)
|
||||
JSObject *aNewGlobal,
|
||||
nsIDocument *aOldDocument,
|
||||
nsIDocument *aNewDocument)
|
||||
{
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> old_wrapper;
|
||||
|
||||
|
@ -737,6 +740,16 @@ nsContentUtils::doReparentContentWrapper(nsIContent *aNode,
|
|||
getter_AddRefs(old_wrapper));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aOldDocument) {
|
||||
nsCOMPtr<nsISupports> old_ref = aOldDocument->RemoveReference(aNode);
|
||||
|
||||
if (old_ref) {
|
||||
// Transfer the reference from aOldDocument to aNewDocument
|
||||
|
||||
aNewDocument->AddReference(aNode, old_ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Whether or not aChild is already wrapped we must iterate through
|
||||
// its descendants since there's no guarantee that a descendant isn't
|
||||
// wrapped even if this child is not wrapped. That used to be true
|
||||
|
@ -749,7 +762,9 @@ nsContentUtils::doReparentContentWrapper(nsIContent *aNode,
|
|||
nsIContent *child = aNode->GetChildAt(i);
|
||||
NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
|
||||
|
||||
rv = doReparentContentWrapper(child, cx, aOldGlobal, aNewGlobal);
|
||||
rv = doReparentContentWrapper(child, cx,
|
||||
aOldGlobal, aNewGlobal,
|
||||
aOldDocument, aNewDocument);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
@ -803,7 +818,8 @@ nsContentUtils::ReparentContentWrapper(nsIContent *aContent,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
return doReparentContentWrapper(aContent, cx, oldScope, newScope);
|
||||
return doReparentContentWrapper(aContent, cx, oldScope, newScope,
|
||||
aOldDocument, aNewDocument);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -2874,6 +2890,24 @@ nsContentUtils::HasMutationListeners(nsINode* aNode,
|
|||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::TraverseListenerManager(nsINode *aNode,
|
||||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
if (!sEventListenerManagersHash.ops) {
|
||||
// We're already shut down, just return.
|
||||
return;
|
||||
}
|
||||
|
||||
EventListenerManagerMapEntry *entry =
|
||||
NS_STATIC_CAST(EventListenerManagerMapEntry *,
|
||||
PL_DHashTableOperate(&sEventListenerManagersHash, aNode,
|
||||
PL_DHASH_LOOKUP));
|
||||
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
|
||||
cb.NoteXPCOMChild(entry->mListenerManager);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::GetListenerManager(nsINode *aNode,
|
||||
PRBool aCreateIfNotFound,
|
||||
|
|
|
@ -72,12 +72,24 @@ nsDOMAttribute::nsDOMAttribute(nsDOMAttributeMap *aAttrMap,
|
|||
// to drop our reference when it goes away.
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttribute)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttribute, nsIDOMAttr)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChild)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute, nsIDOMAttr)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChild)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
// QueryInterface implementation for nsDOMAttribute
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMAttribute)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMAttr)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAttribute)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3Node)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3Attr)
|
||||
|
@ -85,34 +97,12 @@ NS_INTERFACE_MAP_BEGIN(nsDOMAttribute)
|
|||
new nsNodeSupportsWeakRefTearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMAttr)
|
||||
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Attr)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsDOMAttribute)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(nsDOMAttribute)
|
||||
NS_IMPL_RELEASE_WITH_DESTROY(nsDOMAttribute,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
|
||||
// nsIDOMGCParticipant methods
|
||||
nsIDOMGCParticipant*
|
||||
nsDOMAttribute::GetSCCIndex()
|
||||
{
|
||||
nsIContent *owner = GetContentInternal();
|
||||
|
||||
return owner ? owner->GetSCCIndex() : this;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMAttribute::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
NS_ASSERTION(GetContentInternal() == nsnull,
|
||||
"shouldn't be an SCC index if we're in an element");
|
||||
|
||||
// This node is the root of a subtree that's been removed from the
|
||||
// document (since AppendReachableList is only called on SCC index
|
||||
// nodes). The document is reachable from it (through
|
||||
// .ownerDocument), but it's not reachable from the document.
|
||||
aArray.AppendObject(GetOwnerDoc());
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDOMAttribute, nsIDOMAttr)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(nsDOMAttribute, nsIDOMAttr,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
|
||||
void
|
||||
nsDOMAttribute::SetMap(nsDOMAttributeMap *aMap)
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "nsIDOM3Node.h"
|
||||
#include "nsIDOM3Attr.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsDOMAttribute;
|
||||
|
||||
|
@ -66,11 +67,7 @@ public:
|
|||
nsDOMAttribute(nsDOMAttributeMap* aAttrMap, nsINodeInfo *aNodeInfo,
|
||||
const nsAString& aValue);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIDOMGCParticipant interface methods
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
// nsIDOMNode interface
|
||||
NS_DECL_NSIDOMNODE
|
||||
|
@ -108,6 +105,8 @@ public:
|
|||
static void Initialize();
|
||||
static void Shutdown();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMAttribute)
|
||||
|
||||
protected:
|
||||
static PRBool sInitialized;
|
||||
|
||||
|
|
|
@ -90,16 +90,40 @@ nsDOMAttributeMap::DropReference()
|
|||
mContent = nsnull;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap, nsIDOMNamedNodeMap)
|
||||
tmp->DropReference();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
PLDHashOperator
|
||||
TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsCOMPtr<nsIDOMNode>& aData, void* aUserArg)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, aUserArg);
|
||||
|
||||
if (aData.get())
|
||||
cb->NoteXPCOMChild(aData.get());
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap, nsIDOMNamedNodeMap)
|
||||
tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
// QueryInterface implementation for nsDOMAttributeMap
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMAttributeMap)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNamedNodeMap)
|
||||
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NamedNodeMap)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsDOMAttributeMap)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsDOMAttributeMap)
|
||||
NS_IMPL_RELEASE(nsDOMAttributeMap)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap)
|
||||
|
||||
PLDHashOperator
|
||||
SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey, nsCOMPtr<nsIDOMNode>& aData,
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "nsIDOMNamedNodeMap.h"
|
||||
#include "nsString.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIAtom;
|
||||
class nsIContent;
|
||||
|
@ -126,7 +127,7 @@ public:
|
|||
*/
|
||||
PRBool Init();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
// nsIDOMNamedNodeMap interface
|
||||
NS_DECL_NSIDOMNAMEDNODEMAP
|
||||
|
@ -169,6 +170,8 @@ public:
|
|||
*/
|
||||
PRUint32 Enumerate(AttrCache::EnumReadFunction aFunc, void *aUserArg) const;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
|
||||
|
||||
private:
|
||||
nsIContent* mContent; // Weak reference
|
||||
|
||||
|
|
|
@ -752,6 +752,7 @@ nsDocument::~nsDocument()
|
|||
|
||||
delete mHeaderData;
|
||||
delete mBoxObjectTable;
|
||||
delete mContentWrapperHash;
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
|
@ -767,6 +768,8 @@ nsDocument::LastRelease()
|
|||
delete this;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsDocument)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDocument)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMDocument)
|
||||
|
@ -781,7 +784,6 @@ NS_INTERFACE_MAP_BEGIN(nsDocument)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentXBL)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
|
||||
|
@ -792,6 +794,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocument)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsDocument)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocument)
|
||||
if (aIID.Equals(NS_GET_IID(nsIDOMXPathEvaluator)) ||
|
||||
aIID.Equals(NS_GET_IID(nsIXPathEvaluatorInternal))) {
|
||||
|
@ -808,8 +811,72 @@ NS_INTERFACE_MAP_BEGIN(nsDocument)
|
|||
else
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsDocument)
|
||||
NS_IMPL_RELEASE_WITH_DESTROY(nsDocument, LastRelease())
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDocument, nsIDocument)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS_WITH_DESTROY(nsDocument,
|
||||
nsIDocument,
|
||||
LastRelease())
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument, nsIDocument)
|
||||
// Traverse the mChildren nsAttrAndChildArray.
|
||||
for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) {
|
||||
cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
|
||||
}
|
||||
|
||||
// Traverse all nsIDocument nsCOMPtrs.
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBindingManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
|
||||
|
||||
// Traverse all nsDocument nsCOMPtrs.
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptGlobalObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMStyleSheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStyleAttrStyleSheet)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptEventManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
|
||||
|
||||
// Traverse all our nsCOMArrays.
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs)
|
||||
|
||||
// Traverse any associated preserved wrapper.
|
||||
{
|
||||
nsISupports *preservedWrapper = tmp->GetReference(tmp);
|
||||
if (preservedWrapper)
|
||||
cb.NoteXPCOMChild(preservedWrapper);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument, nsIDocument)
|
||||
// Unlink the mChildren nsAttrAndChildArray.
|
||||
for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1;
|
||||
indx >= 0; --indx) {
|
||||
tmp->mChildren.ChildAt(indx)->UnbindFromTree();
|
||||
tmp->mChildren.RemoveChildAt(indx);
|
||||
}
|
||||
|
||||
// Unlink any associated preserved wrapper.
|
||||
tmp->RemoveReference(tmp);
|
||||
|
||||
tmp->mParentDocument = nsnull;
|
||||
tmp->mRootContent = nsnull;
|
||||
|
||||
// nsDocument has a pretty complex destructor, so we're going to
|
||||
// assume that *most* cycles you actually want to break somewhere
|
||||
// else, and not unlink an awful lot here.
|
||||
//
|
||||
// In rare cases where you think an unlink will help here, add one
|
||||
// manually.
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
nsresult
|
||||
nsDocument::Init()
|
||||
|
@ -3347,23 +3414,6 @@ nsDocument::SetDir(const nsAString& aDirection)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// nsIDOMGCParticipant methods
|
||||
//
|
||||
nsIDOMGCParticipant*
|
||||
nsDocument::GetSCCIndex()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> gcp = do_QueryInterface(mScriptGlobalObject);
|
||||
if (gcp)
|
||||
aArray.AppendObject(gcp);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// nsIDOMNode methods
|
||||
|
@ -4374,6 +4424,46 @@ nsDocument::FlushPendingNotifications(mozFlushType aType)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::AddReference(void *aKey, nsISupports *aReference)
|
||||
{
|
||||
if (mScriptGlobalObject) {
|
||||
if (!mContentWrapperHash) {
|
||||
mContentWrapperHash = new nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports>;
|
||||
if (mContentWrapperHash) {
|
||||
mContentWrapperHash->Init(10);
|
||||
}
|
||||
}
|
||||
|
||||
if (mContentWrapperHash)
|
||||
mContentWrapperHash->Put(aKey, aReference);
|
||||
}
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
nsDocument::GetReference(void *aKey)
|
||||
{
|
||||
// NB: This method is part of content cycle collection,
|
||||
// and must *not* Addref its return value.
|
||||
|
||||
if (mContentWrapperHash)
|
||||
return mContentWrapperHash->GetWeak(aKey);
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
nsDocument::RemoveReference(void *aKey)
|
||||
{
|
||||
nsISupports* oldReference = nsnull;
|
||||
|
||||
if (mContentWrapperHash) {
|
||||
mContentWrapperHash->Get(aKey, &oldReference);
|
||||
mContentWrapperHash->Remove(aKey);
|
||||
}
|
||||
|
||||
return oldReference;
|
||||
}
|
||||
|
||||
nsIScriptEventManager*
|
||||
nsDocument::GetScriptEventManager()
|
||||
{
|
||||
|
@ -5007,6 +5097,8 @@ nsDocument::Destroy()
|
|||
mLayoutHistoryState = nsnull;
|
||||
|
||||
nsContentList::OnDocumentDestroy(this);
|
||||
delete mContentWrapperHash;
|
||||
mContentWrapperHash = nsnull;
|
||||
}
|
||||
|
||||
already_AddRefed<nsILayoutHistoryState>
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
#include "nsTObserverArray.h"
|
||||
#include "nsStubMutationObserver.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
// Put these here so all document impls get them automatically
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
|
@ -295,7 +296,7 @@ class nsDocument : public nsIDocument,
|
|||
public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup);
|
||||
virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||
|
@ -493,6 +494,9 @@ public:
|
|||
nsIStyleRule* aStyleRule);
|
||||
|
||||
virtual void FlushPendingNotifications(mozFlushType aType);
|
||||
virtual void AddReference(void *aKey, nsISupports *aReference);
|
||||
virtual nsISupports *GetReference(void *aKey);
|
||||
virtual already_AddRefed<nsISupports> RemoveReference(void *aKey);
|
||||
virtual nsIScriptEventManager* GetScriptEventManager();
|
||||
virtual void SetXMLDeclaration(const PRUnichar *aVersion,
|
||||
const PRUnichar *aEncoding,
|
||||
|
@ -548,10 +552,6 @@ public:
|
|||
nsresult GetRadioGroup(const nsAString& aName,
|
||||
nsRadioGroupStruct **aRadioGroup);
|
||||
|
||||
// nsIDOMGCParticipant interface methods
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
// nsIDOMNode
|
||||
NS_DECL_NSIDOMNODE
|
||||
|
||||
|
@ -643,6 +643,8 @@ public:
|
|||
|
||||
NS_HIDDEN_(void) ClearBoxObjectFor(nsIContent* aContent);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsDocument)
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
|
@ -749,6 +751,7 @@ protected:
|
|||
PRUint8 mDefaultElementType;
|
||||
|
||||
nsInterfaceHashtable<nsISupportsHashKey, nsPIBoxObject> *mBoxObjectTable;
|
||||
nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports> *mContentWrapperHash;
|
||||
|
||||
// The channel that got passed to StartDocumentLoad(), if any
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
|
|
|
@ -200,13 +200,9 @@ nsDocumentFragment::IsNodeOfType(PRUint32 aFlags) const
|
|||
NS_INTERFACE_MAP_BEGIN(nsDocumentFragment)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentFragment)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DocumentFragment)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsGenericElement)
|
||||
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsDocumentFragment, nsGenericElement)
|
||||
|
|
|
@ -76,14 +76,20 @@ nsGenericDOMDataNode::~nsGenericDOMDataNode()
|
|||
"Please remove this from the document properly");
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericDOMDataNode)
|
||||
|
||||
NS_IMPL_ADDREF(nsGenericDOMDataNode)
|
||||
NS_IMPL_RELEASE_WITH_DESTROY(nsGenericDOMDataNode,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver,
|
||||
nsDOMEventRTTearoff::Create(this))
|
||||
|
@ -97,8 +103,13 @@ NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
|
|||
new nsNodeSupportsWeakRefTearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsGenericDOMDataNode)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsGenericDOMDataNode, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(nsGenericDOMDataNode, nsIContent,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
|
||||
|
||||
nsresult
|
||||
nsGenericDOMDataNode::GetNodeValue(nsAString& aNodeValue)
|
||||
|
@ -526,38 +537,6 @@ nsGenericDOMDataNode::ToCString(nsAString& aBuf, PRInt32 aOffset,
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* See comment for nsGenericElement::GetSCCIndex
|
||||
*/
|
||||
nsIDOMGCParticipant*
|
||||
nsGenericDOMDataNode::GetSCCIndex()
|
||||
{
|
||||
// This is an optimized way of walking nsIDOMNode::GetParentNode to
|
||||
// the top of the tree.
|
||||
nsINode *top = GetCurrentDoc();
|
||||
if (!top) {
|
||||
top = this;
|
||||
nsINode *parent;
|
||||
while ((parent = top->GetNodeParent())) {
|
||||
top = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericDOMDataNode::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
NS_ASSERTION(GetCurrentDoc() == nsnull,
|
||||
"shouldn't be an SCC index if we're in a doc");
|
||||
|
||||
// This node is the root of a subtree that's been removed from the
|
||||
// document (since AppendReachableList is only called on SCC index
|
||||
// nodes). The document is reachable from it (through
|
||||
// .ownerDocument), but it's not reachable from the document.
|
||||
aArray.AppendObject(GetOwnerDoc());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
#include "nsDOMError.h"
|
||||
#include "nsIEventListenerManager.h"
|
||||
#include "nsGenericElement.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIDOMAttr;
|
||||
class nsIDOMEventListener;
|
||||
|
@ -63,7 +63,7 @@ class nsURI;
|
|||
class nsGenericDOMDataNode : public nsIContent
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
nsGenericDOMDataNode(nsINodeInfo *aNodeInfo);
|
||||
virtual ~nsGenericDOMDataNode();
|
||||
|
@ -168,10 +168,6 @@ public:
|
|||
nsresult ReplaceData(PRUint32 aOffset, PRUint32 aCount,
|
||||
const nsAString& aArg);
|
||||
|
||||
// nsIDOMGCParticipant interface methods
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
// nsINode methods
|
||||
virtual PRUint32 GetChildCount() const;
|
||||
virtual nsIContent *GetChildAt(PRUint32 aIndex) const;
|
||||
|
@ -263,6 +259,8 @@ public:
|
|||
void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const;
|
||||
#endif
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsGenericDOMDataNode)
|
||||
|
||||
protected:
|
||||
/**
|
||||
* There are a set of DOM- and scripting-specific instance variables
|
||||
|
|
|
@ -115,6 +115,9 @@
|
|||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsIFocusController.h"
|
||||
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
#ifdef MOZ_SVG
|
||||
PRBool NS_SVG_TestFeature(const nsAString &fstr);
|
||||
#endif /* MOZ_SVG */
|
||||
|
@ -961,46 +964,6 @@ nsGenericElement::~nsGenericElement()
|
|||
"Please remove this from the document properly");
|
||||
}
|
||||
|
||||
/**
|
||||
* During the Mark phase of the GC, we need to mark all of the preserved
|
||||
* wrappers that are reachable via DOM APIs. Since reachability for DOM
|
||||
* nodes is symmetric, if one DOM node is reachable from another via DOM
|
||||
* APIs, then they are in the same strongly connected component.
|
||||
* (Strongly connected components are never reachable from each other
|
||||
* via DOM APIs.) We can refer to each strongly connected component by
|
||||
* walking up to the top of the parent chain. This function finds that
|
||||
* root node for any DOM node.
|
||||
*/
|
||||
nsIDOMGCParticipant*
|
||||
nsGenericElement::GetSCCIndex()
|
||||
{
|
||||
// This is an optimized way of walking nsIDOMNode::GetParentNode to
|
||||
// the top of the tree.
|
||||
nsINode *top = GetCurrentDoc();
|
||||
if (!top) {
|
||||
top = this;
|
||||
nsINode *parent;
|
||||
while ((parent = top->GetNodeParent())) {
|
||||
top = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericElement::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
NS_ASSERTION(GetCurrentDoc() == nsnull,
|
||||
"shouldn't be an SCC index if we're in a doc");
|
||||
|
||||
// This node is the root of a subtree that's been removed from the
|
||||
// document (since AppendReachableList is only called on SCC index
|
||||
// nodes). The document is reachable from it (through
|
||||
// .ownerDocument), but it's not reachable from the document.
|
||||
aArray.AppendObject(GetOwnerDoc());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGenericElement::GetNodeName(nsAString& aNodeName)
|
||||
{
|
||||
|
@ -3009,9 +2972,55 @@ nsGenericElement::doRemoveChild(nsIDOMNode* aOldChild, nsIContent* aParent,
|
|||
|
||||
// nsISupports implementation
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
|
||||
// Unlink child content (and unbind our subtree).
|
||||
{
|
||||
PRUint32 i;
|
||||
PRUint32 kids = tmp->mAttrsAndChildren.ChildCount();
|
||||
for (i = kids; i > 0; i--) {
|
||||
tmp->mAttrsAndChildren.ChildAt(i-1)->UnbindFromTree();
|
||||
tmp->mAttrsAndChildren.RemoveChildAt(i-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlink any DOM slots of interest.
|
||||
{
|
||||
nsDOMSlots *slots = tmp->GetExistingDOMSlots();
|
||||
if (slots)
|
||||
slots->mAttributeMap = nsnull;
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericElement, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
|
||||
|
||||
// Traverse child content.
|
||||
{
|
||||
PRUint32 i;
|
||||
PRUint32 kids = tmp->mAttrsAndChildren.ChildCount();
|
||||
for (i = 0; i < kids; i++)
|
||||
cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
|
||||
}
|
||||
|
||||
// Traverse any DOM slots of interest.
|
||||
{
|
||||
nsDOMSlots *slots = tmp->GetExistingDOMSlots();
|
||||
if (slots) {
|
||||
if (slots->mAttributeMap.get())
|
||||
cb.NoteXPCOMChild(slots->mAttributeMap.get());
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsGenericElement)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver,
|
||||
|
@ -3025,11 +3034,13 @@ NS_INTERFACE_MAP_BEGIN(nsGenericElement)
|
|||
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
|
||||
new nsNodeSupportsWeakRefTearoff(this))
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsGenericElement)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsGenericElement)
|
||||
NS_IMPL_RELEASE_WITH_DESTROY(nsGenericElement,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsGenericElement, nsIContent)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS_WITH_DESTROY(nsGenericElement,
|
||||
nsIContent,
|
||||
nsNodeUtils::LastRelease(this, PR_TRUE))
|
||||
|
||||
nsresult
|
||||
nsGenericElement::PostQueryInterface(REFNSIID aIID, void** aInstancePtr)
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include "mozFlushType.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsIWeakReference.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIDOMAttr;
|
||||
class nsIDOMEventListener;
|
||||
|
@ -352,7 +353,7 @@ public:
|
|||
nsGenericElement(nsINodeInfo *aNodeInfo);
|
||||
virtual ~nsGenericElement();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
/**
|
||||
* Called during QueryInterface to give the binding manager a chance to
|
||||
|
@ -360,10 +361,6 @@ public:
|
|||
*/
|
||||
nsresult PostQueryInterface(REFNSIID aIID, void** aInstancePtr);
|
||||
|
||||
// nsIDOMGCParticipant interface methods
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
// nsINode interface methods
|
||||
virtual PRUint32 GetChildCount() const;
|
||||
virtual nsIContent *GetChildAt(PRUint32 aIndex) const;
|
||||
|
@ -775,6 +772,8 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsGenericElement)
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Set attribute and (if needed) notify documentobservers and fire off
|
||||
|
|
|
@ -102,7 +102,6 @@ static void PrintReqURL(imgIRequest* req) {
|
|||
nsImageLoadingContent::nsImageLoadingContent()
|
||||
: mObserverList(nsnull),
|
||||
mImageBlockingStatus(nsIContentPolicy::ACCEPT),
|
||||
mRootRefCount(0),
|
||||
mLoadingEnabled(PR_TRUE),
|
||||
mStartingLoad(PR_FALSE),
|
||||
mLoading(PR_FALSE),
|
||||
|
@ -128,19 +127,6 @@ nsImageLoadingContent::DestroyImageLoadingContent()
|
|||
mPendingRequest->Cancel(NS_ERROR_FAILURE);
|
||||
mPendingRequest = nsnull;
|
||||
}
|
||||
|
||||
// This can actually fire for multipart/x-mixed-replace, since if the
|
||||
// load is canceled between parts (e.g., by cancelling the load
|
||||
// group), we won't get any notification. See bug 321054 comment 31
|
||||
// and bug 339610. *If* that multipart/x-mixed-replace image has
|
||||
// event handlers, we won't even get to this warning; we'll leak
|
||||
// instead.
|
||||
NS_WARN_IF_FALSE(mRootRefCount == 0,
|
||||
"unbalanced handler preservation refcount");
|
||||
if (mRootRefCount != 0) {
|
||||
mRootRefCount = 1;
|
||||
UnpreserveLoadHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
nsImageLoadingContent::~nsImageLoadingContent()
|
||||
|
@ -279,9 +265,6 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
|
|||
{
|
||||
LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
|
||||
|
||||
if (aLastPart)
|
||||
UnpreserveLoadHandlers();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -453,8 +436,6 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PreserveLoadHandlers();
|
||||
|
||||
// Null out our mCurrentURI, in case we have no image requests right now.
|
||||
mCurrentURI = nsnull;
|
||||
|
||||
|
@ -469,9 +450,6 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
|
|||
// Make sure our state is up to date
|
||||
UpdateImageState(PR_TRUE);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
UnpreserveLoadHandlers();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -589,8 +567,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PreserveLoadHandlers();
|
||||
|
||||
nsCOMPtr<imgIRequest> & req = mCurrentRequest ? mPendingRequest : mCurrentRequest;
|
||||
|
||||
rv = nsContentUtils::LoadImage(aNewURI, aDocument,
|
||||
|
@ -599,7 +575,6 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
|
|||
getter_AddRefs(req));
|
||||
if (NS_FAILED(rv)) {
|
||||
FireEvent(NS_LITERAL_STRING("error"));
|
||||
UnpreserveLoadHandlers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -740,7 +715,6 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest,
|
|||
NS_PRECONDITION(aRequest, "Must have a request here!");
|
||||
AutoStateChanger changer(this, aNotify);
|
||||
mCurrentURI = nsnull;
|
||||
PreserveLoadHandlers();
|
||||
CancelImageRequests(NS_BINDING_ABORTED, PR_TRUE, nsIContentPolicy::ACCEPT);
|
||||
|
||||
NS_ASSERTION(!mCurrentRequest, "We should not have a current request now");
|
||||
|
@ -800,7 +774,6 @@ public:
|
|||
~Event()
|
||||
{
|
||||
mDocument->UnblockOnload(PR_TRUE);
|
||||
mContent->UnpreserveLoadHandlers();
|
||||
}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
@ -860,39 +833,7 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType)
|
|||
// Block onload for our event. Since we unblock in the event destructor, we
|
||||
// want to block now, even if posting will fail.
|
||||
document->BlockOnload();
|
||||
PreserveLoadHandlers();
|
||||
|
||||
return NS_DispatchToCurrentThread(evt);
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::PreserveLoadHandlers()
|
||||
{
|
||||
++mRootRefCount;
|
||||
NS_LOG_ADDREF(&mRootRefCount, mRootRefCount,
|
||||
"nsImageLoadingContent::mRootRefCount", sizeof(mRootRefCount));
|
||||
if (mRootRefCount == 1) {
|
||||
nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(this);
|
||||
nsresult rv = nsDOMClassInfo::SetExternallyReferenced(part);
|
||||
// The worst that will happen if we ignore this failure is that
|
||||
// onload or onerror will fail to fire. I suppose we could fire
|
||||
// onerror now as a result of that, but the only reason it would
|
||||
// actually fail is out-of-memory, and it seems silly to bother and
|
||||
// unlikely to work in that case.
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "ignoring failure to root participant");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::UnpreserveLoadHandlers()
|
||||
{
|
||||
NS_ASSERTION(mRootRefCount != 0,
|
||||
"load handler preservation refcount underflow");
|
||||
--mRootRefCount;
|
||||
NS_LOG_RELEASE(&mRootRefCount, mRootRefCount,
|
||||
"nsImageLoadingContent::mRootRefCount");
|
||||
if (mRootRefCount == 0) {
|
||||
nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(this);
|
||||
nsDOMClassInfo::UnsetExternallyReferenced(part);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,15 +238,6 @@ private:
|
|||
class Event;
|
||||
friend class Event;
|
||||
|
||||
/**
|
||||
* Manage the rooting and un-rooting in nsDOMClassInfo of the content
|
||||
* node, so that things reachable from the node are protected from
|
||||
* garbage collection while the onload or onerror handlers (which can
|
||||
* make it reachable again) could fire.
|
||||
*/
|
||||
void PreserveLoadHandlers();
|
||||
void UnpreserveLoadHandlers();
|
||||
|
||||
/* MEMBERS */
|
||||
protected:
|
||||
nsCOMPtr<imgIRequest> mCurrentRequest;
|
||||
|
@ -265,12 +256,6 @@ private:
|
|||
ImageObserver mObserverList;
|
||||
|
||||
PRInt16 mImageBlockingStatus;
|
||||
// This counts the number of operations that we're currently doing
|
||||
// that require us to root in nsDOMClassInfo to say that there is
|
||||
// currently network or other activity that could trigger onload or
|
||||
// onerror handlers. The number of things a single node can do at
|
||||
// once is quite limited, so a PRUint8 should be quite sufficient.
|
||||
PRUint8 mRootRefCount;
|
||||
PRPackedBool mLoadingEnabled : 1;
|
||||
PRPackedBool mStartingLoad : 1;
|
||||
|
||||
|
|
|
@ -86,12 +86,11 @@ nsTreeWalker::nsTreeWalker(nsINode *aRoot,
|
|||
PRBool aExpandEntityReferences) :
|
||||
mRoot(aRoot),
|
||||
mWhatToShow(aWhatToShow),
|
||||
mFilter(aFilter),
|
||||
mExpandEntityReferences(aExpandEntityReferences),
|
||||
mCurrentNode(aRoot),
|
||||
mPossibleIndexesPos(-1)
|
||||
{
|
||||
mFilter.Set(aFilter, this);
|
||||
|
||||
NS_ASSERTION(aRoot, "invalid root in call to nsTreeWalker constructor");
|
||||
}
|
||||
|
||||
|
@ -107,7 +106,6 @@ nsTreeWalker::~nsTreeWalker()
|
|||
// QueryInterface implementation for nsTreeWalker
|
||||
NS_INTERFACE_MAP_BEGIN(nsTreeWalker)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMTreeWalker)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMTreeWalker)
|
||||
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(TreeWalker)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
@ -143,7 +141,7 @@ NS_IMETHODIMP nsTreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
|
|||
{
|
||||
NS_ENSURE_ARG_POINTER(aFilter);
|
||||
|
||||
nsCOMPtr<nsIDOMNodeFilter> filter = mFilter.Get();
|
||||
nsCOMPtr<nsIDOMNodeFilter> filter = mFilter;
|
||||
filter.swap((*aFilter = nsnull));
|
||||
|
||||
return NS_OK;
|
||||
|
@ -307,28 +305,6 @@ NS_IMETHODIMP nsTreeWalker::NextNode(nsIDOMNode **_retval)
|
|||
return result ? CallQueryInterface(result, _retval) : NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* nsIDOMGCParticipant functions
|
||||
*/
|
||||
/* virtual */ nsIDOMGCParticipant*
|
||||
nsTreeWalker::GetSCCIndex()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsTreeWalker::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> gcp;
|
||||
|
||||
gcp = do_QueryInterface(mRoot);
|
||||
if (gcp)
|
||||
aArray.AppendObject(gcp);
|
||||
|
||||
gcp = do_QueryInterface(mCurrentNode);
|
||||
if (gcp)
|
||||
aArray.AppendObject(gcp);
|
||||
}
|
||||
|
||||
/*
|
||||
* nsTreeWalker helper functions
|
||||
|
@ -635,13 +611,12 @@ nsresult nsTreeWalker::TestNode(nsINode* aNode, PRInt16* _filtered)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNodeFilter> filter = mFilter.Get();
|
||||
if (filter) {
|
||||
if (mFilter) {
|
||||
if (!domNode) {
|
||||
domNode = do_QueryInterface(aNode);
|
||||
}
|
||||
|
||||
return filter->AcceptNode(domNode, _filtered);
|
||||
return mFilter->AcceptNode(domNode, _filtered);
|
||||
}
|
||||
|
||||
*_filtered = nsIDOMNodeFilter::FILTER_ACCEPT;
|
||||
|
|
|
@ -47,23 +47,18 @@
|
|||
#include "nsIDOMTreeWalker.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsVoidArray.h"
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
class nsINode;
|
||||
class nsIDOMNode;
|
||||
class nsIDOMNodeFilter;
|
||||
|
||||
class nsTreeWalker : public nsIDOMTreeWalker, public nsIDOMGCParticipant
|
||||
class nsTreeWalker : public nsIDOMTreeWalker
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMTREEWALKER
|
||||
|
||||
// nsIDOMGCParticipant
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
nsTreeWalker(nsINode *aRoot,
|
||||
PRUint32 aWhatToShow,
|
||||
nsIDOMNodeFilter *aFilter,
|
||||
|
@ -73,7 +68,7 @@ public:
|
|||
private:
|
||||
nsCOMPtr<nsINode> mRoot;
|
||||
PRUint32 mWhatToShow;
|
||||
nsMarkedJSFunctionHolder<nsIDOMNodeFilter> mFilter;
|
||||
nsCOMPtr<nsIDOMNodeFilter> mFilter;
|
||||
PRBool mExpandEntityReferences;
|
||||
nsCOMPtr<nsINode> mCurrentNode;
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#include "nsCOMArray.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsIScriptableUConv.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
#define LOAD_STR "load"
|
||||
#define ERROR_STR "error"
|
||||
|
@ -287,6 +288,57 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
|
|||
ClearEventListeners();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequest, nsIXMLHttpRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mLoadEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mErrorEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mProgressEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mUploadProgressEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mReadystatechangeEventListeners);
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUploadProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnReadystatechangeListener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXMLParserStreamListener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mProgressEventSink)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequest, nsIXMLHttpRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mLoadEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mErrorEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mProgressEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mUploadProgressEventListeners);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mReadystatechangeEventListeners);
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUploadProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnReadystatechangeListener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXMLParserStreamListener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannelEventSink)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mProgressEventSink)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
// QueryInterface implementation for nsXMLHttpRequest
|
||||
NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequest)
|
||||
|
@ -300,14 +352,14 @@ NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequest)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLHttpRequest)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsXMLHttpRequest)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(nsXMLHttpRequest)
|
||||
NS_IMPL_RELEASE(nsXMLHttpRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsXMLHttpRequest, nsIXMLHttpRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsXMLHttpRequest, nsIXMLHttpRequest)
|
||||
|
||||
|
||||
/* void addEventListener (in string type, in nsIDOMEventListener
|
||||
|
@ -319,7 +371,7 @@ nsXMLHttpRequest::AddEventListener(const nsAString& type,
|
|||
{
|
||||
NS_ENSURE_ARG(listener);
|
||||
|
||||
nsTArray<ListenerHolder*> *array;
|
||||
nsCOMArray<nsIDOMEventListener> *array;
|
||||
|
||||
#define IMPL_ADD_LISTENER(_type, _member) \
|
||||
if (type.EqualsLiteral(_type)) { \
|
||||
|
@ -335,10 +387,7 @@ nsXMLHttpRequest::AddEventListener(const nsAString& type,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ListenerHolder *holder = new ListenerHolder;
|
||||
NS_ENSURE_TRUE(holder, NS_ERROR_OUT_OF_MEMORY);
|
||||
holder->Set(listener, this);
|
||||
array->AppendElement(holder);
|
||||
array->AppendObject(listener);
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -356,7 +405,7 @@ nsXMLHttpRequest::RemoveEventListener(const nsAString & type,
|
|||
{
|
||||
NS_ENSURE_ARG(listener);
|
||||
|
||||
nsTArray<ListenerHolder*> *array;
|
||||
nsCOMArray<nsIDOMEventListener> *array;
|
||||
#define IMPL_REMOVE_LISTENER(_type, _member) \
|
||||
if (type.EqualsLiteral(_type)) { \
|
||||
array = &(_member); \
|
||||
|
@ -372,11 +421,9 @@ nsXMLHttpRequest::RemoveEventListener(const nsAString & type,
|
|||
}
|
||||
|
||||
// Allow a caller to remove O(N^2) behavior by removing end-to-start.
|
||||
for (PRUint32 i = array->Length() - 1; i != PRUint32(-1); --i) {
|
||||
ListenerHolder *holder = array->ElementAt(i);
|
||||
if (nsCOMPtr<nsIDOMEventListener>(holder->Get()) == listener) {
|
||||
array->RemoveElementAt(i);
|
||||
delete holder;
|
||||
for (PRUint32 i = array->Count() - 1; i != PRUint32(-1); --i) {
|
||||
if (array->ObjectAt(i) == listener) {
|
||||
array->RemoveObjectAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +446,7 @@ nsXMLHttpRequest::GetOnreadystatechange(nsIDOMEventListener * *aOnreadystatechan
|
|||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnreadystatechange);
|
||||
|
||||
mOnReadystatechangeListener.Get(aOnreadystatechange);
|
||||
NS_IF_ADDREF(*aOnreadystatechange = mOnReadystatechangeListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -407,7 +454,7 @@ nsXMLHttpRequest::GetOnreadystatechange(nsIDOMEventListener * *aOnreadystatechan
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::SetOnreadystatechange(nsIDOMEventListener * aOnreadystatechange)
|
||||
{
|
||||
mOnReadystatechangeListener.Set(aOnreadystatechange, this);
|
||||
mOnReadystatechangeListener = aOnreadystatechange;
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -421,7 +468,7 @@ nsXMLHttpRequest::GetOnload(nsIDOMEventListener * *aOnLoad)
|
|||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnLoad);
|
||||
|
||||
mOnLoadListener.Get(aOnLoad);
|
||||
NS_IF_ADDREF(*aOnLoad = mOnLoadListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -429,7 +476,7 @@ nsXMLHttpRequest::GetOnload(nsIDOMEventListener * *aOnLoad)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::SetOnload(nsIDOMEventListener * aOnLoad)
|
||||
{
|
||||
mOnLoadListener.Set(aOnLoad, this);
|
||||
mOnLoadListener = aOnLoad;
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -442,7 +489,7 @@ nsXMLHttpRequest::GetOnerror(nsIDOMEventListener * *aOnerror)
|
|||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnerror);
|
||||
|
||||
mOnErrorListener.Get(aOnerror);
|
||||
NS_IF_ADDREF(*aOnerror = mOnErrorListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -450,7 +497,7 @@ nsXMLHttpRequest::GetOnerror(nsIDOMEventListener * *aOnerror)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::SetOnerror(nsIDOMEventListener * aOnerror)
|
||||
{
|
||||
mOnErrorListener.Set(aOnerror, this);
|
||||
mOnErrorListener = aOnerror;
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -461,9 +508,9 @@ nsXMLHttpRequest::SetOnerror(nsIDOMEventListener * aOnerror)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::GetOnprogress(nsIDOMEventListener * *aOnprogress)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnprogress);
|
||||
NS_ENSURE_ARG_POINTER(aOnprogress);
|
||||
|
||||
mOnProgressListener.Get(aOnprogress);
|
||||
NS_IF_ADDREF(*aOnprogress = mOnProgressListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -471,7 +518,7 @@ nsXMLHttpRequest::GetOnprogress(nsIDOMEventListener * *aOnprogress)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::SetOnprogress(nsIDOMEventListener * aOnprogress)
|
||||
{
|
||||
mOnProgressListener.Set(aOnprogress, this);
|
||||
mOnProgressListener = aOnprogress;
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -482,9 +529,9 @@ nsXMLHttpRequest::SetOnprogress(nsIDOMEventListener * aOnprogress)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::GetOnuploadprogress(nsIDOMEventListener * *aOnuploadprogress)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOnuploadprogress);
|
||||
NS_ENSURE_ARG_POINTER(aOnuploadprogress);
|
||||
|
||||
mOnUploadProgressListener.Get(aOnuploadprogress);
|
||||
NS_IF_ADDREF(*aOnuploadprogress = mOnUploadProgressListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -492,7 +539,7 @@ nsXMLHttpRequest::GetOnuploadprogress(nsIDOMEventListener * *aOnuploadprogress)
|
|||
NS_IMETHODIMP
|
||||
nsXMLHttpRequest::SetOnuploadprogress(nsIDOMEventListener * aOnuploadprogress)
|
||||
{
|
||||
mOnUploadProgressListener.Set(aOnuploadprogress, this);
|
||||
mOnUploadProgressListener = aOnuploadprogress;
|
||||
|
||||
mScriptContext = GetCurrentContext();
|
||||
|
||||
|
@ -504,8 +551,7 @@ NS_IMETHODIMP
|
|||
nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
*aChannel = mChannel;
|
||||
NS_IF_ADDREF(*aChannel);
|
||||
NS_IF_ADDREF(*aChannel = mChannel);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -848,18 +894,15 @@ nsXMLHttpRequest::CreateEvent(const nsAString& aType, nsIDOMEvent** aDOMEvent)
|
|||
}
|
||||
|
||||
void
|
||||
nsXMLHttpRequest::CopyEventListeners(ListenerHolder& aListener,
|
||||
const nsTArray<ListenerHolder*>& aListenerArray,
|
||||
nsXMLHttpRequest::CopyEventListeners(nsCOMPtr<nsIDOMEventListener>& aListener,
|
||||
const nsCOMArray<nsIDOMEventListener>& aListenerArray,
|
||||
nsCOMArray<nsIDOMEventListener>& aCopy)
|
||||
{
|
||||
NS_PRECONDITION(aCopy.Count() == 0, "aCopy should start off empty");
|
||||
nsCOMPtr<nsIDOMEventListener> listener = aListener.Get();
|
||||
if (listener)
|
||||
aCopy.AppendObject(listener);
|
||||
if (aListener)
|
||||
aCopy.AppendObject(aListener);
|
||||
|
||||
PRUint32 count = aListenerArray.Length();
|
||||
for (PRUint32 i = 0; i < count; ++i)
|
||||
aCopy.AppendObject(nsCOMPtr<nsIDOMEventListener>(aListenerArray[i]->Get()));
|
||||
aCopy.AppendObjects(aListenerArray);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -904,33 +947,25 @@ void
|
|||
nsXMLHttpRequest::ClearEventListeners()
|
||||
{
|
||||
if (mState & XML_HTTP_REQUEST_ROOTED) {
|
||||
nsDOMClassInfo::UnsetExternallyReferenced(this);
|
||||
mState &= ~XML_HTTP_REQUEST_ROOTED;
|
||||
}
|
||||
|
||||
// This isn't *really* needed anymore now that we use
|
||||
// nsMarkedJSFunctionHolder, but we may as well keep it for safety
|
||||
// (against leaks) and compatibility, and also for the code to clear
|
||||
// the first listener arrays (called from the destructor).
|
||||
PRUint32 i, i_end;
|
||||
#define CLEAR_ARRAY(member_) \
|
||||
for (i = 0, i_end = (member_).Length(); i < i_end; ++i) \
|
||||
delete (member_)[i]; \
|
||||
(member_).Clear();
|
||||
// This isn't *really* needed anymore now that we use a cycle
|
||||
// collector, but we may as well keep it for safety (against leaks)
|
||||
// and compatibility, and also for the code to clear the first
|
||||
// listener arrays (called from the destructor).
|
||||
|
||||
CLEAR_ARRAY(mLoadEventListeners)
|
||||
CLEAR_ARRAY(mErrorEventListeners)
|
||||
CLEAR_ARRAY(mProgressEventListeners)
|
||||
CLEAR_ARRAY(mUploadProgressEventListeners)
|
||||
CLEAR_ARRAY(mReadystatechangeEventListeners)
|
||||
mLoadEventListeners.Clear();
|
||||
mErrorEventListeners.Clear();
|
||||
mProgressEventListeners.Clear();
|
||||
mUploadProgressEventListeners.Clear();
|
||||
mReadystatechangeEventListeners.Clear();
|
||||
|
||||
#undef CLEAR_ARRAY
|
||||
|
||||
mOnLoadListener.Set(nsnull, this);
|
||||
mOnErrorListener.Set(nsnull, this);
|
||||
mOnProgressListener.Set(nsnull, this);
|
||||
mOnUploadProgressListener.Set(nsnull, this);
|
||||
mOnReadystatechangeListener.Set(nsnull, this);
|
||||
mOnLoadListener = nsnull;
|
||||
mOnErrorListener = nsnull;
|
||||
mOnProgressListener = nsnull;
|
||||
mOnUploadProgressListener = nsnull;
|
||||
mOnReadystatechangeListener = nsnull;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIHttpChannel>
|
||||
|
@ -1025,10 +1060,10 @@ nsXMLHttpRequest::OpenRequest(const nsACString& method,
|
|||
// a progress event handler we must load with nsIRequest::LOAD_NORMAL or
|
||||
// necko won't generate any progress notifications
|
||||
nsLoadFlags loadFlags;
|
||||
if (nsCOMPtr<nsIDOMEventListener>(mOnProgressListener.Get()) ||
|
||||
nsCOMPtr<nsIDOMEventListener>(mOnUploadProgressListener.Get()) ||
|
||||
mProgressEventListeners.Length() != 0 ||
|
||||
mUploadProgressEventListeners.Length() != 0) {
|
||||
if (mOnProgressListener ||
|
||||
mOnUploadProgressListener ||
|
||||
mProgressEventListeners.Count() != 0 ||
|
||||
mUploadProgressEventListeners.Count() != 0) {
|
||||
loadFlags = nsIRequest::LOAD_NORMAL;
|
||||
} else {
|
||||
loadFlags = nsIRequest::LOAD_BACKGROUND;
|
||||
|
@ -1674,9 +1709,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
|
|||
// from being garbage collected even if this object becomes
|
||||
// unreachable from script, since they can fire as a result of our
|
||||
// reachability from the network stack.
|
||||
rv = nsDOMClassInfo::SetExternallyReferenced(this);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mState |= XML_HTTP_REQUEST_ROOTED;
|
||||
mState |= XML_HTTP_REQUEST_ROOTED;
|
||||
}
|
||||
|
||||
if (!mChannel) {
|
||||
|
@ -2070,23 +2103,6 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
|
|||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// nsIDOMGCParticipant methods:
|
||||
//
|
||||
/* virtual */ nsIDOMGCParticipant*
|
||||
nsXMLHttpRequest::GetSCCIndex()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsXMLHttpRequest::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> gcp = do_QueryInterface(mDocument);
|
||||
if (gcp)
|
||||
aArray.AppendObject(gcp);
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#include "nsCOMArray.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
#include "nsIDOMLSProgressEvent.h"
|
||||
|
||||
|
@ -72,14 +73,13 @@ class nsXMLHttpRequest : public nsIXMLHttpRequest,
|
|||
public nsIChannelEventSink,
|
||||
public nsIProgressEventSink,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIDOMGCParticipant,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
nsXMLHttpRequest();
|
||||
virtual ~nsXMLHttpRequest();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
// nsIXMLHttpRequest
|
||||
NS_DECL_NSIXMLHTTPREQUEST
|
||||
|
@ -115,12 +115,9 @@ public:
|
|||
// nsIInterfaceRequestor
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
// nsIDOMGCParticipant
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
|
||||
|
||||
protected:
|
||||
typedef nsMarkedJSFunctionHolder<nsIDOMEventListener> ListenerHolder;
|
||||
|
||||
nsresult DetectCharset(nsACString& aCharset);
|
||||
nsresult ConvertBodyToText(nsAString& aOutBuffer);
|
||||
|
@ -148,8 +145,8 @@ protected:
|
|||
nsresult CreateEvent(const nsAString& aType, nsIDOMEvent** domevent);
|
||||
|
||||
// Make a copy of a pair of members to be passed to NotifyEventListeners.
|
||||
void CopyEventListeners(ListenerHolder& aListener,
|
||||
const nsTArray<ListenerHolder*>& aListenerArray,
|
||||
void CopyEventListeners(nsCOMPtr<nsIDOMEventListener>& aListener,
|
||||
const nsCOMArray<nsIDOMEventListener>& aListenerArray,
|
||||
nsCOMArray<nsIDOMEventListener>& aCopy);
|
||||
|
||||
// aListeners must be a "non-live" list (i.e., addEventListener and
|
||||
|
@ -165,19 +162,19 @@ protected:
|
|||
nsCOMPtr<nsIRequest> mReadRequest;
|
||||
nsCOMPtr<nsIDOMDocument> mDocument;
|
||||
|
||||
nsTArray<ListenerHolder*> mLoadEventListeners;
|
||||
nsTArray<ListenerHolder*> mErrorEventListeners;
|
||||
nsTArray<ListenerHolder*> mProgressEventListeners;
|
||||
nsTArray<ListenerHolder*> mUploadProgressEventListeners;
|
||||
nsTArray<ListenerHolder*> mReadystatechangeEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mLoadEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mErrorEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mProgressEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mUploadProgressEventListeners;
|
||||
nsCOMArray<nsIDOMEventListener> mReadystatechangeEventListeners;
|
||||
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mOnLoadListener;
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mOnErrorListener;
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mOnProgressListener;
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mOnUploadProgressListener;
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mOnReadystatechangeListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnLoadListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnErrorListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnProgressListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnUploadProgressListener;
|
||||
nsCOMPtr<nsIDOMEventListener> mOnReadystatechangeListener;
|
||||
|
||||
nsCOMPtr<nsIStreamListener> mXMLParserStreamListener;
|
||||
|
||||
|
|
|
@ -157,13 +157,6 @@ public:
|
|||
* Tells the event listener manager that its target (which owns it) is
|
||||
* no longer using it (and could go away).
|
||||
*
|
||||
* This causes the removal of all event listeners registered by this
|
||||
* instance of the listener manager. This is important for Bug 323807,
|
||||
* since nsDOMClassInfo::PreserveWrapper (and nsIDOMGCParticipant)
|
||||
* require that we remove all event listeners to remove any weak
|
||||
* references in the nsDOMClassInfo's preserved wrapper table to the
|
||||
* target.
|
||||
*
|
||||
* It also clears the weak pointer set by the call to
|
||||
* |SetListenerTarget|.
|
||||
*/
|
||||
|
|
|
@ -393,8 +393,7 @@ nsEventListenerManager::Shutdown()
|
|||
nsDOMEvent::Shutdown();
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(nsEventListenerManager)
|
||||
NS_IMPL_RELEASE(nsEventListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventListenerManager)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsEventListenerManager)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEventListenerManager)
|
||||
|
@ -402,8 +401,28 @@ NS_INTERFACE_MAP_BEGIN(nsEventListenerManager)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsEventListenerManager)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsEventListenerManager, nsIEventListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsEventListenerManager, nsIEventListenerManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEventListenerManager, nsIEventListenerManager)
|
||||
PRInt32 i, count = tmp->mListeners.Count();
|
||||
nsListenerStruct *ls;
|
||||
for (i = 0; i < count; i++) {
|
||||
ls = NS_STATIC_CAST(nsListenerStruct*, tmp->mListeners.ElementAt(i));
|
||||
if (ls && ls->mListener.get()) {
|
||||
cb.NoteXPCOMChild(ls->mListener.get());
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEventListenerManager, nsIEventListenerManager)
|
||||
tmp->Disconnect();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
const EventTypeData*
|
||||
nsEventListenerManager::GetTypeDataForIID(const nsIID& aIID)
|
||||
{
|
||||
|
@ -476,8 +495,7 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
PRInt32 count = mListeners.Count();
|
||||
for (PRInt32 i = 0; i < count; i++) {
|
||||
ls = NS_STATIC_CAST(nsListenerStruct*, mListeners.ElementAt(i));
|
||||
nsRefPtr<nsIDOMEventListener> listener = ls->mListener.Get();
|
||||
if (listener == aListener && ls->mFlags == aFlags &&
|
||||
if (ls->mListener == aListener && ls->mFlags == aFlags &&
|
||||
ls->mGroupFlags == group &&
|
||||
(EVENT_TYPE_EQUALS(ls, aType, aTypeAtom) ||
|
||||
EVENT_TYPE_DATA_EQUALS(aTypeData, ls->mTypeData))) {
|
||||
|
@ -491,9 +509,7 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
ls = new nsListenerStruct();
|
||||
NS_ENSURE_TRUE(ls, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsCOMPtr<nsIDOMGCParticipant> participant = do_QueryInterface(mTarget);
|
||||
NS_ASSERTION(participant, "must implement nsIDOMGCParticipant");
|
||||
ls->mListener.Set(aListener, participant);
|
||||
ls->mListener = aListener;
|
||||
ls->mEventType = aType;
|
||||
ls->mTypeAtom = aTypeAtom;
|
||||
ls->mFlags = aFlags;
|
||||
|
@ -597,8 +613,7 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
|
|||
PRInt32 count = mListeners.Count();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
ls = NS_STATIC_CAST(nsListenerStruct*, mListeners.ElementAt(i));
|
||||
nsRefPtr<nsIDOMEventListener> listener = ls->mListener.Get();
|
||||
if (listener == aListener &&
|
||||
if (ls->mListener == aListener &&
|
||||
ls->mGroupFlags == group &&
|
||||
((ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) &&
|
||||
(EVENT_TYPE_EQUALS(ls, aType, aUserType) ||
|
||||
|
@ -1360,9 +1375,7 @@ found:
|
|||
// Check that the phase is same in event and event listener.
|
||||
// Handle only trusted events, except when listener permits untrusted events.
|
||||
if (useTypeInterface || useGenericInterface) {
|
||||
nsRefPtr<nsIDOMEventListener> eventListener = ls->mListener.Get();
|
||||
NS_ASSERTION(eventListener, "listener wasn't preserved properly");
|
||||
if (eventListener) {
|
||||
if (ls->mListener) {
|
||||
hasListener = PR_TRUE;
|
||||
if (ls->mFlags & aFlags &&
|
||||
ls->mGroupFlags == currentGroup &&
|
||||
|
@ -1374,10 +1387,10 @@ found:
|
|||
}
|
||||
if (*aDOMEvent) {
|
||||
if (useTypeInterface) {
|
||||
DispatchToInterface(*aDOMEvent, eventListener,
|
||||
DispatchToInterface(*aDOMEvent, ls->mListener,
|
||||
dispData->method, *typeData->iid);
|
||||
} else if (useGenericInterface) {
|
||||
HandleEventSubType(ls, eventListener, *aDOMEvent,
|
||||
HandleEventSubType(ls, ls->mListener, *aDOMEvent,
|
||||
aCurrentTarget, aFlags);
|
||||
}
|
||||
}
|
||||
|
@ -1420,11 +1433,6 @@ NS_IMETHODIMP
|
|||
nsEventListenerManager::Disconnect()
|
||||
{
|
||||
mTarget = nsnull;
|
||||
|
||||
// Bug 323807: nsDOMClassInfo::PreserveWrapper (and
|
||||
// nsIDOMGCParticipant) require that we remove all event listeners now
|
||||
// to remove any weak references in the nsDOMClassInfo's preserved
|
||||
// wrapper table to the target.
|
||||
return RemoveAllListeners();
|
||||
}
|
||||
|
||||
|
|
|
@ -41,11 +41,12 @@
|
|||
#include "nsIEventListenerManager.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIDOMEventReceiver.h"
|
||||
#include "nsIDOM3EventTarget.h"
|
||||
#include "nsHashtable.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIDOMEvent;
|
||||
class nsVoidArray;
|
||||
|
@ -53,21 +54,13 @@ class nsIAtom;
|
|||
struct EventTypeData;
|
||||
|
||||
typedef struct {
|
||||
// The nsMarkedJSFunctionHolder does magic to avoid holding strong
|
||||
// references to listeners implemented in JS. Instead, it protects
|
||||
// them from garbage collection using nsDOMClassInfo::PreserveWrapper,
|
||||
// which protects the event listener from garbage collection as long
|
||||
// as it is still reachable from JS using C++ getters. (It exposes
|
||||
// reachability information to the JS GC instead of treating the C++
|
||||
// reachability information as own-in root-out, which creates roots
|
||||
// that cause reference cycles to entrain garbage.)
|
||||
nsMarkedJSFunctionHolder<nsIDOMEventListener> mListener;
|
||||
PRUint32 mEventType;
|
||||
nsCOMPtr<nsIAtom> mTypeAtom;
|
||||
PRUint16 mFlags;
|
||||
PRUint16 mGroupFlags;
|
||||
PRBool mHandlerIsString;
|
||||
const EventTypeData* mTypeData;
|
||||
nsRefPtr<nsIDOMEventListener> mListener;
|
||||
PRUint32 mEventType;
|
||||
nsCOMPtr<nsIAtom> mTypeAtom;
|
||||
PRUint16 mFlags;
|
||||
PRUint16 mGroupFlags;
|
||||
PRBool mHandlerIsString;
|
||||
const EventTypeData* mTypeData;
|
||||
} nsListenerStruct;
|
||||
|
||||
/*
|
||||
|
@ -83,7 +76,7 @@ public:
|
|||
nsEventListenerManager();
|
||||
virtual ~nsEventListenerManager();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
/**
|
||||
* Sets events listeners of all types.
|
||||
|
@ -159,6 +152,8 @@ public:
|
|||
|
||||
static void Shutdown();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsEventListenerManager)
|
||||
|
||||
protected:
|
||||
nsresult HandleEventSubType(nsListenerStruct* aListenerStruct,
|
||||
nsIDOMEventListener* aListener,
|
||||
|
|
|
@ -91,8 +91,8 @@ nsHTMLIFrameElement::~nsHTMLIFrameElement()
|
|||
}
|
||||
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameElement, nsGenericElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLIFrameElement, nsGenericElement)
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameElement,nsGenericElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLIFrameElement,nsGenericElement)
|
||||
|
||||
// QueryInterface implementation for nsHTMLIFrameElement
|
||||
NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLIFrameElement, nsGenericHTMLFrameElement)
|
||||
|
|
|
@ -281,8 +281,96 @@ SetOrRemoveObject(PLDHashTable& table, nsISupports* aKey, nsISupports* aValue)
|
|||
// Static member variable initialization
|
||||
|
||||
// Implement our nsISupports methods
|
||||
NS_IMPL_ISUPPORTS3(nsBindingManager, nsIBindingManager, nsIStyleRuleSupplier,
|
||||
nsIMutationObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager, nsIBindingManager)
|
||||
tmp->mBindingTable.Clear();
|
||||
tmp->mDocumentTable.Clear();
|
||||
tmp->mLoadingDocTable.Clear();
|
||||
|
||||
if (tmp->mContentListTable.ops)
|
||||
PL_DHashTableFinish(&(tmp->mContentListTable));
|
||||
tmp->mContentListTable.ops = nsnull;
|
||||
|
||||
if (tmp->mAnonymousNodesTable.ops)
|
||||
PL_DHashTableFinish(&(tmp->mAnonymousNodesTable));
|
||||
tmp->mAnonymousNodesTable.ops = nsnull;
|
||||
|
||||
if (tmp->mInsertionParentTable.ops)
|
||||
PL_DHashTableFinish(&(tmp->mInsertionParentTable));
|
||||
tmp->mInsertionParentTable.ops = nsnull;
|
||||
|
||||
if (tmp->mWrapperTable.ops)
|
||||
PL_DHashTableFinish(&(tmp->mWrapperTable));
|
||||
tmp->mWrapperTable.ops = nsnull;
|
||||
|
||||
tmp->mAttachedStack.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
static PLDHashOperator
|
||||
DocumentInfoHashtableTraverser(nsIURI* key,
|
||||
nsIXBLDocumentInfo* di,
|
||||
void* userArg)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, userArg);
|
||||
cb->NoteXPCOMChild(di);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
LoadingDocHashtableTraverser(nsIURI* key,
|
||||
nsIStreamListener* sl,
|
||||
void* userArg)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, userArg);
|
||||
cb->NoteXPCOMChild(sl);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
XBLBindingHashtableTraverser(nsISupports* key,
|
||||
nsXBLBinding* binding,
|
||||
void* userArg)
|
||||
{
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, userArg);
|
||||
|
||||
// XBLBindings aren't nsISupports, so we don't tell the cycle collector
|
||||
// about them explicitly. Instead, we traverse their contents directly
|
||||
// here.
|
||||
|
||||
while (binding) {
|
||||
nsISupports *c = binding->GetAnonymousContent();
|
||||
if (c)
|
||||
cb->NoteXPCOMChild(c);
|
||||
binding = binding->GetBaseBinding();
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager, nsIBindingManager)
|
||||
if (tmp->mBindingTable.IsInitialized())
|
||||
tmp->mBindingTable.EnumerateRead(&XBLBindingHashtableTraverser, &cb);
|
||||
if (tmp->mDocumentTable.IsInitialized())
|
||||
tmp->mDocumentTable.EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
|
||||
if (tmp->mLoadingDocTable.IsInitialized())
|
||||
tmp->mLoadingDocTable.EnumerateRead(&LoadingDocHashtableTraverser, &cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsBindingManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIBindingManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStyleRuleSupplier)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBindingManager)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsBindingManager)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsBindingManager, nsIBindingManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsBindingManager, nsIBindingManager)
|
||||
|
||||
// Constructors/Destructors
|
||||
nsBindingManager::nsBindingManager(void)
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsURIHashKey.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsIXPConnectWrappedJS;
|
||||
|
@ -66,7 +67,7 @@ class nsBindingManager : public nsIBindingManager,
|
|||
public nsIMutationObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIMUTATIONOBSERVER
|
||||
|
||||
nsBindingManager();
|
||||
|
@ -138,6 +139,8 @@ public:
|
|||
RuleProcessorData* aData,
|
||||
PRBool* aCutOffInheritance);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsBindingManager)
|
||||
|
||||
protected:
|
||||
nsresult GetXBLChildNodesInternal(nsIContent* aContent,
|
||||
nsIDOMNodeList** aResult,
|
||||
|
|
|
@ -104,8 +104,6 @@
|
|||
#include "prprf.h"
|
||||
#include "nsNodeUtils.h"
|
||||
|
||||
nsresult NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
|
||||
|
||||
// Helper classes
|
||||
|
||||
/***********************************************************************/
|
||||
|
@ -1178,9 +1176,8 @@ nsXBLBinding::InitClass(const nsCString& aClassName,
|
|||
if (doc) {
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> native_wrapper =
|
||||
do_QueryInterface(wrapper);
|
||||
|
||||
if (native_wrapper) {
|
||||
NS_DOMClassInfo_PreserveNodeWrapper(native_wrapper);
|
||||
doc->AddReference(mBoundElement, native_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -425,7 +425,19 @@ static PRBool IsChromeURI(nsIURI* aURI)
|
|||
}
|
||||
|
||||
/* Implementation file */
|
||||
NS_IMPL_ISUPPORTS3(nsXBLDocumentInfo, nsIXBLDocumentInfo, nsIScriptGlobalObjectOwner, nsISupportsWeakReference)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_2_AMBIGUOUS(nsXBLDocumentInfo, nsIXBLDocumentInfo, mDocument, mGlobalObject)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsXBLDocumentInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXBLDocumentInfo)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXBLDocumentInfo)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsXBLDocumentInfo)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsXBLDocumentInfo, nsIXBLDocumentInfo)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsXBLDocumentInfo, nsIXBLDocumentInfo)
|
||||
|
||||
nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
|
||||
: mDocument(aDocument),
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "nsIScriptGlobalObjectOwner.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsXBLPrototypeBinding;
|
||||
class nsObjectHashtable;
|
||||
|
@ -46,7 +47,7 @@ class nsObjectHashtable;
|
|||
class nsXBLDocumentInfo : public nsIXBLDocumentInfo, public nsIScriptGlobalObjectOwner, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
nsXBLDocumentInfo(nsIDocument* aDocument);
|
||||
virtual ~nsXBLDocumentInfo();
|
||||
|
@ -67,6 +68,8 @@ public:
|
|||
// nsIScriptGlobalObjectOwner methods
|
||||
virtual nsIScriptGlobalObject* GetScriptGlobalObject();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
PRPackedBool mScriptAccess;
|
||||
|
|
|
@ -48,8 +48,6 @@
|
|||
#include "nsIXBLDocumentInfo.h"
|
||||
#include "nsIDOMNode.h"
|
||||
|
||||
nsresult NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
|
||||
|
||||
nsresult
|
||||
nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement)
|
||||
{
|
||||
|
@ -150,9 +148,8 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
|
|||
nsIDocument* doc = aBoundElement->GetOwnerDoc();
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> nativeWrapper(do_QueryInterface(wrapper));
|
||||
if (nativeWrapper) {
|
||||
NS_DOMClassInfo_PreserveNodeWrapper(nativeWrapper);
|
||||
}
|
||||
if (nativeWrapper)
|
||||
doc->AddReference(aBoundElement, nativeWrapper);
|
||||
}
|
||||
|
||||
wrapper.swap(*aScriptObjectHolder);
|
||||
|
|
|
@ -753,6 +753,10 @@ nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
|
|||
|
||||
nsCOMPtr<nsIContent> templParent = aTemplChild->GetParent();
|
||||
nsCOMPtr<nsIContent> childPoint;
|
||||
|
||||
// We may be disconnected from our parent during cycle collection.
|
||||
if (!templParent)
|
||||
return nsnull;
|
||||
|
||||
if (aBoundElement) {
|
||||
if (templParent->NodeInfo()->Equals(nsGkAtoms::children,
|
||||
|
|
|
@ -41,16 +41,15 @@
|
|||
#define nsPIWindowRoot_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
|
||||
class nsIFocusController;
|
||||
|
||||
// a22236a5-db06-4653-94b6-c4b6068e053c
|
||||
// c18dee5a-dcf9-4391-a20c-581e769d095e
|
||||
#define NS_IWINDOWROOT_IID \
|
||||
{ 0xa22236a5, 0xdb06, 0x4653, \
|
||||
{ 0x94, 0xb6, 0xc4, 0xb6, 0x06, 0x8e, 0x05, 0x3c } }
|
||||
{ 0xc18dee5a, 0xdcf9, 0x4391, \
|
||||
{ 0xa2, 0x0c, 0x58, 0x1e, 0x76, 0x9d, 0x09, 0x5e } }
|
||||
|
||||
class nsPIWindowRoot : public nsIDOMGCParticipant {
|
||||
class nsPIWindowRoot : public nsISupports {
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID)
|
||||
|
||||
|
|
|
@ -59,26 +59,9 @@ public:
|
|||
|
||||
nsIJSEventListener(nsIScriptContext *aContext, void *aScopeObject,
|
||||
nsISupports *aTarget)
|
||||
: mContext(aContext), mScopeObject(aScopeObject), mTarget(nsnull)
|
||||
: mContext(aContext), mScopeObject(aScopeObject),
|
||||
mTarget(do_QueryInterface(aTarget))
|
||||
{
|
||||
// We keep a weak-ref to the event target to prevent cycles that prevent
|
||||
// GC from cleaning up our global in all cases. However, as this is a
|
||||
// weak-ref, we must ensure it is the identity of the event target and
|
||||
// not a "tear-off" or similar that may not live as long as we expect.
|
||||
aTarget->QueryInterface(NS_GET_IID(nsISupports),
|
||||
NS_REINTERPRET_CAST(void **, &mTarget));
|
||||
if (mTarget)
|
||||
// We keep a weak-ref, so remove the reference the QI added.
|
||||
mTarget->Release();
|
||||
else {
|
||||
NS_ERROR("Failed to get identity pointer");
|
||||
}
|
||||
// To help debug such leaks, we keep a counter of the event listeners
|
||||
// currently alive. If you change |mTarget| to a strong-ref, this never
|
||||
// hits zero (running seamonkey.)
|
||||
#ifdef NS_DEBUG
|
||||
PR_AtomicIncrement(&sNumJSEventListeners);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsIScriptContext *GetEventContext()
|
||||
|
@ -98,20 +81,13 @@ public:
|
|||
|
||||
virtual void SetEventName(nsIAtom* aName) = 0;
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
static PRInt32 sNumJSEventListeners;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~nsIJSEventListener()
|
||||
{
|
||||
#ifdef NS_DEBUG
|
||||
PR_AtomicDecrement(&sNumJSEventListeners);
|
||||
#endif
|
||||
}
|
||||
nsCOMPtr<nsIScriptContext> mContext;
|
||||
void *mScopeObject;
|
||||
nsISupports *mTarget; // weak ref.
|
||||
nsCOMPtr<nsISupports> mTarget;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID)
|
||||
|
|
|
@ -433,27 +433,20 @@ static const char kDOMStringBundleURL[] =
|
|||
// NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
|
||||
// are defined in nsIDOMClassInfo.h.
|
||||
|
||||
#define GCPARTICIPANT_SCRIPTABLE_FLAGS \
|
||||
(DOM_DEFAULT_SCRIPTABLE_FLAGS | \
|
||||
nsIXPCScriptable::WANT_FINALIZE | \
|
||||
nsIXPCScriptable::WANT_MARK)
|
||||
|
||||
#define WINDOW_SCRIPTABLE_FLAGS \
|
||||
(nsIXPCScriptable::WANT_GETPROPERTY | \
|
||||
nsIXPCScriptable::WANT_SETPROPERTY | \
|
||||
nsIXPCScriptable::WANT_PRECREATE | \
|
||||
nsIXPCScriptable::WANT_FINALIZE | \
|
||||
nsIXPCScriptable::WANT_ADDPROPERTY | \
|
||||
nsIXPCScriptable::WANT_DELPROPERTY | \
|
||||
nsIXPCScriptable::WANT_NEWENUMERATE | \
|
||||
nsIXPCScriptable::WANT_MARK | \
|
||||
nsIXPCScriptable::WANT_EQUALITY | \
|
||||
nsIXPCScriptable::WANT_OUTER_OBJECT | \
|
||||
nsIXPCScriptable::WANT_INNER_OBJECT | \
|
||||
nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
|
||||
|
||||
#define NODE_SCRIPTABLE_FLAGS \
|
||||
((GCPARTICIPANT_SCRIPTABLE_FLAGS | \
|
||||
((DOM_DEFAULT_SCRIPTABLE_FLAGS | \
|
||||
nsIXPCScriptable::WANT_GETPROPERTY | \
|
||||
nsIXPCScriptable::WANT_PRECREATE | \
|
||||
nsIXPCScriptable::WANT_ADDPROPERTY | \
|
||||
|
@ -824,8 +817,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
// DOM Traversal classes
|
||||
NS_DEFINE_CLASSINFO_DATA(TreeWalker, nsDOMGCParticipantSH,
|
||||
GCPARTICIPANT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(TreeWalker, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
// We are now trying to preserve binary compat in classinfo. No
|
||||
// more putting things in those categories up there. New entries
|
||||
|
@ -1130,7 +1123,7 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
// event listeners.
|
||||
// We really don't want any of the default flags!
|
||||
NS_DEFINE_CLASSINFO_DATA(WindowRoot, nsEventReceiverSH,
|
||||
nsIXPCScriptable::WANT_MARK)
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(DOMParser, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
@ -1139,8 +1132,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
|
||||
NS_DEFINE_CLASSINFO_DATA(XMLHttpProgressEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(XMLHttpRequest, nsDOMGCParticipantSH,
|
||||
GCPARTICIPANT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(XMLHttpRequest, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
// Define MOZ_SVG_FOREIGNOBJECT here so that when it gets switched on,
|
||||
// we preserve binary compatibility. New classes should be added
|
||||
|
@ -3671,24 +3664,6 @@ nsDOMClassInfo::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// These are declared here so we can assert that they're empty in ShutDown.
|
||||
/**
|
||||
* Every XPConnect wrapper that needs to be preserved (a wrapped native
|
||||
* with JS properties set on it or used by XBL or a wrapped JS event
|
||||
* handler function) as long as the element it wraps is reachable from
|
||||
* script (via JS or via DOM APIs accessible from JS) gets an entry in
|
||||
* this table.
|
||||
*
|
||||
* See PreservedWrapperEntry.
|
||||
*/
|
||||
static PLDHashTable sPreservedWrapperTable;
|
||||
|
||||
// See ExternallyReferencedEntry.
|
||||
static PLDHashTable sExternallyReferencedTable;
|
||||
|
||||
// See RootWhenExternallyReferencedEntry
|
||||
static PLDHashTable sRootWhenExternallyReferencedTable;
|
||||
|
||||
// static
|
||||
nsIClassInfo *
|
||||
nsDOMClassInfo::GetClassInfoInstance(nsDOMClassInfoID aID)
|
||||
|
@ -3744,24 +3719,36 @@ nsDOMClassInfo::GetClassInfoInstance(nsDOMClassInfoData* aData)
|
|||
return GET_CLEAN_CI_PTR(aData->mCachedClassInfo);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
|
||||
{
|
||||
nsISupports *native = aWrapper->Native();
|
||||
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(native));
|
||||
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
if (node) {
|
||||
nsCOMPtr<nsIDOMDocument> domdoc;
|
||||
node->GetOwnerDocument(getter_AddRefs(domdoc));
|
||||
doc = do_QueryInterface(domdoc);
|
||||
}
|
||||
|
||||
if (!doc) {
|
||||
doc = do_QueryInterface(native);
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
|
||||
doc->AddReference(content, aWrapper);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void
|
||||
nsDOMClassInfo::ShutDown()
|
||||
{
|
||||
NS_ASSERTION(sPreservedWrapperTable.ops == 0,
|
||||
"preserved wrapper table not empty at shutdown");
|
||||
NS_ASSERTION(sExternallyReferencedTable.ops == 0 ||
|
||||
sExternallyReferencedTable.entryCount == 0,
|
||||
"rooted participant table not empty at shutdown");
|
||||
NS_ASSERTION(sRootWhenExternallyReferencedTable.ops == 0,
|
||||
"root when externally referenced table not empty at shutdown");
|
||||
|
||||
if (sExternallyReferencedTable.ops &&
|
||||
sExternallyReferencedTable.entryCount == 0) {
|
||||
PL_DHashTableFinish(&sExternallyReferencedTable);
|
||||
sExternallyReferencedTable.ops = nsnull;
|
||||
}
|
||||
|
||||
if (sClassInfoData[0].u.mConstructorFptr) {
|
||||
PRUint32 i;
|
||||
|
||||
|
@ -5032,527 +5019,6 @@ nsDOMConstructor::ToString(nsAString &aResult)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The problem of how to deal with garbage collection in a system where:
|
||||
* 1. the GC runtime contains lazily-created wrappers for other data
|
||||
* structures not participating in the GC,
|
||||
* 2. where those data structures are reachable from each other in a
|
||||
* way that is reflected through the wrappers, and
|
||||
* 3. where the wrappers can carry additional information that is not
|
||||
* in the wrapped object
|
||||
* is a difficult one. XPConnect is such a system. It creates wrappers
|
||||
* around native objects that do not participate in the JS GC and which
|
||||
* are reachable from each other outside of the knowledge of the JS
|
||||
* engine (but through getters or functions that are accessible to JS
|
||||
* users) and it allows these wrappers to be decorated with JS
|
||||
* properties. It is worth noting one very important use case for our
|
||||
* toolkit: XBL, which uses JS properties on the bound element for
|
||||
* storing fields.
|
||||
*
|
||||
* For DOM nodes, compatibility with behavior in other browsers requires
|
||||
* that we preserve JS properties on wrappers across garbage collection.
|
||||
* To do this in a way that the garbage collector is still effective, we
|
||||
* have to incorporate knowledge of reachability in the underlying
|
||||
* (wrapped) data structures into the garbage collector's mark phase.
|
||||
*
|
||||
* See https://bugzilla.mozilla.org/show_bug.cgi?id=283129 for more details.
|
||||
*/
|
||||
|
||||
#if defined(DEBUG_dbaron)
|
||||
#define DEBUG_PRESERVE_WRAPPERS
|
||||
#endif
|
||||
|
||||
struct RootWhenExternallyReferencedEntry : public PLDHashEntryHdr {
|
||||
// must be first to line up with PLDHashEntryStub
|
||||
nsIDOMGCParticipant *participant;
|
||||
PRUint32 refcnt;
|
||||
};
|
||||
|
||||
struct PreservedWrapperEntry : public PLDHashEntryHdr {
|
||||
void *key; // must be first to line up with PLDHashEntryStub
|
||||
nsIXPConnectJSObjectHolder* (*keyToWrapperFunc)(void* aKey);
|
||||
nsIDOMGCParticipant *participant;
|
||||
PRBool rootWhenExternallyReferenced;
|
||||
|
||||
// See |WrapperSCCEntry::first|. Valid only during mark phase of GC.
|
||||
PreservedWrapperEntry *next;
|
||||
};
|
||||
|
||||
PR_STATIC_CALLBACK(void)
|
||||
PreservedWrapperClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
|
||||
{
|
||||
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr);
|
||||
|
||||
if (entry->rootWhenExternallyReferenced) {
|
||||
NS_ASSERTION(sRootWhenExternallyReferencedTable.ops,
|
||||
"must have been added to rwer table");
|
||||
RootWhenExternallyReferencedEntry *rwerEntry =
|
||||
NS_STATIC_CAST(RootWhenExternallyReferencedEntry*,
|
||||
PL_DHashTableOperate(&sRootWhenExternallyReferencedTable,
|
||||
entry->participant, PL_DHASH_LOOKUP));
|
||||
NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(rwerEntry),
|
||||
"boolean vs. table mismatch");
|
||||
if (PL_DHASH_ENTRY_IS_BUSY(rwerEntry) && --rwerEntry->refcnt == 0) {
|
||||
PL_DHashTableRawRemove(&sRootWhenExternallyReferencedTable, rwerEntry);
|
||||
if (sRootWhenExternallyReferencedTable.entryCount == 0) {
|
||||
PL_DHashTableFinish(&sRootWhenExternallyReferencedTable);
|
||||
sRootWhenExternallyReferencedTable.ops = nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memset(hdr, 0, table->entrySize);
|
||||
}
|
||||
|
||||
static const PLDHashTableOps sPreservedWrapperTableOps = {
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
PL_DHashGetKeyStub,
|
||||
PL_DHashVoidPtrKeyStub,
|
||||
PL_DHashMatchEntryStub,
|
||||
PL_DHashMoveEntryStub,
|
||||
PreservedWrapperClearEntry,
|
||||
PL_DHashFinalizeStub,
|
||||
nsnull
|
||||
};
|
||||
|
||||
/**
|
||||
* At the beginning of the mark phase of the GC, we sort all the
|
||||
* wrappers into their strongly connected components. We maintain this
|
||||
* information for the duration of the mark phase, during which time
|
||||
* |sWrapperSCCTable| maps the SCC roots to a list of the wrappers in
|
||||
* the SCC. This allows the mark callback for any wrapper (preserved or
|
||||
* not) to quickly mark all of the preserved wrappers in its SCC.
|
||||
*
|
||||
* Many of these roots are documents. We know that a document will be
|
||||
* marked if it is still being displayed since the document is reachable
|
||||
* by a JS property on the global object.
|
||||
*/
|
||||
static PLDHashTable sWrapperSCCTable;
|
||||
|
||||
// If we run out of memory constructing sWrapperSCCTable, its |ops|
|
||||
// member holds this value for the remainder of that GC cycle.
|
||||
#define WRAPPER_SCC_OPS_OOM_MARKER ((PLDHashTableOps*)1)
|
||||
|
||||
struct WrapperSCCEntry : public PLDHashEntryHdr {
|
||||
// This could probably be a weak pointer (which would avoid the
|
||||
// need for hash table ops), but it seems safer this way.
|
||||
nsCOMPtr<nsIDOMGCParticipant> key; // must be first to line up with PLDHashEntryStub
|
||||
|
||||
// Linked list of preserved wrappers in the strongly connected
|
||||
// component, to be traversed using |PreservedWrapperEntry::next|.
|
||||
PreservedWrapperEntry *first;
|
||||
|
||||
PRBool marked;
|
||||
|
||||
WrapperSCCEntry(nsIDOMGCParticipant *aKey)
|
||||
: key(aKey), first(nsnull), marked(PR_FALSE) {}
|
||||
};
|
||||
|
||||
PR_STATIC_CALLBACK(void)
|
||||
WrapperSCCsClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
|
||||
{
|
||||
WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, hdr);
|
||||
entry->~WrapperSCCEntry();
|
||||
memset(hdr, 0, table->entrySize);
|
||||
}
|
||||
|
||||
PR_STATIC_CALLBACK(PRBool)
|
||||
WrapperSCCsInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
const void *key)
|
||||
{
|
||||
WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, hdr);
|
||||
new (entry) WrapperSCCEntry(NS_STATIC_CAST(nsIDOMGCParticipant*,
|
||||
NS_CONST_CAST(void*, key)));
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
static const PLDHashTableOps sWrapperSCCTableOps = {
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
PL_DHashGetKeyStub,
|
||||
PL_DHashVoidPtrKeyStub,
|
||||
PL_DHashMatchEntryStub,
|
||||
PL_DHashMoveEntryStub,
|
||||
WrapperSCCsClearEntry,
|
||||
PL_DHashFinalizeStub,
|
||||
WrapperSCCsInitEntry
|
||||
};
|
||||
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsDOMClassInfo::PreserveWrapper(void *aKey,
|
||||
nsIXPConnectJSObjectHolder* (*aKeyToWrapperFunc)(void* aKey),
|
||||
nsIDOMGCParticipant *aParticipant,
|
||||
PRBool aRootWhenExternallyReferenced)
|
||||
{
|
||||
NS_PRECONDITION(aKey, "unexpected null pointer");
|
||||
NS_PRECONDITION(aKeyToWrapperFunc, "unexpected null pointer");
|
||||
NS_PRECONDITION(aParticipant, "unexpected null pointer");
|
||||
NS_ASSERTION(!sWrapperSCCTable.ops,
|
||||
"cannot change preserved wrapper table during mark phase");
|
||||
|
||||
if (!sPreservedWrapperTable.ops &&
|
||||
!PL_DHashTableInit(&sPreservedWrapperTable, &sPreservedWrapperTableOps,
|
||||
nsnull, sizeof(PreservedWrapperEntry), 16)) {
|
||||
sPreservedWrapperTable.ops = nsnull;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*,
|
||||
PL_DHashTableOperate(&sPreservedWrapperTable, aKey, PL_DHASH_ADD));
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ASSERTION(!entry->key ||
|
||||
(entry->key == aKey &&
|
||||
entry->keyToWrapperFunc == aKeyToWrapperFunc &&
|
||||
entry->participant == aParticipant),
|
||||
"preservation key already used");
|
||||
|
||||
PRBool wasExternallyReferenced = entry->rootWhenExternallyReferenced;
|
||||
entry->key = aKey;
|
||||
entry->keyToWrapperFunc = aKeyToWrapperFunc;
|
||||
entry->participant = aParticipant;
|
||||
entry->rootWhenExternallyReferenced =
|
||||
aRootWhenExternallyReferenced || wasExternallyReferenced;
|
||||
|
||||
if (aRootWhenExternallyReferenced && !wasExternallyReferenced) {
|
||||
if (!sRootWhenExternallyReferencedTable.ops &&
|
||||
!PL_DHashTableInit(&sRootWhenExternallyReferencedTable,
|
||||
PL_DHashGetStubOps(), nsnull,
|
||||
sizeof(RootWhenExternallyReferencedEntry), 16)) {
|
||||
PL_DHashTableRawRemove(&sPreservedWrapperTable, entry);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
RootWhenExternallyReferencedEntry *rwerEntry =
|
||||
NS_STATIC_CAST(RootWhenExternallyReferencedEntry*,
|
||||
PL_DHashTableOperate(&sRootWhenExternallyReferencedTable,
|
||||
aParticipant, PL_DHASH_ADD));
|
||||
if (!rwerEntry) {
|
||||
PL_DHashTableRawRemove(&sPreservedWrapperTable, entry);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
NS_ASSERTION(rwerEntry->refcnt == 0 ||
|
||||
rwerEntry->participant == aParticipant,
|
||||
"entry mismatch");
|
||||
rwerEntry->participant = aParticipant;
|
||||
++rwerEntry->refcnt;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsIXPConnectJSObjectHolder* IdentityKeyToWrapperFunc(void* aKey)
|
||||
{
|
||||
return NS_STATIC_CAST(nsIXPConnectJSObjectHolder*, aKey);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper,
|
||||
PRBool aRootWhenExternallyReferenced)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> participant =
|
||||
do_QueryInterface(aWrapper->Native());
|
||||
if (!participant)
|
||||
// nsJSContext::PreserveWrapper needs us to null-check
|
||||
return NS_OK;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIClassInfo> info = do_QueryInterface(participant);
|
||||
NS_ASSERTION(info, "Must have classinfo");
|
||||
|
||||
nsCOMPtr<nsISupports> sup;
|
||||
info->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
getter_AddRefs(sup));
|
||||
|
||||
nsCOMPtr<nsIXPCScriptable> scriptable = do_QueryInterface(sup);
|
||||
NS_ASSERTION(scriptable, "Must have scriptable");
|
||||
|
||||
PRUint32 flags;
|
||||
scriptable->GetScriptableFlags(&flags);
|
||||
NS_ASSERTION(flags & nsIXPCScriptable::WANT_FINALIZE,
|
||||
"We'll never get cleaned up!");
|
||||
#endif
|
||||
|
||||
return nsDOMClassInfo::PreserveWrapper(aWrapper, IdentityKeyToWrapperFunc,
|
||||
participant,
|
||||
aRootWhenExternallyReferenced);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsDOMClassInfo::ReleaseWrapper(void *aKey)
|
||||
{
|
||||
NS_PRECONDITION(aKey, "unexpected null pointer");
|
||||
NS_ASSERTION(!sWrapperSCCTable.ops,
|
||||
"cannot change preserved wrapper table during mark phase");
|
||||
|
||||
if (sPreservedWrapperTable.ops) {
|
||||
PL_DHashTableOperate(&sPreservedWrapperTable, aKey, PL_DHASH_REMOVE);
|
||||
if (sPreservedWrapperTable.entryCount == 0) {
|
||||
PL_DHashTableFinish(&sPreservedWrapperTable);
|
||||
sPreservedWrapperTable.ops = nsnull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MarkAllWrappersData {
|
||||
JSContext *cx;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
MarkAllWrappers(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
PRUint32 number, void *arg)
|
||||
{
|
||||
MarkAllWrappersData *data = NS_STATIC_CAST(MarkAllWrappersData*, arg);
|
||||
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr);
|
||||
|
||||
nsIXPConnectJSObjectHolder *wrapper;
|
||||
JSObject *wrapper_obj;
|
||||
if ((wrapper = entry->keyToWrapperFunc(entry->key)) &&
|
||||
NS_SUCCEEDED(wrapper->GetJSObject(&wrapper_obj)))
|
||||
JS_MarkGCThing(data->cx, wrapper_obj,
|
||||
"nsDOMClassInfo::sPreservedWrapperTable_OOM", data->arg);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsDOMClassInfo::MarkReachablePreservedWrappers(nsIDOMGCParticipant *aParticipant,
|
||||
JSContext *cx, void *arg)
|
||||
{
|
||||
// Magic value indicating we've hit out-of-memory earlier in this GC.
|
||||
if (sWrapperSCCTable.ops == WRAPPER_SCC_OPS_OOM_MARKER)
|
||||
return;
|
||||
|
||||
// The JS engine doesn't have a notification for the beginning of
|
||||
// the mark phase. However, we can determine that the mark phase
|
||||
// has begun by detecting the first mark and lazily call
|
||||
// BeginGCMark.
|
||||
if (!sWrapperSCCTable.ops && !nsDOMClassInfo::BeginGCMark(cx)) {
|
||||
// We didn't have enough memory to create the temporary data
|
||||
// structures we needed.
|
||||
sWrapperSCCTable.ops = WRAPPER_SCC_OPS_OOM_MARKER;
|
||||
|
||||
// Just mark all the preserved wrappers. It won't help the GC free
|
||||
// up memory, but there's nothing better to do.
|
||||
if (sPreservedWrapperTable.ops) {
|
||||
MarkAllWrappersData data;
|
||||
data.cx = cx;
|
||||
data.arg = arg;
|
||||
PL_DHashTableEnumerate(&sPreservedWrapperTable, MarkAllWrappers, &data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDOMGCParticipant *SCCIndex = aParticipant->GetSCCIndex();
|
||||
WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*,
|
||||
PL_DHashTableOperate(&sWrapperSCCTable, SCCIndex, PL_DHASH_LOOKUP));
|
||||
if (!PL_DHASH_ENTRY_IS_BUSY(entry) || entry->marked)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG_PRESERVE_WRAPPERS
|
||||
{
|
||||
nsAutoString nodeName;
|
||||
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(entry->key);
|
||||
if (node)
|
||||
node->GetNodeName(nodeName);
|
||||
else
|
||||
nodeName.AssignLiteral("##not-node##");
|
||||
printf(" marking entries for SCC root %p \"%s\"\n",
|
||||
NS_STATIC_CAST(void*, entry->key.get()),
|
||||
NS_ConvertUTF16toUTF8(nodeName).get());
|
||||
}
|
||||
#endif
|
||||
entry->marked = PR_TRUE;
|
||||
|
||||
// Do the reachable list first to encourage shorter call stacks
|
||||
// (perhaps slightly less recursion through JS marking).
|
||||
nsCOMArray<nsIDOMGCParticipant> reachable;
|
||||
SCCIndex->AppendReachableList(reachable);
|
||||
for (PRInt32 i = 0, i_end = reachable.Count(); i < i_end; ++i) {
|
||||
if (reachable[i])
|
||||
MarkReachablePreservedWrappers(reachable[i], cx, arg);
|
||||
}
|
||||
|
||||
for (PreservedWrapperEntry *pwe = entry->first; pwe; pwe = pwe->next) {
|
||||
nsIXPConnectJSObjectHolder *wrapper;
|
||||
JSObject *wrapper_obj;
|
||||
if ((wrapper = pwe->keyToWrapperFunc(pwe->key)) &&
|
||||
NS_SUCCEEDED(wrapper->GetJSObject(&wrapper_obj)))
|
||||
::JS_MarkGCThing(cx, wrapper_obj,
|
||||
"nsDOMClassInfo::sPreservedWrapperTable", arg);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExternallyReferencedEntry : public PLDHashEntryHdr {
|
||||
nsIDOMGCParticipant* key; // must be first to line up with PLDHashEntryStub
|
||||
};
|
||||
|
||||
/* static */ nsresult
|
||||
nsDOMClassInfo::SetExternallyReferenced(nsIDOMGCParticipant *aParticipant)
|
||||
{
|
||||
NS_PRECONDITION(aParticipant, "unexpected null pointer");
|
||||
|
||||
if (!sExternallyReferencedTable.ops &&
|
||||
!PL_DHashTableInit(&sExternallyReferencedTable, PL_DHashGetStubOps(),
|
||||
nsnull, sizeof(ExternallyReferencedEntry), 16)) {
|
||||
sExternallyReferencedTable.ops = nsnull;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
ExternallyReferencedEntry *entry = NS_STATIC_CAST(ExternallyReferencedEntry*,
|
||||
PL_DHashTableOperate(&sExternallyReferencedTable, aParticipant, PL_DHASH_ADD));
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
NS_ASSERTION(!entry->key, "participant already rooted");
|
||||
entry->key = aParticipant;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsDOMClassInfo::UnsetExternallyReferenced(nsIDOMGCParticipant *aParticipant)
|
||||
{
|
||||
NS_PRECONDITION(aParticipant, "unexpected null pointer");
|
||||
|
||||
NS_ASSERTION(sExternallyReferencedTable.ops, "unbalanced call");
|
||||
if (sExternallyReferencedTable.ops) {
|
||||
#ifdef DEBUG
|
||||
PRUint32 count = sExternallyReferencedTable.entryCount;
|
||||
#endif
|
||||
PL_DHashTableOperate(&sExternallyReferencedTable, aParticipant,
|
||||
PL_DHASH_REMOVE);
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(count == sExternallyReferencedTable.entryCount + 1,
|
||||
"unbalanced call");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Don't destroy the table when the entryCount hits zero, since that
|
||||
// is expected to happen often. Instead, destroy it at shutdown.
|
||||
}
|
||||
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
ClassifyWrapper(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
PRUint32 number, void *arg)
|
||||
{
|
||||
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr);
|
||||
|
||||
WrapperSCCEntry *SCCEntry = NS_STATIC_CAST(WrapperSCCEntry*,
|
||||
PL_DHashTableOperate(&sWrapperSCCTable, entry->participant->GetSCCIndex(),
|
||||
PL_DHASH_ADD));
|
||||
if (!SCCEntry) {
|
||||
*NS_STATIC_CAST(PRBool*, arg) = PR_TRUE;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRESERVE_WRAPPERS
|
||||
if (!SCCEntry->first) {
|
||||
nsAutoString nodeName;
|
||||
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(SCCEntry->key);
|
||||
if (node)
|
||||
node->GetNodeName(nodeName);
|
||||
else
|
||||
nodeName.AssignLiteral("##not-node##");
|
||||
printf(" new SCC root %p \"%s\"\n",
|
||||
NS_STATIC_CAST(void*, SCCEntry->key.get()),
|
||||
NS_ConvertUTF16toUTF8(nodeName).get());
|
||||
}
|
||||
#endif
|
||||
|
||||
entry->next = SCCEntry->first;
|
||||
SCCEntry->first = entry;
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
MarkExternallyReferenced(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
PRUint32 number, void *arg)
|
||||
{
|
||||
ExternallyReferencedEntry *entry =
|
||||
NS_STATIC_CAST(ExternallyReferencedEntry*, hdr);
|
||||
JSContext *cx = NS_STATIC_CAST(JSContext*, arg);
|
||||
|
||||
nsIDOMGCParticipant *erParticipant = entry->key;
|
||||
|
||||
if (sRootWhenExternallyReferencedTable.ops) {
|
||||
RootWhenExternallyReferencedEntry *rwerEntry =
|
||||
NS_STATIC_CAST(RootWhenExternallyReferencedEntry*,
|
||||
PL_DHashTableOperate(&sRootWhenExternallyReferencedTable,
|
||||
erParticipant, PL_DHASH_LOOKUP));
|
||||
if (PL_DHASH_ENTRY_IS_BUSY(rwerEntry)) {
|
||||
NS_ASSERTION(rwerEntry->refcnt > 0, "bad reference count");
|
||||
// XXX Construct something to say where the mark is coming from?
|
||||
nsDOMClassInfo::MarkReachablePreservedWrappers(entry->key, cx, nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
PRBool
|
||||
nsDOMClassInfo::BeginGCMark(JSContext *cx)
|
||||
{
|
||||
NS_PRECONDITION(!sWrapperSCCTable.ops, "table already initialized");
|
||||
|
||||
#ifdef DEBUG_PRESERVE_WRAPPERS
|
||||
printf("Classifying preserved wrappers into SCCs:\n");
|
||||
#endif
|
||||
|
||||
if (!PL_DHashTableInit(&sWrapperSCCTable, &sWrapperSCCTableOps, nsnull,
|
||||
sizeof(WrapperSCCEntry), 16)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool failure = PR_FALSE;
|
||||
if (sPreservedWrapperTable.ops) {
|
||||
PL_DHashTableEnumerate(&sPreservedWrapperTable, ClassifyWrapper, &failure);
|
||||
}
|
||||
if (failure) {
|
||||
PL_DHashTableFinish(&sWrapperSCCTable);
|
||||
// caller will reset table ops
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRESERVE_WRAPPERS
|
||||
printf("Marking:\n");
|
||||
#endif
|
||||
|
||||
if (sExternallyReferencedTable.ops) {
|
||||
PL_DHashTableEnumerate(&sExternallyReferencedTable,
|
||||
MarkExternallyReferenced, cx);
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsDOMClassInfo::EndGCMark()
|
||||
{
|
||||
if (sWrapperSCCTable.ops) {
|
||||
if (sWrapperSCCTable.ops != WRAPPER_SCC_OPS_OOM_MARKER)
|
||||
PL_DHashTableFinish(&sWrapperSCCTable);
|
||||
sWrapperSCCTable.ops = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
// hack to give XBL access to nsDOMClassInfo::PreserveNodeWrapper
|
||||
nsresult
|
||||
NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
|
||||
{
|
||||
return nsDOMClassInfo::PreserveNodeWrapper(aWrapper);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
|
@ -6869,11 +6335,7 @@ NS_IMETHODIMP
|
|||
nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
|
||||
{
|
||||
// This can fail on out-of-memory, which should end up throwing a JS
|
||||
// exception.
|
||||
nsresult rv = nsDOMClassInfo::PreserveNodeWrapper(wrapper);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsDOMClassInfo::PreserveNodeWrapper(wrapper);
|
||||
return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval);
|
||||
}
|
||||
|
||||
|
@ -7167,12 +6629,10 @@ nsEventReceiverSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
|
|||
JSContext *cx, JSObject *obj, jsval id,
|
||||
PRUint32 flags, JSObject **objp, PRBool *_retval)
|
||||
{
|
||||
if (id == sOnload_id || id == sOnerror_id) {
|
||||
// Pass true for aRootWhenExternallyReferenced, so we make sure that
|
||||
// this node can't go away while waiting for a network load that
|
||||
// could fire an event handler.
|
||||
nsresult rv = nsDOMClassInfo::PreserveNodeWrapper(wrapper, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (id == sOnload_id || id == sOnerror_id) {
|
||||
// Make sure that this node can't go away while waiting for a
|
||||
// network load that could fire an event handler.
|
||||
nsDOMClassInfo::PreserveNodeWrapper(wrapper);
|
||||
}
|
||||
|
||||
if (!JSVAL_IS_STRING(id)) {
|
||||
|
@ -7223,8 +6683,8 @@ nsEventReceiverSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
|
|||
*objp = obj;
|
||||
}
|
||||
|
||||
return nsDOMGCParticipantSH::NewResolve(wrapper, cx, obj, id, flags, objp,
|
||||
_retval);
|
||||
return nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp,
|
||||
_retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -7253,30 +6713,6 @@ nsEventReceiverSH::AddProperty(nsIXPConnectWrappedNative *wrapper,
|
|||
return nsEventReceiverSH::SetProperty(wrapper, cx, obj, id, vp, _retval);
|
||||
}
|
||||
|
||||
// XXX nsEventReceiverSH::Finalize: clear event handlers in mListener...
|
||||
|
||||
// DOMGCParticipant helper
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMGCParticipantSH::Finalize(nsIXPConnectWrappedNative *wrapper,
|
||||
JSContext *cx, JSObject *obj)
|
||||
{
|
||||
nsDOMClassInfo::ReleaseWrapper(wrapper);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMGCParticipantSH::Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, void *arg, PRUint32 *_retval)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> participant(do_QueryWrappedNative(wrapper));
|
||||
|
||||
nsDOMClassInfo::MarkReachablePreservedWrappers(participant, cx, arg);
|
||||
|
||||
*_retval = 1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Element helper
|
||||
|
||||
|
|
|
@ -51,10 +51,8 @@ class nsIDOMWindow;
|
|||
class nsIDOMNSHTMLOptionCollection;
|
||||
class nsIPluginInstance;
|
||||
class nsIForm;
|
||||
class nsIDOMNode;
|
||||
class nsIDOMNodeList;
|
||||
class nsIDOMDocument;
|
||||
class nsIDOMGCParticipant;
|
||||
class nsIHTMLDocument;
|
||||
class nsGlobalWindow;
|
||||
|
||||
|
@ -175,87 +173,7 @@ public:
|
|||
::JS_GetClass(cx, obj) == sXPCNativeWrapperClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that the XPConnect wrapper should be protected from garbage
|
||||
* collection as long as the GC participant is reachable.
|
||||
*
|
||||
* A preservation with a given key overwrites any previous
|
||||
* preservation with that key.
|
||||
*
|
||||
* No strong references are held as a result of this function call, so
|
||||
* the caller is responsible for calling |ReleaseWrapper| sometime
|
||||
* before |aParticipant|'s destructor runs.
|
||||
*
|
||||
* aRootWhenExternallyReferenced should be true if the preservation is
|
||||
* for an event handler that could be triggered by the participant
|
||||
* being externally referenced by a network load.
|
||||
*/
|
||||
static nsresult PreserveWrapper(void* aKey,
|
||||
nsIXPConnectJSObjectHolder* (*aKeyToWrapperFunc)(void* aKey),
|
||||
nsIDOMGCParticipant *aParticipant,
|
||||
PRBool aRootWhenExternallyReferenced);
|
||||
|
||||
|
||||
/**
|
||||
* Easier way to call the above just for DOM nodes (and better, since
|
||||
* we get the performance benefits of having the same identity function).
|
||||
* The call to |PreserveWrapper| is made with |aKey| == |aWrapper|.
|
||||
*
|
||||
* The caller need not call |ReleaseWrapper| since the node's
|
||||
* wrapper's scriptable helper does so in its finalize callback.
|
||||
*/
|
||||
static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper,
|
||||
PRBool aRootWhenExternallyReferenced =
|
||||
PR_FALSE);
|
||||
|
||||
/**
|
||||
* Undoes the effects of any prior |PreserveWrapper| calls made with
|
||||
* |aKey|.
|
||||
*/
|
||||
static void ReleaseWrapper(void* aKey);
|
||||
|
||||
/**
|
||||
* Mark all preserved wrappers reachable from |aDOMNode| via DOM APIs.
|
||||
*/
|
||||
static void MarkReachablePreservedWrappers(nsIDOMGCParticipant *aParticipant,
|
||||
JSContext *cx, void *arg);
|
||||
|
||||
/**
|
||||
* Add/remove |aParticipant| from the table of externally referenced
|
||||
* participants. Does not maintain a count, so an object should not
|
||||
* be added when it is already in the table.
|
||||
*
|
||||
* The table of externally referenced participants is a list of
|
||||
* participants that should be marked at GC-time **if they are a
|
||||
* participant in the preserved wrapper table added with
|
||||
* aRootWhenExternallyReferenced**, whether or not they are reachable
|
||||
* from marked participants. This should be used for participants
|
||||
* that hold onto scripts (typically onload or onerror handlers) that
|
||||
* can be triggered at the end of a currently-ongoing operation
|
||||
* (typically a network request) and that could thus expose the
|
||||
* participant to script again in the future even though it is not
|
||||
* otherwise reachable.
|
||||
*
|
||||
* The caller is responsible for ensuring that the GC participant is
|
||||
* alive while it is in this table; the table does not own a reference.
|
||||
*
|
||||
* UnsetExternallyReferenced must be called exactly once for every
|
||||
* successful SetExternallyReferenced call, and in no other cases.
|
||||
*/
|
||||
static nsresult SetExternallyReferenced(nsIDOMGCParticipant *aParticipant);
|
||||
static void UnsetExternallyReferenced(nsIDOMGCParticipant *aParticipant);
|
||||
|
||||
/**
|
||||
* Classify the wrappers for use by |MarkReachablePreservedWrappers|
|
||||
* during the GC. Returns false to indicate failure (out-of-memory).
|
||||
*/
|
||||
static PRBool BeginGCMark(JSContext *cx);
|
||||
|
||||
/**
|
||||
* Clean up data structures (and strong references) created by
|
||||
* |BeginGCMark|.
|
||||
*/
|
||||
static void EndGCMark();
|
||||
static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
|
||||
|
||||
protected:
|
||||
const nsDOMClassInfoData* mData;
|
||||
|
@ -407,39 +325,14 @@ public:
|
|||
|
||||
typedef nsDOMClassInfo nsDOMGenericSH;
|
||||
|
||||
// Scriptable helper for implementations of nsIDOMGCParticipant that
|
||||
// need a mark callback.
|
||||
class nsDOMGCParticipantSH : public nsDOMGenericSH
|
||||
{
|
||||
protected:
|
||||
nsDOMGCParticipantSH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsDOMGCParticipantSH()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj);
|
||||
NS_IMETHOD Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, void *arg, PRUint32 *_retval);
|
||||
|
||||
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
|
||||
{
|
||||
return new nsDOMGCParticipantSH(aData);
|
||||
}
|
||||
};
|
||||
|
||||
// EventProp scriptable helper, this class should be the base class of
|
||||
// all objects that should support things like
|
||||
// obj.onclick=function{...}
|
||||
|
||||
class nsEventReceiverSH : public nsDOMGCParticipantSH
|
||||
class nsEventReceiverSH : public nsDOMGenericSH
|
||||
{
|
||||
protected:
|
||||
nsEventReceiverSH(nsDOMClassInfoData* aData) : nsDOMGCParticipantSH(aData)
|
||||
nsEventReceiverSH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -642,6 +642,7 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope)
|
|||
// nsGlobalWindow::nsISupports
|
||||
//*****************************************************************************
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow)
|
||||
|
||||
// QueryInterface implementation for nsGlobalWindow
|
||||
NS_INTERFACE_MAP_BEGIN(nsGlobalWindow)
|
||||
|
@ -654,7 +655,6 @@ NS_INTERFACE_MAP_BEGIN(nsGlobalWindow)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
|
||||
|
@ -664,12 +664,105 @@ NS_INTERFACE_MAP_BEGIN(nsGlobalWindow)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageWindow)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsGlobalWindow)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(nsGlobalWindow)
|
||||
NS_IMPL_RELEASE(nsGlobalWindow)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsGlobalWindow, nsIScriptGlobalObject)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsGlobalWindow, nsIScriptGlobalObject)
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow, nsIScriptGlobalObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
|
||||
|
||||
// Strange bug: If you uncomment this line you will find that
|
||||
// the cycle collector crashes when working with multiple open
|
||||
// top-level windows. It is as though the windows somehow
|
||||
// race with one another. How can this be? Curious.
|
||||
//
|
||||
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArguments)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgumentsLast)
|
||||
|
||||
for (PRUint32 i = 0; i < NS_STID_ARRAY_UBOUND; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContexts[i])
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(gGlobalStorageList)
|
||||
|
||||
for (PRUint32 i = 0; i < NS_STID_ARRAY_UBOUND; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInnerWindowHolders[i])
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpenerScriptPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSessionStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc)
|
||||
|
||||
// Traverse any associated preserved wrappers.
|
||||
{
|
||||
nsISupports *preservedWrapper = nsnull;
|
||||
if (tmp->mDoc) {
|
||||
preservedWrapper = tmp->mDoc->GetReference(tmp);
|
||||
if (preservedWrapper)
|
||||
cb.NoteXPCOMChild(preservedWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse our possibly-inner window.
|
||||
if (tmp->IsOuterWindow()
|
||||
&& tmp->GetCurrentInnerWindowInternal()) {
|
||||
cb.NoteXPCOMChild(NS_STATIC_CAST(nsIScriptGlobalObject*,
|
||||
tmp->GetCurrentInnerWindowInternal()));
|
||||
}
|
||||
|
||||
// FIXME: somewhere in these commented lines lies a bug that causes
|
||||
// a segfault. So we have disabled them, even though it seems wrong
|
||||
// to do so. Other matters are more pressing at the moment.
|
||||
|
||||
// Traverse stuff from nsPIDOMWindow
|
||||
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChromeEventHandler)
|
||||
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow, nsIScriptGlobalObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
|
||||
|
||||
// See comment about traversing mOpener above.
|
||||
// NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpener)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mArguments)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mArgumentsLast)
|
||||
|
||||
for (PRUint32 i = 0; i < NS_STID_ARRAY_UBOUND; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContexts[i])
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(gGlobalStorageList)
|
||||
|
||||
for (PRUint32 i = 0; i < NS_STID_ARRAY_UBOUND; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInnerWindowHolders[i])
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpenerScriptPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSessionStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocumentPrincipal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDoc)
|
||||
|
||||
// Unlink any associated preserved wrapper.
|
||||
if (tmp->mDoc)
|
||||
tmp->mDoc->RemoveReference(tmp->mDoc.get());
|
||||
|
||||
// Unlink stuff from nsPIDOMWindow
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChromeEventHandler)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
//*****************************************************************************
|
||||
|
@ -5432,37 +5525,6 @@ nsGlobalWindow::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// nsGlobalWindow::nsIDOMGCParticipant
|
||||
//*****************************************************************************
|
||||
|
||||
nsIDOMGCParticipant*
|
||||
nsGlobalWindow::GetSCCIndex()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
static void AppendToReachableList(nsISupports *aObject,
|
||||
nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGCParticipant> p = do_QueryInterface(aObject);
|
||||
if (p)
|
||||
aArray.AppendObject(p);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
AppendToReachableList(mChromeEventHandler, aArray);
|
||||
AppendToReachableList(mDocument, aArray);
|
||||
// XXXldb Do we want this to go both ways?
|
||||
if (IsOuterWindow()) {
|
||||
AppendToReachableList(mInnerWindow, aArray);
|
||||
} else {
|
||||
AppendToReachableList(mOuterWindow, aArray);
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// nsGlobalWindow::nsPIDOMWindow
|
||||
//*****************************************************************************
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "nsWeakReference.h"
|
||||
#include "nsHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
// Interfaces Needed
|
||||
#include "nsDOMWindowList.h"
|
||||
|
@ -91,7 +92,6 @@
|
|||
#include "nsSize.h"
|
||||
#include "mozFlushType.h"
|
||||
#include "prclist.h"
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsIDOMStorageList.h"
|
||||
#include "nsIDOMStorageWindow.h"
|
||||
|
@ -217,7 +217,6 @@ class nsGlobalWindow : public nsPIDOMWindow,
|
|||
public nsIDOMJSWindow,
|
||||
public nsIScriptObjectPrincipal,
|
||||
public nsIDOMEventReceiver,
|
||||
public nsIDOMGCParticipant,
|
||||
public nsIDOM3EventTarget,
|
||||
public nsIDOMNSEventTarget,
|
||||
public nsIDOMViewCSS,
|
||||
|
@ -233,7 +232,7 @@ public:
|
|||
void ReallyCloseWindow();
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
// nsIScriptGlobalObject
|
||||
virtual nsIScriptContext *GetContext();
|
||||
|
@ -288,10 +287,6 @@ public:
|
|||
NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent);
|
||||
NS_IMETHOD GetSystemEventGroup(nsIDOMEventGroup** aGroup);
|
||||
|
||||
// nsIDOMGCParticipant
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
// nsPIDOMWindow
|
||||
virtual NS_HIDDEN_(nsPIDOMWindow*) GetPrivateRoot();
|
||||
virtual NS_HIDDEN_(nsresult) Activate();
|
||||
|
@ -407,6 +402,8 @@ public:
|
|||
|
||||
friend class WindowStateHolder;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsGlobalWindow)
|
||||
|
||||
protected:
|
||||
// Object Management
|
||||
virtual ~nsGlobalWindow();
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "jscntxt.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
#include "nsIContent.h"
|
||||
|
||||
// For locale aware string methods
|
||||
|
@ -3173,14 +3172,6 @@ DOMGCCallback(JSContext *cx, JSGCStatus status)
|
|||
if (status == JSGC_BEGIN && !NS_IsMainThread())
|
||||
return JS_FALSE;
|
||||
|
||||
// XPCJSRuntime::GCCallback does marking from the JSGC_MARK_END callback.
|
||||
// we need to call EndGCMark *after* marking is finished.
|
||||
// XXX This relies on our callback being registered after
|
||||
// XPCJSRuntime's, although if they were registered the other way
|
||||
// around the ordering there would be correct.
|
||||
if (status == JSGC_MARK_END)
|
||||
nsDOMClassInfo::EndGCMark();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,6 @@
|
|||
#include "nsIXPConnect.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsIDOMGCParticipant.h"
|
||||
#include "nsIWeakReference.h"
|
||||
|
||||
#include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
|
||||
|
||||
|
@ -196,101 +193,3 @@ nsJSUtils::GetDynamicScriptContext(JSContext *aContext)
|
|||
{
|
||||
return GetScriptContextFromJSContext(aContext);
|
||||
}
|
||||
|
||||
#define MARKED_OBJECT_BIT (PRWord(1<<0))
|
||||
|
||||
void
|
||||
nsMarkedJSFunctionHolder_base::Set(nsISupports *aPotentialFunction,
|
||||
nsIDOMGCParticipant *aParticipant)
|
||||
{
|
||||
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
|
||||
nsDOMClassInfo::ReleaseWrapper(this);
|
||||
}
|
||||
nsISupports *oldVal = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
|
||||
if (!TryMarkedSet(aPotentialFunction, aParticipant)) {
|
||||
NS_ASSERTION((PRWord(aPotentialFunction) & MARKED_OBJECT_BIT) == 0,
|
||||
"low bit set");
|
||||
NS_IF_ADDREF(aPotentialFunction);
|
||||
mObject = aPotentialFunction;
|
||||
}
|
||||
NS_IF_RELEASE(oldVal);
|
||||
}
|
||||
|
||||
static nsIXPConnectJSObjectHolder* HolderToWrappedJS(void *aKey)
|
||||
{
|
||||
nsMarkedJSFunctionHolder_base *holder = NS_STATIC_CAST(
|
||||
nsMarkedJSFunctionHolder_base*, aKey);
|
||||
|
||||
NS_ASSERTION(PRWord(holder->mObject) & MARKED_OBJECT_BIT,
|
||||
"yikes, not a marked object");
|
||||
|
||||
nsIWeakReference* weakRef =
|
||||
(nsIWeakReference*)(PRWord(holder->mObject) & ~MARKED_OBJECT_BIT);
|
||||
|
||||
// This entire interface is a hack to avoid reference counting, so
|
||||
// this actually doesn't do any reference counting, and we don't leak
|
||||
// anything. This is needed so we don't add and remove GC roots in
|
||||
// the middle of GC.
|
||||
nsWeakRefToIXPConnectWrappedJS *result;
|
||||
if (NS_FAILED(CallQueryReferent(weakRef, &result)))
|
||||
result = nsnull;
|
||||
return result;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsMarkedJSFunctionHolder_base::TryMarkedSet(nsISupports *aPotentialFunction,
|
||||
nsIDOMGCParticipant *aParticipant)
|
||||
{
|
||||
if (!aParticipant)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
|
||||
do_QueryInterface(aPotentialFunction);
|
||||
if (!wrappedJS) // a non-JS implementation
|
||||
return PR_FALSE;
|
||||
|
||||
// XXX We really only need to pass PR_TRUE for
|
||||
// root-if-externally-referenced if this is an onload, onerror,
|
||||
// onreadystatechange, etc., so we could pass the responsibility for
|
||||
// choosing that to the caller.
|
||||
nsresult rv =
|
||||
nsDOMClassInfo::PreserveWrapper(this, HolderToWrappedJS, aParticipant,
|
||||
PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
nsIWeakReference* weakRef; // [STRONG]
|
||||
wrappedJS->GetWeakReference(&weakRef);
|
||||
NS_ENSURE_TRUE(weakRef, PR_FALSE);
|
||||
|
||||
NS_ASSERTION((PRWord(weakRef) & MARKED_OBJECT_BIT) == 0, "low bit set");
|
||||
mObject = (nsISupports*)(PRWord(weakRef) | MARKED_OBJECT_BIT);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
nsMarkedJSFunctionHolder_base::Get(REFNSIID aIID)
|
||||
{
|
||||
nsISupports *result;
|
||||
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
|
||||
nsIWeakReference* weakRef =
|
||||
(nsIWeakReference*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
|
||||
nsresult rv =
|
||||
weakRef->QueryReferent(aIID, NS_REINTERPRET_CAST(void**, &result));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_NOTREACHED("GC preservation didn't work");
|
||||
result = nsnull;
|
||||
}
|
||||
} else {
|
||||
NS_IF_ADDREF(result = mObject);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsMarkedJSFunctionHolder_base::~nsMarkedJSFunctionHolder_base()
|
||||
{
|
||||
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
|
||||
nsDOMClassInfo::ReleaseWrapper(this);
|
||||
}
|
||||
nsISupports *obj = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
|
||||
NS_IF_RELEASE(obj);
|
||||
}
|
||||
|
|
|
@ -48,13 +48,10 @@
|
|||
#include "nsISupports.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIDOMEventListener;
|
||||
class nsIScriptContext;
|
||||
class nsIScriptGlobalObject;
|
||||
class nsIDOMGCParticipant;
|
||||
class nsIXPConnectJSObjectHolder;
|
||||
|
||||
class nsJSUtils
|
||||
{
|
||||
|
@ -102,48 +99,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* nsMarkedJSFunctionHolder<T> is used to store objects of XPCOM
|
||||
* interface T.
|
||||
*
|
||||
* If the object stored is an XPConnect wrapped JS object and the
|
||||
* wrapper can be preserved through nsDOMClassInfo, the holder will hold
|
||||
* a weak reference and preserve the object from garbage collection as
|
||||
* long as the garbage collector can reach |aParticipant|; once both
|
||||
* |aParticipant| and the object are unreachable it will be garbage
|
||||
* collected and the holder will hold null.
|
||||
*
|
||||
* Otherwise, it holds a strong reference.
|
||||
*/
|
||||
|
||||
class nsMarkedJSFunctionHolder_base
|
||||
{
|
||||
public:
|
||||
void Set(nsISupports *aPotentialFunction, nsIDOMGCParticipant *aParticipant);
|
||||
already_AddRefed<nsISupports> Get(REFNSIID aIID);
|
||||
|
||||
nsMarkedJSFunctionHolder_base() : mObject(nsnull) {}
|
||||
~nsMarkedJSFunctionHolder_base();
|
||||
|
||||
PRBool TryMarkedSet(nsISupports *aPotentialFunction, nsIDOMGCParticipant *aParticipant);
|
||||
|
||||
nsISupports *mObject;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class nsMarkedJSFunctionHolder : protected nsMarkedJSFunctionHolder_base
|
||||
{
|
||||
public:
|
||||
void Set(T *aPotentialFunction, nsIDOMGCParticipant *aParticipant) {
|
||||
nsMarkedJSFunctionHolder_base::Set(aPotentialFunction, aParticipant);
|
||||
}
|
||||
already_AddRefed<T> Get() {
|
||||
return already_AddRefed<T>(NS_STATIC_CAST(T*, nsMarkedJSFunctionHolder_base::Get(NS_GET_TEMPLATE_IID(T)).get()));
|
||||
}
|
||||
// An overloaded version that's more useful for XPCOM getters
|
||||
void Get(T** aResult) {
|
||||
*aResult = Get().get();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* nsJSUtils_h__ */
|
||||
|
|
|
@ -52,8 +52,10 @@
|
|||
#include "nsIDOMWindowInternal.h"
|
||||
#include "nsFocusController.h"
|
||||
#include "nsString.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
|
||||
|
||||
|
@ -65,10 +67,10 @@ nsWindowRoot::nsWindowRoot(nsIDOMWindow* aWindow)
|
|||
nsFocusController::Create(getter_AddRefs(mFocusController));
|
||||
|
||||
nsCOMPtr<nsIDOMFocusListener> focusListener(do_QueryInterface(mFocusController));
|
||||
++mRefCnt;
|
||||
mRefCnt.incr(NS_STATIC_CAST(nsIDOMEventReceiver*, this));
|
||||
AddEventListener(NS_LITERAL_STRING("focus"), focusListener, PR_TRUE);
|
||||
AddEventListener(NS_LITERAL_STRING("blur"), focusListener, PR_TRUE);
|
||||
--mRefCnt;
|
||||
mRefCnt.decr(NS_STATIC_CAST(nsIDOMEventReceiver*, this));
|
||||
}
|
||||
|
||||
nsWindowRoot::~nsWindowRoot()
|
||||
|
@ -78,20 +80,22 @@ nsWindowRoot::~nsWindowRoot()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_2_AMBIGUOUS(nsWindowRoot, nsIDOMEventReceiver,
|
||||
mListenerManager, mFocusController)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsWindowRoot)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIChromeEventHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WindowRoot) // XXX right name?
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsWindowRoot)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsWindowRoot)
|
||||
NS_IMPL_RELEASE(nsWindowRoot)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsWindowRoot, nsIDOMEventReceiver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsWindowRoot, nsIDOMEventReceiver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowRoot::AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, PRBool aUseCapture)
|
||||
|
@ -234,16 +238,6 @@ nsWindowRoot::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIDOMGCParticipant*
|
||||
nsWindowRoot::GetSCCIndex()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindowRoot::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowRoot::PreHandleChromeEvent(nsEventChainPreVisitor& aVisitor)
|
||||
|
|
|
@ -55,6 +55,7 @@ class nsEventChainPostVisitor;
|
|||
#include "nsPIWindowRoot.h"
|
||||
#include "nsIFocusController.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsWindowRoot : public nsIDOMEventReceiver,
|
||||
public nsIDOM3EventTarget,
|
||||
|
@ -66,7 +67,7 @@ public:
|
|||
nsWindowRoot(nsIDOMWindow* aWindow);
|
||||
virtual ~nsWindowRoot();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTTARGET
|
||||
NS_DECL_NSIDOM3EVENTTARGET
|
||||
NS_DECL_NSIDOMNSEVENTTARGET
|
||||
|
@ -81,13 +82,11 @@ public:
|
|||
NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent);
|
||||
NS_IMETHOD GetSystemEventGroup(nsIDOMEventGroup** aGroup);
|
||||
|
||||
// nsIDOMGCParticipant
|
||||
virtual nsIDOMGCParticipant* GetSCCIndex();
|
||||
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
|
||||
|
||||
// nsPIWindowRoot
|
||||
NS_IMETHOD GetFocusController(nsIFocusController** aResult);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsWindowRoot)
|
||||
|
||||
protected:
|
||||
// Members
|
||||
nsIDOMWindow* mWindow; // [Weak]. The window will hold on to us and let go when it dies.
|
||||
|
|
|
@ -56,16 +56,10 @@
|
|||
|
||||
#include "nspr.h" // PR_fprintf
|
||||
|
||||
PRInt32 nsIJSEventListener::sNumJSEventListeners = 0;
|
||||
|
||||
class EventListenerCounter
|
||||
{
|
||||
public:
|
||||
~EventListenerCounter() {
|
||||
if (nsIJSEventListener::sNumJSEventListeners) {
|
||||
PR_fprintf(PR_STDERR,"WARNING: LEAKED %d nsIJSEventListeners\n",
|
||||
nsIJSEventListener::sNumJSEventListeners);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,15 +87,17 @@ nsJSEventListener::~nsJSEventListener()
|
|||
mContext->DropScriptObject(mScopeObject);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_1_AMBIGUOUS(nsJSEventListener, nsIDOMEventListener, mTarget)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsJSEventListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIJSEventListener)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(nsJSEventListener)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsJSEventListener)
|
||||
|
||||
NS_IMPL_RELEASE(nsJSEventListener)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSEventListener, nsIDOMEventListener)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSEventListener, nsIDOMEventListener)
|
||||
|
||||
//static nsString onPrefix = "on";
|
||||
|
||||
|
@ -276,4 +272,3 @@ NS_NewJSEventListener(nsIScriptContext *aContext, void *aScopeObject,
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
// nsJSEventListener interface
|
||||
// misnamed - JS no longer has exclusive rights over this interface!
|
||||
|
@ -56,7 +57,7 @@ public:
|
|||
nsISupports* aObject);
|
||||
virtual ~nsJSEventListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
|
||||
// nsIDOMEventListener interface
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
@ -64,6 +65,7 @@ public:
|
|||
// nsIJSEventListener interface
|
||||
virtual void SetEventName(nsIAtom* aName);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
|
||||
protected:
|
||||
nsCOMPtr<nsIAtom> mEventName;
|
||||
|
||||
|
|
|
@ -1982,6 +1982,13 @@ JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb)
|
|||
return oldcb;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetGCThingCallback(JSContext *cx, JSGCThingCallback cb, void *closure)
|
||||
{
|
||||
cx->runtime->gcThingCallback = cb;
|
||||
cx->runtime->gcThingCallbackClosure = closure;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_IsAboutToBeFinalized(JSContext *cx, void *thing)
|
||||
{
|
||||
|
|
|
@ -869,6 +869,9 @@ JS_SetGCCallback(JSContext *cx, JSGCCallback cb);
|
|||
extern JS_PUBLIC_API(JSGCCallback)
|
||||
JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetGCThingCallback(JSContext *cx, JSGCThingCallback cb, void *closure);
|
||||
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_IsAboutToBeFinalized(JSContext *cx, void *thing);
|
||||
|
||||
|
|
|
@ -198,6 +198,8 @@ struct JSRuntime {
|
|||
uint16 gcPadding;
|
||||
|
||||
JSGCCallback gcCallback;
|
||||
JSGCThingCallback gcThingCallback;
|
||||
void *gcThingCallbackClosure;
|
||||
uint32 gcMallocBytes;
|
||||
JSGCArena *gcUnscannedArenaStackTop;
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -2103,9 +2103,13 @@ MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp,
|
|||
if (!JSVAL_IS_GCTHING(v) || v == JSVAL_NULL)
|
||||
continue;
|
||||
next_thing = JSVAL_TO_GCTHING(v);
|
||||
next_flagp = js_GetGCThingFlags(next_thing);
|
||||
if (rt->gcThingCallback) {
|
||||
rt->gcThingCallback(next_thing, *next_flagp,
|
||||
rt->gcThingCallbackClosure);
|
||||
}
|
||||
if (next_thing == thing)
|
||||
continue;
|
||||
next_flagp = js_GetGCThingFlags(next_thing);
|
||||
if (*next_flagp & GCF_MARK)
|
||||
continue;
|
||||
JS_ASSERT(*next_flagp != GCF_FINAL);
|
||||
|
@ -2158,6 +2162,10 @@ MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp,
|
|||
break;
|
||||
thing = JSSTRDEP_BASE(str);
|
||||
flagp = js_GetGCThingFlags(thing);
|
||||
if (rt->gcThingCallback) {
|
||||
rt->gcThingCallback(thing, *flagp,
|
||||
rt->gcThingCallbackClosure);
|
||||
}
|
||||
if (*flagp & GCF_MARK)
|
||||
break;
|
||||
#ifdef GC_MARK_DEBUG
|
||||
|
@ -2468,6 +2476,12 @@ js_MarkGCThing(JSContext *cx, void *thing)
|
|||
|
||||
flagp = js_GetGCThingFlags(thing);
|
||||
JS_ASSERT(*flagp != GCF_FINAL);
|
||||
|
||||
if (cx->runtime->gcThingCallback) {
|
||||
cx->runtime->gcThingCallback(thing, *flagp,
|
||||
cx->runtime->gcThingCallbackClosure);
|
||||
}
|
||||
|
||||
if (*flagp & GCF_MARK)
|
||||
return;
|
||||
*flagp |= GCF_MARK;
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
#ifndef jslock_h__
|
||||
#define jslock_h__
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
|
||||
#include "jstypes.h"
|
||||
|
@ -263,4 +265,6 @@ extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me);
|
|||
#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX))
|
||||
#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX))
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif /* jslock_h___ */
|
||||
|
|
|
@ -569,6 +569,9 @@ typedef enum JSGCStatus {
|
|||
typedef JSBool
|
||||
(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status);
|
||||
|
||||
typedef void
|
||||
(* JS_DLL_CALLBACK JSGCThingCallback)(void *thing, uint8 flags, void *closure);
|
||||
|
||||
typedef JSBool
|
||||
(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script);
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include "nsIInterfaceInfoManager.idl"
|
||||
#include "nsIExceptionService.idl"
|
||||
#include "nsIVariant.idl"
|
||||
#include "nsIWeakReference.idl"
|
||||
|
||||
%{ C++
|
||||
#include "jspubtd.h"
|
||||
|
@ -149,6 +148,10 @@ interface nsIInterfaceInfo;
|
|||
interface nsIXPCSecurityManager;
|
||||
interface nsIPrincipal;
|
||||
|
||||
%{C++
|
||||
struct nsCycleCollectionTraversalCallback;
|
||||
%}
|
||||
|
||||
/***************************************************************************/
|
||||
[uuid(8916a320-d118-11d3-8f3a-0010a4e73d9a)]
|
||||
interface nsIXPConnectJSObjectHolder : nsISupports
|
||||
|
@ -230,7 +233,7 @@ do_QueryWrappedNative(nsIXPConnectWrappedNative *aWrappedNative,
|
|||
|
||||
%}
|
||||
|
||||
[uuid(a052e197-7ba4-494e-b735-8786ac091164)]
|
||||
[uuid(BED52030-BCA6-11d2-BA79-00805F8A5DD7)]
|
||||
interface nsIXPConnectWrappedJS : nsIXPConnectJSObjectHolder
|
||||
{
|
||||
/* attribute 'JSObject' inherited from nsIXPConnectJSObjectHolder */
|
||||
|
@ -242,26 +245,6 @@ interface nsIXPConnectWrappedJS : nsIXPConnectJSObjectHolder
|
|||
void aggregatedQueryInterface(in nsIIDRef uuid,
|
||||
[iid_is(uuid),retval] out nsQIResult result);
|
||||
|
||||
/* This method has the same signature and the same semantics as the
|
||||
* one method on nsISupportsWeakReference. However, it exists here
|
||||
* so that callers who need to manage JS garbage collection for
|
||||
* wrapped objects themselves can get a weak reference to the
|
||||
* wrapped JS object: in other words, it's for callers who know
|
||||
* that they're dealing with a wrapper, and want a weak reference to
|
||||
* the wrapper rather than the wrapped object.
|
||||
*/
|
||||
nsIWeakReference GetWeakReference();
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface is a complete hack. It is used by the DOM code to
|
||||
* call QueryReferent on a weak reference to a wrapped JS object without
|
||||
* causing reference counting, which would add and remove GC roots
|
||||
* (which can't be done in the middle of GC).
|
||||
*/
|
||||
[uuid(3f32871c-d014-4f91-b358-3ece74cbebaa)]
|
||||
interface nsWeakRefToIXPConnectWrappedJS : nsIXPConnectWrappedJS
|
||||
{
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
|
||||
#include "xpcprivate.h"
|
||||
#include "XPCNativeWrapper.h"
|
||||
#include "nsBaseHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsXPConnect,nsIXPConnect,nsISupportsWeakReference)
|
||||
|
||||
|
@ -66,7 +69,8 @@ nsXPConnect::nsXPConnect()
|
|||
mContextStack(nsnull),
|
||||
mDefaultSecurityManager(nsnull),
|
||||
mDefaultSecurityManagerFlags(0),
|
||||
mShuttingDown(JS_FALSE)
|
||||
mShuttingDown(JS_FALSE),
|
||||
mObjRefcounts(nsnull)
|
||||
{
|
||||
// Ignore the result. If the runtime service is not ready to rumble
|
||||
// then we'll set this up later as needed.
|
||||
|
@ -74,6 +78,8 @@ nsXPConnect::nsXPConnect()
|
|||
|
||||
CallGetService(XPC_CONTEXT_STACK_CONTRACTID, &mContextStack);
|
||||
|
||||
nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this);
|
||||
|
||||
#ifdef XPC_TOOLS_SUPPORT
|
||||
{
|
||||
char* filename = PR_GetEnv("MOZILLA_JS_PROFILER_OUTPUT");
|
||||
|
@ -101,6 +107,37 @@ nsXPConnect::nsXPConnect()
|
|||
|
||||
}
|
||||
|
||||
typedef nsBaseHashtable<nsClearingVoidPtrHashKey, PRUint32, PRUint32> PointerSet;
|
||||
|
||||
struct JSObjectRefcounts
|
||||
{
|
||||
PointerSet mRefCounts;
|
||||
JSObjectRefcounts()
|
||||
{
|
||||
mRefCounts.Init();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mRefCounts.Clear();
|
||||
}
|
||||
|
||||
void Ref(void *obj, uint8 flags)
|
||||
{
|
||||
PRUint32 refs;
|
||||
if (mRefCounts.Get(obj, &refs))
|
||||
refs++;
|
||||
else
|
||||
refs = 1;
|
||||
mRefCounts.Put(obj, refs);
|
||||
}
|
||||
|
||||
PRBool Get(void *obj, PRUint32 &refcount)
|
||||
{
|
||||
return mRefCounts.Get(obj, &refcount);
|
||||
}
|
||||
};
|
||||
|
||||
nsXPConnect::~nsXPConnect()
|
||||
{
|
||||
// XXX It would be nice if we could get away with doing a GC here and also
|
||||
|
@ -108,6 +145,13 @@ nsXPConnect::~nsXPConnect()
|
|||
// noted all over the place, this makes bad things happen since shutdown is
|
||||
// an unstable time for so many modules who have not planned well for it.
|
||||
|
||||
nsCycleCollector_forgetRuntime(nsIProgrammingLanguage::JAVASCRIPT);
|
||||
if (mObjRefcounts)
|
||||
{
|
||||
delete mObjRefcounts;
|
||||
mObjRefcounts = NULL;
|
||||
}
|
||||
|
||||
mShuttingDown = JS_TRUE;
|
||||
{ // scoped callcontext
|
||||
XPCCallContext ccx(NATIVE_CALLER);
|
||||
|
@ -386,6 +430,130 @@ nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info)
|
|||
return FindInfo(NameTester, name, mInterfaceInfoManager, info);
|
||||
}
|
||||
|
||||
void XPCMarkNotification(void *thing, uint8 flags, void *closure)
|
||||
{
|
||||
uint8 ty = flags & GCF_TYPEMASK;
|
||||
if (ty != GCX_OBJECT &&
|
||||
ty != GCX_PRIVATE &&
|
||||
ty != GCX_XML)
|
||||
return;
|
||||
|
||||
JSObjectRefcounts* jsr = NS_STATIC_CAST(JSObjectRefcounts*, closure);
|
||||
jsr->Ref(thing, flags);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::BeginCycleCollection()
|
||||
{
|
||||
if (!mObjRefcounts)
|
||||
mObjRefcounts = new JSObjectRefcounts;
|
||||
|
||||
mObjRefcounts->Clear();
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
JS_SetGCThingCallback(cx, XPCMarkNotification, mObjRefcounts);
|
||||
JS_GC(cx);
|
||||
JS_SetGCThingCallback(cx, nsnull, nsnull);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::FinishCycleCollection()
|
||||
{
|
||||
if (mObjRefcounts)
|
||||
mObjRefcounts->Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::Root(const nsDeque &nodes)
|
||||
{
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
|
||||
{
|
||||
void *p = nodes.ObjectAt(i);
|
||||
if (!p)
|
||||
continue;
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
if (!JS_LockGCThing(cx, obj))
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::Unlink(const nsDeque &nodes)
|
||||
{
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
|
||||
for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
|
||||
{
|
||||
void *p = nodes.ObjectAt(i);
|
||||
if (!p)
|
||||
continue;
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
JS_ClearScope(cx, obj);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::Unroot(const nsDeque &nodes)
|
||||
{
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
|
||||
{
|
||||
void *p = nodes.ObjectAt(i);
|
||||
if (!p)
|
||||
continue;
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
if (!JS_UnlockGCThing(cx, obj))
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
|
||||
PRUint32 refcount = 0;
|
||||
if (!mObjRefcounts->Get(p, refcount))
|
||||
return NS_OK;
|
||||
|
||||
cb.DescribeNode(refcount, sizeof(JSObject), "JS Object");
|
||||
|
||||
if (!p)
|
||||
return NS_OK;
|
||||
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE
|
||||
&& OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
|
||||
{
|
||||
void *v = JS_GetPrivate(cx, obj);
|
||||
if (v)
|
||||
cb.NoteXPCOMChild(NS_STATIC_CAST(nsISupports*, v));
|
||||
}
|
||||
|
||||
for (uint32 i = JSSLOT_START(OBJ_GET_CLASS(cx, obj));
|
||||
i < STOBJ_NSLOTS(obj); ++i)
|
||||
{
|
||||
jsval val = STOBJ_GET_SLOT(obj, i);
|
||||
if (!JSVAL_IS_NULL(val)
|
||||
&& JSVAL_IS_OBJECT(val))
|
||||
{
|
||||
JSObject *child = JSVAL_TO_OBJECT(val);
|
||||
if (child)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, child);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
// nsIXPConnect interface methods...
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
#include "nscore.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
|
@ -420,9 +422,11 @@ private:
|
|||
|
||||
const PRBool OBJ_IS_GLOBAL = PR_TRUE;
|
||||
const PRBool OBJ_IS_NOT_GLOBAL = PR_FALSE;
|
||||
struct JSObjectRefcounts;
|
||||
|
||||
class nsXPConnect : public nsIXPConnect,
|
||||
public nsSupportsWeakReference
|
||||
public nsSupportsWeakReference,
|
||||
public nsCycleCollectionLanguageRuntime
|
||||
{
|
||||
public:
|
||||
// all the interface method declarations...
|
||||
|
@ -470,6 +474,14 @@ public:
|
|||
nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
|
||||
nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
|
||||
|
||||
// from nsCycleCollectionLanguageRuntime
|
||||
nsresult BeginCycleCollection();
|
||||
nsresult Root(const nsDeque &nodes);
|
||||
nsresult Unlink(const nsDeque &nodes);
|
||||
nsresult Unroot(const nsDeque &nodes);
|
||||
nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
|
||||
nsresult FinishCycleCollection();
|
||||
|
||||
#ifdef XPC_IDISPATCH_SUPPORT
|
||||
public:
|
||||
static PRBool IsIDispatchEnabled();
|
||||
|
@ -494,6 +506,7 @@ private:
|
|||
nsIXPCSecurityManager* mDefaultSecurityManager;
|
||||
PRUint16 mDefaultSecurityManagerFlags;
|
||||
JSBool mShuttingDown;
|
||||
JSObjectRefcounts* mObjRefcounts;
|
||||
|
||||
#ifdef XPC_TOOLS_SUPPORT
|
||||
nsCOMPtr<nsIXPCToolsProfiler> mProfiler;
|
||||
|
@ -1833,9 +1846,10 @@ private:
|
|||
class XPCWrappedNative : public nsIXPConnectWrappedNative
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
||||
NS_DECL_NSIXPCONNECTWRAPPEDNATIVE
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
virtual nsIPrincipal* GetObjectPrincipal() const;
|
||||
|
@ -2227,7 +2241,7 @@ private:
|
|||
// interface on the single underlying (possibly aggregate) JSObject.
|
||||
|
||||
class nsXPCWrappedJS : protected nsAutoXPTCStub,
|
||||
public nsWeakRefToIXPConnectWrappedJS,
|
||||
public nsIXPConnectWrappedJS,
|
||||
public nsSupportsWeakReference,
|
||||
public nsIPropertyBag
|
||||
{
|
||||
|
@ -2235,8 +2249,9 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
||||
NS_DECL_NSIXPCONNECTWRAPPEDJS
|
||||
//NS_DECL_NSISUPPORTSWEAKREFERENCE // methods also on nsIXPConnectWrappedJS
|
||||
NS_DECL_NSISUPPORTSWEAKREFERENCE
|
||||
NS_DECL_NSIPROPERTYBAG
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
|
||||
|
||||
NS_IMETHOD CallMethod(PRUint16 methodIndex,
|
||||
const XPTMethodDescriptor *info,
|
||||
|
|
|
@ -45,6 +45,56 @@
|
|||
|
||||
// NOTE: much of the fancy footwork is done in xpcstubs.cpp
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
|
||||
(nsISupports *s, nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
// REVIEW ME PLEASE: this is a very odd area and it's easy to get
|
||||
// it wrong. I'm not sure I got it right.
|
||||
//
|
||||
// We *might* have a stub that's not actually connected to an
|
||||
// nsXPCWrappedJS, so we begin by QI'ing over to a "real"
|
||||
// nsIXPConnectWrappedJS. Since that's a mostly-empty class, we
|
||||
// then downcast from there to the "true" nsXPCWrappedJS.
|
||||
//
|
||||
// Is this right? It's hard to know. It seems to work, but who
|
||||
// knows for how long.
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> owner = do_QueryInterface(s, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIXPConnectWrappedJS *base = owner.get();
|
||||
nsXPCWrappedJS *tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base);
|
||||
|
||||
// REVIEW ME PLEASE:
|
||||
//
|
||||
// I am not sure when this represents the true refcount.
|
||||
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(nsXPCWrappedJS),
|
||||
"nsXPCWrappedJS");
|
||||
|
||||
if (tmp->IsValid())
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
tmp->GetJSObject());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Unlink(nsISupports *s)
|
||||
{
|
||||
// NB: We might unlink our outgoing references in the future; for
|
||||
// now we do nothing. This is a harmless conservative behavior; it
|
||||
// just means that we rely on the cycle being broken by some of
|
||||
// the external XPCOM objects' unlink() methods, not our
|
||||
// own. Typically *any* unlinking will break the cycle.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
|
||||
{
|
||||
|
@ -79,6 +129,11 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
if ( aIID.Equals(NS_GET_IID(nsCycleCollectionParticipant)) ) {
|
||||
*aInstancePtr = & NS_CYCLE_COLLECTION_NAME(nsXPCWrappedJS);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Always check for this first so that our 'outer' can get this interface
|
||||
// from us without recurring into a call to the outer's QI!
|
||||
if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS)))
|
||||
|
@ -88,13 +143,6 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// This interface is a hack that says "don't AddRef me".
|
||||
if(aIID.Equals(NS_GET_IID(nsWeakRefToIXPConnectWrappedJS)))
|
||||
{
|
||||
*aInstancePtr = NS_STATIC_CAST(nsWeakRefToIXPConnectWrappedJS*, this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsISupports* outer = GetAggregatedNativeObject();
|
||||
if(outer)
|
||||
return outer->QueryInterface(aIID, aInstancePtr);
|
||||
|
@ -196,7 +244,10 @@ do_decrement:
|
|||
NS_IMETHODIMP
|
||||
nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
|
||||
{
|
||||
return mRoot->nsSupportsWeakReference::GetWeakReference(aInstancePtr);
|
||||
if(mRoot != this)
|
||||
return mRoot->GetWeakReference(aInstancePtr);
|
||||
|
||||
return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -47,6 +47,52 @@
|
|||
|
||||
/***************************************************************************/
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s,
|
||||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
XPCWrappedNative *tmp = NS_STATIC_CAST(XPCWrappedNative*, s);
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(XPCWrappedNative), "XPCWrappedNative");
|
||||
|
||||
if (tmp->mRefCnt.get() > 1) {
|
||||
|
||||
// If our refcount is > 1, our reference to the flat JS object is
|
||||
// considered "strong", and we're going to traverse it.
|
||||
//
|
||||
// If our refcount is <= 1, our reference to the flat JS object is
|
||||
// considered "weak", and we're *not* going to traverse it.
|
||||
//
|
||||
// This reasoning is in line with the slightly confusing lifecycle rules
|
||||
// for XPCWrappedNatives, described in a larger comment below and also
|
||||
// on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
|
||||
|
||||
JSObject *obj = nsnull;
|
||||
nsresult rv = tmp->GetJSObject(&obj);
|
||||
if (NS_SUCCEEDED(rv) && obj) {
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp->GetIdentityObject()) {
|
||||
cb.NoteXPCOMChild(tmp->GetIdentityObject());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(nsISupports *s)
|
||||
{
|
||||
// NB: We might unlink our outgoing references in the future; for
|
||||
// now we do nothing. This is a harmless conservative behavior; it
|
||||
// just means that we rely on the cycle being broken by some of
|
||||
// the external XPCOM objects' unlink() methods, not our
|
||||
// own. Typically *any* unlinking will break the cycle.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef XPC_CHECK_CLASSINFO_CLAIMS
|
||||
static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
|
||||
#else
|
||||
|
@ -828,6 +874,7 @@ NS_INTERFACE_MAP_BEGIN(XPCWrappedNative)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
|
||||
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(XPCWrappedNative)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(XPCWrappedNative)
|
||||
|
|
|
@ -64,6 +64,8 @@ CPPSRCS = \
|
|||
nsInterfaceRequestorAgg.cpp \
|
||||
nsUUIDGenerator.cpp \
|
||||
nsSystemInfo.cpp \
|
||||
nsCycleCollector.cpp \
|
||||
nsCycleCollectionParticipant.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef GC_LEAK_DETECTOR
|
||||
|
@ -115,6 +117,8 @@ SDK_HEADERS = \
|
|||
nsError.h \
|
||||
nsISupportsBase.h \
|
||||
nscore.h \
|
||||
nsCycleCollector.h \
|
||||
nsCycleCollectionParticipant.h
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsIConsoleListener.idl \
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "xpcom/nsCycleCollectionParticipant.h"
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsCycleCollectionParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsCycleCollectionParticipant)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsCycleCollectionParticipant::AddRef(void)
|
||||
{
|
||||
// Do nothing, it's a singleton.
|
||||
return 1;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsCycleCollectionParticipant::Release(void)
|
||||
{
|
||||
// Do nothing, it's a singleton.
|
||||
return 1;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCycleCollectionParticipant::Unlink(nsISupports *n)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCycleCollectionParticipant::Traverse(nsISupports *n,
|
||||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsCycleCollectionParticipant_h__
|
||||
#define nsCycleCollectionParticipant_h__
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
#define NS_CYCLECOLLECTIONPARTICIPANT_IID \
|
||||
{ /* 407bbd61-13b2-4f85-bdc6-23a59d881f80 */ \
|
||||
0x407bbd61, \
|
||||
0x13b2, \
|
||||
0x4f85, \
|
||||
{ 0xbd, 0xc6, 0x23, 0xa5, 0x9d, 0x88, 0x1f, 0x80 } \
|
||||
}
|
||||
|
||||
#undef IMETHOD_VISIBILITY
|
||||
#define IMETHOD_VISIBILITY NS_VISIBILITY_DEFAULT
|
||||
|
||||
struct nsCycleCollectionTraversalCallback
|
||||
{
|
||||
// You must call DescribeNode() with an accurate refcount,
|
||||
// otherwise cycle collection will fail, and probably crash.
|
||||
// Providing an accurate objsz or objname is optional.
|
||||
virtual void DescribeNode(size_t refcount,
|
||||
size_t objsz,
|
||||
const char *objname) = 0;
|
||||
virtual void NoteScriptChild(PRUint32 langID, void *child) = 0;
|
||||
virtual void NoteXPCOMChild(nsISupports *child) = 0;
|
||||
};
|
||||
|
||||
class NS_COM nsCycleCollectionParticipant
|
||||
: public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONPARTICIPANT_IID)
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Unlink(nsISupports *p);
|
||||
NS_IMETHOD Traverse(nsISupports *p,
|
||||
nsCycleCollectionTraversalCallback &cb);
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant,
|
||||
NS_CYCLECOLLECTIONPARTICIPANT_IID)
|
||||
|
||||
#undef IMETHOD_VISIBILITY
|
||||
#define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for implementing a QI to nsCycleCollectionParticipant
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NS_CYCLE_COLLECTION_INNERCLASS \
|
||||
cycleCollection
|
||||
|
||||
#define NS_CYCLE_COLLECTION_CLASSNAME(_class) \
|
||||
_class::NS_CYCLE_COLLECTION_INNERCLASS
|
||||
|
||||
#define NS_CYCLE_COLLECTION_NAME(_class) \
|
||||
_class##_cycleCollectorGlobal
|
||||
|
||||
#define NS_IMPL_QUERY_CYCLE_COLLECTION(_class) \
|
||||
if ( aIID.Equals(NS_GET_IID(nsCycleCollectionParticipant)) ) { \
|
||||
foundInterface = & NS_CYCLE_COLLECTION_NAME(_class); \
|
||||
} else
|
||||
|
||||
#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class) \
|
||||
NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for implementing nsCycleCollectionParticipant::Unlink
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class, _base) \
|
||||
NS_IMETHODIMP \
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s) \
|
||||
{ \
|
||||
_class *tmp = NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base*, s));
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_field) \
|
||||
tmp->_field = NULL;
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(_field) \
|
||||
tmp->_field.Clear();
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
|
||||
return NS_OK; \
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for implementing nsCycleCollectionParticipant::Traverse
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class) \
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(_class), #_class);
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class, _base) \
|
||||
NS_IMETHODIMP \
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
|
||||
(nsISupports *s, \
|
||||
nsCycleCollectionTraversalCallback &cb) \
|
||||
{ \
|
||||
_class *tmp = NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base*, s)); \
|
||||
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_field) \
|
||||
if (tmp->_field) { cb.NoteXPCOMChild(tmp->_field.get()); }
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(_field) \
|
||||
{ \
|
||||
PRInt32 i; \
|
||||
for (i = 0; i < tmp->_field.Count(); ++i) \
|
||||
if (tmp->_field[i]) \
|
||||
cb.NoteXPCOMChild(tmp->_field[i]); \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \
|
||||
return NS_OK; \
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for implementing a concrete nsCycleCollectionParticipant
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
class NS_CYCLE_COLLECTION_INNERCLASS \
|
||||
: public nsCycleCollectionParticipant \
|
||||
{ \
|
||||
NS_IMETHOD Unlink(nsISupports *n); \
|
||||
NS_IMETHOD Traverse(nsISupports *n, \
|
||||
nsCycleCollectionTraversalCallback &cb); \
|
||||
};
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
static NS_CYCLE_COLLECTION_CLASSNAME(_class) \
|
||||
NS_CYCLE_COLLECTION_NAME(_class);
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_0_AMBIGUOUS(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_1_AMBIGUOUS(_class, _base, _f) \
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_2_AMBIGUOUS(_class, _base, _f1, _f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f1) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f1) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_3_AMBIGUOUS(_class, _base, _f1, _f2, _f3) \
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f1) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_f3) \
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class, _base) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f1) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_f3) \
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_0(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTION_0_AMBIGUOUS(_class, _class)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_1(_class, _f) \
|
||||
NS_IMPL_CYCLE_COLLECTION_1_AMBIGUOUS(_class, _class, _f)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_2(_class, _f1, _f2) \
|
||||
NS_IMPL_CYCLE_COLLECTION_2_AMBIGUOUS(_class, _class, _f1, _f2)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_3(_class, _f1, _f2, _f3) \
|
||||
NS_IMPL_CYCLE_COLLECTION_3_AMBIGUOUS(_class, _class, _f1, _f2, _f3)
|
||||
|
||||
#endif // nsCycleCollectionParticipant_h__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsCycleCollector_h__
|
||||
#define nsCycleCollector_h__
|
||||
|
||||
class nsISupports;
|
||||
class nsDeque;
|
||||
struct nsCycleCollectionTraversalCallback;
|
||||
|
||||
// An nsCycleCollectionLanguageRuntime is a per-language object that
|
||||
// implements language-specific aspects of the cycle collection task.
|
||||
|
||||
struct nsCycleCollectionLanguageRuntime
|
||||
{
|
||||
virtual nsresult BeginCycleCollection() = 0;
|
||||
|
||||
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) = 0;
|
||||
|
||||
virtual nsresult Root(const nsDeque &nodes) = 0;
|
||||
virtual nsresult Unlink(const nsDeque &nodes) = 0;
|
||||
virtual nsresult Unroot(const nsDeque &nodes) = 0;
|
||||
|
||||
virtual nsresult FinishCycleCollection() = 0;
|
||||
};
|
||||
|
||||
|
||||
NS_COM PRBool nsCycleCollector_isScanSafe(nsISupports *n);
|
||||
NS_COM void nsCycleCollector_suspect(nsISupports *n);
|
||||
NS_COM void nsCycleCollector_forget(nsISupports *n);
|
||||
NS_COM void nsCycleCollector_collect();
|
||||
|
||||
// Helpers for interacting with language-identified scripts
|
||||
|
||||
NS_COM void nsCycleCollector_registerRuntime(PRUint32 langID, nsCycleCollectionLanguageRuntime *rt);
|
||||
NS_COM void nsCycleCollector_forgetRuntime(PRUint32 langID);
|
||||
|
||||
#endif // nsCycleCollector_h__
|
|
@ -95,6 +95,7 @@
|
|||
#include "nsXPCOMStrings.h"
|
||||
#include "nsStringBuffer.h"
|
||||
#include "nsCategoryCache.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#if !defined(WINCE) && !defined(XP_OS2)
|
||||
|
@ -270,6 +271,8 @@ void XXXNeverCalled()
|
|||
b.ToString(0, y);
|
||||
}
|
||||
|
||||
nsCycleCollectionParticipant();
|
||||
|
||||
#if !defined(WINCE) && !defined(XP_OS2)
|
||||
NS_NewWindowsRegKey(nsnull);
|
||||
#endif
|
||||
|
|
|
@ -226,6 +226,42 @@ private:
|
|||
const void* mKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* hashkey wrapper using void* KeyType, that sets key to NULL upon
|
||||
* destruction. Relevant only in cases where a memory pointer-scanner
|
||||
* like valgrind might get confused about stale references.
|
||||
*
|
||||
* @see nsTHashtable::EntryType for specification
|
||||
*/
|
||||
|
||||
class nsClearingVoidPtrHashKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
typedef const void* KeyType;
|
||||
typedef const void* KeyTypePointer;
|
||||
|
||||
nsClearingVoidPtrHashKey(const void* key) :
|
||||
mKey(key) { }
|
||||
nsClearingVoidPtrHashKey(const nsClearingVoidPtrHashKey& toCopy) :
|
||||
mKey(toCopy.mKey) { }
|
||||
~nsClearingVoidPtrHashKey() { mKey = NULL; }
|
||||
|
||||
KeyType GetKey() const { return mKey; }
|
||||
KeyTypePointer GetKeyPointer() const { return mKey; }
|
||||
|
||||
PRBool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
|
||||
static PLDHashNumber HashKey(KeyTypePointer aKey)
|
||||
{
|
||||
return NS_PTR_TO_INT32(aKey) >>2;
|
||||
}
|
||||
enum { ALLOW_MEMMOVE = PR_TRUE };
|
||||
|
||||
private:
|
||||
const void* mKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* hashkey wrapper using nsID KeyType
|
||||
*
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
|
||||
#include "nsDebug.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsCycleCollector.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Macros to help detect thread-safety:
|
||||
|
@ -88,6 +89,109 @@ private:
|
|||
|
||||
#endif // NS_DEBUG
|
||||
|
||||
#if PR_BITS_PER_WORD == 32
|
||||
#define NS_PURPLE_BIT ((PRUint32)(1 << 31))
|
||||
#elif PR_BITS_PER_WORD == 64
|
||||
#define NS_PURPLE_BIT ((PRUint64)(1 << 63))
|
||||
#endif
|
||||
|
||||
#define NS_PURPLE_MASK (~NS_PURPLE_BIT)
|
||||
#define NS_PURPLE_BIT_SET(x) ((x) & (NS_PURPLE_BIT))
|
||||
#define NS_SET_PURPLE_BIT(x) ((x) |= (NS_PURPLE_BIT))
|
||||
#define NS_CLEAR_PURPLE_BIT(x) ((x) &= (NS_PURPLE_MASK))
|
||||
#define NS_VALUE_WITHOUT_PURPLE_BIT(x) ((x) & (NS_PURPLE_MASK))
|
||||
|
||||
|
||||
// Support for ISupports classes which interact with cycle collector.
|
||||
|
||||
class nsCycleCollectingAutoRefCnt {
|
||||
|
||||
public:
|
||||
nsCycleCollectingAutoRefCnt()
|
||||
: mValue(0)
|
||||
{}
|
||||
|
||||
nsCycleCollectingAutoRefCnt(nsrefcnt aValue)
|
||||
: mValue(aValue)
|
||||
{
|
||||
NS_CLEAR_PURPLE_BIT(mValue);
|
||||
}
|
||||
|
||||
nsrefcnt incr(nsISupports *owner)
|
||||
{
|
||||
|
||||
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
|
||||
{
|
||||
// The sentinel value "purple bit alone, refcount 0" means
|
||||
// that we're stabilized, during finalization. In this
|
||||
// state we lie about our actual refcount if anyone asks
|
||||
// and say it's 1, which is basically true: the caller who
|
||||
// is deleting us has a reference still.
|
||||
return 1;
|
||||
}
|
||||
|
||||
nsrefcnt tmp = get();
|
||||
PRBool purple = NS_STATIC_CAST(PRBool, NS_PURPLE_BIT_SET(mValue));
|
||||
|
||||
if (NS_UNLIKELY(purple)) {
|
||||
NS_ASSERTION(tmp != 0, "purple ISupports pointer with zero refcnt");
|
||||
nsCycleCollector_forget(owner);
|
||||
}
|
||||
|
||||
mValue = tmp + 1;
|
||||
return mValue;
|
||||
}
|
||||
|
||||
void stabilizeForDeletion(nsISupports *owner)
|
||||
{
|
||||
mValue = NS_PURPLE_BIT;
|
||||
}
|
||||
|
||||
nsrefcnt decr(nsISupports *owner)
|
||||
{
|
||||
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
|
||||
return 1;
|
||||
|
||||
nsrefcnt tmp = get();
|
||||
PRBool purple = NS_STATIC_CAST(PRBool, NS_PURPLE_BIT_SET(mValue));
|
||||
|
||||
if (NS_UNLIKELY(tmp > 1 && !purple)) {
|
||||
nsCycleCollector_suspect(owner);
|
||||
purple = PR_TRUE;
|
||||
|
||||
} else if (NS_UNLIKELY(tmp == 1 && purple)) {
|
||||
nsCycleCollector_forget(owner);
|
||||
purple = PR_FALSE;
|
||||
|
||||
} else {
|
||||
NS_ASSERTION(tmp >= 1, "decr() called with zero refcnt");
|
||||
}
|
||||
|
||||
mValue = tmp - 1;
|
||||
|
||||
if (purple)
|
||||
NS_SET_PURPLE_BIT(mValue);
|
||||
|
||||
return get();
|
||||
}
|
||||
|
||||
nsrefcnt get() const
|
||||
{
|
||||
if (NS_UNLIKELY(mValue == NS_PURPLE_BIT))
|
||||
return 1;
|
||||
|
||||
return NS_VALUE_WITHOUT_PURPLE_BIT(mValue);
|
||||
}
|
||||
|
||||
operator nsrefcnt() const
|
||||
{
|
||||
return get();
|
||||
}
|
||||
|
||||
private:
|
||||
nsrefcnt mValue;
|
||||
};
|
||||
|
||||
class nsAutoRefCnt {
|
||||
|
||||
public:
|
||||
|
@ -126,6 +230,17 @@ protected: \
|
|||
NS_DECL_OWNINGTHREAD \
|
||||
public:
|
||||
|
||||
#define NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
|
||||
public: \
|
||||
NS_IMETHOD QueryInterface(REFNSIID aIID, \
|
||||
void** aInstancePtr); \
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void); \
|
||||
NS_IMETHOD_(nsrefcnt) Release(void); \
|
||||
protected: \
|
||||
nsCycleCollectingAutoRefCnt mRefCnt; \
|
||||
NS_DECL_OWNINGTHREAD \
|
||||
public:
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -229,6 +344,46 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
|
|||
}
|
||||
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(_class, _basetype) \
|
||||
NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void) \
|
||||
{ \
|
||||
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); \
|
||||
NS_ASSERT_OWNINGTHREAD(_class); \
|
||||
mRefCnt.incr(NS_STATIC_CAST(_basetype *, this)); \
|
||||
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
|
||||
return mRefCnt; \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(_class, _class)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _basetype, _destroy) \
|
||||
NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
|
||||
{ \
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release"); \
|
||||
NS_ASSERT_OWNINGTHREAD(_class); \
|
||||
mRefCnt.decr(NS_STATIC_CAST(_basetype *, this)); \
|
||||
NS_LOG_RELEASE(this, mRefCnt, #_class); \
|
||||
if (mRefCnt == 0) { \
|
||||
mRefCnt.stabilizeForDeletion(NS_STATIC_CAST(_basetype *, this)); \
|
||||
_destroy; \
|
||||
return 0; \
|
||||
} \
|
||||
return mRefCnt; \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy) \
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _class, _destroy)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS_WITH_DESTROY(_class, _basetype, _destroy) \
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _basetype, _destroy)
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(_class, _basetype) \
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _basetype, NS_DELETEXPCOM(this))
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(_class, _class, NS_DELETEXPCOM(this))
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "nsAutoLock.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prlog.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCycleCollector.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo *sLog = PR_NewLogModule("nsEventQueue");
|
||||
|
@ -64,31 +66,37 @@ nsEventQueue::~nsEventQueue()
|
|||
PRBool
|
||||
nsEventQueue::GetEvent(PRBool mayWait, nsIRunnable **result)
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
while (IsEmpty()) {
|
||||
if (!mayWait) {
|
||||
if (result)
|
||||
*result = nsnull;
|
||||
return PR_FALSE;
|
||||
{
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
while (IsEmpty()) {
|
||||
if (!mayWait) {
|
||||
if (result)
|
||||
*result = nsnull;
|
||||
return PR_FALSE;
|
||||
}
|
||||
LOG(("EVENTQ(%p): wait begin\n", this));
|
||||
mon.Wait();
|
||||
LOG(("EVENTQ(%p): wait end\n", this));
|
||||
}
|
||||
LOG(("EVENTQ(%p): wait begin\n", this));
|
||||
mon.Wait();
|
||||
LOG(("EVENTQ(%p): wait end\n", this));
|
||||
}
|
||||
|
||||
if (result) {
|
||||
*result = mHead->mEvents[mOffsetHead++];
|
||||
|
||||
// Check if mHead points to empty Page
|
||||
if (mOffsetHead == EVENTS_PER_PAGE) {
|
||||
Page *dead = mHead;
|
||||
mHead = mHead->mNext;
|
||||
FreePage(dead);
|
||||
mOffsetHead = 0;
|
||||
|
||||
if (result) {
|
||||
*result = mHead->mEvents[mOffsetHead++];
|
||||
|
||||
// Check if mHead points to empty Page
|
||||
if (mOffsetHead == EVENTS_PER_PAGE) {
|
||||
Page *dead = mHead;
|
||||
mHead = mHead->mNext;
|
||||
FreePage(dead);
|
||||
mOffsetHead = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCycleCollector_collect();
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче