зеркало из https://github.com/mozilla/pjs.git
Bug 721515 - Add Documents, elements and textnodes to BBP, r=mccr8,jst
--HG-- extra : rebase_source : 103b7572ee627990ed53f214b31d0badfdb05585
This commit is contained in:
Родитель
3ae2c4060e
Коммит
134cb7d4b7
|
@ -78,8 +78,8 @@ enum nsLinkState {
|
|||
|
||||
// IID for the nsIContent interface
|
||||
#define NS_ICONTENT_IID \
|
||||
{ 0xdc68f070, 0x226d, 0x11e1, \
|
||||
{ 0xbf, 0xc2, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
|
||||
{ 0x94671671, 0x9e1b, 0x447a, \
|
||||
{ 0xad, 0xb7, 0xc3, 0x2e, 0x05, 0x6a, 0x96, 0xc9 } }
|
||||
|
||||
/**
|
||||
* A node of content in a document's content model. This interface
|
||||
|
@ -948,6 +948,9 @@ public:
|
|||
|
||||
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
|
||||
|
||||
virtual bool IsPurple() = 0;
|
||||
virtual void RemovePurple() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Hook for implementing GetID. This is guaranteed to only be
|
||||
|
|
|
@ -288,8 +288,8 @@ private:
|
|||
|
||||
// IID for the nsINode interface
|
||||
#define NS_INODE_IID \
|
||||
{ 0xd026d280, 0x5b25, 0x41c0, \
|
||||
{ 0x92, 0xcf, 0x6, 0xf6, 0xf, 0xb, 0x9a, 0xfe } }
|
||||
{ 0xfcd3b0d1, 0x75db, 0x46c4, \
|
||||
{ 0xa1, 0xf5, 0x07, 0xc2, 0x09, 0xf8, 0x1f, 0x44 } }
|
||||
|
||||
/**
|
||||
* An internal interface that abstracts some DOMNode-related parts that both
|
||||
|
@ -1223,6 +1223,13 @@ private:
|
|||
NodeIsCommonAncestorForRangeInSelection,
|
||||
// Set if the node is a descendant of a node with the above bit set.
|
||||
NodeIsDescendantOfCommonAncestorForRangeInSelection,
|
||||
// Set if CanSkipInCC check has been done for this subtree root.
|
||||
NodeIsCCMarkedRoot,
|
||||
// Maybe set if this node is in black subtree.
|
||||
NodeIsCCBlackTree,
|
||||
// Maybe set if the node is a root of a subtree
|
||||
// which needs to be kept in the purple buffer.
|
||||
NodeIsPurpleRoot,
|
||||
// Guard value
|
||||
BooleanFlagCount
|
||||
};
|
||||
|
@ -1270,6 +1277,16 @@ public:
|
|||
void ClearDescendantOfCommonAncestorForRangeInSelection()
|
||||
{ ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
|
||||
|
||||
void SetCCMarkedRoot(bool aValue)
|
||||
{ SetBoolFlag(NodeIsCCMarkedRoot, aValue); }
|
||||
bool CCMarkedRoot() const { return GetBoolFlag(NodeIsCCMarkedRoot); }
|
||||
void SetInCCBlackTree(bool aValue)
|
||||
{ SetBoolFlag(NodeIsCCBlackTree, aValue); }
|
||||
bool InCCBlackTree() const { return GetBoolFlag(NodeIsCCBlackTree); }
|
||||
void SetIsPurpleRoot(bool aValue)
|
||||
{ SetBoolFlag(NodeIsPurpleRoot, aValue); }
|
||||
bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); }
|
||||
|
||||
protected:
|
||||
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
|
||||
void SetInDocument() { SetBoolFlag(IsInDocument); }
|
||||
|
|
|
@ -421,6 +421,8 @@ nsContentUtils::Init()
|
|||
"dom.event.handling-user-input-time-limit",
|
||||
1000);
|
||||
|
||||
nsGenericElement::InitCCCallbacks();
|
||||
|
||||
sInitialized = true;
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -1722,6 +1722,18 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument,
|
||||
nsNodeUtils::LastRelease(this))
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
|
||||
return nsGenericElement::CanSkip(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
|
||||
return nsGenericElement::CanSkipInCC(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
|
||||
return nsGenericElement::CanSkipThis(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
static PLDHashOperator
|
||||
SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
|
||||
void *arg)
|
||||
|
|
|
@ -880,8 +880,8 @@ public:
|
|||
MaybeRescheduleAnimationFrameNotifications();
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
|
||||
nsIDocument)
|
||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
|
||||
nsIDocument)
|
||||
|
||||
void DoNotifyPossibleTitleChange();
|
||||
|
||||
|
|
|
@ -97,6 +97,18 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericDOMDataNode)
|
|||
nsINode::Trace(tmp, aCallback, aClosure);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode)
|
||||
return nsGenericElement::CanSkip(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode)
|
||||
return nsGenericElement::CanSkipInCC(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode)
|
||||
return nsGenericElement::CanSkipThis(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode)
|
||||
// Always need to traverse script objects, so do that before we check
|
||||
// if we're uncollectable.
|
||||
|
|
|
@ -272,7 +272,7 @@ public:
|
|||
void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const;
|
||||
#endif
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode)
|
||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode)
|
||||
|
||||
protected:
|
||||
virtual mozilla::dom::Element* GetNameSpaceElement()
|
||||
|
@ -348,6 +348,16 @@ protected:
|
|||
|
||||
nsTextFragment mText;
|
||||
|
||||
public:
|
||||
virtual bool IsPurple()
|
||||
{
|
||||
return mRefCnt.IsPurple();
|
||||
}
|
||||
virtual void RemovePurple()
|
||||
{
|
||||
mRefCnt.RemovePurple();
|
||||
}
|
||||
|
||||
private:
|
||||
already_AddRefed<nsIAtom> GetCurrentValueAtom();
|
||||
};
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
#include "nsSVGFeatures.h"
|
||||
#include "nsDOMMemoryReporter.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
|
||||
#include "nsCycleCollector.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "xpcprivate.h"
|
||||
|
||||
|
@ -1208,11 +1208,20 @@ nsINode::Trace(nsINode *tmp, TraceCallback cb, void *closure)
|
|||
nsContentUtils::TraceWrapper(tmp, cb, closure);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsXBL(nsINode* aNode)
|
||||
|
||||
static
|
||||
bool UnoptimizableCCNode(nsINode* aNode)
|
||||
{
|
||||
return aNode->IsElement() &&
|
||||
aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL);
|
||||
const PtrBits problematicFlags = (NODE_IS_ANONYMOUS |
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE |
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT |
|
||||
NODE_MAY_BE_IN_BINDING_MNGR |
|
||||
NODE_IS_INSERTION_PARENT);
|
||||
return aNode->HasFlag(problematicFlags) ||
|
||||
aNode->NodeType() == nsIDOMNode::ATTRIBUTE_NODE ||
|
||||
// For strange cases like xbl:content/xbl:children
|
||||
(aNode->IsElement() &&
|
||||
aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL));
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -1227,18 +1236,11 @@ nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
|
|||
|
||||
if (nsCCUncollectableMarker::sGeneration) {
|
||||
// If we're black no need to traverse.
|
||||
if (tmp->IsBlack()) {
|
||||
if (tmp->IsBlack() || tmp->InCCBlackTree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PtrBits problematicFlags =
|
||||
(NODE_IS_ANONYMOUS |
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE |
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT |
|
||||
NODE_MAY_BE_IN_BINDING_MNGR |
|
||||
NODE_IS_INSERTION_PARENT);
|
||||
|
||||
if (!tmp->HasFlag(problematicFlags) && !IsXBL(tmp)) {
|
||||
if (!UnoptimizableCCNode(tmp)) {
|
||||
// If we're in a black document, return early.
|
||||
if ((currentDoc && currentDoc->IsBlack())) {
|
||||
return false;
|
||||
|
@ -1246,7 +1248,7 @@ nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
|
|||
// If we're not in anonymous content and we have a black parent,
|
||||
// return early.
|
||||
nsIContent* parent = tmp->GetParent();
|
||||
if (parent && !IsXBL(parent) && parent->IsBlack()) {
|
||||
if (parent && !UnoptimizableCCNode(parent) && parent->IsBlack()) {
|
||||
NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?");
|
||||
return false;
|
||||
}
|
||||
|
@ -4256,6 +4258,382 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement)
|
|||
nsINode::Trace(tmp, aCallback, aClosure);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
static JSObject*
|
||||
GetJSObjectChild(nsINode* aNode)
|
||||
{
|
||||
if (aNode->PreservingWrapper()) {
|
||||
return aNode->GetWrapperPreserveColor();
|
||||
}
|
||||
return aNode->GetExpandoObjectPreserveColor();
|
||||
}
|
||||
|
||||
static bool
|
||||
NeedsScriptTraverse(nsINode* aNode)
|
||||
{
|
||||
JSObject* o = GetJSObjectChild(aNode);
|
||||
return o && xpc_IsGrayGCThing(o);
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
|
||||
void* aData)
|
||||
{
|
||||
PRUint32* gen = static_cast<PRUint32*>(aData);
|
||||
xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
|
||||
void* aChild, void* aData)
|
||||
{
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wjs =
|
||||
do_QueryInterface(static_cast<nsISupports*>(aChild));
|
||||
xpc_UnmarkGrayObject(wjs);
|
||||
}
|
||||
|
||||
static void
|
||||
MarkNodeChildren(nsINode* aNode)
|
||||
{
|
||||
JSObject* o = GetJSObjectChild(aNode);
|
||||
xpc_UnmarkGrayObject(o);
|
||||
|
||||
nsEventListenerManager* elm = aNode->GetListenerManager(false);
|
||||
if (elm) {
|
||||
elm->UnmarkGrayJSListeners();
|
||||
}
|
||||
|
||||
if (aNode->HasProperties()) {
|
||||
nsIDocument* ownerDoc = aNode->OwnerDoc();
|
||||
ownerDoc->PropertyTable(DOM_USER_DATA)->
|
||||
Enumerate(aNode, nsGenericElement::MarkUserData,
|
||||
&nsCCUncollectableMarker::sGeneration);
|
||||
ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
|
||||
Enumerate(aNode, nsGenericElement::MarkUserDataHandler,
|
||||
&nsCCUncollectableMarker::sGeneration);
|
||||
}
|
||||
}
|
||||
|
||||
nsINode*
|
||||
FindOptimizableSubtreeRoot(nsINode* aNode)
|
||||
{
|
||||
nsINode* p;
|
||||
while ((p = aNode->GetNodeParent())) {
|
||||
if (UnoptimizableCCNode(aNode)) {
|
||||
return nsnull;
|
||||
}
|
||||
aNode = p;
|
||||
}
|
||||
|
||||
if (UnoptimizableCCNode(aNode)) {
|
||||
return nsnull;
|
||||
}
|
||||
return aNode;
|
||||
}
|
||||
|
||||
nsAutoTArray<nsINode*, 1020>* gCCBlackMarkedNodes = nsnull;
|
||||
|
||||
void
|
||||
ClearBlackMarkedNodes()
|
||||
{
|
||||
if (!gCCBlackMarkedNodes) {
|
||||
return;
|
||||
}
|
||||
PRUint32 len = gCCBlackMarkedNodes->Length();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
nsINode* n = gCCBlackMarkedNodes->ElementAt(i);
|
||||
n->SetCCMarkedRoot(false);
|
||||
n->SetInCCBlackTree(false);
|
||||
}
|
||||
delete gCCBlackMarkedNodes;
|
||||
gCCBlackMarkedNodes = nsnull;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsGenericElement::CanSkipInCC(nsINode* aNode)
|
||||
{
|
||||
// Don't try to optimize anything during shutdown.
|
||||
if (nsCCUncollectableMarker::sGeneration == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail out early if aNode is somewhere in anonymous content,
|
||||
// or otherwise unusual.
|
||||
if (UnoptimizableCCNode(aNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* currentDoc = aNode->GetCurrentDoc();
|
||||
if (currentDoc &&
|
||||
nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
|
||||
return !NeedsScriptTraverse(aNode);
|
||||
}
|
||||
|
||||
nsINode* root =
|
||||
currentDoc ? static_cast<nsINode*>(currentDoc) :
|
||||
FindOptimizableSubtreeRoot(aNode);
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtree has been traversed already.
|
||||
if (root->CCMarkedRoot()) {
|
||||
return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
|
||||
}
|
||||
|
||||
if (!gCCBlackMarkedNodes) {
|
||||
gCCBlackMarkedNodes = new nsAutoTArray<nsINode*, 1020>;
|
||||
}
|
||||
|
||||
// nodesToUnpurple contains nodes which will be removed
|
||||
// from the purple buffer if the DOM tree is black.
|
||||
nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
|
||||
// grayNodes need script traverse, so they aren't removed from
|
||||
// the purple buffer, but are marked to be in black subtree so that
|
||||
// traverse is faster.
|
||||
nsAutoTArray<nsINode*, 1020> grayNodes;
|
||||
|
||||
bool foundBlack = root->IsBlack();
|
||||
if (root != currentDoc) {
|
||||
currentDoc = nsnull;
|
||||
if (NeedsScriptTraverse(root)) {
|
||||
grayNodes.AppendElement(root);
|
||||
} else if (static_cast<nsIContent*>(root)->IsPurple()) {
|
||||
nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse the subtree and check if we could know without CC
|
||||
// that it is black.
|
||||
// Note, this traverse is non-virtual and inline, so it should be a lot faster
|
||||
// than CC's generic traverse.
|
||||
for (nsIContent* node = root->GetFirstChild(); node;
|
||||
node = node->GetNextNode(root)) {
|
||||
foundBlack = foundBlack || node->IsBlack();
|
||||
if (foundBlack && currentDoc) {
|
||||
// If we can mark the whole document black, no need to optimize
|
||||
// so much, since when the next purple node in the document will be
|
||||
// handled, it is fast to check that currentDoc is in CCGeneration.
|
||||
break;
|
||||
}
|
||||
if (NeedsScriptTraverse(node)) {
|
||||
// Gray nodes need real CC traverse.
|
||||
grayNodes.AppendElement(node);
|
||||
} else if (node->IsPurple()) {
|
||||
nodesToUnpurple.AppendElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
root->SetCCMarkedRoot(true);
|
||||
root->SetInCCBlackTree(foundBlack);
|
||||
gCCBlackMarkedNodes->AppendElement(root);
|
||||
|
||||
if (!foundBlack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentDoc) {
|
||||
// Special case documents. If we know the document is black,
|
||||
// we can mark the document to be in CCGeneration.
|
||||
currentDoc->
|
||||
MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
|
||||
} else {
|
||||
for (PRUint32 i = 0; i < grayNodes.Length(); ++i) {
|
||||
nsINode* node = grayNodes[i];
|
||||
node->SetInCCBlackTree(true);
|
||||
}
|
||||
gCCBlackMarkedNodes->AppendElements(grayNodes);
|
||||
}
|
||||
|
||||
// Subtree is black, we can remove non-gray purple nodes from
|
||||
// purple buffer.
|
||||
for (PRUint32 i = 0; i < nodesToUnpurple.Length(); ++i) {
|
||||
nsIContent* purple = nodesToUnpurple[i];
|
||||
// Can't remove currently handled purple node.
|
||||
if (purple != aNode) {
|
||||
purple->RemovePurple();
|
||||
}
|
||||
}
|
||||
return !NeedsScriptTraverse(aNode);
|
||||
}
|
||||
|
||||
nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nsnull;
|
||||
|
||||
void ClearPurpleRoots()
|
||||
{
|
||||
if (!gPurpleRoots) {
|
||||
return;
|
||||
}
|
||||
PRUint32 len = gPurpleRoots->Length();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
nsINode* n = gPurpleRoots->ElementAt(i);
|
||||
n->SetIsPurpleRoot(false);
|
||||
}
|
||||
delete gPurpleRoots;
|
||||
gPurpleRoots = nsnull;
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldClearPurple(nsIContent* aContent)
|
||||
{
|
||||
if (aContent && aContent->IsPurple()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* o = GetJSObjectChild(aContent);
|
||||
if (o && xpc_IsGrayGCThing(o)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aContent->GetListenerManager(false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return aContent->HasProperties();
|
||||
}
|
||||
|
||||
// CanSkip checks if aNode is black, and if it is, returns
|
||||
// true. If aNode is in a black DOM tree, CanSkip may also remove other objects
|
||||
// from purple buffer and unmark event listeners and user data.
|
||||
// If the root of the DOM tree is a document, less optimizations are done
|
||||
// since checking the blackness of the current document is usually fast and we
|
||||
// don't want slow down such common cases.
|
||||
bool
|
||||
nsGenericElement::CanSkip(nsINode* aNode)
|
||||
{
|
||||
// Don't try to optimize anything during shutdown.
|
||||
if (nsCCUncollectableMarker::sGeneration == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail out early if aNode is somewhere in anonymous content,
|
||||
// or otherwise unusual.
|
||||
if (UnoptimizableCCNode(aNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* currentDoc = aNode->GetCurrentDoc();
|
||||
if (currentDoc &&
|
||||
nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
|
||||
MarkNodeChildren(aNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
|
||||
FindOptimizableSubtreeRoot(aNode);
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtree has been traversed already, and aNode
|
||||
// wasn't removed from purple buffer. No need to do more here.
|
||||
if (root->IsPurpleRoot()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// nodesToClear contains nodes which are either purple or
|
||||
// gray.
|
||||
nsAutoTArray<nsIContent*, 1020> nodesToClear;
|
||||
|
||||
bool foundBlack = root->IsBlack();
|
||||
if (root != currentDoc) {
|
||||
currentDoc = nsnull;
|
||||
if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
|
||||
nodesToClear.AppendElement(static_cast<nsIContent*>(root));
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse the subtree and check if we could know without CC
|
||||
// that it is black.
|
||||
// Note, this traverse is non-virtual and inline, so it should be a lot faster
|
||||
// than CC's generic traverse.
|
||||
for (nsIContent* node = root->GetFirstChild(); node;
|
||||
node = node->GetNextNode(root)) {
|
||||
foundBlack = foundBlack || node->IsBlack();
|
||||
if (foundBlack) {
|
||||
if (currentDoc) {
|
||||
// If we can mark the whole document black, no need to optimize
|
||||
// so much, since when the next purple node in the document will be
|
||||
// handled, it is fast to check that the currentDoc is in CCGeneration.
|
||||
break;
|
||||
}
|
||||
// No need to put stuff to the nodesToClear array, if we can clear it
|
||||
// already here.
|
||||
if (node->IsPurple() && node != aNode) {
|
||||
node->RemovePurple();
|
||||
}
|
||||
MarkNodeChildren(node);
|
||||
} else if (ShouldClearPurple(node)) {
|
||||
// Collect interesting nodes which we can clear if we find that
|
||||
// they are kept alive in a black tree.
|
||||
nodesToClear.AppendElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundBlack) {
|
||||
if (!gPurpleRoots) {
|
||||
gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
|
||||
}
|
||||
root->SetIsPurpleRoot(true);
|
||||
gPurpleRoots->AppendElement(root);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentDoc) {
|
||||
// Special case documents. If we know the document is black,
|
||||
// we can mark the document to be in CCGeneration.
|
||||
currentDoc->
|
||||
MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
|
||||
MarkNodeChildren(currentDoc);
|
||||
}
|
||||
|
||||
// Subtree is black, so we can remove purple nodes from
|
||||
// purple buffer and mark stuff that to be certainly alive.
|
||||
for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) {
|
||||
nsIContent* n = nodesToClear[i];
|
||||
MarkNodeChildren(n);
|
||||
// Can't remove currently handled purple node.
|
||||
if (n != aNode && n->IsPurple()) {
|
||||
n->RemovePurple();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGenericElement::CanSkipThis(nsINode* aNode)
|
||||
{
|
||||
if (nsCCUncollectableMarker::sGeneration == 0) {
|
||||
return false;
|
||||
}
|
||||
if (aNode->IsBlack()) {
|
||||
return true;
|
||||
}
|
||||
nsIDocument* c = aNode->GetCurrentDoc();
|
||||
return
|
||||
((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
|
||||
aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
|
||||
}
|
||||
|
||||
void
|
||||
nsGenericElement::InitCCCallbacks()
|
||||
{
|
||||
nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots);
|
||||
nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericElement)
|
||||
return nsGenericElement::CanSkip(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericElement)
|
||||
return nsGenericElement::CanSkipInCC(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericElement)
|
||||
return nsGenericElement::CanSkipThis(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
static const char* kNSURIs[] = {
|
||||
" ([none])",
|
||||
" (xmlns)",
|
||||
|
|
|
@ -602,7 +602,7 @@ public:
|
|||
*/
|
||||
virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement)
|
||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericElement)
|
||||
|
||||
virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
|
||||
{
|
||||
|
@ -613,6 +613,24 @@ public:
|
|||
*/
|
||||
void FireNodeRemovedForChildren();
|
||||
|
||||
virtual bool IsPurple()
|
||||
{
|
||||
return mRefCnt.IsPurple();
|
||||
}
|
||||
|
||||
virtual void RemovePurple()
|
||||
{
|
||||
mRefCnt.RemovePurple();
|
||||
}
|
||||
|
||||
static bool CanSkip(nsINode* aNode);
|
||||
static bool CanSkipInCC(nsINode* aNode);
|
||||
static bool CanSkipThis(nsINode* aNode);
|
||||
static void InitCCCallbacks();
|
||||
static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
|
||||
void *aData);
|
||||
static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild,
|
||||
void* aData);
|
||||
protected:
|
||||
/**
|
||||
* Set attribute and (if needed) notify documentobservers and fire off
|
||||
|
|
Загрузка…
Ссылка в новой задаче