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

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

Issue 1364063007: Throttle rendering pipeline for invisible frames (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: IntersectionObserverification Created 5 years, 2 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
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 d53a8a73c432037dc734dc2e4868b9d675873f84..1f23640e13d02aa4fb5a5aa37cb655337ae03fd4 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -93,9 +93,11 @@
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/GraphicsLayerDebugInfo.h"
#include "platform/graphics/paint/DisplayItemList.h"
+#include "platform/scheduler/CancellableTaskFactory.h"
#include "platform/scroll/ScrollAnimator.h"
#include "platform/text/TextStream.h"
#include "public/platform/WebDisplayItemList.h"
+#include "public/platform/WebFrameScheduler.h"
#include "wtf/CurrentTime.h"
#include "wtf/StdLibExtras.h"
#include "wtf/TemporaryChange.h"
@@ -119,6 +121,7 @@ FrameView::FrameView(LocalFrame* frame)
, m_inSynchronousPostLayout(false)
, m_postLayoutTasksTimer(this, &FrameView::postLayoutTimerFired)
, m_updateWidgetsTimer(this, &FrameView::updateWidgetsTimerFired)
+ , m_intersectionObserverNotificationFactory(CancellableTaskFactory::create(this, &FrameView::notifyIntersectionObservers))
, m_isTransparent(false)
, m_baseBackgroundColor(Color::white)
, m_mediaType(MediaTypeNames::screen)
@@ -130,6 +133,7 @@ FrameView::FrameView(LocalFrame* frame)
, m_didScrollTimer(this, &FrameView::didScrollTimerFired)
, m_topControlsViewportAdjustment(0)
, m_needsUpdateWidgetPositions(false)
+ , m_needsUpdateViewportIntersection(true)
#if ENABLE(ASSERT)
, m_hasBeenDisposed(false)
#endif
@@ -141,7 +145,9 @@ FrameView::FrameView(LocalFrame* frame)
, m_scrollbarsSuppressed(false)
, m_inUpdateScrollbars(false)
, m_frameTimingRequestsDirty(true)
-
+ , m_viewportIntersectionValid(false)
+ , m_viewportVisibilityForThrottling(ViewportVisibility::Visible)
+ , m_securityOriginStatusForThrottling(SecurityOriginStatus::IsSameOrigin)
{
ASSERT(m_frame);
init();
@@ -267,6 +273,7 @@ void FrameView::dispose()
if (m_didScrollTimer.isActive())
m_didScrollTimer.stop();
+ m_intersectionObserverNotificationFactory->cancel();
// FIXME: Do we need to do something here for OOPI?
HTMLFrameOwnerElement* ownerElement = m_frame->deprecatedLocalOwner();
@@ -877,7 +884,7 @@ void FrameView::layout()
ScriptForbiddenScope forbidScript;
- if (isInPerformLayout() || !m_frame->document()->isActive())
+ if (isInPerformLayout() || !m_frame->document()->isActive() || shouldThrottleStyleLayoutAndCompositingUpdates())
return;
TRACE_EVENT0("blink,benchmark", "FrameView::layout");
@@ -1061,6 +1068,9 @@ void FrameView::layout()
// See http://crbug.com/306706
void FrameView::invalidateTreeIfNeeded(PaintInvalidationState& paintInvalidationState)
{
+ if (shouldThrottleRenderingPipeline())
+ return;
+
lifecycle().advanceTo(DocumentLifecycle::InPaintInvalidation);
ASSERT(layoutView());
@@ -1270,6 +1280,8 @@ bool FrameView::shouldSetCursor() const
void FrameView::scrollContentsIfNeededRecursive()
{
+ if (shouldThrottleStyleLayoutAndCompositingUpdates())
+ return;
scrollContentsIfNeeded();
for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
@@ -1280,6 +1292,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) {
@@ -1690,7 +1713,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);
}
@@ -2380,23 +2404,23 @@ 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::updateLifecycleToLayoutClean()
+void FrameView::updateLifecycleToLayoutClean(DocumentLifecycle::ThrottlingMode throttlingMode)
{
- frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToLayoutClean);
+ frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToLayoutClean, 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.
@@ -2405,13 +2429,12 @@ void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, cons
// Updating layout can run script, which can tear down the FrameView.
RefPtrWillBeRawPtr<FrameView> protector(this);
+ setLifecycleThrottlingModeForSubtree(throttlingMode);
ojan 2015/10/13 23:31:29 This call and the one below are unconditional walk
Sami 2015/10/14 16:52:11 Yeah, I wasn't crazy about the extra tree walks ei
updateStyleAndLayoutIfNeededRecursive();
ASSERT(lifecycle().state() >= DocumentLifecycle::LayoutClean);
- if (phases == OnlyUpToLayoutClean)
- return;
-
- if (LayoutView* view = layoutView()) {
+ LayoutView* view = layoutView();
+ if (view && phases != OnlyUpToLayoutClean) {
TRACE_EVENT1("devtools.timeline", "UpdateLayerTree", "data", InspectorUpdateLayerTreeEvent::data(m_frame.get()));
// This was required for slimming paint v1 but is only temporarily
@@ -2447,6 +2470,9 @@ void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, cons
|| (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled() && lifecycle().state() == DocumentLifecycle::PaintClean));
}
}
+
+ updateViewportIntersectionsForSubtree();
+ setLifecycleThrottlingModeForSubtree(DocumentLifecycle::ThrottlingMode::Disallow);
}
void FrameView::updatePaintProperties()
@@ -2530,6 +2556,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
@@ -2584,6 +2613,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;
@@ -2902,6 +2936,7 @@ void FrameView::setParent(Widget* parentView)
toFrameView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
updateScrollableAreaSet();
+ m_needsUpdateViewportIntersection = true;
}
void FrameView::removeChild(Widget* child)
@@ -2959,6 +2994,7 @@ void FrameView::frameRectsChanged()
if (layoutSizeFixedToFrameSize())
setLayoutSizeInternal(frameRect().size());
+ m_needsUpdateViewportIntersection = true;
for (const auto& child : m_children)
child->frameRectsChanged();
}
@@ -3735,11 +3771,16 @@ void FrameView::paint(GraphicsContext* context, const IntRect& rect) const
void FrameView::paint(GraphicsContext* context, const GlobalPaintFlags globalPaintFlags, const IntRect& rect) const
{
+ // 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) const
{
+ if (shouldThrottleRenderingPipeline())
+ return;
FramePainter(*this).paintContents(context, globalPaintFlags, damageRect);
}
@@ -3927,6 +3968,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)
@@ -3937,4 +3981,108 @@ 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::updateViewportIntersectionIfNeeded()
+{
+ // TODO(skyostil): Replace this with a real intersection observer.
+
+ // Make sure we have valid layout data in our parent view.
+ ASSERT(!parentFrameView() || parentFrameView()->lifecycle().state() >= DocumentLifecycle::LayoutClean);
+ if (!m_needsUpdateViewportIntersection)
+ return;
+
+ // Our frame's bounds in our parent's content coordinates.
+ m_viewportIntersection = frameRect();
+ m_needsUpdateViewportIntersection = false;
+ m_viewportIntersectionValid = true;
+
+ FrameView* parent = parentFrameView();
+ if (!parent)
+ return;
+ ASSERT(!parent->m_needsUpdateViewportIntersection);
+
+ // If our parent is hidden, then we are too.
+ if (parent->m_viewportIntersection.isEmpty()) {
+ m_viewportIntersection = parent->m_viewportIntersection;
+ return;
+ }
+
+ // Transform our bounds into the root frame's content coordinate space.
+ m_viewportIntersection = parent->contentsToRootFrame(m_viewportIntersection);
+
+ // TODO(skyostil): Expand the viewport to make it less likely to see stale content while scrolling.
+ IntRect viewport = parent->m_viewportIntersection;
+ m_viewportIntersection.intersect(viewport);
+}
+
+void FrameView::updateViewportIntersectionsForSubtree()
+{
+ bool hadValidIntersection = m_viewportIntersectionValid;
+ bool hadEmptyIntersection = m_viewportIntersection.isEmpty();
+ updateViewportIntersectionIfNeeded();
+ bool shouldNotify = !hadValidIntersection || hadEmptyIntersection != m_viewportIntersection.isEmpty();
+ if (shouldNotify && !m_intersectionObserverNotificationFactory->isPending())
+ m_frame->frameScheduler()->timerTaskRunner()->postTask(FROM_HERE, m_intersectionObserverNotificationFactory->cancelAndCreate());
+
+ // If this view is being throttled, don't recurse to update viewport
+ // intersections for its children because their frame coordinates may not
+ // be up to date.
+ if (shouldThrottleStyleLayoutAndCompositingUpdates())
+ return;
+
+ for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
+ if (!child->isLocalFrame())
+ continue;
+ if (FrameView* view = toLocalFrame(child)->view())
+ view->updateViewportIntersectionsForSubtree();
+ }
+}
+
+void FrameView::notifyIntersectionObservers()
+{
+ TRACE_EVENT0("blink", "FrameView::notifyIntersectionObservers");
+ ASSERT(!isInPerformLayout());
+ bool wasThrottled = shouldThrottleRenderingPipeline();
+
+ // Only offscreen frames can be throttled.
+ m_viewportVisibilityForThrottling = (m_viewportIntersectionValid && m_viewportIntersection.isEmpty()) ? ViewportVisibility::Hidden : ViewportVisibility::Visible;
+
+ // 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.
+ m_securityOriginStatusForThrottling = SecurityOriginStatus::IsSameOrigin;
+ Frame* parentFrame = m_frame->tree().parent();
+ const SecurityOrigin* origin = frame().securityContext()->securityOrigin();
+ while (parentFrame) {
+ const SecurityOrigin* parentOrigin = parentFrame->securityContext()->securityOrigin();
+ if (!origin->canAccess(parentOrigin)) {
+ m_securityOriginStatusForThrottling = SecurityOriginStatus::IsCrossOrigin;
+ break;
+ }
+ parentFrame = parentFrame->tree().parent();
+ }
+
+ bool becameUnthrottled = wasThrottled && !shouldThrottleRenderingPipeline();
+ if (becameUnthrottled)
+ page()->animator().scheduleVisualUpdate(m_frame.get());
+}
+
+bool FrameView::shouldThrottleStyleLayoutAndCompositingUpdates() const
+{
+ return lifecycle().throttlingMode() == DocumentLifecycle::ThrottlingMode::Allow
+ && shouldThrottleRenderingPipeline();
+}
+
+bool FrameView::shouldThrottleRenderingPipeline() const
+{
+ if (!RuntimeEnabledFeatures::renderingPipelineThrottlingEnabled())
+ return false;
+ return m_viewportVisibilityForThrottling == ViewportVisibility::Hidden
+ && m_securityOriginStatusForThrottling == SecurityOriginStatus::IsCrossOrigin;
+}
+
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698