2 * (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
6 * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "core/rendering/RenderText.h"
28 #include "core/accessibility/AXObjectCache.h"
29 #include "core/dom/Text.h"
30 #include "core/editing/TextIterator.h"
31 #include "core/frame/FrameView.h"
32 #include "core/frame/Settings.h"
33 #include "core/html/parser/TextResourceDecoder.h"
34 #include "core/rendering/AbstractInlineTextBox.h"
35 #include "core/rendering/EllipsisBox.h"
36 #include "core/rendering/InlineTextBox.h"
37 #include "core/rendering/RenderBlock.h"
38 #include "core/rendering/RenderCombineText.h"
39 #include "core/rendering/RenderLayer.h"
40 #include "core/rendering/RenderView.h"
41 #include "core/rendering/TextRunConstructor.h"
42 #include "core/rendering/break_lines.h"
43 #include "platform/fonts/Character.h"
44 #include "platform/fonts/FontCache.h"
45 #include "platform/geometry/FloatQuad.h"
46 #include "platform/text/BidiResolver.h"
47 #include "platform/text/TextBreakIterator.h"
48 #include "platform/text/TextRunIterator.h"
49 #include "wtf/text/StringBuffer.h"
50 #include "wtf/text/StringBuilder.h"
51 #include "wtf/unicode/CharacterNames.h"
54 using namespace Unicode;
58 struct SameSizeAsRenderText : public RenderObject {
59 uint32_t bitfields : 16;
65 COMPILE_ASSERT(sizeof(RenderText) == sizeof(SameSizeAsRenderText), RenderText_should_stay_small);
67 class SecureTextTimer;
68 typedef HashMap<RenderText*, SecureTextTimer*> SecureTextTimerMap;
69 static SecureTextTimerMap* gSecureTextTimers = 0;
71 class SecureTextTimer FINAL : public TimerBase {
73 SecureTextTimer(RenderText* renderText)
74 : m_renderText(renderText)
75 , m_lastTypedCharacterOffset(-1)
79 void restartWithNewText(unsigned lastTypedCharacterOffset)
81 m_lastTypedCharacterOffset = lastTypedCharacterOffset;
82 if (Settings* settings = m_renderText->document().settings())
83 startOneShot(settings->passwordEchoDurationInSeconds(), FROM_HERE);
85 void invalidate() { m_lastTypedCharacterOffset = -1; }
86 unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; }
89 virtual void fired() OVERRIDE
91 ASSERT(gSecureTextTimers->contains(m_renderText));
92 m_renderText->setText(m_renderText->text().impl(), true /* forcing setting text as it may be masked later */);
95 RenderText* m_renderText;
96 int m_lastTypedCharacterOffset;
99 static void makeCapitalized(String* string, UChar previous)
101 if (string->isNull())
104 unsigned length = string->length();
105 const StringImpl& input = *string->impl();
107 if (length >= std::numeric_limits<unsigned>::max())
110 StringBuffer<UChar> stringWithPrevious(length + 1);
111 stringWithPrevious[0] = previous == noBreakSpace ? space : previous;
112 for (unsigned i = 1; i < length + 1; i++) {
113 // Replace   with a real space since ICU no longer treats   as a word separator.
114 if (input[i - 1] == noBreakSpace)
115 stringWithPrevious[i] = space;
117 stringWithPrevious[i] = input[i - 1];
120 TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1);
124 StringBuilder result;
125 result.reserveCapacity(length);
128 int32_t startOfWord = boundary->first();
129 for (endOfWord = boundary->next(); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = boundary->next()) {
130 if (startOfWord) // Ignore first char of previous string
131 result.append(input[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord]));
132 for (int i = startOfWord + 1; i < endOfWord; i++)
133 result.append(input[i - 1]);
136 *string = result.toString();
139 RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
140 : RenderObject(!node || node->isDocumentNode() ? 0 : node)
142 , m_linesDirty(false)
143 , m_containsReversedText(false)
144 , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
147 , m_firstLineMinWidth(0)
148 , m_lastLineLineMinWidth(0)
154 // FIXME: Some clients of RenderText (and subclasses) pass Document as node to create anonymous renderer.
155 // They should be switched to passing null and using setDocumentForAnonymous.
156 if (node && node->isDocumentNode())
157 setDocumentForAnonymous(toDocument(node));
159 m_isAllASCII = m_text.containsOnlyASCII();
160 m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
163 view()->frameView()->incrementVisuallyNonEmptyCharacterCount(m_text.length());
168 RenderText::~RenderText()
170 ASSERT(!m_firstTextBox);
171 ASSERT(!m_lastTextBox);
176 const char* RenderText::renderName() const
181 bool RenderText::isTextFragment() const
186 bool RenderText::isWordBreak() const
191 void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
193 // There is no need to ever schedule paint invalidations from a style change of a text run, since
194 // we already did this for the parent of the text run.
195 // We do have to schedule layouts, though, since a style change can force us to
197 if (diff.needsFullLayout()) {
198 setNeedsLayoutAndPrefWidthsRecalc();
199 m_knownToHaveNoOverflowAndNoFallbackFonts = false;
202 RenderStyle* newStyle = style();
203 ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
204 ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
205 if (oldTransform != newStyle->textTransform() || oldSecurity != newStyle->textSecurity())
208 // This is an optimization that kicks off font load before layout.
209 // In order to make it fast, we only check if the first character of the
210 // text is included in the unicode ranges of the fonts.
211 if (!text().containsOnlyWhitespace())
212 newStyle->font().willUseFontData(text().characterStartingAt(0));
215 void RenderText::removeAndDestroyTextBoxes()
217 if (!documentBeingDestroyed()) {
218 if (firstTextBox()) {
220 RootInlineBox* next = firstTextBox()->root().nextRootBox();
224 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
227 parent()->dirtyLinesFromChangedChild(this);
232 void RenderText::willBeDestroyed()
234 if (SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->take(this) : 0)
235 delete secureTextTimer;
237 removeAndDestroyTextBoxes();
238 RenderObject::willBeDestroyed();
241 void RenderText::extractTextBox(InlineTextBox* box)
245 m_lastTextBox = box->prevTextBox();
246 if (box == m_firstTextBox)
248 if (box->prevTextBox())
249 box->prevTextBox()->setNextTextBox(0);
250 box->setPreviousTextBox(0);
251 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox())
252 curr->setExtracted();
257 void RenderText::attachTextBox(InlineTextBox* box)
262 m_lastTextBox->setNextTextBox(box);
263 box->setPreviousTextBox(m_lastTextBox);
265 m_firstTextBox = box;
266 InlineTextBox* last = box;
267 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
268 curr->setExtracted(false);
271 m_lastTextBox = last;
276 void RenderText::removeTextBox(InlineTextBox* box)
280 if (box == m_firstTextBox)
281 m_firstTextBox = box->nextTextBox();
282 if (box == m_lastTextBox)
283 m_lastTextBox = box->prevTextBox();
284 if (box->nextTextBox())
285 box->nextTextBox()->setPreviousTextBox(box->prevTextBox());
286 if (box->prevTextBox())
287 box->prevTextBox()->setNextTextBox(box->nextTextBox());
292 void RenderText::deleteTextBoxes()
294 if (firstTextBox()) {
296 for (InlineTextBox* curr = firstTextBox(); curr; curr = next) {
297 next = curr->nextTextBox();
300 m_firstTextBox = m_lastTextBox = 0;
304 PassRefPtr<StringImpl> RenderText::originalText() const
307 return (e && e->isTextNode()) ? toText(e)->dataImpl() : 0;
310 String RenderText::plainText() const
313 return blink::plainText(rangeOfContents(node()).get());
315 // FIXME: this is just a stopgap until TextIterator is adapted to support generated text.
316 StringBuilder plainTextBuilder;
317 for (InlineTextBox* textBox = firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
318 String text = m_text.substring(textBox->start(), textBox->len()).simplifyWhiteSpace(WTF::DoNotStripWhiteSpace);
319 plainTextBuilder.append(text);
320 if (textBox->nextTextBox() && textBox->nextTextBox()->start() > textBox->end() && text.length() && !text.right(1).containsOnlyWhitespace())
321 plainTextBuilder.append(space);
323 return plainTextBuilder.toString();
326 void RenderText::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
328 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
329 rects.append(enclosingIntRect(FloatRect(accumulatedOffset + box->topLeft(), box->size())));
332 static FloatRect localQuadForTextBox(InlineTextBox* box, unsigned start, unsigned end, bool useSelectionHeight)
334 unsigned realEnd = std::min(box->end() + 1, end);
335 LayoutRect r = box->localSelectionRect(start, realEnd);
337 if (!useSelectionHeight) {
338 // Change the height and y position (or width and x for vertical text)
339 // because selectionRect uses selection-specific values.
340 if (box->isHorizontal()) {
341 r.setHeight(box->height());
344 r.setWidth(box->width());
353 void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed)
355 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
356 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
357 // function to take ints causes various internal mismatches. But selectionRect takes ints, and
358 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
359 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
360 ASSERT(end == UINT_MAX || end <= INT_MAX);
361 ASSERT(start <= INT_MAX);
362 start = std::min(start, static_cast<unsigned>(INT_MAX));
363 end = std::min(end, static_cast<unsigned>(INT_MAX));
365 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
366 // Note: box->end() returns the index of the last character, not the index past it
367 if (start <= box->start() && box->end() < end) {
368 FloatRect r = box->calculateBoundaries();
369 if (useSelectionHeight) {
370 LayoutRect selectionRect = box->localSelectionRect(start, end);
371 if (box->isHorizontal()) {
372 r.setHeight(selectionRect.height().toFloat());
373 r.setY(selectionRect.y().toFloat());
375 r.setWidth(selectionRect.width().toFloat());
376 r.setX(selectionRect.x().toFloat());
379 rects.append(localToAbsoluteQuad(r, 0, wasFixed).enclosingBoundingBox());
381 // FIXME: This code is wrong. It's converting local to absolute twice. http://webkit.org/b/65722
382 FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight);
384 rects.append(localToAbsoluteQuad(rect, 0, wasFixed).enclosingBoundingBox());
389 static IntRect ellipsisRectForBox(InlineTextBox* box, unsigned startPos, unsigned endPos)
394 unsigned short truncation = box->truncation();
395 if (truncation == cNoTruncation)
399 if (EllipsisBox* ellipsis = box->root().ellipsisBox()) {
400 int ellipsisStartPosition = std::max<int>(startPos - box->start(), 0);
401 int ellipsisEndPosition = std::min<int>(endPos - box->start(), box->len());
403 // The ellipsis should be considered to be selected if the end of
404 // the selection is past the beginning of the truncation and the
405 // beginning of the selection is before or at the beginning of the truncation.
406 if (ellipsisEndPosition >= truncation && ellipsisStartPosition <= truncation)
407 return ellipsis->selectionRect();
413 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed, ClippingOption option) const
415 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
416 FloatRect boundaries = box->calculateBoundaries();
418 // Shorten the width of this text box if it ends in an ellipsis.
419 // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
420 IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(box, 0, textLength()) : IntRect();
421 if (!ellipsisRect.isEmpty()) {
422 if (style()->isHorizontalWritingMode())
423 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
425 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
427 quads.append(localToAbsoluteQuad(boundaries, 0, wasFixed));
431 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
433 absoluteQuads(quads, wasFixed, NoClipping);
436 void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed)
438 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
439 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
440 // function to take ints causes various internal mismatches. But selectionRect takes ints, and
441 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
442 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
443 ASSERT(end == UINT_MAX || end <= INT_MAX);
444 ASSERT(start <= INT_MAX);
445 start = std::min(start, static_cast<unsigned>(INT_MAX));
446 end = std::min(end, static_cast<unsigned>(INT_MAX));
448 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
449 // Note: box->end() returns the index of the last character, not the index past it
450 if (start <= box->start() && box->end() < end) {
451 FloatRect r = box->calculateBoundaries();
452 if (useSelectionHeight) {
453 LayoutRect selectionRect = box->localSelectionRect(start, end);
454 if (box->isHorizontal()) {
455 r.setHeight(selectionRect.height().toFloat());
456 r.setY(selectionRect.y().toFloat());
458 r.setWidth(selectionRect.width().toFloat());
459 r.setX(selectionRect.x().toFloat());
462 quads.append(localToAbsoluteQuad(r, 0, wasFixed));
464 FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight);
466 quads.append(localToAbsoluteQuad(rect, 0, wasFixed));
471 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
473 static bool lineDirectionPointFitsInBox(int pointLineDirection, InlineTextBox* box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
475 shouldAffinityBeDownstream = AlwaysDownstream;
477 // the x coordinate is equal to the left edge of this box
478 // the affinity must be downstream so the position doesn't jump back to the previous line
479 // except when box is the first box in the line
480 if (pointLineDirection <= box->logicalLeft()) {
481 shouldAffinityBeDownstream = !box->prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
485 // and the x coordinate is to the left of the right edge of this box
486 // check to see if position goes in this box
487 if (pointLineDirection < box->logicalRight()) {
488 shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
492 // box is first on line
493 // and the x coordinate is to the left of the first text box left edge
494 if (!box->prevLeafChildIgnoringLineBreak() && pointLineDirection < box->logicalLeft())
497 if (!box->nextLeafChildIgnoringLineBreak()) {
498 // box is last on line
499 // and the x coordinate is to the right of the last text box right edge
500 // generate VisiblePosition, use UPSTREAM affinity if possible
501 shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
508 static PositionWithAffinity createPositionWithAffinityForBox(const InlineBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
510 EAffinity affinity = VP_DEFAULT_AFFINITY;
511 switch (shouldAffinityBeDownstream) {
512 case AlwaysDownstream:
513 affinity = DOWNSTREAM;
516 affinity = VP_UPSTREAM_IF_POSSIBLE;
518 case UpstreamIfPositionIsNotAtStart:
519 affinity = offset > box->caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
522 int textStartOffset = box->renderer().isText() ? toRenderText(box->renderer()).textStartOffset() : 0;
523 return box->renderer().createPositionWithAffinity(offset + textStartOffset, affinity);
526 static PositionWithAffinity createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(const InlineTextBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
531 if (offset && static_cast<unsigned>(offset) < box->len())
532 return createPositionWithAffinityForBox(box, box->start() + offset, shouldAffinityBeDownstream);
534 bool positionIsAtStartOfBox = !offset;
535 if (positionIsAtStartOfBox == box->isLeftToRightDirection()) {
536 // offset is on the left edge
538 const InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak();
539 if ((prevBox && prevBox->bidiLevel() == box->bidiLevel())
540 || box->renderer().containingBlock()->style()->direction() == box->direction()) // FIXME: left on 12CBA
541 return createPositionWithAffinityForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream);
543 if (prevBox && prevBox->bidiLevel() > box->bidiLevel()) {
544 // e.g. left of B in aDC12BAb
545 const InlineBox* leftmostBox;
547 leftmostBox = prevBox;
548 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
549 } while (prevBox && prevBox->bidiLevel() > box->bidiLevel());
550 return createPositionWithAffinityForBox(leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
553 if (!prevBox || prevBox->bidiLevel() < box->bidiLevel()) {
554 // e.g. left of D in aDC12BAb
555 const InlineBox* rightmostBox;
556 const InlineBox* nextBox = box;
558 rightmostBox = nextBox;
559 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
560 } while (nextBox && nextBox->bidiLevel() >= box->bidiLevel());
561 return createPositionWithAffinityForBox(rightmostBox,
562 box->isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
565 return createPositionWithAffinityForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream);
568 const InlineBox* nextBox = box->nextLeafChildIgnoringLineBreak();
569 if ((nextBox && nextBox->bidiLevel() == box->bidiLevel())
570 || box->renderer().containingBlock()->style()->direction() == box->direction())
571 return createPositionWithAffinityForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream);
573 // offset is on the right edge
574 if (nextBox && nextBox->bidiLevel() > box->bidiLevel()) {
575 // e.g. right of C in aDC12BAb
576 const InlineBox* rightmostBox;
578 rightmostBox = nextBox;
579 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
580 } while (nextBox && nextBox->bidiLevel() > box->bidiLevel());
581 return createPositionWithAffinityForBox(rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
584 if (!nextBox || nextBox->bidiLevel() < box->bidiLevel()) {
585 // e.g. right of A in aDC12BAb
586 const InlineBox* leftmostBox;
587 const InlineBox* prevBox = box;
589 leftmostBox = prevBox;
590 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
591 } while (prevBox && prevBox->bidiLevel() >= box->bidiLevel());
592 return createPositionWithAffinityForBox(leftmostBox,
593 box->isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
596 return createPositionWithAffinityForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream);
599 PositionWithAffinity RenderText::positionForPoint(const LayoutPoint& point)
601 if (!firstTextBox() || textLength() == 0)
602 return createPositionWithAffinity(0, DOWNSTREAM);
604 LayoutUnit pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y();
605 LayoutUnit pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x();
606 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode();
608 InlineTextBox* lastBox = 0;
609 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
610 if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
611 box = box->nextTextBox();
613 RootInlineBox& rootBox = box->root();
614 LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
615 if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
616 LayoutUnit bottom = rootBox.selectionBottom();
617 if (rootBox.nextRootBox())
618 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
620 if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
621 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
622 if (lineDirectionPointFitsInBox(pointLineDirection, box, shouldAffinityBeDownstream))
623 return createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(box, box->offsetForPosition(pointLineDirection.toFloat()), shouldAffinityBeDownstream);
630 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
631 lineDirectionPointFitsInBox(pointLineDirection, lastBox, shouldAffinityBeDownstream);
632 return createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(lastBox, lastBox->offsetForPosition(pointLineDirection.toFloat()) + lastBox->start(), shouldAffinityBeDownstream);
634 return createPositionWithAffinity(0, DOWNSTREAM);
637 LayoutRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
642 ASSERT(inlineBox->isInlineTextBox());
643 if (!inlineBox->isInlineTextBox())
646 InlineTextBox* box = toInlineTextBox(inlineBox);
648 int height = box->root().selectionHeight();
649 int top = box->root().selectionTop();
651 // Go ahead and round left to snap it to the nearest pixel.
652 float left = box->positionForOffset(caretOffset);
654 // Distribute the caret's width to either side of the offset.
655 int caretWidthLeftOfOffset = caretWidth / 2;
656 left -= caretWidthLeftOfOffset;
657 int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset;
661 float rootLeft = box->root().logicalLeft();
662 float rootRight = box->root().logicalRight();
664 // FIXME: should we use the width of the root inline box or the
665 // width of the containing block for this?
666 if (extraWidthToEndOfLine)
667 *extraWidthToEndOfLine = (box->root().logicalWidth() + rootLeft) - (left + 1);
669 RenderBlock* cb = containingBlock();
670 RenderStyle* cbStyle = cb->style();
674 leftEdge = std::min<float>(0, rootLeft);
675 rightEdge = std::max<float>(cb->logicalWidth().toFloat(), rootRight);
677 bool rightAligned = false;
678 switch (cbStyle->textAlign()) {
690 rightAligned = !cbStyle->isLeftToRightDirection();
693 rightAligned = cbStyle->isLeftToRightDirection();
697 // for dir=auto, use inlineBoxBidiLevel() to test the correct direction for the cursor.
698 if (rightAligned && (node() && node()->selfOrAncestorHasDirAutoAttribute())) {
699 if (inlineBox->bidiLevel()%2 != 1)
700 rightAligned = false;
704 left = std::max(left, leftEdge);
705 left = std::min(left, rootRight - caretWidth);
707 left = std::min(left, rightEdge - caretWidthRightOfOffset);
708 left = std::max(left, rootLeft);
711 return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth);
714 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, TextDirection textDirection, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
716 if (style()->hasTextCombine() && isCombineText()) {
717 const RenderCombineText* combineText = toRenderCombineText(this);
718 if (combineText->isCombined())
719 return combineText->combinedTextWidth(f);
722 if (f.isFixedPitch() && f.fontDescription().variant() == FontVariantNormal && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) {
723 float monospaceCharacterWidth = f.spaceWidth();
727 StringImpl& text = *m_text.impl();
728 for (int i = start; i < start + len; i++) {
731 if (c == space || c == newlineCharacter) {
732 w += monospaceCharacterWidth;
734 } else if (c == characterTabulation) {
735 if (style()->collapseWhiteSpace()) {
736 w += monospaceCharacterWidth;
739 w += f.tabWidth(style()->tabSize(), xPos + w);
745 w += monospaceCharacterWidth;
748 if (isSpace && i > start)
749 w += f.fontDescription().wordSpacing();
754 TextRun run = constructTextRun(const_cast<RenderText*>(this), f, this, start, len, style(), textDirection);
755 run.setCharactersLength(textLength() - start);
756 ASSERT(run.charactersLength() >= run.length());
758 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath());
759 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize());
761 FontCachePurgePreventer fontCachePurgePreventer;
762 return f.width(run, fallbackFonts, glyphOverflow);
765 void RenderText::trimmedPrefWidths(float leadWidth,
766 float& firstLineMinWidth, bool& hasBreakableStart,
767 float& lastLineMinWidth, bool& hasBreakableEnd,
768 bool& hasBreakableChar, bool& hasBreak,
769 float& firstLineMaxWidth, float& lastLineMaxWidth,
770 float& minWidth, float& maxWidth, bool& stripFrontSpaces,
771 TextDirection direction)
773 bool collapseWhiteSpace = style()->collapseWhiteSpace();
774 if (!collapseWhiteSpace)
775 stripFrontSpaces = false;
777 if (m_hasTab || preferredLogicalWidthsDirty())
778 computePreferredLogicalWidths(leadWidth);
780 hasBreakableStart = !stripFrontSpaces && m_hasBreakableStart;
781 hasBreakableEnd = m_hasBreakableEnd;
783 int len = textLength();
785 if (!len || (stripFrontSpaces && text().impl()->containsOnlyWhitespace())) {
786 firstLineMinWidth = 0;
787 lastLineMinWidth = 0;
788 firstLineMaxWidth = 0;
789 lastLineMaxWidth = 0;
796 minWidth = m_minWidth;
797 maxWidth = m_maxWidth;
799 firstLineMinWidth = m_firstLineMinWidth;
800 lastLineMinWidth = m_lastLineLineMinWidth;
802 hasBreakableChar = m_hasBreakableChar;
803 hasBreak = m_hasBreak;
806 StringImpl& text = *m_text.impl();
807 if (text[0] == space || (text[0] == newlineCharacter && !style()->preserveNewline()) || text[0] == characterTabulation) {
808 const Font& font = style()->font(); // FIXME: This ignores first-line.
809 if (stripFrontSpaces) {
810 const UChar spaceChar = space;
811 float spaceWidth = font.width(constructTextRun(this, font, &spaceChar, 1, style(), direction));
812 maxWidth -= spaceWidth;
814 maxWidth += font.fontDescription().wordSpacing();
818 stripFrontSpaces = collapseWhiteSpace && m_hasEndWhiteSpace;
820 if (!style()->autoWrap() || minWidth > maxWidth)
823 // Compute our max widths by scanning the string for newlines.
825 const Font& f = style()->font(); // FIXME: This ignores first-line.
826 bool firstLine = true;
827 firstLineMaxWidth = maxWidth;
828 lastLineMaxWidth = maxWidth;
829 for (int i = 0; i < len; i++) {
831 while (i + linelen < len && text[i + linelen] != newlineCharacter)
835 lastLineMaxWidth = widthFromCache(f, i, linelen, leadWidth + lastLineMaxWidth, direction, 0, 0);
839 firstLineMaxWidth = lastLineMaxWidth;
842 } else if (firstLine) {
843 firstLineMaxWidth = 0;
849 // A <pre> run that ends with a newline, as in, e.g.,
850 // <pre>Some text\n\n<span>More text</pre>
851 lastLineMaxWidth = 0;
857 float RenderText::minLogicalWidth() const
859 if (preferredLogicalWidthsDirty())
860 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
865 float RenderText::maxLogicalWidth() const
867 if (preferredLogicalWidthsDirty())
868 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
873 void RenderText::computePreferredLogicalWidths(float leadWidth)
875 HashSet<const SimpleFontData*> fallbackFonts;
876 GlyphOverflow glyphOverflow;
877 computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
879 // We shouldn't change our mind once we "know".
880 ASSERT(!m_knownToHaveNoOverflowAndNoFallbackFonts || (fallbackFonts.isEmpty() && glyphOverflow.isZero()));
881 m_knownToHaveNoOverflowAndNoFallbackFonts = fallbackFonts.isEmpty() && glyphOverflow.isZero();
884 static inline float hyphenWidth(RenderText* renderer, const Font& font, TextDirection direction)
886 RenderStyle* style = renderer->style();
887 return font.width(constructTextRun(renderer, font, style->hyphenString().string(), style, direction));
890 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
892 ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
896 m_firstLineMinWidth = 0;
897 m_lastLineLineMinWidth = 0;
902 float currMinWidth = 0;
903 float currMaxWidth = 0;
904 m_hasBreakableChar = false;
907 m_hasBreakableStart = false;
908 m_hasBreakableEnd = false;
909 m_hasEndWhiteSpace = false;
911 RenderStyle* styleToUse = style();
912 const Font& f = styleToUse->font(); // FIXME: This ignores first-line.
913 float wordSpacing = styleToUse->wordSpacing();
914 int len = textLength();
915 LazyLineBreakIterator breakIterator(m_text, styleToUse->locale());
916 bool needsWordSpacing = false;
917 bool ignoringSpaces = false;
918 bool isSpace = false;
919 bool firstWord = true;
920 bool firstLine = true;
921 int nextBreakable = -1;
922 int lastWordBoundary = 0;
923 float cachedWordTrailingSpaceWidth[2] = { 0, 0 }; // LTR, RTL
925 int firstGlyphLeftOverflow = -1;
927 bool breakAll = (styleToUse->wordBreak() == BreakAllWordBreak || styleToUse->wordBreak() == BreakWordBreak) && styleToUse->autoWrap();
929 TextRun textRun(text());
930 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
931 BidiCharacterRun* run;
932 TextDirection textDirection = styleToUse->direction();
933 if (isOverride(styleToUse->unicodeBidi())) {
936 BidiStatus status(textDirection, false);
937 bidiResolver.setStatus(status);
938 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0));
939 bool hardLineBreak = false;
940 bool reorderRuns = false;
941 bidiResolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length()), NoVisualOverride, hardLineBreak, reorderRuns);
942 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
943 run = bidiRuns.firstRun();
946 for (int i = 0; i < len; i++) {
947 UChar c = uncheckedCharacterAt(i);
950 // Treat adjacent runs with the same resolved directionality
951 // (TextDirection as opposed to WTF::Unicode::Direction) as belonging
952 // to the same run to avoid breaking unnecessarily.
953 while (i >= run->stop() || (run->next() && run->next()->direction() == run->direction()))
957 ASSERT(i <= run->stop());
958 textDirection = run->direction();
961 bool previousCharacterIsSpace = isSpace;
962 bool isNewline = false;
963 if (c == newlineCharacter) {
964 if (styleToUse->preserveNewline()) {
970 } else if (c == characterTabulation) {
971 if (!styleToUse->collapseWhiteSpace()) {
977 isSpace = c == space;
980 bool isBreakableLocation = isNewline || (isSpace && styleToUse->autoWrap());
982 m_hasBreakableStart = isBreakableLocation;
984 m_hasBreakableEnd = isBreakableLocation;
985 m_hasEndWhiteSpace = isNewline || isSpace;
988 if (!ignoringSpaces && styleToUse->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
989 ignoringSpaces = true;
991 if (ignoringSpaces && !isSpace)
992 ignoringSpaces = false;
994 // Ignore spaces and soft hyphens
995 if (ignoringSpaces) {
996 ASSERT(lastWordBoundary == i);
999 } else if (c == softHyphen) {
1000 currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow);
1001 if (firstGlyphLeftOverflow < 0)
1002 firstGlyphLeftOverflow = glyphOverflow.left;
1003 lastWordBoundary = i + 1;
1007 bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable);
1008 bool betweenWords = true;
1010 while (c != newlineCharacter && c != space && c != characterTabulation && (c != softHyphen)) {
1014 c = uncheckedCharacterAt(j);
1015 if (isBreakable(breakIterator, j, nextBreakable) && characterAt(j - 1) != softHyphen)
1018 betweenWords = false;
1023 // Terminate word boundary at bidi run boundary.
1025 j = std::min(j, run->stop() + 1);
1026 int wordLen = j - i;
1028 bool isSpace = (j < len) && c == space;
1030 // Non-zero only when kerning is enabled, in which case we measure words with their trailing
1031 // space, then subtract its width.
1032 float wordTrailingSpaceWidth = 0;
1033 if (isSpace && (f.fontDescription().typesettingFeatures() & Kerning)) {
1034 ASSERT(textDirection >=0 && textDirection <= 1);
1035 if (!cachedWordTrailingSpaceWidth[textDirection])
1036 cachedWordTrailingSpaceWidth[textDirection] = f.width(constructTextRun(this, f, &space, 1, styleToUse, textDirection)) + wordSpacing;
1037 wordTrailingSpaceWidth = cachedWordTrailingSpaceWidth[textDirection];
1041 if (wordTrailingSpaceWidth && isSpace)
1042 w = widthFromCache(f, i, wordLen + 1, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow) - wordTrailingSpaceWidth;
1044 w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow);
1045 if (c == softHyphen)
1046 currMinWidth += hyphenWidth(this, f, textDirection);
1049 if (firstGlyphLeftOverflow < 0)
1050 firstGlyphLeftOverflow = glyphOverflow.left;
1053 if (lastWordBoundary == i)
1056 currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow);
1057 lastWordBoundary = j;
1060 bool isCollapsibleWhiteSpace = (j < len) && styleToUse->isCollapsibleWhiteSpace(c);
1061 if (j < len && styleToUse->autoWrap())
1062 m_hasBreakableChar = true;
1064 // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
1065 // last word in the run.
1066 if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
1067 currMaxWidth += wordSpacing;
1071 // If the first character in the run is breakable, then we consider ourselves to have a beginning
1072 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
1073 // being appended to a previous text run when considering the total minimum width of the containing block.
1075 m_hasBreakableChar = true;
1076 m_firstLineMinWidth = hasBreak ? 0 : currMinWidth;
1078 m_lastLineLineMinWidth = currMinWidth;
1080 if (currMinWidth > m_minWidth)
1081 m_minWidth = currMinWidth;
1086 // Nowrap can never be broken, so don't bother setting the
1087 // breakable character boolean. Pre can only be broken if we encounter a newline.
1088 if (style()->autoWrap() || isNewline)
1089 m_hasBreakableChar = true;
1091 if (currMinWidth > m_minWidth)
1092 m_minWidth = currMinWidth;
1095 if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
1099 if (!styleToUse->autoWrap())
1100 m_firstLineMinWidth = currMaxWidth;
1103 if (currMaxWidth > m_maxWidth)
1104 m_maxWidth = currMaxWidth;
1107 TextRun run = constructTextRun(this, f, this, i, 1, styleToUse, textDirection);
1108 run.setCharactersLength(len - i);
1109 run.setUseComplexCodePath(!canUseSimpleFontCodePath());
1110 ASSERT(run.charactersLength() >= run.length());
1111 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize());
1112 run.setXPos(leadWidth + currMaxWidth);
1114 currMaxWidth += f.width(run);
1115 glyphOverflow.right = 0;
1116 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
1118 ASSERT(lastWordBoundary == i);
1123 bidiResolver.runs().deleteRuns();
1125 if (firstGlyphLeftOverflow > 0)
1126 glyphOverflow.left = firstGlyphLeftOverflow;
1128 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
1129 currMaxWidth += wordSpacing;
1131 m_minWidth = std::max(currMinWidth, m_minWidth);
1132 m_maxWidth = std::max(currMaxWidth, m_maxWidth);
1134 if (!styleToUse->autoWrap())
1135 m_minWidth = m_maxWidth;
1137 if (styleToUse->whiteSpace() == PRE) {
1139 m_firstLineMinWidth = m_maxWidth;
1140 m_lastLineLineMinWidth = currMaxWidth;
1143 clearPreferredLogicalWidthsDirty();
1146 bool RenderText::isAllCollapsibleWhitespace() const
1148 unsigned length = textLength();
1150 for (unsigned i = 0; i < length; ++i) {
1151 if (!style()->isCollapsibleWhiteSpace(characters8()[i]))
1156 for (unsigned i = 0; i < length; ++i) {
1157 if (!style()->isCollapsibleWhiteSpace(characters16()[i]))
1163 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
1166 StringImpl& text = *m_text.impl();
1168 for (currPos = from;
1169 currPos < from + len && (text[currPos] == newlineCharacter || text[currPos] == space || text[currPos] == characterTabulation);
1171 return currPos >= (from + len);
1174 FloatPoint RenderText::firstRunOrigin() const
1176 return IntPoint(firstRunX(), firstRunY());
1179 float RenderText::firstRunX() const
1181 return m_firstTextBox ? m_firstTextBox->x() : 0;
1184 float RenderText::firstRunY() const
1186 return m_firstTextBox ? m_firstTextBox->y() : 0;
1189 void RenderText::setSelectionState(SelectionState state)
1191 RenderObject::setSelectionState(state);
1193 if (canUpdateSelectionOnRootLineBoxes()) {
1194 if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
1195 int startPos, endPos;
1196 selectionStartEnd(startPos, endPos);
1197 if (selectionState() == SelectionStart) {
1198 endPos = textLength();
1200 // to handle selection from end of text to end of line
1201 if (startPos && startPos == endPos)
1202 startPos = endPos - 1;
1203 } else if (selectionState() == SelectionEnd)
1206 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1207 if (box->isSelected(startPos, endPos)) {
1208 box->root().setHasSelectedChildren(true);
1212 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1213 box->root().setHasSelectedChildren(state == SelectionInside);
1218 // The containing block can be null in case of an orphaned tree.
1219 RenderBlock* containingBlock = this->containingBlock();
1220 if (containingBlock && !containingBlock->isRenderView())
1221 containingBlock->setSelectionState(state);
1224 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force)
1226 if (!force && equal(m_text.impl(), text.get()))
1229 unsigned oldLen = textLength();
1230 unsigned newLen = text->length();
1231 int delta = newLen - oldLen;
1232 unsigned end = len ? offset + len - 1 : offset;
1234 RootInlineBox* firstRootBox = 0;
1235 RootInlineBox* lastRootBox = 0;
1237 bool dirtiedLines = false;
1239 // Dirty all text boxes that include characters in between offset and offset+len.
1240 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1241 // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264
1242 // Text run is entirely before the affected range.
1243 if (curr->end() < offset)
1246 // Text run is entirely after the affected range.
1247 if (curr->start() > end) {
1248 curr->offsetRun(delta);
1249 RootInlineBox* root = &curr->root();
1250 if (!firstRootBox) {
1251 firstRootBox = root;
1252 // The affected area was in between two runs. Go ahead and mark the root box of
1253 // the run after the affected area as dirty.
1254 firstRootBox->markDirty();
1255 dirtiedLines = true;
1258 } else if (curr->end() >= offset && curr->end() <= end) {
1259 // Text run overlaps with the left end of the affected range.
1260 curr->dirtyLineBoxes();
1261 dirtiedLines = true;
1262 } else if (curr->start() <= offset && curr->end() >= end) {
1263 // Text run subsumes the affected range.
1264 curr->dirtyLineBoxes();
1265 dirtiedLines = true;
1266 } else if (curr->start() <= end && curr->end() >= end) {
1267 // Text run overlaps with right end of the affected range.
1268 curr->dirtyLineBoxes();
1269 dirtiedLines = true;
1273 // Now we have to walk all of the clean lines and adjust their cached line break information
1274 // to reflect our updated offsets.
1276 lastRootBox = lastRootBox->nextRootBox();
1278 RootInlineBox* prev = firstRootBox->prevRootBox();
1280 firstRootBox = prev;
1281 } else if (lastTextBox()) {
1282 ASSERT(!lastRootBox);
1283 firstRootBox = &lastTextBox()->root();
1284 firstRootBox->markDirty();
1285 dirtiedLines = true;
1287 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
1288 if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
1289 curr->setLineBreakPos(clampToInteger(curr->lineBreakPos() + delta));
1292 // If the text node is empty, dirty the line where new text will be inserted.
1293 if (!firstTextBox() && parent()) {
1294 parent()->dirtyLinesFromChangedChild(this);
1295 dirtiedLines = true;
1298 m_linesDirty = dirtiedLines;
1299 setText(text, force || dirtiedLines);
1302 void RenderText::transformText()
1304 if (RefPtr<StringImpl> textToTransform = originalText())
1305 setText(textToTransform.release(), true);
1308 static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
1310 if (o->isRenderInline())
1314 return toRenderText(o)->text().isEmpty();
1317 UChar RenderText::previousCharacter() const
1319 // find previous text renderer if one exists
1320 const RenderObject* previousText = previousInPreOrder();
1321 for (; previousText; previousText = previousText->previousInPreOrder())
1322 if (!isInlineFlowOrEmptyText(previousText))
1325 if (previousText && previousText->isText())
1326 if (StringImpl* previousString = toRenderText(previousText)->text().impl())
1327 prev = (*previousString)[previousString->length() - 1];
1331 void RenderText::addLayerHitTestRects(LayerHitTestRects&, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
1333 // Text nodes aren't event targets, so don't descend any further.
1336 void applyTextTransform(const RenderStyle* style, String& text, UChar previousCharacter)
1341 switch (style->textTransform()) {
1345 makeCapitalized(&text, previousCharacter);
1348 text = text.upper(style->locale());
1351 text = text.lower(style->locale());
1356 void RenderText::setTextInternal(PassRefPtr<StringImpl> text)
1362 applyTextTransform(style(), m_text, previousCharacter());
1364 // We use the same characters here as for list markers.
1365 // See the listMarkerText function in RenderListMarker.cpp.
1366 switch (style()->textSecurity()) {
1370 secureText(whiteBullet);
1376 secureText(blackSquare);
1381 ASSERT(!isBR() || (textLength() == 1 && m_text[0] == newlineCharacter));
1383 m_isAllASCII = m_text.containsOnlyASCII();
1384 m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
1387 void RenderText::secureText(UChar mask)
1389 if (!m_text.length())
1392 int lastTypedCharacterOffsetToReveal = -1;
1394 SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0;
1395 if (secureTextTimer && secureTextTimer->isActive()) {
1396 lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset();
1397 if (lastTypedCharacterOffsetToReveal >= 0)
1398 revealedText = m_text[lastTypedCharacterOffsetToReveal];
1402 if (lastTypedCharacterOffsetToReveal >= 0) {
1403 m_text.replace(lastTypedCharacterOffsetToReveal, 1, String(&revealedText, 1));
1404 // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency.
1405 secureTextTimer->invalidate();
1409 void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
1413 if (!force && equal(m_text.impl(), text.get()))
1416 setTextInternal(text);
1417 // If preferredLogicalWidthsDirty() of an orphan child is true, RenderObjectChildList::
1418 // insertChildNode() fails to set true to owner. To avoid that, we call
1419 // setNeedsLayoutAndPrefWidthsRecalc() only if this RenderText has parent.
1421 setNeedsLayoutAndPrefWidthsRecalc();
1422 m_knownToHaveNoOverflowAndNoFallbackFonts = false;
1424 if (AXObjectCache* cache = document().existingAXObjectCache())
1425 cache->textChanged(this);
1428 void RenderText::dirtyLineBoxes(bool fullLayout)
1432 else if (!m_linesDirty) {
1433 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1434 box->dirtyLineBoxes();
1436 m_linesDirty = false;
1439 InlineTextBox* RenderText::createTextBox()
1441 return new InlineTextBox(*this);
1444 InlineTextBox* RenderText::createInlineTextBox()
1446 InlineTextBox* textBox = createTextBox();
1447 if (!m_firstTextBox)
1448 m_firstTextBox = m_lastTextBox = textBox;
1450 m_lastTextBox->setNextTextBox(textBox);
1451 textBox->setPreviousTextBox(m_lastTextBox);
1452 m_lastTextBox = textBox;
1454 textBox->setIsText(true);
1458 void RenderText::positionLineBox(InlineBox* box)
1460 InlineTextBox* s = toInlineTextBox(box);
1462 // FIXME: should not be needed!!!
1464 // We want the box to be destroyed.
1465 s->remove(DontMarkLineBoxes);
1466 if (m_firstTextBox == s)
1467 m_firstTextBox = s->nextTextBox();
1469 s->prevTextBox()->setNextTextBox(s->nextTextBox());
1470 if (m_lastTextBox == s)
1471 m_lastTextBox = s->prevTextBox();
1473 s->nextTextBox()->setPreviousTextBox(s->prevTextBox());
1478 m_containsReversedText |= !s->isLeftToRightDirection();
1481 float RenderText::width(unsigned from, unsigned len, float xPos, TextDirection textDirection, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1483 if (from >= textLength())
1486 if (from + len > textLength())
1487 len = textLength() - from;
1489 return width(from, len, style(firstLine)->font(), xPos, textDirection, fallbackFonts, glyphOverflow);
1492 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, TextDirection textDirection, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1494 ASSERT(from + len <= textLength());
1499 if (&f == &style()->font()) {
1500 if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) {
1501 if (fallbackFonts) {
1502 ASSERT(glyphOverflow);
1503 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
1504 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
1505 // We shouldn't change our mind once we "know".
1506 ASSERT(!m_knownToHaveNoOverflowAndNoFallbackFonts
1507 || (fallbackFonts->isEmpty() && glyphOverflow->isZero()));
1508 m_knownToHaveNoOverflowAndNoFallbackFonts = fallbackFonts->isEmpty() && glyphOverflow->isZero();
1512 w = maxLogicalWidth();
1515 w = widthFromCache(f, from, len, xPos, textDirection, fallbackFonts, glyphOverflow);
1518 TextRun run = constructTextRun(const_cast<RenderText*>(this), f, this, from, len, style(), textDirection);
1519 run.setCharactersLength(textLength() - from);
1520 ASSERT(run.charactersLength() >= run.length());
1522 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath());
1523 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize());
1525 w = f.width(run, fallbackFonts, glyphOverflow);
1531 IntRect RenderText::linesBoundingBox() const
1535 ASSERT(!firstTextBox() == !lastTextBox()); // Either both are null or both exist.
1536 if (firstTextBox() && lastTextBox()) {
1537 // Return the width of the minimal left side and the maximal right side.
1538 float logicalLeftSide = 0;
1539 float logicalRightSide = 0;
1540 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1541 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide)
1542 logicalLeftSide = curr->logicalLeft();
1543 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide)
1544 logicalRightSide = curr->logicalRight();
1547 bool isHorizontal = style()->isHorizontalWritingMode();
1549 float x = isHorizontal ? logicalLeftSide : firstTextBox()->x();
1550 float y = isHorizontal ? firstTextBox()->y() : logicalLeftSide;
1551 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x;
1552 float height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
1553 result = enclosingIntRect(FloatRect(x, y, width, height));
1559 LayoutRect RenderText::linesVisualOverflowBoundingBox() const
1561 if (!firstTextBox())
1562 return LayoutRect();
1564 // Return the width of the minimal left side and the maximal right side.
1565 LayoutUnit logicalLeftSide = LayoutUnit::max();
1566 LayoutUnit logicalRightSide = LayoutUnit::min();
1567 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1568 LayoutRect logicalVisualOverflow = curr->logicalOverflowRect();
1569 logicalLeftSide = std::min(logicalLeftSide, logicalVisualOverflow.x());
1570 logicalRightSide = std::max(logicalRightSide, logicalVisualOverflow.maxX());
1573 LayoutUnit logicalTop = firstTextBox()->logicalTopVisualOverflow();
1574 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
1575 LayoutUnit logicalHeight = lastTextBox()->logicalBottomVisualOverflow() - logicalTop;
1577 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1578 if (!style()->isHorizontalWritingMode())
1579 rect = rect.transposedRect();
1583 LayoutRect RenderText::clippedOverflowRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const
1585 // This method doesn't support paintInvalidationState, but invalidateTreeIfNeeded() never reaches RenderText.
1586 ASSERT(!paintInvalidationState);
1587 return parent()->clippedOverflowRectForPaintInvalidation(paintInvalidationContainer);
1590 LayoutRect RenderText::selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const
1592 ASSERT(!needsLayout());
1594 if (selectionState() == SelectionNone)
1595 return LayoutRect();
1596 RenderBlock* cb = containingBlock();
1598 return LayoutRect();
1600 // Now calculate startPos and endPos for painting selection.
1601 // We include a selection while endPos > 0
1602 int startPos, endPos;
1603 if (selectionState() == SelectionInside) {
1604 // We are fully selected.
1606 endPos = textLength();
1608 selectionStartEnd(startPos, endPos);
1609 if (selectionState() == SelectionStart)
1610 endPos = textLength();
1611 else if (selectionState() == SelectionEnd)
1615 if (startPos == endPos)
1619 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1620 rect.unite(box->localSelectionRect(startPos, endPos));
1621 rect.unite(ellipsisRectForBox(box, startPos, endPos));
1624 mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0);
1628 int RenderText::caretMinOffset() const
1630 InlineTextBox* box = firstTextBox();
1633 int minOffset = box->start();
1634 for (box = box->nextTextBox(); box; box = box->nextTextBox())
1635 minOffset = std::min<int>(minOffset, box->start());
1639 int RenderText::caretMaxOffset() const
1641 InlineTextBox* box = lastTextBox();
1643 return textLength();
1645 int maxOffset = box->start() + box->len();
1646 for (box = box->prevTextBox(); box; box = box->prevTextBox())
1647 maxOffset = std::max<int>(maxOffset, box->start() + box->len());
1651 unsigned RenderText::renderedTextLength() const
1654 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1659 int RenderText::previousOffset(int current) const
1661 if (isAllASCII() || m_text.is8Bit())
1664 StringImpl* textImpl = m_text.impl();
1665 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1669 long result = iterator->preceding(current);
1670 if (result == TextBreakDone)
1671 result = current - 1;
1679 #define HANGUL_CHOSEONG_START (0x1100)
1680 #define HANGUL_CHOSEONG_END (0x115F)
1681 #define HANGUL_JUNGSEONG_START (0x1160)
1682 #define HANGUL_JUNGSEONG_END (0x11A2)
1683 #define HANGUL_JONGSEONG_START (0x11A8)
1684 #define HANGUL_JONGSEONG_END (0x11F9)
1685 #define HANGUL_SYLLABLE_START (0xAC00)
1686 #define HANGUL_SYLLABLE_END (0xD7AF)
1687 #define HANGUL_JONGSEONG_COUNT (28)
1698 inline bool isHangulLVT(UChar32 character)
1700 return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
1703 inline bool isMark(UChar32 c)
1705 int8_t charType = u_charType(c);
1706 return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK;
1709 inline bool isRegionalIndicator(UChar32 c)
1711 // National flag emoji each consists of a pair of regional indicator symbols.
1712 return 0x1F1E6 <= c && c <= 0x1F1FF;
1717 int RenderText::previousOffsetForBackwardDeletion(int current) const
1721 StringImpl& text = *m_text.impl();
1723 bool sawRegionalIndicator = false;
1724 while (current > 0) {
1725 if (U16_IS_TRAIL(text[--current]))
1730 UChar32 character = text.characterStartingAt(current);
1732 if (sawRegionalIndicator) {
1733 // We don't check if the pair of regional indicator symbols before current position can actually be combined
1734 // into a flag, and just delete it. This may not agree with how the pair is rendered in edge cases,
1735 // but is good enough in practice.
1736 if (isRegionalIndicator(character))
1738 // Don't delete a preceding character that isn't a regional indicator symbol.
1739 U16_FWD_1_UNSAFE(text, current);
1742 // We don't combine characters in Armenian ... Limbu range for backward deletion.
1743 if ((character >= 0x0530) && (character < 0x1950))
1746 if (isRegionalIndicator(character)) {
1747 sawRegionalIndicator = true;
1751 if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F))
1759 character = text.characterStartingAt(current);
1760 if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
1763 if (character < HANGUL_JUNGSEONG_START)
1764 state = HangulStateL;
1765 else if (character < HANGUL_JONGSEONG_START)
1766 state = HangulStateV;
1767 else if (character < HANGUL_SYLLABLE_START)
1768 state = HangulStateT;
1770 state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
1772 while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
1775 if (character <= HANGUL_CHOSEONG_END)
1776 state = HangulStateL;
1777 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
1778 state = HangulStateLV;
1779 else if (character > HANGUL_JUNGSEONG_END)
1780 state = HangulStateBreak;
1783 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
1784 state = HangulStateV;
1785 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
1786 state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
1787 else if (character < HANGUL_JUNGSEONG_START)
1788 state = HangulStateBreak;
1791 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
1794 if (state == HangulStateBreak)
1803 // Platforms other than Unix-like delete by one code point.
1804 if (U16_IS_TRAIL(m_text[--current]))
1812 int RenderText::nextOffset(int current) const
1814 if (isAllASCII() || m_text.is8Bit())
1817 StringImpl* textImpl = m_text.impl();
1818 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1822 long result = iterator->following(current);
1823 if (result == TextBreakDone)
1824 result = current + 1;
1829 bool RenderText::computeCanUseSimpleFontCodePath() const
1831 if (isAllASCII() || m_text.is8Bit())
1833 return Character::characterRangeCodePath(characters16(), length()) == SimplePath;
1838 void RenderText::checkConsistency() const
1840 #ifdef CHECK_CONSISTENCY
1841 const InlineTextBox* prev = 0;
1842 for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) {
1843 ASSERT(child->renderer() == this);
1844 ASSERT(child->prevTextBox() == prev);
1847 ASSERT(prev == m_lastTextBox);
1853 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)
1855 if (!gSecureTextTimers)
1856 gSecureTextTimers = new SecureTextTimerMap;
1858 SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this);
1859 if (!secureTextTimer) {
1860 secureTextTimer = new SecureTextTimer(this);
1861 gSecureTextTimers->add(this, secureTextTimer);
1863 secureTextTimer->restartWithNewText(lastTypedCharacterOffset);
1866 PassRefPtr<AbstractInlineTextBox> RenderText::firstAbstractInlineTextBox()
1868 return AbstractInlineTextBox::getOrCreate(this, m_firstTextBox);
1871 } // namespace blink