#include "core/CSSPropertyNames.h"
#include "core/dom/Document.h"
+#include "core/paint/BoxPainter.h"
+#include "core/paint/InlineFlowBoxPainter.h"
#include "core/rendering/HitTestResult.h"
#include "core/rendering/InlineTextBox.h"
#include "core/rendering/RenderBlock.h"
if (curr->renderer().isText()) {
InlineTextBox* text = toInlineTextBox(curr);
RenderText& rt = text->renderer();
+ float space = 0;
if (rt.textLength()) {
if (needsWordSpacing && isSpaceOrNewline(rt.characterAt(text->start())))
- logicalLeft += rt.style(isFirstLineStyle())->font().fontDescription().wordSpacing();
+ space = rt.style(isFirstLineStyle())->font().fontDescription().wordSpacing();
needsWordSpacing = !isSpaceOrNewline(rt.characterAt(text->end()));
}
- text->setLogicalLeft(logicalLeft);
+ if (isLeftToRightDirection()) {
+ logicalLeft += space;
+ text->setLogicalLeft(logicalLeft);
+ } else {
+ text->setLogicalLeft(logicalLeft);
+ logicalLeft += space;
+ }
if (knownToHaveNoOverflow())
minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
logicalLeft += text->logicalWidth();
if (m_overflow)
m_overflow.clear();
- // Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored.
+ // Visual overflow just includes overflow for stuff we need to issues paint invalidations for ourselves. Self-painting layers are ignored.
// Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in
// transforms, relative positioning, etc.
LayoutRect logicalLayoutOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom)));
void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
{
- LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom));
- flipForWritingMode(overflowRect);
- overflowRect.moveBy(paintOffset);
-
- if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
- return;
-
- if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
- // Add ourselves to the paint info struct's list of inlines that need to paint their
- // outlines.
- if (renderer().style()->visibility() == VISIBLE && renderer().hasOutline() && !isRootInlineBox()) {
- RenderInline& inlineFlow = toRenderInline(renderer());
-
- RenderBlock* cb = 0;
- bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation();
- if (containingBlockPaintsContinuationOutline) {
- // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
- // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
- // anonymous blocks. In this case, it is better to bail out and paint it ourself.
- RenderBlock* enclosingAnonymousBlock = renderer().containingBlock();
- if (!enclosingAnonymousBlock->isAnonymousBlock()) {
- containingBlockPaintsContinuationOutline = false;
- } else {
- cb = enclosingAnonymousBlock->containingBlock();
- for (RenderBoxModelObject* box = boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
- if (box->hasSelfPaintingLayer()) {
- containingBlockPaintsContinuationOutline = false;
- break;
- }
- }
- }
- }
-
- if (containingBlockPaintsContinuationOutline) {
- // Add ourselves to the containing block of the entire continuation so that it can
- // paint us atomically.
- cb->addContinuationWithOutline(toRenderInline(renderer().node()->renderer()));
- } else if (!inlineFlow.isInlineElementContinuation()) {
- paintInfo.outlineObjects()->add(&inlineFlow);
- }
- }
- } else if (paintInfo.phase == PaintPhaseMask) {
- paintMask(paintInfo, paintOffset);
- return;
- } else if (paintInfo.phase == PaintPhaseForeground) {
- // Paint our background, border and box-shadow.
- paintBoxDecorationBackground(paintInfo, paintOffset);
- }
-
- // Paint our children.
- if (paintInfo.phase != PaintPhaseSelfOutline) {
- PaintInfo childInfo(paintInfo);
- childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;
-
- if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&renderer()))
- childInfo.paintingRoot = 0;
- else
- childInfo.updatePaintingRootForChildren(&renderer());
-
- for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
- if (curr->renderer().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
- curr->paint(childInfo, paintOffset, lineTop, lineBottom);
- }
- }
-}
-
-void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, CompositeOperator op)
-{
- if (fillLayer.next())
- paintFillLayers(paintInfo, c, *fillLayer.next(), rect, op);
- paintFillLayer(paintInfo, c, fillLayer, rect, op);
+ InlineFlowBoxPainter(*this).paint(paintInfo, paintOffset, lineTop, lineBottom);
}
bool InlineFlowBox::boxShadowCanBeAppliedToBackground(const FillLayer& lastBackgroundLayer) const
return (!hasFillImage && !renderer().style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent();
}
-void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, CompositeOperator op)
-{
- StyleImage* img = fillLayer.image();
- bool hasFillImage = img && img->canRender(renderer(), renderer().style()->effectiveZoom());
- if ((!hasFillImage && !renderer().style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) {
- boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
- } else if (renderer().style()->boxDecorationBreak() == DCLONE) {
- GraphicsContextStateSaver stateSaver(*paintInfo.context);
- paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height()));
- boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
- } else {
- // We have a fill image that spans multiple lines.
- // We need to adjust tx and ty by the width of all previous lines.
- // Think of background painting on inlines as though you had one long line, a single continuous
- // strip. Even though that strip has been broken up across multiple lines, you still paint it
- // as though you had one single line. This means each line has to pick up the background where
- // the previous line left off.
- LayoutUnit logicalOffsetOnLine = 0;
- LayoutUnit totalLogicalWidth;
- if (renderer().style()->direction() == LTR) {
- for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
- logicalOffsetOnLine += curr->logicalWidth();
- totalLogicalWidth = logicalOffsetOnLine;
- for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
- totalLogicalWidth += curr->logicalWidth();
- } else {
- for (InlineFlowBox* curr = nextLineBox(); curr; curr = curr->nextLineBox())
- logicalOffsetOnLine += curr->logicalWidth();
- totalLogicalWidth = logicalOffsetOnLine;
- for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox())
- totalLogicalWidth += curr->logicalWidth();
- }
- LayoutUnit stripX = rect.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
- LayoutUnit stripY = rect.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
- LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : static_cast<LayoutUnit>(width());
- LayoutUnit stripHeight = isHorizontal() ? static_cast<LayoutUnit>(height()) : totalLogicalWidth;
-
- GraphicsContextStateSaver stateSaver(*paintInfo.context);
- paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height()));
- boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, LayoutRect(stripX, stripY, stripWidth, stripHeight), BackgroundBleedNone, this, rect.size(), op);
- }
-}
-
-void InlineFlowBox::paintBoxShadow(const PaintInfo& info, RenderStyle* s, ShadowStyle shadowStyle, const LayoutRect& paintRect)
-{
- if ((!prevLineBox() && !nextLineBox()) || !parent())
- boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle);
- else {
- // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't
- // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines
- boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge());
- }
-}
-
void InlineFlowBox::constrainToLineTopAndBottomIfNeeded(LayoutRect& rect) const
{
bool noQuirksMode = renderer().document().inNoQuirksMode();
}
}
-static LayoutRect clipRectForNinePieceImageStrip(InlineFlowBox* box, const NinePieceImage& image, const LayoutRect& paintRect)
-{
- LayoutRect clipRect(paintRect);
- RenderStyle* style = box->renderer().style();
- LayoutBoxExtent outsets = style->imageOutsets(image);
- if (box->isHorizontal()) {
- clipRect.setY(paintRect.y() - outsets.top());
- clipRect.setHeight(paintRect.height() + outsets.top() + outsets.bottom());
- if (box->includeLogicalLeftEdge()) {
- clipRect.setX(paintRect.x() - outsets.left());
- clipRect.setWidth(paintRect.width() + outsets.left());
- }
- if (box->includeLogicalRightEdge())
- clipRect.setWidth(clipRect.width() + outsets.right());
- } else {
- clipRect.setX(paintRect.x() - outsets.left());
- clipRect.setWidth(paintRect.width() + outsets.left() + outsets.right());
- if (box->includeLogicalLeftEdge()) {
- clipRect.setY(paintRect.y() - outsets.top());
- clipRect.setHeight(paintRect.height() + outsets.top());
- }
- if (box->includeLogicalRightEdge())
- clipRect.setHeight(clipRect.height() + outsets.bottom());
- }
- return clipRect;
-}
-
-void InlineFlowBox::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
-{
- ASSERT(paintInfo.phase == PaintPhaseForeground);
- if (!paintInfo.shouldPaintWithinRoot(&renderer()) || renderer().style()->visibility() != VISIBLE)
- return;
-
- // You can use p::first-line to specify a background. If so, the root line boxes for
- // a line may actually have to paint a background.
- RenderStyle* styleToUse = renderer().style(isFirstLineStyle());
- bool shouldPaintBoxDecorationBackground;
- if (parent())
- shouldPaintBoxDecorationBackground = renderer().hasBoxDecorationBackground();
- else
- shouldPaintBoxDecorationBackground = isFirstLineStyle() && styleToUse != renderer().style();
-
- if (!shouldPaintBoxDecorationBackground)
- return;
-
- // Pixel snap background/border painting.
- LayoutRect frameRect = roundedFrameRect();
- constrainToLineTopAndBottomIfNeeded(frameRect);
-
- // Move x/y to our coordinates.
- LayoutRect localRect(frameRect);
- flipForWritingMode(localRect);
- LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();
-
- LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size());
-
- // Shadow comes first and is behind the background and border.
- if (!boxModelObject()->boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, this))
- paintBoxShadow(paintInfo, styleToUse, Normal, paintRect);
-
- Color backgroundColor = renderer().resolveColor(styleToUse, CSSPropertyBackgroundColor);
- paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), paintRect);
- paintBoxShadow(paintInfo, styleToUse, Inset, paintRect);
-
- // :first-line cannot be used to put borders on a line. Always paint borders with our
- // non-first-line style.
- if (parent() && renderer().style()->hasBorder()) {
- const NinePieceImage& borderImage = renderer().style()->borderImage();
- StyleImage* borderImageSource = borderImage.image();
- bool hasBorderImage = borderImageSource && borderImageSource->canRender(renderer(), styleToUse->effectiveZoom());
- if (hasBorderImage && !borderImageSource->isLoaded())
- return; // Don't paint anything while we wait for the image to load.
-
- // The simple case is where we either have no border image or we are the only box for this object.
- // In those cases only a single call to draw is required.
- if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) {
- boxModelObject()->paintBorder(paintInfo, paintRect, renderer().style(isFirstLineStyle()), BackgroundBleedNone, includeLogicalLeftEdge(), includeLogicalRightEdge());
- } else {
- // We have a border image that spans multiple lines.
- // We need to adjust tx and ty by the width of all previous lines.
- // Think of border image painting on inlines as though you had one long line, a single continuous
- // strip. Even though that strip has been broken up across multiple lines, you still paint it
- // as though you had one single line. This means each line has to pick up the image where
- // the previous line left off.
- // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
- // but it isn't even clear how this should work at all.
- LayoutUnit logicalOffsetOnLine = 0;
- for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
- logicalOffsetOnLine += curr->logicalWidth();
- LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
- for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
- totalLogicalWidth += curr->logicalWidth();
- LayoutUnit stripX = adjustedPaintOffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
- LayoutUnit stripY = adjustedPaintOffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
- LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
- LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
-
- LayoutRect clipRect = clipRectForNinePieceImageStrip(this, borderImage, paintRect);
- GraphicsContextStateSaver stateSaver(*paintInfo.context);
- paintInfo.context->clip(clipRect);
- boxModelObject()->paintBorder(paintInfo, LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer().style(isFirstLineStyle()));
- }
- }
-}
-
-void InlineFlowBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
-{
- if (!paintInfo.shouldPaintWithinRoot(&renderer()) || renderer().style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
- return;
-
- // Pixel snap mask painting.
- LayoutRect frameRect = roundedFrameRect();
-
- constrainToLineTopAndBottomIfNeeded(frameRect);
-
- // Move x/y to our coordinates.
- LayoutRect localRect(frameRect);
- flipForWritingMode(localRect);
- LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();
-
- const NinePieceImage& maskNinePieceImage = renderer().style()->maskBoxImage();
- StyleImage* maskBoxImage = renderer().style()->maskBoxImage().image();
-
- // Figure out if we need to push a transparency layer to render our mask.
- bool pushTransparencyLayer = false;
- bool compositedMask = renderer().hasLayer() && boxModelObject()->layer()->hasCompositedMask();
- bool flattenCompositingLayers = renderer().view()->frameView() && renderer().view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
- CompositeOperator compositeOp = CompositeSourceOver;
- if (!compositedMask || flattenCompositingLayers) {
- if ((maskBoxImage && renderer().style()->maskLayers().hasImage()) || renderer().style()->maskLayers().next())
- pushTransparencyLayer = true;
-
- compositeOp = CompositeDestinationIn;
- if (pushTransparencyLayer) {
- paintInfo.context->setCompositeOperation(CompositeDestinationIn);
- paintInfo.context->beginTransparencyLayer(1.0f);
- compositeOp = CompositeSourceOver;
- }
- }
-
- LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size());
- paintFillLayers(paintInfo, Color::transparent, renderer().style()->maskLayers(), paintRect, compositeOp);
-
- bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer(), renderer().style()->effectiveZoom());
- if (!hasBoxImage || !maskBoxImage->isLoaded()) {
- if (pushTransparencyLayer)
- paintInfo.context->endLayer();
- return; // Don't paint anything while we wait for the image to load.
- }
-
- // The simple case is where we are the only box for this object. In those
- // cases only a single call to draw is required.
- if (!prevLineBox() && !nextLineBox()) {
- boxModelObject()->paintNinePieceImage(paintInfo.context, LayoutRect(adjustedPaintOffset, frameRect.size()), renderer().style(), maskNinePieceImage, compositeOp);
- } else {
- // We have a mask image that spans multiple lines.
- // We need to adjust _tx and _ty by the width of all previous lines.
- LayoutUnit logicalOffsetOnLine = 0;
- for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
- logicalOffsetOnLine += curr->logicalWidth();
- LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
- for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
- totalLogicalWidth += curr->logicalWidth();
- LayoutUnit stripX = adjustedPaintOffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
- LayoutUnit stripY = adjustedPaintOffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
- LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
- LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
-
- LayoutRect clipRect = clipRectForNinePieceImageStrip(this, maskNinePieceImage, paintRect);
- GraphicsContextStateSaver stateSaver(*paintInfo.context);
- paintInfo.context->clip(clipRect);
- boxModelObject()->paintNinePieceImage(paintInfo.context, LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer().style(), maskNinePieceImage, compositeOp);
- }
-
- if (pushTransparencyLayer)
- paintInfo.context->endLayer();
-}
-
InlineBox* InlineFlowBox::firstLeafChild() const
{
InlineBox* leaf = 0;
return leaf;
}
-RenderObject::SelectionState InlineFlowBox::selectionState()
+RenderObject::SelectionState InlineFlowBox::selectionState() const
{
return RenderObject::SelectionNone;
}