| 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
|
|
|