diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderView.cpp')
| -rw-r--r-- | Source/WebCore/rendering/RenderView.cpp | 344 |
1 files changed, 284 insertions, 60 deletions
diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp index bdb9c8069..251659fc1 100644 --- a/Source/WebCore/rendering/RenderView.cpp +++ b/Source/WebCore/rendering/RenderView.cpp @@ -27,19 +27,23 @@ #include "FloatQuad.h" #include "FlowThreadController.h" #include "Frame.h" +#include "FrameSelection.h" #include "FrameView.h" #include "GraphicsContext.h" #include "HTMLFrameOwnerElement.h" +#include "HTMLIFrameElement.h" #include "HitTestResult.h" #include "Page.h" #include "RenderGeometryMap.h" #include "RenderLayer.h" +#include "RenderLayerBacking.h" #include "RenderNamedFlowThread.h" #include "RenderSelectionInfo.h" #include "RenderWidget.h" #include "RenderWidgetProtector.h" #include "StyleInheritedData.h" #include "TransformState.h" +#include <wtf/StackStats.h> #if USE(ACCELERATED_COMPOSITING) #include "RenderLayerCompositor.h" @@ -51,9 +55,9 @@ namespace WebCore { -RenderView::RenderView(Node* node, FrameView* view) - : RenderBlock(node) - , m_frameView(view) +RenderView::RenderView(Document* document) + : RenderBlock(document) + , m_frameView(document->view()) , m_selectionStart(0) , m_selectionEnd(0) , m_selectionStartPos(-1) @@ -65,12 +69,8 @@ RenderView::RenderView(Node* node, FrameView* view) , m_layoutStateDisableCount(0) , m_renderQuoteHead(0) , m_renderCounterCount(0) - , m_layoutPhase(RenderViewNormalLayout) + , m_selectionWasCaret(false) { - // Clear our anonymous bit, set because RenderObject assumes - // any renderer with document as the node is anonymous. - setIsAnonymous(false); - // init RenderObject attributes setInline(false); @@ -79,7 +79,7 @@ RenderView::RenderView(Node* node, FrameView* view) setPreferredLogicalWidthsDirty(true, MarkOnlyThis); - setPositioned(true); // to 0,0 :) + setPositionState(AbsolutePosition); // to 0,0 :) } RenderView::~RenderView() @@ -93,12 +93,21 @@ bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result) bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result) { - bool inside = layer()->hitTest(request, location, result); + if (layer()->hitTest(request, location, result)) + return true; - // Next set up the correct :hover/:active state along the new chain. - document()->updateHoverActiveState(request, result); + // FIXME: Consider if this test should be done unconditionally. + if (request.allowsFrameScrollbars() && m_frameView) { + // ScrollView scrollbars are not the same as RenderLayer scrollbars tested by RenderLayer::hitTestOverflowControls, + // so we need to test ScrollView scrollbars separately here. + Scrollbar* frameScrollbar = m_frameView->scrollbarAtPoint(location.roundedPoint()); + if (frameScrollbar) { + result.setScrollbar(frameScrollbar); + return true; + } + } - return inside; + return false; } void RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit, LogicalExtentComputedValues& computedValues) const @@ -112,19 +121,12 @@ void RenderView::updateLogicalWidth() setLogicalWidth(viewLogicalWidth()); } -void RenderView::computePreferredLogicalWidths() -{ - ASSERT(preferredLogicalWidthsDirty()); - - RenderBlock::computePreferredLogicalWidths(); -} - -LayoutUnit RenderView::availableLogicalHeight() const +LayoutUnit RenderView::availableLogicalHeight(AvailableLogicalHeightType heightType) const { // If we have columns, then the available logical height is reduced to the column height. if (hasColumns()) return columnInfo()->columnHeight(); - return RenderBlock::availableLogicalHeight(); + return RenderBlock::availableLogicalHeight(heightType); } bool RenderView::isChildAllowed(RenderObject* child, RenderStyle*) const @@ -154,6 +156,110 @@ void RenderView::checkLayoutState(const LayoutState& state) } #endif +static RenderBox* enclosingSeamlessRenderer(Document* doc) +{ + if (!doc) + return 0; + Element* ownerElement = doc->seamlessParentIFrame(); + if (!ownerElement) + return 0; + return ownerElement->renderBox(); +} + +void RenderView::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + // 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()); + } + RenderBlock::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; +} + +// The algorithm below assumes this is a full layout. In case there are previously computed values for regions, supplemental steps are taken +// to ensure the results are the same as those obtained from a full layout (i.e. the auto-height regions from all the flows are marked as needing +// layout). +// 1. The flows are laid out from the outer flow to the inner flow. This successfully computes the outer non-auto-height regions size so the +// inner flows have the necessary information to correctly fragment the content. +// 2. The flows are laid out from the inner flow to the outer flow. After an inner flow is laid out it goes into the constrained layout phase +// and marks the auto-height regions they need layout. This means the outer flows will relayout if they depend on regions with auto-height regions +// belonging to inner flows. This step will correctly set the computedAutoHeight for the auto-height regions. It's possible for non-auto-height +// regions to relayout if they depend on auto-height regions. This will invalidate the inner flow threads and mark them as needing layout. +// 3. The last step is to do one last layout if there are pathological dependencies between non-auto-height regions and auto-height regions +// as detected in the previous step. +void RenderView::layoutContentInAutoLogicalHeightRegions(const LayoutState& state) +{ + // We need to invalidate all the flows with auto-height regions if one such flow needs layout. + // If none is found we do a layout a check back again afterwards. + if (!flowThreadController()->updateFlowThreadsNeedingLayout()) { + // Do a first layout of the content. In some cases more layouts are not needed (e.g. only flows with non-auto-height regions have changed). + layoutContent(state); + + // If we find no named flow needing a two step layout after the first layout, exit early. + // Otherwise, initiate the two step layout algorithm and recompute all the flows. + if (!flowThreadController()->updateFlowThreadsNeedingTwoStepLayout()) + return; + } + + // Layout to recompute all the named flows with auto-height regions. + layoutContent(state); + + // Propagate the computed auto-height values upwards. + // Non-auto-height regions may invalidate the flow thread because they depended on auto-height regions, but that's ok. + flowThreadController()->updateFlowThreadsIntoConstrainedPhase(); + + // Do one last layout that should update the auto-height regions found in the main flow + // and solve pathological dependencies between regions (e.g. a non-auto-height region depending + // on an auto-height one). + if (needsLayout()) + layoutContent(state); +} + void RenderView::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; @@ -168,10 +274,17 @@ void RenderView::layout() if (relayoutChildren) { setChildNeedsLayout(true, MarkOnlyThis); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) + if ((child->isBox() && (toRenderBox(child)->hasRelativeLogicalHeight() || toRenderBox(child)->hasViewportPercentageLogicalHeight())) || child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() - || child->style()->logicalMaxHeight().isPercent()) + || child->style()->logicalMaxHeight().isPercent() + || child->style()->logicalHeight().isViewportPercentage() + || child->style()->logicalMinHeight().isViewportPercentage() + || child->style()->logicalMaxHeight().isViewportPercentage() +#if ENABLE(SVG) + || child->isSVGRoot() +#endif + ) child->setChildNeedsLayout(true, MarkOnlyThis); } } @@ -181,33 +294,37 @@ void RenderView::layout() return; LayoutState state; - // FIXME: May be better to push a clip and avoid issuing offscreen repaints. - state.m_clipped = false; - state.m_pageLogicalHeight = m_pageLogicalHeight; - state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged; - state.m_isPaginated = state.m_pageLogicalHeight; + bool isSeamlessAncestorInFlowThread = initializeLayoutState(state); + m_pageLogicalHeightChanged = false; m_layoutState = &state; - m_layoutPhase = RenderViewNormalLayout; - bool needsTwoPassLayoutForAutoLogicalHeightRegions = hasRenderNamedFlowThreads() && flowThreadController()->hasAutoLogicalHeightRegions(); - - if (needsTwoPassLayoutForAutoLogicalHeightRegions) - flowThreadController()->resetRegionsOverrideLogicalContentHeight(); - - layoutContent(state); - - if (needsTwoPassLayoutForAutoLogicalHeightRegions) { - m_layoutPhase = ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions; - flowThreadController()->markAutoLogicalHeightRegionsForLayout(); + if (checkTwoPassLayoutForAutoHeightRegions()) + layoutContentInAutoLogicalHeightRegions(state); + else layoutContent(state); - } #ifndef NDEBUG checkLayoutState(state); #endif m_layoutState = 0; setNeedsLayout(false); + + if (isSeamlessAncestorInFlowThread) + flowThreadController()->setCurrentRenderFlowThread(0); +} + +LayoutUnit RenderView::pageOrViewLogicalHeight() const +{ + if (document()->printing()) + return pageLogicalHeight(); + + if (hasColumns() && !style()->hasInlineColumnAxis()) { + if (int pageLength = frameView()->pagination().pageLength) + return pageLength; + } + + return viewLogicalHeight(); } void RenderView::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const @@ -294,7 +411,7 @@ void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) ASSERT(LayoutPoint(IntPoint(paintOffset.x(), paintOffset.y())) == paintOffset); // This avoids painting garbage between columns if there is a column gap. - if (m_frameView && m_frameView->pagination().mode != Pagination::Unpaginated) + if (m_frameView && m_frameView->pagination().mode != Pagination::Unpaginated && paintInfo.shouldPaintWithinRoot(this)) paintInfo.context->fillRect(paintInfo.rect, m_frameView->baseBackgroundColor(), ColorSpaceDeviceRGB); paintObject(paintInfo, paintOffset); @@ -328,6 +445,9 @@ static inline bool rendererObscuresBackground(RenderObject* rootObject) void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) { + if (!paintInfo.shouldPaintWithinRoot(this)) + return; + // Check to see if we are enclosed by a layer that requires complex painting rules. If so, we cannot blit // when scrolling, and we need to use slow repaints. Examples of layers that require this are transparent layers, // layers with reflections, or transformed layers. @@ -354,6 +474,9 @@ void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&) if (document()->ownerElement() || !view()) return; + if (paintInfo.skipRootBackground()) + return; + bool rootFillsViewport = false; bool rootObscuresBackground = false; Node* documentElement = document()->documentElement(); @@ -452,6 +575,14 @@ void RenderView::repaintViewAndCompositedLayers() #endif } +LayoutRect RenderView::visualOverflowRect() const +{ + if (m_frameView->paintsEntireContents()) + return layoutOverflowRect(); + + return RenderBlock::visualOverflowRect(); +} + void RenderView::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const { // If a container was specified, and was not 0 or the RenderView, @@ -533,7 +664,7 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates. LayoutRect currRect = info->rect(); if (RenderLayerModelObject* repaintContainer = info->repaintContainer()) { - FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect), SnapOffsetForTransforms); + FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect)); currRect = absQuad.enclosingBoundingBox(); } selRect.unite(currRect); @@ -541,6 +672,30 @@ IntRect RenderView::selectionBounds(bool clipToVisibleContent) const return pixelSnappedIntRect(selRect); } +void RenderView::repaintSelection() const +{ + document()->updateStyleIfNeeded(); + + 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) + continue; + if (o->selectionState() == SelectionNone) + continue; + + 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()) { + if (!processedBlocks.add(block).isNewEntry) + break; + 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. @@ -563,9 +718,14 @@ void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* e if ((start && !end) || (end && !start)) return; + 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) + m_selectionEnd == end && m_selectionEndPos == endPos && !caretChanged) + return; + + if ((start && end) && (start->flowThreadContainingBlock() != end->flowThreadContainingBlock())) return; // Record the old selected objects. These will be used later @@ -819,6 +979,16 @@ IntRect RenderView::unscaledDocumentRect() const return pixelSnappedIntRect(overflowRect); } +bool RenderView::rootBackgroundIsEntirelyFixed() const +{ + RenderObject* rootObject = document()->documentElement() ? document()->documentElement()->renderer() : 0; + if (!rootObject) + return false; + + RenderObject* rootRenderer = rootObject->rendererForRootBackground(); + return rootRenderer->hasEntirelyFixedBackground(); +} + LayoutRect RenderView::backgroundRect(RenderBox* backgroundRenderer) const { if (!hasColumns()) @@ -864,12 +1034,6 @@ int RenderView::viewWidth() const int RenderView::viewLogicalHeight() const { int height = style()->isHorizontalWritingMode() ? viewHeight() : viewWidth(); - - if (hasColumns() && !style()->hasInlineColumnAxis()) { - if (int pageLength = m_frameView->pagination().pageLength) - height = pageLength; - } - return height; } @@ -884,6 +1048,7 @@ void RenderView::pushLayoutState(RenderObject* root) ASSERT(m_layoutStateDisableCount == 0); ASSERT(m_layoutState == 0); + pushLayoutStateForCurrentFlowThread(root); m_layoutState = new (renderArena()) LayoutState(root); } @@ -956,19 +1121,11 @@ RenderLayerCompositor* RenderView::compositor() } #endif -void RenderView::didMoveOnscreen() -{ -#if USE(ACCELERATED_COMPOSITING) - if (m_compositor) - m_compositor->didMoveOnscreen(); -#endif -} - -void RenderView::willMoveOffscreen() +void RenderView::setIsInWindow(bool isInWindow) { #if USE(ACCELERATED_COMPOSITING) if (m_compositor) - m_compositor->willMoveOffscreen(); + m_compositor->setIsInWindow(isInWindow); #endif } @@ -993,6 +1150,11 @@ bool RenderView::hasRenderNamedFlowThreads() const return m_flowThreadController && m_flowThreadController->hasRenderNamedFlowThreads(); } +bool RenderView::checkTwoPassLayoutForAutoHeightRegions() const +{ + return hasRenderNamedFlowThreads() && m_flowThreadController->hasFlowThreadsWithAutoLogicalHeightRegions(); +} + FlowThreadController* RenderView::flowThreadController() { if (!m_flowThreadController) @@ -1001,6 +1163,30 @@ FlowThreadController* RenderView::flowThreadController() return m_flowThreadController.get(); } +void RenderView::pushLayoutStateForCurrentFlowThread(const RenderObject* object) +{ + if (!m_flowThreadController) + return; + + RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread(); + if (!currentFlowThread) + return; + + currentFlowThread->pushFlowThreadLayoutState(object); +} + +void RenderView::popLayoutStateForCurrentFlowThread() +{ + if (!m_flowThreadController) + return; + + RenderFlowThread* currentFlowThread = m_flowThreadController->currentRenderFlowThread(); + if (!currentFlowThread) + return; + + currentFlowThread->popFlowThreadLayoutState(); +} + RenderBlock::IntervalArena* RenderView::intervalArena() { if (!m_intervalArena) @@ -1008,4 +1194,42 @@ RenderBlock::IntervalArena* RenderView::intervalArena() return m_intervalArena.get(); } +FragmentationDisabler::FragmentationDisabler(RenderObject* root) +{ + RenderView* renderView = root->view(); + ASSERT(renderView); + + LayoutState* layoutState = renderView->layoutState(); + + m_root = root; + m_fragmenting = layoutState && layoutState->isPaginated(); + m_flowThreadState = m_root->flowThreadState(); +#ifndef NDEBUG + m_layoutState = layoutState; +#endif + + if (layoutState) + layoutState->m_isPaginated = false; + + if (m_flowThreadState != RenderObject::NotInsideFlowThread) + m_root->setFlowThreadStateIncludingDescendants(RenderObject::NotInsideFlowThread); +} + +FragmentationDisabler::~FragmentationDisabler() +{ + RenderView* renderView = m_root->view(); + ASSERT(renderView); + + LayoutState* layoutState = renderView->layoutState(); +#ifndef NDEBUG + ASSERT(m_layoutState == layoutState); +#endif + + if (layoutState) + layoutState->m_isPaginated = m_fragmenting; + + if (m_flowThreadState != RenderObject::NotInsideFlowThread) + m_root->setFlowThreadStateIncludingDescendants(m_flowThreadState); +} + } // namespace WebCore |
