Index: Source/core/frame/FrameView.cpp |
diff --git a/Source/core/frame/FrameView.cpp b/Source/core/frame/FrameView.cpp |
index cdfa3a72851e8005b125f67f28d907238286c97d..39018a13ad20682c0b6f8cabc8a6efcb8e732639 100644 |
--- a/Source/core/frame/FrameView.cpp |
+++ b/Source/core/frame/FrameView.cpp |
@@ -140,7 +140,10 @@ FrameView::FrameView(LocalFrame* frame) |
, m_inUpdateScrollbars(false) |
, m_clipsRepaints(true) |
, m_frameTimingRequestsDirty(true) |
- |
+ , m_lifecycleThrottlingMode(LifecycleThrottlingMode::Disallow) |
+ , m_viewportVisibility(ViewportVisibility::Unknown) |
+ , m_viewportVisibilityForThrottling(ViewportVisibility::Unknown) |
+ , m_isCrossOriginForThrottling(false) |
{ |
ASSERT(m_frame); |
init(); |
@@ -1090,6 +1093,9 @@ void FrameView::layout() |
// See http://crbug.com/306706 |
void FrameView::invalidateTreeIfNeeded(PaintInvalidationState& paintInvalidationState) |
{ |
+ if (shouldThrottleRenderingPipeline()) |
+ return; |
+ |
lifecycle().advanceTo(DocumentLifecycle::InPaintInvalidation); |
ASSERT(layoutView()); |
@@ -1303,6 +1309,8 @@ bool FrameView::shouldSetCursor() const |
void FrameView::scrollContentsIfNeededRecursive() |
{ |
+ if (shouldThrottleStyleLayoutAndCompositingUpdates()) |
+ return; |
scrollContentsIfNeeded(); |
for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) { |
@@ -1313,6 +1321,18 @@ void FrameView::scrollContentsIfNeededRecursive() |
} |
} |
+void FrameView::setLifecycleThrottlingModeRecursive(LifecycleThrottlingMode lifecycleThrottlingMode) |
esprehn
2015/09/02 21:10:37
This actually doesn't need to be recursive, you ca
Sami
2015/09/03 17:23:20
Neat, thanks!
|
+{ |
+ m_lifecycleThrottlingMode = lifecycleThrottlingMode; |
+ |
+ for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) { |
+ if (!child->isLocalFrame()) |
+ continue; |
+ if (FrameView* view = toLocalFrame(child)->view()) |
+ view->setLifecycleThrottlingModeRecursive(lifecycleThrottlingMode); |
+ } |
+} |
+ |
bool FrameView::invalidateViewportConstrainedObjects() |
{ |
for (const auto& viewportConstrainedObject : *m_viewportConstrainedObjects) { |
@@ -2427,18 +2447,18 @@ void FrameView::updateWidgetPositionsIfNeeded() |
updateWidgetPositions(); |
} |
-void FrameView::updateAllLifecyclePhases() |
+void FrameView::updateAllLifecyclePhases(LifecycleThrottlingMode lifecycleThrottlingMode) |
{ |
- frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(AllPhases); |
+ frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(AllPhases, lifecycleThrottlingMode); |
} |
// TODO(chrishtr): add a scrolling update lifecycle phase. |
-void FrameView::updateLifecycleToCompositingCleanPlusScrolling() |
+void FrameView::updateLifecycleToCompositingCleanPlusScrolling(LifecycleThrottlingMode lifecycleThrottlingMode) |
{ |
- frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToCompositingCleanPlusScrolling); |
+ frame().localFrameRoot()->view()->updateLifecyclePhasesInternal(OnlyUpToCompositingCleanPlusScrolling, lifecycleThrottlingMode); |
} |
-void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases) |
+void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases, LifecycleThrottlingMode lifecycleThrottlingMode) |
{ |
// 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. |
@@ -2447,6 +2467,7 @@ void FrameView::updateLifecyclePhasesInternal(LifeCycleUpdateOption phases) |
// Updating layout can run script, which can tear down the FrameView. |
RefPtrWillBeRawPtr<FrameView> protector(this); |
+ setLifecycleThrottlingModeRecursive(lifecycleThrottlingMode); |
updateStyleAndLayoutIfNeededRecursive(); |
if (LayoutView* view = layoutView()) { |
@@ -2551,6 +2572,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 |
@@ -2602,6 +2626,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; |
@@ -2928,6 +2957,7 @@ void FrameView::setParent(Widget* parentView) |
toFrameView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer); |
updateScrollableAreaSet(); |
+ computeViewportVisibility(); |
} |
void FrameView::removeChild(Widget* child) |
@@ -2985,6 +3015,7 @@ void FrameView::frameRectsChanged() |
if (layoutSizeFixedToFrameSize()) |
setLayoutSizeInternal(frameRect().size()); |
+ computeViewportVisibility(); |
for (const auto& child : m_children) |
child->frameRectsChanged(); |
} |
@@ -3775,11 +3806,15 @@ void FrameView::paint(GraphicsContext* context, const IntRect& rect) |
void FrameView::paint(GraphicsContext* context, const GlobalPaintFlags globalPaintFlags, const IntRect& rect) |
{ |
+ 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); |
} |
@@ -3967,6 +4002,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) |
@@ -3977,4 +4015,94 @@ 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::computeViewportVisibility() |
+{ |
+ // TODO(skyostil): Replace this with a position observer. |
esprehn
2015/09/02 21:10:37
This needs lifecycle asserts, what phases is this
Sami
2015/09/03 17:23:20
Yeah, agreed. Since frameRectsChanged() is called
|
+ |
+ // Our frame's bounds in our parent's content coordinates. |
+ m_clippedBounds = frameRect(); |
+ m_viewportVisibility = ViewportVisibility::Visible; |
+ |
+ FrameView* parent = parentFrameView(); |
+ if (!parent) |
+ return; |
+ |
+ // If our parent has indeterminate visibility, then so do we. |
+ if (parent->m_viewportVisibility == ViewportVisibility::Unknown) { |
+ m_viewportVisibility = ViewportVisibility::Unknown; |
+ return; |
+ } |
+ |
+ // 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::updateThrottling() |
+{ |
+ ASSERT(m_frame->isLocalRoot()); |
+ updateThrottlingRecursive(false); |
esprehn
2015/09/02 21:10:37
This boolean is not clear.
Sami
2015/09/03 17:23:20
Replaced with an enum.
|
+} |
+ |
+void FrameView::updateThrottlingRecursive(bool hasCrossedSecurityOrigin) |
+{ |
+ ASSERT(!isInPerformLayout()); |
+ bool wasThrottled = shouldThrottleRenderingPipeline(); |
+ |
+ // Visibility should never go back to an unknown state. |
+ ASSERT(m_viewportVisibility != ViewportVisibility::Unknown |
+ || m_viewportVisibilityForThrottling == ViewportVisibility::Unknown); |
esprehn
2015/09/02 21:10:37
Lets remove the Unknown state, you're either visib
Sami
2015/09/03 17:23:20
Ok, let's default to visible.
|
+ m_viewportVisibilityForThrottling = m_viewportVisibility; |
+ |
+ // Check if we can access our parent's security origin. As an optimization only do this if we are invisible. |
+ if (!hasCrossedSecurityOrigin && parentFrameView() && m_viewportVisibilityForThrottling == ViewportVisibility::Hidden) { |
esprehn
2015/09/02 21:10:37
What's the reason for this?
Sami
2015/09/03 17:23:20
Added a big comment, but the tl;dr is that we want
|
+ const SecurityOrigin* origin = frame().securityContext()->securityOrigin(); |
+ const SecurityOrigin* parentOrigin = parentFrameView()->frame().securityContext()->securityOrigin(); |
+ hasCrossedSecurityOrigin = !origin->canAccess(parentOrigin); |
Mike West
2015/09/02 11:30:08
This looks pretty reasonable.
That said:
1. Do w
Sami
2015/09/03 14:07:56
Great, thanks for checking.
|
+ } |
+ // FIXME: Always throttle for testing. |
+ hasCrossedSecurityOrigin = true; |
+ m_isCrossOriginForThrottling = hasCrossedSecurityOrigin; |
+ |
+ 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(hasCrossedSecurityOrigin); |
+ } |
+} |
+ |
+bool FrameView::shouldThrottleStyleLayoutAndCompositingUpdates() const |
+{ |
+ return m_lifecycleThrottlingMode == LifecycleThrottlingMode::Allow |
esprehn
2015/09/02 21:10:37
This bit seems like it should be in the DocumentLi
Sami
2015/09/03 17:23:20
Good idea, done.
|
+ && shouldThrottleRenderingPipeline(); |
+} |
+ |
+bool FrameView::shouldThrottleRenderingPipeline() const |
+{ |
+ // FIXME: Ignore for layout testing |
+ //if (!RuntimeEnabledFeatures::renderingPipelineThrottlingEnabled()) |
+ //return false; |
+ return parent() |
esprehn
2015/09/02 21:10:37
This parent check shouldn't be needed.
Sami
2015/09/03 17:23:20
Done.
|
+ && m_viewportVisibilityForThrottling == ViewportVisibility::Hidden |
+ && m_isCrossOriginForThrottling; |
+} |
+ |
} // namespace blink |