Bug 721515 - Add Documents, elements and textnodes to BBP, r=mccr8,jst

--HG--
extra : rebase_source : 103b7572ee627990ed53f214b31d0badfdb05585
This commit is contained in:
Olli Pettay 2012-01-30 22:08:13 +02:00
parent bc55d7028a
commit ee55c49fb2
9 changed files with 475 additions and 23 deletions

View File

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

View File

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

View File

@ -421,6 +421,8 @@ nsContentUtils::Init()
"dom.event.handling-user-input-time-limit",
1000);
nsGenericElement::InitCCCallbacks();
sInitialized = true;
return NS_OK;

View File

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

View File

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

View File

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

View File

@ -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();
};

View File

@ -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)",

View File

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