зеркало из https://github.com/mozilla/gecko-dev.git
Bug 730581, unbind content tree lazily after unlink, r=jst
This commit is contained in:
Родитель
ae146ef230
Коммит
0d972e78e7
|
@ -157,6 +157,8 @@
|
|||
#include "nsCycleCollector.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -4374,6 +4376,102 @@ nsINode::IsEqualNode(nsIDOMNode* aOther, bool* aReturn)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement)
|
||||
|
||||
#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
|
||||
|
||||
class ContentUnbinder : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ContentUnbinder()
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
mLast = this;
|
||||
}
|
||||
|
||||
~ContentUnbinder()
|
||||
{
|
||||
Run();
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
void UnbindSubtree(nsIContent* aNode)
|
||||
{
|
||||
if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
|
||||
aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
nsGenericElement* container = static_cast<nsGenericElement*>(aNode);
|
||||
PRUint32 childCount = container->mAttrsAndChildren.ChildCount();
|
||||
if (childCount) {
|
||||
while (childCount-- > 0) {
|
||||
// Hold a strong ref to the node when we remove it, because we may be
|
||||
// the last reference to it. We need to call TakeChildAt() and
|
||||
// update mFirstChild before calling UnbindFromTree, since this last
|
||||
// can notify various observers and they should really see consistent
|
||||
// tree state.
|
||||
nsCOMPtr<nsIContent> child =
|
||||
container->mAttrsAndChildren.TakeChildAt(childCount);
|
||||
if (childCount == 0) {
|
||||
container->mFirstChild = nsnull;
|
||||
}
|
||||
UnbindSubtree(child);
|
||||
child->UnbindFromTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
PRUint32 len = mSubtreeRoots.Length();
|
||||
if (len) {
|
||||
PRTime start = PR_Now();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
UnbindSubtree(mSubtreeRoots[i]);
|
||||
}
|
||||
mSubtreeRoots.Clear();
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
|
||||
PRUint32(PR_Now() - start) / PR_USEC_PER_MSEC);
|
||||
}
|
||||
if (this == sContentUnbinder) {
|
||||
sContentUnbinder = nsnull;
|
||||
if (mNext) {
|
||||
nsRefPtr<ContentUnbinder> next;
|
||||
next.swap(mNext);
|
||||
sContentUnbinder = next;
|
||||
next->mLast = mLast;
|
||||
mLast = nsnull;
|
||||
NS_DispatchToMainThread(next);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void Append(nsIContent* aSubtreeRoot)
|
||||
{
|
||||
if (!sContentUnbinder) {
|
||||
sContentUnbinder = new ContentUnbinder();
|
||||
nsCOMPtr<nsIRunnable> e = sContentUnbinder;
|
||||
NS_DispatchToMainThread(e);
|
||||
}
|
||||
|
||||
if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
|
||||
SUBTREE_UNBINDINGS_PER_RUNNABLE) {
|
||||
sContentUnbinder->mLast->mNext = new ContentUnbinder();
|
||||
sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
|
||||
}
|
||||
sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoTArray<nsCOMPtr<nsIContent>,
|
||||
SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
|
||||
nsRefPtr<ContentUnbinder> mNext;
|
||||
ContentUnbinder* mLast;
|
||||
static ContentUnbinder* sContentUnbinder;
|
||||
};
|
||||
|
||||
ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
||||
nsINode::Unlink(tmp);
|
||||
|
||||
|
@ -4383,16 +4481,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
|||
}
|
||||
|
||||
// Unlink child content (and unbind our subtree).
|
||||
{
|
||||
if (UnoptimizableCCNode(tmp) || !nsCCUncollectableMarker::sGeneration) {
|
||||
PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
|
||||
if (childCount) {
|
||||
// Don't allow script to run while we're unbinding everything.
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
while (childCount-- > 0) {
|
||||
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree.
|
||||
// We could probably do a non-deep unbind here when IsInDoc is false
|
||||
// for better performance.
|
||||
|
||||
// Hold a strong ref to the node when we remove it, because we may be
|
||||
// the last reference to it. We need to call TakeChildAt() and
|
||||
// update mFirstChild before calling UnbindFromTree, since this last
|
||||
|
@ -4405,7 +4499,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
|||
child->UnbindFromTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
|
||||
ContentUnbinder::Append(tmp);
|
||||
} /* else {
|
||||
The subtree root will end up to a ContentUnbinder, and that will
|
||||
unbind the child nodes.
|
||||
} */
|
||||
|
||||
// Unlink any DOM slots of interest.
|
||||
{
|
||||
|
|
|
@ -82,6 +82,7 @@ class nsIScrollableFrame;
|
|||
class nsAttrValueOrString;
|
||||
class nsContentList;
|
||||
class nsDOMTokenList;
|
||||
class ContentUnbinder;
|
||||
struct nsRect;
|
||||
|
||||
typedef PRUptrdiff PtrBits;
|
||||
|
@ -956,6 +957,7 @@ protected:
|
|||
*/
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
|
||||
friend class ContentUnbinder;
|
||||
/**
|
||||
* Array containing all attributes and children for this element
|
||||
*/
|
||||
|
|
|
@ -70,7 +70,7 @@ HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of J
|
|||
HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
|
||||
HISTOGRAM_BOOLEAN(CYCLE_COLLECTOR_NEED_GC, "Needed garbage collection before cycle collection.")
|
||||
HISTOGRAM(CYCLE_COLLECTOR_TIME_BETWEEN, 1, 120, 50, EXPONENTIAL, "Time spent in between cycle collections (seconds)")
|
||||
|
||||
HISTOGRAM(CYCLE_COLLECTOR_CONTENT_UNBIND, 1, 10000, 50, EXPONENTIAL, "Time spent on one ContentUnbinder (ms)")
|
||||
HISTOGRAM(FORGET_SKIPPABLE_MAX, 1, 10000, 50, EXPONENTIAL, "Max time spent on one forget skippable (ms)")
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче