1 /*
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)
7 *
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.
12 *
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.
17 *
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.
22 *
23 */
24
25 #include "config.h"
26 #include "RenderText.h"
27
28 #include "AXObjectCache.h"
29 #include "EllipsisBox.h"
30 #include "FloatQuad.h"
31 #include "FontTranscoder.h"
32 #include "FrameView.h"
33 #include "InlineTextBox.h"
34 #include "Range.h"
35 #include "RenderArena.h"
36 #include "RenderBlock.h"
37 #include "RenderCombineText.h"
38 #include "RenderLayer.h"
39 #include "RenderView.h"
40 #include "Text.h"
41 #include "TextBreakIterator.h"
42 #include "TextResourceDecoder.h"
43 #include "TextRun.h"
44 #include "VisiblePosition.h"
45 #include "break_lines.h"
46 #include <wtf/AlwaysInline.h>
47 #include <wtf/text/StringBuffer.h>
48 #include <wtf/unicode/CharacterNames.h>
49
50 using namespace std;
51 using namespace WTF;
52 using namespace Unicode;
53
54 namespace WebCore {
55
makeCapitalized(String * string,UChar previous)56 static void makeCapitalized(String* string, UChar previous)
57 {
58 if (string->isNull())
59 return;
60
61 unsigned length = string->length();
62 const UChar* characters = string->characters();
63
64 if (length >= numeric_limits<unsigned>::max())
65 CRASH();
66
67 StringBuffer stringWithPrevious(length + 1);
68 stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous;
69 for (unsigned i = 1; i < length + 1; i++) {
70 // Replace   with a real space since ICU no longer treats   as a word separator.
71 if (characters[i - 1] == noBreakSpace)
72 stringWithPrevious[i] = ' ';
73 else
74 stringWithPrevious[i] = characters[i - 1];
75 }
76
77 TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1);
78 if (!boundary)
79 return;
80
81 StringBuffer data(length);
82
83 int32_t endOfWord;
84 int32_t startOfWord = textBreakFirst(boundary);
85 for (endOfWord = textBreakNext(boundary); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = textBreakNext(boundary)) {
86 if (startOfWord != 0) // Ignore first char of previous string
87 data[startOfWord - 1] = characters[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord]);
88 for (int i = startOfWord + 1; i < endOfWord; i++)
89 data[i - 1] = characters[i - 1];
90 }
91
92 *string = String::adopt(data);
93 }
94
RenderText(Node * node,PassRefPtr<StringImpl> str)95 RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
96 : RenderObject(node)
97 , m_minWidth(-1)
98 , m_text(str)
99 , m_firstTextBox(0)
100 , m_lastTextBox(0)
101 , m_maxWidth(-1)
102 , m_beginMinWidth(0)
103 , m_endMinWidth(0)
104 , m_hasTab(false)
105 , m_linesDirty(false)
106 , m_containsReversedText(false)
107 , m_isAllASCII(m_text.containsOnlyASCII())
108 , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
109 , m_needsTranscoding(false)
110 {
111 ASSERT(m_text);
112
113 setIsText();
114
115 // FIXME: It would be better to call this only if !m_text->containsOnlyWhitespace().
116 // But that might slow things down, and maybe should only be done if visuallyNonEmpty
117 // is still false. Not making any change for now, but should consider in the future.
118 view()->frameView()->setIsVisuallyNonEmpty();
119 }
120
121 #ifndef NDEBUG
122
~RenderText()123 RenderText::~RenderText()
124 {
125 ASSERT(!m_firstTextBox);
126 ASSERT(!m_lastTextBox);
127 }
128
129 #endif
130
renderName() const131 const char* RenderText::renderName() const
132 {
133 return "RenderText";
134 }
135
isTextFragment() const136 bool RenderText::isTextFragment() const
137 {
138 return false;
139 }
140
isWordBreak() const141 bool RenderText::isWordBreak() const
142 {
143 return false;
144 }
145
updateNeedsTranscoding()146 void RenderText::updateNeedsTranscoding()
147 {
148 const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
149 m_needsTranscoding = fontTranscoder().needsTranscoding(style()->font().fontDescription(), encoding);
150 }
151
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)152 void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
153 {
154 // There is no need to ever schedule repaints from a style change of a text run, since
155 // we already did this for the parent of the text run.
156 // We do have to schedule layouts, though, since a style change can force us to
157 // need to relayout.
158 if (diff == StyleDifferenceLayout) {
159 setNeedsLayoutAndPrefWidthsRecalc();
160 m_knownToHaveNoOverflowAndNoFallbackFonts = false;
161 }
162
163 bool needsResetText = false;
164 if (!oldStyle) {
165 updateNeedsTranscoding();
166 needsResetText = m_needsTranscoding;
167 } else if (oldStyle->font().needsTranscoding() != style()->font().needsTranscoding() || (style()->font().needsTranscoding() && oldStyle->font().family().family() != style()->font().family().family())) {
168 updateNeedsTranscoding();
169 needsResetText = true;
170 }
171
172 ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
173 ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
174 if (needsResetText || oldTransform != style()->textTransform() || oldSecurity != style()->textSecurity()) {
175 if (RefPtr<StringImpl> textToTransform = originalText())
176 setText(textToTransform.release(), true);
177 }
178 }
179
removeAndDestroyTextBoxes()180 void RenderText::removeAndDestroyTextBoxes()
181 {
182 if (!documentBeingDestroyed()) {
183 if (firstTextBox()) {
184 if (isBR()) {
185 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
186 if (next)
187 next->markDirty();
188 }
189 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
190 box->remove();
191 } else if (parent())
192 parent()->dirtyLinesFromChangedChild(this);
193 }
194 deleteTextBoxes();
195 }
196
destroy()197 void RenderText::destroy()
198 {
199 removeAndDestroyTextBoxes();
200 RenderObject::destroy();
201 }
202
extractTextBox(InlineTextBox * box)203 void RenderText::extractTextBox(InlineTextBox* box)
204 {
205 checkConsistency();
206
207 m_lastTextBox = box->prevTextBox();
208 if (box == m_firstTextBox)
209 m_firstTextBox = 0;
210 if (box->prevTextBox())
211 box->prevTextBox()->setNextTextBox(0);
212 box->setPreviousTextBox(0);
213 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox())
214 curr->setExtracted();
215
216 checkConsistency();
217 }
218
attachTextBox(InlineTextBox * box)219 void RenderText::attachTextBox(InlineTextBox* box)
220 {
221 checkConsistency();
222
223 if (m_lastTextBox) {
224 m_lastTextBox->setNextTextBox(box);
225 box->setPreviousTextBox(m_lastTextBox);
226 } else
227 m_firstTextBox = box;
228 InlineTextBox* last = box;
229 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
230 curr->setExtracted(false);
231 last = curr;
232 }
233 m_lastTextBox = last;
234
235 checkConsistency();
236 }
237
removeTextBox(InlineTextBox * box)238 void RenderText::removeTextBox(InlineTextBox* box)
239 {
240 checkConsistency();
241
242 if (box == m_firstTextBox)
243 m_firstTextBox = box->nextTextBox();
244 if (box == m_lastTextBox)
245 m_lastTextBox = box->prevTextBox();
246 if (box->nextTextBox())
247 box->nextTextBox()->setPreviousTextBox(box->prevTextBox());
248 if (box->prevTextBox())
249 box->prevTextBox()->setNextTextBox(box->nextTextBox());
250
251 checkConsistency();
252 }
253
deleteTextBoxes()254 void RenderText::deleteTextBoxes()
255 {
256 if (firstTextBox()) {
257 RenderArena* arena = renderArena();
258 InlineTextBox* next;
259 for (InlineTextBox* curr = firstTextBox(); curr; curr = next) {
260 next = curr->nextTextBox();
261 curr->destroy(arena);
262 }
263 m_firstTextBox = m_lastTextBox = 0;
264 }
265 }
266
originalText() const267 PassRefPtr<StringImpl> RenderText::originalText() const
268 {
269 Node* e = node();
270 return (e && e->isTextNode()) ? static_cast<Text*>(e)->dataImpl() : 0;
271 }
272
absoluteRects(Vector<IntRect> & rects,int tx,int ty)273 void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
274 {
275 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
276 rects.append(enclosingIntRect(FloatRect(tx + box->x(), ty + box->y(), box->width(), box->height())));
277 }
278
absoluteRectsForRange(Vector<IntRect> & rects,unsigned start,unsigned end,bool useSelectionHeight)279 void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
280 {
281 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
282 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
283 // function to take ints causes various internal mismatches. But selectionRect takes ints, and
284 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
285 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
286 ASSERT(end == UINT_MAX || end <= INT_MAX);
287 ASSERT(start <= INT_MAX);
288 start = min(start, static_cast<unsigned>(INT_MAX));
289 end = min(end, static_cast<unsigned>(INT_MAX));
290
291 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
292 // Note: box->end() returns the index of the last character, not the index past it
293 if (start <= box->start() && box->end() < end) {
294 IntRect r = IntRect(box->x(), box->y(), box->logicalWidth(), box->logicalHeight());
295 if (useSelectionHeight) {
296 IntRect selectionRect = box->selectionRect(0, 0, start, end);
297 r.setHeight(selectionRect.height());
298 r.setY(selectionRect.y());
299 }
300 FloatPoint origin = localToAbsolute(r.location());
301 r.setX(origin.x());
302 r.setY(origin.y());
303 rects.append(r);
304 } else {
305 unsigned realEnd = min(box->end() + 1, end);
306 IntRect r = box->selectionRect(0, 0, start, realEnd);
307 if (!r.isEmpty()) {
308 if (!useSelectionHeight) {
309 // change the height and y position because selectionRect uses selection-specific values
310 r.setHeight(box->logicalHeight());
311 r.setY(box->y());
312 }
313 FloatPoint origin = localToAbsolute(r.location());
314 localToAbsolute(origin);
315 r.setX(origin.x());
316 r.setY(origin.y());
317 rects.append(r);
318 }
319 }
320 }
321 }
322
ellipsisRectForBox(InlineTextBox * box,unsigned startPos,unsigned endPos)323 static IntRect ellipsisRectForBox(InlineTextBox* box, unsigned startPos, unsigned endPos)
324 {
325 if (!box)
326 return IntRect();
327
328 unsigned short truncation = box->truncation();
329 if (truncation == cNoTruncation)
330 return IntRect();
331
332 IntRect rect;
333 if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) {
334 int ellipsisStartPosition = max<int>(startPos - box->start(), 0);
335 int ellipsisEndPosition = min<int>(endPos - box->start(), box->len());
336
337 // The ellipsis should be considered to be selected if the end of
338 // the selection is past the beginning of the truncation and the
339 // beginning of the selection is before or at the beginning of the truncation.
340 if (ellipsisEndPosition >= truncation && ellipsisStartPosition <= truncation)
341 return ellipsis->selectionRect(0, 0);
342 }
343
344 return IntRect();
345 }
346
absoluteQuads(Vector<FloatQuad> & quads,ClippingOption option)347 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, ClippingOption option)
348 {
349 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
350 IntRect boundaries = box->calculateBoundaries();
351
352 // Shorten the width of this text box if it ends in an ellipsis.
353 IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(box, 0, textLength()) : IntRect();
354 if (!ellipsisRect.isEmpty()) {
355 if (style()->isHorizontalWritingMode())
356 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
357 else
358 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
359 }
360 quads.append(localToAbsoluteQuad(FloatRect(boundaries)));
361 }
362 }
363
absoluteQuads(Vector<FloatQuad> & quads)364 void RenderText::absoluteQuads(Vector<FloatQuad>& quads)
365 {
366 absoluteQuads(quads, NoClipping);
367 }
368
absoluteQuadsForRange(Vector<FloatQuad> & quads,unsigned start,unsigned end,bool useSelectionHeight)369 void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight)
370 {
371 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
372 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
373 // function to take ints causes various internal mismatches. But selectionRect takes ints, and
374 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
375 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
376 ASSERT(end == UINT_MAX || end <= INT_MAX);
377 ASSERT(start <= INT_MAX);
378 start = min(start, static_cast<unsigned>(INT_MAX));
379 end = min(end, static_cast<unsigned>(INT_MAX));
380
381 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
382 // Note: box->end() returns the index of the last character, not the index past it
383 if (start <= box->start() && box->end() < end) {
384 IntRect r(box->calculateBoundaries());
385 if (useSelectionHeight) {
386 IntRect selectionRect = box->selectionRect(0, 0, start, end);
387 if (box->isHorizontal()) {
388 r.setHeight(selectionRect.height());
389 r.setY(selectionRect.y());
390 } else {
391 r.setWidth(selectionRect.width());
392 r.setX(selectionRect.x());
393 }
394 }
395 quads.append(localToAbsoluteQuad(FloatRect(r)));
396 } else {
397 unsigned realEnd = min(box->end() + 1, end);
398 IntRect r = box->selectionRect(0, 0, start, realEnd);
399 if (r.height()) {
400 if (!useSelectionHeight) {
401 // change the height and y position because selectionRect uses selection-specific values
402 if (box->isHorizontal()) {
403 r.setHeight(box->logicalHeight());
404 r.setY(box->y());
405 } else {
406 r.setWidth(box->logicalHeight());
407 r.setX(box->x());
408 }
409 }
410 quads.append(localToAbsoluteQuad(FloatRect(r)));
411 }
412 }
413 }
414 }
415
findNextInlineTextBox(int offset,int & pos) const416 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const
417 {
418 // The text runs point to parts of the RenderText's m_text
419 // (they don't include '\n')
420 // Find the text run that includes the character at offset
421 // and return pos, which is the position of the char in the run.
422
423 if (!m_firstTextBox)
424 return 0;
425
426 InlineTextBox* s = m_firstTextBox;
427 int off = s->len();
428 while (offset > off && s->nextTextBox()) {
429 s = s->nextTextBox();
430 off = s->start() + s->len();
431 }
432 // we are now in the correct text run
433 pos = (offset > off ? s->len() : s->len() - (off - offset) );
434 return s;
435 }
436
positionForPoint(const IntPoint & point)437 VisiblePosition RenderText::positionForPoint(const IntPoint& point)
438 {
439 if (!firstTextBox() || textLength() == 0)
440 return createVisiblePosition(0, DOWNSTREAM);
441
442 // Get the offset for the position, since this will take rtl text into account.
443 int offset;
444
445 int pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y();
446 int pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x();
447
448 // FIXME: We should be able to roll these special cases into the general cases in the loop below.
449 if (firstTextBox() && pointBlockDirection < firstTextBox()->root()->selectionBottom() && pointLineDirection < firstTextBox()->logicalLeft()) {
450 // at the y coordinate of the first line or above
451 // and the x coordinate is to the left of the first text box left edge
452 offset = firstTextBox()->offsetForPosition(pointLineDirection);
453 return createVisiblePosition(offset + firstTextBox()->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
454 }
455 if (lastTextBox() && pointBlockDirection >= lastTextBox()->root()->selectionTop() && pointLineDirection >= lastTextBox()->logicalRight()) {
456 // at the y coordinate of the last line or below
457 // and the x coordinate is to the right of the last text box right edge
458 offset = lastTextBox()->offsetForPosition(pointLineDirection);
459 return createVisiblePosition(offset + lastTextBox()->start(), VP_UPSTREAM_IF_POSSIBLE);
460 }
461
462 InlineTextBox* lastBoxAbove = 0;
463 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
464 RootInlineBox* rootBox = box->root();
465 if (pointBlockDirection >= rootBox->selectionTop()) {
466 int bottom = rootBox->selectionBottom();
467 if (rootBox->nextRootBox())
468 bottom = min(bottom, rootBox->nextRootBox()->lineTop());
469 if (pointBlockDirection < bottom) {
470 offset = box->offsetForPosition(pointLineDirection);
471
472 if (pointLineDirection == box->logicalLeft())
473 // the x coordinate is equal to the left edge of this box
474 // the affinity must be downstream so the position doesn't jump back to the previous line
475 return createVisiblePosition(offset + box->start(), DOWNSTREAM);
476
477 if (pointLineDirection < box->logicalRight())
478 // and the x coordinate is to the left of the right edge of this box
479 // check to see if position goes in this box
480 return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
481
482 if (!box->prevOnLine() && pointLineDirection < box->logicalLeft())
483 // box is first on line
484 // and the x coordinate is to the left of the first text box left edge
485 return createVisiblePosition(offset + box->start(), DOWNSTREAM);
486
487 if (!box->nextOnLine())
488 // box is last on line
489 // and the x coordinate is to the right of the last text box right edge
490 // generate VisiblePosition, use UPSTREAM affinity if possible
491 return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
492 }
493 lastBoxAbove = box;
494 }
495 }
496
497 return createVisiblePosition(lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM);
498 }
499
localCaretRect(InlineBox * inlineBox,int caretOffset,int * extraWidthToEndOfLine)500 IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
501 {
502 if (!inlineBox)
503 return IntRect();
504
505 ASSERT(inlineBox->isInlineTextBox());
506 if (!inlineBox->isInlineTextBox())
507 return IntRect();
508
509 InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox);
510
511 int height = box->root()->selectionHeight();
512 int top = box->root()->selectionTop();
513
514 // Go ahead and round left to snap it to the nearest pixel.
515 float left = box->positionForOffset(caretOffset);
516
517 // Distribute the caret's width to either side of the offset.
518 int caretWidthLeftOfOffset = caretWidth / 2;
519 left -= caretWidthLeftOfOffset;
520 int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset;
521
522 left = roundf(left);
523
524 float rootLeft = box->root()->logicalLeft();
525 float rootRight = box->root()->logicalRight();
526
527 // FIXME: should we use the width of the root inline box or the
528 // width of the containing block for this?
529 if (extraWidthToEndOfLine)
530 *extraWidthToEndOfLine = (box->root()->logicalWidth() + rootLeft) - (left + 1);
531
532 RenderBlock* cb = containingBlock();
533 RenderStyle* cbStyle = cb->style();
534 float leftEdge;
535 float rightEdge;
536 if (style()->autoWrap()) {
537 leftEdge = cb->logicalLeft();
538 rightEdge = cb->logicalRight();
539 } else {
540 leftEdge = min(static_cast<float>(cb->logicalLeft()), rootLeft);
541 rightEdge = max(static_cast<float>(cb->logicalRight()), rootRight);
542 }
543
544 bool rightAligned = false;
545 switch (cbStyle->textAlign()) {
546 case TAAUTO:
547 case JUSTIFY:
548 rightAligned = !cbStyle->isLeftToRightDirection();
549 break;
550 case RIGHT:
551 case WEBKIT_RIGHT:
552 rightAligned = true;
553 break;
554 case LEFT:
555 case WEBKIT_LEFT:
556 case CENTER:
557 case WEBKIT_CENTER:
558 break;
559 case TASTART:
560 rightAligned = !cbStyle->isLeftToRightDirection();
561 break;
562 case TAEND:
563 rightAligned = cbStyle->isLeftToRightDirection();
564 break;
565 }
566
567 if (rightAligned) {
568 left = max(left, leftEdge);
569 left = min(left, rootRight - caretWidth);
570 } else {
571 left = min(left, rightEdge - caretWidthRightOfOffset);
572 left = max(left, rootLeft);
573 }
574
575 return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth);
576 }
577
widthFromCache(const Font & f,int start,int len,float xPos,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const578 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
579 {
580 if (style()->hasTextCombine() && isCombineText()) {
581 const RenderCombineText* combineText = toRenderCombineText(this);
582 if (combineText->isCombined())
583 return combineText->combinedTextWidth(f);
584 }
585
586 if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) {
587 float monospaceCharacterWidth = f.spaceWidth();
588 float tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0;
589 float w = 0;
590 bool isSpace;
591 bool previousCharWasSpace = true; // FIXME: Preserves historical behavior, but seems wrong for start > 0.
592 ASSERT(m_text);
593 StringImpl& text = *m_text.impl();
594 for (int i = start; i < start + len; i++) {
595 char c = text[i];
596 if (c <= ' ') {
597 if (c == ' ' || c == '\n') {
598 w += monospaceCharacterWidth;
599 isSpace = true;
600 } else if (c == '\t') {
601 w += tabWidth ? tabWidth - fmodf(xPos + w, tabWidth) : monospaceCharacterWidth;
602 isSpace = true;
603 } else
604 isSpace = false;
605 } else {
606 w += monospaceCharacterWidth;
607 isSpace = false;
608 }
609 if (isSpace && !previousCharWasSpace)
610 w += f.wordSpacing();
611 previousCharWasSpace = isSpace;
612 }
613 return w;
614 }
615
616 return f.width(TextRun(text()->characters() + start, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
617 }
618
trimmedPrefWidths(float leadWidth,float & beginMinW,bool & beginWS,float & endMinW,bool & endWS,bool & hasBreakableChar,bool & hasBreak,float & beginMaxW,float & endMaxW,float & minW,float & maxW,bool & stripFrontSpaces)619 void RenderText::trimmedPrefWidths(float leadWidth,
620 float& beginMinW, bool& beginWS,
621 float& endMinW, bool& endWS,
622 bool& hasBreakableChar, bool& hasBreak,
623 float& beginMaxW, float& endMaxW,
624 float& minW, float& maxW, bool& stripFrontSpaces)
625 {
626 bool collapseWhiteSpace = style()->collapseWhiteSpace();
627 if (!collapseWhiteSpace)
628 stripFrontSpaces = false;
629
630 if (m_hasTab || preferredLogicalWidthsDirty())
631 computePreferredLogicalWidths(leadWidth);
632
633 beginWS = !stripFrontSpaces && m_hasBeginWS;
634 endWS = m_hasEndWS;
635
636 int len = textLength();
637
638 if (!len || (stripFrontSpaces && text()->containsOnlyWhitespace())) {
639 beginMinW = 0;
640 endMinW = 0;
641 beginMaxW = 0;
642 endMaxW = 0;
643 minW = 0;
644 maxW = 0;
645 hasBreak = false;
646 return;
647 }
648
649 minW = m_minWidth;
650 maxW = m_maxWidth;
651
652 beginMinW = m_beginMinWidth;
653 endMinW = m_endMinWidth;
654
655 hasBreakableChar = m_hasBreakableChar;
656 hasBreak = m_hasBreak;
657
658 ASSERT(m_text);
659 StringImpl& text = *m_text.impl();
660 if (text[0] == ' ' || (text[0] == '\n' && !style()->preserveNewline()) || text[0] == '\t') {
661 const Font& f = style()->font(); // FIXME: This ignores first-line.
662 if (stripFrontSpaces) {
663 const UChar space = ' ';
664 float spaceWidth = f.width(TextRun(&space, 1));
665 maxW -= spaceWidth;
666 } else
667 maxW += f.wordSpacing();
668 }
669
670 stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
671
672 if (!style()->autoWrap() || minW > maxW)
673 minW = maxW;
674
675 // Compute our max widths by scanning the string for newlines.
676 if (hasBreak) {
677 const Font& f = style()->font(); // FIXME: This ignores first-line.
678 bool firstLine = true;
679 beginMaxW = maxW;
680 endMaxW = maxW;
681 for (int i = 0; i < len; i++) {
682 int linelen = 0;
683 while (i + linelen < len && text[i + linelen] != '\n')
684 linelen++;
685
686 if (linelen) {
687 endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW, 0, 0);
688 if (firstLine) {
689 firstLine = false;
690 leadWidth = 0;
691 beginMaxW = endMaxW;
692 }
693 i += linelen;
694 } else if (firstLine) {
695 beginMaxW = 0;
696 firstLine = false;
697 leadWidth = 0;
698 }
699
700 if (i == len - 1)
701 // A <pre> run that ends with a newline, as in, e.g.,
702 // <pre>Some text\n\n<span>More text</pre>
703 endMaxW = 0;
704 }
705 }
706 }
707
isSpaceAccordingToStyle(UChar c,RenderStyle * style)708 static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style)
709 {
710 return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE);
711 }
712
minLogicalWidth() const713 float RenderText::minLogicalWidth() const
714 {
715 if (preferredLogicalWidthsDirty())
716 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
717
718 return m_minWidth;
719 }
720
maxLogicalWidth() const721 float RenderText::maxLogicalWidth() const
722 {
723 if (preferredLogicalWidthsDirty())
724 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
725
726 return m_maxWidth;
727 }
728
computePreferredLogicalWidths(float leadWidth)729 void RenderText::computePreferredLogicalWidths(float leadWidth)
730 {
731 HashSet<const SimpleFontData*> fallbackFonts;
732 GlyphOverflow glyphOverflow;
733 computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
734 if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom)
735 m_knownToHaveNoOverflowAndNoFallbackFonts = true;
736 }
737
computePreferredLogicalWidths(float leadWidth,HashSet<const SimpleFontData * > & fallbackFonts,GlyphOverflow & glyphOverflow)738 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
739 {
740 ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
741
742 m_minWidth = 0;
743 m_beginMinWidth = 0;
744 m_endMinWidth = 0;
745 m_maxWidth = 0;
746
747 if (isBR())
748 return;
749
750 float currMinWidth = 0;
751 float currMaxWidth = 0;
752 m_hasBreakableChar = false;
753 m_hasBreak = false;
754 m_hasTab = false;
755 m_hasBeginWS = false;
756 m_hasEndWS = false;
757
758 const Font& f = style()->font(); // FIXME: This ignores first-line.
759 float wordSpacing = style()->wordSpacing();
760 int len = textLength();
761 const UChar* txt = characters();
762 LazyLineBreakIterator breakIterator(txt, len);
763 bool needsWordSpacing = false;
764 bool ignoringSpaces = false;
765 bool isSpace = false;
766 bool firstWord = true;
767 bool firstLine = true;
768 int nextBreakable = -1;
769 int lastWordBoundary = 0;
770
771 int firstGlyphLeftOverflow = -1;
772
773 bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE;
774 bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap();
775
776 for (int i = 0; i < len; i++) {
777 UChar c = txt[i];
778
779 bool previousCharacterIsSpace = isSpace;
780
781 bool isNewline = false;
782 if (c == '\n') {
783 if (style()->preserveNewline()) {
784 m_hasBreak = true;
785 isNewline = true;
786 isSpace = false;
787 } else
788 isSpace = true;
789 } else if (c == '\t') {
790 if (!style()->collapseWhiteSpace()) {
791 m_hasTab = true;
792 isSpace = false;
793 } else
794 isSpace = true;
795 } else
796 isSpace = c == ' ';
797
798 if ((isSpace || isNewline) && !i)
799 m_hasBeginWS = true;
800 if ((isSpace || isNewline) && i == len - 1)
801 m_hasEndWS = true;
802
803 if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
804 ignoringSpaces = true;
805
806 if (ignoringSpaces && !isSpace)
807 ignoringSpaces = false;
808
809 // Ignore spaces and soft hyphens
810 if (ignoringSpaces) {
811 ASSERT(lastWordBoundary == i);
812 lastWordBoundary++;
813 continue;
814 } else if (c == softHyphen) {
815 currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
816 if (firstGlyphLeftOverflow < 0)
817 firstGlyphLeftOverflow = glyphOverflow.left;
818 lastWordBoundary = i + 1;
819 continue;
820 }
821
822 bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable, breakNBSP);
823 bool betweenWords = true;
824 int j = i;
825 while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) {
826 j++;
827 if (j == len)
828 break;
829 c = txt[j];
830 if (isBreakable(breakIterator, j, nextBreakable, breakNBSP))
831 break;
832 if (breakAll) {
833 betweenWords = false;
834 break;
835 }
836 }
837
838 int wordLen = j - i;
839 if (wordLen) {
840 float w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
841 if (firstGlyphLeftOverflow < 0)
842 firstGlyphLeftOverflow = glyphOverflow.left;
843 currMinWidth += w;
844 if (betweenWords) {
845 if (lastWordBoundary == i)
846 currMaxWidth += w;
847 else
848 currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
849 lastWordBoundary = j;
850 }
851
852 bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style());
853 bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
854 if (j < len && style()->autoWrap())
855 m_hasBreakableChar = true;
856
857 // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
858 // last word in the run.
859 if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
860 currMaxWidth += wordSpacing;
861
862 if (firstWord) {
863 firstWord = false;
864 // If the first character in the run is breakable, then we consider ourselves to have a beginning
865 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
866 // being appended to a previous text run when considering the total minimum width of the containing block.
867 if (hasBreak)
868 m_hasBreakableChar = true;
869 m_beginMinWidth = hasBreak ? 0 : w;
870 }
871 m_endMinWidth = w;
872
873 if (currMinWidth > m_minWidth)
874 m_minWidth = currMinWidth;
875 currMinWidth = 0;
876
877 i += wordLen - 1;
878 } else {
879 // Nowrap can never be broken, so don't bother setting the
880 // breakable character boolean. Pre can only be broken if we encounter a newline.
881 if (style()->autoWrap() || isNewline)
882 m_hasBreakableChar = true;
883
884 if (currMinWidth > m_minWidth)
885 m_minWidth = currMinWidth;
886 currMinWidth = 0;
887
888 if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
889 if (firstLine) {
890 firstLine = false;
891 leadWidth = 0;
892 if (!style()->autoWrap())
893 m_beginMinWidth = currMaxWidth;
894 }
895
896 if (currMaxWidth > m_maxWidth)
897 m_maxWidth = currMaxWidth;
898 currMaxWidth = 0;
899 } else {
900 currMaxWidth += f.width(TextRun(txt + i, 1, allowTabs(), leadWidth + currMaxWidth));
901 glyphOverflow.right = 0;
902 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
903 }
904 ASSERT(lastWordBoundary == i);
905 lastWordBoundary++;
906 }
907 }
908
909 if (firstGlyphLeftOverflow > 0)
910 glyphOverflow.left = firstGlyphLeftOverflow;
911
912 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
913 currMaxWidth += wordSpacing;
914
915 m_minWidth = max(currMinWidth, m_minWidth);
916 m_maxWidth = max(currMaxWidth, m_maxWidth);
917
918 if (!style()->autoWrap())
919 m_minWidth = m_maxWidth;
920
921 if (style()->whiteSpace() == PRE) {
922 if (firstLine)
923 m_beginMinWidth = m_maxWidth;
924 m_endMinWidth = currMaxWidth;
925 }
926
927 setPreferredLogicalWidthsDirty(false);
928 }
929
isAllCollapsibleWhitespace()930 bool RenderText::isAllCollapsibleWhitespace()
931 {
932 int length = textLength();
933 const UChar* text = characters();
934 for (int i = 0; i < length; i++) {
935 if (!style()->isCollapsibleWhiteSpace(text[i]))
936 return false;
937 }
938 return true;
939 }
940
containsOnlyWhitespace(unsigned from,unsigned len) const941 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
942 {
943 ASSERT(m_text);
944 StringImpl& text = *m_text.impl();
945 unsigned currPos;
946 for (currPos = from;
947 currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t');
948 currPos++) { }
949 return currPos >= (from + len);
950 }
951
firstRunOrigin() const952 FloatPoint RenderText::firstRunOrigin() const
953 {
954 return IntPoint(firstRunX(), firstRunY());
955 }
956
firstRunX() const957 float RenderText::firstRunX() const
958 {
959 return m_firstTextBox ? m_firstTextBox->m_x : 0;
960 }
961
firstRunY() const962 float RenderText::firstRunY() const
963 {
964 return m_firstTextBox ? m_firstTextBox->m_y : 0;
965 }
966
setSelectionState(SelectionState state)967 void RenderText::setSelectionState(SelectionState state)
968 {
969 InlineTextBox* box;
970
971 RenderObject::setSelectionState(state);
972 if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
973 int startPos, endPos;
974 selectionStartEnd(startPos, endPos);
975 if (selectionState() == SelectionStart) {
976 endPos = textLength();
977
978 // to handle selection from end of text to end of line
979 if (startPos != 0 && startPos == endPos)
980 startPos = endPos - 1;
981 } else if (selectionState() == SelectionEnd)
982 startPos = 0;
983
984 for (box = firstTextBox(); box; box = box->nextTextBox()) {
985 if (box->isSelected(startPos, endPos)) {
986 RootInlineBox* line = box->root();
987 if (line)
988 line->setHasSelectedChildren(true);
989 }
990 }
991 } else {
992 for (box = firstTextBox(); box; box = box->nextTextBox()) {
993 RootInlineBox* line = box->root();
994 if (line)
995 line->setHasSelectedChildren(state == SelectionInside);
996 }
997 }
998
999 // The returned value can be null in case of an orphaned tree.
1000 if (RenderBlock* cb = containingBlock())
1001 cb->setSelectionState(state);
1002 }
1003
setTextWithOffset(PassRefPtr<StringImpl> text,unsigned offset,unsigned len,bool force)1004 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force)
1005 {
1006 unsigned oldLen = textLength();
1007 unsigned newLen = text->length();
1008 int delta = newLen - oldLen;
1009 unsigned end = len ? offset + len - 1 : offset;
1010
1011 RootInlineBox* firstRootBox = 0;
1012 RootInlineBox* lastRootBox = 0;
1013
1014 bool dirtiedLines = false;
1015
1016 // Dirty all text boxes that include characters in between offset and offset+len.
1017 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1018 // Text run is entirely before the affected range.
1019 if (curr->end() < offset)
1020 continue;
1021
1022 // Text run is entirely after the affected range.
1023 if (curr->start() > end) {
1024 curr->offsetRun(delta);
1025 RootInlineBox* root = curr->root();
1026 if (!firstRootBox) {
1027 firstRootBox = root;
1028 if (!dirtiedLines) {
1029 // The affected area was in between two runs. Go ahead and mark the root box of
1030 // the run after the affected area as dirty.
1031 firstRootBox->markDirty();
1032 dirtiedLines = true;
1033 }
1034 }
1035 lastRootBox = root;
1036 } else if (curr->end() >= offset && curr->end() <= end) {
1037 // Text run overlaps with the left end of the affected range.
1038 curr->dirtyLineBoxes();
1039 dirtiedLines = true;
1040 } else if (curr->start() <= offset && curr->end() >= end) {
1041 // Text run subsumes the affected range.
1042 curr->dirtyLineBoxes();
1043 dirtiedLines = true;
1044 } else if (curr->start() <= end && curr->end() >= end) {
1045 // Text run overlaps with right end of the affected range.
1046 curr->dirtyLineBoxes();
1047 dirtiedLines = true;
1048 }
1049 }
1050
1051 // Now we have to walk all of the clean lines and adjust their cached line break information
1052 // to reflect our updated offsets.
1053 if (lastRootBox)
1054 lastRootBox = lastRootBox->nextRootBox();
1055 if (firstRootBox) {
1056 RootInlineBox* prev = firstRootBox->prevRootBox();
1057 if (prev)
1058 firstRootBox = prev;
1059 } else if (lastTextBox()) {
1060 ASSERT(!lastRootBox);
1061 firstRootBox = lastTextBox()->root();
1062 firstRootBox->markDirty();
1063 dirtiedLines = true;
1064 }
1065 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
1066 if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
1067 curr->setLineBreakPos(curr->lineBreakPos() + delta);
1068 }
1069
1070 // If the text node is empty, dirty the line where new text will be inserted.
1071 if (!firstTextBox() && parent()) {
1072 parent()->dirtyLinesFromChangedChild(this);
1073 dirtiedLines = true;
1074 }
1075
1076 m_linesDirty = dirtiedLines;
1077 setText(text, force);
1078 }
1079
isInlineFlowOrEmptyText(const RenderObject * o)1080 static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
1081 {
1082 if (o->isRenderInline())
1083 return true;
1084 if (!o->isText())
1085 return false;
1086 StringImpl* text = toRenderText(o)->text();
1087 if (!text)
1088 return true;
1089 return !text->length();
1090 }
1091
previousCharacter() const1092 UChar RenderText::previousCharacter() const
1093 {
1094 // find previous text renderer if one exists
1095 const RenderObject* previousText = this;
1096 while ((previousText = previousText->previousInPreOrder()))
1097 if (!isInlineFlowOrEmptyText(previousText))
1098 break;
1099 UChar prev = ' ';
1100 if (previousText && previousText->isText())
1101 if (StringImpl* previousString = toRenderText(previousText)->text())
1102 prev = (*previousString)[previousString->length() - 1];
1103 return prev;
1104 }
1105
transformText(String & text) const1106 void RenderText::transformText(String& text) const
1107 {
1108 ASSERT(style());
1109 switch (style()->textTransform()) {
1110 case TTNONE:
1111 break;
1112 case CAPITALIZE:
1113 makeCapitalized(&text, previousCharacter());
1114 break;
1115 case UPPERCASE:
1116 text.makeUpper();
1117 break;
1118 case LOWERCASE:
1119 text.makeLower();
1120 break;
1121 }
1122 }
1123
setTextInternal(PassRefPtr<StringImpl> text)1124 void RenderText::setTextInternal(PassRefPtr<StringImpl> text)
1125 {
1126 ASSERT(text);
1127 m_text = text;
1128 if (m_needsTranscoding) {
1129 const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
1130 fontTranscoder().convert(m_text, style()->font().fontDescription(), encoding);
1131 }
1132 ASSERT(m_text);
1133
1134 if (style()) {
1135 transformText(m_text);
1136
1137 // We use the same characters here as for list markers.
1138 // See the listMarkerText function in RenderListMarker.cpp.
1139 switch (style()->textSecurity()) {
1140 case TSNONE:
1141 break;
1142 case TSCIRCLE:
1143 m_text.makeSecure(whiteBullet);
1144 break;
1145 case TSDISC:
1146 m_text.makeSecure(bullet);
1147 break;
1148 case TSSQUARE:
1149 m_text.makeSecure(blackSquare);
1150 }
1151 }
1152
1153 ASSERT(m_text);
1154 ASSERT(!isBR() || (textLength() == 1 && m_text[0] == '\n'));
1155
1156 m_isAllASCII = m_text.containsOnlyASCII();
1157 }
1158
setText(PassRefPtr<StringImpl> text,bool force)1159 void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
1160 {
1161 ASSERT(text);
1162
1163 if (!force && equal(m_text.impl(), text.get()))
1164 return;
1165
1166 setTextInternal(text);
1167 setNeedsLayoutAndPrefWidthsRecalc();
1168 m_knownToHaveNoOverflowAndNoFallbackFonts = false;
1169
1170 AXObjectCache* axObjectCache = document()->axObjectCache();
1171 if (axObjectCache->accessibilityEnabled())
1172 axObjectCache->contentChanged(this);
1173 }
1174
textWithoutTranscoding() const1175 String RenderText::textWithoutTranscoding() const
1176 {
1177 // If m_text isn't transcoded or is secure, we can just return the modified text.
1178 if (!m_needsTranscoding || style()->textSecurity() != TSNONE)
1179 return text();
1180
1181 // Otherwise, we should use original text. If text-transform is
1182 // specified, we should transform the text on the fly.
1183 String text = originalText();
1184 if (style())
1185 transformText(text);
1186 return text;
1187 }
1188
dirtyLineBoxes(bool fullLayout)1189 void RenderText::dirtyLineBoxes(bool fullLayout)
1190 {
1191 if (fullLayout)
1192 deleteTextBoxes();
1193 else if (!m_linesDirty) {
1194 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1195 box->dirtyLineBoxes();
1196 }
1197 m_linesDirty = false;
1198 }
1199
createTextBox()1200 InlineTextBox* RenderText::createTextBox()
1201 {
1202 return new (renderArena()) InlineTextBox(this);
1203 }
1204
createInlineTextBox()1205 InlineTextBox* RenderText::createInlineTextBox()
1206 {
1207 InlineTextBox* textBox = createTextBox();
1208 if (!m_firstTextBox)
1209 m_firstTextBox = m_lastTextBox = textBox;
1210 else {
1211 m_lastTextBox->setNextTextBox(textBox);
1212 textBox->setPreviousTextBox(m_lastTextBox);
1213 m_lastTextBox = textBox;
1214 }
1215 textBox->setIsText(true);
1216 return textBox;
1217 }
1218
positionLineBox(InlineBox * box)1219 void RenderText::positionLineBox(InlineBox* box)
1220 {
1221 InlineTextBox* s = static_cast<InlineTextBox*>(box);
1222
1223 // FIXME: should not be needed!!!
1224 if (!s->len()) {
1225 // We want the box to be destroyed.
1226 s->remove();
1227 if (m_firstTextBox == s)
1228 m_firstTextBox = s->nextTextBox();
1229 else
1230 s->prevTextBox()->setNextTextBox(s->nextTextBox());
1231 if (m_lastTextBox == s)
1232 m_lastTextBox = s->prevTextBox();
1233 else
1234 s->nextTextBox()->setPreviousTextBox(s->prevTextBox());
1235 s->destroy(renderArena());
1236 return;
1237 }
1238
1239 m_containsReversedText |= !s->isLeftToRightDirection();
1240 }
1241
width(unsigned from,unsigned len,float xPos,bool firstLine,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const1242 float RenderText::width(unsigned from, unsigned len, float xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1243 {
1244 if (from >= textLength())
1245 return 0;
1246
1247 if (from + len > textLength())
1248 len = textLength() - from;
1249
1250 return width(from, len, style(firstLine)->font(), xPos, fallbackFonts, glyphOverflow);
1251 }
1252
width(unsigned from,unsigned len,const Font & f,float xPos,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const1253 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1254 {
1255 ASSERT(from + len <= textLength());
1256 if (!characters())
1257 return 0;
1258
1259 float w;
1260 if (&f == &style()->font()) {
1261 if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) {
1262 if (fallbackFonts) {
1263 ASSERT(glyphOverflow);
1264 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
1265 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
1266 if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom)
1267 m_knownToHaveNoOverflowAndNoFallbackFonts = true;
1268 }
1269 w = m_maxWidth;
1270 } else
1271 w = maxLogicalWidth();
1272 } else
1273 w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow);
1274 } else
1275 w = f.width(TextRun(text()->characters() + from, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
1276
1277 return w;
1278 }
1279
linesBoundingBox() const1280 IntRect RenderText::linesBoundingBox() const
1281 {
1282 IntRect result;
1283
1284 ASSERT(!firstTextBox() == !lastTextBox()); // Either both are null or both exist.
1285 if (firstTextBox() && lastTextBox()) {
1286 // Return the width of the minimal left side and the maximal right side.
1287 float logicalLeftSide = 0;
1288 float logicalRightSide = 0;
1289 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1290 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide)
1291 logicalLeftSide = curr->logicalLeft();
1292 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide)
1293 logicalRightSide = curr->logicalRight();
1294 }
1295
1296 bool isHorizontal = style()->isHorizontalWritingMode();
1297
1298 float x = isHorizontal ? logicalLeftSide : firstTextBox()->x();
1299 float y = isHorizontal ? firstTextBox()->y() : logicalLeftSide;
1300 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x;
1301 float height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
1302 result = enclosingIntRect(FloatRect(x, y, width, height));
1303 }
1304
1305 return result;
1306 }
1307
linesVisualOverflowBoundingBox() const1308 IntRect RenderText::linesVisualOverflowBoundingBox() const
1309 {
1310 if (!firstTextBox())
1311 return IntRect();
1312
1313 // Return the width of the minimal left side and the maximal right side.
1314 int logicalLeftSide = numeric_limits<int>::max();
1315 int logicalRightSide = numeric_limits<int>::min();
1316 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1317 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1318 logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
1319 }
1320
1321 int logicalTop = firstTextBox()->logicalTopVisualOverflow();
1322 int logicalWidth = logicalRightSide - logicalLeftSide;
1323 int logicalHeight = lastTextBox()->logicalBottomVisualOverflow() - logicalTop;
1324
1325 IntRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1326 if (!style()->isHorizontalWritingMode())
1327 rect = rect.transposedRect();
1328 return rect;
1329 }
1330
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)1331 IntRect RenderText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
1332 {
1333 bool repaintContainerSkipped;
1334 RenderObject* container = this->container(repaintContainer, &repaintContainerSkipped);
1335 // The container may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint.
1336 if (repaintContainerSkipped)
1337 return repaintContainer->clippedOverflowRectForRepaint(repaintContainer);
1338
1339 return container->clippedOverflowRectForRepaint(repaintContainer);
1340 }
1341
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool clipToVisibleContent)1342 IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
1343 {
1344 ASSERT(!needsLayout());
1345
1346 if (selectionState() == SelectionNone)
1347 return IntRect();
1348 RenderBlock* cb = containingBlock();
1349 if (!cb)
1350 return IntRect();
1351
1352 // Now calculate startPos and endPos for painting selection.
1353 // We include a selection while endPos > 0
1354 int startPos, endPos;
1355 if (selectionState() == SelectionInside) {
1356 // We are fully selected.
1357 startPos = 0;
1358 endPos = textLength();
1359 } else {
1360 selectionStartEnd(startPos, endPos);
1361 if (selectionState() == SelectionStart)
1362 endPos = textLength();
1363 else if (selectionState() == SelectionEnd)
1364 startPos = 0;
1365 }
1366
1367 if (startPos == endPos)
1368 return IntRect();
1369
1370 IntRect rect;
1371 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1372 rect.unite(box->selectionRect(0, 0, startPos, endPos));
1373 rect.unite(ellipsisRectForBox(box, startPos, endPos));
1374 }
1375
1376 if (clipToVisibleContent)
1377 computeRectForRepaint(repaintContainer, rect);
1378 else {
1379 if (cb->hasColumns())
1380 cb->adjustRectForColumns(rect);
1381
1382 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1383 }
1384
1385 return rect;
1386 }
1387
caretMinOffset() const1388 int RenderText::caretMinOffset() const
1389 {
1390 InlineTextBox* box = firstTextBox();
1391 if (!box)
1392 return 0;
1393 int minOffset = box->start();
1394 for (box = box->nextTextBox(); box; box = box->nextTextBox())
1395 minOffset = min<int>(minOffset, box->start());
1396 return minOffset;
1397 }
1398
caretMaxOffset() const1399 int RenderText::caretMaxOffset() const
1400 {
1401 InlineTextBox* box = lastTextBox();
1402 if (!box)
1403 return textLength();
1404 int maxOffset = box->start() + box->len();
1405 for (box = box->prevTextBox(); box; box = box->prevTextBox())
1406 maxOffset = max<int>(maxOffset, box->start() + box->len());
1407 return maxOffset;
1408 }
1409
caretMaxRenderedOffset() const1410 unsigned RenderText::caretMaxRenderedOffset() const
1411 {
1412 int l = 0;
1413 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1414 l += box->len();
1415 return l;
1416 }
1417
previousOffset(int current) const1418 int RenderText::previousOffset(int current) const
1419 {
1420 StringImpl* si = m_text.impl();
1421 TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
1422 if (!iterator)
1423 return current - 1;
1424
1425 long result = textBreakPreceding(iterator, current);
1426 if (result == TextBreakDone)
1427 result = current - 1;
1428
1429 #ifdef BUILDING_ON_TIGER
1430 // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
1431 if (static_cast<unsigned>(result) < si->length()) {
1432 UChar character = (*si)[result];
1433 if (character == 0xFF9E || character == 0xFF9F)
1434 --result;
1435 }
1436 #endif
1437
1438 return result;
1439 }
1440
1441 #if PLATFORM(MAC)
1442
1443 #define HANGUL_CHOSEONG_START (0x1100)
1444 #define HANGUL_CHOSEONG_END (0x115F)
1445 #define HANGUL_JUNGSEONG_START (0x1160)
1446 #define HANGUL_JUNGSEONG_END (0x11A2)
1447 #define HANGUL_JONGSEONG_START (0x11A8)
1448 #define HANGUL_JONGSEONG_END (0x11F9)
1449 #define HANGUL_SYLLABLE_START (0xAC00)
1450 #define HANGUL_SYLLABLE_END (0xD7AF)
1451 #define HANGUL_JONGSEONG_COUNT (28)
1452
1453 enum HangulState {
1454 HangulStateL,
1455 HangulStateV,
1456 HangulStateT,
1457 HangulStateLV,
1458 HangulStateLVT,
1459 HangulStateBreak
1460 };
1461
isHangulLVT(UChar32 character)1462 inline bool isHangulLVT(UChar32 character)
1463 {
1464 return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
1465 }
1466
isMark(UChar32 c)1467 inline bool isMark(UChar32 c)
1468 {
1469 int8_t charType = u_charType(c);
1470 return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK;
1471 }
1472
1473 #endif
1474
previousOffsetForBackwardDeletion(int current) const1475 int RenderText::previousOffsetForBackwardDeletion(int current) const
1476 {
1477 #if PLATFORM(MAC)
1478 ASSERT(m_text);
1479 StringImpl& text = *m_text.impl();
1480 UChar32 character;
1481 while (current > 0) {
1482 if (U16_IS_TRAIL(text[--current]))
1483 --current;
1484 if (current < 0)
1485 break;
1486
1487 UChar32 character = text.characterStartingAt(current);
1488
1489 // We don't combine characters in Armenian ... Limbu range for backward deletion.
1490 if ((character >= 0x0530) && (character < 0x1950))
1491 break;
1492
1493 if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F))
1494 break;
1495 }
1496
1497 if (current <= 0)
1498 return current;
1499
1500 // Hangul
1501 character = text.characterStartingAt(current);
1502 if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
1503 HangulState state;
1504 HangulState initialState;
1505
1506 if (character < HANGUL_JUNGSEONG_START)
1507 state = HangulStateL;
1508 else if (character < HANGUL_JONGSEONG_START)
1509 state = HangulStateV;
1510 else if (character < HANGUL_SYLLABLE_START)
1511 state = HangulStateT;
1512 else
1513 state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
1514
1515 initialState = state;
1516
1517 while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
1518 switch (state) {
1519 case HangulStateV:
1520 if (character <= HANGUL_CHOSEONG_END)
1521 state = HangulStateL;
1522 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
1523 state = HangulStateLV;
1524 else if (character > HANGUL_JUNGSEONG_END)
1525 state = HangulStateBreak;
1526 break;
1527 case HangulStateT:
1528 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
1529 state = HangulStateV;
1530 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
1531 state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
1532 else if (character < HANGUL_JUNGSEONG_START)
1533 state = HangulStateBreak;
1534 break;
1535 default:
1536 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
1537 break;
1538 }
1539 if (state == HangulStateBreak)
1540 break;
1541
1542 --current;
1543 }
1544 }
1545
1546 return current;
1547 #else
1548 // Platforms other than Mac delete by one code point.
1549 return current - 1;
1550 #endif
1551 }
1552
nextOffset(int current) const1553 int RenderText::nextOffset(int current) const
1554 {
1555 StringImpl* si = m_text.impl();
1556 TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
1557 if (!iterator)
1558 return current + 1;
1559
1560 long result = textBreakFollowing(iterator, current);
1561 if (result == TextBreakDone)
1562 result = current + 1;
1563
1564 #ifdef BUILDING_ON_TIGER
1565 // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
1566 if (static_cast<unsigned>(result) < si->length()) {
1567 UChar character = (*si)[result];
1568 if (character == 0xFF9E || character == 0xFF9F)
1569 ++result;
1570 }
1571 #endif
1572
1573 return result;
1574 }
1575
1576 #ifndef NDEBUG
1577
checkConsistency() const1578 void RenderText::checkConsistency() const
1579 {
1580 #ifdef CHECK_CONSISTENCY
1581 const InlineTextBox* prev = 0;
1582 for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) {
1583 ASSERT(child->renderer() == this);
1584 ASSERT(child->prevTextBox() == prev);
1585 prev = child;
1586 }
1587 ASSERT(prev == m_lastTextBox);
1588 #endif
1589 }
1590
1591 #endif
1592
1593 } // namespace WebCore
1594