[FrameView::layout cleanup] Move core layout logic to a separate class.
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Oct 2017 15:24:58 +0000 (15:24 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Oct 2017 15:24:58 +0000 (15:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178771
<rdar://problem/35166542>

Reviewed by Simon Fraser.

Move layout code out from FrameView to LayoutContext.

Source/WebCore:

LayoutContext holds all the layout related logic (scheduling, needsLayout, handling layout states),
while scrolling, view sizing methods stay in FrameView.
Having a dedicated LayoutContext allows to structure the code better.

Covered by existing tests.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* accessibility/AXObjectCache.cpp:
(WebCore::rendererNeedsDeferredUpdate):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::updateBackingStore):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::textUnderElement const):
(WebCore::AccessibilityRenderObject::layoutCount const):
* dom/Document.cpp:
(WebCore::Document::setVisualUpdatesAllowed):
(WebCore::Document::resolveStyle):
(WebCore::Document::updateStyleIfNeeded):
(WebCore::Document::updateLayout):
(WebCore::Document::updateLayoutIfDimensionsOutOfDate):
(WebCore::Document::implicitClose):
(WebCore::Document::isLayoutTimerActive):
* dom/Element.cpp:
(WebCore::Element::absoluteEventHandlerBounds):
* editing/FrameSelection.cpp:
(WebCore::FrameSelection::setSelection):
* html/HTMLEmbedElement.cpp:
(WebCore::HTMLEmbedElement::renderWidgetLoadingPlugin const):
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::didAttachRenderers):
* inspector/InspectorOverlay.cpp:
(WebCore::InspectorOverlay::update):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::stopForUserCancel):
* page/Frame.cpp:
(WebCore::Frame::setView):
(WebCore::Frame::clearTimers):
(WebCore::Frame::setPageAndTextZoomFactors):
(WebCore::Frame::resumeActiveDOMObjectsAndAnimations):
* page/FrameView.cpp:
(WebCore::FrameView::FrameView):
(WebCore::FrameView::~FrameView):
(WebCore::FrameView::reset):
(WebCore::FrameView::resetScrollbars):
(WebCore::FrameView::didFirstLayout const):
(WebCore::FrameView::willDestroyRenderTree):
(WebCore::FrameView::didDestroyRenderTree):
(WebCore::FrameView::setContentsSize):
(WebCore::FrameView::calculateScrollbarModesForLayout):
(WebCore::FrameView::updateCompositingLayersAfterStyleChange):
(WebCore::FrameView::topContentInsetDidChange):
(WebCore::FrameView::forceLayoutParentViewIfNeeded):
(WebCore::FrameView::adjustScrollbarsForLayout):
(WebCore::FrameView::willDoLayout): This takes care of the view related task right before entering render tree layout.
(WebCore::FrameView::didLayout): post layout tasks.
(WebCore::FrameView::shouldDeferScrollUpdateAfterContentSizeChange):
(WebCore::FrameView::updateLayoutViewport):
(WebCore::FrameView::maintainScrollPositionAtAnchor):
(WebCore::FrameView::updateLayerPositionsAfterScrolling):
(WebCore::FrameView::updateCompositingLayersAfterScrolling):
(WebCore::FrameView::availableContentSizeChanged):
(WebCore::FrameView::updateContentsSize):
(WebCore::FrameView::needsLayout const):
(WebCore::FrameView::setNeedsLayout):
(WebCore::FrameView::scheduleSelectionUpdate):
(WebCore::FrameView::updateEmbeddedObjects):
(WebCore::FrameView::flushAnyPendingPostLayoutTasks):
(WebCore::FrameView::flushPostLayoutTasksQueue):
(WebCore::FrameView::performPostLayoutTasks):
(WebCore::FrameView::sendResizeEventIfNeeded):
(WebCore::FrameView::autoSizeIfEnabled):
(WebCore::FrameView::paintControlTints):
(WebCore::FrameView::paintContents):
(WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):
(WebCore::FrameView::enableAutoSizeMode):
(WebCore::FrameView::forceLayout):
(WebCore::SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer): Deleted.
(WebCore::SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer): Deleted.
(WebCore::SubtreeLayoutStateMaintainer::shouldDisableLayoutStateForSubtree): Deleted.
(): Deleted.
(WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker): Deleted.
(WebCore::applyTextSizingIfNeeded): Deleted.
(WebCore::FrameView::handleLayoutWithFrameFlatteningIfNeeded): Deleted.
(WebCore::FrameView::updateStyleForLayout): Deleted.
(WebCore::FrameView::canPerformLayout const): Deleted.
(WebCore::FrameView::layout): Deleted.
(WebCore::FrameView::runOrSchedulePostLayoutTasks): Deleted.
(WebCore::FrameView::convertSubtreeLayoutToFullLayout): Deleted.
(WebCore::FrameView::layoutTimerFired): Deleted.
(WebCore::FrameView::scheduleRelayout): Deleted.
(WebCore::isObjectAncestorContainerOf): Deleted.
(WebCore::FrameView::scheduleRelayoutOfSubtree): Deleted.
(WebCore::FrameView::layoutPending const): Deleted.
(WebCore::FrameView::unscheduleRelayout): Deleted.
(WebCore::FrameView::startLayoutAtMainFrameViewIfNeeded): Deleted.
* page/FrameView.h:
* page/LayoutContext.cpp: Added.
(WebCore::isObjectAncestorContainerOf):
(WebCore::SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer):
(WebCore::SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer):
(WebCore::SubtreeLayoutStateMaintainer::shouldDisableLayoutStateForSubtree):
(WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker):
(WebCore::LayoutScope::LayoutScope):
(WebCore::LayoutScope::~LayoutScope):
(WebCore::LayoutContext::LayoutContext):
(WebCore::LayoutContext::layout):
(WebCore::LayoutContext::runOrScheduleAsynchronousTasks):
(WebCore::LayoutContext::runAsynchronousTasks):
(WebCore::LayoutContext::flushAsynchronousTasks):
(WebCore::LayoutContext::reset):
(WebCore::LayoutContext::needsLayout const):
(WebCore::LayoutContext::setNeedsLayout):
(WebCore::LayoutContext::enableSetNeedsLayout):
(WebCore::LayoutContext::disableSetNeedsLayout):
(WebCore::LayoutContext::scheduleLayout):
(WebCore::LayoutContext::unscheduleLayout):
(WebCore::LayoutContext::scheduleSubtreeLayout):
(WebCore::LayoutContext::layoutTimerFired):
(WebCore::LayoutContext::convertSubtreeLayoutToFullLayout):
(WebCore::LayoutContext::setSubtreeLayoutRoot):
(WebCore::LayoutContext::canPerformLayout const):
(WebCore::LayoutContext::applyTextSizingIfNeeded):
(WebCore::LayoutContext::updateStyleForLayout):
(WebCore::LayoutContext::handleLayoutWithFrameFlatteningIfNeeded):
(WebCore::LayoutContext::startLayoutAtMainFrameViewIfNeeded):
(WebCore::LayoutContext::frame const):
(WebCore::LayoutContext::view const):
(WebCore::LayoutContext::renderView const):
(WebCore::LayoutContext::document const):
* page/LayoutContext.h: Added.
(WebCore::LayoutContext::startDisallowingLayout):
(WebCore::LayoutContext::endDisallowingLayout):
(WebCore::LayoutContext::layoutPhase const):
(WebCore::LayoutContext::isLayoutNested const):
(WebCore::LayoutContext::layoutCount const):
(WebCore::LayoutContext::isLayoutPending const):
(WebCore::LayoutContext::isInLayout const):
(WebCore::LayoutContext::isInRenderTreeLayout const):
(WebCore::LayoutContext::inPaintableState const):
(WebCore::LayoutContext::subtreeLayoutRoot const):
(WebCore::LayoutContext::clearSubtreeLayoutRoot):
(WebCore::LayoutContext::resetFirstLayoutFlag):
(WebCore::LayoutContext::didFirstLayout const):
(WebCore::LayoutContext::setNeedsFullRepaint):
(WebCore::LayoutContext::needsFullRepaint const):
(WebCore::LayoutContext::layoutDisallowed const):
(WebCore::LayoutContext::isLayoutSchedulingEnabled const):
(WebCore::LayoutContext::inAsynchronousTasks const):
* page/Page.cpp:
(WebCore::Page::setPageScaleFactor):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::insertPositionedObject):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::imageChanged):
(WebCore::RenderBox::computeLogicalWidthInFragment const):
* rendering/RenderElement.cpp:
(WebCore::RenderElement::clearSubtreeLayoutRootIfNeeded const):
(WebCore::RenderElement::checkForRepaintDuringLayout const):
* rendering/RenderFrameBase.cpp:
(WebCore::RenderFrameBase::performLayoutWithFlattening):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::scrollTo):
* rendering/RenderObject.cpp:
(WebCore::scheduleRelayoutForSubtree):
* rendering/RenderTreeAsText.cpp:
(WebCore::write):
* rendering/RenderVideo.cpp:
(WebCore::RenderVideo::updatePlayer):
* rendering/RenderView.h:
* rendering/RenderWidget.cpp:
(WebCore::RenderWidget::updateWidgetPosition):
* svg/graphics/SVGImage.cpp:
(WebCore::SVGImage::draw):
* testing/Internals.cpp:
(WebCore::Internals::layoutCount const):

Source/WebKitLegacy/mac:

* WebView/WebClipView.mm:
(-[WebClipView _immediateScrollToPoint:]):
* WebView/WebFrame.mm:
(-[WebFrame layoutCount]):

Source/WebKitLegacy/win:

* WebFrame.cpp:
(WebFrame::layout):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@224150 268f45cc-cd09-0410-ab3c-d52691b4dbfc

36 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/editing/FrameSelection.cpp
Source/WebCore/html/HTMLEmbedElement.cpp
Source/WebCore/html/HTMLFormControlElement.cpp
Source/WebCore/inspector/InspectorOverlay.cpp
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/page/Frame.cpp
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/page/LayoutContext.cpp [new file with mode: 0644]
Source/WebCore/page/LayoutContext.h [new file with mode: 0644]
Source/WebCore/page/Page.cpp
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderElement.cpp
Source/WebCore/rendering/RenderFrameBase.cpp
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderObject.cpp
Source/WebCore/rendering/RenderTreeAsText.cpp
Source/WebCore/rendering/RenderVideo.cpp
Source/WebCore/rendering/RenderView.h
Source/WebCore/rendering/RenderWidget.cpp
Source/WebCore/svg/graphics/SVGImage.cpp
Source/WebCore/testing/Internals.cpp
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebView/WebClipView.mm
Source/WebKitLegacy/mac/WebView/WebFrame.mm
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebFrame.cpp

index e916402..374716b 100644 (file)
@@ -1,3 +1,190 @@
+2017-10-28  Zalan Bujtas  <zalan@apple.com>
+
+        [FrameView::layout cleanup] Move core layout logic to a separate class.
+        https://bugs.webkit.org/show_bug.cgi?id=178771
+        <rdar://problem/35166542>
+
+        Reviewed by Simon Fraser.
+
+        Move layout code out from FrameView to LayoutContext.
+
+        LayoutContext holds all the layout related logic (scheduling, needsLayout, handling layout states),
+        while scrolling, view sizing methods stay in FrameView.
+        Having a dedicated LayoutContext allows to structure the code better.   
+
+        Covered by existing tests.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::rendererNeedsDeferredUpdate):
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::updateBackingStore):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::textUnderElement const):
+        (WebCore::AccessibilityRenderObject::layoutCount const):
+        * dom/Document.cpp:
+        (WebCore::Document::setVisualUpdatesAllowed):
+        (WebCore::Document::resolveStyle):
+        (WebCore::Document::updateStyleIfNeeded):
+        (WebCore::Document::updateLayout):
+        (WebCore::Document::updateLayoutIfDimensionsOutOfDate):
+        (WebCore::Document::implicitClose):
+        (WebCore::Document::isLayoutTimerActive):
+        * dom/Element.cpp:
+        (WebCore::Element::absoluteEventHandlerBounds):
+        * editing/FrameSelection.cpp:
+        (WebCore::FrameSelection::setSelection):
+        * html/HTMLEmbedElement.cpp:
+        (WebCore::HTMLEmbedElement::renderWidgetLoadingPlugin const):
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::didAttachRenderers):
+        * inspector/InspectorOverlay.cpp:
+        (WebCore::InspectorOverlay::update):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::stopForUserCancel):
+        * page/Frame.cpp:
+        (WebCore::Frame::setView):
+        (WebCore::Frame::clearTimers):
+        (WebCore::Frame::setPageAndTextZoomFactors):
+        (WebCore::Frame::resumeActiveDOMObjectsAndAnimations):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::FrameView):
+        (WebCore::FrameView::~FrameView):
+        (WebCore::FrameView::reset):
+        (WebCore::FrameView::resetScrollbars):
+        (WebCore::FrameView::didFirstLayout const):
+        (WebCore::FrameView::willDestroyRenderTree):
+        (WebCore::FrameView::didDestroyRenderTree):
+        (WebCore::FrameView::setContentsSize):
+        (WebCore::FrameView::calculateScrollbarModesForLayout):
+        (WebCore::FrameView::updateCompositingLayersAfterStyleChange):
+        (WebCore::FrameView::topContentInsetDidChange):
+        (WebCore::FrameView::forceLayoutParentViewIfNeeded):
+        (WebCore::FrameView::adjustScrollbarsForLayout):
+        (WebCore::FrameView::willDoLayout): This takes care of the view related task right before entering render tree layout.
+        (WebCore::FrameView::didLayout): post layout tasks.
+        (WebCore::FrameView::shouldDeferScrollUpdateAfterContentSizeChange):
+        (WebCore::FrameView::updateLayoutViewport):
+        (WebCore::FrameView::maintainScrollPositionAtAnchor):
+        (WebCore::FrameView::updateLayerPositionsAfterScrolling):
+        (WebCore::FrameView::updateCompositingLayersAfterScrolling):
+        (WebCore::FrameView::availableContentSizeChanged):
+        (WebCore::FrameView::updateContentsSize):
+        (WebCore::FrameView::needsLayout const):
+        (WebCore::FrameView::setNeedsLayout):
+        (WebCore::FrameView::scheduleSelectionUpdate):
+        (WebCore::FrameView::updateEmbeddedObjects):
+        (WebCore::FrameView::flushAnyPendingPostLayoutTasks):
+        (WebCore::FrameView::flushPostLayoutTasksQueue):
+        (WebCore::FrameView::performPostLayoutTasks):
+        (WebCore::FrameView::sendResizeEventIfNeeded):
+        (WebCore::FrameView::autoSizeIfEnabled):
+        (WebCore::FrameView::paintControlTints):
+        (WebCore::FrameView::paintContents):
+        (WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):
+        (WebCore::FrameView::enableAutoSizeMode):
+        (WebCore::FrameView::forceLayout):
+        (WebCore::SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer): Deleted.
+        (WebCore::SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer): Deleted.
+        (WebCore::SubtreeLayoutStateMaintainer::shouldDisableLayoutStateForSubtree): Deleted.
+        (): Deleted.
+        (WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker): Deleted.
+        (WebCore::applyTextSizingIfNeeded): Deleted.
+        (WebCore::FrameView::handleLayoutWithFrameFlatteningIfNeeded): Deleted.
+        (WebCore::FrameView::updateStyleForLayout): Deleted.
+        (WebCore::FrameView::canPerformLayout const): Deleted.
+        (WebCore::FrameView::layout): Deleted.
+        (WebCore::FrameView::runOrSchedulePostLayoutTasks): Deleted.
+        (WebCore::FrameView::convertSubtreeLayoutToFullLayout): Deleted.
+        (WebCore::FrameView::layoutTimerFired): Deleted.
+        (WebCore::FrameView::scheduleRelayout): Deleted.
+        (WebCore::isObjectAncestorContainerOf): Deleted.
+        (WebCore::FrameView::scheduleRelayoutOfSubtree): Deleted.
+        (WebCore::FrameView::layoutPending const): Deleted.
+        (WebCore::FrameView::unscheduleRelayout): Deleted.
+        (WebCore::FrameView::startLayoutAtMainFrameViewIfNeeded): Deleted.
+        * page/FrameView.h:
+        * page/LayoutContext.cpp: Added.
+        (WebCore::isObjectAncestorContainerOf):
+        (WebCore::SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer):
+        (WebCore::SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer):
+        (WebCore::SubtreeLayoutStateMaintainer::shouldDisableLayoutStateForSubtree):
+        (WebCore::RenderTreeNeedsLayoutChecker::~RenderTreeNeedsLayoutChecker):
+        (WebCore::LayoutScope::LayoutScope):
+        (WebCore::LayoutScope::~LayoutScope):
+        (WebCore::LayoutContext::LayoutContext):
+        (WebCore::LayoutContext::layout):
+        (WebCore::LayoutContext::runOrScheduleAsynchronousTasks):
+        (WebCore::LayoutContext::runAsynchronousTasks):
+        (WebCore::LayoutContext::flushAsynchronousTasks):
+        (WebCore::LayoutContext::reset):
+        (WebCore::LayoutContext::needsLayout const):
+        (WebCore::LayoutContext::setNeedsLayout):
+        (WebCore::LayoutContext::enableSetNeedsLayout):
+        (WebCore::LayoutContext::disableSetNeedsLayout):
+        (WebCore::LayoutContext::scheduleLayout):
+        (WebCore::LayoutContext::unscheduleLayout):
+        (WebCore::LayoutContext::scheduleSubtreeLayout):
+        (WebCore::LayoutContext::layoutTimerFired):
+        (WebCore::LayoutContext::convertSubtreeLayoutToFullLayout):
+        (WebCore::LayoutContext::setSubtreeLayoutRoot):
+        (WebCore::LayoutContext::canPerformLayout const):
+        (WebCore::LayoutContext::applyTextSizingIfNeeded):
+        (WebCore::LayoutContext::updateStyleForLayout):
+        (WebCore::LayoutContext::handleLayoutWithFrameFlatteningIfNeeded):
+        (WebCore::LayoutContext::startLayoutAtMainFrameViewIfNeeded):
+        (WebCore::LayoutContext::frame const):
+        (WebCore::LayoutContext::view const):
+        (WebCore::LayoutContext::renderView const):
+        (WebCore::LayoutContext::document const):
+        * page/LayoutContext.h: Added.
+        (WebCore::LayoutContext::startDisallowingLayout):
+        (WebCore::LayoutContext::endDisallowingLayout):
+        (WebCore::LayoutContext::layoutPhase const):
+        (WebCore::LayoutContext::isLayoutNested const):
+        (WebCore::LayoutContext::layoutCount const):
+        (WebCore::LayoutContext::isLayoutPending const):
+        (WebCore::LayoutContext::isInLayout const):
+        (WebCore::LayoutContext::isInRenderTreeLayout const):
+        (WebCore::LayoutContext::inPaintableState const):
+        (WebCore::LayoutContext::subtreeLayoutRoot const):
+        (WebCore::LayoutContext::clearSubtreeLayoutRoot):
+        (WebCore::LayoutContext::resetFirstLayoutFlag):
+        (WebCore::LayoutContext::didFirstLayout const):
+        (WebCore::LayoutContext::setNeedsFullRepaint):
+        (WebCore::LayoutContext::needsFullRepaint const):
+        (WebCore::LayoutContext::layoutDisallowed const):
+        (WebCore::LayoutContext::isLayoutSchedulingEnabled const):
+        (WebCore::LayoutContext::inAsynchronousTasks const):
+        * page/Page.cpp:
+        (WebCore::Page::setPageScaleFactor):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::insertPositionedObject):
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::imageChanged):
+        (WebCore::RenderBox::computeLogicalWidthInFragment const):
+        * rendering/RenderElement.cpp:
+        (WebCore::RenderElement::clearSubtreeLayoutRootIfNeeded const):
+        (WebCore::RenderElement::checkForRepaintDuringLayout const):
+        * rendering/RenderFrameBase.cpp:
+        (WebCore::RenderFrameBase::performLayoutWithFlattening):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::scrollTo):
+        * rendering/RenderObject.cpp:
+        (WebCore::scheduleRelayoutForSubtree):
+        * rendering/RenderTreeAsText.cpp:
+        (WebCore::write):
+        * rendering/RenderVideo.cpp:
+        (WebCore::RenderVideo::updatePlayer):
+        * rendering/RenderView.h:
+        * rendering/RenderWidget.cpp:
+        (WebCore::RenderWidget::updateWidgetPosition):
+        * svg/graphics/SVGImage.cpp:
+        (WebCore::SVGImage::draw):
+        * testing/Internals.cpp:
+        (WebCore::Internals::layoutCount const):
+        
 2017-10-28  Joseph Pecoraro  <pecoraro@apple.com>
 
         Cleanup PageDebuggable
index 216a140..f795d92 100644 (file)
@@ -1280,6 +1280,7 @@ page/FrameView.cpp
 page/History.cpp
 page/IntersectionObserver.cpp
 page/IntersectionObserverEntry.cpp
+page/LayoutContext.cpp
 page/Location.cpp
 page/MainFrame.cpp
 page/MemoryRelease.cpp
index 2437642..ea569d1 100644 (file)
                0FFD4D6118651FA300512F6E /* AsyncScrollingCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFD4D5F18651FA300512F6E /* AsyncScrollingCoordinator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                10FB084B14E15C7E00A3DB98 /* PublicURLManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 10FB084A14E15C7E00A3DB98 /* PublicURLManager.h */; };
                112B34D51E60B98300BB310A /* SimpleLineLayoutPagination.h in Headers */ = {isa = PBXBuildFile; fileRef = 112B34D41E60B98300BB310A /* SimpleLineLayoutPagination.h */; };
+               113D0B521F9FDD2B00F611BB /* LayoutContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 113D0B501F9FDD2B00F611BB /* LayoutContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                11E067EE1E6246E500162D16 /* SimpleLineLayoutCoverage.h in Headers */ = {isa = PBXBuildFile; fileRef = 11E067ED1E6246E500162D16 /* SimpleLineLayoutCoverage.h */; settings = {ATTRIBUTES = (Private, ); }; };
                1400D7A817136EA70077CE05 /* ScriptWrappableInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 1400D7A717136EA70077CE05 /* ScriptWrappableInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                1403B99709EB13AF00797C7F /* DOMWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 1403B99509EB13AF00797C7F /* DOMWindow.h */; settings = {ATTRIBUTES = (Private, ); }; };
                10FB084A14E15C7E00A3DB98 /* PublicURLManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicURLManager.h; sourceTree = "<group>"; };
                112B34D01E60B8A700BB310A /* SimpleLineLayoutPagination.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleLineLayoutPagination.cpp; sourceTree = "<group>"; };
                112B34D41E60B98300BB310A /* SimpleLineLayoutPagination.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleLineLayoutPagination.h; sourceTree = "<group>"; };
+               113D0B4F1F9FDD2B00F611BB /* LayoutContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutContext.cpp; sourceTree = "<group>"; };
+               113D0B501F9FDD2B00F611BB /* LayoutContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutContext.h; sourceTree = "<group>"; };
                11E067EB1E62461300162D16 /* SimpleLineLayoutCoverage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleLineLayoutCoverage.cpp; sourceTree = "<group>"; };
                11E067ED1E6246E500162D16 /* SimpleLineLayoutCoverage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleLineLayoutCoverage.h; sourceTree = "<group>"; };
                1400D7A717136EA70077CE05 /* ScriptWrappableInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptWrappableInlines.h; sourceTree = "<group>"; };
                                0F4710D91DB6FE22002DCEC3 /* IntersectionObserverEntry.h */,
                                0F4710DA1DB6FE22002DCEC3 /* IntersectionObserverEntry.idl */,
                                E4916FF6195DF6A0005AB349 /* LayerFlushThrottleState.h */,
+                               113D0B4F1F9FDD2B00F611BB /* LayoutContext.cpp */,
+                               113D0B501F9FDD2B00F611BB /* LayoutContext.h */,
                                931D72F515FE695300C4C07E /* LayoutMilestones.h */,
                                BCE1C41A0D982980003B02F2 /* Location.cpp */,
                                BCE1C4190D982980003B02F2 /* Location.h */,
                                E4916FF7195DF6A0005AB349 /* LayerFlushThrottleState.h in Headers */,
                                580371641A66F1D300BAF519 /* LayerFragment.h in Headers */,
                                7AA3A6A0194B59B6001CBD24 /* LayerPool.h in Headers */,
+                               113D0B521F9FDD2B00F611BB /* LayoutContext.h in Headers */,
                                931D72F615FE695300C4C07E /* LayoutMilestones.h in Headers */,
                                141DC051164834B900371E5A /* LayoutPoint.h in Headers */,
                                141DC053164834B900371E5A /* LayoutRect.h in Headers */,
index db22204..511714d 100644 (file)
@@ -2807,7 +2807,7 @@ static bool rendererNeedsDeferredUpdate(RenderObject& renderer)
 {
     ASSERT(!renderer.beingDestroyed());
     auto& document = renderer.document();
-    return renderer.needsLayout() || document.needsStyleRecalc() || document.inRenderTreeUpdate() || (document.view() && document.view()->isInRenderTreeLayout());
+    return renderer.needsLayout() || document.needsStyleRecalc() || document.inRenderTreeUpdate() || (document.view() && document.view()->layoutContext().isInRenderTreeLayout());
 }
 
 void AXObjectCache::deferRecomputeIsIgnoredIfNeeded(Element* element)
index a2499f4..68be6a9 100644 (file)
@@ -1770,7 +1770,7 @@ void AccessibilityObject::updateBackingStore()
     RefPtr<AccessibilityObject> protectedThis(this);
 
     if (Document* document = this->document()) {
-        if (!document->view()->isInRenderTreeLayout())
+        if (!document->view()->layoutContext().isInRenderTreeLayout())
             document->updateLayoutIgnorePendingStylesheets();
     }
     
index 1f46c31..3ddba38 100644 (file)
@@ -674,7 +674,7 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement
                 // Renders referenced by accessibility objects could get destroyed, if TextIterator ends up triggering
                 // style update/layout here. See also AXObjectCache::deferTextChangedIfNeeded().
                 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->childNeedsStyleRecalc());
-                ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->view()->isInRenderTreeLayout());
+                ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->view()->layoutContext().isInRenderTreeLayout());
                 return plainText(textRange.get(), textIteratorBehaviorForTextRange());
             }
         }
@@ -1446,7 +1446,7 @@ int AccessibilityRenderObject::layoutCount() const
 {
     if (!is<RenderView>(*m_renderer))
         return 0;
-    return downcast<RenderView>(*m_renderer).frameView().layoutCount();
+    return downcast<RenderView>(*m_renderer).frameView().layoutContext().layoutCount();
 }
 
 String AccessibilityRenderObject::text() const
index 55bad30..34010d6 100644 (file)
@@ -1258,7 +1258,7 @@ void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
         return;
 
     RefPtr<FrameView> frameView = view();
-    bool needsLayout = frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout());
+    bool needsLayout = frameView && renderView() && (frameView->layoutContext().isLayoutPending() || renderView()->needsLayout());
     if (needsLayout)
         updateLayout();
 
@@ -1850,7 +1850,7 @@ void Document::resolveStyle(ResolveStyleType type)
         updatedCompositingLayers = frameView.updateCompositingLayersAfterStyleChange();
 
         if (m_renderView->needsLayout())
-            frameView.scheduleRelayout();
+            frameView.layoutContext().scheduleLayout();
     }
 
     // If we wanted to call implicitClose() during recalcStyle, do so now that we're finished.
@@ -1923,7 +1923,7 @@ bool Document::updateStyleIfNeeded()
     ASSERT(isMainThread());
     ASSERT(!view() || !view()->isPainting());
 
-    if (!view() || view()->isInRenderTreeLayout())
+    if (!view() || view()->layoutContext().isInRenderTreeLayout())
         return false;
 
     styleScope().flushPendingUpdate();
@@ -1941,7 +1941,7 @@ void Document::updateLayout()
     ASSERT(isMainThread());
 
     RefPtr<FrameView> frameView = view();
-    if (frameView && frameView->isInRenderTreeLayout()) {
+    if (frameView && frameView->layoutContext().isInRenderTreeLayout()) {
         // View layout should not be re-entrant.
         ASSERT_NOT_REACHED();
         return;
@@ -1957,8 +1957,8 @@ void Document::updateLayout()
     StackStats::LayoutCheckPoint layoutCheckPoint;
 
     // Only do a layout if changes have occurred that make it necessary.      
-    if (frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
-        frameView->layout();
+    if (frameView && renderView() && (frameView->layoutContext().isLayoutPending() || renderView()->needsLayout()))
+        frameView->layoutContext().layout();
 }
 
 void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks runPostLayoutTasks)
@@ -2016,7 +2016,7 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, DimensionsChe
     
     // Check for re-entrancy and assert (same code that is in updateLayout()).
     RefPtr<FrameView> frameView = view();
-    if (frameView && frameView->isInRenderTreeLayout()) {
+    if (frameView && frameView->layoutContext().isInRenderTreeLayout()) {
         // View layout should not be re-entrant.
         ASSERT_NOT_REACHED();
         return true;
@@ -2088,7 +2088,7 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, DimensionsChe
                 break;
             }
             
-            if (currRenderer == frameView->subtreeLayoutRoot())
+            if (currRenderer == frameView->layoutContext().subtreeLayoutRoot())
                 break;
         }
     }
@@ -2096,8 +2096,8 @@ bool Document::updateLayoutIfDimensionsOutOfDate(Element& element, DimensionsChe
     StackStats::LayoutCheckPoint layoutCheckPoint;
 
     // Only do a layout if changes have occurred that make it necessary.      
-    if (requireFullLayout && frameView && renderView() && (frameView->layoutPending() || renderView()->needsLayout()))
-        frameView->layout();
+    if (requireFullLayout && frameView && renderView() && (frameView->layoutContext().isLayoutPending() || renderView()->needsLayout()))
+        frameView->layoutContext().layout();
     
     return requireFullLayout;
 }
@@ -2790,7 +2790,7 @@ void Document::implicitClose()
         // Just bail out. Before or during the onload we were shifted to another page.
         // The old i-Bench suite does this. When this happens don't bother painting or laying out.        
         m_processingLoadEvent = false;
-        view()->unscheduleRelayout();
+        view()->layoutContext().unscheduleLayout();
         return;
     }
 
@@ -2805,7 +2805,7 @@ void Document::implicitClose()
         
         // Always do a layout after loading if needed.
         if (view() && renderView() && (!renderView()->firstChild() || renderView()->needsLayout()))
-            view()->layout();
+            view()->layoutContext().layout();
     }
 
     m_processingLoadEvent = false;
@@ -2862,7 +2862,7 @@ bool Document::shouldScheduleLayout()
     
 bool Document::isLayoutTimerActive()
 {
-    return view() && view()->layoutPending() && !minimumLayoutDelay();
+    return view() && view()->layoutContext().isLayoutPending() && !minimumLayoutDelay();
 }
 
 Seconds Document::minimumLayoutDelay()
index b8aa1e8..f2a0de0 100644 (file)
@@ -1131,7 +1131,7 @@ LayoutRect Element::absoluteEventHandlerBounds(bool& includesFixedPositionElemen
         return LayoutRect();
 
     if (frameView->needsLayout())
-        frameView->layout();
+        frameView->layoutContext().layout();
 
     return absoluteEventBoundsOfElementAndDescendants(includesFixedPositionElements);
 }
index 44a1e88..570bb21 100644 (file)
@@ -363,7 +363,7 @@ void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectio
         return;
 
     FrameView* frameView = document->view();
-    if (frameView && frameView->layoutPending())
+    if (frameView && frameView->layoutContext().isLayoutPending())
         return;
 
     updateAndRevealSelection(intent);
index ccc6634..248f24c 100644 (file)
@@ -78,7 +78,7 @@ static inline RenderWidget* findWidgetRenderer(const Node* node)
 RenderWidget* HTMLEmbedElement::renderWidgetLoadingPlugin() const
 {
     RefPtr<FrameView> view = document().view();
-    if (!view || (!view->isInRenderTreeLayout() && !view->isPainting())) {
+    if (!view || (!view->layoutContext().isInRenderTreeLayout() && !view->isPainting())) {
         // Needs to load the plugin immediatedly because this function is called
         // when JavaScript code accesses the plugin.
         // FIXME: <rdar://16893708> Check if dispatching events here is safe.
index 8697a24..eafdcef 100644 (file)
@@ -238,7 +238,7 @@ void HTMLFormControlElement::didAttachRenderers()
 
         RefPtr<HTMLFormControlElement> element = this;
         auto* frameView = document().view();
-        if (frameView && frameView->isInLayout()) {
+        if (frameView && frameView->layoutContext().isInLayout()) {
             frameView->queuePostLayoutCallback([element] {
                 element->focus();
             });
index bceb8f0..cc06d03 100644 (file)
@@ -301,7 +301,7 @@ void InspectorOverlay::update()
     // Position DOM elements.
     overlayPage()->mainFrame().document()->resolveStyle(Document::ResolveStyleType::Rebuild);
     if (overlayView->needsLayout())
-        overlayView->layout();
+        overlayView->layoutContext().layout();
 
     forcePaint();
 }
index f69b556..c953c2a 100644 (file)
@@ -1687,7 +1687,7 @@ void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
     // but haven't laid out/painted yet.
     // FIXME: Is this behavior specific to iOS? Or should we expose a setting to toggle this behavior?
     if (m_frame.view() && !m_frame.view()->didFirstLayout())
-        m_frame.view()->layout();
+        m_frame.view()->layoutContext().layout();
 #endif
 
     if (deferCheckLoadComplete)
index 7f74e32..c3be635 100644 (file)
@@ -255,7 +255,7 @@ void Frame::setView(RefPtr<FrameView>&& view)
         m_doc->prepareForDestruction();
     
     if (m_view)
-        m_view->unscheduleRelayout();
+        m_view->layoutContext().unscheduleLayout();
     
     m_eventHandler->clear();
 
@@ -763,7 +763,7 @@ Frame* Frame::frameForWidget(const Widget& widget)
 void Frame::clearTimers(FrameView *view, Document *document)
 {
     if (view) {
-        view->unscheduleRelayout();
+        view->layoutContext().unscheduleLayout();
         view->frame().animation().suspendAnimationsForDocument(document);
         view->frame().eventHandler().stopAutoscrollTimer();
     }
@@ -992,7 +992,7 @@ void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor
 
     if (FrameView* view = this->view()) {
         if (document->renderView() && document->renderView()->needsLayout() && view->didFirstLayout())
-            view->layout();
+            view->layoutContext().layout();
     }
 }
 
@@ -1041,7 +1041,7 @@ void Frame::resumeActiveDOMObjectsAndAnimations()
     // Frame::clearTimers() suspended animations and pending relayouts.
     animation().resumeAnimationsForDocument(m_doc.get());
     if (m_view)
-        m_view->scheduleRelayout();
+        m_view->layoutContext().scheduleLayout();
 }
 
 void Frame::deviceOrPageScaleFactorChanged()
index b08b193..3923337 100644 (file)
@@ -164,86 +164,9 @@ Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style)
     return Pagination::BottomToTopPaginated;
 }
 
-class SubtreeLayoutStateMaintainer {
-public:
-    SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
-        : m_subtreeLayoutRoot(subtreeLayoutRoot)
-    {
-        if (m_subtreeLayoutRoot) {
-            RenderView& view = m_subtreeLayoutRoot->view();
-            view.pushLayoutState(*m_subtreeLayoutRoot);
-            if (shouldDisableLayoutStateForSubtree()) {
-                view.disableLayoutState();
-                m_didDisableLayoutState = true;
-            }
-        }
-    }
-
-    ~SubtreeLayoutStateMaintainer()
-    {
-        if (m_subtreeLayoutRoot) {
-            RenderView& view = m_subtreeLayoutRoot->view();
-            view.popLayoutState(*m_subtreeLayoutRoot);
-            if (m_didDisableLayoutState)
-                view.enableLayoutState();
-        }
-    }
-
-    bool shouldDisableLayoutStateForSubtree()
-    {
-        for (auto* renderer = m_subtreeLayoutRoot; renderer; renderer = renderer->container()) {
-            if (renderer->hasTransform() || renderer->hasReflection())
-                return true;
-        }
-        return false;
-    }
-    
-private:
-    RenderElement* m_subtreeLayoutRoot { nullptr };
-    bool m_didDisableLayoutState { false };
-};
-
-#ifndef NDEBUG
-class RenderTreeNeedsLayoutChecker {
-public :
-    RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot)
-        : m_layoutRoot(layoutRoot)
-    {
-    }
-
-    ~RenderTreeNeedsLayoutChecker()
-    {
-        auto reportNeedsLayoutError = [] (const RenderObject& renderer) {
-            WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)");
-            renderer.showRenderTreeForThis();
-            ASSERT_NOT_REACHED();
-        };
-
-        if (m_layoutRoot.needsLayout()) {
-            reportNeedsLayoutError(m_layoutRoot);
-            return;
-        }
-
-        for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) {
-            if (!descendant->needsLayout())
-                continue;
-            
-            reportNeedsLayoutError(*descendant);
-            return;
-        }
-    }
-
-private:
-    const RenderElement& m_layoutRoot;
-};
-#endif
-
 FrameView::FrameView(Frame& frame)
     : m_frame(frame)
     , m_canHaveScrollbars(true)
-    , m_layoutTimer(*this, &FrameView::layoutTimerFired)
-    , m_layoutPhase(OutsideLayout)
-    , m_postLayoutTasksTimer(*this, &FrameView::performPostLayoutTasks)
     , m_updateEmbeddedObjectsTimer(*this, &FrameView::updateEmbeddedObjectsTimerFired)
     , m_isTransparent(false)
     , m_baseBackgroundColor(Color::white)
@@ -255,8 +178,6 @@ FrameView::FrameView(Frame& frame)
     , m_delayedScrollEventTimer(*this, &FrameView::sendScrollEvent)
     , m_isTrackingRepaints(false)
     , m_shouldUpdateWhileOffscreen(true)
-    , m_deferSetNeedsLayoutCount(0)
-    , m_setNeedsLayoutWasDeferred(false)
     , m_speculativeTilingEnabled(false)
     , m_speculativeTilingEnableTimer(*this, &FrameView::speculativeTilingEnableTimerFired)
 #if PLATFORM(IOS)
@@ -274,6 +195,7 @@ FrameView::FrameView(Frame& frame)
     , m_visualUpdatesAllowedByClient(true)
     , m_hasFlippedBlockRenderers(false)
     , m_scrollPinningBehavior(DoNotPin)
+    , m_layoutContext(*this)
 {
     init();
 
@@ -312,9 +234,6 @@ Ref<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize)
 
 FrameView::~FrameView()
 {
-    if (m_postLayoutTasksTimer.isActive())
-        m_postLayoutTasksTimer.stop();
-    
     removeFromAXObjectCache();
     resetScrollbars();
 
@@ -335,16 +254,7 @@ void FrameView::reset()
     m_cannotBlitToWindow = false;
     m_isOverlapped = false;
     m_contentIsOpaque = false;
-    m_layoutTimer.stop();
-    clearSubtreeLayoutRoot();
-    m_delayedLayout = false;
-    m_needsFullRepaint = true;
-    m_layoutSchedulingEnabled = true;
-    m_layoutPhase = OutsideLayout;
-    m_layoutCount = 0;
-    m_postLayoutTasksTimer.stop();
     m_updateEmbeddedObjectsTimer.stop();
-    m_firstLayout = true;
     m_firstLayoutCallbackPending = false;
     m_wasScrolledByUser = false;
     m_safeToPropagateScrollToParent = true;
@@ -362,6 +272,7 @@ void FrameView::reset()
     m_firstVisuallyNonEmptyLayoutCallbackPending = true;
     m_needsDeferredScrollbarsUpdate = false;
     m_maintainScrollPositionAnchor = nullptr;
+    layoutContext().reset();
 }
 
 void FrameView::removeFromAXObjectCache()
@@ -375,8 +286,9 @@ void FrameView::removeFromAXObjectCache()
 
 void FrameView::resetScrollbars()
 {
+    // FIXME: Do we really need this?
+    layoutContext().resetFirstLayoutFlag();
     // Reset the document's scrollbars back to our defaults before we yield the floor.
-    m_firstLayout = true;
     setScrollbarsSuppressed(true);
     if (m_canHaveScrollbars)
         setScrollbarModes(ScrollbarAuto, ScrollbarAuto);
@@ -500,7 +412,7 @@ void FrameView::didReplaceMultipartContent()
 
 bool FrameView::didFirstLayout() const
 {
-    return !m_firstLayout;
+    return layoutContext().didFirstLayout();
 }
 
 void FrameView::invalidateRect(const IntRect& rect)
@@ -653,12 +565,12 @@ void FrameView::didRestoreFromPageCache()
 void FrameView::willDestroyRenderTree()
 {
     detachCustomScrollbars();
-    clearSubtreeLayoutRoot();
+    layoutContext().clearSubtreeLayoutRoot();
 }
 
 void FrameView::didDestroyRenderTree()
 {
-    ASSERT(!subtreeLayoutRoot());
+    ASSERT(!layoutContext().subtreeLayoutRoot());
     ASSERT(m_widgetsInRenderTree.isEmpty());
 
     // If the render tree is destroyed below FrameView::updateEmbeddedObjects(), there will still be a null sentinel in the set.
@@ -676,7 +588,7 @@ void FrameView::setContentsSize(const IntSize& size)
     if (size == contentsSize())
         return;
 
-    m_deferSetNeedsLayoutCount++;
+    layoutContext().disableSetNeedsLayout();
 
     ScrollView::setContentsSize(size);
     contentsResized();
@@ -693,12 +605,7 @@ void FrameView::setContentsSize(const IntSize& size)
         frame().mainFrame().pageOverlayController().didChangeDocumentSize();
         PageCache::singleton().markPagesForContentsSizeChanged(*page);
     }
-
-    ASSERT(m_deferSetNeedsLayoutCount);
-    m_deferSetNeedsLayoutCount--;
-    
-    if (!m_deferSetNeedsLayoutCount)
-        m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen.
+    layoutContext().enableSetNeedsLayout();
 }
 
 void FrameView::adjustViewSize()
@@ -826,7 +733,7 @@ void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, Scrollbar
         vMode = ScrollbarAlwaysOff;
     }
     
-    if (subtreeLayoutRoot())
+    if (layoutContext().subtreeLayoutRoot())
         return;
     
     auto* document = frame().document();
@@ -881,7 +788,7 @@ void FrameView::willRecalcStyle()
 bool FrameView::updateCompositingLayersAfterStyleChange()
 {
     // If we expect to update compositing after an incipient layout, don't do so here.
-    if (!renderView() || needsLayout() || isInLayout())
+    if (!renderView() || needsLayout() || layoutContext().isInLayout())
         return false;
     return renderView()->compositor().didRecalcStyleWithNoPendingLayout();
 }
@@ -1178,7 +1085,7 @@ void FrameView::topContentInsetDidChange(float newTopContentInset)
     if (platformWidget())
         platformSetTopContentInset(newTopContentInset);
     
-    layout();
+    layoutContext().layout();
     // Every scroll that happens as the result of content inset change is programmatic.
     SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
     updateScrollbars(scrollPosition());
@@ -1262,7 +1169,7 @@ void FrameView::setIsInWindow(bool isInWindow)
         renderView->setIsInWindow(isInWindow);
 }
 
-inline void FrameView::forceLayoutParentViewIfNeeded()
+void FrameView::forceLayoutParentViewIfNeeded()
 {
     RenderWidget* ownerRenderer = frame().ownerRenderer();
     if (!ownerRenderer)
@@ -1287,43 +1194,7 @@ inline void FrameView::forceLayoutParentViewIfNeeded()
     // out for the first time, or when the RenderSVGRoot size has changed dynamically (eg. via <script>).
 
     ownerRenderer->setNeedsLayoutAndPrefWidthsRecalc();
-    ownerRenderer->view().frameView().scheduleRelayout();
-}
-
-#if ENABLE(TEXT_AUTOSIZING)
-static void applyTextSizingIfNeeded(RenderElement& layoutRoot)
-{
-    auto& settings = layoutRoot.settings();
-    if (!settings.textAutosizingEnabled() || layoutRoot.view().printing())
-        return;
-    auto minimumZoomFontSize = settings.minimumZoomFontSize();
-    if (!minimumZoomFontSize)
-        return;
-    auto textAutosizingWidth = layoutRoot.page().textAutosizingWidth();
-    if (auto overrideWidth = settings.textAutosizingWindowSizeOverride().width())
-        textAutosizingWidth = overrideWidth;
-    if (!textAutosizingWidth)
-        return;
-    layoutRoot.adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth);
-    if (!layoutRoot.needsLayout())
-        return;
-    LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth);
-    layoutRoot.layout();
-}
-#endif
-
-bool FrameView::handleLayoutWithFrameFlatteningIfNeeded()
-{
-    if (!isInChildFrameWithFrameFlattening())
-        return false;
-    
-    if (!m_frameFlatteningViewSizeForMediaQuery) {
-        LOG_WITH_STREAM(MediaQueries, stream << "FrameView " << this << " snapshotting size " <<  ScrollView::layoutSize() << " for media queries");
-        m_frameFlatteningViewSizeForMediaQuery = ScrollView::layoutSize();
-    }
-    startLayoutAtMainFrameViewIfNeeded();
-    auto* layoutRoot = subtreeLayoutRoot() ? subtreeLayoutRoot() : frame().document()->renderView();
-    return !layoutRoot || !layoutRoot->needsLayout();
+    ownerRenderer->view().frameView().layoutContext().scheduleLayout();
 }
 
 void FrameView::markRootOrBodyRendererDirty() const
@@ -1343,7 +1214,7 @@ void FrameView::adjustScrollbarsForLayout(bool isFirstLayout)
     ScrollbarMode hMode;
     ScrollbarMode vMode;
     calculateScrollbarModesForLayout(hMode, vMode);
-    if (isFirstLayout && !isLayoutNested()) {
+    if (isFirstLayout && !layoutContext().isLayoutNested()) {
         setScrollbarsSuppressed(true);
         // Set the initial vMode to AlwaysOn if we're auto.
         if (vMode == ScrollbarAuto)
@@ -1360,237 +1231,72 @@ void FrameView::adjustScrollbarsForLayout(bool isFirstLayout)
         setScrollbarModes(hMode, vMode);
 }
 
-void FrameView::updateStyleForLayout()
-{
-    Document& document = *frame().document();
-    // Viewport-dependent media queries may cause us to need completely different style information.
-    auto* styleResolver = document.styleScope().resolverIfExists();
-    if (!styleResolver || styleResolver->hasMediaQueriesAffectedByViewportChange()) {
-        LOG(Layout, "  hasMediaQueriesAffectedByViewportChange, enqueueing style recalc");
-        document.styleScope().didChangeStyleSheetEnvironment();
-        // FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds.
-        InspectorInstrumentation::mediaQueryResultChanged(document);
-    }
-    document.evaluateMediaQueryList();
-    // If there is any pagination to apply, it will affect the RenderView's style, so we should
-    // take care of that now.
-    applyPaginationToViewport();
-    // Always ensure our style info is up-to-date. This can happen in situations where
-    // the layout beats any sort of style recalc update that needs to occur.
-    document.updateStyleIfNeeded();
-}
-
-bool FrameView::canPerformLayout() const
+void FrameView::willDoLayout(WeakPtr<RenderElement> layoutRoot)
 {
-    if (isInRenderTreeLayout())
-        return false;
-
-    if (layoutDisallowed())
-        return false;
-
-    if (isPainting())
-        return false;
-
-    if (!subtreeLayoutRoot() && !frame().document()->renderView())
-        return false;
-
-    return true;
-}
-
-void FrameView::layout()
-{
-    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate());
-    ASSERT(!isPainting());
-    ASSERT(frame().view() == this);
-    ASSERT(frame().document());
-    ASSERT(frame().document()->pageCacheState() == Document::NotInPageCache);
-
-    if (!canPerformLayout()) {
-        LOG(Layout, "  is not allowed, bailing");
+    bool subtreeLayout = !is<RenderView>(*layoutRoot);
+    if (subtreeLayout)
         return;
+    
+    if (auto* body = frame().document()->bodyOrFrameset()) {
+        if (is<HTMLFrameSetElement>(*body) && !frameFlatteningEnabled() && body->renderer())
+            body->renderer()->setChildNeedsLayout();
     }
-    // Protect the view from being deleted during layout (in recalcStyle).
-    Ref<FrameView> protectedThis(*this);
-    TraceScope tracingScope(LayoutStart, LayoutEnd);
-    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLayout(frame());
-    AnimationUpdateBlock animationUpdateBlock(&frame().animation());
-
-    SetForScope<LayoutNestedState> nestedState(m_layoutNestedState, m_layoutNestedState == LayoutNestedState::NotInLayout ? LayoutNestedState::NotNested : LayoutNestedState::Nested);
-    // Every scroll that happens during layout is programmatic.
-    SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true);
-    SetForScope<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false);
-
-    m_layoutTimer.stop();
-    m_delayedLayout = false;
-    m_setNeedsLayoutWasDeferred = false;
-#if PLATFORM(IOS)
-    if (updateFixedPositionLayoutRect() && subtreeLayoutRoot())
-        convertSubtreeLayoutToFullLayout();
-#endif
-
-    if (handleLayoutWithFrameFlatteningIfNeeded())
-        return;
-
-    Document& document = *frame().document();
-    WeakPtr<RenderElement> layoutRoot;
-    bool isSubtreeLayout = false;
-    {
-        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, InPreLayout);
-
-        // If this is a new top-level layout and there are any remaining tasks from the previous layout, finish them now.
-        if (!isLayoutNested() && m_postLayoutTasksTimer.isActive() && !isInChildFrameWithFrameFlattening())
-            performPostLayoutTasks();
-
-        updateStyleForLayout();
-        if (hasOneRef())
-            return;
-
-        autoSizeIfEnabled();
-        if (!renderView())
-            return;
-
-        isSubtreeLayout = subtreeLayoutRoot();
-        layoutRoot = makeWeakPtr(subtreeLayoutRoot() ? subtreeLayoutRoot() : renderView());
-        m_needsFullRepaint = !isSubtreeLayout && (m_firstLayout || renderView()->printing());
-
-        if (!isSubtreeLayout) {
-            if (auto* body = document.bodyOrFrameset()) {
-                if (is<HTMLFrameSetElement>(*body) && !frameFlatteningEnabled() && body->renderer())
-                    body->renderer()->setChildNeedsLayout();
-            }
-#if !LOG_DISABLED
-            if (m_firstLayout && !frame().ownerElement())
-                LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs\n", this, document.timeSinceDocumentCreation().value());
-#endif
-            if (m_firstLayout) {
-                m_lastViewportSize = sizeForResizeEvent();
-                m_lastZoomFactor = layoutRoot->style().zoom();
-                m_firstLayoutCallbackPending = true;
-            }
-            adjustScrollbarsForLayout(m_firstLayout);
-
-            auto oldSize = m_size;
-            m_size = layoutSize();
-            if (oldSize != m_size) {
-                LOG(Layout, "  layout size changed from %.3fx%.3f to %.3fx%.3f", oldSize.width().toFloat(), oldSize.height().toFloat(), m_size.width().toFloat(), m_size.height().toFloat());
-                m_needsFullRepaint = true;
-                if (!m_firstLayout)
-                    markRootOrBodyRendererDirty();
-            }
-            m_firstLayout = false;
-        }
-
-        ASSERT(m_layoutPhase == InPreLayout);
-        forceLayoutParentViewIfNeeded();
+    auto firstLayout = !layoutContext().didFirstLayout();
+    if (firstLayout) {
+        m_lastViewportSize = sizeForResizeEvent();
+        m_lastZoomFactor = layoutRoot->style().zoom();
+        m_firstLayoutCallbackPending = true;
     }
-    {
-        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, InRenderTreeLayout);
-
-        SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(subtreeLayoutRoot());
-        RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
-#ifndef NDEBUG
-        RenderTreeNeedsLayoutChecker checker(*layoutRoot);
-#endif
-        layoutRoot->layout();
-        ASSERT(m_layoutPhase == InRenderTreeLayout);
-#if ENABLE(TEXT_AUTOSIZING)
-        applyTextSizingIfNeeded(*layoutRoot);
-#endif
-        clearSubtreeLayoutRoot();
-    }
-    {
-        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, InViewSizeAdjust);
-
-        if (!isSubtreeLayout && !renderView()->printing()) {
-            // This is to protect m_needsFullRepaint's value when layout() is getting re-entered through adjustViewSize().
-            SetForScope<bool> needsFullRepaint(m_needsFullRepaint);
-            adjustViewSize();
-            // FIXME: Firing media query callbacks synchronously on nested frames could produced a detached FrameView here by
-            // navigating away from the current document (see webkit.org/b/173329).
-            if (hasOneRef())
-                return;
-        }
+    adjustScrollbarsForLayout(firstLayout);
+        
+    auto oldSize = m_size;
+    LayoutSize newSize = layoutSize();
+    if (oldSize != newSize) {
+        m_size = newSize;
+        LOG(Layout, "  layout size changed from %.3fx%.3f to %.3fx%.3f", oldSize.width().toFloat(), oldSize.height().toFloat(),     newSize.width().toFloat(), newSize.height().toFloat());
+        layoutContext().setNeedsFullRepaint();
+        if (!firstLayout)
+            markRootOrBodyRendererDirty();
     }
-    {
-        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, InPostLayout);
-
-        // Now update the positions of all layers.
-        if (m_needsFullRepaint)
-            renderView()->repaintRootContents();
-
-        renderView()->releaseProtectedRenderWidgets();
-
-        ASSERT(!layoutRoot->needsLayout());
-        auto* layoutRootEnclosingLayer = layoutRoot->enclosingLayer();
-        layoutRootEnclosingLayer->updateLayerPositionsAfterLayout(renderView()->layer(), updateLayerPositionFlags(layoutRootEnclosingLayer, isSubtreeLayout, m_needsFullRepaint));
+    forceLayoutParentViewIfNeeded();
+}
 
-        updateCompositingLayersAfterLayout();
+void FrameView::didLayout(WeakPtr<RenderElement> layoutRoot)
+{
+    renderView()->releaseProtectedRenderWidgets();
+    auto* layoutRootEnclosingLayer = layoutRoot->enclosingLayer();
+    layoutRootEnclosingLayer->updateLayerPositionsAfterLayout(renderView()->layer(), updateLayerPositionFlags(layoutRootEnclosingLayer, !is<RenderView>(*layoutRoot), layoutContext().needsFullRepaint()));
 
-        m_layoutCount++;
+    updateCompositingLayersAfterLayout();
 
 #if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK)
-        if (AXObjectCache* cache = document.existingAXObjectCache())
-            cache->postNotification(layoutRoot.get(), AXObjectCache::AXLayoutComplete);
+    if (auto* cache = frame().document()->existingAXObjectCache())
+        cache->postNotification(layoutRoot.get(), AXObjectCache::AXLayoutComplete);
 #endif
 
 #if ENABLE(DASHBOARD_SUPPORT)
-        updateAnnotatedRegions();
+    updateAnnotatedRegions();
 #endif
 
 #if ENABLE(IOS_TOUCH_EVENTS)
-        document.setTouchEventRegionsNeedUpdate();
+    frame().document()->setTouchEventRegionsNeedUpdate();
 #endif
 
-        updateCanBlitOnScrollRecursively();
-
-        handleDeferredScrollUpdateAfterContentSizeChange();
-
-        handleDeferredScrollbarsUpdateAfterDirectionChange();
-
-        if (document.hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
-            updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight());
-
-        document.markers().invalidateRectsForAllMarkers();
-
-        runOrSchedulePostLayoutTasks();
+    updateCanBlitOnScrollRecursively();
 
-        InspectorInstrumentation::didLayout(cookie, *layoutRoot);
-        DebugPageOverlays::didLayout(frame());
-    }
-}
-    
-void FrameView::runOrSchedulePostLayoutTasks()
-{
-    if (m_postLayoutTasksTimer.isActive())
-        return;
+    handleDeferredScrollUpdateAfterContentSizeChange();
 
-    if (isInChildFrameWithFrameFlattening()) {
-        // While flattening frames, we defer post layout tasks to avoid getting stuck in a cycle,
-        // except updateWidgetPositions() which is required to kick off subframe layout in certain cases.
-        if (!m_inPerformPostLayoutTasks)
-            updateWidgetPositions();
-        m_postLayoutTasksTimer.startOneShot(0_s);
-        return;
-    }
+    handleDeferredScrollbarsUpdateAfterDirectionChange();
 
-    // If we are already in performPostLayoutTasks(), defer post layout tasks until after we return
-    // to avoid re-entrancy.
-    if (m_inPerformPostLayoutTasks) {
-        m_postLayoutTasksTimer.startOneShot(0_s);
-        return;
-    }
+    if (frame().document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
+        updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight());
 
-    performPostLayoutTasks();
-    if (needsLayout()) {
-        // If performPostLayoutTasks() made us layout again, let's defer the tasks until after we return.
-        m_postLayoutTasksTimer.startOneShot(0_s);
-        layout();
-    }
+    frame().document()->markers().invalidateRectsForAllMarkers();
 }
 
 bool FrameView::shouldDeferScrollUpdateAfterContentSizeChange()
 {
-    return (m_layoutPhase < InPostLayout) && (m_layoutPhase != OutsideLayout);
+    return (layoutContext().layoutPhase() < LayoutContext::LayoutPhase::InPostLayout) && (layoutContext().layoutPhase() != LayoutContext::LayoutPhase::OutsideLayout);
 }
 
 RenderBox* FrameView::embeddedContentBox() const
@@ -1939,7 +1645,7 @@ void FrameView::updateLayoutViewport()
     
     // Don't update the layout viewport if we're in the middle of adjusting scrollbars. We'll get another call
     // as a post-layout task.
-    if (m_layoutPhase == InViewSizeAdjust)
+    if (layoutContext().layoutPhase() == LayoutContext::LayoutPhase::InViewSizeAdjust)
         return;
 
     LayoutRect layoutViewport = layoutViewportRect();
@@ -2459,7 +2165,7 @@ void FrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
     // Only do a layout if changes have occurred that make it necessary.
     RenderView* renderView = this->renderView();
     if (renderView && renderView->needsLayout())
-        layout();
+        layoutContext().layout();
     else
         scrollToAnchor();
 }
@@ -2633,10 +2339,10 @@ void FrameView::resumeVisibleImageAnimationsIncludingSubframes()
 void FrameView::updateLayerPositionsAfterScrolling()
 {
     // If we're scrolling as a result of updating the view size after layout, we'll update widgets and layer positions soon anyway.
-    if (m_layoutPhase == InViewSizeAdjust)
+    if (layoutContext().layoutPhase() == LayoutContext::LayoutPhase::InViewSizeAdjust)
         return;
 
-    if (!isLayoutNested() && hasViewportConstrainedObjects()) {
+    if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
         if (RenderView* renderView = this->renderView()) {
             updateWidgetPositions();
             renderView->layer()->updateLayerPositionsAfterDocumentScroll();
@@ -2673,12 +2379,12 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const
 
 void FrameView::updateCompositingLayersAfterScrolling()
 {
-    ASSERT(m_layoutPhase >= InPostLayout || m_layoutPhase == OutsideLayout);
+    ASSERT(layoutContext().layoutPhase() >= LayoutContext::LayoutPhase::InPostLayout || layoutContext().layoutPhase() == LayoutContext::LayoutPhase::OutsideLayout);
 
     if (!shouldUpdateCompositingLayersAfterScrolling())
         return;
 
-    if (!isLayoutNested() && hasViewportConstrainedObjects()) {
+    if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
         if (RenderView* renderView = this->renderView())
             renderView->compositor().updateCompositingLayers(CompositingUpdateType::OnScroll);
     }
@@ -2781,7 +2487,7 @@ void FrameView::availableContentSizeChanged(AvailableSizeChangeReason reason)
         // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate
         // way of handling potential recursive layouts when the viewport is resized to accomodate
         // the content but the content always overflows the viewport. See webkit.org/b/165781.
-        if (!(layoutPhase() == InViewSizeAdjust && useFixedLayout()))
+        if (!(layoutContext().layoutPhase() == LayoutContext::LayoutPhase::InViewSizeAdjust && useFixedLayout()))
             document->updateViewportUnitsOnResize();
     }
 
@@ -2817,7 +2523,7 @@ void FrameView::updateContentsSize()
 #endif
 
     if (shouldLayoutAfterContentsResized() && needsLayout())
-        layout();
+        layoutContext().layout();
 
     if (RenderView* renderView = this->renderView()) {
         if (renderView->usesCompositing())
@@ -3015,169 +2721,14 @@ void FrameView::hide()
     adjustTiledBackingCoverage();
 }
 
-void FrameView::convertSubtreeLayoutToFullLayout()
-{
-    ASSERT(subtreeLayoutRoot());
-    subtreeLayoutRoot()->markContainingBlocksForLayout(ScheduleRelayout::No);
-    clearSubtreeLayoutRoot();
-}
-
-void FrameView::layoutTimerFired()
-{
-#if !LOG_DISABLED
-    if (!frame().document()->ownerElement())
-        LOG(Layout, "FrameView %p layout timer fired at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
-#endif
-    layout();
-}
-
-void FrameView::scheduleRelayout()
-{
-    // FIXME: We should assert the page is not in the page cache, but that is causing
-    // too many false assertions.  See <rdar://problem/7218118>.
-    ASSERT(frame().view() == this);
-
-    if (subtreeLayoutRoot())
-        convertSubtreeLayoutToFullLayout();
-    if (!m_layoutSchedulingEnabled)
-        return;
-    if (!needsLayout())
-        return;
-    if (!frame().document()->shouldScheduleLayout())
-        return;
-    InspectorInstrumentation::didInvalidateLayout(frame());
-    // When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames.
-    // Also invalidate parent frame starting from the owner element of this frame.
-    if (frame().ownerRenderer() && isInChildFrameWithFrameFlattening())
-        frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain);
-
-    Seconds delay = frame().document()->minimumLayoutDelay();
-    if (m_layoutTimer.isActive() && m_delayedLayout && !delay)
-        unscheduleRelayout();
-
-    if (m_layoutTimer.isActive())
-        return;
-
-    m_delayedLayout = delay.value();
-
-#if !LOG_DISABLED
-    if (!frame().document()->ownerElement())
-        LOG(Layout, "FrameView %p scheduling layout for %.3fs", this, delay.value());
-#endif
-
-    m_layoutTimer.startOneShot(delay);
-}
-
-static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* descendant)
-{
-    for (RenderObject* r = descendant; r; r = r->container()) {
-        if (r == ancestor)
-            return true;
-    }
-    return false;
-}
-
-void FrameView::scheduleRelayoutOfSubtree(RenderElement& newRelayoutRoot)
-{
-    ASSERT(renderView());
-    const RenderView& renderView = *this->renderView();
-
-    // Try to catch unnecessary work during render tree teardown.
-    ASSERT(!renderView.renderTreeBeingDestroyed());
-    ASSERT(frame().view() == this);
-
-    if (renderView.needsLayout() && !subtreeLayoutRoot()) {
-        newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
-        return;
-    }
-
-    if (!layoutPending() && m_layoutSchedulingEnabled) {
-        Seconds delay = renderView.document().minimumLayoutDelay();
-        ASSERT(!newRelayoutRoot.container() || is<RenderView>(newRelayoutRoot.container()) || !newRelayoutRoot.container()->needsLayout());
-        m_subtreeLayoutRoot = makeWeakPtr(newRelayoutRoot);
-        InspectorInstrumentation::didInvalidateLayout(frame());
-        m_delayedLayout = delay.value();
-        m_layoutTimer.startOneShot(delay);
-        return;
-    }
-
-    auto* subtreeLayoutRoot = this->subtreeLayoutRoot();
-    if (subtreeLayoutRoot == &newRelayoutRoot)
-        return;
-
-    if (!subtreeLayoutRoot) {
-        // We already have a pending (full) layout. Just mark the subtree for layout.
-        newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
-        InspectorInstrumentation::didInvalidateLayout(frame());
-        return;
-    }
-
-    if (isObjectAncestorContainerOf(subtreeLayoutRoot, &newRelayoutRoot)) {
-        // Keep the current root.
-        newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, subtreeLayoutRoot);
-        ASSERT(!subtreeLayoutRoot->container() || is<RenderView>(subtreeLayoutRoot->container()) || !subtreeLayoutRoot->container()->needsLayout());
-        return;
-    }
-
-    if (isObjectAncestorContainerOf(&newRelayoutRoot, subtreeLayoutRoot)) {
-        // Re-root at newRelayoutRoot.
-        subtreeLayoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &newRelayoutRoot);
-        m_subtreeLayoutRoot = makeWeakPtr(newRelayoutRoot);
-        ASSERT(!newRelayoutRoot.container() || is<RenderView>(newRelayoutRoot.container()) || !newRelayoutRoot.container()->needsLayout());
-        InspectorInstrumentation::didInvalidateLayout(frame());
-        return;
-    }
-    // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead.
-    convertSubtreeLayoutToFullLayout();
-    newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
-    InspectorInstrumentation::didInvalidateLayout(frame());
-}
-
-bool FrameView::layoutPending() const
-{
-    return m_layoutTimer.isActive();
-}
-
 bool FrameView::needsLayout() const
 {
-    // This can return true in cases where the document does not have a body yet.
-    // Document::shouldScheduleLayout takes care of preventing us from scheduling
-    // layout in that case.
-    RenderView* renderView = this->renderView();
-    return layoutPending()
-        || (renderView && renderView->needsLayout())
-        || subtreeLayoutRoot()
-        || (m_deferSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred);
+    return layoutContext().needsLayout();
 }
 
 void FrameView::setNeedsLayout()
 {
-    if (m_deferSetNeedsLayoutCount) {
-        m_setNeedsLayoutWasDeferred = true;
-        return;
-    }
-
-    if (auto* renderView = this->renderView()) {
-        ASSERT(!renderView->inHitTesting());
-        renderView->setNeedsLayout();
-    }
-}
-
-void FrameView::unscheduleRelayout()
-{
-    if (m_postLayoutTasksTimer.isActive())
-        m_postLayoutTasksTimer.stop();
-
-    if (!m_layoutTimer.isActive())
-        return;
-
-#if !LOG_DISABLED
-    if (!frame().document()->ownerElement())
-        LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
-#endif
-    
-    m_layoutTimer.stop();
-    m_delayedLayout = false;
+    layoutContext().setNeedsLayout();
 }
 
 void FrameView::scheduleSelectionUpdate()
@@ -3187,7 +2738,7 @@ void FrameView::scheduleSelectionUpdate()
     // FIXME: We should not need to go through the layout process since selection update does not change dimension/geometry.
     // However we can't tell at this point if the tree is stable yet, so let's just schedule a root only layout for now.
     setNeedsLayout();
-    scheduleRelayout();
+    layoutContext().scheduleLayout();
 }
 
 bool FrameView::isTransparent() const
@@ -3444,7 +2995,7 @@ void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
 
 bool FrameView::updateEmbeddedObjects()
 {
-    if (isLayoutNested() || !m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())
+    if (layoutContext().isLayoutNested() || !m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())
         return true;
 
     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
@@ -3475,8 +3026,7 @@ void FrameView::updateEmbeddedObjectsTimerFired()
 
 void FrameView::flushAnyPendingPostLayoutTasks()
 {
-    if (m_postLayoutTasksTimer.isActive())
-        performPostLayoutTasks();
+    layoutContext().flushAsynchronousTasks();
     if (m_updateEmbeddedObjectsTimer.isActive())
         updateEmbeddedObjectsTimerFired();
 }
@@ -3488,7 +3038,7 @@ void FrameView::queuePostLayoutCallback(Function<void()>&& callback)
 
 void FrameView::flushPostLayoutTasksQueue()
 {
-    if (isLayoutNested())
+    if (layoutContext().isLayoutNested())
         return;
 
     if (!m_postLayoutCallbackQueue.size())
@@ -3501,20 +3051,14 @@ void FrameView::flushPostLayoutTasksQueue()
 
 void FrameView::performPostLayoutTasks()
 {
-    if (m_inPerformPostLayoutTasks)
-        return;
-
-    SetForScope<bool> inPerformPostLayoutTasks(m_inPerformPostLayoutTasks, true);
     // FIXME: We should not run any JavaScript code in this function.
     LOG(Layout, "FrameView %p performPostLayoutTasks", this);
 
-    m_postLayoutTasksTimer.stop();
-
     frame().selection().updateAppearanceAfterLayout();
 
     flushPostLayoutTasksQueue();
 
-    if (!isLayoutNested() && frame().document()->documentElement())
+    if (!layoutContext().isLayoutNested() && frame().document()->documentElement())
         fireLayoutRelatedMilestonesIfNeeded();
 
 #if PLATFORM(IOS)
@@ -3574,7 +3118,7 @@ IntSize FrameView::sizeForResizeEvent() const
 
 void FrameView::sendResizeEventIfNeeded()
 {
-    if (isInRenderTreeLayout() || needsLayout())
+    if (layoutContext().isInRenderTreeLayout() || needsLayout())
         return;
 
     RenderView* renderView = this->renderView();
@@ -3593,7 +3137,7 @@ void FrameView::sendResizeEventIfNeeded()
     m_lastViewportSize = currentSize;
     m_lastZoomFactor = currentZoomFactor;
 
-    if (m_firstLayout)
+    if (!layoutContext().didFirstLayout())
         return;
 
 #if PLATFORM(IOS)
@@ -3659,8 +3203,8 @@ void FrameView::autoSizeIfEnabled()
 
     LOG(Layout, "FrameView %p autoSizeIfEnabled", this);
     SetForScope<bool> changeInAutoSize(m_inAutoSize, true);
-    if (subtreeLayoutRoot())
-        convertSubtreeLayoutToFullLayout();
+    if (layoutContext().subtreeLayoutRoot())
+        layoutContext().convertSubtreeLayoutToFullLayout();
     // Start from the minimum size and allow it to grow.
     resize(m_minAutoSize.width(), m_minAutoSize.height());
     IntSize size = frameRect().size();
@@ -4269,30 +3813,6 @@ bool FrameView::isInChildFrameWithFrameFlattening() const
     return false;
 }
 
-void FrameView::startLayoutAtMainFrameViewIfNeeded()
-{
-    // When we start a layout at the child level as opposed to the topmost frame view and this child
-    // frame requires flattening, we need to re-initiate the layout at the topmost view. Layout
-    // will hit this view eventually.
-    FrameView* parentView = parentFrameView();
-    if (!parentView)
-        return;
-
-    // In the middle of parent layout, no need to restart from topmost.
-    if (parentView->isInLayout())
-        return;
-
-    // Parent tree is clean. Starting layout from it would have no effect.
-    if (!parentView->needsLayout())
-        return;
-
-    while (parentView->parentFrameView())
-        parentView = parentView->parentFrameView();
-
-    LOG(Layout, "  frame flattening, starting from root");
-    parentView->layout();
-}
-
 void FrameView::updateControlTints()
 {
     // This is called when control tints are changed from aqua/graphite to clear and vice versa.
@@ -4323,7 +3843,7 @@ void FrameView::updateControlTints()
 void FrameView::paintControlTints()
 {
     if (needsLayout())
-        layout();
+        layoutContext().layout();
 
     GraphicsContext context(GraphicsContext::NonPaintingReasons::UpdatingControlTints);
     if (platformWidget()) {
@@ -4440,7 +3960,7 @@ void FrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect
         return;
     }
 
-    if (!inPaintableState())
+    if (!layoutContext().inPaintableState())
         return;
 
     ASSERT(!needsLayout());
@@ -4581,7 +4101,7 @@ void FrameView::updateLayoutAndStyleIfNeededRecursive()
             if (view->frame().document()->updateStyleIfNeeded())
                 didWork = true;
             if (view->needsLayout()) {
-                view->layout();
+                view->layoutContext().layout();
                 didWork = true;
             }
         }
@@ -4679,7 +4199,7 @@ void FrameView::enableAutoSizeMode(bool enable, const IntSize& minSize, const In
     m_didRunAutosize = false;
 
     setNeedsLayout();
-    scheduleRelayout();
+    layoutContext().scheduleLayout();
     if (m_shouldAutoSize)
         return;
 
@@ -4691,9 +4211,9 @@ void FrameView::enableAutoSizeMode(bool enable, const IntSize& minSize, const In
 
 void FrameView::forceLayout(bool allowSubtreeLayout)
 {
-    if (!allowSubtreeLayout && subtreeLayoutRoot())
-        convertSubtreeLayoutToFullLayout();
-    layout();
+    if (!allowSubtreeLayout && layoutContext().subtreeLayoutRoot())
+        layoutContext().convertSubtreeLayoutToFullLayout();
+    layoutContext().layout();
 }
 
 void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSizeOrNot shouldAdjustViewSize)
index 8a5c75d..eeaef2a 100644 (file)
@@ -27,6 +27,7 @@
 #include "AdjustViewSizeOrNot.h"
 #include "Color.h"
 #include "ContainerNode.h"
+#include "LayoutContext.h"
 #include "LayoutMilestones.h"
 #include "LayoutRect.h"
 #include "Pagination.h"
@@ -69,6 +70,7 @@ class FrameView final : public ScrollView {
 public:
     friend class RenderView;
     friend class Internals;
+    friend class LayoutContext;
 
     WEBCORE_EXPORT static Ref<FrameView> create(Frame&);
     static Ref<FrameView> create(Frame&, const IntSize& initialSize);
@@ -104,28 +106,16 @@ public:
     void setContentsSize(const IntSize&) final;
     void updateContentsSize() final;
 
-    void layout();
+    const LayoutContext& layoutContext() const { return m_layoutContext; }
+    LayoutContext& layoutContext() { return m_layoutContext; }
+
     WEBCORE_EXPORT bool didFirstLayout() const;
-    void layoutTimerFired();
-    void scheduleRelayout();
-    void scheduleRelayoutOfSubtree(RenderElement&);
-    void unscheduleRelayout();
     void queuePostLayoutCallback(WTF::Function<void ()>&&);
-    bool layoutPending() const;
-    bool isInLayout() const { return m_layoutPhase != OutsideLayout; }
-    bool isInRenderTreeLayout() const { return m_layoutPhase == InRenderTreeLayout; }
-    bool inPaintableState() const { return m_layoutPhase != InRenderTreeLayout && m_layoutPhase != InViewSizeAdjust && (m_layoutPhase != InPostLayout || m_inPerformPostLayoutTasks); }
-
-    RenderElement* subtreeLayoutRoot() const { return m_subtreeLayoutRoot.get(); }
-    void clearSubtreeLayoutRoot() { m_subtreeLayoutRoot.clear(); }
-    int layoutCount() const { return m_layoutCount; }
 
     WEBCORE_EXPORT bool needsLayout() const;
     WEBCORE_EXPORT void setNeedsLayout();
     void setViewportConstrainedObjectsNeedLayout();
 
-    bool needsFullRepaint() const { return m_needsFullRepaint; }
-
     WEBCORE_EXPORT bool renderedCharactersExceed(unsigned threshold);
 
     void scheduleSelectionUpdate();
@@ -386,9 +376,8 @@ public:
 
     bool isInChildFrameWithFrameFlattening() const;
 
-    void startDisallowingLayout() { ++m_layoutDisallowedCount; }
-    void endDisallowingLayout() { ASSERT(m_layoutDisallowedCount > 0); --m_layoutDisallowedCount; }
-    bool layoutDisallowed() const { return m_layoutDisallowedCount; }
+    void startDisallowingLayout() { layoutContext().startDisallowingLayout(); }
+    void endDisallowingLayout() { layoutContext().endDisallowingLayout(); }
 
     static double currentPaintTimeStamp() { return sCurrentPaintTimeStamp; } // returns 0 if not painting
     
@@ -661,7 +650,6 @@ private:
         InViewSizeAdjust,
         InPostLayout
     };
-    LayoutPhase layoutPhase() const { return m_layoutPhase; }
 
     bool isFrameView() const final { return true; }
 
@@ -687,7 +675,6 @@ private:
 
     void forceLayoutParentViewIfNeeded();
     void flushPostLayoutTasksQueue();
-    void runOrSchedulePostLayoutTasks();
     void performPostLayoutTasks();
     void autoSizeIfEnabled();
 
@@ -736,7 +723,6 @@ private:
     void sendResizeEventIfNeeded();
 
     void adjustScrollbarsForLayout(bool firstLayout);
-    void updateStyleForLayout();
 
     void handleDeferredScrollbarsUpdateAfterDirectionChange();
 
@@ -762,13 +748,10 @@ private:
 
     FrameView* parentFrameView() const;
 
-    bool handleLayoutWithFrameFlatteningIfNeeded();
-    void startLayoutAtMainFrameViewIfNeeded();
     bool frameFlatteningEnabled() const;
     bool isFrameFlatteningValidForThisFrame() const;
-    
+
     void markRootOrBodyRendererDirty() const;
-    bool canPerformLayout() const;
 
     bool qualifiesAsVisuallyNonEmpty() const;
     bool isViewForDocumentInFrame() const;
@@ -778,11 +761,12 @@ private:
     void removeFromAXObjectCache();
     void notifyWidgets(WidgetNotification);
 
-    void convertSubtreeLayoutToFullLayout();
-
+    void setFrameFlatteningViewSizeForMediaQuery() { m_frameFlatteningViewSizeForMediaQuery = layoutSize(); }
+    bool frameFlatteningViewSizeForMediaQueryIsSet() const { return m_frameFlatteningViewSizeForMediaQuery.has_value(); }
     RenderElement* viewportRenderer() const;
     
-    bool isLayoutNested() const { return m_layoutNestedState == LayoutNestedState::Nested; }
+    void willDoLayout(WeakPtr<RenderElement> layoutRoot);
+    void didLayout(WeakPtr<RenderElement> layoutRoot);
 
     HashSet<Widget*> m_widgetsInRenderTree;
 
@@ -796,28 +780,14 @@ private:
 
     std::unique_ptr<HashSet<const RenderElement*>> m_slowRepaintObjects;
 
-    bool m_needsFullRepaint;
-    
     bool m_canHaveScrollbars;
     bool m_cannotBlitToWindow;
     bool m_isOverlapped { false };
     bool m_contentIsOpaque;
 
-    Timer m_layoutTimer;
-    bool m_delayedLayout;
-    WeakPtr<RenderElement> m_subtreeLayoutRoot;
-
-    LayoutPhase m_layoutPhase;
-    bool m_layoutSchedulingEnabled;
-    bool m_inPerformPostLayoutTasks { false };
-    int m_layoutCount;
-    enum class LayoutNestedState { NotInLayout, NotNested, Nested };
-    LayoutNestedState m_layoutNestedState { LayoutNestedState::NotInLayout };
-    Timer m_postLayoutTasksTimer;
     Timer m_updateEmbeddedObjectsTimer;
     bool m_firstLayoutCallbackPending;
 
-    bool m_firstLayout;
     bool m_isTransparent;
     Color m_baseBackgroundColor;
     IntSize m_lastViewportSize;
@@ -851,10 +821,6 @@ private:
     LayoutPoint m_layoutViewportOrigin;
     std::optional<LayoutRect> m_layoutViewportOverrideRect;
 
-    unsigned m_deferSetNeedsLayoutCount;
-    bool m_setNeedsLayoutWasDeferred;
-    int m_layoutDisallowedCount { 0 };
-
     RefPtr<Node> m_nodeToDraw;
     PaintBehavior m_paintBehavior;
     bool m_isPainting;
@@ -926,6 +892,8 @@ private:
 
     IntRect* m_cachedWindowClipRect { nullptr };
     Vector<WTF::Function<void ()>> m_postLayoutCallbackQueue;
+
+    LayoutContext m_layoutContext;
 };
 
 inline void FrameView::incrementVisuallyNonEmptyCharacterCount(unsigned count)
diff --git a/Source/WebCore/page/LayoutContext.cpp b/Source/WebCore/page/LayoutContext.cpp
new file mode 100644 (file)
index 0000000..16acf85
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LayoutContext.h"
+
+#include "CSSAnimationController.h"
+#include "DebugPageOverlays.h"
+#include "Document.h"
+#include "FrameView.h"
+#include "InspectorInstrumentation.h"
+#include "Logging.h"
+#include "RenderElement.h"
+#include "RenderView.h"
+#include "Settings.h"
+
+#include <wtf/SetForScope.h>
+#include <wtf/SystemTracing.h>
+
+namespace WebCore {
+
+static bool isObjectAncestorContainerOf(RenderElement& ancestor, RenderElement& descendant)
+{
+    for (auto* renderer = &descendant; renderer; renderer = renderer->container()) {
+        if (renderer == &ancestor)
+            return true;
+    }
+    return false;
+}
+
+class SubtreeLayoutStateMaintainer {
+public:
+    SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
+        : m_subtreeLayoutRoot(subtreeLayoutRoot)
+    {
+        if (m_subtreeLayoutRoot) {
+            RenderView& view = m_subtreeLayoutRoot->view();
+            view.pushLayoutState(*m_subtreeLayoutRoot);
+            if (shouldDisableLayoutStateForSubtree()) {
+                view.disableLayoutState();
+                m_didDisableLayoutState = true;
+            }
+        }
+    }
+
+    ~SubtreeLayoutStateMaintainer()
+    {
+        if (m_subtreeLayoutRoot) {
+            RenderView& view = m_subtreeLayoutRoot->view();
+            view.popLayoutState(*m_subtreeLayoutRoot);
+            if (m_didDisableLayoutState)
+                view.enableLayoutState();
+        }
+    }
+
+    bool shouldDisableLayoutStateForSubtree()
+    {
+        for (auto* renderer = m_subtreeLayoutRoot; renderer; renderer = renderer->container()) {
+            if (renderer->hasTransform() || renderer->hasReflection())
+                return true;
+        }
+        return false;
+    }
+    
+private:
+    RenderElement* m_subtreeLayoutRoot { nullptr };
+    bool m_didDisableLayoutState { false };
+};
+
+#ifndef NDEBUG
+class RenderTreeNeedsLayoutChecker {
+public :
+    RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot)
+        : m_layoutRoot(layoutRoot)
+    {
+    }
+
+    ~RenderTreeNeedsLayoutChecker()
+    {
+        auto reportNeedsLayoutError = [] (const RenderObject& renderer) {
+            WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)");
+            renderer.showRenderTreeForThis();
+            ASSERT_NOT_REACHED();
+        };
+
+        if (m_layoutRoot.needsLayout()) {
+            reportNeedsLayoutError(m_layoutRoot);
+            return;
+        }
+
+        for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) {
+            if (!descendant->needsLayout())
+                continue;
+            
+            reportNeedsLayoutError(*descendant);
+            return;
+        }
+    }
+
+private:
+    const RenderElement& m_layoutRoot;
+};
+#endif
+
+class LayoutScope {
+public:
+    LayoutScope(LayoutContext& layoutContext)
+        : m_view(layoutContext.view())
+        , m_nestedState(layoutContext.m_layoutNestedState, layoutContext.m_layoutNestedState == LayoutContext::LayoutNestedState::NotInLayout ? LayoutContext::LayoutNestedState::NotNested : LayoutContext::LayoutNestedState::Nested)
+        , m_schedulingIsEnabled(layoutContext.m_layoutSchedulingIsEnabled, false)
+        , m_inProgrammaticScroll(layoutContext.view().inProgrammaticScroll())
+    {
+        m_view.setInProgrammaticScroll(true);
+    }
+        
+    ~LayoutScope()
+    {
+        m_view.setInProgrammaticScroll(m_inProgrammaticScroll);
+    }
+        
+private:
+    FrameView& m_view;
+    SetForScope<LayoutContext::LayoutNestedState> m_nestedState;
+    SetForScope<bool> m_schedulingIsEnabled;
+    bool m_inProgrammaticScroll { false };
+};
+
+LayoutContext::LayoutContext(FrameView& frameView)
+    : m_frameView(frameView)
+    , m_layoutTimer(*this, &LayoutContext::layoutTimerFired)
+    , m_asynchronousTasksTimer(*this, &LayoutContext::runAsynchronousTasks)
+{
+}
+
+void LayoutContext::layout()
+{
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate());
+    ASSERT(!view().isPainting());
+    ASSERT(frame().view() == &view());
+    ASSERT(frame().document());
+    ASSERT(frame().document()->pageCacheState() == Document::NotInPageCache);
+    if (!canPerformLayout()) {
+        LOG(Layout, "  is not allowed, bailing");
+        return;
+    }
+
+    Ref<FrameView> protectView(view());
+    LayoutScope layoutScope(*this);
+    TraceScope tracingScope(LayoutStart, LayoutEnd);
+    InspectorInstrumentationCookie inspectorLayoutScope(InspectorInstrumentation::willLayout(view().frame()));
+    AnimationUpdateBlock animationUpdateBlock(&view().frame().animation());
+    WeakPtr<RenderElement> layoutRoot;
+    
+    m_layoutTimer.stop();
+    m_delayedLayout = false;
+    m_setNeedsLayoutWasDeferred = false;
+
+#if !LOG_DISABLED
+    if (m_firstLayout && !frame().ownerElement())
+        LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs\n", this, document()->timeSinceDocumentCreation().value());
+#endif
+#if PLATFORM(IOS)
+    if (view().updateFixedPositionLayoutRect() && subtreeLayoutRoot())
+        convertSubtreeLayoutToFullLayout();
+#endif
+    if (handleLayoutWithFrameFlatteningIfNeeded())
+        return;
+
+    {
+        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPreLayout);
+
+        // If this is a new top-level layout and there are any remaining tasks from the previous layout, finish them now.
+        if (!isLayoutNested() && m_asynchronousTasksTimer.isActive() && !view().isInChildFrameWithFrameFlattening())
+            runAsynchronousTasks();
+
+        updateStyleForLayout();
+        if (view().hasOneRef())
+            return;
+
+        view().autoSizeIfEnabled();
+        if (!renderView())
+            return;
+
+        layoutRoot = makeWeakPtr(subtreeLayoutRoot() ? subtreeLayoutRoot() : renderView());
+        m_needsFullRepaint = is<RenderView>(layoutRoot.get()) && (m_firstLayout || renderView()->printing());
+        view().willDoLayout(layoutRoot);
+        m_firstLayout = false;
+    }
+    {
+        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InRenderTreeLayout);
+    
+        SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(subtreeLayoutRoot());
+        RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
+#ifndef NDEBUG
+        RenderTreeNeedsLayoutChecker checker(*layoutRoot);
+#endif
+        layoutRoot->layout();
+        ++m_layoutCount;
+#if ENABLE(TEXT_AUTOSIZING)
+        applyTextSizingIfNeeded(*layoutRoot.get());
+#endif
+        clearSubtreeLayoutRoot();
+
+    }
+    {
+        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InViewSizeAdjust);
+        if (is<RenderView>(layoutRoot.get()) && !renderView()->printing()) {
+            // This is to protect m_needsFullRepaint's value when layout() is getting re-entered through adjustViewSize().
+            SetForScope<bool> needsFullRepaint(m_needsFullRepaint);
+            view().adjustViewSize();
+            // FIXME: Firing media query callbacks synchronously on nested frames could produced a detached FrameView here by
+            // navigating away from the current document (see webkit.org/b/173329).
+            if (view().hasOneRef())
+                return;
+        }
+    }
+    {
+        SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPostLayout);
+        if (m_needsFullRepaint)
+            renderView()->repaintRootContents();
+        ASSERT(!layoutRoot->needsLayout());
+        view().didLayout(layoutRoot);
+        runOrScheduleAsynchronousTasks();
+    }
+    InspectorInstrumentation::didLayout(inspectorLayoutScope, *layoutRoot);
+    DebugPageOverlays::didLayout(view().frame());
+}
+
+void LayoutContext::runOrScheduleAsynchronousTasks()
+{
+    if (m_asynchronousTasksTimer.isActive())
+        return;
+
+    if (view().isInChildFrameWithFrameFlattening()) {
+        // While flattening frames, we defer post layout tasks to avoid getting stuck in a cycle,
+        // except updateWidgetPositions() which is required to kick off subframe layout in certain cases.
+        if (!m_inAsynchronousTasks)
+            view().updateWidgetPositions();
+        m_asynchronousTasksTimer.startOneShot(0_s);
+        return;
+    }
+
+    // If we are already in performPostLayoutTasks(), defer post layout tasks until after we return
+    // to avoid re-entrancy.
+    if (m_inAsynchronousTasks) {
+        m_asynchronousTasksTimer.startOneShot(0_s);
+        return;
+    }
+
+    runAsynchronousTasks();
+    if (needsLayout()) {
+        // If runAsynchronousTasks() made us layout again, let's defer the tasks until after we return.
+        m_asynchronousTasksTimer.startOneShot(0_s);
+        layout();
+    }
+}
+
+void LayoutContext::runAsynchronousTasks()
+{
+    m_asynchronousTasksTimer.stop();
+    if (m_inAsynchronousTasks)
+        return;
+    SetForScope<bool> inAsynchronousTasks(m_inAsynchronousTasks, true);
+    view().performPostLayoutTasks();
+}
+
+void LayoutContext::flushAsynchronousTasks()
+{
+    if (!m_asynchronousTasksTimer.isActive())
+        return;
+    runAsynchronousTasks();
+}
+
+void LayoutContext::reset()
+{
+    m_layoutPhase = LayoutPhase::OutsideLayout;
+    clearSubtreeLayoutRoot();
+    m_layoutCount = 0;
+    m_layoutSchedulingIsEnabled = true;
+    m_delayedLayout = false;
+    m_layoutTimer.stop();
+    m_firstLayout = true;
+    m_asynchronousTasksTimer.stop();
+    m_needsFullRepaint = true;
+}
+
+bool LayoutContext::needsLayout() const
+{
+    // This can return true in cases where the document does not have a body yet.
+    // Document::shouldScheduleLayout takes care of preventing us from scheduling
+    // layout in that case.
+    auto* renderView = this->renderView();
+    return isLayoutPending()
+        || (renderView && renderView->needsLayout())
+        || subtreeLayoutRoot()
+        || (m_disableSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred);
+}
+
+void LayoutContext::setNeedsLayout()
+{
+    if (m_disableSetNeedsLayoutCount) {
+        m_setNeedsLayoutWasDeferred = true;
+        return;
+    }
+
+    if (auto* renderView = this->renderView()) {
+        ASSERT(!renderView->inHitTesting());
+        renderView->setNeedsLayout();
+    }
+}
+
+void LayoutContext::enableSetNeedsLayout()
+{
+    ASSERT(m_disableSetNeedsLayoutCount);
+    if (!--m_disableSetNeedsLayoutCount)
+        m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen.
+}
+
+void LayoutContext::disableSetNeedsLayout()
+{
+    ++m_disableSetNeedsLayoutCount;
+}
+
+void LayoutContext::scheduleLayout()
+{
+    // FIXME: We should assert the page is not in the page cache, but that is causing
+    // too many false assertions. See <rdar://problem/7218118>.
+    ASSERT(frame().view() == &view());
+
+    if (subtreeLayoutRoot())
+        convertSubtreeLayoutToFullLayout();
+    if (!isLayoutSchedulingEnabled())
+        return;
+    if (!needsLayout())
+        return;
+    if (!frame().document()->shouldScheduleLayout())
+        return;
+    InspectorInstrumentation::didInvalidateLayout(frame());
+    // When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames.
+    // Also invalidate parent frame starting from the owner element of this frame.
+    if (frame().ownerRenderer() && view().isInChildFrameWithFrameFlattening())
+        frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain);
+
+    Seconds delay = frame().document()->minimumLayoutDelay();
+    if (m_layoutTimer.isActive() && m_delayedLayout && !delay)
+        unscheduleLayout();
+
+    if (m_layoutTimer.isActive())
+        return;
+
+    m_delayedLayout = delay.value();
+
+#if !LOG_DISABLED
+    if (!frame().document()->ownerElement())
+        LOG(Layout, "FrameView %p scheduling layout for %.3fs", this, delay.value());
+#endif
+
+    m_layoutTimer.startOneShot(delay);
+}
+
+void LayoutContext::unscheduleLayout()
+{
+    if (m_asynchronousTasksTimer.isActive())
+        m_asynchronousTasksTimer.stop();
+
+    if (!m_layoutTimer.isActive())
+        return;
+
+#if !LOG_DISABLED
+    if (!frame().document()->ownerElement())
+        LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
+#endif
+
+    m_layoutTimer.stop();
+    m_delayedLayout = false;
+}
+
+void LayoutContext::scheduleSubtreeLayout(RenderElement& layoutRoot)
+{
+    ASSERT(renderView());
+    auto& renderView = *this->renderView();
+
+    // Try to catch unnecessary work during render tree teardown.
+    ASSERT(!renderView.renderTreeBeingDestroyed());
+    ASSERT(frame().view() == &view());
+
+    if (renderView.needsLayout() && !subtreeLayoutRoot()) {
+        layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
+        return;
+    }
+
+    if (!isLayoutPending() && isLayoutSchedulingEnabled()) {
+        Seconds delay = renderView.document().minimumLayoutDelay();
+        ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
+        setSubtreeLayoutRoot(layoutRoot);
+        InspectorInstrumentation::didInvalidateLayout(frame());
+        m_delayedLayout = delay.value();
+        m_layoutTimer.startOneShot(delay);
+        return;
+    }
+
+    auto* subtreeLayoutRoot = this->subtreeLayoutRoot();
+    if (subtreeLayoutRoot == &layoutRoot)
+        return;
+
+    if (!subtreeLayoutRoot) {
+        // We already have a pending (full) layout. Just mark the subtree for layout.
+        layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
+        InspectorInstrumentation::didInvalidateLayout(frame());
+        return;
+    }
+
+    if (isObjectAncestorContainerOf(*subtreeLayoutRoot, layoutRoot)) {
+        // Keep the current root.
+        layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, subtreeLayoutRoot);
+        ASSERT(!subtreeLayoutRoot->container() || is<RenderView>(subtreeLayoutRoot->container()) || !subtreeLayoutRoot->container()->needsLayout());
+        return;
+    }
+
+    if (isObjectAncestorContainerOf(layoutRoot, *subtreeLayoutRoot)) {
+        // Re-root at newRelayoutRoot.
+        subtreeLayoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &layoutRoot);
+        setSubtreeLayoutRoot(layoutRoot);
+        ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
+        InspectorInstrumentation::didInvalidateLayout(frame());
+        return;
+    }
+    // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead.
+    convertSubtreeLayoutToFullLayout();
+    layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
+    InspectorInstrumentation::didInvalidateLayout(frame());
+}
+
+void LayoutContext::layoutTimerFired()
+{
+#if !LOG_DISABLED
+    if (!frame().document()->ownerElement())
+        LOG(Layout, "FrameView %p layout timer fired at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
+#endif
+    layout();
+}
+
+void LayoutContext::convertSubtreeLayoutToFullLayout()
+{
+    ASSERT(subtreeLayoutRoot());
+    subtreeLayoutRoot()->markContainingBlocksForLayout(ScheduleRelayout::No);
+    clearSubtreeLayoutRoot();
+}
+
+void LayoutContext::setSubtreeLayoutRoot(RenderElement& layoutRoot)
+{
+    m_subtreeLayoutRoot = makeWeakPtr(layoutRoot);
+}
+
+bool LayoutContext::canPerformLayout() const
+{
+    if (isInRenderTreeLayout())
+        return false;
+
+    if (layoutDisallowed())
+        return false;
+
+    if (view().isPainting())
+        return false;
+
+    if (!subtreeLayoutRoot() && !frame().document()->renderView())
+        return false;
+
+    return true;
+}
+
+#if ENABLE(TEXT_AUTOSIZING)
+void LayoutContext::applyTextSizingIfNeeded(RenderElement& layoutRoot)
+{
+    auto& settings = layoutRoot.settings();
+    if (!settings.textAutosizingEnabled() || renderView()->printing())
+        return;
+    auto minimumZoomFontSize = settings.minimumZoomFontSize();
+    if (!minimumZoomFontSize)
+        return;
+    auto textAutosizingWidth = layoutRoot.page().textAutosizingWidth();
+    if (auto overrideWidth = settings.textAutosizingWindowSizeOverride().width())
+        textAutosizingWidth = overrideWidth;
+    if (!textAutosizingWidth)
+        return;
+    layoutRoot.adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth);
+    if (!layoutRoot.needsLayout())
+        return;
+    LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth);
+    layoutRoot.layout();
+}
+#endif
+
+void LayoutContext::updateStyleForLayout()
+{
+    Document& document = *frame().document();
+    // Viewport-dependent media queries may cause us to need completely different style information.
+    auto* styleResolver = document.styleScope().resolverIfExists();
+    if (!styleResolver || styleResolver->hasMediaQueriesAffectedByViewportChange()) {
+        LOG(Layout, "  hasMediaQueriesAffectedByViewportChange, enqueueing style recalc");
+        document.styleScope().didChangeStyleSheetEnvironment();
+        // FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds.
+        InspectorInstrumentation::mediaQueryResultChanged(document);
+    }
+    document.evaluateMediaQueryList();
+    // If there is any pagination to apply, it will affect the RenderView's style, so we should
+    // take care of that now.
+    view().applyPaginationToViewport();
+    // Always ensure our style info is up-to-date. This can happen in situations where
+    // the layout beats any sort of style recalc update that needs to occur.
+    document.updateStyleIfNeeded();
+}
+
+bool LayoutContext::handleLayoutWithFrameFlatteningIfNeeded()
+{
+    if (!view().isInChildFrameWithFrameFlattening())
+        return false;
+    
+    if (!view().frameFlatteningViewSizeForMediaQueryIsSet()) {
+        LOG_WITH_STREAM(MediaQueries, stream << "FrameView " << this << " snapshotting size " <<  view().layoutSize() << " for media queries");
+        view().setFrameFlatteningViewSizeForMediaQuery();
+    }
+    startLayoutAtMainFrameViewIfNeeded();
+    auto* layoutRoot = subtreeLayoutRoot() ? subtreeLayoutRoot() : frame().document()->renderView();
+    return !layoutRoot || !layoutRoot->needsLayout();
+}
+
+void LayoutContext::startLayoutAtMainFrameViewIfNeeded()
+{
+    // When we start a layout at the child level as opposed to the topmost frame view and this child
+    // frame requires flattening, we need to re-initiate the layout at the topmost view. Layout
+    // will hit this view eventually.
+    auto* parentView = view().parentFrameView();
+    if (!parentView)
+        return;
+
+    // In the middle of parent layout, no need to restart from topmost.
+    if (parentView->layoutContext().isInLayout())
+        return;
+
+    // Parent tree is clean. Starting layout from it would have no effect.
+    if (!parentView->needsLayout())
+        return;
+
+    while (parentView->parentFrameView())
+        parentView = parentView->parentFrameView();
+
+    LOG(Layout, "  frame flattening, starting from root");
+    parentView->layoutContext().layout();
+}
+
+Frame& LayoutContext::frame() const
+{
+    return view().frame();
+}
+
+FrameView& LayoutContext::view() const
+{
+    return m_frameView;
+}
+
+RenderView* LayoutContext::renderView() const
+{
+    return view().renderView();
+}
+
+Document* LayoutContext::document() const
+{
+    return frame().document();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/LayoutContext.h b/Source/WebCore/page/LayoutContext.h
new file mode 100644 (file)
index 0000000..1458939
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Timer.h"
+
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+
+class Document;
+class Frame;
+class FrameView;
+class LayoutScope;
+class RenderElement;
+class RenderView;
+    
+class LayoutContext {
+public:
+    LayoutContext(FrameView&);
+
+    void layout();
+
+    void setNeedsLayout();
+    bool needsLayout() const;
+
+    void scheduleLayout();
+    void scheduleSubtreeLayout(RenderElement& layoutRoot);
+    void unscheduleLayout();
+
+    void startDisallowingLayout() { ++m_layoutDisallowedCount; }
+    void endDisallowingLayout() { ASSERT(m_layoutDisallowedCount > 0); --m_layoutDisallowedCount; }
+    
+    void disableSetNeedsLayout();
+    void enableSetNeedsLayout();
+
+    enum class LayoutPhase {
+        OutsideLayout,
+        InPreLayout,
+        InRenderTreeLayout,
+        InViewSizeAdjust,
+        InPostLayout
+    };
+    LayoutPhase layoutPhase() const { return m_layoutPhase; }
+    bool isLayoutNested() const { return m_layoutNestedState == LayoutNestedState::Nested; }
+    bool isLayoutPending() const { return m_layoutTimer.isActive(); }
+    bool isInLayout() const { return layoutPhase() != LayoutPhase::OutsideLayout; }
+    bool isInRenderTreeLayout() const { return layoutPhase() == LayoutPhase::InRenderTreeLayout; }
+    bool inPaintableState() const { return layoutPhase() != LayoutPhase::InRenderTreeLayout && layoutPhase() != LayoutPhase::InViewSizeAdjust && (layoutPhase() != LayoutPhase::InPostLayout || inAsynchronousTasks()); }
+
+    unsigned layoutCount() const { return m_layoutCount; }
+
+    RenderElement* subtreeLayoutRoot() const { return m_subtreeLayoutRoot.get(); }
+    void clearSubtreeLayoutRoot() { m_subtreeLayoutRoot.clear(); }
+    void convertSubtreeLayoutToFullLayout();
+
+    void reset();
+    void resetFirstLayoutFlag() { m_firstLayout = true; }
+    bool didFirstLayout() const { return !m_firstLayout; }
+
+    void setNeedsFullRepaint() { m_needsFullRepaint = true; }
+    bool needsFullRepaint() const { return m_needsFullRepaint; }
+
+    void flushAsynchronousTasks();
+
+private:
+    friend class LayoutScope;
+
+    bool canPerformLayout() const;
+    bool layoutDisallowed() const { return m_layoutDisallowedCount; }
+    bool isLayoutSchedulingEnabled() const { return m_layoutSchedulingIsEnabled; }
+
+    void layoutTimerFired();
+    void runAsynchronousTasks();
+    void runOrScheduleAsynchronousTasks();
+    bool inAsynchronousTasks() const { return m_inAsynchronousTasks; }
+
+    void setSubtreeLayoutRoot(RenderElement&);
+
+#if ENABLE(TEXT_AUTOSIZING)
+    void applyTextSizingIfNeeded(RenderElement& layoutRoot);
+#endif
+    void updateStyleForLayout();
+
+    bool handleLayoutWithFrameFlatteningIfNeeded();
+    void startLayoutAtMainFrameViewIfNeeded();
+
+    Frame& frame() const;
+    FrameView& view() const;
+    RenderView* renderView() const;
+    Document* document() const;
+
+    FrameView& m_frameView;
+    Timer m_layoutTimer;
+    Timer m_asynchronousTasksTimer;
+
+    bool m_layoutSchedulingIsEnabled { true };
+    bool m_delayedLayout { false };
+    bool m_firstLayout { true };
+    bool m_needsFullRepaint { true };
+    bool m_inAsynchronousTasks { false };
+    bool m_setNeedsLayoutWasDeferred { false };
+    LayoutPhase m_layoutPhase { LayoutPhase::OutsideLayout };
+    enum class LayoutNestedState { NotInLayout, NotNested, Nested };
+    LayoutNestedState m_layoutNestedState { LayoutNestedState::NotInLayout };
+    unsigned m_layoutCount { 0 };
+    unsigned m_disableSetNeedsLayoutCount { 0 };
+    int m_layoutDisallowedCount { 0 };
+    WeakPtr<RenderElement> m_subtreeLayoutRoot;
+};
+
+} // namespace WebCore
index 5b97093..ca369be 100644 (file)
@@ -843,7 +843,7 @@ void Page::setPageScaleFactor(float scale, const IntPoint& origin, bool inStable
 
     if (view && view->scrollPosition() != origin) {
         if (!m_settings->delegatesPageScaling() && document->renderView() && document->renderView()->needsLayout() && view->didFirstLayout())
-            view->layout();
+            view->layoutContext().layout();
 
         if (!view->delegatesScrolling())
             view->setScrollPosition(origin);
index a34fb37..47ee5d8 100644 (file)
@@ -2213,7 +2213,7 @@ void RenderBlock::insertPositionedObject(RenderBox& positioned)
     // FIXME: Find out if we can do this as part of positioned.setChildNeedsLayout(MarkOnlyThis)
     if (positioned.needsLayout()) {
         // We should turn this bit on only while in layout.
-        ASSERT(posChildNeedsLayout() || view().frameView().isInLayout());
+        ASSERT(posChildNeedsLayout() || view().frameView().layoutContext().isInLayout());
         setPosChildNeedsLayoutBit(true);
     }
     positionedDescendantsMap().addDescendant(*this, positioned, isRenderView() ? PositionedDescendantsMap::MoveDescendantToEnd::Yes
index 12e347a..5d14f00 100644 (file)
@@ -1624,7 +1624,7 @@ void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
     }
 
     ShapeValue* shapeOutsideValue = style().shapeOutside();
-    if (!view().frameView().isInRenderTreeLayout() && isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) {
+    if (!view().frameView().layoutContext().isInRenderTreeLayout() && isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) {
         ShapeOutsideInfo::ensureInfo(*this).markShapeAsDirty();
         markShapeOutsideDependentsForLayout();
     }
@@ -2296,7 +2296,7 @@ void RenderBox::computeLogicalWidthInFragment(LogicalExtentComputedValues& compu
     }
 
     // If layout is limited to a subtree, the subtree root's logical width does not change.
-    if (element() && !view().frameView().layoutPending() && view().frameView().subtreeLayoutRoot() == this)
+    if (element() && !view().frameView().layoutContext().isLayoutPending() && view().frameView().layoutContext().subtreeLayoutRoot() == this)
         return;
 
     // The parent box is flexing us, so it has increased or decreased our
index 45a5b47..dd9cc1b 100644 (file)
@@ -1101,7 +1101,7 @@ inline void RenderElement::clearSubtreeLayoutRootIfNeeded() const
     if (renderTreeBeingDestroyed())
         return;
 
-    if (view().frameView().subtreeLayoutRoot() != this)
+    if (view().frameView().layoutContext().subtreeLayoutRoot() != this)
         return;
 
     // Normally when a renderer is detached from the tree, the appropriate dirty bits get set
@@ -1111,7 +1111,7 @@ inline void RenderElement::clearSubtreeLayoutRootIfNeeded() const
     // This indicates a failure to layout the child, which is why
     // the layout root is still set to |this|. Make sure to clear it
     // since we are getting destroyed.
-    view().frameView().clearSubtreeLayoutRoot();
+    view().frameView().layoutContext().clearSubtreeLayoutRoot();
 }
 
 void RenderElement::willBeDestroyed()
@@ -2171,7 +2171,7 @@ bool RenderElement::hasSelfPaintingLayer() const
 
 bool RenderElement::checkForRepaintDuringLayout() const
 {
-    if (document().view()->needsFullRepaint() || !everHadLayout() || hasSelfPaintingLayer())
+    if (document().view()->layoutContext().needsFullRepaint() || !everHadLayout() || hasSelfPaintingLayer())
         return false;
     return !settings().repaintOutsideLayoutEnabled();
 }
index 614edfb..f1e802b 100644 (file)
@@ -76,7 +76,7 @@ void RenderFrameBase::performLayoutWithFlattening(bool hasFixedWidth, bool hasFi
     if (!shouldExpandFrame(width(), height(), hasFixedWidth, hasFixedHeight)) {
         if (updateWidgetPosition() == ChildWidgetState::Destroyed)
             return;
-        childView()->layout();
+        childView()->layoutContext().layout();
         return;
     }
 
@@ -100,7 +100,7 @@ void RenderFrameBase::performLayoutWithFlattening(bool hasFixedWidth, bool hasFi
         // update again to pass the new width to the child frame
         if (updateWidgetPosition() == ChildWidgetState::Destroyed)
             return;
-        childView()->layout();
+        childView()->layoutContext().layout();
     }
 
     ASSERT(childView());
@@ -113,7 +113,7 @@ void RenderFrameBase::performLayoutWithFlattening(bool hasFixedWidth, bool hasFi
     if (updateWidgetPosition() == ChildWidgetState::Destroyed)
         return;
 
-    ASSERT(!childView()->layoutPending());
+    ASSERT(!childView()->layoutContext().isLayoutPending());
     ASSERT(!childRenderView()->needsLayout());
     ASSERT(!childRenderView()->firstChild() || !childRenderView()->firstChild()->firstChildSlow() || !childRenderView()->firstChild()->firstChildSlow()->needsLayout());
 }
index 339c453..458747c 100644 (file)
@@ -2393,7 +2393,7 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
 
     // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll).
     // We don't update compositing layers, because we need to do a deep update from the compositing ancestor.
-    if (!view.frameView().isInRenderTreeLayout()) {
+    if (!view.frameView().layoutContext().isInRenderTreeLayout()) {
         // If we're in the middle of layout, we'll just update layers once layout has finished.
         updateLayerPositionsAfterOverflowScroll();
         // Update regions, scrolling may change the clip of a particular region.
index 5016a06..5819680 100644 (file)
@@ -482,12 +482,12 @@ void RenderObject::clearNeedsLayout()
 static void scheduleRelayoutForSubtree(RenderElement& renderer)
 {
     if (is<RenderView>(renderer)) {
-        downcast<RenderView>(renderer).frameView().scheduleRelayout();
+        downcast<RenderView>(renderer).frameView().layoutContext().scheduleLayout();
         return;
     }
 
     if (renderer.isRooted())
-        renderer.view().frameView().scheduleRelayoutOfSubtree(renderer);
+        renderer.view().frameView().layoutContext().scheduleSubtreeLayout(renderer);
 }
 
 void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayout, RenderElement* newRoot)
index 8caa7ac..56beb90 100644 (file)
@@ -593,7 +593,7 @@ void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavi
             FrameView& view = downcast<FrameView>(*widget);
             if (RenderView* root = view.frame().contentRenderer()) {
                 if (!(behavior & RenderAsTextDontUpdateLayout))
-                    view.layout();
+                    view.layoutContext().layout();
                 if (RenderLayer* layer = root->layer())
                     writeLayers(ts, *layer, *layer, layer->rect(), indent + 1, behavior);
             }
index 8a81320..fbf125c 100644 (file)
@@ -233,7 +233,7 @@ void RenderVideo::updatePlayer()
 
     bool intrinsicSizeChanged;
     intrinsicSizeChanged = updateIntrinsicSize();
-    ASSERT_UNUSED(intrinsicSizeChanged, !intrinsicSizeChanged || !view().frameView().isInRenderTreeLayout());
+    ASSERT_UNUSED(intrinsicSizeChanged, !intrinsicSizeChanged || !view().frameView().layoutContext().isInRenderTreeLayout());
 
     auto mediaPlayer = videoElement().player();
     if (!mediaPlayer)
index 960ab56..4df8212 100644 (file)
@@ -117,7 +117,7 @@ public:
     }
 #endif
 
-    bool doingFullRepaint() const { return frameView().needsFullRepaint(); }
+    bool doingFullRepaint() const { return frameView().layoutContext().needsFullRepaint(); }
 
     // Subtree push/pop
     void pushLayoutState(RenderObject&);
index 6e0a761..dc9c530 100644 (file)
@@ -333,7 +333,7 @@ RenderWidget::ChildWidgetState RenderWidget::updateWidgetPosition()
         FrameView& frameView = downcast<FrameView>(*m_widget);
         // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
         if ((widgetSizeChanged || frameView.needsLayout()) && frameView.frame().page() && frameView.frame().document())
-            frameView.layout();
+            frameView.layoutContext().layout();
     }
     return ChildWidgetState::Valid;
 }
index 1345f9e..e25d564 100644 (file)
@@ -311,7 +311,7 @@ ImageDrawResult SVGImage::draw(GraphicsContext& context, const FloatRect& dstRec
     view->resize(containerSize());
 
     if (view->needsLayout())
-        view->layout();
+        view->layoutContext().layout();
 
     view->paint(context, intersection(context.clipBounds(), enclosingIntRect(srcRect)));
 
index 6af3aac..3cd5020 100644 (file)
@@ -2829,7 +2829,7 @@ unsigned Internals::layoutCount() const
     Document* document = contextDocument();
     if (!document || !document->view())
         return 0;
-    return document->view()->layoutCount();
+    return document->view()->layoutContext().layoutCount();
 }
 
 #if !PLATFORM(IOS)
index 541a8d8..373b4cc 100644 (file)
@@ -1,3 +1,18 @@
+2017-10-28  Zalan Bujtas  <zalan@apple.com>
+
+        [FrameView::layout cleanup] Move core layout logic to a separate class.
+        https://bugs.webkit.org/show_bug.cgi?id=178771
+        <rdar://problem/35166542>
+
+        Reviewed by Simon Fraser.
+
+        Move layout code out from FrameView to LayoutContext.
+
+        * WebView/WebClipView.mm:
+        (-[WebClipView _immediateScrollToPoint:]):
+        * WebView/WebFrame.mm:
+        (-[WebFrame layoutCount]):
+
 2017-10-26  Simon Fraser  <simon.fraser@apple.com>
 
         Fix issues with WebView subframe painting
index 5976b5e..5fd5c14 100644 (file)
@@ -119,7 +119,7 @@ using namespace WebCore;
     if ([webFrameView isKindOfClass:[WebFrameView class]]) {
         if (Frame* coreFrame = core([webFrameView webFrame])) {
             if (FrameView* frameView = coreFrame->view()) {
-                if (!frameView->inPaintableState())
+                if (!frameView->layoutContext().inPaintableState())
                     [self setNeedsDisplay:YES];
             }
         }
index a977541..bee2a55 100644 (file)
@@ -1441,7 +1441,7 @@ static WebFrameLoadType toWebFrameLoadType(FrameLoadType frameLoadType)
     WebCore::Frame *frame = core(self);
     if (!frame || !frame->view())
         return 0;
-    return frame->view()->layoutCount();
+    return frame->view()->layoutContext().layoutCount();
 }
 
 - (BOOL)isTelephoneNumberParsingAllowed
index fba74ad..4749157 100644 (file)
@@ -1,3 +1,16 @@
+2017-10-28  Zalan Bujtas  <zalan@apple.com>
+
+        [FrameView::layout cleanup] Move core layout logic to a separate class.
+        https://bugs.webkit.org/show_bug.cgi?id=178771
+        <rdar://problem/35166542>
+
+        Reviewed by Simon Fraser.
+
+        Move layout code out from FrameView to LayoutContext.
+
+        * WebFrame.cpp:
+        (WebFrame::layout):
+
 2017-10-26  Alex Christensen  <achristensen@webkit.org>
 
         Fix Windows build
index 619f33a..ff7bbf5 100644 (file)
@@ -916,7 +916,7 @@ HRESULT WebFrame::layout()
     if (!view)
         return E_FAIL;
 
-    view->layout();
+    view->layoutContext().layout();
     return S_OK;
 }