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