diff --git a/dom/base/BindContext.h b/dom/base/BindContext.h index 4d2b80e4b60a..7f5fc99a52b4 100644 --- a/dom/base/BindContext.h +++ b/dom/base/BindContext.h @@ -9,8 +9,10 @@ #ifndef mozilla_dom_BindContext_h__ #define mozilla_dom_BindContext_h__ -#include "mozilla/Attributes.h" #include "nsXBLBinding.h" +#include "mozilla/Attributes.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ShadowRoot.h" @@ -18,6 +20,9 @@ namespace mozilla { namespace dom { struct MOZ_STACK_CLASS BindContext final { + struct NestingLevel; + friend struct NestingLevel; + // The document that owns the tree we're getting bound to. // // This is mostly an optimization to avoid silly pointer-chases to get the @@ -49,14 +54,17 @@ struct MOZ_STACK_CLASS BindContext final { Element* GetBindingParent() const { return mBindingParent; } // This constructor should be used for regular appends to content. - explicit BindContext(nsINode& aParentNode) - : mDoc(*aParentNode.OwnerDoc()), - mBindingParent(aParentNode.IsContent() - ? aParentNode.AsContent()->GetBindingParent() + explicit BindContext(nsINode& aParent) + : mDoc(*aParent.OwnerDoc()), + mBindingParent(aParent.IsContent() + ? aParent.AsContent()->GetBindingParent() : nullptr), - mInComposedDoc(aParentNode.IsInComposedDoc()), - mInUncomposedDoc(aParentNode.IsInUncomposedDoc()), - mSubtreeRootChanges(true) {} + mInComposedDoc(aParent.IsInComposedDoc()), + mInUncomposedDoc(aParent.IsInUncomposedDoc()), + mSubtreeRootChanges(true), + mCollectingDisplayedNodeDataDuringLoad( + ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc, + aParent)) {} // When re-binding a shadow host into a tree, we re-bind all the shadow tree // from the root. In that case, the shadow tree contents remain within the @@ -69,7 +77,10 @@ struct MOZ_STACK_CLASS BindContext final { mBindingParent(aShadowRoot.Host()), mInComposedDoc(aShadowRoot.IsInComposedDoc()), mInUncomposedDoc(false), - mSubtreeRootChanges(false) {} + mSubtreeRootChanges(false), + mCollectingDisplayedNodeDataDuringLoad( + ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc, + aShadowRoot)) {} // This constructor is meant to be used when inserting native-anonymous // children into a subtree. @@ -79,7 +90,10 @@ struct MOZ_STACK_CLASS BindContext final { mBindingParent(&aParentElement), mInComposedDoc(aParentElement.IsInComposedDoc()), mInUncomposedDoc(aParentElement.IsInUncomposedDoc()), - mSubtreeRootChanges(true) { + mSubtreeRootChanges(true), + mCollectingDisplayedNodeDataDuringLoad( + ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc, + aParentElement)) { MOZ_ASSERT(mInComposedDoc, "Binding NAC in a disconnected subtree?"); } @@ -89,9 +103,28 @@ struct MOZ_STACK_CLASS BindContext final { mBindingParent(aBinding.GetBoundElement()), mInComposedDoc(aParentElement.IsInComposedDoc()), mInUncomposedDoc(aParentElement.IsInUncomposedDoc()), - mSubtreeRootChanges(true) {} + mSubtreeRootChanges(true), + mCollectingDisplayedNodeDataDuringLoad( + ShouldCollectDisplayedNodeDataDuringLoad(mInComposedDoc, mDoc, + aParentElement)) {} + + bool CollectingDisplayedNodeDataDuringLoad() const { + return mCollectingDisplayedNodeDataDuringLoad; + } private: + + static bool IsLikelyUndisplayed(const nsINode& aParent) { + return aParent.IsAnyOfHTMLElements(nsGkAtoms::style, nsGkAtoms::script); + } + + static bool ShouldCollectDisplayedNodeDataDuringLoad(bool aConnected, + Document& aDoc, + nsINode& aParent) { + return aDoc.GetReadyStateEnum() == Document::READYSTATE_LOADING && + aConnected && !IsLikelyUndisplayed(aParent); + } + Document& mDoc; Element* const mBindingParent; @@ -102,6 +135,27 @@ struct MOZ_STACK_CLASS BindContext final { // Whether the bind operation will change the subtree root of the content // we're binding. const bool mSubtreeRootChanges; + + // Whether it's likely that we're in an undisplayed part of the DOM. + // + // NOTE(emilio): We don't inherit this in BindContext's for Shadow DOM or XBL + // or such. This means that if you have a shadow tree inside an undisplayed + // element it will be incorrectly counted. But given our current definition + // of undisplayed element this is not likely to matter in practice. + bool mCollectingDisplayedNodeDataDuringLoad; +}; + +struct MOZ_STACK_CLASS BindContext::NestingLevel { + explicit NestingLevel(BindContext& aContext, const Element& aParent) + : mRestoreCollecting(aContext.mCollectingDisplayedNodeDataDuringLoad) { + if (aContext.mCollectingDisplayedNodeDataDuringLoad) { + aContext.mCollectingDisplayedNodeDataDuringLoad = + BindContext::IsLikelyUndisplayed(aParent); + } + } + + private: + AutoRestore mRestoreCollecting; }; } // namespace dom diff --git a/dom/base/CharacterData.cpp b/dom/base/CharacterData.cpp index 52b89da7680e..4c3e0f3b717a 100644 --- a/dom/base/CharacterData.cpp +++ b/dom/base/CharacterData.cpp @@ -461,8 +461,13 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) { aParent.AsContent()->GetContainingShadow(); } - if (IsInComposedDoc() && mText.IsBidi()) { - aContext.OwnerDoc().SetBidiEnabled(); + if (IsInComposedDoc()) { + if (mText.IsBidi()) { + aContext.OwnerDoc().SetBidiEnabled(); + } + if (aContext.CollectingDisplayedNodeDataDuringLoad()) { + aContext.OwnerDoc().AddToVisibleContentHeuristic(mText.GetLength()); + } } // Clear the lazy frame construction bits. diff --git a/dom/base/Document.h b/dom/base/Document.h index d7830d65bb7e..12af77b4ff68 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -3699,6 +3699,16 @@ class Document : public nsINode, void PropagateUseCounters(Document* aParentDocument); + void AddToVisibleContentHeuristic(size_t aNumber) { + if (MOZ_UNLIKELY(SIZE_MAX - mVisibleContentHeuristic < aNumber)) { + mVisibleContentHeuristic = SIZE_MAX; + } else { + mVisibleContentHeuristic += aNumber; + } + } + + size_t GetVisibleContentHeuristic() const { return mVisibleContentHeuristic; } + // Called to track whether this document has had any interaction. // This is used to track whether we should permit "beforeunload". void SetUserHasInteracted(); @@ -4895,6 +4905,16 @@ class Document : public nsINode, // The CSS property use counters. UniquePtr mStyleUseCounters; + // An ever-increasing heuristic number that is higher the more content is + // likely to be visible in the page. + // + // Right now it effectively measures amount of text content that has ever been + // connected to the document in some way, and is not under a