зеркало из 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 "nsCycleCollector.h"
|
||||||
#include "xpcpublic.h"
|
#include "xpcpublic.h"
|
||||||
#include "xpcprivate.h"
|
#include "xpcprivate.h"
|
||||||
|
#include "nsLayoutStatics.h"
|
||||||
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
@ -4374,6 +4376,102 @@ nsINode::IsEqualNode(nsIDOMNode* aOther, bool* aReturn)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement)
|
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)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
||||||
nsINode::Unlink(tmp);
|
nsINode::Unlink(tmp);
|
||||||
|
|
||||||
|
@ -4383,16 +4481,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink child content (and unbind our subtree).
|
// Unlink child content (and unbind our subtree).
|
||||||
{
|
if (UnoptimizableCCNode(tmp) || !nsCCUncollectableMarker::sGeneration) {
|
||||||
PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
|
PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
|
||||||
if (childCount) {
|
if (childCount) {
|
||||||
// Don't allow script to run while we're unbinding everything.
|
// Don't allow script to run while we're unbinding everything.
|
||||||
nsAutoScriptBlocker scriptBlocker;
|
nsAutoScriptBlocker scriptBlocker;
|
||||||
while (childCount-- > 0) {
|
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
|
// 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
|
// the last reference to it. We need to call TakeChildAt() and
|
||||||
// update mFirstChild before calling UnbindFromTree, since this last
|
// update mFirstChild before calling UnbindFromTree, since this last
|
||||||
|
@ -4405,7 +4499,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
||||||
child->UnbindFromTree();
|
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.
|
// Unlink any DOM slots of interest.
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,6 +82,7 @@ class nsIScrollableFrame;
|
||||||
class nsAttrValueOrString;
|
class nsAttrValueOrString;
|
||||||
class nsContentList;
|
class nsContentList;
|
||||||
class nsDOMTokenList;
|
class nsDOMTokenList;
|
||||||
|
class ContentUnbinder;
|
||||||
struct nsRect;
|
struct nsRect;
|
||||||
|
|
||||||
typedef PRUptrdiff PtrBits;
|
typedef PRUptrdiff PtrBits;
|
||||||
|
@ -956,6 +957,7 @@ protected:
|
||||||
*/
|
*/
|
||||||
virtual void GetLinkTarget(nsAString& aTarget);
|
virtual void GetLinkTarget(nsAString& aTarget);
|
||||||
|
|
||||||
|
friend class ContentUnbinder;
|
||||||
/**
|
/**
|
||||||
* Array containing all attributes and children for this element
|
* 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(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_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_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)")
|
HISTOGRAM(FORGET_SKIPPABLE_MAX, 1, 10000, 50, EXPONENTIAL, "Max time spent on one forget skippable (ms)")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче