зеркало из https://github.com/mozilla/gecko-dev.git
Bug 730639 - Blast DOM owned subtrees during canSkip, r=mccr8
This commit is contained in:
Родитель
8a67ba9232
Коммит
cccfba0c08
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче