1 /* 2 * (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2000 Dirk Mueller (mueller (at) kde.org) 4 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. 5 * Copyright (C) 2006 Andrew Wellington (proton (at) wiretapped.net) 6 * Copyright (C) 2006 Graham Dennis (graham.dennis (at) 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 "core/rendering/RenderText.h" 27 28 #include "core/accessibility/AXObjectCache.h" 29 #include "core/dom/Text.h" 30 #include "core/editing/TextIterator.h" 31 #include "core/fetch/TextResourceDecoder.h" 32 #include "core/frame/FrameView.h" 33 #include "core/frame/Settings.h" 34 #include "core/rendering/AbstractInlineTextBox.h" 35 #include "core/rendering/EllipsisBox.h" 36 #include "core/rendering/InlineTextBox.h" 37 #include "core/rendering/RenderBlock.h" 38 #include "core/rendering/RenderCombineText.h" 39 #include "core/rendering/RenderLayer.h" 40 #include "core/rendering/RenderView.h" 41 #include "core/rendering/break_lines.h" 42 #include "platform/geometry/FloatQuad.h" 43 #include "platform/text/TextBreakIterator.h" 44 #include "wtf/text/StringBuffer.h" 45 #include "wtf/text/StringBuilder.h" 46 #include "wtf/unicode/CharacterNames.h" 47 48 using namespace std; 49 using namespace WTF; 50 using namespace Unicode; 51 52 namespace WebCore { 53 54 struct SameSizeAsRenderText : public RenderObject { 55 uint32_t bitfields : 16; 56 float widths[4]; 57 String text; 58 void* pointers[2]; 59 }; 60 61 COMPILE_ASSERT(sizeof(RenderText) == sizeof(SameSizeAsRenderText), RenderText_should_stay_small); 62 63 class SecureTextTimer; 64 typedef HashMap<RenderText*, SecureTextTimer*> SecureTextTimerMap; 65 static SecureTextTimerMap* gSecureTextTimers = 0; 66 67 class SecureTextTimer : public TimerBase { 68 public: 69 SecureTextTimer(RenderText* renderText) 70 : m_renderText(renderText) 71 , m_lastTypedCharacterOffset(-1) 72 { 73 } 74 75 void restartWithNewText(unsigned lastTypedCharacterOffset) 76 { 77 m_lastTypedCharacterOffset = lastTypedCharacterOffset; 78 if (Settings* settings = m_renderText->document().settings()) 79 startOneShot(settings->passwordEchoDurationInSeconds()); 80 } 81 void invalidate() { m_lastTypedCharacterOffset = -1; } 82 unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; } 83 84 private: 85 virtual void fired() 86 { 87 ASSERT(gSecureTextTimers->contains(m_renderText)); 88 m_renderText->setText(m_renderText->text().impl(), true /* forcing setting text as it may be masked later */); 89 } 90 91 RenderText* m_renderText; 92 int m_lastTypedCharacterOffset; 93 }; 94 95 static void makeCapitalized(String* string, UChar previous) 96 { 97 if (string->isNull()) 98 return; 99 100 unsigned length = string->length(); 101 const StringImpl& input = *string->impl(); 102 103 if (length >= numeric_limits<unsigned>::max()) 104 CRASH(); 105 106 StringBuffer<UChar> stringWithPrevious(length + 1); 107 stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous; 108 for (unsigned i = 1; i < length + 1; i++) { 109 // Replace   with a real space since ICU no longer treats   as a word separator. 110 if (input[i - 1] == noBreakSpace) 111 stringWithPrevious[i] = ' '; 112 else 113 stringWithPrevious[i] = input[i - 1]; 114 } 115 116 TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1); 117 if (!boundary) 118 return; 119 120 StringBuilder result; 121 result.reserveCapacity(length); 122 123 int32_t endOfWord; 124 int32_t startOfWord = boundary->first(); 125 for (endOfWord = boundary->next(); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = boundary->next()) { 126 if (startOfWord) // Ignore first char of previous string 127 result.append(input[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord])); 128 for (int i = startOfWord + 1; i < endOfWord; i++) 129 result.append(input[i - 1]); 130 } 131 132 *string = result.toString(); 133 } 134 135 RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str) 136 : RenderObject(!node || node->isDocumentNode() ? 0 : node) 137 , m_hasTab(false) 138 , m_linesDirty(false) 139 , m_containsReversedText(false) 140 , m_knownToHaveNoOverflowAndNoFallbackFonts(false) 141 , m_minWidth(-1) 142 , m_maxWidth(-1) 143 , m_firstLineMinWidth(0) 144 , m_lastLineLineMinWidth(0) 145 , m_text(str) 146 , m_firstTextBox(0) 147 , m_lastTextBox(0) 148 { 149 ASSERT(m_text); 150 // FIXME: Some clients of RenderText (and subclasses) pass Document as node to create anonymous renderer. 151 // They should be switched to passing null and using setDocumentForAnonymous. 152 if (node && node->isDocumentNode()) 153 setDocumentForAnonymous(toDocument(node)); 154 155 m_isAllASCII = m_text.containsOnlyASCII(); 156 m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath(); 157 setIsText(); 158 159 view()->frameView()->incrementVisuallyNonEmptyCharacterCount(m_text.length()); 160 } 161 162 #ifndef NDEBUG 163 164 RenderText::~RenderText() 165 { 166 ASSERT(!m_firstTextBox); 167 ASSERT(!m_lastTextBox); 168 } 169 170 #endif 171 172 const char* RenderText::renderName() const 173 { 174 return "RenderText"; 175 } 176 177 bool RenderText::isTextFragment() const 178 { 179 return false; 180 } 181 182 bool RenderText::isWordBreak() const 183 { 184 return false; 185 } 186 187 void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 188 { 189 // There is no need to ever schedule repaints from a style change of a text run, since 190 // we already did this for the parent of the text run. 191 // We do have to schedule layouts, though, since a style change can force us to 192 // need to relayout. 193 if (diff == StyleDifferenceLayout) { 194 setNeedsLayoutAndPrefWidthsRecalc(); 195 m_knownToHaveNoOverflowAndNoFallbackFonts = false; 196 } 197 198 RenderStyle* newStyle = style(); 199 ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE; 200 ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE; 201 if (oldTransform != newStyle->textTransform() || oldSecurity != newStyle->textSecurity()) 202 transformText(); 203 204 if (!text().containsOnlyWhitespace()) 205 newStyle->font().willUseFontData(); 206 } 207 208 void RenderText::removeAndDestroyTextBoxes() 209 { 210 if (!documentBeingDestroyed()) { 211 if (firstTextBox()) { 212 if (isBR()) { 213 RootInlineBox* next = firstTextBox()->root()->nextRootBox(); 214 if (next) 215 next->markDirty(); 216 } 217 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 218 box->remove(); 219 } else if (parent()) 220 parent()->dirtyLinesFromChangedChild(this); 221 } 222 deleteTextBoxes(); 223 } 224 225 void RenderText::willBeDestroyed() 226 { 227 if (SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->take(this) : 0) 228 delete secureTextTimer; 229 230 removeAndDestroyTextBoxes(); 231 RenderObject::willBeDestroyed(); 232 } 233 234 void RenderText::extractTextBox(InlineTextBox* box) 235 { 236 checkConsistency(); 237 238 m_lastTextBox = box->prevTextBox(); 239 if (box == m_firstTextBox) 240 m_firstTextBox = 0; 241 if (box->prevTextBox()) 242 box->prevTextBox()->setNextTextBox(0); 243 box->setPreviousTextBox(0); 244 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) 245 curr->setExtracted(); 246 247 checkConsistency(); 248 } 249 250 void RenderText::attachTextBox(InlineTextBox* box) 251 { 252 checkConsistency(); 253 254 if (m_lastTextBox) { 255 m_lastTextBox->setNextTextBox(box); 256 box->setPreviousTextBox(m_lastTextBox); 257 } else 258 m_firstTextBox = box; 259 InlineTextBox* last = box; 260 for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) { 261 curr->setExtracted(false); 262 last = curr; 263 } 264 m_lastTextBox = last; 265 266 checkConsistency(); 267 } 268 269 void RenderText::removeTextBox(InlineTextBox* box) 270 { 271 checkConsistency(); 272 273 if (box == m_firstTextBox) 274 m_firstTextBox = box->nextTextBox(); 275 if (box == m_lastTextBox) 276 m_lastTextBox = box->prevTextBox(); 277 if (box->nextTextBox()) 278 box->nextTextBox()->setPreviousTextBox(box->prevTextBox()); 279 if (box->prevTextBox()) 280 box->prevTextBox()->setNextTextBox(box->nextTextBox()); 281 282 checkConsistency(); 283 } 284 285 void RenderText::deleteTextBoxes() 286 { 287 if (firstTextBox()) { 288 InlineTextBox* next; 289 for (InlineTextBox* curr = firstTextBox(); curr; curr = next) { 290 next = curr->nextTextBox(); 291 curr->destroy(); 292 } 293 m_firstTextBox = m_lastTextBox = 0; 294 } 295 } 296 297 PassRefPtr<StringImpl> RenderText::originalText() const 298 { 299 Node* e = node(); 300 return (e && e->isTextNode()) ? toText(e)->dataImpl() : 0; 301 } 302 303 String RenderText::plainText() const 304 { 305 if (node()) 306 return WebCore::plainText(rangeOfContents(node()).get()); 307 308 // FIXME: this is just a stopgap until TextIterator is adapted to support generated text. 309 StringBuilder plainTextBuilder; 310 for (InlineTextBox* textBox = firstTextBox(); textBox; textBox = textBox->nextTextBox()) { 311 String text = m_text.substring(textBox->start(), textBox->len()).simplifyWhiteSpace(WTF::DoNotStripWhiteSpace); 312 plainTextBuilder.append(text); 313 if (textBox->nextTextBox() && textBox->nextTextBox()->start() > textBox->end() && text.length() && !text.right(1).containsOnlyWhitespace()) 314 plainTextBuilder.append(" "); 315 } 316 return plainTextBuilder.toString(); 317 } 318 319 void RenderText::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const 320 { 321 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 322 rects.append(enclosingIntRect(FloatRect(accumulatedOffset + box->topLeft(), box->size()))); 323 } 324 325 static FloatRect localQuadForTextBox(InlineTextBox* box, unsigned start, unsigned end, bool useSelectionHeight) 326 { 327 unsigned realEnd = min(box->end() + 1, end); 328 LayoutRect r = box->localSelectionRect(start, realEnd); 329 if (r.height()) { 330 if (!useSelectionHeight) { 331 // Change the height and y position (or width and x for vertical text) 332 // because selectionRect uses selection-specific values. 333 if (box->isHorizontal()) { 334 r.setHeight(box->height()); 335 r.setY(box->y()); 336 } else { 337 r.setWidth(box->width()); 338 r.setX(box->x()); 339 } 340 } 341 return FloatRect(r); 342 } 343 return FloatRect(); 344 } 345 346 void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) 347 { 348 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX 349 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this 350 // function to take ints causes various internal mismatches. But selectionRect takes ints, and 351 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but 352 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX. 353 ASSERT(end == UINT_MAX || end <= INT_MAX); 354 ASSERT(start <= INT_MAX); 355 start = min(start, static_cast<unsigned>(INT_MAX)); 356 end = min(end, static_cast<unsigned>(INT_MAX)); 357 358 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 359 // Note: box->end() returns the index of the last character, not the index past it 360 if (start <= box->start() && box->end() < end) { 361 FloatRect r = box->calculateBoundaries(); 362 if (useSelectionHeight) { 363 LayoutRect selectionRect = box->localSelectionRect(start, end); 364 if (box->isHorizontal()) { 365 r.setHeight(selectionRect.height()); 366 r.setY(selectionRect.y()); 367 } else { 368 r.setWidth(selectionRect.width()); 369 r.setX(selectionRect.x()); 370 } 371 } 372 rects.append(localToAbsoluteQuad(r, 0, wasFixed).enclosingBoundingBox()); 373 } else { 374 // FIXME: This code is wrong. It's converting local to absolute twice. http://webkit.org/b/65722 375 FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight); 376 if (!rect.isZero()) 377 rects.append(localToAbsoluteQuad(rect, 0, wasFixed).enclosingBoundingBox()); 378 } 379 } 380 } 381 382 static IntRect ellipsisRectForBox(InlineTextBox* box, unsigned startPos, unsigned endPos) 383 { 384 if (!box) 385 return IntRect(); 386 387 unsigned short truncation = box->truncation(); 388 if (truncation == cNoTruncation) 389 return IntRect(); 390 391 IntRect rect; 392 if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) { 393 int ellipsisStartPosition = max<int>(startPos - box->start(), 0); 394 int ellipsisEndPosition = min<int>(endPos - box->start(), box->len()); 395 396 // The ellipsis should be considered to be selected if the end of 397 // the selection is past the beginning of the truncation and the 398 // beginning of the selection is before or at the beginning of the truncation. 399 if (ellipsisEndPosition >= truncation && ellipsisStartPosition <= truncation) 400 return ellipsis->selectionRect(); 401 } 402 403 return IntRect(); 404 } 405 406 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed, ClippingOption option) const 407 { 408 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 409 FloatRect boundaries = box->calculateBoundaries(); 410 411 // Shorten the width of this text box if it ends in an ellipsis. 412 // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch. 413 IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(box, 0, textLength()) : IntRect(); 414 if (!ellipsisRect.isEmpty()) { 415 if (style()->isHorizontalWritingMode()) 416 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x()); 417 else 418 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y()); 419 } 420 quads.append(localToAbsoluteQuad(boundaries, 0, wasFixed)); 421 } 422 } 423 424 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const 425 { 426 absoluteQuads(quads, wasFixed, NoClipping); 427 } 428 429 void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) 430 { 431 // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX 432 // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this 433 // function to take ints causes various internal mismatches. But selectionRect takes ints, and 434 // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but 435 // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX. 436 ASSERT(end == UINT_MAX || end <= INT_MAX); 437 ASSERT(start <= INT_MAX); 438 start = min(start, static_cast<unsigned>(INT_MAX)); 439 end = min(end, static_cast<unsigned>(INT_MAX)); 440 441 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 442 // Note: box->end() returns the index of the last character, not the index past it 443 if (start <= box->start() && box->end() < end) { 444 FloatRect r = box->calculateBoundaries(); 445 if (useSelectionHeight) { 446 LayoutRect selectionRect = box->localSelectionRect(start, end); 447 if (box->isHorizontal()) { 448 r.setHeight(selectionRect.height()); 449 r.setY(selectionRect.y()); 450 } else { 451 r.setWidth(selectionRect.width()); 452 r.setX(selectionRect.x()); 453 } 454 } 455 quads.append(localToAbsoluteQuad(r, 0, wasFixed)); 456 } else { 457 FloatRect rect = localQuadForTextBox(box, start, end, useSelectionHeight); 458 if (!rect.isZero()) 459 quads.append(localToAbsoluteQuad(rect, 0, wasFixed)); 460 } 461 } 462 } 463 464 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const 465 { 466 // The text runs point to parts of the RenderText's m_text 467 // (they don't include '\n') 468 // Find the text run that includes the character at offset 469 // and return pos, which is the position of the char in the run. 470 471 if (!m_firstTextBox) 472 return 0; 473 474 InlineTextBox* s = m_firstTextBox; 475 int off = s->len(); 476 while (offset > off && s->nextTextBox()) { 477 s = s->nextTextBox(); 478 off = s->start() + s->len(); 479 } 480 // we are now in the correct text run 481 pos = (offset > off ? s->len() : s->len() - (off - offset) ); 482 return s; 483 } 484 485 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart }; 486 487 static bool lineDirectionPointFitsInBox(int pointLineDirection, InlineTextBox* box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream) 488 { 489 shouldAffinityBeDownstream = AlwaysDownstream; 490 491 // the x coordinate is equal to the left edge of this box 492 // the affinity must be downstream so the position doesn't jump back to the previous line 493 // except when box is the first box in the line 494 if (pointLineDirection <= box->logicalLeft()) { 495 shouldAffinityBeDownstream = !box->prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream; 496 return true; 497 } 498 499 // and the x coordinate is to the left of the right edge of this box 500 // check to see if position goes in this box 501 if (pointLineDirection < box->logicalRight()) { 502 shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart; 503 return true; 504 } 505 506 // box is first on line 507 // and the x coordinate is to the left of the first text box left edge 508 if (!box->prevLeafChildIgnoringLineBreak() && pointLineDirection < box->logicalLeft()) 509 return true; 510 511 if (!box->nextLeafChildIgnoringLineBreak()) { 512 // box is last on line 513 // and the x coordinate is to the right of the last text box right edge 514 // generate VisiblePosition, use UPSTREAM affinity if possible 515 shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart; 516 return true; 517 } 518 519 return false; 520 } 521 522 static PositionWithAffinity createPositionWithAffinityForBox(const InlineBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream) 523 { 524 EAffinity affinity = VP_DEFAULT_AFFINITY; 525 switch (shouldAffinityBeDownstream) { 526 case AlwaysDownstream: 527 affinity = DOWNSTREAM; 528 break; 529 case AlwaysUpstream: 530 affinity = VP_UPSTREAM_IF_POSSIBLE; 531 break; 532 case UpstreamIfPositionIsNotAtStart: 533 affinity = offset > box->caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM; 534 break; 535 } 536 int textStartOffset = box->renderer()->isText() ? toRenderText(box->renderer())->textStartOffset() : 0; 537 return box->renderer()->createPositionWithAffinity(offset + textStartOffset, affinity); 538 } 539 540 static PositionWithAffinity createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(const InlineTextBox* box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream) 541 { 542 ASSERT(box); 543 ASSERT(box->renderer()); 544 ASSERT(offset >= 0); 545 546 if (offset && static_cast<unsigned>(offset) < box->len()) 547 return createPositionWithAffinityForBox(box, box->start() + offset, shouldAffinityBeDownstream); 548 549 bool positionIsAtStartOfBox = !offset; 550 if (positionIsAtStartOfBox == box->isLeftToRightDirection()) { 551 // offset is on the left edge 552 553 const InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak(); 554 if ((prevBox && prevBox->bidiLevel() == box->bidiLevel()) 555 || box->renderer()->containingBlock()->style()->direction() == box->direction()) // FIXME: left on 12CBA 556 return createPositionWithAffinityForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream); 557 558 if (prevBox && prevBox->bidiLevel() > box->bidiLevel()) { 559 // e.g. left of B in aDC12BAb 560 const InlineBox* leftmostBox; 561 do { 562 leftmostBox = prevBox; 563 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak(); 564 } while (prevBox && prevBox->bidiLevel() > box->bidiLevel()); 565 return createPositionWithAffinityForBox(leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream); 566 } 567 568 if (!prevBox || prevBox->bidiLevel() < box->bidiLevel()) { 569 // e.g. left of D in aDC12BAb 570 const InlineBox* rightmostBox; 571 const InlineBox* nextBox = box; 572 do { 573 rightmostBox = nextBox; 574 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak(); 575 } while (nextBox && nextBox->bidiLevel() >= box->bidiLevel()); 576 return createPositionWithAffinityForBox(rightmostBox, 577 box->isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream); 578 } 579 580 return createPositionWithAffinityForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream); 581 } 582 583 const InlineBox* nextBox = box->nextLeafChildIgnoringLineBreak(); 584 if ((nextBox && nextBox->bidiLevel() == box->bidiLevel()) 585 || box->renderer()->containingBlock()->style()->direction() == box->direction()) 586 return createPositionWithAffinityForBox(box, box->caretRightmostOffset(), shouldAffinityBeDownstream); 587 588 // offset is on the right edge 589 if (nextBox && nextBox->bidiLevel() > box->bidiLevel()) { 590 // e.g. right of C in aDC12BAb 591 const InlineBox* rightmostBox; 592 do { 593 rightmostBox = nextBox; 594 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak(); 595 } while (nextBox && nextBox->bidiLevel() > box->bidiLevel()); 596 return createPositionWithAffinityForBox(rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream); 597 } 598 599 if (!nextBox || nextBox->bidiLevel() < box->bidiLevel()) { 600 // e.g. right of A in aDC12BAb 601 const InlineBox* leftmostBox; 602 const InlineBox* prevBox = box; 603 do { 604 leftmostBox = prevBox; 605 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak(); 606 } while (prevBox && prevBox->bidiLevel() >= box->bidiLevel()); 607 return createPositionWithAffinityForBox(leftmostBox, 608 box->isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream); 609 } 610 611 return createPositionWithAffinityForBox(box, box->caretLeftmostOffset(), shouldAffinityBeDownstream); 612 } 613 614 PositionWithAffinity RenderText::positionForPoint(const LayoutPoint& point) 615 { 616 if (!firstTextBox() || textLength() == 0) 617 return createPositionWithAffinity(0, DOWNSTREAM); 618 619 LayoutUnit pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y(); 620 LayoutUnit pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x(); 621 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); 622 623 InlineTextBox* lastBox = 0; 624 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 625 if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak()) 626 box = box->nextTextBox(); 627 628 RootInlineBox* rootBox = box->root(); 629 LayoutUnit top = min(rootBox->selectionTop(), rootBox->lineTop()); 630 if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) { 631 LayoutUnit bottom = rootBox->selectionBottom(); 632 if (rootBox->nextRootBox()) 633 bottom = min(bottom, rootBox->nextRootBox()->lineTop()); 634 635 if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) { 636 ShouldAffinityBeDownstream shouldAffinityBeDownstream; 637 if (lineDirectionPointFitsInBox(pointLineDirection, box, shouldAffinityBeDownstream)) 638 return createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream); 639 } 640 } 641 lastBox = box; 642 } 643 644 if (lastBox) { 645 ShouldAffinityBeDownstream shouldAffinityBeDownstream; 646 lineDirectionPointFitsInBox(pointLineDirection, lastBox, shouldAffinityBeDownstream); 647 return createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream); 648 } 649 return createPositionWithAffinity(0, DOWNSTREAM); 650 } 651 652 LayoutRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine) 653 { 654 if (!inlineBox) 655 return LayoutRect(); 656 657 ASSERT(inlineBox->isInlineTextBox()); 658 if (!inlineBox->isInlineTextBox()) 659 return LayoutRect(); 660 661 InlineTextBox* box = toInlineTextBox(inlineBox); 662 663 int height = box->root()->selectionHeight(); 664 int top = box->root()->selectionTop(); 665 666 // Go ahead and round left to snap it to the nearest pixel. 667 float left = box->positionForOffset(caretOffset); 668 669 // Distribute the caret's width to either side of the offset. 670 int caretWidthLeftOfOffset = caretWidth / 2; 671 left -= caretWidthLeftOfOffset; 672 int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset; 673 674 left = roundf(left); 675 676 float rootLeft = box->root()->logicalLeft(); 677 float rootRight = box->root()->logicalRight(); 678 679 // FIXME: should we use the width of the root inline box or the 680 // width of the containing block for this? 681 if (extraWidthToEndOfLine) 682 *extraWidthToEndOfLine = (box->root()->logicalWidth() + rootLeft) - (left + 1); 683 684 RenderBlock* cb = containingBlock(); 685 RenderStyle* cbStyle = cb->style(); 686 687 float leftEdge; 688 float rightEdge; 689 leftEdge = min<float>(0, rootLeft); 690 rightEdge = max<float>(cb->logicalWidth(), rootRight); 691 692 bool rightAligned = false; 693 switch (cbStyle->textAlign()) { 694 case RIGHT: 695 case WEBKIT_RIGHT: 696 rightAligned = true; 697 break; 698 case LEFT: 699 case WEBKIT_LEFT: 700 case CENTER: 701 case WEBKIT_CENTER: 702 break; 703 case JUSTIFY: 704 case TASTART: 705 rightAligned = !cbStyle->isLeftToRightDirection(); 706 break; 707 case TAEND: 708 rightAligned = cbStyle->isLeftToRightDirection(); 709 break; 710 } 711 712 if (rightAligned) { 713 left = max(left, leftEdge); 714 left = min(left, rootRight - caretWidth); 715 } else { 716 left = min(left, rightEdge - caretWidthRightOfOffset); 717 left = max(left, rootLeft); 718 } 719 720 return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth); 721 } 722 723 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 724 { 725 if (style()->hasTextCombine() && isCombineText()) { 726 const RenderCombineText* combineText = toRenderCombineText(this); 727 if (combineText->isCombined()) 728 return combineText->combinedTextWidth(f); 729 } 730 731 if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) { 732 float monospaceCharacterWidth = f.spaceWidth(); 733 float w = 0; 734 bool isSpace; 735 ASSERT(m_text); 736 StringImpl& text = *m_text.impl(); 737 for (int i = start; i < start + len; i++) { 738 char c = text[i]; 739 if (c <= ' ') { 740 if (c == ' ' || c == '\n') { 741 w += monospaceCharacterWidth; 742 isSpace = true; 743 } else if (c == '\t') { 744 if (style()->collapseWhiteSpace()) { 745 w += monospaceCharacterWidth; 746 isSpace = true; 747 } else { 748 w += f.tabWidth(style()->tabSize(), xPos + w); 749 isSpace = false; 750 } 751 } else 752 isSpace = false; 753 } else { 754 w += monospaceCharacterWidth; 755 isSpace = false; 756 } 757 if (isSpace && i > start) 758 w += f.wordSpacing(); 759 } 760 return w; 761 } 762 763 TextRun run = RenderBlockFlow::constructTextRun(const_cast<RenderText*>(this), f, this, start, len, style()); 764 run.setCharactersLength(textLength() - start); 765 ASSERT(run.charactersLength() >= run.length()); 766 767 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath()); 768 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 769 run.setXPos(xPos); 770 return f.width(run, fallbackFonts, glyphOverflow); 771 } 772 773 void RenderText::trimmedPrefWidths(float leadWidth, 774 float& firstLineMinWidth, bool& hasBreakableStart, 775 float& lastLineMinWidth, bool& hasBreakableEnd, 776 bool& hasBreakableChar, bool& hasBreak, 777 float& firstLineMaxWidth, float& lastLineMaxWidth, 778 float& minWidth, float& maxWidth, bool& stripFrontSpaces) 779 { 780 bool collapseWhiteSpace = style()->collapseWhiteSpace(); 781 if (!collapseWhiteSpace) 782 stripFrontSpaces = false; 783 784 if (m_hasTab || preferredLogicalWidthsDirty()) 785 computePreferredLogicalWidths(leadWidth); 786 787 hasBreakableStart = !stripFrontSpaces && m_hasBreakableStart; 788 hasBreakableEnd = m_hasBreakableEnd; 789 790 int len = textLength(); 791 792 if (!len || (stripFrontSpaces && text().impl()->containsOnlyWhitespace())) { 793 firstLineMinWidth = 0; 794 lastLineMinWidth = 0; 795 firstLineMaxWidth = 0; 796 lastLineMaxWidth = 0; 797 minWidth = 0; 798 maxWidth = 0; 799 hasBreak = false; 800 return; 801 } 802 803 minWidth = m_minWidth; 804 maxWidth = m_maxWidth; 805 806 firstLineMinWidth = m_firstLineMinWidth; 807 lastLineMinWidth = m_lastLineLineMinWidth; 808 809 hasBreakableChar = m_hasBreakableChar; 810 hasBreak = m_hasBreak; 811 812 ASSERT(m_text); 813 StringImpl& text = *m_text.impl(); 814 if (text[0] == ' ' || (text[0] == '\n' && !style()->preserveNewline()) || text[0] == '\t') { 815 const Font& font = style()->font(); // FIXME: This ignores first-line. 816 if (stripFrontSpaces) { 817 const UChar space = ' '; 818 float spaceWidth = font.width(RenderBlockFlow::constructTextRun(this, font, &space, 1, style())); 819 maxWidth -= spaceWidth; 820 } else { 821 maxWidth += font.wordSpacing(); 822 } 823 } 824 825 stripFrontSpaces = collapseWhiteSpace && m_hasEndWhiteSpace; 826 827 if (!style()->autoWrap() || minWidth > maxWidth) 828 minWidth = maxWidth; 829 830 // Compute our max widths by scanning the string for newlines. 831 if (hasBreak) { 832 const Font& f = style()->font(); // FIXME: This ignores first-line. 833 bool firstLine = true; 834 firstLineMaxWidth = maxWidth; 835 lastLineMaxWidth = maxWidth; 836 for (int i = 0; i < len; i++) { 837 int linelen = 0; 838 while (i + linelen < len && text[i + linelen] != '\n') 839 linelen++; 840 841 if (linelen) { 842 lastLineMaxWidth = widthFromCache(f, i, linelen, leadWidth + lastLineMaxWidth, 0, 0); 843 if (firstLine) { 844 firstLine = false; 845 leadWidth = 0; 846 firstLineMaxWidth = lastLineMaxWidth; 847 } 848 i += linelen; 849 } else if (firstLine) { 850 firstLineMaxWidth = 0; 851 firstLine = false; 852 leadWidth = 0; 853 } 854 855 if (i == len - 1) { 856 // A <pre> run that ends with a newline, as in, e.g., 857 // <pre>Some text\n\n<span>More text</pre> 858 lastLineMaxWidth = 0; 859 } 860 } 861 } 862 } 863 864 float RenderText::minLogicalWidth() const 865 { 866 if (preferredLogicalWidthsDirty()) 867 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0); 868 869 return m_minWidth; 870 } 871 872 float RenderText::maxLogicalWidth() const 873 { 874 if (preferredLogicalWidthsDirty()) 875 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0); 876 877 return m_maxWidth; 878 } 879 880 void RenderText::computePreferredLogicalWidths(float leadWidth) 881 { 882 HashSet<const SimpleFontData*> fallbackFonts; 883 GlyphOverflow glyphOverflow; 884 computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow); 885 if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom) 886 m_knownToHaveNoOverflowAndNoFallbackFonts = true; 887 } 888 889 static inline float hyphenWidth(RenderText* renderer, const Font& font) 890 { 891 RenderStyle* style = renderer->style(); 892 return font.width(RenderBlockFlow::constructTextRun(renderer, font, style->hyphenString().string(), style)); 893 } 894 895 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow) 896 { 897 ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts); 898 899 m_minWidth = 0; 900 m_maxWidth = 0; 901 m_firstLineMinWidth = 0; 902 m_lastLineLineMinWidth = 0; 903 904 if (isBR()) 905 return; 906 907 float currMinWidth = 0; 908 float currMaxWidth = 0; 909 m_hasBreakableChar = false; 910 m_hasBreak = false; 911 m_hasTab = false; 912 m_hasBreakableStart = false; 913 m_hasBreakableEnd = false; 914 m_hasEndWhiteSpace = false; 915 916 RenderStyle* styleToUse = style(); 917 const Font& f = styleToUse->font(); // FIXME: This ignores first-line. 918 float wordSpacing = styleToUse->wordSpacing(); 919 int len = textLength(); 920 LazyLineBreakIterator breakIterator(m_text, styleToUse->locale()); 921 bool needsWordSpacing = false; 922 bool ignoringSpaces = false; 923 bool isSpace = false; 924 bool firstWord = true; 925 bool firstLine = true; 926 int nextBreakable = -1; 927 int lastWordBoundary = 0; 928 929 // Non-zero only when kerning is enabled, in which case we measure words with their trailing 930 // space, then subtract its width. 931 float wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(RenderBlockFlow::constructTextRun(this, f, &space, 1, styleToUse)) + wordSpacing : 0; 932 933 // If automatic hyphenation is allowed, we keep track of the width of the widest word (or word 934 // fragment) encountered so far, and only try hyphenating words that are wider. 935 float maxWordWidth = numeric_limits<float>::max(); 936 int firstGlyphLeftOverflow = -1; 937 938 bool breakAll = (styleToUse->wordBreak() == BreakAllWordBreak || styleToUse->wordBreak() == BreakWordBreak) && styleToUse->autoWrap(); 939 940 for (int i = 0; i < len; i++) { 941 UChar c = uncheckedCharacterAt(i); 942 943 bool previousCharacterIsSpace = isSpace; 944 945 bool isNewline = false; 946 if (c == '\n') { 947 if (styleToUse->preserveNewline()) { 948 m_hasBreak = true; 949 isNewline = true; 950 isSpace = false; 951 } else 952 isSpace = true; 953 } else if (c == '\t') { 954 if (!styleToUse->collapseWhiteSpace()) { 955 m_hasTab = true; 956 isSpace = false; 957 } else 958 isSpace = true; 959 } else 960 isSpace = c == ' '; 961 962 bool isBreakableLocation = isNewline || (isSpace && styleToUse->autoWrap()); 963 if (!i) 964 m_hasBreakableStart = isBreakableLocation; 965 if (i == len - 1) { 966 m_hasBreakableEnd = isBreakableLocation; 967 m_hasEndWhiteSpace = isNewline || isSpace; 968 } 969 970 if (!ignoringSpaces && styleToUse->collapseWhiteSpace() && previousCharacterIsSpace && isSpace) 971 ignoringSpaces = true; 972 973 if (ignoringSpaces && !isSpace) 974 ignoringSpaces = false; 975 976 // Ignore spaces and soft hyphens 977 if (ignoringSpaces) { 978 ASSERT(lastWordBoundary == i); 979 lastWordBoundary++; 980 continue; 981 } else if (c == softHyphen) { 982 currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow); 983 if (firstGlyphLeftOverflow < 0) 984 firstGlyphLeftOverflow = glyphOverflow.left; 985 lastWordBoundary = i + 1; 986 continue; 987 } 988 989 bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable); 990 bool betweenWords = true; 991 int j = i; 992 while (c != '\n' && c != ' ' && c != '\t' && (c != softHyphen)) { 993 j++; 994 if (j == len) 995 break; 996 c = uncheckedCharacterAt(j); 997 if (isBreakable(breakIterator, j, nextBreakable) && characterAt(j - 1) != softHyphen) 998 break; 999 if (breakAll) { 1000 betweenWords = false; 1001 break; 1002 } 1003 } 1004 1005 int wordLen = j - i; 1006 if (wordLen) { 1007 bool isSpace = (j < len) && c == ' '; 1008 float w; 1009 if (wordTrailingSpaceWidth && isSpace) 1010 w = widthFromCache(f, i, wordLen + 1, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow) - wordTrailingSpaceWidth; 1011 else { 1012 w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow); 1013 if (c == softHyphen) 1014 currMinWidth += hyphenWidth(this, f); 1015 } 1016 1017 maxWordWidth = max(maxWordWidth, w); 1018 1019 if (firstGlyphLeftOverflow < 0) 1020 firstGlyphLeftOverflow = glyphOverflow.left; 1021 currMinWidth += w; 1022 if (betweenWords) { 1023 if (lastWordBoundary == i) 1024 currMaxWidth += w; 1025 else 1026 currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow); 1027 lastWordBoundary = j; 1028 } 1029 1030 bool isCollapsibleWhiteSpace = (j < len) && styleToUse->isCollapsibleWhiteSpace(c); 1031 if (j < len && styleToUse->autoWrap()) 1032 m_hasBreakableChar = true; 1033 1034 // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the 1035 // last word in the run. 1036 if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j)) 1037 currMaxWidth += wordSpacing; 1038 1039 if (firstWord) { 1040 firstWord = false; 1041 // If the first character in the run is breakable, then we consider ourselves to have a beginning 1042 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever 1043 // being appended to a previous text run when considering the total minimum width of the containing block. 1044 if (hasBreak) 1045 m_hasBreakableChar = true; 1046 m_firstLineMinWidth = hasBreak ? 0 : currMinWidth; 1047 } 1048 m_lastLineLineMinWidth = currMinWidth; 1049 1050 if (currMinWidth > m_minWidth) 1051 m_minWidth = currMinWidth; 1052 currMinWidth = 0; 1053 1054 i += wordLen - 1; 1055 } else { 1056 // Nowrap can never be broken, so don't bother setting the 1057 // breakable character boolean. Pre can only be broken if we encounter a newline. 1058 if (style()->autoWrap() || isNewline) 1059 m_hasBreakableChar = true; 1060 1061 if (currMinWidth > m_minWidth) 1062 m_minWidth = currMinWidth; 1063 currMinWidth = 0; 1064 1065 if (isNewline) { // Only set if preserveNewline was true and we saw a newline. 1066 if (firstLine) { 1067 firstLine = false; 1068 leadWidth = 0; 1069 if (!styleToUse->autoWrap()) 1070 m_firstLineMinWidth = currMaxWidth; 1071 } 1072 1073 if (currMaxWidth > m_maxWidth) 1074 m_maxWidth = currMaxWidth; 1075 currMaxWidth = 0; 1076 } else { 1077 TextRun run = RenderBlockFlow::constructTextRun(this, f, this, i, 1, styleToUse); 1078 run.setCharactersLength(len - i); 1079 ASSERT(run.charactersLength() >= run.length()); 1080 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 1081 run.setXPos(leadWidth + currMaxWidth); 1082 1083 currMaxWidth += f.width(run); 1084 glyphOverflow.right = 0; 1085 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; 1086 } 1087 ASSERT(lastWordBoundary == i); 1088 lastWordBoundary++; 1089 } 1090 } 1091 1092 if (firstGlyphLeftOverflow > 0) 1093 glyphOverflow.left = firstGlyphLeftOverflow; 1094 1095 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord)) 1096 currMaxWidth += wordSpacing; 1097 1098 m_minWidth = max(currMinWidth, m_minWidth); 1099 m_maxWidth = max(currMaxWidth, m_maxWidth); 1100 1101 if (!styleToUse->autoWrap()) 1102 m_minWidth = m_maxWidth; 1103 1104 if (styleToUse->whiteSpace() == PRE) { 1105 if (firstLine) 1106 m_firstLineMinWidth = m_maxWidth; 1107 m_lastLineLineMinWidth = currMaxWidth; 1108 } 1109 1110 clearPreferredLogicalWidthsDirty(); 1111 } 1112 1113 bool RenderText::isAllCollapsibleWhitespace() const 1114 { 1115 unsigned length = textLength(); 1116 if (is8Bit()) { 1117 for (unsigned i = 0; i < length; ++i) { 1118 if (!style()->isCollapsibleWhiteSpace(characters8()[i])) 1119 return false; 1120 } 1121 return true; 1122 } 1123 for (unsigned i = 0; i < length; ++i) { 1124 if (!style()->isCollapsibleWhiteSpace(characters16()[i])) 1125 return false; 1126 } 1127 return true; 1128 } 1129 1130 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const 1131 { 1132 ASSERT(m_text); 1133 StringImpl& text = *m_text.impl(); 1134 unsigned currPos; 1135 for (currPos = from; 1136 currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t'); 1137 currPos++) { } 1138 return currPos >= (from + len); 1139 } 1140 1141 FloatPoint RenderText::firstRunOrigin() const 1142 { 1143 return IntPoint(firstRunX(), firstRunY()); 1144 } 1145 1146 float RenderText::firstRunX() const 1147 { 1148 return m_firstTextBox ? m_firstTextBox->x() : 0; 1149 } 1150 1151 float RenderText::firstRunY() const 1152 { 1153 return m_firstTextBox ? m_firstTextBox->y() : 0; 1154 } 1155 1156 void RenderText::setSelectionState(SelectionState state) 1157 { 1158 RenderObject::setSelectionState(state); 1159 1160 if (canUpdateSelectionOnRootLineBoxes()) { 1161 if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { 1162 int startPos, endPos; 1163 selectionStartEnd(startPos, endPos); 1164 if (selectionState() == SelectionStart) { 1165 endPos = textLength(); 1166 1167 // to handle selection from end of text to end of line 1168 if (startPos && startPos == endPos) 1169 startPos = endPos - 1; 1170 } else if (selectionState() == SelectionEnd) 1171 startPos = 0; 1172 1173 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1174 if (box->isSelected(startPos, endPos)) { 1175 RootInlineBox* root = box->root(); 1176 if (root) 1177 root->setHasSelectedChildren(true); 1178 } 1179 } 1180 } else { 1181 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1182 RootInlineBox* root = box->root(); 1183 if (root) 1184 root->setHasSelectedChildren(state == SelectionInside); 1185 } 1186 } 1187 } 1188 1189 // The containing block can be null in case of an orphaned tree. 1190 RenderBlock* containingBlock = this->containingBlock(); 1191 if (containingBlock && !containingBlock->isRenderView()) 1192 containingBlock->setSelectionState(state); 1193 } 1194 1195 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force) 1196 { 1197 if (!force && equal(m_text.impl(), text.get())) 1198 return; 1199 1200 unsigned oldLen = textLength(); 1201 unsigned newLen = text->length(); 1202 int delta = newLen - oldLen; 1203 unsigned end = len ? offset + len - 1 : offset; 1204 1205 RootInlineBox* firstRootBox = 0; 1206 RootInlineBox* lastRootBox = 0; 1207 1208 bool dirtiedLines = false; 1209 1210 // Dirty all text boxes that include characters in between offset and offset+len. 1211 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1212 // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264 1213 // Text run is entirely before the affected range. 1214 if (curr->end() < offset) 1215 continue; 1216 1217 // Text run is entirely after the affected range. 1218 if (curr->start() > end) { 1219 curr->offsetRun(delta); 1220 RootInlineBox* root = curr->root(); 1221 if (!firstRootBox) { 1222 firstRootBox = root; 1223 // The affected area was in between two runs. Go ahead and mark the root box of 1224 // the run after the affected area as dirty. 1225 firstRootBox->markDirty(); 1226 dirtiedLines = true; 1227 } 1228 lastRootBox = root; 1229 } else if (curr->end() >= offset && curr->end() <= end) { 1230 // Text run overlaps with the left end of the affected range. 1231 curr->dirtyLineBoxes(); 1232 dirtiedLines = true; 1233 } else if (curr->start() <= offset && curr->end() >= end) { 1234 // Text run subsumes the affected range. 1235 curr->dirtyLineBoxes(); 1236 dirtiedLines = true; 1237 } else if (curr->start() <= end && curr->end() >= end) { 1238 // Text run overlaps with right end of the affected range. 1239 curr->dirtyLineBoxes(); 1240 dirtiedLines = true; 1241 } 1242 } 1243 1244 // Now we have to walk all of the clean lines and adjust their cached line break information 1245 // to reflect our updated offsets. 1246 if (lastRootBox) 1247 lastRootBox = lastRootBox->nextRootBox(); 1248 if (firstRootBox) { 1249 RootInlineBox* prev = firstRootBox->prevRootBox(); 1250 if (prev) 1251 firstRootBox = prev; 1252 } else if (lastTextBox()) { 1253 ASSERT(!lastRootBox); 1254 firstRootBox = lastTextBox()->root(); 1255 firstRootBox->markDirty(); 1256 dirtiedLines = true; 1257 } 1258 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) { 1259 if (curr->lineBreakObj() == this && curr->lineBreakPos() > end) 1260 curr->setLineBreakPos(clampToInteger(curr->lineBreakPos() + delta)); 1261 } 1262 1263 // If the text node is empty, dirty the line where new text will be inserted. 1264 if (!firstTextBox() && parent()) { 1265 parent()->dirtyLinesFromChangedChild(this); 1266 dirtiedLines = true; 1267 } 1268 1269 m_linesDirty = dirtiedLines; 1270 setText(text, force || dirtiedLines); 1271 } 1272 1273 void RenderText::transformText() 1274 { 1275 if (RefPtr<StringImpl> textToTransform = originalText()) 1276 setText(textToTransform.release(), true); 1277 } 1278 1279 static inline bool isInlineFlowOrEmptyText(const RenderObject* o) 1280 { 1281 if (o->isRenderInline()) 1282 return true; 1283 if (!o->isText()) 1284 return false; 1285 return toRenderText(o)->text().isEmpty(); 1286 } 1287 1288 UChar RenderText::previousCharacter() const 1289 { 1290 // find previous text renderer if one exists 1291 const RenderObject* previousText = this; 1292 while ((previousText = previousText->previousInPreOrder())) 1293 if (!isInlineFlowOrEmptyText(previousText)) 1294 break; 1295 UChar prev = ' '; 1296 if (previousText && previousText->isText()) 1297 if (StringImpl* previousString = toRenderText(previousText)->text().impl()) 1298 prev = (*previousString)[previousString->length() - 1]; 1299 return prev; 1300 } 1301 1302 void RenderText::addLayerHitTestRects(LayerHitTestRects&, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const 1303 { 1304 // Text nodes aren't event targets, so don't descend any further. 1305 } 1306 1307 void applyTextTransform(const RenderStyle* style, String& text, UChar previousCharacter) 1308 { 1309 if (!style) 1310 return; 1311 1312 switch (style->textTransform()) { 1313 case TTNONE: 1314 break; 1315 case CAPITALIZE: 1316 makeCapitalized(&text, previousCharacter); 1317 break; 1318 case UPPERCASE: 1319 text = text.upper(style->locale()); 1320 break; 1321 case LOWERCASE: 1322 text = text.lower(style->locale()); 1323 break; 1324 } 1325 } 1326 1327 void RenderText::setTextInternal(PassRefPtr<StringImpl> text) 1328 { 1329 ASSERT(text); 1330 m_text = text; 1331 1332 if (style()) { 1333 applyTextTransform(style(), m_text, previousCharacter()); 1334 1335 // We use the same characters here as for list markers. 1336 // See the listMarkerText function in RenderListMarker.cpp. 1337 switch (style()->textSecurity()) { 1338 case TSNONE: 1339 break; 1340 case TSCIRCLE: 1341 secureText(whiteBullet); 1342 break; 1343 case TSDISC: 1344 secureText(bullet); 1345 break; 1346 case TSSQUARE: 1347 secureText(blackSquare); 1348 } 1349 } 1350 1351 ASSERT(m_text); 1352 ASSERT(!isBR() || (textLength() == 1 && m_text[0] == '\n')); 1353 1354 m_isAllASCII = m_text.containsOnlyASCII(); 1355 m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath(); 1356 } 1357 1358 void RenderText::secureText(UChar mask) 1359 { 1360 if (!m_text.length()) 1361 return; 1362 1363 int lastTypedCharacterOffsetToReveal = -1; 1364 UChar revealedText; 1365 SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0; 1366 if (secureTextTimer && secureTextTimer->isActive()) { 1367 lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset(); 1368 if (lastTypedCharacterOffsetToReveal >= 0) 1369 revealedText = m_text[lastTypedCharacterOffsetToReveal]; 1370 } 1371 1372 m_text.fill(mask); 1373 if (lastTypedCharacterOffsetToReveal >= 0) { 1374 m_text.replace(lastTypedCharacterOffsetToReveal, 1, String(&revealedText, 1)); 1375 // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency. 1376 secureTextTimer->invalidate(); 1377 } 1378 } 1379 1380 void RenderText::setText(PassRefPtr<StringImpl> text, bool force) 1381 { 1382 ASSERT(text); 1383 1384 if (!force && equal(m_text.impl(), text.get())) 1385 return; 1386 1387 setTextInternal(text); 1388 setNeedsLayoutAndPrefWidthsRecalc(); 1389 m_knownToHaveNoOverflowAndNoFallbackFonts = false; 1390 1391 if (AXObjectCache* cache = document().existingAXObjectCache()) 1392 cache->textChanged(this); 1393 } 1394 1395 void RenderText::dirtyLineBoxes(bool fullLayout) 1396 { 1397 if (fullLayout) 1398 deleteTextBoxes(); 1399 else if (!m_linesDirty) { 1400 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 1401 box->dirtyLineBoxes(); 1402 } 1403 m_linesDirty = false; 1404 } 1405 1406 InlineTextBox* RenderText::createTextBox() 1407 { 1408 return new InlineTextBox(this); 1409 } 1410 1411 InlineTextBox* RenderText::createInlineTextBox() 1412 { 1413 InlineTextBox* textBox = createTextBox(); 1414 if (!m_firstTextBox) 1415 m_firstTextBox = m_lastTextBox = textBox; 1416 else { 1417 m_lastTextBox->setNextTextBox(textBox); 1418 textBox->setPreviousTextBox(m_lastTextBox); 1419 m_lastTextBox = textBox; 1420 } 1421 textBox->setIsText(true); 1422 return textBox; 1423 } 1424 1425 void RenderText::positionLineBox(InlineBox* box) 1426 { 1427 InlineTextBox* s = toInlineTextBox(box); 1428 1429 // FIXME: should not be needed!!! 1430 if (!s->len()) { 1431 // We want the box to be destroyed. 1432 s->remove(); 1433 if (m_firstTextBox == s) 1434 m_firstTextBox = s->nextTextBox(); 1435 else 1436 s->prevTextBox()->setNextTextBox(s->nextTextBox()); 1437 if (m_lastTextBox == s) 1438 m_lastTextBox = s->prevTextBox(); 1439 else 1440 s->nextTextBox()->setPreviousTextBox(s->prevTextBox()); 1441 s->destroy(); 1442 return; 1443 } 1444 1445 m_containsReversedText |= !s->isLeftToRightDirection(); 1446 } 1447 1448 float RenderText::width(unsigned from, unsigned len, float xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 1449 { 1450 if (from >= textLength()) 1451 return 0; 1452 1453 if (from + len > textLength()) 1454 len = textLength() - from; 1455 1456 return width(from, len, style(firstLine)->font(), xPos, fallbackFonts, glyphOverflow); 1457 } 1458 1459 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 1460 { 1461 ASSERT(from + len <= textLength()); 1462 if (!textLength()) 1463 return 0; 1464 1465 float w; 1466 if (&f == &style()->font()) { 1467 if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) { 1468 if (fallbackFonts) { 1469 ASSERT(glyphOverflow); 1470 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) { 1471 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow); 1472 if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom) 1473 m_knownToHaveNoOverflowAndNoFallbackFonts = true; 1474 } 1475 w = m_maxWidth; 1476 } else 1477 w = maxLogicalWidth(); 1478 } else 1479 w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow); 1480 } else { 1481 TextRun run = RenderBlockFlow::constructTextRun(const_cast<RenderText*>(this), f, this, from, len, style()); 1482 run.setCharactersLength(textLength() - from); 1483 ASSERT(run.charactersLength() >= run.length()); 1484 1485 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath()); 1486 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 1487 run.setXPos(xPos); 1488 w = f.width(run, fallbackFonts, glyphOverflow); 1489 } 1490 1491 return w; 1492 } 1493 1494 IntRect RenderText::linesBoundingBox() const 1495 { 1496 IntRect result; 1497 1498 ASSERT(!firstTextBox() == !lastTextBox()); // Either both are null or both exist. 1499 if (firstTextBox() && lastTextBox()) { 1500 // Return the width of the minimal left side and the maximal right side. 1501 float logicalLeftSide = 0; 1502 float logicalRightSide = 0; 1503 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1504 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide) 1505 logicalLeftSide = curr->logicalLeft(); 1506 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide) 1507 logicalRightSide = curr->logicalRight(); 1508 } 1509 1510 bool isHorizontal = style()->isHorizontalWritingMode(); 1511 1512 float x = isHorizontal ? logicalLeftSide : firstTextBox()->x(); 1513 float y = isHorizontal ? firstTextBox()->y() : logicalLeftSide; 1514 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x; 1515 float height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide; 1516 result = enclosingIntRect(FloatRect(x, y, width, height)); 1517 } 1518 1519 return result; 1520 } 1521 1522 LayoutRect RenderText::linesVisualOverflowBoundingBox() const 1523 { 1524 if (!firstTextBox()) 1525 return LayoutRect(); 1526 1527 // Return the width of the minimal left side and the maximal right side. 1528 LayoutUnit logicalLeftSide = LayoutUnit::max(); 1529 LayoutUnit logicalRightSide = LayoutUnit::min(); 1530 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1531 logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow()); 1532 logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow()); 1533 } 1534 1535 LayoutUnit logicalTop = firstTextBox()->logicalTopVisualOverflow(); 1536 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide; 1537 LayoutUnit logicalHeight = lastTextBox()->logicalBottomVisualOverflow() - logicalTop; 1538 1539 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight); 1540 if (!style()->isHorizontalWritingMode()) 1541 rect = rect.transposedRect(); 1542 return rect; 1543 } 1544 1545 LayoutRect RenderText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const 1546 { 1547 RenderObject* rendererToRepaint = containingBlock(); 1548 1549 // Do not cross self-painting layer boundaries. 1550 RenderObject* enclosingLayerRenderer = enclosingLayer()->renderer(); 1551 if (enclosingLayerRenderer != rendererToRepaint && !rendererToRepaint->isDescendantOf(enclosingLayerRenderer)) 1552 rendererToRepaint = enclosingLayerRenderer; 1553 1554 // The renderer we chose to repaint may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint. 1555 if (repaintContainer && repaintContainer != rendererToRepaint && !rendererToRepaint->isDescendantOf(repaintContainer)) 1556 return repaintContainer->clippedOverflowRectForRepaint(repaintContainer); 1557 1558 return rendererToRepaint->clippedOverflowRectForRepaint(repaintContainer); 1559 } 1560 1561 LayoutRect RenderText::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent) 1562 { 1563 ASSERT(!needsLayout()); 1564 1565 if (selectionState() == SelectionNone) 1566 return LayoutRect(); 1567 RenderBlock* cb = containingBlock(); 1568 if (!cb) 1569 return LayoutRect(); 1570 1571 // Now calculate startPos and endPos for painting selection. 1572 // We include a selection while endPos > 0 1573 int startPos, endPos; 1574 if (selectionState() == SelectionInside) { 1575 // We are fully selected. 1576 startPos = 0; 1577 endPos = textLength(); 1578 } else { 1579 selectionStartEnd(startPos, endPos); 1580 if (selectionState() == SelectionStart) 1581 endPos = textLength(); 1582 else if (selectionState() == SelectionEnd) 1583 startPos = 0; 1584 } 1585 1586 if (startPos == endPos) 1587 return IntRect(); 1588 1589 LayoutRect rect; 1590 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1591 rect.unite(box->localSelectionRect(startPos, endPos)); 1592 rect.unite(ellipsisRectForBox(box, startPos, endPos)); 1593 } 1594 1595 if (clipToVisibleContent) 1596 computeRectForRepaint(repaintContainer, rect); 1597 else { 1598 if (cb->hasColumns()) 1599 cb->adjustRectForColumns(rect); 1600 1601 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox(); 1602 } 1603 1604 return rect; 1605 } 1606 1607 int RenderText::caretMinOffset() const 1608 { 1609 InlineTextBox* box = firstTextBox(); 1610 if (!box) 1611 return 0; 1612 int minOffset = box->start(); 1613 for (box = box->nextTextBox(); box; box = box->nextTextBox()) 1614 minOffset = min<int>(minOffset, box->start()); 1615 return minOffset; 1616 } 1617 1618 int RenderText::caretMaxOffset() const 1619 { 1620 InlineTextBox* box = lastTextBox(); 1621 if (!lastTextBox()) 1622 return textLength(); 1623 1624 int maxOffset = box->start() + box->len(); 1625 for (box = box->prevTextBox(); box; box = box->prevTextBox()) 1626 maxOffset = max<int>(maxOffset, box->start() + box->len()); 1627 return maxOffset; 1628 } 1629 1630 unsigned RenderText::renderedTextLength() const 1631 { 1632 int l = 0; 1633 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 1634 l += box->len(); 1635 return l; 1636 } 1637 1638 int RenderText::previousOffset(int current) const 1639 { 1640 if (isAllASCII() || m_text.is8Bit()) 1641 return current - 1; 1642 1643 StringImpl* textImpl = m_text.impl(); 1644 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); 1645 if (!iterator) 1646 return current - 1; 1647 1648 long result = iterator->preceding(current); 1649 if (result == TextBreakDone) 1650 result = current - 1; 1651 1652 1653 return result; 1654 } 1655 1656 #if OS(POSIX) 1657 1658 #define HANGUL_CHOSEONG_START (0x1100) 1659 #define HANGUL_CHOSEONG_END (0x115F) 1660 #define HANGUL_JUNGSEONG_START (0x1160) 1661 #define HANGUL_JUNGSEONG_END (0x11A2) 1662 #define HANGUL_JONGSEONG_START (0x11A8) 1663 #define HANGUL_JONGSEONG_END (0x11F9) 1664 #define HANGUL_SYLLABLE_START (0xAC00) 1665 #define HANGUL_SYLLABLE_END (0xD7AF) 1666 #define HANGUL_JONGSEONG_COUNT (28) 1667 1668 enum HangulState { 1669 HangulStateL, 1670 HangulStateV, 1671 HangulStateT, 1672 HangulStateLV, 1673 HangulStateLVT, 1674 HangulStateBreak 1675 }; 1676 1677 inline bool isHangulLVT(UChar32 character) 1678 { 1679 return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT; 1680 } 1681 1682 inline bool isMark(UChar32 c) 1683 { 1684 int8_t charType = u_charType(c); 1685 return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK; 1686 } 1687 1688 inline bool isRegionalIndicator(UChar32 c) 1689 { 1690 // National flag emoji each consists of a pair of regional indicator symbols. 1691 return 0x1F1E6 <= c && c <= 0x1F1FF; 1692 } 1693 1694 #endif 1695 1696 int RenderText::previousOffsetForBackwardDeletion(int current) const 1697 { 1698 #if OS(POSIX) 1699 ASSERT(m_text); 1700 StringImpl& text = *m_text.impl(); 1701 UChar32 character; 1702 bool sawRegionalIndicator = false; 1703 while (current > 0) { 1704 if (U16_IS_TRAIL(text[--current])) 1705 --current; 1706 if (current < 0) 1707 break; 1708 1709 UChar32 character = text.characterStartingAt(current); 1710 1711 if (sawRegionalIndicator) { 1712 // We don't check if the pair of regional indicator symbols before current position can actually be combined 1713 // into a flag, and just delete it. This may not agree with how the pair is rendered in edge cases, 1714 // but is good enough in practice. 1715 if (isRegionalIndicator(character)) 1716 break; 1717 // Don't delete a preceding character that isn't a regional indicator symbol. 1718 U16_FWD_1_UNSAFE(text, current); 1719 } 1720 1721 // We don't combine characters in Armenian ... Limbu range for backward deletion. 1722 if ((character >= 0x0530) && (character < 0x1950)) 1723 break; 1724 1725 if (isRegionalIndicator(character)) { 1726 sawRegionalIndicator = true; 1727 continue; 1728 } 1729 1730 if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F)) 1731 break; 1732 } 1733 1734 if (current <= 0) 1735 return current; 1736 1737 // Hangul 1738 character = text.characterStartingAt(current); 1739 if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) { 1740 HangulState state; 1741 1742 if (character < HANGUL_JUNGSEONG_START) 1743 state = HangulStateL; 1744 else if (character < HANGUL_JONGSEONG_START) 1745 state = HangulStateV; 1746 else if (character < HANGUL_SYLLABLE_START) 1747 state = HangulStateT; 1748 else 1749 state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV; 1750 1751 while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) { 1752 switch (state) { 1753 case HangulStateV: 1754 if (character <= HANGUL_CHOSEONG_END) 1755 state = HangulStateL; 1756 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character)) 1757 state = HangulStateLV; 1758 else if (character > HANGUL_JUNGSEONG_END) 1759 state = HangulStateBreak; 1760 break; 1761 case HangulStateT: 1762 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END)) 1763 state = HangulStateV; 1764 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END)) 1765 state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV); 1766 else if (character < HANGUL_JUNGSEONG_START) 1767 state = HangulStateBreak; 1768 break; 1769 default: 1770 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak; 1771 break; 1772 } 1773 if (state == HangulStateBreak) 1774 break; 1775 1776 --current; 1777 } 1778 } 1779 1780 return current; 1781 #else 1782 // Platforms other than Unix-like delete by one code point. 1783 if (U16_IS_TRAIL(m_text[--current])) 1784 --current; 1785 if (current < 0) 1786 current = 0; 1787 return current; 1788 #endif 1789 } 1790 1791 int RenderText::nextOffset(int current) const 1792 { 1793 if (isAllASCII() || m_text.is8Bit()) 1794 return current + 1; 1795 1796 StringImpl* textImpl = m_text.impl(); 1797 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); 1798 if (!iterator) 1799 return current + 1; 1800 1801 long result = iterator->following(current); 1802 if (result == TextBreakDone) 1803 result = current + 1; 1804 1805 return result; 1806 } 1807 1808 bool RenderText::computeCanUseSimpleFontCodePath() const 1809 { 1810 if (isAllASCII() || m_text.is8Bit()) 1811 return true; 1812 return Font::characterRangeCodePath(characters16(), length()) == Font::Simple; 1813 } 1814 1815 #ifndef NDEBUG 1816 1817 void RenderText::checkConsistency() const 1818 { 1819 #ifdef CHECK_CONSISTENCY 1820 const InlineTextBox* prev = 0; 1821 for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { 1822 ASSERT(child->renderer() == this); 1823 ASSERT(child->prevTextBox() == prev); 1824 prev = child; 1825 } 1826 ASSERT(prev == m_lastTextBox); 1827 #endif 1828 } 1829 1830 #endif 1831 1832 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset) 1833 { 1834 if (!gSecureTextTimers) 1835 gSecureTextTimers = new SecureTextTimerMap; 1836 1837 SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this); 1838 if (!secureTextTimer) { 1839 secureTextTimer = new SecureTextTimer(this); 1840 gSecureTextTimers->add(this, secureTextTimer); 1841 } 1842 secureTextTimer->restartWithNewText(lastTypedCharacterOffset); 1843 } 1844 1845 PassRefPtr<AbstractInlineTextBox> RenderText::firstAbstractInlineTextBox() 1846 { 1847 return AbstractInlineTextBox::getOrCreate(this, m_firstTextBox); 1848 } 1849 1850 } // namespace WebCore 1851