Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Unified Diff: third_party/WebKit/Source/core/frame/FrameView.cpp

Issue 1246173002: Throttle rendering pipeline for invisible iframes (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebased to post merge awesomeness. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/core/frame/FrameView.h ('k') | third_party/WebKit/Source/core/frame/LocalFrame.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/frame/FrameView.cpp
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index a956cd4ee393e62f18998302b8d94965cdf65e8a..7f7ede60546cb005c30531a41b7bac2861e5c688 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -130,6 +130,7 @@ FrameView::FrameView(LocalFrame* frame)
, m_didScrollTimer(this, &FrameView::didScrollTimerFired)
, m_topControlsViewportAdjustment(0)
, m_needsUpdateWidgetPositions(false)
+ , m_needsUpdateViewportVisibility(true)
#if ENABLE(ASSERT)
, m_hasBeenDisposed(false)
#endif
@@ -142,7 +143,8 @@ FrameView::FrameView(LocalFrame* frame)
, m_inUpdateScrollbars(false)
, m_clipsRepaints(true)
, m_frameTimingRequestsDirty(true)
-
+ , m_viewportVisibility(ViewportVisibility::Visible)
+ , m_securityOriginStatusForThrottling(SecurityOriginStatus::IsSameOrigin)
{
ASSERT(m_frame);
init();
@@ -1084,6 +1086,9 @@ void FrameView::layout()
// See http://crbug.com/306706
void FrameView::invalidateTreeIfNeeded(PaintInvalidationState& paintInvalidationState)
{
+ if (shouldThrottleRenderingPipeline())
+ return;
+
lifecycle().advanceTo(DocumentLifecycle::InPaintInvalidation);
ASSERT(layoutView());
@@ -1293,6 +1298,8 @@ bool FrameView::shouldSetCursor() const
void FrameView::scrollContentsIfNeededRecursive()
{
+ if (shouldThrottleStyleLayoutAndCompositingUpdates())
+ return;
scrollContentsIfNeeded();
for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
@@ -1303,6 +1310,17 @@ void FrameView::scrollContentsIfNeededRecursive()
}
}
+void FrameView::setLifecycleThrottlingModeForSubtree(DocumentLifecycle::ThrottlingMode throttlingMode)
+{
+ ASSERT(m_frame->isLocalRoot());
+ for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) {
+ if (!frame->isLocalFrame())
+ continue;
+ if (FrameView* view = toLocalFrame(frame)->view())
+ view->lifecycle().setThrottlingMode(throttlingMode);
+ }
+}
+
bool FrameView::invalidateViewportConstrainedObjects()
{
for (const auto& viewportConstrainedObject : *m_viewportConstrainedObjects) {
@@ -1728,7 +1746,8 @@ void FrameView::scheduleRelayout()
return;
m_hasPendingLayout = true;
- page()->animator().scheduleVisualUpdate(m_frame.get());
+ if (!shouldThrottleRenderingPipeline())
+ page()->animator().scheduleVisualUpdate(m_frame.get());
lifecycle().ensureStateAtMost(DocumentLifecycle::StyleClean);
}
@@ -2418,18 +2437,18 @@ void FrameView::updateWidgetPositionsIfNeeded()
updateWidgetPositions();
}
-void FrameView::updateAllLifecyclePhases(const LayoutRect& interestRect)
+void FrameView::updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode throttlingMode, const LayoutRect& interestRect)
{
- frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(AllPhases, interestRect);
+ frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(AllPhases, throttlingMode, interestRect);
}
// TODO(chrishtr): add a scrolling update lifecycle phase.
-void FrameView::updateLifecycleToCompositingCleanPlusScrolling()
+void FrameView::updateLifecycleToCompositingCleanPlusScrolling(DocumentLifecycle::ThrottlingMode throttlingMode)
{
- frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToCompositingCleanPlusScrolling);
+ frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToCompositingCleanPlusScrolling, throttlingMode, LayoutRect::infiniteRect());
}
-void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, const LayoutRect& interestRect)
+void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, DocumentLifecycle::ThrottlingMode throttlingMode, const LayoutRect& interestRect)
{
// This must be called from the root frame, since it recurses down, not up.
// Otherwise the lifecycles of the frames might be out of sync.
@@ -2438,6 +2457,7 @@ void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, cons
// Updating layout can run script, which can tear down the FrameView.
RefPtrWillBeRawPtr<FrameView> protector(this);
+ setLifecycleThrottlingModeForSubtree(throttlingMode);
updateStyleAndLayoutIfNeededRecursive();
if (LayoutView* view = layoutView()) {
@@ -2532,6 +2552,9 @@ void FrameView::updateFrameTimingRequestsIfNeeded()
void FrameView::updateStyleAndLayoutIfNeededRecursive()
{
+ if (shouldThrottleStyleLayoutAndCompositingUpdates())
+ return;
+
// We have to crawl our entire subtree looking for any FrameViews that need
// layout and make sure they are up to date.
// Mac actually tests for intersection with the dirty region and tries not to
@@ -2583,6 +2606,11 @@ void FrameView::updateStyleAndLayoutIfNeededRecursive()
void FrameView::invalidateTreeIfNeededRecursive()
{
ASSERT(layoutView());
+
+ // We need to stop recursing here since a child frame view might not be throttled
+ // even though we are (e.g., it didn't compute its visibility yet).
+ if (shouldThrottleRenderingPipeline())
+ return;
TRACE_EVENT1("blink", "FrameView::invalidateTreeIfNeededRecursive", "root", layoutView()->debugName().ascii());
Vector<LayoutObject*> pendingDelayedPaintInvalidations;
@@ -2901,6 +2929,7 @@ void FrameView::setParent(Widget* parentView)
toFrameView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
updateScrollableAreaSet();
+ m_needsUpdateViewportVisibility = true;
}
void FrameView::removeChild(Widget* child)
@@ -2958,6 +2987,7 @@ void FrameView::frameRectsChanged()
if (layoutSizeFixedToFrameSize())
setLayoutSizeInternal(frameRect().size());
+ m_needsUpdateViewportVisibility = true;
for (const auto& child : m_children)
child->frameRectsChanged();
}
@@ -3739,11 +3769,16 @@ void FrameView::paint(GraphicsContext* context, const IntRect& rect)
void FrameView::paint(GraphicsContext* context, const GlobalPaintFlags globalPaintFlags, const IntRect& rect)
{
+ // TODO(skyostil): Remove this early-out in favor of painting cached scrollbars.
+ if (shouldThrottleRenderingPipeline())
+ return;
FramePainter(*this).paint(context, globalPaintFlags, rect);
}
void FrameView::paintContents(GraphicsContext* context, const GlobalPaintFlags globalPaintFlags, const IntRect& damageRect)
{
+ if (shouldThrottleRenderingPipeline())
+ return;
FramePainter(*this).paintContents(context, globalPaintFlags, damageRect);
}
@@ -3931,6 +3966,9 @@ void FrameView::collectFrameTimingRequests(GraphicsLayerFrameTimingRequests& gra
LocalFrame* localFrame = toLocalFrame(frame);
LayoutRect viewRect = localFrame->contentLayoutObject()->viewRect();
const LayoutBoxModelObject* paintInvalidationContainer = localFrame->contentLayoutObject()->containerForPaintInvalidation();
+ // If the frame is being throttled, its compositing state may not be up to date.
+ if (!paintInvalidationContainer->enclosingLayer()->isAllowedToQueryCompositingState())
+ return;
const GraphicsLayer* graphicsLayer = paintInvalidationContainer->enclosingLayer()->graphicsLayerBacking();
if (!graphicsLayer)
@@ -3941,4 +3979,107 @@ void FrameView::collectFrameTimingRequests(GraphicsLayerFrameTimingRequests& gra
graphicsLayerTimingRequests.add(graphicsLayer, Vector<std::pair<int64_t, WebRect>>()).storedValue->value.append(std::make_pair(m_frame->frameID(), enclosingIntRect(viewRect)));
}
+void FrameView::updateViewportVisibilityIfNeeded()
+{
+ // TODO(skyostil): Replace this with an intersection observer.
+
+ // Without slimming paint v2 we can sometimes end up back in LayoutClean
+ // during painting (e.g., because of block selection gaps).
+ if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+ ASSERT(shouldThrottleRenderingPipeline() || lifecycle().state() >= DocumentLifecycle::PaintInvalidationClean);
+ else
+ ASSERT(shouldThrottleRenderingPipeline() || lifecycle().state() >= DocumentLifecycle::LayoutClean);
+ if (!m_needsUpdateViewportVisibility)
+ return;
+
+ // Our frame's bounds in our parent's content coordinates.
+ m_clippedBounds = frameRect();
+ m_viewportVisibility = ViewportVisibility::Visible;
+ m_needsUpdateViewportVisibility = false;
+
+ FrameView* parent = parentFrameView();
+ if (!parent)
+ return;
+ ASSERT(!parent->m_needsUpdateViewportVisibility);
+
+ // If our parent is hidden, then we are too.
+ if (parent->m_viewportVisibility == ViewportVisibility::Hidden) {
+ m_viewportVisibility = ViewportVisibility::Hidden;
+ return;
+ }
+
+ // Transform our bounds into the root frame's content coordinate space.
+ m_clippedBounds = parent->contentsToRootFrame(m_clippedBounds);
+
+ // TODO(skyostil): Expand the viewport to make it less likely to see stale content while scrolling.
+ IntRect viewport = parent->m_clippedBounds;
+ m_clippedBounds.intersect(viewport);
+
+ // If our bounds got clipped away, we are invisible.
+ if (m_clippedBounds.isEmpty())
+ m_viewportVisibility = ViewportVisibility::Hidden;
+}
+
+void FrameView::finalizeLifecycleUpdate()
+{
+ ASSERT(m_frame->isLocalRoot());
+ updateThrottlingRecursive(SecurityOriginStatus::IsSameOrigin);
+}
+
+void FrameView::updateThrottlingRecursive(SecurityOriginStatus securityOriginStatus)
+{
+ ASSERT(!isInPerformLayout());
+ // Without slimming paint v2 we can sometimes end up back in LayoutClean
+ // during painting (e.g., because of block selection gaps -- see
+ // BlockFlowPainter::paintSelection).
+ if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
+ ASSERT(shouldThrottleRenderingPipeline() || lifecycle().state() >= DocumentLifecycle::PaintInvalidationClean);
+ else
+ ASSERT(shouldThrottleRenderingPipeline() || lifecycle().state() >= DocumentLifecycle::LayoutClean);
+ bool wasThrottled = shouldThrottleRenderingPipeline();
+
+ updateViewportVisibilityIfNeeded();
+
+ // We only throttle the rendering pipeline in cross-origin frames. This is
+ // to avoid a situation where an ancestor frame directly depends on the
+ // pipeline timing of a descendant and breaks as a result of throttling.
+ // The rationale is that cross-origin frames must already communicate with
+ // asynchronous messages, so they should be able to tolerate some delay in
+ // receiving replies from a throttled peer.
+ //
+ // Check if we can access our parent's security origin.
+ if (securityOriginStatus == SecurityOriginStatus::IsSameOrigin && parentFrameView()) {
+ const SecurityOrigin* origin = frame().securityContext()->securityOrigin();
+ const SecurityOrigin* parentOrigin = parentFrameView()->frame().securityContext()->securityOrigin();
+ if (!origin->canAccess(parentOrigin))
+ securityOriginStatus = SecurityOriginStatus::IsCrossOrigin;
+ }
+ m_securityOriginStatusForThrottling = securityOriginStatus;
+
+ bool becameUnthrottled = wasThrottled && !shouldThrottleRenderingPipeline();
+ if (becameUnthrottled)
+ page()->animator().scheduleVisualUpdate(m_frame.get());
+
+ for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
+ if (!child->isLocalFrame())
+ continue;
+ if (FrameView* view = toLocalFrame(child)->view())
+ view->updateThrottlingRecursive(securityOriginStatus);
+ }
+}
+
+bool FrameView::shouldThrottleStyleLayoutAndCompositingUpdates() const
+{
+ return lifecycle().throttlingMode() == DocumentLifecycle::ThrottlingMode::Allow
+ && shouldThrottleRenderingPipeline();
+}
+
+bool FrameView::shouldThrottleRenderingPipeline() const
+{
+ if (!RuntimeEnabledFeatures::renderingPipelineThrottlingEnabled())
+ return false;
+ return m_viewportVisibility == ViewportVisibility::Hidden
+ && m_securityOriginStatusForThrottling == SecurityOriginStatus::IsCrossOrigin;
+}
+
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/core/frame/FrameView.h ('k') | third_party/WebKit/Source/core/frame/LocalFrame.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698