void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font*) { // See if we have a selection to paint at all. int sPos, ePos; selectionStartEnd(sPos, ePos); if (sPos >= ePos) return; Color textColor = style->color(); Color c = object()->selectionBackgroundColor(); if (!c.isValid() || c.alpha() == 0) return; // If the text color ends up being the same as the selection background, invert the selection // background. This should basically never happen, since the selection has transparency. if (textColor == c) c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); context->save(); updateGraphicsContext(context, c, c, 0); // Don't draw text at all! int y = selectionTop(); int h = selectionHeight(); context->clip(IntRect(m_x + tx, y + ty, m_width, h)); context->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); context->restore(); }
void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo) { ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(truncation() == cNoTruncation); if (renderer()->style()->visibility() != VISIBLE) return; RenderObject* parentRenderer = parent()->renderer(); ASSERT(parentRenderer); ASSERT(!parentRenderer->document().printing()); // Determine whether or not we're selected. bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; bool hasSelection = selectionState() != RenderObject::SelectionNone; if (!hasSelection || paintSelectedTextOnly) return; Color backgroundColor = renderer()->selectionBackgroundColor(); if (!backgroundColor.alpha()) return; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); if (!textShouldBePainted(textRenderer)) return; RenderStyle* style = parentRenderer->style(); ASSERT(style); int startPosition, endPosition; selectionStartEnd(startPosition, endPosition); int fragmentStartPosition = 0; int fragmentEndPosition = 0; AffineTransform fragmentTransform; unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_textFragments.at(i); ASSERT(!m_paintingResource); fragmentStartPosition = startPosition; fragmentEndPosition = endPosition; if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) continue; GraphicsContextStateSaver stateSaver(*paintInfo.context); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) paintInfo.context->concatCTM(fragmentTransform); paintInfo.context->setFillColor(backgroundColor); paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor); m_paintingResourceMode = ApplyToDefaultMode; } ASSERT(!m_paintingResource); }
IntRect RenderSVGInlineText::selectionRect(bool) { ASSERT(!needsLayout()); IntRect rect; if (selectionState() == SelectionNone) return rect; // Early exit if we're ie. a <text> within a <defs> section. if (isChildOfHiddenContainer(this)) return rect; // Now calculate startPos and endPos for painting selection. // We include a selection while endPos > 0 int startPos, endPos; if (selectionState() == SelectionInside) { // We are fully selected. startPos = 0; endPos = textLength(); } else { selectionStartEnd(startPos, endPos); if (selectionState() == SelectionStart) endPos = textLength(); else if (selectionState() == SelectionEnd) startPos = 0; } if (startPos == endPos) return rect; return computeAbsoluteRectForRange(startPos, endPos); }
IntRect RenderSVGInlineText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) { ASSERT(!needsLayout()); if (selectionState() == SelectionNone) return IntRect(); // Early exit if we're ie. a <text> within a <defs> section. if (isChildOfHiddenContainer(this)) return IntRect(); // Now calculate startPos and endPos for painting selection. // We include a selection while endPos > 0 int startPos, endPos; if (selectionState() == SelectionInside) { // We are fully selected. startPos = 0; endPos = textLength(); } else { selectionStartEnd(startPos, endPos); if (selectionState() == SelectionStart) endPos = textLength(); else if (selectionState() == SelectionEnd) startPos = 0; } if (startPos == endPos) return IntRect(); return computeRepaintRectForRange(repaintContainer, startPos, endPos); }
SelectionState InlineTextBox::getSelectionState() const { SelectionState state = getLineLayoutItem().getSelectionState(); if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { int startPos, endPos; getLineLayoutItem().selectionStartEnd(startPos, endPos); // The position after a hard line break is considered to be past its end. // See the corresponding code in InlineTextBox::isSelected. int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); // FIXME: Remove -webkit-line-break: LineBreakAfterWhiteSpace. int endOfLineAdjustmentForCSSLineBreak = getLineLayoutItem().style()->getLineBreak() == LineBreakAfterWhiteSpace ? -1 : 0; bool start = (state != SelectionEnd && startPos >= m_start && startPos <= m_start + m_len + endOfLineAdjustmentForCSSLineBreak); bool end = (state != SelectionStart && endPos > m_start && endPos <= lastSelectable); if (start && end) state = SelectionBoth; else if (start) state = SelectionStart; else if (end) state = SelectionEnd; else if ((state == SelectionEnd || startPos < m_start) && (state == SelectionStart || endPos > lastSelectable)) state = SelectionInside; else if (state == SelectionBoth) state = SelectionNone; } // If there are ellipsis following, make sure their selection is updated. if (m_truncation != cNoTruncation && root().ellipsisBox()) { EllipsisBox* ellipsis = root().ellipsisBox(); if (state != SelectionNone) { int start, end; selectionStartEnd(start, end); // The ellipsis should be considered to be selected if the end of the // selection is past the beginning of the truncation and the beginning of // the selection is before or at the beginning of the truncation. ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ? SelectionInside : SelectionNone); } else { ellipsis->setSelectionState(SelectionNone); } } return state; }
bool RenderReplaced::isSelected() const { SelectionState s = selectionState(); if (s == SelectionNone) return false; if (s == SelectionInside) return true; int selectionStart, selectionEnd; selectionStartEnd(selectionStart, selectionEnd); if (s == SelectionStart) return selectionStart == 0; int end = element()->hasChildNodes() ? element()->countChildNodes() : 1; if (s == SelectionEnd) return selectionEnd == end; if (s == SelectionBoth) return selectionStart == 0 && selectionEnd == end; ASSERT(0); return false; }
void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, bool hasSelection, bool paintSelectedTextOnly) { ASSERT(style); ASSERT(selectionStyle); int startPosition = 0; int endPosition = 0; if (hasSelection) { selectionStartEnd(startPosition, endPosition); hasSelection = mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition); } // Fast path if there is no selection, just draw the whole chunk part using the regular style TextRun textRun = constructTextRun(style, fragment); if (!hasSelection || startPosition >= endPosition) { paintTextWithShadows(context, style, textRun, fragment, 0, fragment.length); return; } // Eventually draw text using regular style until the start position of the selection if (startPosition > 0 && !paintSelectedTextOnly) paintTextWithShadows(context, style, textRun, fragment, 0, startPosition); // Draw text using selection style from the start to the end position of the selection if (style != selectionStyle) SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle); TextRun selectionTextRun = constructTextRun(selectionStyle, fragment); paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition); if (style != selectionStyle) SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style); // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly) paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length); }
void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo) { ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(truncation() == cNoTruncation); if (renderer()->style()->visibility() != VISIBLE) return; RenderObject* parentRenderer = parent()->renderer(); ASSERT(parentRenderer); ASSERT(!parentRenderer->document()->printing()); // Determine whether or not we're selected. bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; bool hasSelection = selectionState() != RenderObject::SelectionNone; if (!hasSelection || paintSelectedTextOnly) return; Color backgroundColor = renderer()->selectionBackgroundColor(); if (!backgroundColor.isValid() || !backgroundColor.alpha()) return; RenderStyle* style = parentRenderer->style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); bool hasFill = svgStyle->hasFill(); bool hasStroke = svgStyle->hasStroke(); RenderStyle* selectionStyle = style; if (hasSelection) { selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); if (selectionStyle) { const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); ASSERT(svgSelectionStyle); if (!hasFill) hasFill = svgSelectionStyle->hasFill(); if (!hasStroke) hasStroke = svgSelectionStyle->hasStroke(); } else selectionStyle = style; } int startPosition, endPosition; selectionStartEnd(startPosition, endPosition); int fragmentStartPosition = 0; int fragmentEndPosition = 0; unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_textFragments.at(i); ASSERT(!m_paintingResource); fragmentStartPosition = startPosition; fragmentEndPosition = endPosition; if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) continue; paintInfo.context->save(); if (!fragment.transform.isIdentity()) paintInfo.context->concatCTM(fragment.transform); paintInfo.context->setFillColor(backgroundColor, style->colorSpace()); paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor, style->colorSpace()); m_paintingResourceMode = ApplyToDefaultMode; paintInfo.context->restore(); } ASSERT(!m_paintingResource); }
void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline) return; ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow(); int w = width() + 2 * parent()->maxHorizontalVisualOverflow(); if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x()) return; bool isPrinting = textObject()->document()->printing(); // Determine whether or not we're selected. bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; if (!haveSelection && paintInfo.phase == PaintPhaseSelection) // When only painting the selection, don't bother to paint if there is none. return; GraphicsContext* context = paintInfo.context; // Determine whether or not we have composition underlines to draw. bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node(); bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines(); // Set our font. RenderStyle* styleToUse = object()->style(m_firstLine); int d = styleToUse->textDecorationsInEffect(); const Font* font = &styleToUse->font(); if (*font != context->font()) context->setFont(*font); // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection // and composition underlines. if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) { #if PLATFORM(MAC) // Custom highlighters go behind everything else. if (styleToUse->highlight() != nullAtom && !context->paintingDisabled()) paintCustomHighlight(tx, ty, styleToUse->highlight()); #endif if (containsComposition && !useCustomUnderlines) paintCompositionBackground(context, tx, ty, styleToUse, font, object()->document()->frame()->editor()->compositionStart(), object()->document()->frame()->editor()->compositionEnd()); paintDocumentMarkers(context, tx, ty, styleToUse, font, true); if (haveSelection && !useCustomUnderlines) paintSelection(context, tx, ty, styleToUse, font); } // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). if (m_len <= 0) return; Color textFillColor; Color textStrokeColor; float textStrokeWidth = styleToUse->textStrokeWidth(); ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow(); if (paintInfo.forceBlackText) { textFillColor = Color::black; textStrokeColor = Color::black; } else { textFillColor = styleToUse->textFillColor(); if (!textFillColor.isValid()) textFillColor = styleToUse->color(); // Make the text fill color legible against a white background if (styleToUse->forceBackgroundsToWhite()) textFillColor = correctedTextColor(textFillColor, Color::white); textStrokeColor = styleToUse->textStrokeColor(); if (!textStrokeColor.isValid()) textStrokeColor = styleToUse->color(); // Make the text stroke color legible against a white background if (styleToUse->forceBackgroundsToWhite()) textStrokeColor = correctedTextColor(textStrokeColor, Color::white); } bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); bool paintSelectedTextSeparately = false; Color selectionFillColor = textFillColor; Color selectionStrokeColor = textStrokeColor; float selectionStrokeWidth = textStrokeWidth; ShadowData* selectionShadow = textShadow; if (haveSelection) { // Check foreground color first. Color foreground = paintInfo.forceBlackText ? Color::black : object()->selectionForegroundColor(); if (foreground.isValid() && foreground != selectionFillColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionFillColor = foreground; } if (RenderStyle* pseudoStyle = object()->getCachedPseudoStyle(RenderStyle::SELECTION)) { ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); if (shadow != selectionShadow) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionShadow = shadow; } float strokeWidth = pseudoStyle->textStrokeWidth(); if (strokeWidth != selectionStrokeWidth) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeWidth = strokeWidth; } Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->textStrokeColor(); if (!stroke.isValid()) stroke = pseudoStyle->color(); if (stroke != selectionStrokeColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeColor = stroke; } } } IntPoint textOrigin(m_x + tx, m_y + ty + m_baseline); TextRun textRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; if (paintSelectedTextOnly || paintSelectedTextSeparately) selectionStartEnd(sPos, ePos); if (!paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. if (textStrokeWidth > 0) context->save(); updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(context, textRun, 0, m_truncation == cNoTruncation ? m_len : m_truncation, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); } else paintTextWithShadows(context, textRun, ePos, sPos, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); if (textStrokeWidth > 0) context->restore(); } if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) { // paint only the text that is selected if (selectionStrokeWidth > 0) context->save(); updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth); paintTextWithShadows(context, textRun, sPos, ePos, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); if (selectionStrokeWidth > 0) context->restore(); } // Paint decorations if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) { context->setStrokeColor(styleToUse->color()); paintDecoration(context, tx, ty, d, textShadow); } if (paintInfo.phase == PaintPhaseForeground) { paintDocumentMarkers(context, tx, ty, styleToUse, font, false); if (useCustomUnderlines) { const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines(); size_t numUnderlines = underlines.size(); for (size_t index = 0; index < numUnderlines; ++index) { const CompositionUnderline& underline = underlines[index]; if (underline.endOffset <= start()) // underline is completely before this run. This might be an underline that sits // before the first run we draw, or underlines that were within runs we skipped // due to truncation. continue; if (underline.startOffset <= end()) { // underline intersects this run. Paint it. paintCompositionUnderline(context, tx, ty, underline); if (underline.endOffset > end() + 1) // underline also runs into the next run. Bail now, no more marker advancement. break; } else // underline is completely after this run, bail. A later run will paint it. break; } } } }