LayoutState::LayoutState(RenderObject& root) : m_clipped(false) , m_isPaginated(false) , m_pageLogicalHeightChanged(false) #if !ASSERT_DISABLED && ENABLE(SATURATED_LAYOUT_ARITHMETIC) , m_layoutDeltaXSaturated(false) , m_layoutDeltaYSaturated(false) #endif , m_lineGrid(0) , m_pageLogicalHeight(0) #ifndef NDEBUG , m_renderer(&root) #endif { RenderElement* container = root.container(); FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms); m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y()); if (container->hasOverflowClip()) { m_clipped = true; RenderBox* containerBox = toRenderBox(container); m_clipRect = LayoutRect(toLayoutPoint(m_paintOffset), containerBox->cachedSizeForOverflowClip()); m_paintOffset -= containerBox->scrolledContentOffset(); } }
LayoutPoint LayoutMultiColumnFlowThread::visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const { LayoutUnit blockOffset = isHorizontalWritingMode() ? visualPoint.y() : visualPoint.x(); const LayoutMultiColumnSet* columnSet = nullptr; for (const LayoutMultiColumnSet* candidate = firstMultiColumnSet(); candidate; candidate = candidate->nextSiblingMultiColumnSet()) { columnSet = candidate; if (candidate->logicalBottom() > blockOffset) break; } return columnSet ? columnSet->visualPointToFlowThreadPoint(toLayoutPoint(visualPoint + location() - columnSet->location())) : visualPoint; }
LayoutState::LayoutState(RenderObject& root) : m_clipped(false) , m_isPaginated(false) , m_pageLogicalHeightChanged(false) #if !ASSERT_DISABLED , m_layoutDeltaXSaturated(false) , m_layoutDeltaYSaturated(false) #endif #ifndef NDEBUG , m_renderer(&root) #endif { if (RenderElement* container = root.container()) { FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms); m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y()); if (container->hasOverflowClip()) { m_clipped = true; auto& containerBox = downcast<RenderBox>(*container); m_clipRect = LayoutRect(toLayoutPoint(m_paintOffset), containerBox.cachedSizeForOverflowClip()); m_paintOffset -= containerBox.scrolledContentOffset(); } } }
void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction) { ASSERT(isMainThread()); if (!m_page) return; FrameView* frameView = m_page->mainFrame().view(); if (!frameView) return; // Main frame. if (scrollingNodeID == frameView->scrollLayerID()) { bool oldProgrammaticScroll = frameView->inProgrammaticScroll(); frameView->setInProgrammaticScroll(programmaticScroll); frameView->setConstrainsScrollingToContentEdge(false); frameView->notifyScrollPositionChanged(roundedIntPoint(scrollPosition)); frameView->setConstrainsScrollingToContentEdge(true); frameView->setInProgrammaticScroll(oldProgrammaticScroll); if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) { GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView); GraphicsLayer* insetClipLayer = insetClipLayerForFrameView(frameView); GraphicsLayer* contentShadowLayer = contentShadowLayerForFrameView(frameView); GraphicsLayer* scrolledContentsLayer = rootContentLayerForFrameView(frameView); GraphicsLayer* headerLayer = headerLayerForFrameView(frameView); GraphicsLayer* footerLayer = footerLayerForFrameView(frameView); LayoutSize scrollOffsetForFixed = frameView->scrollOffsetForFixedPosition(); float topContentInset = frameView->topContentInset(); FloatPoint positionForInsetClipLayer = FloatPoint(0, FrameView::yPositionForInsetClipLayer(scrollPosition, topContentInset)); FloatPoint positionForContentsLayer = FloatPoint(scrolledContentsLayer->position().x(), FrameView::yPositionForRootContentLayer(scrollPosition, topContentInset, frameView->headerHeight())); FloatPoint positionForHeaderLayer = FloatPoint(scrollOffsetForFixed.width(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset)); FloatPoint positionForFooterLayer = FloatPoint(scrollOffsetForFixed.width(), FrameView::yPositionForFooterLayer(scrollPosition, topContentInset, frameView->totalContentsSize().height(), frameView->footerHeight())); if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) { scrollLayer->setPosition(-frameView->scrollPosition()); if (counterScrollingLayer) counterScrollingLayer->setPosition(toLayoutPoint(scrollOffsetForFixed)); if (insetClipLayer) insetClipLayer->setPosition(positionForInsetClipLayer); if (contentShadowLayer) contentShadowLayer->setPosition(positionForContentsLayer); if (scrolledContentsLayer) scrolledContentsLayer->setPosition(positionForContentsLayer); if (headerLayer) headerLayer->setPosition(positionForHeaderLayer); if (footerLayer) footerLayer->setPosition(positionForFooterLayer); } else { scrollLayer->syncPosition(-frameView->scrollPosition()); if (counterScrollingLayer) counterScrollingLayer->syncPosition(toLayoutPoint(scrollOffsetForFixed)); if (insetClipLayer) insetClipLayer->syncPosition(positionForInsetClipLayer); if (contentShadowLayer) contentShadowLayer->syncPosition(positionForContentsLayer); if (scrolledContentsLayer) scrolledContentsLayer->syncPosition(positionForContentsLayer); if (headerLayer) headerLayer->syncPosition(positionForHeaderLayer); if (footerLayer) footerLayer->syncPosition(positionForFooterLayer); LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect(); syncChildPositions(viewportRect); } } return; } // Overflow-scroll area. if (ScrollableArea* scrollableArea = frameView->scrollableAreaForScrollLayerID(scrollingNodeID)) { scrollableArea->setIsUserScroll(scrollingLayerPositionAction == SyncScrollingLayerPosition); scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition); scrollableArea->setIsUserScroll(false); } }
void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, bool programmaticScroll, SetOrSyncScrollingLayerPosition scrollingLayerPositionAction) { ASSERT(isMainThread()); if (!m_page) return; FrameView* frameViewPtr = frameViewForScrollingNode(scrollingNodeID); if (!frameViewPtr) return; FrameView& frameView = *frameViewPtr; if (scrollingNodeID == frameView.scrollLayerID()) { bool oldProgrammaticScroll = frameView.inProgrammaticScroll(); frameView.setInProgrammaticScroll(programmaticScroll); frameView.setConstrainsScrollingToContentEdge(false); frameView.notifyScrollPositionChanged(roundedIntPoint(scrollPosition)); frameView.setConstrainsScrollingToContentEdge(true); frameView.setInProgrammaticScroll(oldProgrammaticScroll); if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView)) { GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView); GraphicsLayer* insetClipLayer = insetClipLayerForFrameView(frameView); GraphicsLayer* contentShadowLayer = contentShadowLayerForFrameView(frameView); GraphicsLayer* scrolledContentsLayer = rootContentLayerForFrameView(frameView); GraphicsLayer* headerLayer = headerLayerForFrameView(frameView); GraphicsLayer* footerLayer = footerLayerForFrameView(frameView); LayoutSize scrollOffsetForFixed = frameView.scrollOffsetForFixedPosition(); float topContentInset = frameView.topContentInset(); FloatPoint positionForInsetClipLayer = FloatPoint(0, FrameView::yPositionForInsetClipLayer(scrollPosition, topContentInset)); FloatPoint positionForContentsLayer = FloatPoint(scrolledContentsLayer->position().x(), FrameView::yPositionForRootContentLayer(scrollPosition, topContentInset, frameView.headerHeight())); FloatPoint positionForHeaderLayer = FloatPoint(scrollOffsetForFixed.width(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset)); FloatPoint positionForFooterLayer = FloatPoint(scrollOffsetForFixed.width(), FrameView::yPositionForFooterLayer(scrollPosition, topContentInset, frameView.totalContentsSize().height(), frameView.footerHeight())); if (programmaticScroll || scrollingLayerPositionAction == SetScrollingLayerPosition) { scrollLayer->setPosition(-frameView.scrollPosition()); if (counterScrollingLayer) counterScrollingLayer->setPosition(toLayoutPoint(scrollOffsetForFixed)); if (insetClipLayer) insetClipLayer->setPosition(positionForInsetClipLayer); if (contentShadowLayer) contentShadowLayer->setPosition(positionForContentsLayer); if (scrolledContentsLayer) scrolledContentsLayer->setPosition(positionForContentsLayer); if (headerLayer) headerLayer->setPosition(positionForHeaderLayer); if (footerLayer) footerLayer->setPosition(positionForFooterLayer); } else { scrollLayer->syncPosition(-frameView.scrollPosition()); if (counterScrollingLayer) counterScrollingLayer->syncPosition(toLayoutPoint(scrollOffsetForFixed)); if (insetClipLayer) insetClipLayer->syncPosition(positionForInsetClipLayer); if (contentShadowLayer) contentShadowLayer->syncPosition(positionForContentsLayer); if (scrolledContentsLayer) scrolledContentsLayer->syncPosition(positionForContentsLayer); if (headerLayer) headerLayer->syncPosition(positionForHeaderLayer); if (footerLayer) footerLayer->syncPosition(positionForFooterLayer); LayoutRect viewportRect = frameView.viewportConstrainedVisibleContentRect(); syncChildPositions(viewportRect); } } #if PLATFORM(COCOA) if (m_page->expectsWheelEventTriggers()) { frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); if (const auto& trigger = m_page->testTrigger()) trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); } #endif return; } // Overflow-scroll area. if (ScrollableArea* scrollableArea = frameView.scrollableAreaForScrollLayerID(scrollingNodeID)) { scrollableArea->setIsUserScroll(scrollingLayerPositionAction == SyncScrollingLayerPosition); scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition); scrollableArea->setIsUserScroll(false); if (scrollingLayerPositionAction == SetScrollingLayerPosition) m_page->editorClient().overflowScrollPositionChanged(); #if PLATFORM(COCOA) if (m_page->expectsWheelEventTriggers()) { frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); if (const auto& trigger = m_page->testTrigger()) trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); } #endif } }
LayoutState::LayoutState(std::unique_ptr<LayoutState> next, RenderBox* renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged) : m_next(WTFMove(next)) #ifndef NDEBUG , m_renderer(renderer) #endif { ASSERT(m_next); bool fixed = renderer->isOutOfFlowPositioned() && renderer->style().position() == FixedPosition; if (fixed) { // FIXME: This doesn't work correctly with transforms. FloatPoint fixedOffset = renderer->view().localToAbsolute(FloatPoint(), IsFixed); m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset; } else m_paintOffset = m_next->m_paintOffset + offset; if (renderer->isOutOfFlowPositioned() && !fixed) { if (RenderElement* container = renderer->container()) { if (container->isInFlowPositioned() && is<RenderInline>(*container)) m_paintOffset += downcast<RenderInline>(*container).offsetForInFlowPositionedInline(renderer); } } m_layoutOffset = m_paintOffset; if (renderer->isInFlowPositioned() && renderer->hasLayer()) m_paintOffset += renderer->layer()->offsetForInFlowPosition(); m_clipped = !fixed && m_next->m_clipped; if (m_clipped) m_clipRect = m_next->m_clipRect; if (renderer->hasOverflowClip()) { LayoutRect clipRect(toLayoutPoint(m_paintOffset) + renderer->view().layoutDelta(), renderer->cachedSizeForOverflowClip()); if (m_clipped) m_clipRect.intersect(clipRect); else { m_clipRect = clipRect; m_clipped = true; } m_paintOffset -= renderer->scrolledContentOffset(); } // If we establish a new page height, then cache the offset to the top of the first page. // We can compare this later on to figure out what part of the page we're actually on, if (pageLogicalHeight || renderer->isRenderFlowThread()) { m_pageLogicalHeight = pageLogicalHeight; bool isFlipped = renderer->style().isFlippedBlocksWritingMode(); m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? renderer->borderLeft() + renderer->paddingLeft() : renderer->borderRight() + renderer->paddingRight()), m_layoutOffset.height() + (!isFlipped ? renderer->borderTop() + renderer->paddingTop() : renderer->borderBottom() + renderer->paddingBottom())); m_pageLogicalHeightChanged = pageLogicalHeightChanged; m_isPaginated = true; } else { // If we don't establish a new page height, then propagate the old page height and offset down. m_pageLogicalHeight = m_next->m_pageLogicalHeight; m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged; m_pageOffset = m_next->m_pageOffset; // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and // writing mode roots. if (renderer->isUnsplittableForPagination()) { m_pageLogicalHeight = 0; m_isPaginated = false; } else m_isPaginated = m_pageLogicalHeight || renderer->flowThreadContainingBlock(); } // Propagate line grid information. propagateLineGridInfo(renderer); m_layoutDelta = m_next->m_layoutDelta; #if !ASSERT_DISABLED m_layoutDeltaXSaturated = m_next->m_layoutDeltaXSaturated; m_layoutDeltaYSaturated = m_next->m_layoutDeltaYSaturated; #endif if (lineGrid() && (lineGrid()->style().writingMode() == renderer->style().writingMode()) && is<RenderMultiColumnFlowThread>(*renderer)) downcast<RenderMultiColumnFlowThread>(*renderer).computeLineGridPaginationOrigin(*this); // If we have a new grid to track, then add it to our set. if (renderer->style().lineGrid() != RenderStyle::initialLineGrid() && is<RenderBlockFlow>(*renderer)) establishLineGrid(downcast<RenderBlockFlow>(renderer)); // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. }
void MultiColumnFragmentainerGroup::collectLayerFragments(PaintLayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) const { // |layerBoundingBox| is in the flow thread coordinate space, relative to the top/left edge of // the flow thread, but note that it has been converted with respect to writing mode (so that // it's visual/physical in that sense). // // |dirtyRect| is visual, relative to the multicol container. // // Then there's the output from this method - the stuff we put into the list of fragments. The // fragment.paginationOffset point is the actual visual translation required to get from a // location in the flow thread to a location in a given column. The fragment.paginationClip // rectangle, on the other hand, is in flow thread coordinates, but otherwise completely // physical in terms of writing mode. LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in // a layoutObject, most rectangles are represented this way. LayoutRect layerBoundsInFlowThread(layerBoundingBox); flowThread->flipForWritingMode(layerBoundsInFlowThread); // Now we can compare with the flow thread portions owned by each column. First let's // see if the rect intersects our flow thread portion at all. LayoutRect clippedRect(layerBoundsInFlowThread); clippedRect.intersect(m_columnSet.flowThreadPortionOverflowRect()); if (clippedRect.isEmpty()) return; // Now we know we intersect at least one column. Let's figure out the logical top and logical // bottom of the area we're checking. LayoutUnit layerLogicalTop = isHorizontalWritingMode ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x(); LayoutUnit layerLogicalBottom = (isHorizontalWritingMode ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()); // Figure out the start and end columns for the layer and only check within that range so that // we don't walk the entire column row. unsigned startColumn; unsigned endColumn; columnIntervalForBlockRangeInFlowThread(layerLogicalTop, layerLogicalBottom, startColumn, endColumn); // Now intersect with the columns actually occupied by the dirty rect, to narrow it down even further. unsigned firstColumnInDirtyRect, lastColumnInDirtyRect; columnIntervalForVisualRect(dirtyRect, firstColumnInDirtyRect, lastColumnInDirtyRect); if (firstColumnInDirtyRect > endColumn || lastColumnInDirtyRect < startColumn) return; // The two column intervals are disjoint. There's nothing to collect. if (startColumn < firstColumnInDirtyRect) startColumn = firstColumnInDirtyRect; if (endColumn > lastColumnInDirtyRect) endColumn = lastColumnInDirtyRect; ASSERT(endColumn >= startColumn); for (unsigned i = startColumn; i <= endColumn; i++) { PaintLayerFragment fragment; // Set the physical translation offset. fragment.paginationOffset = toLayoutPoint(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(i))); // Set the overflow clip rect that corresponds to the column. fragment.paginationClip = flowThreadPortionOverflowRectAt(i); // Flip it into more a physical (PaintLayer-style) rectangle. flowThread->flipForWritingMode(fragment.paginationClip); fragments.append(fragment); } }