diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RootInlineBox.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RootInlineBox.cpp')
-rw-r--r-- | Source/WebCore/rendering/RootInlineBox.cpp | 458 |
1 files changed, 270 insertions, 188 deletions
diff --git a/Source/WebCore/rendering/RootInlineBox.cpp b/Source/WebCore/rendering/RootInlineBox.cpp index dde962178..ca681682d 100644 --- a/Source/WebCore/rendering/RootInlineBox.cpp +++ b/Source/WebCore/rendering/RootInlineBox.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,16 +30,17 @@ #include "HitTestResult.h" #include "InlineTextBox.h" #include "LogicalSelectionOffsetCaches.h" -#include "Page.h" #include "PaintInfo.h" #include "RenderFlowThread.h" +#include "RenderInline.h" +#include "RenderRubyBase.h" +#include "RenderRubyRun.h" +#include "RenderRubyText.h" #include "RenderView.h" #include "VerticalPositionCache.h" -#include <wtf/NeverDestroyed.h> -#include <wtf/unicode/Unicode.h> namespace WebCore { - + struct SameSizeAsRootInlineBox : public InlineFlowBox { unsigned variables[7]; void* pointers[3]; @@ -48,13 +49,12 @@ struct SameSizeAsRootInlineBox : public InlineFlowBox { COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small); typedef WTF::HashMap<const RootInlineBox*, std::unique_ptr<EllipsisBox>> EllipsisBoxMap; -static EllipsisBoxMap* gEllipsisBoxMap = 0; +static EllipsisBoxMap* gEllipsisBoxMap; -typedef HashMap<const RootInlineBox*, RenderRegion*> ContainingRegionMap; -static ContainingRegionMap& containingRegionMap() +static ContainingRegionMap& containingRegionMap(RenderBlockFlow& block) { - static NeverDestroyed<ContainingRegionMap> map; - return map; + ASSERT(block.flowThreadContainingBlock()); + return block.flowThreadContainingBlock()->containingRegionMap(); } RootInlineBox::RootInlineBox(RenderBlockFlow& block) @@ -69,8 +69,8 @@ RootInlineBox::~RootInlineBox() { detachEllipsisBox(); - if (m_hasContainingRegion) - containingRegionMap().remove(this); + if (blockFlow().flowThreadContainingBlock()) + containingRegionMap(blockFlow()).remove(this); } void RootInlineBox::detachEllipsisBox() @@ -93,12 +93,9 @@ void RootInlineBox::clearTruncation() bool RootInlineBox::isHyphenated() const { for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { - if (box->isInlineTextBox()) { - if (toInlineTextBox(box)->hasHyphen()) - return true; - } + if (is<InlineTextBox>(*box) && downcast<InlineTextBox>(*box).hasHyphen()) + return true; } - return false; } @@ -129,13 +126,9 @@ float RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, f if (!gEllipsisBoxMap) gEllipsisBoxMap = new EllipsisBoxMap(); - // Create an ellipsis box. - auto newEllipsisBox = std::make_unique<EllipsisBox>(blockFlow(), ellipsisStr, this, ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(), y(), !prevRootBox(), isHorizontal(), markupBox); - auto ellipsisBox = newEllipsisBox.get(); - - gEllipsisBoxMap->add(this, std::move(newEllipsisBox)); + ASSERT(!hasEllipsisBox()); + auto* ellipsisBox = gEllipsisBoxMap->set(this, std::make_unique<EllipsisBox>(blockFlow(), ellipsisStr, this, ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(), y(), !prevRootBox(), isHorizontal(), markupBox)).iterator->value.get(); setHasEllipsisBox(true); - // FIXME: Do we need an RTL version of this? if (ltr && (x() + logicalWidth() + ellipsisWidth) <= blockRightEdge) { ellipsisBox->setX(x() + logicalWidth()); @@ -143,7 +136,7 @@ float RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, f } // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) - // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being + // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being // truncated). bool foundBox = false; float truncatedWidth = 0; @@ -169,62 +162,30 @@ void RootInlineBox::paintEllipsisBox(PaintInfo& paintInfo, const LayoutPoint& pa ellipsisBox()->paint(paintInfo, paintOffset, lineTop, lineBottom); } -#if PLATFORM(MAC) - -void RootInlineBox::addHighlightOverflow() -{ - Page* page = renderer().frame().page(); - if (!page) - return; - - // Highlight acts as a selection inflation. - FloatRect rootRect(0, selectionTop(), logicalWidth(), selectionHeight()); - IntRect inflatedRect = enclosingIntRect(page->chrome().client().customHighlightRect(renderer().element(), renderer().style().highlight(), rootRect)); - setOverflowFromLogicalRects(inflatedRect, inflatedRect, lineTop(), lineBottom()); -} - -void RootInlineBox::paintCustomHighlight(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const AtomicString& highlightType) -{ - if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) - return; - - Page* page = renderer().frame().page(); - if (!page) - return; - - // Get the inflated rect so that we can properly hit test. - FloatRect rootRect(paintOffset.x() + x(), paintOffset.y() + selectionTop(), logicalWidth(), selectionHeight()); - FloatRect inflatedRect = page->chrome().client().customHighlightRect(renderer().element(), highlightType, rootRect); - if (inflatedRect.intersects(paintInfo.rect)) - page->chrome().client().paintCustomHighlight(renderer().element(), highlightType, rootRect, rootRect, false, true); -} - -#endif - void RootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { + RenderNamedFlowFragment* namedFlowFragment = renderer().currentRenderNamedFlowFragment(); + // Check if we are in the correct region. - if (paintInfo.renderRegion && m_hasContainingRegion && containingRegion() != paintInfo.renderRegion) - return; + if (namedFlowFragment) { + RenderRegion* region = containingRegion(); + if (region && region != reinterpret_cast<RenderRegion*>(namedFlowFragment)) + return; + } InlineFlowBox::paint(paintInfo, paintOffset, lineTop, lineBottom); paintEllipsisBox(paintInfo, paintOffset, lineTop, lineBottom); -#if PLATFORM(MAC) - const RenderStyle& lineStyle = this->lineStyle(); - if (lineStyle.highlight() != nullAtom && !paintInfo.context->paintingDisabled()) - paintCustomHighlight(paintInfo, paintOffset, lineStyle.highlight()); -#endif } -bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) +bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction hitTestAction) { if (hasEllipsisBox() && visibleToHitTesting()) { - if (ellipsisBox()->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { + if (ellipsisBox()->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom, hitTestAction)) { renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); return true; } } - return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom); + return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom, hitTestAction); } void RootInlineBox::adjustPosition(float dx, float dy) @@ -242,54 +203,53 @@ void RootInlineBox::adjustPosition(float dx, float dy) void RootInlineBox::childRemoved(InlineBox* box) { if (&box->renderer() == m_lineBreakObj) - setLineBreakInfo(0, 0, BidiStatus()); + setLineBreakInfo(nullptr, 0, BidiStatus()); for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == &box->renderer(); prev = prev->prevRootBox()) { - prev->setLineBreakInfo(0, 0, BidiStatus()); + prev->setLineBreakInfo(nullptr, 0, BidiStatus()); prev->markDirty(); } } RenderRegion* RootInlineBox::containingRegion() const { + ContainingRegionMap& regionMap = containingRegionMap(blockFlow()); + bool hasContainingRegion = regionMap.contains(this); + RenderRegion* region = hasContainingRegion ? regionMap.get(this) : nullptr; + #ifndef NDEBUG - if (m_hasContainingRegion) { + if (hasContainingRegion) { RenderFlowThread* flowThread = blockFlow().flowThreadContainingBlock(); const RenderRegionList& regionList = flowThread->renderRegionList(); - ASSERT(regionList.contains(containingRegionMap().get(this))); + ASSERT_WITH_SECURITY_IMPLICATION(regionList.contains(region)); } #endif - return m_hasContainingRegion ? containingRegionMap().get(this) : nullptr; + + return region; } void RootInlineBox::clearContainingRegion() { ASSERT(!isDirty()); - ASSERT(blockFlow().flowThreadContainingBlock()); - if (!m_hasContainingRegion) + if (!containingRegionMap(blockFlow()).contains(this)) return; - containingRegionMap().remove(this); - m_hasContainingRegion = false; + containingRegionMap(blockFlow()).remove(this); } void RootInlineBox::setContainingRegion(RenderRegion& region) { ASSERT(!isDirty()); - ASSERT(blockFlow().flowThreadContainingBlock()); - containingRegionMap().set(this, ®ion); - m_hasContainingRegion = true; + containingRegionMap(blockFlow()).set(this, ®ion); } LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) { -#if ENABLE(SVG) // SVG will handle vertical alignment on its own. if (isSVGRootInlineBox()) return 0; -#endif LayoutUnit maxPositionTop = 0; LayoutUnit maxPositionBottom = 0; @@ -303,7 +263,7 @@ LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, G m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline; - computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, + computeLogicalBoxHeights(*this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, textBoxDataMap, baselineType(), verticalPositionCache); if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom)) @@ -324,7 +284,9 @@ LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, G maxHeight = std::max<LayoutUnit>(0, maxHeight); // FIXME: Is this really necessary? - setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock, heightOfBlock + maxHeight); + LayoutUnit lineTopWithLeading = !hasAnonymousInlineBlock() ? heightOfBlock : lineTop; + LayoutUnit lineBottomWithLeading = !hasAnonymousInlineBlock() ? heightOfBlock + maxHeight : lineBottom; + setLineTopBottomPositions(lineTop, lineBottom, lineTopWithLeading, lineBottomWithLeading); setPaginatedLineWidth(blockFlow().availableLogicalWidthForContent(heightOfBlock)); LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment(); @@ -344,13 +306,6 @@ LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, G return heightOfBlock + maxHeight; } -float RootInlineBox::maxLogicalTop() const -{ - float maxLogicalTop = 0; - computeMaxLogicalTop(maxLogicalTop); - return maxLogicalTop; -} - LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const { LayoutUnit result = 0; @@ -364,12 +319,12 @@ LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const return result; // Annotations over this line may push us further down. - LayoutUnit highestAllowedPosition = prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result : static_cast<LayoutUnit>(blockFlow().borderBefore()); + LayoutUnit highestAllowedPosition = prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result : blockFlow().borderBefore(); result = computeOverAnnotationAdjustment(highestAllowedPosition); } else { // Annotations under this line may push us up. if (hasAnnotationsBefore()) - result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : static_cast<LayoutUnit>(blockFlow().borderBefore())); + result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : blockFlow().borderBefore()); if (!prevRootBox() || !prevRootBox()->hasAnnotationsAfter()) return result; @@ -512,7 +467,7 @@ GapRects RootInlineBox::lineSelectionGap(RenderBlock& rootBlock, const LayoutPoi LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) { if (paintInfo && box->parent()->renderer().style().visibility() == VISIBLE) - paintInfo->context->fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor(), box->parent()->renderer().style().colorSpace()); + paintInfo->context().fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor()); // VisibleSelection may be non-contiguous, see comment above. result.uniteCenter(gapRect); } @@ -606,43 +561,72 @@ RenderObject::SelectionState RootInlineBox::selectionState() InlineBox* RootInlineBox::firstSelectedBox() { - for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { + for (auto* box = firstLeafChild(); box; box = box->nextLeafChild()) { if (box->selectionState() != RenderObject::SelectionNone) return box; } - - return 0; + return nullptr; } InlineBox* RootInlineBox::lastSelectedBox() { - for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) { + for (auto* box = lastLeafChild(); box; box = box->prevLeafChild()) { if (box->selectionState() != RenderObject::SelectionNone) return box; } - - return 0; + return nullptr; } LayoutUnit RootInlineBox::selectionTop() const { LayoutUnit selectionTop = m_lineTop; - + if (m_hasAnnotationsBefore) selectionTop -= !renderer().style().isFlippedLinesWritingMode() ? computeOverAnnotationAdjustment(m_lineTop) : computeUnderAnnotationAdjustment(m_lineTop); if (renderer().style().isFlippedLinesWritingMode()) return selectionTop; +#if !PLATFORM(IOS) + // See rdar://problem/19692206 ... don't want to do this adjustment for iOS where overlap is ok and handled. + if (renderer().isRubyBase()) { + // The ruby base selection should avoid intruding into the ruby text. This is only the case if there is an actual ruby text above us. + RenderRubyBase* base = &downcast<RenderRubyBase>(renderer()); + RenderRubyRun* run = base->rubyRun(); + if (run) { + RenderRubyText* text = run->rubyText(); + if (text && text->logicalTop() < base->logicalTop()) { + // The ruby text is above the ruby base. Just return now in order to avoid painting on top of the ruby text. + return selectionTop; + } + } + } else if (renderer().isRubyText()) { + // The ruby text selection should go all the way to the selection top of the containing line. + RenderRubyText* text = &downcast<RenderRubyText>(renderer()); + RenderRubyRun* run = text->rubyRun(); + if (run && run->inlineBoxWrapper()) { + RenderRubyBase* base = run->rubyBase(); + if (base && text->logicalTop() < base->logicalTop()) { + // The ruby text is above the ruby base. + const RootInlineBox& containingLine = run->inlineBoxWrapper()->root(); + LayoutUnit enclosingSelectionTop = containingLine.selectionTop(); + LayoutUnit deltaBetweenObjects = text->logicalTop() + run->logicalTop(); + LayoutUnit selectionTopInRubyTextCoords = enclosingSelectionTop - deltaBetweenObjects; + return std::min(selectionTop, selectionTopInRubyTextCoords); + } + } + } +#endif + LayoutUnit prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : blockFlow().borderAndPaddingBefore(); if (prevBottom < selectionTop && blockFlow().containsFloats()) { // This line has actually been moved further down, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous // line's bottom if the offsets are greater on both sides. - LayoutUnit prevLeft = blockFlow().logicalLeftOffsetForLine(prevBottom, false); - LayoutUnit prevRight = blockFlow().logicalRightOffsetForLine(prevBottom, false); - LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionTop, false); - LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionTop, false); + LayoutUnit prevLeft = blockFlow().logicalLeftOffsetForLine(prevBottom, DoNotIndentText); + LayoutUnit prevRight = blockFlow().logicalRightOffsetForLine(prevBottom, DoNotIndentText); + LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionTop, DoNotIndentText); + LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionTop, DoNotIndentText); if (prevLeft > newLeft || prevRight < newRight) return selectionTop; } @@ -650,29 +634,64 @@ LayoutUnit RootInlineBox::selectionTop() const return prevBottom; } +static RenderBlock* blockBeforeWithinSelectionRoot(const RenderBlockFlow& blockFlow, LayoutSize& offset) +{ + if (blockFlow.isSelectionRoot()) + return nullptr; + + const RenderElement* object = &blockFlow; + RenderObject* sibling; + do { + sibling = object->previousSibling(); + while (sibling && (!is<RenderBlock>(*sibling) || downcast<RenderBlock>(*sibling).isSelectionRoot())) + sibling = sibling->previousSibling(); + + offset -= LayoutSize(downcast<RenderBlock>(*object).logicalLeft(), downcast<RenderBlock>(*object).logicalTop()); + object = object->parent(); + } while (!sibling && is<RenderBlock>(object) && !downcast<RenderBlock>(*object).isSelectionRoot()); + + if (!sibling) + return nullptr; + + RenderBlock* beforeBlock = downcast<RenderBlock>(sibling); + + offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); + + RenderObject* child = beforeBlock->lastChild(); + while (is<RenderBlock>(child)) { + beforeBlock = downcast<RenderBlock>(child); + offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); + child = beforeBlock->lastChild(); + } + return beforeBlock; +} + LayoutUnit RootInlineBox::selectionTopAdjustedForPrecedingBlock() const { const RootInlineBox& rootBox = root(); LayoutUnit top = selectionTop(); - RenderObject::SelectionState blockSelectionState = rootBox.blockFlow().selectionState(); + auto blockSelectionState = rootBox.blockFlow().selectionState(); if (blockSelectionState != RenderObject::SelectionInside && blockSelectionState != RenderObject::SelectionEnd) return top; LayoutSize offsetToBlockBefore; - if (RenderBlock* block = rootBox.blockFlow().blockBeforeWithinSelectionRoot(offsetToBlockBefore)) { - if (block->isRenderBlockFlow()) { - if (RootInlineBox* lastLine = toRenderBlockFlow(block)->lastRootBox()) { - RenderObject::SelectionState lastLineSelectionState = lastLine->selectionState(); - if (lastLineSelectionState != RenderObject::SelectionInside && lastLineSelectionState != RenderObject::SelectionStart) - return top; - - LayoutUnit lastLineSelectionBottom = lastLine->selectionBottom() + offsetToBlockBefore.height(); - top = std::max(top, lastLineSelectionBottom); - } - } - } + auto* blockBefore = blockBeforeWithinSelectionRoot(rootBox.blockFlow(), offsetToBlockBefore); + if (!is<RenderBlockFlow>(blockBefore)) + return top; + // Do not adjust blocks sharing the same line. + if (!offsetToBlockBefore.height()) + return top; + + if (auto* lastLine = downcast<RenderBlockFlow>(*blockBefore).lastRootBox()) { + RenderObject::SelectionState lastLineSelectionState = lastLine->selectionState(); + if (lastLineSelectionState != RenderObject::SelectionInside && lastLineSelectionState != RenderObject::SelectionStart) + return top; + + LayoutUnit lastLineSelectionBottom = lastLine->selectionBottom() + offsetToBlockBefore.height(); + top = std::max(top, lastLineSelectionBottom); + } return top; } @@ -682,19 +701,50 @@ LayoutUnit RootInlineBox::selectionBottom() const if (m_hasAnnotationsAfter) selectionBottom += !renderer().style().isFlippedLinesWritingMode() ? computeUnderAnnotationAdjustment(m_lineBottom) : computeOverAnnotationAdjustment(m_lineBottom); - + if (!renderer().style().isFlippedLinesWritingMode() || !nextRootBox()) return selectionBottom; + +#if !PLATFORM(IOS) + // See rdar://problem/19692206 ... don't want to do this adjustment for iOS where overlap is ok and handled. + if (renderer().isRubyBase()) { + // The ruby base selection should avoid intruding into the ruby text. This is only the case if there is an actual ruby text below us. + RenderRubyBase* base = &downcast<RenderRubyBase>(renderer()); + RenderRubyRun* run = base->rubyRun(); + if (run) { + RenderRubyText* text = run->rubyText(); + if (text && text->logicalTop() > base->logicalTop()) { + // The ruby text is below the ruby base. Just return now in order to avoid painting on top of the ruby text. + return selectionBottom; + } + } + } else if (renderer().isRubyText()) { + // The ruby text selection should go all the way to the selection bottom of the containing line. + RenderRubyText* text = &downcast<RenderRubyText>(renderer()); + RenderRubyRun* run = text->rubyRun(); + if (run && run->inlineBoxWrapper()) { + RenderRubyBase* base = run->rubyBase(); + if (base && text->logicalTop() > base->logicalTop()) { + // The ruby text is above the ruby base. + const RootInlineBox& containingLine = run->inlineBoxWrapper()->root(); + LayoutUnit enclosingSelectionBottom = containingLine.selectionBottom(); + LayoutUnit deltaBetweenObjects = text->logicalTop() + run->logicalTop(); + LayoutUnit selectionBottomInRubyTextCoords = enclosingSelectionBottom - deltaBetweenObjects; + return std::min(selectionBottom, selectionBottomInRubyTextCoords); + } + } + } +#endif LayoutUnit nextTop = nextRootBox()->selectionTop(); if (nextTop > selectionBottom && blockFlow().containsFloats()) { // The next line has actually been moved further over, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the next // line's top if the offsets are greater on both sides. - LayoutUnit nextLeft = blockFlow().logicalLeftOffsetForLine(nextTop, false); - LayoutUnit nextRight = blockFlow().logicalRightOffsetForLine(nextTop, false); - LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionBottom, false); - LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionBottom, false); + LayoutUnit nextLeft = blockFlow().logicalLeftOffsetForLine(nextTop, DoNotIndentText); + LayoutUnit nextRight = blockFlow().logicalRightOffsetForLine(nextTop, DoNotIndentText); + LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionBottom, DoNotIndentText); + LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionBottom, DoNotIndentText); if (nextLeft > newLeft || nextRight < newRight) return selectionBottom; } @@ -709,7 +759,7 @@ int RootInlineBox::blockDirectionPointInLine() const RenderBlockFlow& RootInlineBox::blockFlow() const { - return toRenderBlockFlow(renderer()); + return downcast<RenderBlockFlow>(renderer()); } static bool isEditableLeaf(InlineBox* leaf) @@ -738,17 +788,17 @@ InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPositio return firstLeaf; // Avoid returning a list marker when possible. - if (leftPosition <= firstLeaf->logicalLeft() && !firstLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) + if (firstLeaf && leftPosition <= firstLeaf->logicalLeft() && !firstLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) // The leftPosition coordinate is less or equal to left edge of the firstLeaf. // Return it. return firstLeaf; - if (leftPosition >= lastLeaf->logicalRight() && !lastLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) + if (lastLeaf && leftPosition >= lastLeaf->logicalRight() && !lastLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) // The leftPosition coordinate is greater or equal to right edge of the lastLeaf. // Return it. return lastLeaf; - InlineBox* closestLeaf = 0; + InlineBox* closestLeaf = nullptr; for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChildIgnoringLineBreak()) { if (!leaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { closestLeaf = leaf; @@ -764,13 +814,13 @@ InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPositio BidiStatus RootInlineBox::lineBreakBidiStatus() const { - return BidiStatus(static_cast<UCharDirection>(m_lineBreakBidiStatusEor), static_cast<UCharDirection>(m_lineBreakBidiStatusLastStrong), static_cast<UCharDirection>(m_lineBreakBidiStatusLast), m_lineBreakContext); + return { static_cast<UCharDirection>(m_lineBreakBidiStatusEor), static_cast<UCharDirection>(m_lineBreakBidiStatusLastStrong), static_cast<UCharDirection>(m_lineBreakBidiStatusLast), m_lineBreakContext.copyRef() }; } -void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status) +void RootInlineBox::setLineBreakInfo(RenderObject* object, unsigned breakPosition, const BidiStatus& status) { - m_lineBreakObj = obj; - m_lineBreakPos = breakPos; + m_lineBreakObj = object; + m_lineBreakPos = breakPosition; m_lineBreakBidiStatusEor = status.eor; m_lineBreakBidiStatusLastStrong = status.lastStrong; m_lineBreakBidiStatusLast = status.last; @@ -780,7 +830,7 @@ void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const EllipsisBox* RootInlineBox::ellipsisBox() const { if (!hasEllipsisBox()) - return 0; + return nullptr; return gEllipsisBoxMap->get(this); } @@ -805,17 +855,16 @@ LayoutRect RootInlineBox::paddedLayoutOverflowRect(LayoutUnit endPadding) const if (!endPadding) return lineLayoutOverflow; - // FIXME: Audit whether to use pixel snapped values when not using integers for layout: https://bugs.webkit.org/show_bug.cgi?id=63656 if (isHorizontal()) { if (isLeftToRightDirection()) - lineLayoutOverflow.shiftMaxXEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxX(), pixelSnappedLogicalRight() + endPadding)); + lineLayoutOverflow.shiftMaxXEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxX(), logicalRight() + endPadding)); else - lineLayoutOverflow.shiftXEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.x(), pixelSnappedLogicalLeft() - endPadding)); + lineLayoutOverflow.shiftXEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.x(), logicalLeft() - endPadding)); } else { if (isLeftToRightDirection()) - lineLayoutOverflow.shiftMaxYEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxY(), pixelSnappedLogicalRight() + endPadding)); + lineLayoutOverflow.shiftMaxYEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxY(), logicalRight() + endPadding)); else - lineLayoutOverflow.shiftYEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.y(), pixelSnappedLogicalLeft() - endPadding)); + lineLayoutOverflow.shiftYEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.y(), logicalLeft() - endPadding)); } return lineLayoutOverflow; @@ -833,17 +882,25 @@ static void setAscentAndDescent(int& ascent, int& descent, int newAscent, int ne } } -void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent, +void RootInlineBox::ascentAndDescentForBox(InlineBox& box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent, bool& affectsAscent, bool& affectsDescent) const { bool ascentDescentSet = false; // Replaced boxes will return 0 for the line-height if line-box-contain says they are // not to be included. - if (box->renderer().isReplaced()) { + if (box.renderer().isReplaced()) { + if (hasAnonymousInlineBlock()) { + ascent = 0; // Margins exist "outside" the line, since they have to collapse. + descent = 0; + affectsAscent = true; + affectsDescent = true; + return; + } + if (lineStyle().lineBoxContain() & LineBoxContainReplaced) { - ascent = box->baselinePosition(baselineType()); - descent = box->lineHeight() - ascent; + ascent = box.baselinePosition(baselineType()); + descent = box.lineHeight() - ascent; // Replaced elements always affect both the ascent and descent. affectsAscent = true; @@ -851,13 +908,16 @@ void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallb } return; } + + if (hasAnonymousInlineBlock()) + return; - Vector<const SimpleFontData*>* usedFonts = 0; - GlyphOverflow* glyphOverflow = 0; - if (box->isInlineTextBox()) { - GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(toInlineTextBox(box)); - usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first; - glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second; + Vector<const Font*>* usedFonts = nullptr; + GlyphOverflow* glyphOverflow = nullptr; + if (is<InlineTextBox>(box)) { + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(&downcast<InlineTextBox>(box)); + usedFonts = it == textBoxDataMap.end() ? nullptr : &it->value.first; + glyphOverflow = it == textBoxDataMap.end() ? nullptr : &it->value.second; } bool includeLeading = includeLeadingForBox(box); @@ -866,15 +926,11 @@ void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallb bool setUsedFont = false; bool setUsedFontWithLeading = false; - const RenderStyle& boxLineStyle = box->lineStyle(); -#if PLATFORM(IOS) - if (usedFonts && !usedFonts->isEmpty() && (includeFont || (boxLineStyle.lineHeight().isNegative() && includeLeading)) && !box->renderer().document().settings()->alwaysUseBaselineOfPrimaryFont()) { -#else + const RenderStyle& boxLineStyle = box.lineStyle(); if (usedFonts && !usedFonts->isEmpty() && (includeFont || (boxLineStyle.lineHeight().isNegative() && includeLeading))) { -#endif - usedFonts->append(boxLineStyle.font().primaryFont()); - for (size_t i = 0; i < usedFonts->size(); ++i) { - const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics(); + usedFonts->append(&boxLineStyle.fontCascade().primaryFont()); + for (auto& font : *usedFonts) { + auto& fontMetrics = font->fontMetrics(); int usedFontAscent = fontMetrics.ascent(baselineType()); int usedFontDescent = fontMetrics.descent(baselineType()); int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2; @@ -889,48 +945,62 @@ void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallb setUsedFontWithLeading = true; } if (!affectsAscent) - affectsAscent = usedFontAscent - box->logicalTop() > 0; + affectsAscent = usedFontAscent - box.logicalTop() > 0; if (!affectsDescent) - affectsDescent = usedFontDescent + box->logicalTop() > 0; + affectsDescent = usedFontDescent + box.logicalTop() > 0; } } // If leading is included for the box, then we compute that box. if (includeLeading && !setUsedFontWithLeading) { - int ascentWithLeading = box->baselinePosition(baselineType()); - int descentWithLeading = box->lineHeight() - ascentWithLeading; + int ascentWithLeading = box.baselinePosition(baselineType()); + int descentWithLeading = box.lineHeight() - ascentWithLeading; setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading, ascentDescentSet); // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. // If the top of our font box relative to the root box baseline is above the root box baseline, then // we are contributing to the maxAscent value. Descent is similar. If any part of our font box is below // the root box's baseline, then we contribute to the maxDescent value. - affectsAscent = ascentWithLeading - box->logicalTop() > 0; - affectsDescent = descentWithLeading + box->logicalTop() > 0; + affectsAscent = ascentWithLeading - box.logicalTop() > 0; + affectsDescent = descentWithLeading + box.logicalTop() > 0; } if (includeFontForBox(box) && !setUsedFont) { int fontAscent = boxLineStyle.fontMetrics().ascent(baselineType()); int fontDescent = boxLineStyle.fontMetrics().descent(baselineType()); setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet); - affectsAscent = fontAscent - box->logicalTop() > 0; - affectsDescent = fontDescent + box->logicalTop() > 0; + affectsAscent = fontAscent - box.logicalTop() > 0; + affectsDescent = fontDescent + box.logicalTop() > 0; } if (includeGlyphsForBox(box) && glyphOverflow && glyphOverflow->computeBounds) { setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet); - affectsAscent = glyphOverflow->top - box->logicalTop() > 0; - affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0; + affectsAscent = glyphOverflow->top - box.logicalTop() > 0; + affectsDescent = glyphOverflow->bottom + box.logicalTop() > 0; glyphOverflow->top = std::min(glyphOverflow->top, std::max(0, glyphOverflow->top - boxLineStyle.fontMetrics().ascent(baselineType()))); glyphOverflow->bottom = std::min(glyphOverflow->bottom, std::max(0, glyphOverflow->bottom - boxLineStyle.fontMetrics().descent(baselineType()))); } + + if (includeInitialLetterForBox(box)) { + // FIXME: Can't use glyph bounds in vertical writing mode because they are garbage. + bool canUseGlyphs = isHorizontal() && glyphOverflow && glyphOverflow->computeBounds; + int letterAscent = baselineType() == AlphabeticBaseline ? boxLineStyle.fontMetrics().capHeight() : (canUseGlyphs ? glyphOverflow->top : boxLineStyle.fontMetrics().ascent(baselineType())); + int letterDescent = canUseGlyphs ? glyphOverflow->bottom : (box.isRootInlineBox() ? 0 : boxLineStyle.fontMetrics().descent(baselineType())); + setAscentAndDescent(ascent, descent, letterAscent, letterDescent, ascentDescentSet); + affectsAscent = letterAscent - box.logicalTop() > 0; + affectsDescent = letterDescent + box.logicalTop() > 0; + if (canUseGlyphs) { + glyphOverflow->top = std::min(glyphOverflow->top, std::max(0, glyphOverflow->top - boxLineStyle.fontMetrics().ascent(baselineType()))); + glyphOverflow->bottom = std::min(glyphOverflow->bottom, std::max(0, glyphOverflow->bottom - boxLineStyle.fontMetrics().descent(baselineType()))); + } + } if (includeMarginForBox(box)) { LayoutUnit ascentWithMargin = boxLineStyle.fontMetrics().ascent(baselineType()); LayoutUnit descentWithMargin = boxLineStyle.fontMetrics().descent(baselineType()); - if (box->parent() && !box->renderer().isTextOrLineBreak()) { - ascentWithMargin += box->boxModelObject()->borderAndPaddingBefore() + box->boxModelObject()->marginBefore(); - descentWithMargin += box->boxModelObject()->borderAndPaddingAfter() + box->boxModelObject()->marginAfter(); + if (box.parent() && !box.renderer().isTextOrLineBreak()) { + ascentWithMargin += box.boxModelObject()->borderAndPaddingBefore() + box.boxModelObject()->marginBefore(); + descentWithMargin += box.boxModelObject()->borderAndPaddingAfter() + box.boxModelObject()->marginAfter(); } setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet); @@ -952,7 +1022,7 @@ LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositio // This method determines the vertical position for inline elements. bool firstLine = isFirstLine(); - if (firstLine && !renderer->document().styleSheetCollection().usesFirstLineRules()) + if (firstLine && !blockFlow().view().usesFirstLineRules()) firstLine = false; // Check the cache. @@ -974,7 +1044,7 @@ LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositio if (verticalAlign != BASELINE) { const RenderStyle& parentLineStyle = firstLine ? parent->firstLineStyle() : parent->style(); - const Font& font = parentLineStyle.font(); + const FontCascade& font = parentLineStyle.fontCascade(); const FontMetrics& fontMetrics = font.fontMetrics(); int fontSize = font.pixelSize(); @@ -987,7 +1057,7 @@ LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositio else if (verticalAlign == TEXT_TOP) verticalPosition += renderer->baselinePosition(baselineType(), firstLine, lineDirection) - fontMetrics.ascent(baselineType()); else if (verticalAlign == MIDDLE) - verticalPosition = (verticalPosition - static_cast<LayoutUnit>(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection)).round(); + verticalPosition = (verticalPosition - LayoutUnit(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection)).round(); else if (verticalAlign == TEXT_BOTTOM) { verticalPosition += fontMetrics.descent(baselineType()); // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. @@ -998,11 +1068,11 @@ LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositio else if (verticalAlign == LENGTH) { LayoutUnit lineHeight; //Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align: 'Percentages: refer to the 'line-height' of the element itself'. - if (renderer->style().verticalAlignLength().isPercent()) + if (renderer->style().verticalAlignLength().isPercentOrCalculated()) lineHeight = renderer->style().computedLineHeight(); else lineHeight = renderer->lineHeight(firstLine, lineDirection); - verticalPosition -= valueForLength(renderer->style().verticalAlignLength(), lineHeight, &renderer->view()); + verticalPosition -= valueForLength(renderer->style().verticalAlignLength(), lineHeight); } } @@ -1013,21 +1083,21 @@ LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositio return verticalPosition; } -bool RootInlineBox::includeLeadingForBox(InlineBox* box) const +bool RootInlineBox::includeLeadingForBox(InlineBox& box) const { - if (box->renderer().isReplaced() || (box->renderer().isTextOrLineBreak() && !box->behavesLikeText())) + if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) return false; LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); - return (lineBoxContain & LineBoxContainInline) || (box == this && (lineBoxContain & LineBoxContainBlock)); + return (lineBoxContain & LineBoxContainInline) || (&box == this && (lineBoxContain & LineBoxContainBlock)); } -bool RootInlineBox::includeFontForBox(InlineBox* box) const +bool RootInlineBox::includeFontForBox(InlineBox& box) const { - if (box->renderer().isReplaced() || (box->renderer().isTextOrLineBreak() && !box->behavesLikeText())) + if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) return false; - if (!box->behavesLikeText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren()) + if (!box.behavesLikeText() && is<InlineFlowBox>(box) && !downcast<InlineFlowBox>(box).hasTextChildren()) return false; // For now map "glyphs" to "font" in vertical text mode until the bounds returned by glyphs aren't garbage. @@ -1035,12 +1105,12 @@ bool RootInlineBox::includeFontForBox(InlineBox* box) const return (lineBoxContain & LineBoxContainFont) || (!isHorizontal() && (lineBoxContain & LineBoxContainGlyphs)); } -bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const +bool RootInlineBox::includeGlyphsForBox(InlineBox& box) const { - if (box->renderer().isReplaced() || (box->renderer().isTextOrLineBreak() && !box->behavesLikeText())) + if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) return false; - if (!box->behavesLikeText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren()) + if (!box.behavesLikeText() && is<InlineFlowBox>(box) && !downcast<InlineFlowBox>(box).hasTextChildren()) return false; // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. @@ -1048,9 +1118,21 @@ bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); } -bool RootInlineBox::includeMarginForBox(InlineBox* box) const +bool RootInlineBox::includeInitialLetterForBox(InlineBox& box) const { - if (box->renderer().isReplaced() || (box->renderer().isTextOrLineBreak() && !box->behavesLikeText())) + if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) + return false; + + if (!box.behavesLikeText() && is<InlineFlowBox>(box) && !downcast<InlineFlowBox>(box).hasTextChildren()) + return false; + + LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); + return (lineBoxContain & LineBoxContainInitialLetter); +} + +bool RootInlineBox::includeMarginForBox(InlineBox& box) const +{ + if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) return false; LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); @@ -1062,7 +1144,7 @@ bool RootInlineBox::fitsToGlyphs() const { // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); - return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); + return isHorizontal() && ((lineBoxContain & LineBoxContainGlyphs) || (lineBoxContain & LineBoxContainInitialLetter)); } bool RootInlineBox::includesRootLineBoxFontOrLeading() const @@ -1081,8 +1163,8 @@ Node* RootInlineBox::getLogicalStartBoxWithNode(InlineBox*& startBox) const return startBox->renderer().node(); } } - startBox = 0; - return 0; + startBox = nullptr; + return nullptr; } Node* RootInlineBox::getLogicalEndBoxWithNode(InlineBox*& endBox) const @@ -1095,11 +1177,11 @@ Node* RootInlineBox::getLogicalEndBoxWithNode(InlineBox*& endBox) const return endBox->renderer().node(); } } - endBox = 0; - return 0; + endBox = nullptr; + return nullptr; } -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) const char* RootInlineBox::boxName() const { return "RootInlineBox"; |