void RenderFlowThread::pushFlowThreadLayoutState(const RenderObject& object) { if (const RenderBox* currentBoxDescendant = currentStatePusherRenderBox()) { LayoutState* layoutState = currentBoxDescendant->view()->layoutState(); if (layoutState && layoutState->isPaginated()) { ASSERT(layoutState->renderer() == currentBoxDescendant); LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset(); setOffsetFromLogicalTopOfFirstRegion(currentBoxDescendant, currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width()); } } m_statePusherObjectsStack.add(&object); }
LayoutUnit RenderFlowThread::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const { // First check if we cached the offset for the block if it's an ancestor containing block of the box // being currently laid out. LayoutUnit offset; if (cachedOffsetFromLogicalTopOfFirstRegion(currentBlock, offset)) return offset; // If it's the current box being laid out, use the layout state. const RenderBox* currentBoxDescendant = currentStatePusherRenderBox(); if (currentBlock == currentBoxDescendant) { LayoutState* layoutState = view()->layoutState(); ASSERT(layoutState->renderer() == currentBlock); ASSERT(layoutState && layoutState->isPaginated()); LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset(); return currentBoxDescendant->isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); } // As a last resort, take the slow path. LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height()); while (currentBlock && !currentBlock->isRenderFlowThread()) { RenderBlock* containerBlock = currentBlock->containingBlock(); ASSERT(containerBlock); if (!containerBlock) return 0; LayoutPoint currentBlockLocation = currentBlock->location(); if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) { // We have to put the block rect in container coordinates // and we have to take into account both the container and current block flipping modes if (containerBlock->style()->isFlippedBlocksWritingMode()) { if (containerBlock->isHorizontalWritingMode()) blockRect.setY(currentBlock->height() - blockRect.maxY()); else blockRect.setX(currentBlock->width() - blockRect.maxX()); } currentBlock->flipForWritingMode(blockRect); } blockRect.moveBy(currentBlockLocation); currentBlock = containerBlock; } return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x(); }
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; }
LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const { // If our block doesn't have snapping turned on, do nothing. // FIXME: Implement bounds snapping. if (blockFlow().style().lineSnap() == LineSnapNone) return 0; // Get the current line grid and offset. LayoutState* layoutState = blockFlow().view().layoutState(); RenderBlockFlow* lineGrid = layoutState->lineGrid(); LayoutSize lineGridOffset = layoutState->lineGridOffset(); if (!lineGrid || lineGrid->style().writingMode() != blockFlow().style().writingMode()) return 0; // Get the hypothetical line box used to establish the grid. RootInlineBox* lineGridBox = lineGrid->lineGridBox(); if (!lineGridBox) return 0; LayoutUnit lineGridBlockOffset = lineGrid->isHorizontalWritingMode() ? lineGridOffset.height() : lineGridOffset.width(); LayoutUnit blockOffset = blockFlow().isHorizontalWritingMode() ? layoutState->layoutOffset().height() : layoutState->layoutOffset().width(); // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple // as established by the line box. // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume // the grid should honor line-box-contain. LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); if (!gridLineHeight) return 0; LayoutUnit lineGridFontAscent = lineGrid->style().fontMetrics().ascent(baselineType()); LayoutUnit lineGridFontHeight = lineGridBox->logicalHeight(); LayoutUnit firstTextTop = lineGridBlockOffset + lineGridBox->logicalTop(); LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); LayoutUnit firstBaselinePosition = firstTextTop + lineGridFontAscent; LayoutUnit currentTextTop = blockOffset + logicalTop() + delta; LayoutUnit currentFontAscent = blockFlow().style().fontMetrics().ascent(baselineType()); LayoutUnit currentBaselinePosition = currentTextTop + currentFontAscent; LayoutUnit lineGridPaginationOrigin = isHorizontal() ? layoutState->lineGridPaginationOrigin().height() : layoutState->lineGridPaginationOrigin().width(); // If we're paginated, see if we're on a page after the first one. If so, the grid resets on subsequent pages. // FIXME: If the grid is an ancestor of the pagination establisher, then this is incorrect. LayoutUnit pageLogicalTop = 0; if (layoutState->isPaginated() && layoutState->pageLogicalHeight()) { pageLogicalTop = blockFlow().pageLogicalTopForOffset(lineTopWithLeading() + delta); if (pageLogicalTop > firstLineTopWithLeading) firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderAndPaddingBefore() + lineGridPaginationOrigin; } if (blockFlow().style().lineSnap() == LineSnapContain) { // Compute the desired offset from the text-top of a grid line. // Look at our height (logicalHeight()). // Look at the total available height. It's going to be (textBottom - textTop) + (n-1)*(multiple with leading) // where n is number of grid lines required to enclose us. if (logicalHeight() <= lineGridFontHeight) firstTextTop += (lineGridFontHeight - logicalHeight()) / 2; else { LayoutUnit numberOfLinesWithLeading = ceilf(static_cast<float>(logicalHeight() - lineGridFontHeight) / gridLineHeight); LayoutUnit totalHeight = lineGridFontHeight + numberOfLinesWithLeading * gridLineHeight; firstTextTop += (totalHeight - logicalHeight()) / 2; } firstBaselinePosition = firstTextTop + currentFontAscent; } else firstBaselinePosition = firstTextTop + lineGridFontAscent; // If we're above the first line, just push to the first line. if (currentBaselinePosition < firstBaselinePosition) return delta + firstBaselinePosition - currentBaselinePosition; // Otherwise we're in the middle of the grid somewhere. Just push to the next line. LayoutUnit baselineOffset = currentBaselinePosition - firstBaselinePosition; LayoutUnit remainder = roundToInt(baselineOffset) % roundToInt(gridLineHeight); LayoutUnit result = delta; if (remainder) result += gridLineHeight - remainder; // If we aren't paginated we can return the result. if (!layoutState->isPaginated() || !layoutState->pageLogicalHeight() || result == delta) return result; // We may end up shifted to a new page. We need to do a re-snap when that happens. LayoutUnit newPageLogicalTop = blockFlow().pageLogicalTopForOffset(lineBottomWithLeading() + result); if (newPageLogicalTop == pageLogicalTop) return result; // Put ourselves at the top of the next page to force a snap onto the new grid established by that page. return lineSnapAdjustment(newPageLogicalTop - (blockOffset + lineTopWithLeading())); }