blob: a512e5c58fccc31ae2bb337dd518e84b8292e061 [file] [log] [blame]
/*
* 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 "LocalFrameViewLayoutContext.h"
#include "DebugPageOverlays.h"
#include "Document.h"
#include "DocumentEnums.h"
#include "InspectorInstrumentation.h"
#include "LayoutBoxGeometry.h"
#include "LayoutContext.h"
#include "LayoutDisallowedScope.h"
#include "LayoutIntegrationLineLayout.h"
#include "LayoutState.h"
#include "LayoutTreeBuilder.h"
#include "LocalFrameView.h"
#include "Logging.h"
#include "Quirks.h"
#include "RenderBoxInlines.h"
#include "RenderDescendantIterator.h"
#include "RenderElement.h"
#include "RenderElementInlines.h"
#include "RenderLayerCompositor.h"
#include "RenderLayoutState.h"
#include "RenderObjectInlines.h"
#include "RenderStyle.h"
#include "RenderStyleInlines.h"
#include "RenderView.h"
#include "ScriptDisallowedScope.h"
#include "Settings.h"
#include "StyleScope.h"
#include <wtf/SetForScope.h>
#include <wtf/SystemTracing.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(LocalFrameViewLayoutContext);
UpdateScrollInfoAfterLayoutTransaction::UpdateScrollInfoAfterLayoutTransaction() = default;
UpdateScrollInfoAfterLayoutTransaction::~UpdateScrollInfoAfterLayoutTransaction() = default;
static bool isObjectAncestorContainerOf(RenderElement& ancestor, RenderElement& descendant)
{
for (auto* renderer = &descendant; renderer; renderer = renderer->container()) {
if (renderer == &ancestor)
return true;
}
return false;
}
#ifndef NDEBUG
class RenderTreeNeedsLayoutChecker {
public :
RenderTreeNeedsLayoutChecker(const RenderView& renderView)
: m_renderView(renderView)
{
}
~RenderTreeNeedsLayoutChecker()
{
auto checkAndReportNeedsLayoutError = [] (const RenderObject& renderer) {
auto needsLayout = [&] {
if (renderer.needsLayout())
return true;
if (auto* renderBlockFlow = dynamicDowncast<RenderBlockFlow>(renderer); renderBlockFlow && renderBlockFlow->inlineLayout())
return renderBlockFlow->inlineLayout()->hasDetachedContent();
return false;
};
if (needsLayout()) {
WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)");
renderer.showRenderTreeForThis();
ASSERT_NOT_REACHED();
}
};
checkAndReportNeedsLayoutError(m_renderView);
for (auto* descendant = m_renderView.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_renderView))
checkAndReportNeedsLayoutError(*descendant);
}
private:
const RenderView& m_renderView;
};
#endif
class LayoutFrameScope {
public:
LayoutFrameScope(LocalFrameViewLayoutContext& layoutContext)
: m_view(layoutContext.view())
, m_nestedState(layoutContext.m_layoutNestedState, layoutContext.m_layoutNestedState == LocalFrameViewLayoutContext::LayoutNestedState::NotInLayout ? LocalFrameViewLayoutContext::LayoutNestedState::NotNested : LocalFrameViewLayoutContext::LayoutNestedState::Nested)
, m_schedulingIsEnabled(layoutContext.m_layoutSchedulingIsEnabled, false)
, m_previousScrollType(layoutContext.view().currentScrollType())
{
m_view->setCurrentScrollType(ScrollType::Programmatic);
}
~LayoutFrameScope()
{
m_view->setCurrentScrollType(m_previousScrollType);
}
private:
const Ref<LocalFrameView> m_view;
SetForScope<LocalFrameViewLayoutContext::LayoutNestedState> m_nestedState;
SetForScope<bool> m_schedulingIsEnabled;
ScrollType m_previousScrollType;
};
LocalFrameViewLayoutContext::LocalFrameViewLayoutContext(LocalFrameView& frameView)
: m_frameView(frameView)
, m_layoutTimer(*this, &LocalFrameViewLayoutContext::layoutTimerFired)
, m_postLayoutTaskTimer(*this, &LocalFrameViewLayoutContext::runPostLayoutTasks)
{
}
LocalFrameViewLayoutContext::~LocalFrameViewLayoutContext() = default;
UpdateScrollInfoAfterLayoutTransaction& LocalFrameViewLayoutContext::updateScrollInfoAfterLayoutTransaction()
{
if (!m_updateScrollInfoAfterLayoutTransaction)
m_updateScrollInfoAfterLayoutTransaction = makeUnique<UpdateScrollInfoAfterLayoutTransaction>();
return *m_updateScrollInfoAfterLayoutTransaction;
}
void LocalFrameViewLayoutContext::layout(bool canDeferUpdateLayerPositions)
{
LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << &view() << " LocalFrameViewLayoutContext::layout() with size " << view().layoutSize());
Ref protectedView(view());
performLayout(canDeferUpdateLayerPositions);
if (view().hasOneRef())
return;
Style::Scope::LayoutDependencyUpdateContext layoutDependencyUpdateContext;
while (document() && document()->styleScope().invalidateForLayoutDependencies(layoutDependencyUpdateContext)) {
protectedDocument()->updateStyleIfNeeded();
if (!needsLayout())
break;
performLayout(canDeferUpdateLayerPositions);
if (view().hasOneRef())
return;
}
}
void LocalFrameViewLayoutContext::interleavedLayout()
{
LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << &view() << " LocalFrameViewLayoutContext::interleavedLayout() with size " << view().layoutSize());
Ref protectedView(view());
performLayout(false);
Style::Scope::LayoutDependencyUpdateContext layoutDependencyUpdateContext;
document()->styleScope().invalidateForLayoutDependencies(layoutDependencyUpdateContext);
}
void LocalFrameViewLayoutContext::performLayout(bool canDeferUpdateLayerPositions)
{
Ref frame = this->frame();
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!document()->inRenderTreeUpdate());
ASSERT(LayoutDisallowedScope::isLayoutAllowed());
ASSERT(!view().isPainting());
ASSERT(frame->view() == &view());
ASSERT(document());
ASSERT(document()->backForwardCacheState() == Document::NotInBackForwardCache
|| document()->backForwardCacheState() == Document::AboutToEnterBackForwardCache);
if (!canPerformLayout()) {
LOG(Layout, " is not allowed, bailing");
return;
}
LayoutFrameScope layoutFrameScope(*this);
TraceScope tracingScope(PerformLayoutStart, PerformLayoutEnd);
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
InspectorInstrumentation::willLayout(frame);
SingleThreadWeakPtr<RenderElement> layoutRoot;
m_layoutTimer.stop();
m_setNeedsLayoutWasDeferred = false;
#if !LOG_DISABLED
if (m_firstLayout && !frame->ownerElement())
LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << &view() << " elapsed time before first layout: " << document()->timeSinceDocumentCreation());
#endif
#if PLATFORM(IOS_FAMILY)
if (protectedView()->updateFixedPositionLayoutRect() && subtreeLayoutRoot())
convertSubtreeLayoutToFullLayout();
#endif
{
SetForScope layoutPhase(m_layoutPhase, LayoutPhase::InPreLayout);
if (!protectedDocument()->isInStyleInterleavedLayoutForSelfOrAncestor()) {
// If this is a new top-level layout and there are any remaining tasks from the previous layout, finish them now.
if (!isLayoutNested() && m_postLayoutTaskTimer.isActive())
runPostLayoutTasks();
updateStyleForLayout();
}
if (view().hasOneRef())
return;
protectedView()->autoSizeIfEnabled();
if (!renderView())
return;
layoutRoot = subtreeLayoutRoot() ? subtreeLayoutRoot() : renderView();
m_needsFullRepaint = is<RenderView>(layoutRoot) && (m_firstLayout || renderView()->printing());
LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << &view() << " layout " << m_layoutUpdateCount << " - subtree root " << subtreeLayoutRoot() << ", needsFullRepaint " << m_needsFullRepaint);
protectedView()->willDoLayout(layoutRoot);
m_firstLayout = false;
}
Vector<FloatQuad> layoutAreas;
{
TraceScope tracingScope(RenderTreeLayoutStart, RenderTreeLayoutEnd);
SetForScope layoutPhase(m_layoutPhase, LayoutPhase::InRenderTreeLayout);
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(subtreeLayoutRoot());
RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
#ifndef NDEBUG
RenderTreeNeedsLayoutChecker checker(*renderView());
#endif
layoutRoot->layout();
#if ENABLE(TEXT_AUTOSIZING)
applyTextSizingIfNeeded(*layoutRoot.get());
#endif
layoutRoot->absoluteQuads(layoutAreas);
clearSubtreeLayoutRoot();
ASSERT(m_percentHeightIgnoreList.isEmptyIgnoringNullReferences());
#if !LOG_DISABLED && ENABLE(TREE_DEBUGGING)
auto layoutLogEnabled = [] {
return LogLayout.state == WTFLogChannelState::On;
};
if (layoutLogEnabled())
showRenderTree(renderView());
#endif
}
{
SetForScope layoutPhase(m_layoutPhase, LayoutPhase::InViewSizeAdjust);
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
if (is<RenderView>(layoutRoot) && !renderView()->printing()) {
// This is to protect m_needsFullRepaint's value when layout() is getting re-entered through adjustViewSize().
SetForScope needsFullRepaint(m_needsFullRepaint);
protectedView()->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(m_layoutPhase, LayoutPhase::InPostLayout);
if (m_needsFullRepaint)
renderView()->repaintRootContents();
ASSERT(!layoutRoot->needsLayout());
protectedView()->didLayout(layoutRoot, canDeferUpdateLayerPositions);
runOrScheduleAsynchronousTasks(canDeferUpdateLayerPositions);
}
InspectorInstrumentation::didLayout(frame, layoutAreas);
DebugPageOverlays::didLayout(frame);
}
void LocalFrameViewLayoutContext::runOrScheduleAsynchronousTasks(bool canDeferUpdateLayerPositions)
{
if (m_postLayoutTaskTimer.isActive())
return;
if (document()->isInStyleInterleavedLayout()) {
// We are doing layout from style resolution to resolve container queries and/or anchor-positioned elements.
m_postLayoutTaskTimer.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_postLayoutTaskTimer.startOneShot(0_s);
return;
}
runPostLayoutTasks();
if (needsLayoutInternal()) {
// If runPostLayoutTasks() made us layout again, let's defer the tasks until after we return.
m_postLayoutTaskTimer.startOneShot(0_s);
layout(canDeferUpdateLayerPositions);
}
}
void LocalFrameViewLayoutContext::runPostLayoutTasks()
{
m_postLayoutTaskTimer.stop();
if (m_inAsynchronousTasks)
return;
SetForScope inAsynchronousTasks(m_inAsynchronousTasks, true);
protectedView()->performPostLayoutTasks();
}
void LocalFrameViewLayoutContext::flushPostLayoutTasks()
{
if (!m_postLayoutTaskTimer.isActive())
return;
runPostLayoutTasks();
}
void LocalFrameViewLayoutContext::didLayout(bool canDeferUpdateLayerPositions)
{
m_layoutUpdateCount++;
auto updateLayerPositions = UpdateLayerPositions { needsFullRepaint() };
if (m_pendingUpdateLayerPositions)
m_pendingUpdateLayerPositions->merge(updateLayerPositions);
else
m_pendingUpdateLayerPositions = updateLayerPositions;
if (!canDeferUpdateLayerPositions)
flushUpdateLayerPositions();
m_updateCompositingLayersIsPending = true;
}
void LocalFrameViewLayoutContext::flushUpdateLayerPositions()
{
if (!m_pendingUpdateLayerPositions)
return;
CheckedPtr view = renderView();
if (!view)
return;
auto repaintRectEnvironment = RepaintRectEnvironment { view->page().deviceScaleFactor(), document()->printing(), protectedView()->useFixedLayout() };
bool environmentChanged = repaintRectEnvironment != m_lastRepaintRectEnvironment;
auto updateLayerPositions = *std::exchange(m_pendingUpdateLayerPositions, std::nullopt);
view->layer()->updateLayerPositionsAfterLayout(updateLayerPositions.needsFullRepaint, environmentChanged);
m_renderLayerPositionUpdateCount++;
m_lastRepaintRectEnvironment = WTFMove(repaintRectEnvironment);
}
bool LocalFrameViewLayoutContext::updateCompositingLayersAfterStyleChange()
{
// If we expect to update compositing after an incipient layout, don't do so here.
CheckedPtr view = renderView();
if (!view)
return false;
if (needsLayout() || isInLayout())
return false;
auto repaintRectEnvironment = RepaintRectEnvironment { view->page().deviceScaleFactor(), document()->printing(), protectedView()->useFixedLayout() };
bool environmentChanged = repaintRectEnvironment != m_lastRepaintRectEnvironment;
view->layer()->updateLayerPositionsAfterStyleChange(environmentChanged);
m_lastRepaintRectEnvironment = WTFMove(repaintRectEnvironment);
return view->compositor().didRecalcStyleWithNoPendingLayout();
}
void LocalFrameViewLayoutContext::updateCompositingLayersAfterLayout()
{
auto* renderView = this->renderView();
if (!renderView)
return;
renderView->compositor().updateCompositingLayers(CompositingUpdateType::AfterLayout);
m_updateCompositingLayersIsPending = false;
}
bool LocalFrameViewLayoutContext::updateCompositingLayersAfterLayoutIfNeeded()
{
if (m_updateCompositingLayersIsPending) {
updateCompositingLayersAfterLayout();
return true;
}
return false;
}
void LocalFrameViewLayoutContext::reset()
{
m_layoutPhase = LayoutPhase::OutsideLayout;
clearSubtreeLayoutRoot();
m_layoutSchedulingIsEnabled = true;
m_layoutTimer.stop();
m_firstLayout = true;
m_postLayoutTaskTimer.stop();
m_needsFullRepaint = true;
}
bool LocalFrameViewLayoutContext::needsLayout(OptionSet<LayoutOptions> layoutOptions) const
{
if (needsLayoutInternal())
return true;
if (layoutOptions.contains(LayoutOptions::CanDeferUpdateLayerPositions))
return false;
return hasPendingUpdateLayerPositions();
}
bool LocalFrameViewLayoutContext::needsLayoutInternal() 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 LocalFrameViewLayoutContext::setNeedsLayoutAfterViewConfigurationChange()
{
if (m_disableSetNeedsLayoutCount) {
m_setNeedsLayoutWasDeferred = true;
return;
}
if (auto* renderView = this->renderView()) {
ASSERT(!document()->inHitTesting());
renderView->setNeedsLayout();
scheduleLayout();
}
}
void LocalFrameViewLayoutContext::enableSetNeedsLayout()
{
ASSERT(m_disableSetNeedsLayoutCount);
if (!--m_disableSetNeedsLayoutCount)
m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen.
}
void LocalFrameViewLayoutContext::disableSetNeedsLayout()
{
++m_disableSetNeedsLayoutCount;
}
void LocalFrameViewLayoutContext::scheduleLayout()
{
// FIXME: We should assert the page is not in the back/forward cache, but that is causing
// too many false assertions. See <rdar://problem/7218118>.
ASSERT(frame().view() == &view());
RefPtr document = this->document();
if (!document)
return;
if (subtreeLayoutRoot())
convertSubtreeLayoutToFullLayout();
if (isLayoutPending())
return;
if (!isLayoutSchedulingEnabled() || !document->shouldScheduleLayout())
return;
if (!needsLayout())
return;
#if !LOG_DISABLED
if (!document->ownerElement())
LOG(Layout, "LocalFrameView %p layout timer scheduled at %.3fs", this, document->timeSinceDocumentCreation().value());
#endif
InspectorInstrumentation::didInvalidateLayout(protectedFrame());
m_layoutTimer.startOneShot(0_s);
}
void LocalFrameViewLayoutContext::unscheduleLayout()
{
if (m_postLayoutTaskTimer.isActive())
m_postLayoutTaskTimer.stop();
if (!m_layoutTimer.isActive())
return;
#if !LOG_DISABLED
if (!document()->ownerElement())
LOG_WITH_STREAM(Layout, stream << "LocalFrameViewLayoutContext for LocalFrameView " << frame().view() << " layout timer unscheduled at " << document()->timeSinceDocumentCreation().value());
#endif
m_layoutTimer.stop();
}
void LocalFrameViewLayoutContext::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(&renderView);
return;
}
if (!isLayoutPending() && isLayoutSchedulingEnabled()) {
ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
setSubtreeLayoutRoot(layoutRoot);
InspectorInstrumentation::didInvalidateLayout(protectedFrame());
m_layoutTimer.startOneShot(0_s);
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(&renderView);
InspectorInstrumentation::didInvalidateLayout(protectedFrame());
return;
}
if (isObjectAncestorContainerOf(*subtreeLayoutRoot, layoutRoot)) {
// Keep the current root.
layoutRoot.markContainingBlocksForLayout(subtreeLayoutRoot);
ASSERT(!subtreeLayoutRoot->container() || is<RenderView>(subtreeLayoutRoot->container()) || !subtreeLayoutRoot->container()->needsLayout());
return;
}
if (isObjectAncestorContainerOf(layoutRoot, *subtreeLayoutRoot)) {
// Re-root at newRelayoutRoot.
subtreeLayoutRoot->markContainingBlocksForLayout(&layoutRoot);
setSubtreeLayoutRoot(layoutRoot);
ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
InspectorInstrumentation::didInvalidateLayout(protectedFrame());
return;
}
// Two disjoint subtrees need layout. Mark both of them and issue a full layout instead.
convertSubtreeLayoutToFullLayout();
layoutRoot.markContainingBlocksForLayout(&renderView);
InspectorInstrumentation::didInvalidateLayout(protectedFrame());
}
void LocalFrameViewLayoutContext::layoutTimerFired()
{
#if !LOG_DISABLED
if (!document()->ownerElement())
LOG_WITH_STREAM(Layout, stream << "LocalFrameViewLayoutContext for LocalFrameView " << frame().view() << " layout timer fired at " << document()->timeSinceDocumentCreation().value());
#endif
layout();
}
RenderElement* LocalFrameViewLayoutContext::subtreeLayoutRoot() const
{
return m_subtreeLayoutRoot.get();
}
void LocalFrameViewLayoutContext::convertSubtreeLayoutToFullLayout()
{
ASSERT(subtreeLayoutRoot());
subtreeLayoutRoot()->markContainingBlocksForLayout(renderView());
clearSubtreeLayoutRoot();
}
void LocalFrameViewLayoutContext::setSubtreeLayoutRoot(RenderElement& layoutRoot)
{
m_subtreeLayoutRoot = layoutRoot;
}
bool LocalFrameViewLayoutContext::canPerformLayout() const
{
if (isInRenderTreeLayout())
return false;
if (view().isPainting())
return false;
if (!subtreeLayoutRoot() && !document()->renderView())
return false;
return true;
}
#if ENABLE(TEXT_AUTOSIZING)
void LocalFrameViewLayoutContext::applyTextSizingIfNeeded(RenderElement& layoutRoot)
{
ASSERT(document());
if (protectedDocument()->quirks().shouldIgnoreTextAutoSizing())
return;
Ref settings = layoutRoot.settings();
bool idempotentMode = settings->textAutosizingUsesIdempotentMode();
if (!settings->textAutosizingEnabled() || idempotentMode || renderView()->printing())
return;
auto minimumZoomFontSize = settings->minimumZoomFontSize();
if (!idempotentMode && !minimumZoomFontSize)
return;
auto textAutosizingWidth = layoutRoot.page().textAutosizingWidth();
if (auto overrideWidth = settings->textAutosizingWindowSizeOverrideWidth())
textAutosizingWidth = overrideWidth;
if (!idempotentMode && !textAutosizingWidth)
return;
layoutRoot.adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth);
if (!layoutRoot.needsLayout())
return;
LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth);
layoutRoot.layout();
}
#endif
void LocalFrameViewLayoutContext::updateStyleForLayout()
{
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
Ref document = *this->document();
// FIXME: This shouldn't be necessary, but see rdar://problem/36670246.
if (!document->styleScope().resolverIfExists())
document->styleScope().didChangeStyleSheetEnvironment();
// Viewport-dependent media queries may cause us to need completely different style information.
document->styleScope().evaluateMediaQueriesForViewportChange();
document->updateElementsAffectedByMediaQueries();
// If there is any pagination to apply, it will affect the RenderView's style, so we should
// take care of that now.
protectedView()->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();
}
LayoutSize LocalFrameViewLayoutContext::layoutDelta() const
{
if (auto* layoutState = this->layoutState())
return layoutState->layoutDelta();
return { };
}
void LocalFrameViewLayoutContext::addLayoutDelta(const LayoutSize& delta)
{
if (auto* layoutState = this->layoutState())
layoutState->addLayoutDelta(delta);
}
bool LocalFrameViewLayoutContext::isSkippedContentForLayout(const RenderElement& renderer) const
{
if (isVisiblityHiddenIgnored() || isVisiblityAutoIgnored() || (isRevealedWhenFoundIgnored() && renderer.style().autoRevealsWhenFound())) {
// In theory we should only descend into a hidden/auto subree when hidden/auto root is ignored (see isSkippedContentRootForLayout below).
return false;
}
return renderer.isSkippedContent();
}
bool LocalFrameViewLayoutContext::isSkippedContentRootForLayout(const RenderBox& renderBox) const
{
if (!isSkippedContentRoot(renderBox))
return false;
auto contentVisibility = renderBox.style().contentVisibility();
if (contentVisibility == ContentVisibility::Hidden && isVisiblityHiddenIgnored())
return false;
if (contentVisibility == ContentVisibility::Auto && isVisiblityAutoIgnored())
return false;
if (renderBox.style().autoRevealsWhenFound() && isRevealedWhenFoundIgnored())
return false;
return true;
}
#if ASSERT_ENABLED
bool LocalFrameViewLayoutContext::layoutDeltaMatches(const LayoutSize& delta)
{
if (auto* layoutState = this->layoutState())
return layoutState->layoutDeltaMatches(delta);
return false;
}
#endif
RenderLayoutState* LocalFrameViewLayoutContext::layoutState() const
{
if (m_layoutStateStack.isEmpty())
return nullptr;
return m_layoutStateStack.last().get();
}
void LocalFrameViewLayoutContext::pushLayoutState(RenderElement& root)
{
ASSERT(!m_paintOffsetCacheDisableCount);
ASSERT(!layoutState());
m_layoutStateStack.append(makeUnique<RenderLayoutState>(root));
}
bool LocalFrameViewLayoutContext::pushLayoutState(RenderBox& renderer, const LayoutSize& offset, LayoutUnit pageHeight, bool pageHeightChanged)
{
// We push LayoutState even if layoutState is disabled because it stores layoutDelta too.
auto* layoutState = this->layoutState();
if (!layoutState || !needsFullRepaint() || layoutState->isPaginated() || renderer.enclosingFragmentedFlow()
|| layoutState->lineGrid() || (!renderer.style().lineGrid().isNone() && renderer.isRenderBlockFlow())) {
m_layoutStateStack.append(makeUnique<RenderLayoutState>(m_layoutStateStack
, renderer
, offset
, pageHeight
, pageHeightChanged
, layoutState ? layoutState->lineClamp() : std::nullopt
, layoutState ? layoutState->legacyLineClamp() : std::nullopt));
return true;
}
return false;
}
void LocalFrameViewLayoutContext::popLayoutState()
{
if (!layoutState())
return;
auto currentLineClamp = layoutState()->legacyLineClamp();
m_layoutStateStack.removeLast();
if (currentLineClamp) {
// Propagates the current line clamp state to the parent.
if (auto* layoutState = this->layoutState(); layoutState && layoutState->legacyLineClamp()) {
ASSERT(layoutState->legacyLineClamp()->maximumLineCount == currentLineClamp->maximumLineCount);
layoutState->setLegacyLineClamp(currentLineClamp);
}
}
}
bool LocalFrameViewLayoutContext::DetachedRendererList::append(RenderPtr<RenderObject>&& detachedRenderer)
{
ASSERT(!detachedRenderer->parent());
ASSERT(!detachedRenderer->beingDestroyed());
if (detachedRenderer->renderTreeBeingDestroyed())
return false;
if (is<RenderWidget>(detachedRenderer)) {
// FIXME: Cleanup RenderWidget's destruction process (ref vs. delete this. see RenderObject::destroy)
return false;
}
static constexpr int maximumNumberOfDetachedRenderers = 5000;
if (m_renderers.size() == maximumNumberOfDetachedRenderers)
clear();
m_renderers.append(detachedRenderer.release());
return true;
}
void LocalFrameViewLayoutContext::setBoxNeedsTransformUpdateAfterContainerLayout(RenderBox& box, RenderBlock& container)
{
auto it = m_containersWithDescendantsNeedingTransformUpdate.ensure(container, [] { return Vector<SingleThreadWeakPtr<RenderBox>>({ }); });
it.iterator->value.append(WeakPtr { box });
}
Vector<SingleThreadWeakPtr<RenderBox>> LocalFrameViewLayoutContext::takeBoxesNeedingTransformUpdateAfterContainerLayout(RenderBlock& container)
{
return m_containersWithDescendantsNeedingTransformUpdate.take(container);
}
void LocalFrameViewLayoutContext::disablePercentHeightResolveFor(const RenderBox& flexItem)
{
m_percentHeightIgnoreList.add(flexItem);
}
void LocalFrameViewLayoutContext::enablePercentHeightResolveFor(const RenderBox& flexItem)
{
m_percentHeightIgnoreList.remove(flexItem);
}
bool LocalFrameViewLayoutContext::isPercentHeightResolveDisabledFor(const RenderBox& flexItem)
{
return m_percentHeightIgnoreList.contains(flexItem);
}
#ifndef NDEBUG
void LocalFrameViewLayoutContext::checkLayoutState()
{
ASSERT(layoutDeltaMatches(LayoutSize()));
ASSERT(!m_paintOffsetCacheDisableCount);
}
#endif
const AnchorScrollAdjuster* LocalFrameViewLayoutContext::anchorScrollAdjusterFor(const RenderBox& anchored) const
{
auto index = m_anchorScrollAdjusters.findIf([&](auto& item) {
return item.anchored() == &anchored;
});
if (index == WTF::notFound)
return { };
return &m_anchorScrollAdjusters[index];
}
AnchorScrollAdjuster::Diff LocalFrameViewLayoutContext::registerAnchorScrollAdjuster(AnchorScrollAdjuster&& scrollAdjuster)
{
auto index = m_anchorScrollAdjusters.findIf([&](auto& item) {
return item.anchored() == scrollAdjuster.anchored();
});
bool recaptureDiffers = false;
if (WTF::notFound == index) {
m_anchorScrollAdjusters.append(WTFMove(scrollAdjuster));
return AnchorScrollAdjuster::New;
}
recaptureDiffers = m_anchorScrollAdjusters[index].recaptureDiffers(scrollAdjuster);
m_anchorScrollAdjusters[index] = WTFMove(scrollAdjuster);
return recaptureDiffers ? AnchorScrollAdjuster::SnapshotsDiffer : AnchorScrollAdjuster::SnapshotsMatch;
}
void LocalFrameViewLayoutContext::unregisterAnchorScrollAdjusterFor(const RenderBox& anchored)
{
m_anchorScrollAdjusters.removeFirstMatching([&](auto& item) {
return item.anchored() == &anchored;
});
ASSERT(!m_anchorScrollAdjusters.containsIf([&](auto& item) {
return item.anchored() == &anchored;
}));
if (anchored.layer())
anchored.layer()->clearAnchorScrollAdjustment();
}
void LocalFrameViewLayoutContext::invalidateAnchorDependenciesForScroller(const RenderBox& scroller)
{
for (auto& adjuster : m_anchorScrollAdjusters)
adjuster.invalidateForScroller(scroller);
}
void LocalFrameViewLayoutContext::removeScrollerFromAnchorScrollAdjusters(const RenderBox& scroller)
{
if (!renderView() || renderView()->renderTreeBeingDestroyed())
m_anchorScrollAdjusters.clear();
else {
for (auto& adjuster : m_anchorScrollAdjusters) {
if (adjuster.invalidateForScroller(scroller))
unregisterAnchorScrollAdjusterFor(*adjuster.anchored());
}
}
}
LocalFrame& LocalFrameViewLayoutContext::frame() const
{
return view().frame();
}
Ref<LocalFrame> LocalFrameViewLayoutContext::protectedFrame()
{
return frame();
}
LocalFrameView& LocalFrameViewLayoutContext::view() const
{
return m_frameView.get();
}
Ref<LocalFrameView> LocalFrameViewLayoutContext::protectedView() const
{
return m_frameView.get();
}
RenderView* LocalFrameViewLayoutContext::renderView() const
{
return protectedView()->renderView();
}
Document* LocalFrameViewLayoutContext::document() const
{
return frame().document();
}
RefPtr<Document> LocalFrameViewLayoutContext::protectedDocument() const
{
return document();
}
} // namespace WebCore