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