summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderView.cpp')
-rw-r--r--Source/WebCore/rendering/RenderView.cpp1098
1 files changed, 651 insertions, 447 deletions
diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp
index 2968bf21d..e9f1f700a 100644
--- a/Source/WebCore/rendering/RenderView.cpp
+++ b/Source/WebCore/rendering/RenderView.cpp
@@ -21,7 +21,6 @@
#include "config.h"
#include "RenderView.h"
-#include "ColumnInfo.h"
#include "Document.h"
#include "Element.h"
#include "FloatQuad.h"
@@ -31,46 +30,101 @@
#include "FrameSelection.h"
#include "FrameView.h"
#include "GraphicsContext.h"
+#include "HTMLBodyElement.h"
#include "HTMLFrameOwnerElement.h"
+#include "HTMLHtmlElement.h"
#include "HTMLIFrameElement.h"
#include "HitTestResult.h"
#include "ImageQualityController.h"
+#include "NodeTraversal.h"
#include "Page.h"
#include "RenderGeometryMap.h"
#include "RenderIterator.h"
#include "RenderLayer.h"
#include "RenderLayerBacking.h"
+#include "RenderLayerCompositor.h"
+#include "RenderMultiColumnFlowThread.h"
+#include "RenderMultiColumnSet.h"
+#include "RenderMultiColumnSpannerPlaceholder.h"
#include "RenderNamedFlowThread.h"
#include "RenderSelectionInfo.h"
#include "RenderWidget.h"
+#include "ScrollbarTheme.h"
+#include "Settings.h"
#include "StyleInheritedData.h"
#include "TransformState.h"
+#include <wtf/SetForScope.h>
#include <wtf/StackStats.h>
-#if USE(ACCELERATED_COMPOSITING)
-#include "RenderLayerCompositor.h"
-#endif
-
namespace WebCore {
-RenderView::RenderView(Document& document, PassRef<RenderStyle> style)
- : RenderBlockFlow(document, std::move(style))
+struct FrameFlatteningLayoutDisallower {
+ FrameFlatteningLayoutDisallower(FrameView& frameView)
+ : m_frameView(frameView)
+ , m_disallowLayout(frameView.frame().settings().frameFlatteningEnabled())
+ {
+ if (m_disallowLayout)
+ m_frameView.startDisallowingLayout();
+ }
+
+ ~FrameFlatteningLayoutDisallower()
+ {
+ if (m_disallowLayout)
+ m_frameView.endDisallowingLayout();
+ }
+
+private:
+ FrameView& m_frameView;
+ bool m_disallowLayout { false };
+};
+
+struct SelectionIterator {
+ SelectionIterator(RenderObject* start)
+ : m_current(start)
+ {
+ checkForSpanner();
+ }
+
+ RenderObject* current() const
+ {
+ return m_current;
+ }
+
+ RenderObject* next()
+ {
+ RenderObject* currentSpan = m_spannerStack.isEmpty() ? nullptr : m_spannerStack.last()->spanner();
+ m_current = m_current->nextInPreOrder(currentSpan);
+ checkForSpanner();
+ if (!m_current && currentSpan) {
+ RenderObject* placeholder = m_spannerStack.last();
+ m_spannerStack.removeLast();
+ m_current = placeholder->nextInPreOrder();
+ checkForSpanner();
+ }
+ return m_current;
+ }
+
+private:
+ void checkForSpanner()
+ {
+ if (!is<RenderMultiColumnSpannerPlaceholder>(m_current))
+ return;
+ auto& placeholder = downcast<RenderMultiColumnSpannerPlaceholder>(*m_current);
+ m_spannerStack.append(&placeholder);
+ m_current = placeholder.spanner();
+ }
+
+ RenderObject* m_current { nullptr };
+ Vector<RenderMultiColumnSpannerPlaceholder*> m_spannerStack;
+};
+
+RenderView::RenderView(Document& document, RenderStyle&& style)
+ : RenderBlockFlow(document, WTFMove(style))
, m_frameView(*document.view())
- , m_selectionStart(0)
- , m_selectionEnd(0)
- , m_selectionStartPos(-1)
- , m_selectionEndPos(-1)
- , m_rendererCount(0)
- , m_maximalOutlineSize(0)
- , m_pageLogicalHeight(0)
- , m_pageLogicalHeightChanged(false)
- , m_layoutState(nullptr)
- , m_layoutStateDisableCount(0)
- , m_renderQuoteHead(0)
- , m_renderCounterCount(0)
- , m_selectionWasCaret(false)
-#if ENABLE(CSS_FILTERS)
- , m_hasSoftwareFilters(false)
+ , m_weakFactory(this)
+ , m_lazyRepaintTimer(*this, &RenderView::lazyRepaintTimerFired)
+#if ENABLE(SERVICE_CONTROLS)
+ , m_selectionRectGatherer(*this)
#endif
{
setIsRenderView();
@@ -93,6 +147,35 @@ RenderView::~RenderView()
{
}
+void RenderView::scheduleLazyRepaint(RenderBox& renderer)
+{
+ if (renderer.renderBoxNeedsLazyRepaint())
+ return;
+ renderer.setRenderBoxNeedsLazyRepaint(true);
+ m_renderersNeedingLazyRepaint.add(&renderer);
+ if (!m_lazyRepaintTimer.isActive())
+ m_lazyRepaintTimer.startOneShot(0);
+}
+
+void RenderView::unscheduleLazyRepaint(RenderBox& renderer)
+{
+ if (!renderer.renderBoxNeedsLazyRepaint())
+ return;
+ renderer.setRenderBoxNeedsLazyRepaint(false);
+ m_renderersNeedingLazyRepaint.remove(&renderer);
+ if (m_renderersNeedingLazyRepaint.isEmpty())
+ m_lazyRepaintTimer.stop();
+}
+
+void RenderView::lazyRepaintTimerFired()
+{
+ for (auto& renderer : m_renderersNeedingLazyRepaint) {
+ renderer->repaint();
+ renderer->setRenderBoxNeedsLazyRepaint(false);
+ }
+ m_renderersNeedingLazyRepaint.clear();
+}
+
bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result)
{
return hitTest(request, result.hitTestLocation(), result);
@@ -100,26 +183,36 @@ bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result)
bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result)
{
- if (layer()->hitTest(request, location, result))
- return true;
-
- // FIXME: Consider if this test should be done unconditionally.
- if (request.allowsFrameScrollbars()) {
- // ScrollView scrollbars are not the same as RenderLayer scrollbars tested by RenderLayer::hitTestOverflowControls,
- // so we need to test ScrollView scrollbars separately here.
- Scrollbar* frameScrollbar = frameView().scrollbarAtPoint(location.roundedPoint());
- if (frameScrollbar) {
- result.setScrollbar(frameScrollbar);
- return true;
+ document().updateLayout();
+
+#if !ASSERT_DISABLED
+ SetForScope<bool> hitTestRestorer { m_inHitTesting, true };
+#endif
+
+ FrameFlatteningLayoutDisallower disallower(frameView());
+
+ bool resultLayer = layer()->hitTest(request, location, result);
+
+ // ScrollView scrollbars are not the same as RenderLayer scrollbars tested by RenderLayer::hitTestOverflowControls,
+ // so we need to test ScrollView scrollbars separately here. In case of using overlay scrollbars, the layer hit test
+ // will always work so we need to check the ScrollView scrollbars in that case too.
+ if (!resultLayer || ScrollbarTheme::theme().usesOverlayScrollbars()) {
+ // FIXME: Consider if this test should be done unconditionally.
+ if (request.allowsFrameScrollbars()) {
+ IntPoint windowPoint = frameView().contentsToWindow(location.roundedPoint());
+ if (Scrollbar* frameScrollbar = frameView().scrollbarAtPoint(windowPoint)) {
+ result.setScrollbar(frameScrollbar);
+ return true;
+ }
}
}
- return false;
+ return resultLayer;
}
-void RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit, LogicalExtentComputedValues& computedValues) const
+RenderBox::LogicalExtentComputedValues RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit) const
{
- computedValues.m_extent = !shouldUsePrintingLayout() ? LayoutUnit(viewLogicalHeight()) : logicalHeight;
+ return { !shouldUsePrintingLayout() ? LayoutUnit(viewLogicalHeight()) : logicalHeight, LayoutUnit(), ComputedMarginValues() };
}
void RenderView::updateLogicalWidth()
@@ -130,9 +223,11 @@ void RenderView::updateLogicalWidth()
LayoutUnit RenderView::availableLogicalHeight(AvailableLogicalHeightType) const
{
- // If we have columns, then the available logical height is reduced to the column height.
- if (hasColumns())
- return columnInfo()->columnHeight();
+ // Make sure block progression pagination for percentages uses the column extent and
+ // not the view's extent. See https://bugs.webkit.org/show_bug.cgi?id=135204.
+ if (multiColumnFlowThread() && multiColumnFlowThread()->firstMultiColumnSet())
+ return multiColumnFlowThread()->firstMultiColumnSet()->computedColumnHeight();
+
#if PLATFORM(IOS)
// Workaround for <rdar://problem/7166808>.
if (document().isPluginDocument() && frameView().useFixedLayout())
@@ -168,67 +263,15 @@ void RenderView::checkLayoutState(const LayoutState& state)
}
#endif
-static RenderBox* enclosingSeamlessRenderer(Document& document)
-{
- Element* ownerElement = document.seamlessParentIFrame();
- if (!ownerElement)
- return 0;
- return ownerElement->renderBox();
-}
-
-void RenderView::addChild(RenderObject* newChild, RenderObject* beforeChild)
+void RenderView::initializeLayoutState(LayoutState& state)
{
- // Seamless iframes are considered part of an enclosing render flow thread from the parent document. This is necessary for them to look
- // up regions in the parent document during layout.
- if (newChild && !newChild->isRenderFlowThread()) {
- RenderBox* seamlessBox = enclosingSeamlessRenderer(document());
- if (seamlessBox && seamlessBox->flowThreadContainingBlock())
- newChild->setFlowThreadState(seamlessBox->flowThreadState());
- }
- RenderBlockFlow::addChild(newChild, beforeChild);
-}
-
-bool RenderView::initializeLayoutState(LayoutState& state)
-{
- bool isSeamlessAncestorInFlowThread = false;
-
// FIXME: May be better to push a clip and avoid issuing offscreen repaints.
state.m_clipped = false;
-
- // Check the writing mode of the seamless ancestor. It has to match our document's writing mode, or we won't inherit any
- // pagination information.
- RenderBox* seamlessAncestor = enclosingSeamlessRenderer(document());
- LayoutState* seamlessLayoutState = seamlessAncestor ? seamlessAncestor->view().layoutState() : 0;
- bool shouldInheritPagination = seamlessLayoutState && !m_pageLogicalHeight && seamlessAncestor->style().writingMode() == style().writingMode();
-
- state.m_pageLogicalHeight = shouldInheritPagination ? seamlessLayoutState->m_pageLogicalHeight : m_pageLogicalHeight;
- state.m_pageLogicalHeightChanged = shouldInheritPagination ? seamlessLayoutState->m_pageLogicalHeightChanged : m_pageLogicalHeightChanged;
- state.m_isPaginated = state.m_pageLogicalHeight;
- if (state.m_isPaginated && shouldInheritPagination) {
- // Set up the correct pagination offset. We can use a negative offset in order to push the top of the RenderView into its correct place
- // on a page. We can take the iframe's offset from the logical top of the first page and make the negative into the pagination offset within the child
- // view.
- bool isFlipped = seamlessAncestor->style().isFlippedBlocksWritingMode();
- LayoutSize layoutOffset = seamlessLayoutState->layoutOffset();
- LayoutSize iFrameOffset(layoutOffset.width() + seamlessAncestor->x() + (!isFlipped ? seamlessAncestor->borderLeft() + seamlessAncestor->paddingLeft() :
- seamlessAncestor->borderRight() + seamlessAncestor->paddingRight()),
- layoutOffset.height() + seamlessAncestor->y() + (!isFlipped ? seamlessAncestor->borderTop() + seamlessAncestor->paddingTop() :
- seamlessAncestor->borderBottom() + seamlessAncestor->paddingBottom()));
-
- LayoutSize offsetDelta = seamlessLayoutState->m_pageOffset - iFrameOffset;
- state.m_pageOffset = offsetDelta;
-
- // Set the current render flow thread to point to our ancestor. This will allow the seamless document to locate the correct
- // regions when doing a layout.
- if (seamlessAncestor->flowThreadContainingBlock()) {
- flowThreadController().setCurrentRenderFlowThread(seamlessAncestor->view().flowThreadController().currentRenderFlowThread());
- isSeamlessAncestorInFlowThread = true;
- }
- }
- // FIXME: We need to make line grids and exclusions work with seamless iframes as well here. Basically all layout state information needs
- // to propagate here and not just pagination information.
- return isSeamlessAncestorInFlowThread;
+ state.m_pageLogicalHeight = m_pageLogicalHeight;
+ state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged;
+ ASSERT(state.m_pageLogicalHeight >= 0);
+ state.m_isPaginated = state.m_pageLogicalHeight > 0;
}
// The algorithm below assumes this is a full layout. In case there are previously computed values for regions, supplemental steps are taken
@@ -308,16 +351,10 @@ void RenderView::layout()
for (auto& box : childrenOfType<RenderBox>(*this)) {
if (box.hasRelativeLogicalHeight()
- || box.hasViewportPercentageLogicalHeight()
- || box.style().logicalHeight().isPercent()
- || box.style().logicalMinHeight().isPercent()
- || box.style().logicalMaxHeight().isPercent()
- || box.style().logicalHeight().isViewportPercentage()
- || box.style().logicalMinHeight().isViewportPercentage()
- || box.style().logicalMaxHeight().isViewportPercentage()
-#if ENABLE(SVG)
+ || box.style().logicalHeight().isPercentOrCalculated()
+ || box.style().logicalMinHeight().isPercentOrCalculated()
+ || box.style().logicalMaxHeight().isPercentOrCalculated()
|| box.isSVGRoot()
-#endif
)
box.setChildNeedsLayout(MarkOnlyThis);
}
@@ -328,7 +365,7 @@ void RenderView::layout()
return;
m_layoutState = std::make_unique<LayoutState>();
- bool isSeamlessAncestorInFlowThread = initializeLayoutState(*m_layoutState);
+ initializeLayoutState(*m_layoutState);
m_pageLogicalHeightChanged = false;
@@ -344,9 +381,6 @@ void RenderView::layout()
#endif
m_layoutState = nullptr;
clearNeedsLayout();
-
- if (isSeamlessAncestorInFlowThread)
- flowThreadController().setCurrentRenderFlowThread(0);
}
LayoutUnit RenderView::pageOrViewLogicalHeight() const
@@ -354,7 +388,7 @@ LayoutUnit RenderView::pageOrViewLogicalHeight() const
if (document().printing())
return pageLogicalHeight();
- if (hasColumns() && !style().hasInlineColumnAxis()) {
+ if (multiColumnFlowThread() && !style().hasInlineColumnAxis()) {
if (int pageLength = frameView().pagination().pageLength)
return pageLength;
}
@@ -373,6 +407,9 @@ LayoutUnit RenderView::clientLogicalWidthForFixedPosition() const
return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().width() : frameView().customFixedPositionLayoutRect().height();
#endif
+ if (settings().visualViewportEnabled())
+ return isHorizontalWritingMode() ? frameView().layoutViewportRect().width() : frameView().layoutViewportRect().height();
+
return clientLogicalWidth();
}
@@ -387,73 +424,57 @@ LayoutUnit RenderView::clientLogicalHeightForFixedPosition() const
return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().height() : frameView().customFixedPositionLayoutRect().width();
#endif
- return clientLogicalHeight();
-}
+ if (settings().visualViewportEnabled())
+ return isHorizontalWritingMode() ? frameView().layoutViewportRect().height() : frameView().layoutViewportRect().width();
-#if PLATFORM(IOS)
-static inline LayoutSize fixedPositionOffset(const FrameView& frameView)
-{
- return frameView.useCustomFixedPositionLayoutRect() ? (frameView.customFixedPositionLayoutRect().location() - LayoutPoint()) : frameView.scrollOffset();
+ return clientLogicalHeight();
}
-#endif
void RenderView::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
{
- // If a container was specified, and was not 0 or the RenderView,
+ // If a container was specified, and was not nullptr or the RenderView,
// then we should have found it by now.
ASSERT_ARG(repaintContainer, !repaintContainer || repaintContainer == this);
ASSERT_UNUSED(wasFixed, !wasFixed || *wasFixed == (mode & IsFixed));
- if (!repaintContainer && mode & UseTransforms && shouldUseTransformFromContainer(0)) {
+ if (mode & IsFixed)
+ transformState.move(toLayoutSize(frameView().scrollPositionRespectingCustomFixedPosition()));
+
+ if (!repaintContainer && mode & UseTransforms && shouldUseTransformFromContainer(nullptr)) {
TransformationMatrix t;
- getTransformFromContainer(0, LayoutSize(), t);
+ getTransformFromContainer(nullptr, LayoutSize(), t);
transformState.applyTransform(t);
}
-
- if (mode & IsFixed)
-#if PLATFORM(IOS)
- transformState.move(fixedPositionOffset(m_frameView));
-#else
- transformState.move(frameView().scrollOffsetForFixedPosition());
-#endif
}
const RenderObject* RenderView::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
{
- // If a container was specified, and was not 0 or the RenderView,
+ // If a container was specified, and was not nullptr or the RenderView,
// then we should have found it by now.
ASSERT_ARG(ancestorToStopAt, !ancestorToStopAt || ancestorToStopAt == this);
-#if PLATFORM(IOS)
- LayoutSize scrollOffset = fixedPositionOffset(frameView());
-#else
- LayoutSize scrollOffset = frameView().scrollOffsetForFixedPosition();
-#endif
+ LayoutPoint scrollPosition = frameView().scrollPositionRespectingCustomFixedPosition();
- if (!ancestorToStopAt && shouldUseTransformFromContainer(0)) {
+ if (!ancestorToStopAt && shouldUseTransformFromContainer(nullptr)) {
TransformationMatrix t;
- getTransformFromContainer(0, LayoutSize(), t);
- geometryMap.pushView(this, scrollOffset, &t);
+ getTransformFromContainer(nullptr, LayoutSize(), t);
+ geometryMap.pushView(this, toLayoutSize(scrollPosition), &t);
} else
- geometryMap.pushView(this, scrollOffset);
+ geometryMap.pushView(this, toLayoutSize(scrollPosition));
- return 0;
+ return nullptr;
}
void RenderView::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
{
- if (mode & IsFixed)
-#if PLATFORM(IOS)
- transformState.move(fixedPositionOffset(frameView()));
-#else
- transformState.move(frameView().scrollOffsetForFixedPosition());
-#endif
-
- if (mode & UseTransforms && shouldUseTransformFromContainer(0)) {
+ if (mode & UseTransforms && shouldUseTransformFromContainer(nullptr)) {
TransformationMatrix t;
- getTransformFromContainer(0, LayoutSize(), t);
+ getTransformFromContainer(nullptr, LayoutSize(), t);
transformState.applyTransform(t);
}
+
+ if (mode & IsFixed)
+ transformState.move(toLayoutSize(frameView().scrollPositionRespectingCustomFixedPosition()));
}
bool RenderView::requiresColumns(int) const
@@ -471,11 +492,6 @@ void RenderView::computeColumnCountAndWidth()
setComputedColumnCountAndWidth(1, columnWidth);
}
-ColumnInfo::PaginationUnit RenderView::paginationUnit() const
-{
- return frameView().pagination().behavesLikeColumns ? ColumnInfo::Column : ColumnInfo::Page;
-}
-
void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
// If we ever require layout but receive a paint anyway, something has gone horribly wrong.
@@ -485,31 +501,50 @@ void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
// This avoids painting garbage between columns if there is a column gap.
if (frameView().pagination().mode != Pagination::Unpaginated && paintInfo.shouldPaintWithinRoot(*this))
- paintInfo.context->fillRect(paintInfo.rect, frameView().baseBackgroundColor(), ColorSpaceDeviceRGB);
+ paintInfo.context().fillRect(paintInfo.rect, frameView().baseBackgroundColor());
paintObject(paintInfo, paintOffset);
}
-static inline bool isComposited(RenderElement* object)
+RenderElement* RenderView::rendererForRootBackground() const
{
- return object->hasLayer() && toRenderLayerModelObject(object)->layer()->isComposited();
+ auto* firstChild = this->firstChild();
+ if (!firstChild)
+ return nullptr;
+ ASSERT(is<RenderElement>(*firstChild));
+ auto& documentRenderer = downcast<RenderElement>(*firstChild);
+
+ if (documentRenderer.hasBackground())
+ return &documentRenderer;
+
+ // We propagate the background only for HTML content.
+ if (!is<HTMLHtmlElement>(documentRenderer.element()))
+ return &documentRenderer;
+
+ if (auto* body = document().body()) {
+ if (auto* renderer = body->renderer())
+ return renderer;
+ }
+ return &documentRenderer;
}
-static inline bool rendererObscuresBackground(RenderElement* rootObject)
+static inline bool rendererObscuresBackground(const RenderElement& rootElement)
{
- if (!rootObject)
+ auto& style = rootElement.style();
+ if (style.visibility() != VISIBLE || style.opacity() != 1 || style.hasTransform())
return false;
-
- const RenderStyle& style = rootObject->style();
- if (style.visibility() != VISIBLE
- || style.opacity() != 1
- || style.hasTransform())
+
+ if (style.hasBorderRadius())
return false;
-
- if (isComposited(rootObject))
+
+ if (rootElement.isComposited())
+ return false;
+
+ auto* rendererForBackground = rootElement.view().rendererForRootBackground();
+ if (!rendererForBackground)
return false;
- if (rootObject->rendererForRootBackground().style().backgroundClip() == TextFillBox)
+ if (rendererForBackground->style().backgroundClip() == TextFillBox)
return false;
return true;
@@ -525,22 +560,19 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&)
// layers with reflections, or transformed layers.
// FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being inside
// a transform, transparency layer, etc.
- Element* elt;
- for (elt = document().ownerElement(); elt && elt->renderer(); elt = elt->document().ownerElement()) {
- RenderLayer* layer = elt->renderer()->enclosingLayer();
+ for (HTMLFrameOwnerElement* element = document().ownerElement(); element && element->renderer(); element = element->document().ownerElement()) {
+ RenderLayer* layer = element->renderer()->enclosingLayer();
if (layer->cannotBlitToWindow()) {
frameView().setCannotBlitToWindow();
break;
}
-#if USE(ACCELERATED_COMPOSITING)
if (RenderLayer* compositingLayer = layer->enclosingCompositingLayerForRepaint()) {
if (!compositingLayer->backing()->paintsIntoWindow()) {
frameView().setCannotBlitToWindow();
break;
}
}
-#endif
}
if (document().ownerElement())
@@ -552,40 +584,41 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&)
bool rootFillsViewport = false;
bool rootObscuresBackground = false;
Element* documentElement = document().documentElement();
- if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : 0) {
+ if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr) {
// The document element's renderer is currently forced to be a block, but may not always be.
- RenderBox* rootBox = rootRenderer->isBox() ? toRenderBox(rootRenderer) : 0;
+ RenderBox* rootBox = is<RenderBox>(*rootRenderer) ? downcast<RenderBox>(rootRenderer) : nullptr;
rootFillsViewport = rootBox && !rootBox->x() && !rootBox->y() && rootBox->width() >= width() && rootBox->height() >= height();
- rootObscuresBackground = rendererObscuresBackground(rootRenderer);
+ rootObscuresBackground = rendererObscuresBackground(*rootRenderer);
}
- bool hasTiledMargin = false;
-#if USE(ACCELERATED_COMPOSITING)
- hasTiledMargin = compositor().mainFrameBackingIsTiledWithMargin();
-#endif
+ bool backgroundShouldExtendBeyondPage = settings().backgroundShouldExtendBeyondPage();
+ compositor().setRootExtendedBackgroundColor(backgroundShouldExtendBeyondPage ? frameView().documentBackgroundColor() : Color());
Page* page = document().page();
float pageScaleFactor = page ? page->pageScaleFactor() : 1;
// If painting will entirely fill the view, no need to fill the background.
- if (!hasTiledMargin && rootFillsViewport && rootObscuresBackground && pageScaleFactor >= 1)
+ if (rootFillsViewport && rootObscuresBackground && pageScaleFactor >= 1)
return;
// This code typically only executes if the root element's visibility has been set to hidden,
// if there is a transform on the <html>, or if there is a page scale factor less than 1.
- // Only fill with the base background color (typically white) if we're the root document,
+ // Only fill with a background color (typically white) if we're the root document,
// since iframes/frames with no background in the child document should show the parent's background.
+ // We use the base background color unless the backgroundShouldExtendBeyondPage setting is set,
+ // in which case we use the document's background color.
if (frameView().isTransparent()) // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent.
frameView().setCannotBlitToWindow(); // The parent must show behind the child.
else {
- Color backgroundColor = hasTiledMargin ? frameView().documentBackgroundColor() : frameView().baseBackgroundColor();
- if (backgroundColor.alpha()) {
- CompositeOperator previousOperator = paintInfo.context->compositeOperation();
- paintInfo.context->setCompositeOperation(CompositeCopy);
- paintInfo.context->fillRect(paintInfo.rect, backgroundColor, style().colorSpace());
- paintInfo.context->setCompositeOperation(previousOperator);
+ const Color& documentBackgroundColor = frameView().documentBackgroundColor();
+ const Color& backgroundColor = (backgroundShouldExtendBeyondPage && documentBackgroundColor.isValid()) ? documentBackgroundColor : frameView().baseBackgroundColor();
+ if (backgroundColor.isVisible()) {
+ CompositeOperator previousOperator = paintInfo.context().compositeOperation();
+ paintInfo.context().setCompositeOperation(CompositeCopy);
+ paintInfo.context().fillRect(paintInfo.rect, backgroundColor);
+ paintInfo.context().setCompositeOperation(previousOperator);
} else
- paintInfo.context->clearRect(paintInfo.rect);
+ paintInfo.context().clearRect(paintInfo.rect);
}
}
@@ -596,46 +629,57 @@ bool RenderView::shouldRepaint(const LayoutRect& rect) const
void RenderView::repaintRootContents()
{
-#if USE(ACCELERATED_COMPOSITING)
if (layer()->isComposited()) {
layer()->setBackingNeedsRepaint(GraphicsLayer::DoNotClipToLayer);
return;
}
-#endif
- repaint();
+
+ // Always use layoutOverflowRect() to fix rdar://problem/27182267.
+ // This should be cleaned up via webkit.org/b/159913 and webkit.org/b/159914.
+ RenderLayerModelObject* repaintContainer = containerForRepaint();
+ repaintUsingContainer(repaintContainer, computeRectForRepaint(layoutOverflowRect(), repaintContainer));
}
-void RenderView::repaintViewRectangle(const LayoutRect& repaintRect, bool immediate) const
+void RenderView::repaintViewRectangle(const LayoutRect& repaintRect) const
{
- // FIXME: Get rid of the 'immediate' argument. It only works on Mac WK1 and should never be used.
if (!shouldRepaint(repaintRect))
return;
+ // FIXME: enclosingRect is needed as long as we integral snap ScrollView/FrameView/RenderWidget size/position.
+ IntRect enclosingRect = enclosingIntRect(repaintRect);
if (auto ownerElement = document().ownerElement()) {
- if (!ownerElement->renderer())
+ RenderBox* ownerBox = ownerElement->renderBox();
+ if (!ownerBox)
return;
- auto& ownerBox = toRenderBox(*ownerElement->renderer());
LayoutRect viewRect = this->viewRect();
#if PLATFORM(IOS)
// Don't clip using the visible rect since clipping is handled at a higher level on iPhone.
- LayoutRect adjustedRect = repaintRect;
+ LayoutRect adjustedRect = enclosingRect;
#else
- LayoutRect adjustedRect = intersection(repaintRect, viewRect);
+ LayoutRect adjustedRect = intersection(enclosingRect, viewRect);
#endif
adjustedRect.moveBy(-viewRect.location());
- adjustedRect.moveBy(ownerBox.contentBoxRect().location());
- ownerBox.repaintRectangle(adjustedRect, immediate);
+ adjustedRect.moveBy(ownerBox->contentBoxRect().location());
+
+ // A dirty rect in an iframe is relative to the contents of that iframe.
+ // When we traverse between parent frames and child frames, we need to make sure
+ // that the coordinate system is mapped appropriately between the iframe's contents
+ // and the Renderer that contains the iframe. This transformation must account for a
+ // left scrollbar (if one exists).
+ FrameView& frameView = this->frameView();
+ if (frameView.shouldPlaceBlockDirectionScrollbarOnLeft() && frameView.verticalScrollbar())
+ adjustedRect.move(LayoutSize(frameView.verticalScrollbar()->occupiedWidth(), 0));
+
+ ownerBox->repaintRectangle(adjustedRect);
return;
}
- IntRect pixelSnappedRect = pixelSnappedIntRect(repaintRect);
- frameView().addTrackedRepaintRect(pixelSnappedRect);
-
- if (!m_accumulatedRepaintRegion || immediate) {
- frameView().repaintContentRectangle(pixelSnappedRect, immediate);
+ frameView().addTrackedRepaintRect(snapRectToDevicePixels(repaintRect, document().deviceScaleFactor()));
+ if (!m_accumulatedRepaintRegion) {
+ frameView().repaintContentRectangle(enclosingRect);
return;
}
- m_accumulatedRepaintRegion->unite(pixelSnappedRect);
+ m_accumulatedRepaintRegion->unite(enclosingRect);
// Region will get slow if it gets too complex. Merge all rects so far to bounds if this happens.
// FIXME: Maybe there should be a region type that does this automatically.
@@ -650,34 +694,17 @@ void RenderView::flushAccumulatedRepaintRegion() const
ASSERT(m_accumulatedRepaintRegion);
auto repaintRects = m_accumulatedRepaintRegion->rects();
for (auto& rect : repaintRects)
- frameView().repaintContentRectangle(rect, false);
+ frameView().repaintContentRectangle(rect);
m_accumulatedRepaintRegion = nullptr;
}
-void RenderView::repaintRectangleInViewAndCompositedLayers(const LayoutRect& ur, bool immediate)
-{
- if (!shouldRepaint(ur))
- return;
-
- repaintViewRectangle(ur, immediate);
-
-#if USE(ACCELERATED_COMPOSITING)
- RenderLayerCompositor& compositor = this->compositor();
- if (compositor.inCompositingMode()) {
- IntRect repaintRect = pixelSnappedIntRect(ur);
- compositor.repaintCompositedLayers(&repaintRect);
- }
-#endif
-}
-
void RenderView::repaintViewAndCompositedLayers()
{
repaintRootContents();
-#if USE(ACCELERATED_COMPOSITING)
+
RenderLayerCompositor& compositor = this->compositor();
if (compositor.inCompositingMode())
compositor.repaintCompositedLayers();
-#endif
}
LayoutRect RenderView::visualOverflowRect() const
@@ -688,40 +715,45 @@ LayoutRect RenderView::visualOverflowRect() const
return RenderBlockFlow::visualOverflowRect();
}
-void RenderView::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
+LayoutRect RenderView::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, RepaintContext context) const
{
- // If a container was specified, and was not 0 or the RenderView,
+ // If a container was specified, and was not nullptr or the RenderView,
// then we should have found it by now.
ASSERT_ARG(repaintContainer, !repaintContainer || repaintContainer == this);
if (printing())
- return;
-
+ return rect;
+
+ LayoutRect adjustedRect = rect;
if (style().isFlippedBlocksWritingMode()) {
// We have to flip by hand since the view's logical height has not been determined. We
// can use the viewport width and height.
if (style().isHorizontalWritingMode())
- rect.setY(viewHeight() - rect.maxY());
+ adjustedRect.setY(viewHeight() - adjustedRect.maxY());
else
- rect.setX(viewWidth() - rect.maxX());
+ adjustedRect.setX(viewWidth() - adjustedRect.maxX());
}
- if (fixed) {
-#if PLATFORM(IOS)
- rect.move(fixedPositionOffset(frameView()));
-#else
- rect.move(frameView().scrollOffsetForFixedPosition());
-#endif
- }
-
+ if (context.m_hasPositionFixedDescendant)
+ adjustedRect.moveBy(frameView().scrollPositionRespectingCustomFixedPosition());
+
// Apply our transform if we have one (because of full page zooming).
if (!repaintContainer && layer() && layer()->transform())
- rect = layer()->transform()->mapRect(rect);
+ adjustedRect = LayoutRect(layer()->transform()->mapRect(snapRectToDevicePixels(adjustedRect, document().deviceScaleFactor())));
+ return adjustedRect;
+}
+
+bool RenderView::isScrollableOrRubberbandableBox() const
+{
+ // The main frame might be allowed to rubber-band even if there is no content to scroll to. This is unique to
+ // the main frame; subframes and overflow areas have to have content that can be scrolled to in order to rubber-band.
+ FrameView::Scrollability defineScrollable = frame().ownerElement() ? FrameView::Scrollability::Scrollable : FrameView::Scrollability::ScrollableOrRubberbandable;
+ return frameView().isScrollable(defineScrollable);
}
void RenderView::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
{
- rects.append(pixelSnappedIntRect(accumulatedOffset, layer()->size()));
+ rects.append(snappedIntRect(accumulatedOffset, layer()->size()));
}
void RenderView::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
@@ -734,7 +766,7 @@ void RenderView::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
static RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset)
{
if (!object)
- return 0;
+ return nullptr;
RenderObject* child = object->childAt(offset);
return child ? child : object->nextInPreOrderAfterChildren();
@@ -742,26 +774,44 @@ static RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset
IntRect RenderView::selectionBounds(bool clipToVisibleContent) const
{
- typedef HashMap<RenderObject*, OwnPtr<RenderSelectionInfo>> SelectionMap;
+ LayoutRect selRect = subtreeSelectionBounds(*this, clipToVisibleContent);
+
+ if (hasRenderNamedFlowThreads()) {
+ for (auto* namedFlowThread : *m_flowThreadController->renderNamedFlowThreadList()) {
+ LayoutRect currRect = subtreeSelectionBounds(*namedFlowThread, clipToVisibleContent);
+ selRect.unite(currRect);
+ }
+ }
+
+ return snappedIntRect(selRect);
+}
+
+LayoutRect RenderView::subtreeSelectionBounds(const SelectionSubtreeRoot& root, bool clipToVisibleContent) const
+{
+ typedef HashMap<RenderObject*, std::unique_ptr<RenderSelectionInfo>> SelectionMap;
SelectionMap selectedObjects;
- RenderObject* os = m_selectionStart;
- RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
+ RenderObject* os = root.selectionData().selectionStart();
+ auto* selectionEnd = root.selectionData().selectionEnd();
+ RenderObject* stop = nullptr;
+ if (selectionEnd)
+ stop = rendererAfterPosition(selectionEnd, root.selectionData().selectionEndPos().value());
+ SelectionIterator selectionIterator(os);
while (os && os != stop) {
- if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
+ if ((os->canBeSelectionLeaf() || os == root.selectionData().selectionStart() || os == root.selectionData().selectionEnd()) && os->selectionState() != SelectionNone) {
// Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
- selectedObjects.set(os, adoptPtr(new RenderSelectionInfo(os, clipToVisibleContent)));
+ selectedObjects.set(os, std::make_unique<RenderSelectionInfo>(*os, clipToVisibleContent));
RenderBlock* cb = os->containingBlock();
- while (cb && !cb->isRenderView()) {
- OwnPtr<RenderSelectionInfo>& blockInfo = selectedObjects.add(cb, nullptr).iterator->value;
+ while (cb && !is<RenderView>(*cb)) {
+ std::unique_ptr<RenderSelectionInfo>& blockInfo = selectedObjects.add(cb, nullptr).iterator->value;
if (blockInfo)
break;
- blockInfo = adoptPtr(new RenderSelectionInfo(cb, clipToVisibleContent));
+ blockInfo = std::make_unique<RenderSelectionInfo>(*cb, clipToVisibleContent);
cb = cb->containingBlock();
}
}
- os = os->nextInPreOrder();
+ os = selectionIterator.next();
}
// Now create a single bounding box rect that encloses the whole selection.
@@ -777,149 +827,222 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const
}
selRect.unite(currRect);
}
- return pixelSnappedIntRect(selRect);
+ return selRect;
}
void RenderView::repaintSelection() const
{
+ repaintSubtreeSelection(*this);
+
+ if (hasRenderNamedFlowThreads()) {
+ for (auto* namedFlowThread : *m_flowThreadController->renderNamedFlowThreadList())
+ repaintSubtreeSelection(*namedFlowThread);
+ }
+}
+
+void RenderView::repaintSubtreeSelection(const SelectionSubtreeRoot& root) const
+{
HashSet<RenderBlock*> processedBlocks;
- RenderObject* end = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
- for (RenderObject* o = m_selectionStart; o && o != end; o = o->nextInPreOrder()) {
- if (!o->canBeSelectionLeaf() && o != m_selectionStart && o != m_selectionEnd)
+ auto* selectionEnd = root.selectionData().selectionEnd();
+ RenderObject* end = nullptr;
+ if (selectionEnd)
+ end = rendererAfterPosition(selectionEnd, root.selectionData().selectionEndPos().value());
+ SelectionIterator selectionIterator(root.selectionData().selectionStart());
+ for (RenderObject* o = selectionIterator.current(); o && o != end; o = selectionIterator.next()) {
+ if (!o->canBeSelectionLeaf() && o != root.selectionData().selectionStart() && o != root.selectionData().selectionEnd())
continue;
if (o->selectionState() == SelectionNone)
continue;
- RenderSelectionInfo(o, true).repaint();
+ RenderSelectionInfo(*o, true).repaint();
// Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
- for (RenderBlock* block = o->containingBlock(); block && !block->isRenderView(); block = block->containingBlock()) {
+ for (RenderBlock* block = o->containingBlock(); block && !is<RenderView>(*block); block = block->containingBlock()) {
if (!processedBlocks.add(block).isNewEntry)
break;
- RenderSelectionInfo(block, true).repaint();
+ RenderSelectionInfo(*block, true).repaint();
}
}
}
-#if USE(ACCELERATED_COMPOSITING)
-// Compositing layer dimensions take outline size into account, so we have to recompute layer
-// bounds when it changes.
-// FIXME: This is ugly; it would be nice to have a better way to do this.
-void RenderView::setMaximalOutlineSize(int o)
+void RenderView::setSelection(RenderObject* start, std::optional<unsigned> startPos, RenderObject* end, std::optional<unsigned> endPos, SelectionRepaintMode blockRepaintMode)
{
- if (o != m_maximalOutlineSize) {
- m_maximalOutlineSize = o;
+ // Make sure both our start and end objects are defined.
+ // Check www.msnbc.com and try clicking around to find the case where this happened.
+ if ((start && !end) || (end && !start))
+ return;
- // maximalOutlineSize affects compositing layer dimensions.
- compositor().setCompositingLayersNeedRebuild(); // FIXME: this really just needs to be a geometry update.
+ bool caretChanged = m_selectionWasCaret != frame().selection().isCaret();
+ m_selectionWasCaret = frame().selection().isCaret();
+ // Just return if the selection hasn't changed.
+ if (m_selectionUnsplitStart == start && m_selectionUnsplitStartPos == startPos
+ && m_selectionUnsplitEnd == end && m_selectionUnsplitEndPos == endPos && !caretChanged) {
+ return;
}
+
+#if ENABLE(SERVICE_CONTROLS)
+ // Clear the current rects and create a notifier for the new rects we are about to gather.
+ // The Notifier updates the Editor when it goes out of scope and is destroyed.
+ std::unique_ptr<SelectionRectGatherer::Notifier> rectNotifier = m_selectionRectGatherer.clearAndCreateNotifier();
+#endif // ENABLE(SERVICE_CONTROLS)
+ // Set global positions for new selection.
+ m_selectionUnsplitStart = start;
+ m_selectionUnsplitStartPos = startPos;
+ m_selectionUnsplitEnd = end;
+ m_selectionUnsplitEndPos = endPos;
+
+ // If there is no RenderNamedFlowThreads we follow the regular selection.
+ if (!hasRenderNamedFlowThreads()) {
+ RenderSubtreesMap singleSubtreeMap;
+ singleSubtreeMap.set(this, SelectionSubtreeData(start, startPos, end, endPos));
+ updateSelectionForSubtrees(singleSubtreeMap, blockRepaintMode);
+ return;
+ }
+
+ splitSelectionBetweenSubtrees(start, startPos, end, endPos, blockRepaintMode);
}
-#endif
-// When exploring the RenderTree looking for the nodes involved in the Selection, sometimes it's
-// required to change the traversing direction because the "start" position is below the "end" one.
-static inline RenderObject* getNextOrPrevRenderObjectBasedOnDirection(const RenderObject* o, const RenderObject* stop, bool& continueExploring, bool& exploringBackwards)
-{
- RenderObject* next;
- if (exploringBackwards) {
- next = o->previousInPreOrder();
- continueExploring = next && !(next)->isRenderView();
- } else {
- next = o->nextInPreOrder();
- continueExploring = next && next != stop;
- exploringBackwards = !next && (next != stop);
- if (exploringBackwards) {
- next = stop->previousInPreOrder();
- continueExploring = next && !next->isRenderView();
+void RenderView::splitSelectionBetweenSubtrees(const RenderObject* start, std::optional<unsigned> startPos, const RenderObject* end, std::optional<unsigned> endPos, SelectionRepaintMode blockRepaintMode)
+{
+ // Compute the visible selection end points for each of the subtrees.
+ RenderSubtreesMap renderSubtreesMap;
+
+ SelectionSubtreeData initialSelection;
+ renderSubtreesMap.set(this, initialSelection);
+ for (auto* namedFlowThread : *flowThreadController().renderNamedFlowThreadList())
+ renderSubtreesMap.set(namedFlowThread, initialSelection);
+
+ if (start && end) {
+ Node* startNode = start->node();
+ Node* endNode = end->node();
+ ASSERT(endNode);
+ Node* stopNode = NodeTraversal::nextSkippingChildren(*endNode);
+
+ for (Node* node = startNode; node != stopNode; node = NodeTraversal::next(*node)) {
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ continue;
+
+ SelectionSubtreeRoot& root = renderer->selectionRoot();
+ SelectionSubtreeData selectionData = renderSubtreesMap.get(&root);
+ if (selectionData.selectionClear()) {
+ selectionData.setSelectionStart(node->renderer());
+ selectionData.setSelectionStartPos(node == startNode ? startPos : std::optional<unsigned>(0));
+ }
+
+ selectionData.setSelectionEnd(node->renderer());
+ if (node == endNode)
+ selectionData.setSelectionEndPos(endPos);
+ else {
+ unsigned newEndPos = node->offsetInCharacters() ? node->maxCharacterOffset() : node->countChildNodes();
+ selectionData.setSelectionEndPos(newEndPos);
+ }
+
+ renderSubtreesMap.set(&root, selectionData);
}
}
-
- return next;
+
+ updateSelectionForSubtrees(renderSubtreesMap, blockRepaintMode);
}
-void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode blockRepaintMode)
+void RenderView::updateSelectionForSubtrees(RenderSubtreesMap& renderSubtreesMap, SelectionRepaintMode blockRepaintMode)
{
- // Make sure both our start and end objects are defined.
- // Check www.msnbc.com and try clicking around to find the case where this happened.
- if ((start && !end) || (end && !start))
- return;
+ SubtreeOldSelectionDataMap oldSelectionDataMap;
+ for (auto& subtreeSelectionInfo : renderSubtreesMap) {
+ SelectionSubtreeRoot& root = *subtreeSelectionInfo.key;
+ std::unique_ptr<OldSelectionData> oldSelectionData = std::make_unique<OldSelectionData>();
- bool caretChanged = m_selectionWasCaret != view().frame().selection().isCaret();
- m_selectionWasCaret = view().frame().selection().isCaret();
- // Just return if the selection hasn't changed.
- if (m_selectionStart == start && m_selectionStartPos == startPos &&
- m_selectionEnd == end && m_selectionEndPos == endPos && !caretChanged)
- return;
+ clearSubtreeSelection(root, blockRepaintMode, *oldSelectionData);
+ oldSelectionDataMap.set(&root, WTFMove(oldSelectionData));
- // Record the old selected objects. These will be used later
- // when we compare against the new selected objects.
- int oldStartPos = m_selectionStartPos;
- int oldEndPos = m_selectionEndPos;
+ root.setSelectionData(subtreeSelectionInfo.value);
+ if (hasRenderNamedFlowThreads())
+ root.adjustForVisibleSelection(document());
+ }
- // Objects each have a single selection rect to examine.
- typedef HashMap<RenderObject*, OwnPtr<RenderSelectionInfo>> SelectedObjectMap;
- SelectedObjectMap oldSelectedObjects;
- SelectedObjectMap newSelectedObjects;
+ // Update selection status for the objects inside the selection subtrees.
+ // This needs to be done after the previous loop updated the selectionStart/End
+ // parameters of all subtrees because we're going to be climbing up the containing
+ // block chain and we might end up in a different selection subtree.
+ for (const auto* subtreeSelectionRoot : renderSubtreesMap.keys()) {
+ OldSelectionData& oldSelectionData = *oldSelectionDataMap.get(subtreeSelectionRoot);
+ applySubtreeSelection(*subtreeSelectionRoot, blockRepaintMode, oldSelectionData);
+ }
+}
+static inline bool isValidObjectForNewSelection(const SelectionSubtreeRoot& root, const RenderObject& object)
+{
+ return (object.canBeSelectionLeaf() || &object == root.selectionData().selectionStart() || &object == root.selectionData().selectionEnd()) && object.selectionState() != RenderObject::SelectionNone && object.containingBlock();
+}
+
+void RenderView::clearSubtreeSelection(const SelectionSubtreeRoot& root, SelectionRepaintMode blockRepaintMode, OldSelectionData& oldSelectionData) const
+{
+ // Record the old selected objects. These will be used later
+ // when we compare against the new selected objects.
+ oldSelectionData.selectionStartPos = root.selectionData().selectionStartPos();
+ oldSelectionData.selectionEndPos = root.selectionData().selectionEndPos();
+
// Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks.
// In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise
// the union of those rects might remain the same even when changes have occurred.
- typedef HashMap<RenderBlock*, OwnPtr<RenderBlockSelectionInfo>> SelectedBlockMap;
- SelectedBlockMap oldSelectedBlocks;
- SelectedBlockMap newSelectedBlocks;
- RenderObject* os = m_selectionStart;
- RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
- bool exploringBackwards = false;
- bool continueExploring = os && (os != stop);
- while (continueExploring) {
- if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
+ RenderObject* os = root.selectionData().selectionStart();
+ auto* selectionEnd = root.selectionData().selectionEnd();
+ RenderObject* stop = nullptr;
+ if (selectionEnd)
+ stop = rendererAfterPosition(selectionEnd, root.selectionData().selectionEndPos().value());
+ SelectionIterator selectionIterator(os);
+ while (os && os != stop) {
+ if (isValidObjectForNewSelection(root, *os)) {
// Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
- oldSelectedObjects.set(os, adoptPtr(new RenderSelectionInfo(os, true)));
+ oldSelectionData.selectedObjects.set(os, std::make_unique<RenderSelectionInfo>(*os, true));
if (blockRepaintMode == RepaintNewXOROld) {
RenderBlock* cb = os->containingBlock();
- while (cb && !cb->isRenderView()) {
- OwnPtr<RenderBlockSelectionInfo>& blockInfo = oldSelectedBlocks.add(cb, nullptr).iterator->value;
+ while (cb && !is<RenderView>(*cb)) {
+ std::unique_ptr<RenderBlockSelectionInfo>& blockInfo = oldSelectionData.selectedBlocks.add(cb, nullptr).iterator->value;
if (blockInfo)
break;
- blockInfo = adoptPtr(new RenderBlockSelectionInfo(cb));
+ blockInfo = std::make_unique<RenderBlockSelectionInfo>(*cb);
cb = cb->containingBlock();
}
}
}
- os = getNextOrPrevRenderObjectBasedOnDirection(os, stop, continueExploring, exploringBackwards);
+ os = selectionIterator.next();
}
- // Now clear the selection.
- SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end();
- for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i)
- i->key->setSelectionStateIfNeeded(SelectionNone);
-
- // set selection start and end
- m_selectionStart = start;
- m_selectionStartPos = startPos;
- m_selectionEnd = end;
- m_selectionEndPos = endPos;
+ for (auto* selectedObject : oldSelectionData.selectedObjects.keys())
+ selectedObject->setSelectionStateIfNeeded(SelectionNone);
+}
- // Update the selection status of all objects between m_selectionStart and m_selectionEnd
- if (start && start == end)
- start->setSelectionStateIfNeeded(SelectionBoth);
+void RenderView::applySubtreeSelection(const SelectionSubtreeRoot& root, SelectionRepaintMode blockRepaintMode, const OldSelectionData& oldSelectionData)
+{
+ // Update the selection status of all objects between selectionStart and selectionEnd
+ if (root.selectionData().selectionStart() && root.selectionData().selectionStart() == root.selectionData().selectionEnd())
+ root.selectionData().selectionStart()->setSelectionStateIfNeeded(SelectionBoth);
else {
- if (start)
- start->setSelectionStateIfNeeded(SelectionStart);
- if (end)
- end->setSelectionStateIfNeeded(SelectionEnd);
+ if (root.selectionData().selectionStart())
+ root.selectionData().selectionStart()->setSelectionStateIfNeeded(SelectionStart);
+ if (root.selectionData().selectionEnd())
+ root.selectionData().selectionEnd()->setSelectionStateIfNeeded(SelectionEnd);
}
- RenderObject* o = start;
- stop = rendererAfterPosition(end, endPos);
-
- while (o && o != stop) {
- if (o != start && o != end && o->canBeSelectionLeaf())
- o->setSelectionStateIfNeeded(SelectionInside);
- o = o->nextInPreOrder();
+ RenderObject* selectionStart = root.selectionData().selectionStart();
+ auto* selectionDataEnd = root.selectionData().selectionEnd();
+ RenderObject* selectionEnd = nullptr;
+ if (selectionDataEnd)
+ selectionEnd = rendererAfterPosition(selectionDataEnd, root.selectionData().selectionEndPos().value());
+ SelectionIterator selectionIterator(selectionStart);
+ for (RenderObject* currentRenderer = selectionStart; currentRenderer && currentRenderer != selectionEnd; currentRenderer = selectionIterator.next()) {
+ if (currentRenderer == root.selectionData().selectionStart() || currentRenderer == root.selectionData().selectionEnd())
+ continue;
+ if (!currentRenderer->canBeSelectionLeaf())
+ continue;
+ // FIXME: Move this logic to SelectionIterator::next()
+ if (&currentRenderer->selectionRoot() != &root)
+ continue;
+ currentRenderer->setSelectionStateIfNeeded(SelectionInside);
}
if (blockRepaintMode != RepaintNothing)
@@ -927,36 +1050,48 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
// Now that the selection state has been updated for the new objects, walk them again and
// put them in the new objects list.
- o = start;
- exploringBackwards = false;
- continueExploring = o && (o != stop);
- while (continueExploring) {
- if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) {
- newSelectedObjects.set(o, adoptPtr(new RenderSelectionInfo(o, true)));
- RenderBlock* cb = o->containingBlock();
- while (cb && !cb->isRenderView()) {
- OwnPtr<RenderBlockSelectionInfo>& blockInfo = newSelectedBlocks.add(cb, nullptr).iterator->value;
+ SelectedObjectMap newSelectedObjects;
+ SelectedBlockMap newSelectedBlocks;
+ selectionIterator = SelectionIterator(selectionStart);
+ for (RenderObject* currentRenderer = selectionStart; currentRenderer && currentRenderer != selectionEnd; currentRenderer = selectionIterator.next()) {
+ if (isValidObjectForNewSelection(root, *currentRenderer)) {
+ std::unique_ptr<RenderSelectionInfo> selectionInfo = std::make_unique<RenderSelectionInfo>(*currentRenderer, true);
+
+#if ENABLE(SERVICE_CONTROLS)
+ for (auto& rect : selectionInfo->collectedSelectionRects())
+ m_selectionRectGatherer.addRect(selectionInfo->repaintContainer(), rect);
+ if (!currentRenderer->isTextOrLineBreak())
+ m_selectionRectGatherer.setTextOnly(false);
+#endif
+
+ newSelectedObjects.set(currentRenderer, WTFMove(selectionInfo));
+
+ RenderBlock* containingBlock = currentRenderer->containingBlock();
+ while (containingBlock && !is<RenderView>(*containingBlock)) {
+ std::unique_ptr<RenderBlockSelectionInfo>& blockInfo = newSelectedBlocks.add(containingBlock, nullptr).iterator->value;
if (blockInfo)
break;
- blockInfo = adoptPtr(new RenderBlockSelectionInfo(cb));
- cb = cb->containingBlock();
+ blockInfo = std::make_unique<RenderBlockSelectionInfo>(*containingBlock);
+ containingBlock = containingBlock->containingBlock();
+
+#if ENABLE(SERVICE_CONTROLS)
+ m_selectionRectGatherer.addGapRects(blockInfo->repaintContainer(), blockInfo->rects());
+#endif
}
}
-
- o = getNextOrPrevRenderObjectBasedOnDirection(o, stop, continueExploring, exploringBackwards);
}
if (blockRepaintMode == RepaintNothing)
return;
// Have any of the old selected objects changed compared to the new selection?
- for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) {
- RenderObject* obj = i->key;
+ for (const auto& selectedObjectInfo : oldSelectionData.selectedObjects) {
+ RenderObject* obj = selectedObjectInfo.key;
RenderSelectionInfo* newInfo = newSelectedObjects.get(obj);
- RenderSelectionInfo* oldInfo = i->value.get();
- if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() ||
- (m_selectionStart == obj && oldStartPos != m_selectionStartPos) ||
- (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) {
+ RenderSelectionInfo* oldInfo = selectedObjectInfo.value.get();
+ if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state()
+ || (root.selectionData().selectionStart() == obj && oldSelectionData.selectionStartPos != root.selectionData().selectionStartPos())
+ || (root.selectionData().selectionEnd() == obj && oldSelectionData.selectionEndPos != root.selectionData().selectionEndPos())) {
oldInfo->repaint();
if (newInfo) {
newInfo->repaint();
@@ -966,16 +1101,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
}
// Any new objects that remain were not found in the old objects dict, and so they need to be updated.
- SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end();
- for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i)
- i->value->repaint();
+ for (const auto& selectedObjectInfo : newSelectedObjects)
+ selectedObjectInfo.value->repaint();
// Have any of the old blocks changed?
- SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end();
- for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) {
- RenderBlock* block = i->key;
+ for (const auto& selectedBlockInfo : oldSelectionData.selectedBlocks) {
+ const RenderBlock* block = selectedBlockInfo.key;
RenderBlockSelectionInfo* newInfo = newSelectedBlocks.get(block);
- RenderBlockSelectionInfo* oldInfo = i->value.get();
+ RenderBlockSelectionInfo* oldInfo = selectedBlockInfo.value.get();
if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) {
oldInfo->repaint();
if (newInfo) {
@@ -986,29 +1119,22 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e
}
// Any new blocks that remain were not found in the old blocks dict, and so they need to be updated.
- SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end();
- for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i)
- i->value->repaint();
+ for (const auto& selectedBlockInfo : newSelectedBlocks)
+ selectedBlockInfo.value->repaint();
}
-void RenderView::getSelection(RenderObject*& startRenderer, int& startOffset, RenderObject*& endRenderer, int& endOffset) const
+void RenderView::getSelection(RenderObject*& startRenderer, std::optional<unsigned>& startOffset, RenderObject*& endRenderer, std::optional<unsigned>& endOffset) const
{
- startRenderer = m_selectionStart;
- startOffset = m_selectionStartPos;
- endRenderer = m_selectionEnd;
- endOffset = m_selectionEndPos;
+ startRenderer = m_selectionUnsplitStart;
+ startOffset = m_selectionUnsplitStartPos;
+ endRenderer = m_selectionUnsplitEnd;
+ endOffset = m_selectionUnsplitEndPos;
}
void RenderView::clearSelection()
{
layer()->repaintBlockSelectionGaps();
- setSelection(nullptr, -1, nullptr, -1, RepaintNewMinusOld);
-}
-
-void RenderView::selectionStartEnd(int& startPos, int& endPos) const
-{
- startPos = m_selectionStartPos;
- endPos = m_selectionEndPos;
+ setSelection(nullptr, std::nullopt, nullptr, std::nullopt, RepaintNewMinusOld);
}
bool RenderView::printing() const
@@ -1034,30 +1160,29 @@ IntRect RenderView::unscaledDocumentRect() const
{
LayoutRect overflowRect(layoutOverflowRect());
flipForWritingMode(overflowRect);
- return pixelSnappedIntRect(overflowRect);
+ return snappedIntRect(overflowRect);
}
bool RenderView::rootBackgroundIsEntirelyFixed() const
{
- RenderElement* rootObject = document().documentElement() ? document().documentElement()->renderer() : 0;
- if (!rootObject)
- return false;
-
- return rootObject->rendererForRootBackground().hasEntirelyFixedBackground();
+ if (auto* rootBackgroundRenderer = rendererForRootBackground())
+ return rootBackgroundRenderer->style().hasEntirelyFixedBackground();
+ return false;
}
-
-LayoutRect RenderView::backgroundRect(RenderBox* backgroundRenderer) const
+
+LayoutRect RenderView::unextendedBackgroundRect() const
{
- if (!hasColumns())
- return frameView().hasExtendedBackground() ? frameView().extendedBackgroundRect() : unscaledDocumentRect();
-
- ColumnInfo* columnInfo = this->columnInfo();
- LayoutRect backgroundRect(0, 0, columnInfo->desiredColumnWidth(), columnInfo->columnHeight() * columnInfo->columnCount());
- if (!isHorizontalWritingMode())
- backgroundRect = backgroundRect.transposedRect();
- backgroundRenderer->flipForWritingMode(backgroundRect);
+ // FIXME: What is this? Need to patch for new columns?
+ return unscaledDocumentRect();
+}
+
+LayoutRect RenderView::backgroundRect() const
+{
+ // FIXME: New columns care about this?
+ if (frameView().hasExtendedBackgroundRectForPainting())
+ return frameView().extendedBackgroundRectForPainting();
- return backgroundRect;
+ return unextendedBackgroundRect();
}
IntRect RenderView::documentRect() const
@@ -1104,24 +1229,22 @@ void RenderView::pushLayoutState(RenderObject& root)
ASSERT(m_layoutStateDisableCount == 0);
ASSERT(m_layoutState == 0);
- pushLayoutStateForCurrentFlowThread(root);
m_layoutState = std::make_unique<LayoutState>(root);
+ pushLayoutStateForCurrentFlowThread(root);
}
-bool RenderView::shouldDisableLayoutStateForSubtree(RenderObject* renderer) const
+void RenderView::pushLayoutStateForPagination(RenderBlockFlow& layoutRoot)
{
- RenderObject* o = renderer;
- while (o) {
- if (o->hasColumns() || o->hasTransform() || o->hasReflection())
- return true;
- o = o->container();
- }
- return false;
+ pushLayoutState(layoutRoot);
+ ASSERT(m_layoutState);
+ m_layoutState->m_isPaginated = true;
+ // This is just a flag for known page height (see RenderBlockFlow::checkForPaginationLogicalHeightChange).
+ m_layoutState->m_pageLogicalHeight = 1;
}
-IntSize RenderView::viewportSize() const
+IntSize RenderView::viewportSizeForCSSViewportUnits() const
{
- return frameView().visibleContentRectIncludingScrollbars(ScrollableArea::LegacyIOSDocumentVisibleRect).size();
+ return frameView().viewportSizeForCSSViewportUnits();
}
void RenderView::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
@@ -1129,6 +1252,9 @@ void RenderView::updateHitTestResult(HitTestResult& result, const LayoutPoint& p
if (result.innerNode())
return;
+ if (multiColumnFlowThread() && multiColumnFlowThread()->firstMultiColumnSet())
+ return multiColumnFlowThread()->firstMultiColumnSet()->updateHitTestResult(result, point);
+
Node* node = document().documentElement();
if (node) {
result.setInnerNode(node);
@@ -1160,14 +1286,13 @@ void RenderView::setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bo
}
// Prefer the widest object that tries to move the pagination point
- IntRect boundingBox = forRenderer->borderBoundingBox();
+ LayoutRect boundingBox = forRenderer->borderBoundingBox();
if (boundingBox.width() > m_legacyPrinting.m_truncatorWidth) {
m_legacyPrinting.m_truncatorWidth = boundingBox.width();
m_legacyPrinting.m_bestTruncatedAt = y;
}
}
-#if USE(ACCELERATED_COMPOSITING)
bool RenderView::usesCompositing() const
{
return m_compositor && m_compositor->inCompositingMode();
@@ -1176,20 +1301,15 @@ bool RenderView::usesCompositing() const
RenderLayerCompositor& RenderView::compositor()
{
if (!m_compositor)
- m_compositor = adoptPtr(new RenderLayerCompositor(*this));
+ m_compositor = std::make_unique<RenderLayerCompositor>(*this);
return *m_compositor;
}
-#endif
void RenderView::setIsInWindow(bool isInWindow)
{
-#if USE(ACCELERATED_COMPOSITING)
if (m_compositor)
m_compositor->setIsInWindow(isInWindow);
-#else
- UNUSED_PARAM(isInWindow);
-#endif
}
void RenderView::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
@@ -1197,6 +1317,8 @@ void RenderView::styleDidChange(StyleDifference diff, const RenderStyle* oldStyl
RenderBlockFlow::styleDidChange(diff, oldStyle);
if (hasRenderNamedFlowThreads())
flowThreadController().styleDidChange();
+
+ frameView().styleDidChange();
}
bool RenderView::hasRenderNamedFlowThreads() const
@@ -1212,38 +1334,22 @@ bool RenderView::checkTwoPassLayoutForAutoHeightRegions() const
FlowThreadController& RenderView::flowThreadController()
{
if (!m_flowThreadController)
- m_flowThreadController = FlowThreadController::create(this);
+ m_flowThreadController = std::make_unique<FlowThreadController>(this);
return *m_flowThreadController;
}
-#if PLATFORM(IOS)
-static bool isFixedPositionInViewport(const RenderObject& renderer, const RenderObject* container)
-{
- return (renderer.style().position() == FixedPosition) && renderer.container() == container;
-}
-
-bool RenderView::hasCustomFixedPosition(const RenderObject& renderer, ContainingBlockCheck checkContainer) const
-{
- if (!frameView().useCustomFixedPositionLayoutRect())
- return false;
-
- if (checkContainer == CheckContainingBlock)
- return isFixedPositionInViewport(renderer, this);
-
- return renderer.style().position() == FixedPosition;
-}
-#endif
-
void RenderView::pushLayoutStateForCurrentFlowThread(const RenderObject& object)
{
if (!m_flowThreadController)
return;
- RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread();
+ RenderFlowThread* currentFlowThread = object.flowThreadContainingBlock();
if (!currentFlowThread)
return;
+ m_layoutState->setCurrentRenderFlowThread(currentFlowThread);
+
currentFlowThread->pushFlowThreadLayoutState(object);
}
@@ -1252,32 +1358,80 @@ void RenderView::popLayoutStateForCurrentFlowThread()
if (!m_flowThreadController)
return;
- RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread();
+ RenderFlowThread* currentFlowThread = m_layoutState->currentRenderFlowThread();
if (!currentFlowThread)
return;
currentFlowThread->popFlowThreadLayoutState();
}
-IntervalArena* RenderView::intervalArena()
-{
- if (!m_intervalArena)
- m_intervalArena = IntervalArena::create();
- return m_intervalArena.get();
-}
-
ImageQualityController& RenderView::imageQualityController()
{
if (!m_imageQualityController)
- m_imageQualityController = ImageQualityController::create(*this);
+ m_imageQualityController = std::make_unique<ImageQualityController>(*this);
return *m_imageQualityController;
}
+void RenderView::registerForVisibleInViewportCallback(RenderElement& renderer)
+{
+ ASSERT(!m_visibleInViewportRenderers.contains(&renderer));
+ m_visibleInViewportRenderers.add(&renderer);
+}
+
+void RenderView::unregisterForVisibleInViewportCallback(RenderElement& renderer)
+{
+ ASSERT(m_visibleInViewportRenderers.contains(&renderer));
+ m_visibleInViewportRenderers.remove(&renderer);
+}
+
+void RenderView::updateVisibleViewportRect(const IntRect& visibleRect)
+{
+ resumePausedImageAnimationsIfNeeded(visibleRect);
+
+ for (auto* renderer : m_visibleInViewportRenderers)
+ renderer->visibleInViewportStateChanged(visibleRect.intersects(enclosingIntRect(renderer->absoluteClippedOverflowRect())) ? RenderElement::VisibleInViewport : RenderElement::NotVisibleInViewport);
+}
+
+void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer)
+{
+ if (renderer.hasPausedImageAnimations()) {
+ ASSERT(m_renderersWithPausedImageAnimation.contains(&renderer));
+ return;
+ }
+ renderer.setHasPausedImageAnimations(true);
+ m_renderersWithPausedImageAnimation.add(&renderer);
+}
+
+void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer)
+{
+ ASSERT(renderer.hasPausedImageAnimations());
+ ASSERT(m_renderersWithPausedImageAnimation.contains(&renderer));
+
+ renderer.setHasPausedImageAnimations(false);
+ m_renderersWithPausedImageAnimation.remove(&renderer);
+}
+
+void RenderView::resumePausedImageAnimationsIfNeeded(IntRect visibleRect)
+{
+ Vector<RenderElement*, 10> toRemove;
+ for (auto* renderer : m_renderersWithPausedImageAnimation) {
+ if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect))
+ toRemove.append(renderer);
+ }
+ for (auto& renderer : toRemove)
+ removeRendererWithPausedImageAnimations(*renderer);
+}
+
RenderView::RepaintRegionAccumulator::RepaintRegionAccumulator(RenderView* view)
- : m_rootView(view ? view->document().topDocument().renderView() : nullptr)
{
- if (!m_rootView)
+ if (!view)
+ return;
+
+ auto* rootRenderView = view->document().topDocument().renderView();
+ if (!rootRenderView)
return;
+
+ m_rootView = rootRenderView->createWeakPtr();
m_wasAccumulatingRepaintRegion = !!m_rootView->m_accumulatedRepaintRegion;
if (!m_wasAccumulatingRepaintRegion)
m_rootView->m_accumulatedRepaintRegion = std::make_unique<Region>();
@@ -1292,4 +1446,54 @@ RenderView::RepaintRegionAccumulator::~RepaintRegionAccumulator()
m_rootView->flushAccumulatedRepaintRegion();
}
+unsigned RenderView::pageNumberForBlockProgressionOffset(int offset) const
+{
+ int columnNumber = 0;
+ const Pagination& pagination = page().pagination();
+ if (pagination.mode == Pagination::Unpaginated)
+ return columnNumber;
+
+ bool progressionIsInline = false;
+ bool progressionIsReversed = false;
+
+ if (multiColumnFlowThread()) {
+ progressionIsInline = multiColumnFlowThread()->progressionIsInline();
+ progressionIsReversed = multiColumnFlowThread()->progressionIsReversed();
+ } else
+ return columnNumber;
+
+ if (!progressionIsInline) {
+ if (!progressionIsReversed)
+ columnNumber = (pagination.pageLength + pagination.gap - offset) / (pagination.pageLength + pagination.gap);
+ else
+ columnNumber = offset / (pagination.pageLength + pagination.gap);
+ }
+
+ return columnNumber;
+}
+
+unsigned RenderView::pageCount() const
+{
+ const Pagination& pagination = page().pagination();
+ if (pagination.mode == Pagination::Unpaginated)
+ return 0;
+
+ if (multiColumnFlowThread() && multiColumnFlowThread()->firstMultiColumnSet())
+ return multiColumnFlowThread()->firstMultiColumnSet()->columnCount();
+
+ return 0;
+}
+
+#if ENABLE(CSS_SCROLL_SNAP)
+void RenderView::registerBoxWithScrollSnapPositions(const RenderBox& box)
+{
+ m_boxesWithScrollSnapPositions.add(&box);
+}
+
+void RenderView::unregisterBoxWithScrollSnapPositions(const RenderBox& box)
+{
+ m_boxesWithScrollSnapPositions.remove(&box);
+}
+#endif
+
} // namespace WebCore