Bug 1363805 - Part 3: Do lazy flushing if possible. r=heycam

Skips flushing current document if the target of getComputedDOMStyle cannot be
affected by any pending restyles.

MozReview-Commit-ID: C87HDIDvOth

--HG--
extra : rebase_source : 064880493f9aac2599689cdd0749200bb579c60b
This commit is contained in:
Wei-Cheng Pan 2017-05-23 12:02:11 +08:00
Родитель 619e849e4c
Коммит f3f406f76b
7 изменённых файлов: 136 добавлений и 8 удалений

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

@ -569,6 +569,20 @@ EffectCompositor::HasThrottledStyleUpdates() const
return false;
}
bool
EffectCompositor::HasPendingStyleUpdatesFor(Element* aElement) const
{
for (auto& elementSet : mElementsToRestyle) {
for (auto iter = elementSet.ConstIter(); !iter.Done(); iter.Next()) {
if (iter.Key().mElement->Contains(aElement)) {
return true;
}
}
}
return false;
}
void
EffectCompositor::AddStyleUpdatesTo(RestyleTracker& aTracker)
{

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

@ -168,6 +168,7 @@ public:
bool HasPendingStyleUpdates() const;
bool HasThrottledStyleUpdates() const;
bool HasPendingStyleUpdatesFor(dom::Element* aElement) const;
// Tell the restyle tracker about all the animated styles that have
// pending updates so that it can update the animation rule for these

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

@ -3651,6 +3651,12 @@ GeckoRestyleManager::ComputeAndProcessStyleChange(
ClearCachedInheritedStyleDataOnDescendants(contextsToClear);
}
bool
GeckoRestyleManager::HasPendingRestyles() const
{
return mPendingRestyles.Count() != 0;
}
nsStyleSet*
ElementRestyler::StyleSet() const
{

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

@ -345,6 +345,7 @@ public:
#endif
bool IsProcessingRestyles() { return mIsProcessingRestyles; }
bool HasPendingRestyles() const;
private:
inline nsStyleSet* StyleSet() const {

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

@ -1196,11 +1196,16 @@ ServoRestyleManager::ProcessPendingRestyles()
void
ServoRestyleManager::ProcessAllPendingAttributeAndStateInvalidations()
{
AutoTimelineMarker marker(mPresContext->GetDocShell(),
"ProcessAllPendingAttributeAndStateInvalidations");
if (mSnapshots.IsEmpty()) {
return;
}
for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
// Servo data for the element might have been dropped. (e.g. by removing
// from its document)
if (iter.Key()->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
Servo_ProcessInvalidations(StyleSet()->RawSet(), iter.Key(), &mSnapshots);
}
}
ClearSnapshots();
}

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

@ -38,6 +38,7 @@
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "mozilla/GeckoRestyleManager.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/RestyleManagerInlines.h"
#include "imgIRequest.h"
#include "nsLayoutUtils.h"
@ -101,6 +102,71 @@ GetBackgroundList(T nsStyleImageLayers::Layer::* aMember,
return valueList.forget();
}
// Whether there is any pending restyle for the element or any of its ancestors.
static bool
ContentNeedsRestyle(nsIContent* aContent)
{
MOZ_ASSERT(aContent);
nsIContent* node = aContent;
while (node) {
// Check if the element has any flag for restyling. For Gecko, we also need
// another flag to know if there is any child has LaterSiblings restyle
// hint.
if (node->HasFlag(ELEMENT_ALL_RESTYLE_FLAGS |
ELEMENT_HAS_CHILD_WITH_LATER_SIBLINGS_HINT)) {
return true;
}
node = node->GetFlattenedTreeParent();
}
return false;
}
// Whether aDocument needs to restyle for aElement
static bool
DocumentNeedsRestyle(const nsIDocument* aDocument, Element* aElement)
{
nsIPresShell* shell = aDocument->GetShell();
if (!shell) {
return true;
}
// Unfortunately we don't know if the sheet change affects mContent or not, so
// just assume it will and that we need to flush normally.
StyleSetHandle styleSet = shell->StyleSet();
if (styleSet->StyleSheetsHaveChanged()) {
return true;
}
// If any ancestor has pending animation, flush it.
nsPresContext* context = shell->GetPresContext();
if (context->EffectCompositor()->HasPendingStyleUpdatesFor(aElement)) {
return true;
}
if (styleSet->IsServo()) {
// For Servo, we need to process the restyle-hint-invalidations first, to
// expand LaterSiblings hint, so that we can look whether ancestors need
// restyling.
ServoRestyleManager* restyleManager = context->RestyleManager()->AsServo();
restyleManager->ProcessAllPendingAttributeAndStateInvalidations();
// Then if there is a restyle root, we check if the root is an ancestor of
// this content. If it is not, then we don't need to restyle immediately.
// Note this is different from Gecko: we only check if any ancestor needs
// to restyle _itself_, not descendants, since dirty descendants can be
// another subtree.
if (aDocument->GetServoRestyleRoot() &&
restyleManager->HasPendingRestyleAncestor(aElement)) {
return true;
}
} else {
// For Gecko, first check if there is any pending restyle, then we check if
// any ancestor has dirty bits for restyle.
GeckoRestyleManager* restyleManager = context->RestyleManager()->AsGecko();
if (restyleManager->HasPendingRestyles() && ContentNeedsRestyle(aElement)) {
return true;
}
}
return false;
}
/**
* An object that represents the ordered set of properties that are exposed on
* an nsComputedDOMStyle object and how their computed values can be obtained.
@ -807,6 +873,32 @@ nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext,
mStyleContextGeneration = aGeneration;
}
FlushTarget
nsComputedDOMStyle::GetFlushTarget(nsIDocument* aDocument) const
{
// If mContent is not in the same document, we could do some checks to know if
// there are some pending restyles can be ignored across documents (since we
// will use the caller document's style), but it can be complicated and should
// be an edge case, so we just don't bother to do the optimization in this
// case.
if (aDocument != mContent->OwnerDoc()) {
return FlushTarget::Normal;
}
if (DocumentNeedsRestyle(aDocument, mContent->AsElement())) {
return FlushTarget::Normal;
}
// If parent document is there, also needs to check if there is some change
// that needs to flush this document (e.g. size change for iframe).
while (nsIDocument* parentDocument = aDocument->GetParentDocument()) {
Element* element = parentDocument->FindContentForSubDocument(aDocument);
if (DocumentNeedsRestyle(parentDocument, element)) {
return FlushTarget::Normal;
}
aDocument = parentDocument;
}
return FlushTarget::ParentOnly;
}
void
nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
{
@ -816,12 +908,15 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
return;
}
// If the property we are computing relies on layout, then we must flush.
FlushTarget target = aNeedsLayoutFlush ? FlushTarget::Normal : GetFlushTarget(document);
// Flush _before_ getting the presshell, since that could create a new
// presshell. Also note that we want to flush the style on the document
// we're computing style in, not on the document mContent is in -- the two
// may be different.
document->FlushPendingNotifications(
aNeedsLayoutFlush ? FlushType::Layout : FlushType::Style);
aNeedsLayoutFlush ? FlushType::Layout : FlushType::Style, target);
#ifdef DEBUG
mFlushedPendingReflows = aNeedsLayoutFlush;
#endif
@ -933,9 +1028,11 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
// No need to re-get the generation, even though GetStyleContext
// will flush, since we flushed style at the top of this function.
NS_ASSERTION(mPresShell &&
// We don't need to check this if we only flushed the parent.
NS_ASSERTION(target == FlushTarget::ParentOnly ||
(mPresShell &&
currentGeneration ==
mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(),
mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration()),
"why should we have flushed style again?");
SetResolvedStyleContext(Move(resolvedStyleContext), currentGeneration);

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

@ -715,6 +715,10 @@ private:
void BasicShapeRadiiToString(nsAString& aCssText,
const nsStyleCorners& aCorners);
// Find out if we can safely skip flushing for aDocument (i.e. pending
// restyles does not affect mContent).
mozilla::FlushTarget GetFlushTarget(nsIDocument* aDocument) const;
static nsComputedStyleMap* GetComputedStyleMap();