Chromium Code Reviews| Index: Source/core/frame/FrameView.cpp |
| diff --git a/Source/core/frame/FrameView.cpp b/Source/core/frame/FrameView.cpp |
| index 836b86a952c6831e085b16f2b128876f7b043a7b..933c02cf60ab3ea70201393b060c36892e27941f 100644 |
| --- a/Source/core/frame/FrameView.cpp |
| +++ b/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) { |
| @@ -2418,18 +2436,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 +2456,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 +2551,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 +2605,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 +2928,7 @@ void FrameView::setParent(Widget* parentView) |
| toFrameView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer); |
| updateScrollableAreaSet(); |
| + m_needsUpdateViewportVisibility = true; |
| } |
| void FrameView::removeChild(Widget* child) |
| @@ -2958,6 +2986,7 @@ void FrameView::frameRectsChanged() |
| if (layoutSizeFixedToFrameSize()) |
| setLayoutSizeInternal(frameRect().size()); |
| + m_needsUpdateViewportVisibility = true; |
| for (const auto& child : m_children) |
| child->frameRectsChanged(); |
| } |
| @@ -3739,11 +3768,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 +3965,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 +3978,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::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). |
|
esprehn
2015/09/21 21:27:20
huh?
Sami
2015/09/23 18:31:07
Yeah :( I found out that block selections can inva
|
| + 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::updateThrottling() |
| +{ |
| + 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). |
| + 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. As an optimization |
| + // only do this if we are invisible. |
| + if (securityOriginStatus == SecurityOriginStatus::IsSameOrigin |
| + && parentFrameView() && m_viewportVisibility == ViewportVisibility::Hidden) { |
| + 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 |