Index: Source/core/frame/FrameView.cpp |
diff --git a/Source/core/frame/FrameView.cpp b/Source/core/frame/FrameView.cpp |
index 8a39570e8b213508269616b772b35f1171af8afe..223155c17d9fa5fb0bc4cbcfc98a12eef69eda58 100644 |
--- a/Source/core/frame/FrameView.cpp |
+++ b/Source/core/frame/FrameView.cpp |
@@ -108,7 +108,7 @@ FrameView::FrameView(LocalFrame* frame) |
, m_canHaveScrollbars(true) |
, m_slowRepaintObjectCount(0) |
, m_hasPendingLayout(false) |
- , m_layoutSubtreeRoot(0) |
+ , m_isSubtreeLayout(false) |
, m_inSynchronousPostLayout(false) |
, m_postLayoutTasksTimer(this, &FrameView::postLayoutTimerFired) |
, m_updateWidgetsTimer(this, &FrameView::updateWidgetsTimerFired) |
@@ -227,7 +227,7 @@ void FrameView::trace(Visitor* visitor) |
void FrameView::reset() |
{ |
m_hasPendingLayout = false; |
- m_layoutSubtreeRoot = nullptr; |
+ m_isSubtreeLayout = false; |
m_doFullPaintInvalidation = false; |
m_layoutSchedulingEnabled = true; |
m_inPerformLayout = false; |
@@ -764,9 +764,32 @@ bool FrameView::isEnclosedInCompositingLayer() const |
return frameOwnerRenderer && frameOwnerRenderer->enclosingLayer()->enclosingLayerForPaintInvalidationCrossingFrameBoundaries(); |
} |
-LayoutObject* FrameView::layoutRoot(bool onlyDuringLayout) const |
+static inline void countObjectsNeedingLayoutInRoot(const LayoutObject* root, unsigned& needsLayoutObjects, unsigned& totalObjects) |
{ |
- return onlyDuringLayout && layoutPending() ? nullptr : m_layoutSubtreeRoot; |
+ for (const LayoutObject* o = root; o; o = o->nextInPreOrder(root)) { |
+ ++totalObjects; |
+ if (o->needsLayout()) |
+ ++needsLayoutObjects; |
+ } |
+} |
+ |
+void FrameView::countObjectsNeedingLayout(unsigned& needsLayoutObjects, unsigned& totalObjects, bool& isSubtree) |
+{ |
+ needsLayoutObjects = 0; |
+ totalObjects = 0; |
+ if (isSubtreeLayout()) { |
+ isSubtree = true; |
+ for (auto& subtreeRoot : m_layoutSubtreeRoots) |
+ countObjectsNeedingLayoutInRoot(subtreeRoot, needsLayoutObjects, totalObjects); |
+ } else { |
+ isSubtree = false; |
+ countObjectsNeedingLayoutInRoot(m_frame->document()->renderView(), needsLayoutObjects, totalObjects); |
+ } |
+} |
+ |
+bool FrameView::isLayoutRoot(const LayoutObject* object) const |
+{ |
+ return m_layoutSubtreeRoots.contains(const_cast<LayoutObject*>(object)); |
} |
inline void FrameView::forceLayoutParentViewIfNeeded() |
@@ -835,8 +858,43 @@ void FrameView::lineLayoutTime(double ms) |
m_lineLayoutMs += ms; |
} |
-void FrameView::performLayout(LayoutObject* rootForThisLayout, bool inSubtreeLayout) |
+static void gatherDebugLayoutRects(LayoutObject& layoutRoot) |
+{ |
+ bool isTracing; |
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"), &isTracing); |
+ if (!isTracing) |
+ return; |
+ if (!layoutRoot.enclosingLayer()->hasCompositedLayerMapping()) |
+ return; |
+ // For access to compositedLayerMapping(). |
+ DisableCompositingQueryAsserts disabler; |
+ GraphicsLayer* graphicsLayer = layoutRoot.enclosingLayer()->compositedLayerMapping()->mainGraphicsLayer(); |
+ if (!graphicsLayer) |
+ return; |
+ |
+ GraphicsLayerDebugInfo& debugInfo = graphicsLayer->debugInfo(); |
+ |
+ debugInfo.currentLayoutRects().clear(); |
+ for (LayoutObject* renderer = &layoutRoot; renderer; renderer = renderer->nextInPreOrder()) { |
+ if (renderer->layoutDidGetCalledSinceLastFrame()) { |
+ FloatQuad quad = renderer->localToAbsoluteQuad(FloatQuad(renderer->previousPaintInvalidationRect())); |
+ LayoutRect rect = quad.enclosingBoundingBox(); |
+ debugInfo.currentLayoutRects().append(rect); |
+ } |
+ } |
+} |
+ |
+static inline void layoutFromRootObject(LayoutObject& root) |
{ |
+ LayoutState layoutState(root); |
+ root.layout(); |
+ gatherDebugLayoutRects(root); |
+} |
+ |
+void FrameView::performLayout(bool inSubtreeLayout) |
+{ |
+ ASSERT(inSubtreeLayout || m_layoutSubtreeRoots.isEmpty()); |
+ |
m_lineLayoutMs = 0; |
TRACE_EVENT0("blink,benchmark", "FrameView::performLayout"); |
double start = WTF::currentTimeMS(); |
@@ -852,12 +910,24 @@ void FrameView::performLayout(LayoutObject* rootForThisLayout, bool inSubtreeLay |
// FIXME: The 300 other lines in layout() probably belong in other helper functions |
// so that a single human could understand what layout() is actually doing. |
- LayoutState layoutState(*rootForThisLayout); |
- |
forceLayoutParentViewIfNeeded(); |
- rootForThisLayout->layout(); |
- gatherDebugLayoutRects(rootForThisLayout); |
+ if (inSubtreeLayout) { |
+ while (m_layoutSubtreeRoots.size()) { |
+ LayoutObject& root = *m_layoutSubtreeRoots.takeAny(); |
+ if (!root.needsLayout()) |
+ continue; |
+ layoutFromRootObject(root); |
+ |
+ // We need to ensure that we mark up all renderers up to the RenderView |
+ // for paint invalidation. This simplifies our code as we just always |
+ // do a full tree walk. |
+ if (LayoutObject* container = root.container()) |
+ container->setMayNeedPaintInvalidation(); |
+ } |
+ } else { |
+ layoutFromRootObject(*m_frame->document()->renderView()); |
+ } |
ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->updateAllImageResourcePriorities(); |
@@ -933,7 +1003,9 @@ void FrameView::layout() |
Document* document = m_frame->document(); |
bool inSubtreeLayout = isSubtreeLayout(); |
- LayoutObject* rootForThisLayout = inSubtreeLayout ? m_layoutSubtreeRoot : document->renderView(); |
+ |
+ // FIXME: The notion of a single root for layout is no longer applicable. Remove or update this code. crbug.com/460596 |
+ LayoutObject* rootForThisLayout = inSubtreeLayout ? *(m_layoutSubtreeRoots.begin()) : renderView(); |
if (!rootForThisLayout) { |
// FIXME: Do we need to set m_size here? |
ASSERT_NOT_REACHED(); |
@@ -941,13 +1013,12 @@ void FrameView::layout() |
} |
FontCachePurgePreventer fontCachePurgePreventer; |
- Layer* layer; |
{ |
TemporaryChange<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false); |
m_nestedLayoutCount++; |
if (!inSubtreeLayout) { |
- Document* document = m_frame->document(); |
+ clearLayoutSubtreeRootsAndMarkContainingBlocks(); |
Node* body = document->body(); |
if (body && body->renderer()) { |
if (isHTMLFrameSetElement(*body)) { |
@@ -976,7 +1047,7 @@ void FrameView::layout() |
m_firstLayout = false; |
m_firstLayoutCallbackPending = true; |
m_lastViewportSize = layoutSize(IncludeScrollbars); |
- m_lastZoomFactor = rootForThisLayout->style()->zoom(); |
+ m_lastZoomFactor = renderView()->style()->zoom(); |
// Set the initial vMode to AlwaysOn if we're auto. |
if (vMode == ScrollbarAuto) |
@@ -1012,43 +1083,37 @@ void FrameView::layout() |
m_doFullPaintInvalidation |= renderView()->shouldDoFullPaintInvalidationForNextLayout(); |
} |
- layer = rootForThisLayout->enclosingLayer(); |
- |
- performLayout(rootForThisLayout, inSubtreeLayout); |
+ performLayout(inSubtreeLayout); |
- m_layoutSubtreeRoot = nullptr; |
- // We need to ensure that we mark up all renderers up to the RenderView |
- // for paint invalidation. This simplifies our code as we just always |
- // do a full tree walk. |
- if (LayoutObject* container = rootForThisLayout->container()) |
- container->setMayNeedPaintInvalidation(); |
+ m_layoutSubtreeRoots.clear(); |
+ m_isSubtreeLayout = false; |
} // Reset m_layoutSchedulingEnabled to its previous value. |
- if (!inSubtreeLayout && !toRenderView(rootForThisLayout)->document().printing()) |
+ if (!inSubtreeLayout && !document->printing()) |
adjustViewSize(); |
- layer->updateLayerPositionsAfterLayout(); |
+ // FIXME: Could find the common ancestor layer of all dirty subtrees and mark from there. |
+ renderView()->enclosingLayer()->updateLayerPositionsAfterLayout(); |
renderView()->compositor()->didLayout(); |
m_layoutCount++; |
- if (AXObjectCache* cache = rootForThisLayout->document().axObjectCache()) { |
- const KURL& url = rootForThisLayout->document().url(); |
+ if (AXObjectCache* cache = document->axObjectCache()) { |
+ const KURL& url = document->url(); |
if (url.isValid() && !url.isAboutBlankURL()) |
- cache->handleLayoutComplete(rootForThisLayout); |
+ cache->handleLayoutComplete(document); |
} |
updateAnnotatedRegions(); |
- ASSERT(!rootForThisLayout->needsLayout()); |
- |
if (document->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) |
updateOverflowStatus(layoutSize().width() < contentsWidth(), layoutSize().height() < contentsHeight()); |
scheduleOrPerformPostLayoutTasks(); |
+ // FIXME: The notion of a single root for layout is no longer applicable. Remove or update this code. crbug.com/460596 |
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Layout", "endData", InspectorLayoutEvent::endData(rootForThisLayout)); |
- InspectorInstrumentation::didLayout(m_frame.get(), rootForThisLayout); |
+ InspectorInstrumentation::didLayout(m_frame.get()); |
m_nestedLayoutCount--; |
if (m_nestedLayoutCount) |
@@ -1056,7 +1121,7 @@ void FrameView::layout() |
#if ENABLE(ASSERT) |
// Post-layout assert that nobody was re-marked as needing layout during layout. |
- document->renderView()->assertSubtreeIsLaidOut(); |
+ renderView()->assertSubtreeIsLaidOut(); |
#endif |
// FIXME: It should be not possible to remove the FrameView from the frame/page during layout |
@@ -1109,32 +1174,6 @@ DocumentLifecycle& FrameView::lifecycle() const |
return m_frame->document()->lifecycle(); |
} |
-void FrameView::gatherDebugLayoutRects(LayoutObject* layoutRoot) |
-{ |
- bool isTracing; |
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"), &isTracing); |
- if (!isTracing) |
- return; |
- if (!layoutRoot->enclosingLayer()->hasCompositedLayerMapping()) |
- return; |
- // For access to compositedLayerMapping(). |
- DisableCompositingQueryAsserts disabler; |
- GraphicsLayer* graphicsLayer = layoutRoot->enclosingLayer()->compositedLayerMapping()->mainGraphicsLayer(); |
- if (!graphicsLayer) |
- return; |
- |
- GraphicsLayerDebugInfo& debugInfo = graphicsLayer->debugInfo(); |
- |
- debugInfo.currentLayoutRects().clear(); |
- for (LayoutObject* renderer = layoutRoot; renderer; renderer = renderer->nextInPreOrder()) { |
- if (renderer->layoutDidGetCalledSinceLastFrame()) { |
- FloatQuad quad = renderer->localToAbsoluteQuad(FloatQuad(renderer->previousPaintInvalidationRect())); |
- LayoutRect rect = quad.enclosingBoundingBox(); |
- debugInfo.currentLayoutRects().append(rect); |
- } |
- } |
-} |
- |
RenderBox* FrameView::embeddedContentBox() const |
{ |
RenderView* renderView = this->renderView(); |
@@ -1811,14 +1850,25 @@ void FrameView::handleLoadCompleted() |
maintainScrollPositionAtAnchor(0); |
} |
+void FrameView::clearLayoutSubtreeRoot(const LayoutObject* root) |
+{ |
+ m_layoutSubtreeRoots.remove(const_cast<LayoutObject*>(root)); |
+ if (m_layoutSubtreeRoots.isEmpty()) |
+ m_isSubtreeLayout = false; |
+} |
+ |
+void FrameView::clearLayoutSubtreeRootsAndMarkContainingBlocks() |
+{ |
+ for (auto& iter : m_layoutSubtreeRoots) |
+ iter->markContainingBlocksForLayout(false); |
+ m_layoutSubtreeRoots.clear(); |
+} |
+ |
void FrameView::scheduleRelayout() |
{ |
ASSERT(m_frame->view() == this); |
- if (isSubtreeLayout()) { |
- m_layoutSubtreeRoot->markContainingBlocksForLayout(false); |
- m_layoutSubtreeRoot = nullptr; |
- } |
+ m_isSubtreeLayout = false; |
if (!m_layoutSchedulingEnabled) |
return; |
if (!needsLayout()) |
@@ -1829,21 +1879,13 @@ void FrameView::scheduleRelayout() |
if (m_hasPendingLayout) |
return; |
+ clearLayoutSubtreeRootsAndMarkContainingBlocks(); |
m_hasPendingLayout = true; |
page()->animator().scheduleVisualUpdate(m_frame.get()); |
lifecycle().ensureStateAtMost(DocumentLifecycle::StyleClean); |
} |
-static bool isObjectAncestorContainerOf(LayoutObject* ancestor, LayoutObject* descendant) |
-{ |
- for (LayoutObject* r = descendant; r; r = r->container()) { |
- if (r == ancestor) |
- return true; |
- } |
- return false; |
-} |
- |
void FrameView::scheduleRelayoutOfSubtree(LayoutObject* relayoutRoot) |
{ |
ASSERT(m_frame->view() == this); |
@@ -1854,33 +1896,20 @@ void FrameView::scheduleRelayoutOfSubtree(LayoutObject* relayoutRoot) |
RenderView* renderView = this->renderView(); |
if (renderView && renderView->needsLayout()) { |
+ ASSERT(!m_isSubtreeLayout); |
if (relayoutRoot) |
relayoutRoot->markContainingBlocksForLayout(false); |
return; |
} |
- if (layoutPending() || !m_layoutSchedulingEnabled) { |
- if (m_layoutSubtreeRoot != relayoutRoot) { |
- if (isObjectAncestorContainerOf(m_layoutSubtreeRoot, relayoutRoot)) { |
- // Keep the current root |
- relayoutRoot->markContainingBlocksForLayout(false, m_layoutSubtreeRoot); |
- ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); |
- } else if (isSubtreeLayout() && isObjectAncestorContainerOf(relayoutRoot, m_layoutSubtreeRoot)) { |
- // Re-root at relayoutRoot |
- m_layoutSubtreeRoot->markContainingBlocksForLayout(false, relayoutRoot); |
- m_layoutSubtreeRoot = relayoutRoot; |
- ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); |
- } else { |
- // Just do a full relayout |
- if (isSubtreeLayout()) |
- m_layoutSubtreeRoot->markContainingBlocksForLayout(false); |
- m_layoutSubtreeRoot = nullptr; |
- relayoutRoot->markContainingBlocksForLayout(false); |
- } |
- } |
- } else if (m_layoutSchedulingEnabled) { |
- m_layoutSubtreeRoot = relayoutRoot; |
- ASSERT(!m_layoutSubtreeRoot->container() || !m_layoutSubtreeRoot->container()->needsLayout()); |
+ if (relayoutRoot == renderView) { |
+ m_isSubtreeLayout = false; |
+ clearLayoutSubtreeRootsAndMarkContainingBlocks(); |
+ } else { |
+ m_isSubtreeLayout = true; |
+ m_layoutSubtreeRoots.add(relayoutRoot); |
+ } |
+ if (m_layoutSchedulingEnabled) { |
m_hasPendingLayout = true; |
page()->animator().scheduleVisualUpdate(m_frame.get()); |