Bug 730639 - Blast DOM owned subtrees during canSkip, r=mccr8

This commit is contained in:
Olli Pettay 2012-06-28 01:09:15 +03:00
Родитель 8a67ba9232
Коммит cccfba0c08
6 изменённых файлов: 94 добавлений и 25 удалений

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

@ -57,8 +57,8 @@ class Link;
// IID for the dom::Element interface
#define NS_ELEMENT_IID \
{ 0x8493dd61, 0x4d59, 0x410a, \
{ 0x97, 0x40, 0xd0, 0xa0, 0xc2, 0x03, 0xff, 0x52 } }
{ 0xc6c049a1, 0x96e8, 0x4580, \
{ 0xa6, 0x93, 0xb9, 0x5f, 0x53, 0xbe, 0xe8, 0x1c } }
class Element : public nsIContent
{

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

@ -45,8 +45,8 @@ enum nsLinkState {
// IID for the nsIContent interface
#define NS_ICONTENT_IID \
{ 0xa887c108, 0xc25e, 0x42ab, \
{ 0x87, 0xef, 0xad, 0x4b, 0xee, 0x50, 0x28, 0x28 } }
{ 0x98fb308d, 0xc6dd, 0x4c6d, \
{ 0xb7, 0x7c, 0x91, 0x18, 0x0c, 0xf0, 0x6f, 0x23 } }
/**
* A node of content in a document's content model. This interface
@ -876,6 +876,7 @@ public:
virtual bool IsPurple() = 0;
virtual void RemovePurple() = 0;
virtual bool OwnedOnlyByTheDOMTree() { return false; }
protected:
/**
* Hook for implementing GetID. This is guaranteed to only be

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

@ -265,6 +265,8 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
nsGenericElement::ClearContentUnbinder();
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
if (!obs)
@ -289,7 +291,9 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
!strcmp(aTopic, "cycle-collector-forget-skippable");
bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
if (prepareForCC) {
nsGenericElement::ClearContentUnbinder();
}
// Increase generation to effectivly unmark all current objects
if (!++sGeneration) {

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

@ -309,6 +309,11 @@ protected:
nsTextFragment mText;
public:
virtual bool OwnedOnlyByTheDOMTree()
{
return GetParent() && mRefCnt.get() == 1;
}
virtual bool IsPurple()
{
return mRefCnt.IsPurple();

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

@ -2809,6 +2809,16 @@ public:
return NS_OK;
}
static void UnbindAll()
{
nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
sContentUnbinder = nsnull;
while (ub) {
ub->Run();
ub = ub->mNext;
}
}
static void Append(nsIContent* aSubtreeRoot)
{
if (!sContentUnbinder) {
@ -2835,6 +2845,12 @@ private:
ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull;
void
nsGenericElement::ClearContentUnbinder()
{
ContentUnbinder::UnbindAll();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
nsINode::Unlink(tmp);
@ -3078,19 +3094,29 @@ nsGenericElement::CanSkipInCC(nsINode* aNode)
}
nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nsnull;
nsAutoTArray<nsIContent*, 1020>* gNodesToUnbind = nsnull;
void ClearPurpleRoots()
void ClearCycleCollectorCleanupData()
{
if (!gPurpleRoots) {
return;
if (gPurpleRoots) {
PRUint32 len = gPurpleRoots->Length();
for (PRUint32 i = 0; i < len; ++i) {
nsINode* n = gPurpleRoots->ElementAt(i);
n->SetIsPurpleRoot(false);
}
delete gPurpleRoots;
gPurpleRoots = nsnull;
}
PRUint32 len = gPurpleRoots->Length();
for (PRUint32 i = 0; i < len; ++i) {
nsINode* n = gPurpleRoots->ElementAt(i);
n->SetIsPurpleRoot(false);
if (gNodesToUnbind) {
PRUint32 len = gNodesToUnbind->Length();
for (PRUint32 i = 0; i < len; ++i) {
nsIContent* c = gNodesToUnbind->ElementAt(i);
c->SetIsPurpleRoot(false);
ContentUnbinder::Append(c);
}
delete gNodesToUnbind;
gNodesToUnbind = nsnull;
}
delete gPurpleRoots;
gPurpleRoots = nsnull;
}
static bool
@ -3164,8 +3190,8 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
return false;
}
// Subtree has been traversed already, and aNode
// wasn't removed from purple buffer. No need to do more here.
// Subtree has been traversed already, and aNode has
// been handled in a way that doesn't require revisiting it.
if (root->IsPurpleRoot()) {
return false;
}
@ -3175,8 +3201,12 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
nsAutoTArray<nsIContent*, 1020> nodesToClear;
bool foundBlack = root->IsBlack();
bool domOnlyCycle = false;
if (root != currentDoc) {
currentDoc = nsnull;
if (!foundBlack) {
domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
}
if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
nodesToClear.AppendElement(static_cast<nsIContent*>(root));
}
@ -3190,6 +3220,7 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
node = node->GetNextNode(root)) {
foundBlack = foundBlack || node->IsBlack();
if (foundBlack) {
domOnlyCycle = false;
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
@ -3202,19 +3233,36 @@ nsGenericElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
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);
} else {
domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
if (ShouldClearPurple(node)) {
// Collect interesting nodes which we can clear if we find that
// they are kept alive in a black tree or are in a DOM-only cycle.
nodesToClear.AppendElement(node);
}
}
}
if (!currentDoc || !foundBlack) {
if (!gPurpleRoots) {
gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
}
root->SetIsPurpleRoot(true);
gPurpleRoots->AppendElement(root);
if (domOnlyCycle) {
if (!gNodesToUnbind) {
gNodesToUnbind = new nsAutoTArray<nsIContent*, 1020>();
}
gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) {
nsIContent* n = nodesToClear[i];
if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
n->RemovePurple();
}
}
return true;
} else {
if (!gPurpleRoots) {
gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
}
gPurpleRoots->AppendElement(root);
}
}
if (!foundBlack) {
@ -3261,7 +3309,7 @@ nsGenericElement::CanSkipThis(nsINode* aNode)
void
nsGenericElement::InitCCCallbacks()
{
nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots);
nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
}

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

@ -591,6 +591,16 @@ public:
*/
void FireNodeRemovedForChildren();
virtual bool OwnedOnlyByTheDOMTree()
{
PRUint32 rc = mRefCnt.get();
if (GetParent()) {
--rc;
}
rc -= mAttrsAndChildren.ChildCount();
return rc == 0;
}
virtual bool IsPurple()
{
return mRefCnt.IsPurple();
@ -601,6 +611,7 @@ public:
mRefCnt.RemovePurple();
}
static void ClearContentUnbinder();
static bool CanSkip(nsINode* aNode, bool aRemovingAllowed);
static bool CanSkipInCC(nsINode* aNode);
static bool CanSkipThis(nsINode* aNode);