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/TextRunConstructor.h" 42 #include "core/rendering/break_lines.h" 43 #include "platform/fonts/Character.h" 44 #include "platform/fonts/FontCache.h" 45 #include "platform/geometry/FloatQuad.h" 46 #include "platform/text/BidiResolver.h" 47 #include "platform/text/TextBreakIterator.h" 48 #include "platform/text/TextRunIterator.h" 49 #include "wtf/text/StringBuffer.h" 50 #include "wtf/text/StringBuilder.h" 51 #include "wtf/unicode/CharacterNames.h" 52 53 using namespace WTF; 54 using namespace Unicode; 55 56 namespace blink { 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 >= std::numeric_limits<unsigned>::max()) 108 CRASH(); 109 110 StringBuffer<UChar> stringWithPrevious(length + 1); 111 stringWithPrevious[0] = previous == noBreakSpace ? space : previous; 112 for (unsigned i = 1; i < length + 1; i++) { 113 // Replace   with a real space since ICU no longer treats   as a word separator. 114 if (input[i - 1] == noBreakSpace) 115 stringWithPrevious[i] = space; 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 #if ENABLE(ASSERT) 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 paint invalidations from a style change of a text run, since 194 // we already did this for the parent of the text run. 195 // We do have to schedule layouts, though, since a style change can force us to 196 // need to relayout. 197 if (diff.needsFullLayout()) { 198 setNeedsLayoutAndPrefWidthsRecalc(); 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 blink::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(space); 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 = std::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 = std::min(start, static_cast<unsigned>(INT_MAX)); 363 end = std::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 = std::max<int>(startPos - box->start(), 0); 401 int ellipsisEndPosition = std::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 = std::min(start, static_cast<unsigned>(INT_MAX)); 446 end = std::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 = std::min(rootBox.selectionTop(), rootBox.lineTop()); 615 if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) { 616 LayoutUnit bottom = rootBox.selectionBottom(); 617 if (rootBox.nextRootBox()) 618 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop()); 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 = std::min<float>(0, rootLeft); 675 rightEdge = std::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 // for dir=auto, use inlineBoxBidiLevel() to test the correct direction for the cursor. 698 if (rightAligned && (node() && node()->selfOrAncestorHasDirAutoAttribute())) { 699 if (inlineBox->bidiLevel()%2 != 1) 700 rightAligned = false; 701 } 702 703 if (rightAligned) { 704 left = std::max(left, leftEdge); 705 left = std::min(left, rootRight - caretWidth); 706 } else { 707 left = std::min(left, rightEdge - caretWidthRightOfOffset); 708 left = std::max(left, rootLeft); 709 } 710 711 return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth); 712 } 713 714 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, TextDirection textDirection, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 715 { 716 if (style()->hasTextCombine() && isCombineText()) { 717 const RenderCombineText* combineText = toRenderCombineText(this); 718 if (combineText->isCombined()) 719 return combineText->combinedTextWidth(f); 720 } 721 722 if (f.isFixedPitch() && f.fontDescription().variant() == FontVariantNormal && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) { 723 float monospaceCharacterWidth = f.spaceWidth(); 724 float w = 0; 725 bool isSpace; 726 ASSERT(m_text); 727 StringImpl& text = *m_text.impl(); 728 for (int i = start; i < start + len; i++) { 729 char c = text[i]; 730 if (c <= space) { 731 if (c == space || c == newlineCharacter) { 732 w += monospaceCharacterWidth; 733 isSpace = true; 734 } else if (c == characterTabulation) { 735 if (style()->collapseWhiteSpace()) { 736 w += monospaceCharacterWidth; 737 isSpace = true; 738 } else { 739 w += f.tabWidth(style()->tabSize(), xPos + w); 740 isSpace = false; 741 } 742 } else 743 isSpace = false; 744 } else { 745 w += monospaceCharacterWidth; 746 isSpace = false; 747 } 748 if (isSpace && i > start) 749 w += f.fontDescription().wordSpacing(); 750 } 751 return w; 752 } 753 754 TextRun run = constructTextRun(const_cast<RenderText*>(this), f, this, start, len, style(), textDirection); 755 run.setCharactersLength(textLength() - start); 756 ASSERT(run.charactersLength() >= run.length()); 757 758 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath()); 759 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 760 run.setXPos(xPos); 761 FontCachePurgePreventer fontCachePurgePreventer; 762 return f.width(run, fallbackFonts, glyphOverflow); 763 } 764 765 void RenderText::trimmedPrefWidths(float leadWidth, 766 float& firstLineMinWidth, bool& hasBreakableStart, 767 float& lastLineMinWidth, bool& hasBreakableEnd, 768 bool& hasBreakableChar, bool& hasBreak, 769 float& firstLineMaxWidth, float& lastLineMaxWidth, 770 float& minWidth, float& maxWidth, bool& stripFrontSpaces, 771 TextDirection direction) 772 { 773 bool collapseWhiteSpace = style()->collapseWhiteSpace(); 774 if (!collapseWhiteSpace) 775 stripFrontSpaces = false; 776 777 if (m_hasTab || preferredLogicalWidthsDirty()) 778 computePreferredLogicalWidths(leadWidth); 779 780 hasBreakableStart = !stripFrontSpaces && m_hasBreakableStart; 781 hasBreakableEnd = m_hasBreakableEnd; 782 783 int len = textLength(); 784 785 if (!len || (stripFrontSpaces && text().impl()->containsOnlyWhitespace())) { 786 firstLineMinWidth = 0; 787 lastLineMinWidth = 0; 788 firstLineMaxWidth = 0; 789 lastLineMaxWidth = 0; 790 minWidth = 0; 791 maxWidth = 0; 792 hasBreak = false; 793 return; 794 } 795 796 minWidth = m_minWidth; 797 maxWidth = m_maxWidth; 798 799 firstLineMinWidth = m_firstLineMinWidth; 800 lastLineMinWidth = m_lastLineLineMinWidth; 801 802 hasBreakableChar = m_hasBreakableChar; 803 hasBreak = m_hasBreak; 804 805 ASSERT(m_text); 806 StringImpl& text = *m_text.impl(); 807 if (text[0] == space || (text[0] == newlineCharacter && !style()->preserveNewline()) || text[0] == characterTabulation) { 808 const Font& font = style()->font(); // FIXME: This ignores first-line. 809 if (stripFrontSpaces) { 810 const UChar spaceChar = space; 811 float spaceWidth = font.width(constructTextRun(this, font, &spaceChar, 1, style(), direction)); 812 maxWidth -= spaceWidth; 813 } else { 814 maxWidth += font.fontDescription().wordSpacing(); 815 } 816 } 817 818 stripFrontSpaces = collapseWhiteSpace && m_hasEndWhiteSpace; 819 820 if (!style()->autoWrap() || minWidth > maxWidth) 821 minWidth = maxWidth; 822 823 // Compute our max widths by scanning the string for newlines. 824 if (hasBreak) { 825 const Font& f = style()->font(); // FIXME: This ignores first-line. 826 bool firstLine = true; 827 firstLineMaxWidth = maxWidth; 828 lastLineMaxWidth = maxWidth; 829 for (int i = 0; i < len; i++) { 830 int linelen = 0; 831 while (i + linelen < len && text[i + linelen] != newlineCharacter) 832 linelen++; 833 834 if (linelen) { 835 lastLineMaxWidth = widthFromCache(f, i, linelen, leadWidth + lastLineMaxWidth, direction, 0, 0); 836 if (firstLine) { 837 firstLine = false; 838 leadWidth = 0; 839 firstLineMaxWidth = lastLineMaxWidth; 840 } 841 i += linelen; 842 } else if (firstLine) { 843 firstLineMaxWidth = 0; 844 firstLine = false; 845 leadWidth = 0; 846 } 847 848 if (i == len - 1) { 849 // A <pre> run that ends with a newline, as in, e.g., 850 // <pre>Some text\n\n<span>More text</pre> 851 lastLineMaxWidth = 0; 852 } 853 } 854 } 855 } 856 857 float RenderText::minLogicalWidth() const 858 { 859 if (preferredLogicalWidthsDirty()) 860 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0); 861 862 return m_minWidth; 863 } 864 865 float RenderText::maxLogicalWidth() const 866 { 867 if (preferredLogicalWidthsDirty()) 868 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0); 869 870 return m_maxWidth; 871 } 872 873 void RenderText::computePreferredLogicalWidths(float leadWidth) 874 { 875 HashSet<const SimpleFontData*> fallbackFonts; 876 GlyphOverflow glyphOverflow; 877 computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow); 878 879 // We shouldn't change our mind once we "know". 880 ASSERT(!m_knownToHaveNoOverflowAndNoFallbackFonts || (fallbackFonts.isEmpty() && glyphOverflow.isZero())); 881 m_knownToHaveNoOverflowAndNoFallbackFonts = fallbackFonts.isEmpty() && glyphOverflow.isZero(); 882 } 883 884 static inline float hyphenWidth(RenderText* renderer, const Font& font, TextDirection direction) 885 { 886 RenderStyle* style = renderer->style(); 887 return font.width(constructTextRun(renderer, font, style->hyphenString().string(), style, direction)); 888 } 889 890 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow) 891 { 892 ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts); 893 894 m_minWidth = 0; 895 m_maxWidth = 0; 896 m_firstLineMinWidth = 0; 897 m_lastLineLineMinWidth = 0; 898 899 if (isBR()) 900 return; 901 902 float currMinWidth = 0; 903 float currMaxWidth = 0; 904 m_hasBreakableChar = false; 905 m_hasBreak = false; 906 m_hasTab = false; 907 m_hasBreakableStart = false; 908 m_hasBreakableEnd = false; 909 m_hasEndWhiteSpace = false; 910 911 RenderStyle* styleToUse = style(); 912 const Font& f = styleToUse->font(); // FIXME: This ignores first-line. 913 float wordSpacing = styleToUse->wordSpacing(); 914 int len = textLength(); 915 LazyLineBreakIterator breakIterator(m_text, styleToUse->locale()); 916 bool needsWordSpacing = false; 917 bool ignoringSpaces = false; 918 bool isSpace = false; 919 bool firstWord = true; 920 bool firstLine = true; 921 int nextBreakable = -1; 922 int lastWordBoundary = 0; 923 float cachedWordTrailingSpaceWidth[2] = { 0, 0 }; // LTR, RTL 924 925 int firstGlyphLeftOverflow = -1; 926 927 bool breakAll = (styleToUse->wordBreak() == BreakAllWordBreak || styleToUse->wordBreak() == BreakWordBreak) && styleToUse->autoWrap(); 928 929 TextRun textRun(text()); 930 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; 931 BidiCharacterRun* run; 932 TextDirection textDirection = styleToUse->direction(); 933 if (isOverride(styleToUse->unicodeBidi())) { 934 run = 0; 935 } else { 936 BidiStatus status(textDirection, false); 937 bidiResolver.setStatus(status); 938 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); 939 bool hardLineBreak = false; 940 bool reorderRuns = false; 941 bidiResolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length()), NoVisualOverride, hardLineBreak, reorderRuns); 942 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); 943 run = bidiRuns.firstRun(); 944 } 945 946 for (int i = 0; i < len; i++) { 947 UChar c = uncheckedCharacterAt(i); 948 949 if (run) { 950 // Treat adjacent runs with the same resolved directionality 951 // (TextDirection as opposed to WTF::Unicode::Direction) as belonging 952 // to the same run to avoid breaking unnecessarily. 953 while (i >= run->stop() || (run->next() && run->next()->direction() == run->direction())) 954 run = run->next(); 955 956 ASSERT(run); 957 ASSERT(i <= run->stop()); 958 textDirection = run->direction(); 959 } 960 961 bool previousCharacterIsSpace = isSpace; 962 bool isNewline = false; 963 if (c == newlineCharacter) { 964 if (styleToUse->preserveNewline()) { 965 m_hasBreak = true; 966 isNewline = true; 967 isSpace = false; 968 } else 969 isSpace = true; 970 } else if (c == characterTabulation) { 971 if (!styleToUse->collapseWhiteSpace()) { 972 m_hasTab = true; 973 isSpace = false; 974 } else 975 isSpace = true; 976 } else { 977 isSpace = c == space; 978 } 979 980 bool isBreakableLocation = isNewline || (isSpace && styleToUse->autoWrap()); 981 if (!i) 982 m_hasBreakableStart = isBreakableLocation; 983 if (i == len - 1) { 984 m_hasBreakableEnd = isBreakableLocation; 985 m_hasEndWhiteSpace = isNewline || isSpace; 986 } 987 988 if (!ignoringSpaces && styleToUse->collapseWhiteSpace() && previousCharacterIsSpace && isSpace) 989 ignoringSpaces = true; 990 991 if (ignoringSpaces && !isSpace) 992 ignoringSpaces = false; 993 994 // Ignore spaces and soft hyphens 995 if (ignoringSpaces) { 996 ASSERT(lastWordBoundary == i); 997 lastWordBoundary++; 998 continue; 999 } else if (c == softHyphen) { 1000 currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow); 1001 if (firstGlyphLeftOverflow < 0) 1002 firstGlyphLeftOverflow = glyphOverflow.left; 1003 lastWordBoundary = i + 1; 1004 continue; 1005 } 1006 1007 bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable); 1008 bool betweenWords = true; 1009 int j = i; 1010 while (c != newlineCharacter && c != space && c != characterTabulation && (c != softHyphen)) { 1011 j++; 1012 if (j == len) 1013 break; 1014 c = uncheckedCharacterAt(j); 1015 if (isBreakable(breakIterator, j, nextBreakable) && characterAt(j - 1) != softHyphen) 1016 break; 1017 if (breakAll) { 1018 betweenWords = false; 1019 break; 1020 } 1021 } 1022 1023 // Terminate word boundary at bidi run boundary. 1024 if (run) 1025 j = std::min(j, run->stop() + 1); 1026 int wordLen = j - i; 1027 if (wordLen) { 1028 bool isSpace = (j < len) && c == space; 1029 1030 // Non-zero only when kerning is enabled, in which case we measure words with their trailing 1031 // space, then subtract its width. 1032 float wordTrailingSpaceWidth = 0; 1033 if (isSpace && (f.fontDescription().typesettingFeatures() & Kerning)) { 1034 ASSERT(textDirection >=0 && textDirection <= 1); 1035 if (!cachedWordTrailingSpaceWidth[textDirection]) 1036 cachedWordTrailingSpaceWidth[textDirection] = f.width(constructTextRun(this, f, &space, 1, styleToUse, textDirection)) + wordSpacing; 1037 wordTrailingSpaceWidth = cachedWordTrailingSpaceWidth[textDirection]; 1038 } 1039 1040 float w; 1041 if (wordTrailingSpaceWidth && isSpace) 1042 w = widthFromCache(f, i, wordLen + 1, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow) - wordTrailingSpaceWidth; 1043 else { 1044 w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow); 1045 if (c == softHyphen) 1046 currMinWidth += hyphenWidth(this, f, textDirection); 1047 } 1048 1049 if (firstGlyphLeftOverflow < 0) 1050 firstGlyphLeftOverflow = glyphOverflow.left; 1051 currMinWidth += w; 1052 if (betweenWords) { 1053 if (lastWordBoundary == i) 1054 currMaxWidth += w; 1055 else 1056 currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, textDirection, &fallbackFonts, &glyphOverflow); 1057 lastWordBoundary = j; 1058 } 1059 1060 bool isCollapsibleWhiteSpace = (j < len) && styleToUse->isCollapsibleWhiteSpace(c); 1061 if (j < len && styleToUse->autoWrap()) 1062 m_hasBreakableChar = true; 1063 1064 // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the 1065 // last word in the run. 1066 if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j)) 1067 currMaxWidth += wordSpacing; 1068 1069 if (firstWord) { 1070 firstWord = false; 1071 // If the first character in the run is breakable, then we consider ourselves to have a beginning 1072 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever 1073 // being appended to a previous text run when considering the total minimum width of the containing block. 1074 if (hasBreak) 1075 m_hasBreakableChar = true; 1076 m_firstLineMinWidth = hasBreak ? 0 : currMinWidth; 1077 } 1078 m_lastLineLineMinWidth = currMinWidth; 1079 1080 if (currMinWidth > m_minWidth) 1081 m_minWidth = currMinWidth; 1082 currMinWidth = 0; 1083 1084 i += wordLen - 1; 1085 } else { 1086 // Nowrap can never be broken, so don't bother setting the 1087 // breakable character boolean. Pre can only be broken if we encounter a newline. 1088 if (style()->autoWrap() || isNewline) 1089 m_hasBreakableChar = true; 1090 1091 if (currMinWidth > m_minWidth) 1092 m_minWidth = currMinWidth; 1093 currMinWidth = 0; 1094 1095 if (isNewline) { // Only set if preserveNewline was true and we saw a newline. 1096 if (firstLine) { 1097 firstLine = false; 1098 leadWidth = 0; 1099 if (!styleToUse->autoWrap()) 1100 m_firstLineMinWidth = currMaxWidth; 1101 } 1102 1103 if (currMaxWidth > m_maxWidth) 1104 m_maxWidth = currMaxWidth; 1105 currMaxWidth = 0; 1106 } else { 1107 TextRun run = constructTextRun(this, f, this, i, 1, styleToUse, textDirection); 1108 run.setCharactersLength(len - i); 1109 run.setUseComplexCodePath(!canUseSimpleFontCodePath()); 1110 ASSERT(run.charactersLength() >= run.length()); 1111 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 1112 run.setXPos(leadWidth + currMaxWidth); 1113 1114 currMaxWidth += f.width(run); 1115 glyphOverflow.right = 0; 1116 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; 1117 } 1118 ASSERT(lastWordBoundary == i); 1119 lastWordBoundary++; 1120 } 1121 } 1122 if (run) 1123 bidiResolver.runs().deleteRuns(); 1124 1125 if (firstGlyphLeftOverflow > 0) 1126 glyphOverflow.left = firstGlyphLeftOverflow; 1127 1128 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord)) 1129 currMaxWidth += wordSpacing; 1130 1131 m_minWidth = std::max(currMinWidth, m_minWidth); 1132 m_maxWidth = std::max(currMaxWidth, m_maxWidth); 1133 1134 if (!styleToUse->autoWrap()) 1135 m_minWidth = m_maxWidth; 1136 1137 if (styleToUse->whiteSpace() == PRE) { 1138 if (firstLine) 1139 m_firstLineMinWidth = m_maxWidth; 1140 m_lastLineLineMinWidth = currMaxWidth; 1141 } 1142 1143 clearPreferredLogicalWidthsDirty(); 1144 } 1145 1146 bool RenderText::isAllCollapsibleWhitespace() const 1147 { 1148 unsigned length = textLength(); 1149 if (is8Bit()) { 1150 for (unsigned i = 0; i < length; ++i) { 1151 if (!style()->isCollapsibleWhiteSpace(characters8()[i])) 1152 return false; 1153 } 1154 return true; 1155 } 1156 for (unsigned i = 0; i < length; ++i) { 1157 if (!style()->isCollapsibleWhiteSpace(characters16()[i])) 1158 return false; 1159 } 1160 return true; 1161 } 1162 1163 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const 1164 { 1165 ASSERT(m_text); 1166 StringImpl& text = *m_text.impl(); 1167 unsigned currPos; 1168 for (currPos = from; 1169 currPos < from + len && (text[currPos] == newlineCharacter || text[currPos] == space || text[currPos] == characterTabulation); 1170 currPos++) { } 1171 return currPos >= (from + len); 1172 } 1173 1174 FloatPoint RenderText::firstRunOrigin() const 1175 { 1176 return IntPoint(firstRunX(), firstRunY()); 1177 } 1178 1179 float RenderText::firstRunX() const 1180 { 1181 return m_firstTextBox ? m_firstTextBox->x() : 0; 1182 } 1183 1184 float RenderText::firstRunY() const 1185 { 1186 return m_firstTextBox ? m_firstTextBox->y() : 0; 1187 } 1188 1189 void RenderText::setSelectionState(SelectionState state) 1190 { 1191 RenderObject::setSelectionState(state); 1192 1193 if (canUpdateSelectionOnRootLineBoxes()) { 1194 if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { 1195 int startPos, endPos; 1196 selectionStartEnd(startPos, endPos); 1197 if (selectionState() == SelectionStart) { 1198 endPos = textLength(); 1199 1200 // to handle selection from end of text to end of line 1201 if (startPos && startPos == endPos) 1202 startPos = endPos - 1; 1203 } else if (selectionState() == SelectionEnd) 1204 startPos = 0; 1205 1206 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1207 if (box->isSelected(startPos, endPos)) { 1208 box->root().setHasSelectedChildren(true); 1209 } 1210 } 1211 } else { 1212 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1213 box->root().setHasSelectedChildren(state == SelectionInside); 1214 } 1215 } 1216 } 1217 1218 // The containing block can be null in case of an orphaned tree. 1219 RenderBlock* containingBlock = this->containingBlock(); 1220 if (containingBlock && !containingBlock->isRenderView()) 1221 containingBlock->setSelectionState(state); 1222 } 1223 1224 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force) 1225 { 1226 if (!force && equal(m_text.impl(), text.get())) 1227 return; 1228 1229 unsigned oldLen = textLength(); 1230 unsigned newLen = text->length(); 1231 int delta = newLen - oldLen; 1232 unsigned end = len ? offset + len - 1 : offset; 1233 1234 RootInlineBox* firstRootBox = 0; 1235 RootInlineBox* lastRootBox = 0; 1236 1237 bool dirtiedLines = false; 1238 1239 // Dirty all text boxes that include characters in between offset and offset+len. 1240 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1241 // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264 1242 // Text run is entirely before the affected range. 1243 if (curr->end() < offset) 1244 continue; 1245 1246 // Text run is entirely after the affected range. 1247 if (curr->start() > end) { 1248 curr->offsetRun(delta); 1249 RootInlineBox* root = &curr->root(); 1250 if (!firstRootBox) { 1251 firstRootBox = root; 1252 // The affected area was in between two runs. Go ahead and mark the root box of 1253 // the run after the affected area as dirty. 1254 firstRootBox->markDirty(); 1255 dirtiedLines = true; 1256 } 1257 lastRootBox = root; 1258 } else if (curr->end() >= offset && curr->end() <= end) { 1259 // Text run overlaps with the left end of the affected range. 1260 curr->dirtyLineBoxes(); 1261 dirtiedLines = true; 1262 } else if (curr->start() <= offset && curr->end() >= end) { 1263 // Text run subsumes the affected range. 1264 curr->dirtyLineBoxes(); 1265 dirtiedLines = true; 1266 } else if (curr->start() <= end && curr->end() >= end) { 1267 // Text run overlaps with right end of the affected range. 1268 curr->dirtyLineBoxes(); 1269 dirtiedLines = true; 1270 } 1271 } 1272 1273 // Now we have to walk all of the clean lines and adjust their cached line break information 1274 // to reflect our updated offsets. 1275 if (lastRootBox) 1276 lastRootBox = lastRootBox->nextRootBox(); 1277 if (firstRootBox) { 1278 RootInlineBox* prev = firstRootBox->prevRootBox(); 1279 if (prev) 1280 firstRootBox = prev; 1281 } else if (lastTextBox()) { 1282 ASSERT(!lastRootBox); 1283 firstRootBox = &lastTextBox()->root(); 1284 firstRootBox->markDirty(); 1285 dirtiedLines = true; 1286 } 1287 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) { 1288 if (curr->lineBreakObj() == this && curr->lineBreakPos() > end) 1289 curr->setLineBreakPos(clampToInteger(curr->lineBreakPos() + delta)); 1290 } 1291 1292 // If the text node is empty, dirty the line where new text will be inserted. 1293 if (!firstTextBox() && parent()) { 1294 parent()->dirtyLinesFromChangedChild(this); 1295 dirtiedLines = true; 1296 } 1297 1298 m_linesDirty = dirtiedLines; 1299 setText(text, force || dirtiedLines); 1300 } 1301 1302 void RenderText::transformText() 1303 { 1304 if (RefPtr<StringImpl> textToTransform = originalText()) 1305 setText(textToTransform.release(), true); 1306 } 1307 1308 static inline bool isInlineFlowOrEmptyText(const RenderObject* o) 1309 { 1310 if (o->isRenderInline()) 1311 return true; 1312 if (!o->isText()) 1313 return false; 1314 return toRenderText(o)->text().isEmpty(); 1315 } 1316 1317 UChar RenderText::previousCharacter() const 1318 { 1319 // find previous text renderer if one exists 1320 const RenderObject* previousText = previousInPreOrder(); 1321 for (; previousText; previousText = previousText->previousInPreOrder()) 1322 if (!isInlineFlowOrEmptyText(previousText)) 1323 break; 1324 UChar prev = space; 1325 if (previousText && previousText->isText()) 1326 if (StringImpl* previousString = toRenderText(previousText)->text().impl()) 1327 prev = (*previousString)[previousString->length() - 1]; 1328 return prev; 1329 } 1330 1331 void RenderText::addLayerHitTestRects(LayerHitTestRects&, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const 1332 { 1333 // Text nodes aren't event targets, so don't descend any further. 1334 } 1335 1336 void applyTextTransform(const RenderStyle* style, String& text, UChar previousCharacter) 1337 { 1338 if (!style) 1339 return; 1340 1341 switch (style->textTransform()) { 1342 case TTNONE: 1343 break; 1344 case CAPITALIZE: 1345 makeCapitalized(&text, previousCharacter); 1346 break; 1347 case UPPERCASE: 1348 text = text.upper(style->locale()); 1349 break; 1350 case LOWERCASE: 1351 text = text.lower(style->locale()); 1352 break; 1353 } 1354 } 1355 1356 void RenderText::setTextInternal(PassRefPtr<StringImpl> text) 1357 { 1358 ASSERT(text); 1359 m_text = text; 1360 1361 if (style()) { 1362 applyTextTransform(style(), m_text, previousCharacter()); 1363 1364 // We use the same characters here as for list markers. 1365 // See the listMarkerText function in RenderListMarker.cpp. 1366 switch (style()->textSecurity()) { 1367 case TSNONE: 1368 break; 1369 case TSCIRCLE: 1370 secureText(whiteBullet); 1371 break; 1372 case TSDISC: 1373 secureText(bullet); 1374 break; 1375 case TSSQUARE: 1376 secureText(blackSquare); 1377 } 1378 } 1379 1380 ASSERT(m_text); 1381 ASSERT(!isBR() || (textLength() == 1 && m_text[0] == newlineCharacter)); 1382 1383 m_isAllASCII = m_text.containsOnlyASCII(); 1384 m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath(); 1385 } 1386 1387 void RenderText::secureText(UChar mask) 1388 { 1389 if (!m_text.length()) 1390 return; 1391 1392 int lastTypedCharacterOffsetToReveal = -1; 1393 UChar revealedText; 1394 SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0; 1395 if (secureTextTimer && secureTextTimer->isActive()) { 1396 lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset(); 1397 if (lastTypedCharacterOffsetToReveal >= 0) 1398 revealedText = m_text[lastTypedCharacterOffsetToReveal]; 1399 } 1400 1401 m_text.fill(mask); 1402 if (lastTypedCharacterOffsetToReveal >= 0) { 1403 m_text.replace(lastTypedCharacterOffsetToReveal, 1, String(&revealedText, 1)); 1404 // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency. 1405 secureTextTimer->invalidate(); 1406 } 1407 } 1408 1409 void RenderText::setText(PassRefPtr<StringImpl> text, bool force) 1410 { 1411 ASSERT(text); 1412 1413 if (!force && equal(m_text.impl(), text.get())) 1414 return; 1415 1416 setTextInternal(text); 1417 // If preferredLogicalWidthsDirty() of an orphan child is true, RenderObjectChildList:: 1418 // insertChildNode() fails to set true to owner. To avoid that, we call 1419 // setNeedsLayoutAndPrefWidthsRecalc() only if this RenderText has parent. 1420 if (parent()) 1421 setNeedsLayoutAndPrefWidthsRecalc(); 1422 m_knownToHaveNoOverflowAndNoFallbackFonts = false; 1423 1424 if (AXObjectCache* cache = document().existingAXObjectCache()) 1425 cache->textChanged(this); 1426 } 1427 1428 void RenderText::dirtyLineBoxes(bool fullLayout) 1429 { 1430 if (fullLayout) 1431 deleteTextBoxes(); 1432 else if (!m_linesDirty) { 1433 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 1434 box->dirtyLineBoxes(); 1435 } 1436 m_linesDirty = false; 1437 } 1438 1439 InlineTextBox* RenderText::createTextBox() 1440 { 1441 return new InlineTextBox(*this); 1442 } 1443 1444 InlineTextBox* RenderText::createInlineTextBox() 1445 { 1446 InlineTextBox* textBox = createTextBox(); 1447 if (!m_firstTextBox) 1448 m_firstTextBox = m_lastTextBox = textBox; 1449 else { 1450 m_lastTextBox->setNextTextBox(textBox); 1451 textBox->setPreviousTextBox(m_lastTextBox); 1452 m_lastTextBox = textBox; 1453 } 1454 textBox->setIsText(true); 1455 return textBox; 1456 } 1457 1458 void RenderText::positionLineBox(InlineBox* box) 1459 { 1460 InlineTextBox* s = toInlineTextBox(box); 1461 1462 // FIXME: should not be needed!!! 1463 if (!s->len()) { 1464 // We want the box to be destroyed. 1465 s->remove(DontMarkLineBoxes); 1466 if (m_firstTextBox == s) 1467 m_firstTextBox = s->nextTextBox(); 1468 else 1469 s->prevTextBox()->setNextTextBox(s->nextTextBox()); 1470 if (m_lastTextBox == s) 1471 m_lastTextBox = s->prevTextBox(); 1472 else 1473 s->nextTextBox()->setPreviousTextBox(s->prevTextBox()); 1474 s->destroy(); 1475 return; 1476 } 1477 1478 m_containsReversedText |= !s->isLeftToRightDirection(); 1479 } 1480 1481 float RenderText::width(unsigned from, unsigned len, float xPos, TextDirection textDirection, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 1482 { 1483 if (from >= textLength()) 1484 return 0; 1485 1486 if (from + len > textLength()) 1487 len = textLength() - from; 1488 1489 return width(from, len, style(firstLine)->font(), xPos, textDirection, fallbackFonts, glyphOverflow); 1490 } 1491 1492 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, TextDirection textDirection, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const 1493 { 1494 ASSERT(from + len <= textLength()); 1495 if (!textLength()) 1496 return 0; 1497 1498 float w; 1499 if (&f == &style()->font()) { 1500 if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) { 1501 if (fallbackFonts) { 1502 ASSERT(glyphOverflow); 1503 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) { 1504 const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow); 1505 // We shouldn't change our mind once we "know". 1506 ASSERT(!m_knownToHaveNoOverflowAndNoFallbackFonts 1507 || (fallbackFonts->isEmpty() && glyphOverflow->isZero())); 1508 m_knownToHaveNoOverflowAndNoFallbackFonts = fallbackFonts->isEmpty() && glyphOverflow->isZero(); 1509 } 1510 w = m_maxWidth; 1511 } else { 1512 w = maxLogicalWidth(); 1513 } 1514 } else { 1515 w = widthFromCache(f, from, len, xPos, textDirection, fallbackFonts, glyphOverflow); 1516 } 1517 } else { 1518 TextRun run = constructTextRun(const_cast<RenderText*>(this), f, this, from, len, style(), textDirection); 1519 run.setCharactersLength(textLength() - from); 1520 ASSERT(run.charactersLength() >= run.length()); 1521 1522 run.setCharacterScanForCodePath(!canUseSimpleFontCodePath()); 1523 run.setTabSize(!style()->collapseWhiteSpace(), style()->tabSize()); 1524 run.setXPos(xPos); 1525 w = f.width(run, fallbackFonts, glyphOverflow); 1526 } 1527 1528 return w; 1529 } 1530 1531 IntRect RenderText::linesBoundingBox() const 1532 { 1533 IntRect result; 1534 1535 ASSERT(!firstTextBox() == !lastTextBox()); // Either both are null or both exist. 1536 if (firstTextBox() && lastTextBox()) { 1537 // Return the width of the minimal left side and the maximal right side. 1538 float logicalLeftSide = 0; 1539 float logicalRightSide = 0; 1540 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1541 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide) 1542 logicalLeftSide = curr->logicalLeft(); 1543 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide) 1544 logicalRightSide = curr->logicalRight(); 1545 } 1546 1547 bool isHorizontal = style()->isHorizontalWritingMode(); 1548 1549 float x = isHorizontal ? logicalLeftSide : firstTextBox()->x(); 1550 float y = isHorizontal ? firstTextBox()->y() : logicalLeftSide; 1551 float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x; 1552 float height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide; 1553 result = enclosingIntRect(FloatRect(x, y, width, height)); 1554 } 1555 1556 return result; 1557 } 1558 1559 LayoutRect RenderText::linesVisualOverflowBoundingBox() const 1560 { 1561 if (!firstTextBox()) 1562 return LayoutRect(); 1563 1564 // Return the width of the minimal left side and the maximal right side. 1565 LayoutUnit logicalLeftSide = LayoutUnit::max(); 1566 LayoutUnit logicalRightSide = LayoutUnit::min(); 1567 for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { 1568 LayoutRect logicalVisualOverflow = curr->logicalOverflowRect(); 1569 logicalLeftSide = std::min(logicalLeftSide, logicalVisualOverflow.x()); 1570 logicalRightSide = std::max(logicalRightSide, logicalVisualOverflow.maxX()); 1571 } 1572 1573 LayoutUnit logicalTop = firstTextBox()->logicalTopVisualOverflow(); 1574 LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide; 1575 LayoutUnit logicalHeight = lastTextBox()->logicalBottomVisualOverflow() - logicalTop; 1576 1577 LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight); 1578 if (!style()->isHorizontalWritingMode()) 1579 rect = rect.transposedRect(); 1580 return rect; 1581 } 1582 1583 LayoutRect RenderText::clippedOverflowRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const 1584 { 1585 // This method doesn't support paintInvalidationState, but invalidateTreeIfNeeded() never reaches RenderText. 1586 ASSERT(!paintInvalidationState); 1587 return parent()->clippedOverflowRectForPaintInvalidation(paintInvalidationContainer); 1588 } 1589 1590 LayoutRect RenderText::selectionRectForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer) const 1591 { 1592 ASSERT(!needsLayout()); 1593 1594 if (selectionState() == SelectionNone) 1595 return LayoutRect(); 1596 RenderBlock* cb = containingBlock(); 1597 if (!cb) 1598 return LayoutRect(); 1599 1600 // Now calculate startPos and endPos for painting selection. 1601 // We include a selection while endPos > 0 1602 int startPos, endPos; 1603 if (selectionState() == SelectionInside) { 1604 // We are fully selected. 1605 startPos = 0; 1606 endPos = textLength(); 1607 } else { 1608 selectionStartEnd(startPos, endPos); 1609 if (selectionState() == SelectionStart) 1610 endPos = textLength(); 1611 else if (selectionState() == SelectionEnd) 1612 startPos = 0; 1613 } 1614 1615 if (startPos == endPos) 1616 return IntRect(); 1617 1618 LayoutRect rect; 1619 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { 1620 rect.unite(box->localSelectionRect(startPos, endPos)); 1621 rect.unite(ellipsisRectForBox(box, startPos, endPos)); 1622 } 1623 1624 mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, 0); 1625 return rect; 1626 } 1627 1628 int RenderText::caretMinOffset() const 1629 { 1630 InlineTextBox* box = firstTextBox(); 1631 if (!box) 1632 return 0; 1633 int minOffset = box->start(); 1634 for (box = box->nextTextBox(); box; box = box->nextTextBox()) 1635 minOffset = std::min<int>(minOffset, box->start()); 1636 return minOffset; 1637 } 1638 1639 int RenderText::caretMaxOffset() const 1640 { 1641 InlineTextBox* box = lastTextBox(); 1642 if (!lastTextBox()) 1643 return textLength(); 1644 1645 int maxOffset = box->start() + box->len(); 1646 for (box = box->prevTextBox(); box; box = box->prevTextBox()) 1647 maxOffset = std::max<int>(maxOffset, box->start() + box->len()); 1648 return maxOffset; 1649 } 1650 1651 unsigned RenderText::renderedTextLength() const 1652 { 1653 int l = 0; 1654 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) 1655 l += box->len(); 1656 return l; 1657 } 1658 1659 int RenderText::previousOffset(int current) const 1660 { 1661 if (isAllASCII() || m_text.is8Bit()) 1662 return current - 1; 1663 1664 StringImpl* textImpl = m_text.impl(); 1665 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); 1666 if (!iterator) 1667 return current - 1; 1668 1669 long result = iterator->preceding(current); 1670 if (result == TextBreakDone) 1671 result = current - 1; 1672 1673 1674 return result; 1675 } 1676 1677 #if OS(POSIX) 1678 1679 #define HANGUL_CHOSEONG_START (0x1100) 1680 #define HANGUL_CHOSEONG_END (0x115F) 1681 #define HANGUL_JUNGSEONG_START (0x1160) 1682 #define HANGUL_JUNGSEONG_END (0x11A2) 1683 #define HANGUL_JONGSEONG_START (0x11A8) 1684 #define HANGUL_JONGSEONG_END (0x11F9) 1685 #define HANGUL_SYLLABLE_START (0xAC00) 1686 #define HANGUL_SYLLABLE_END (0xD7AF) 1687 #define HANGUL_JONGSEONG_COUNT (28) 1688 1689 enum HangulState { 1690 HangulStateL, 1691 HangulStateV, 1692 HangulStateT, 1693 HangulStateLV, 1694 HangulStateLVT, 1695 HangulStateBreak 1696 }; 1697 1698 inline bool isHangulLVT(UChar32 character) 1699 { 1700 return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT; 1701 } 1702 1703 inline bool isMark(UChar32 c) 1704 { 1705 int8_t charType = u_charType(c); 1706 return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK; 1707 } 1708 1709 inline bool isRegionalIndicator(UChar32 c) 1710 { 1711 // National flag emoji each consists of a pair of regional indicator symbols. 1712 return 0x1F1E6 <= c && c <= 0x1F1FF; 1713 } 1714 1715 #endif 1716 1717 int RenderText::previousOffsetForBackwardDeletion(int current) const 1718 { 1719 #if OS(POSIX) 1720 ASSERT(m_text); 1721 StringImpl& text = *m_text.impl(); 1722 UChar32 character; 1723 bool sawRegionalIndicator = false; 1724 while (current > 0) { 1725 if (U16_IS_TRAIL(text[--current])) 1726 --current; 1727 if (current < 0) 1728 break; 1729 1730 UChar32 character = text.characterStartingAt(current); 1731 1732 if (sawRegionalIndicator) { 1733 // We don't check if the pair of regional indicator symbols before current position can actually be combined 1734 // into a flag, and just delete it. This may not agree with how the pair is rendered in edge cases, 1735 // but is good enough in practice. 1736 if (isRegionalIndicator(character)) 1737 break; 1738 // Don't delete a preceding character that isn't a regional indicator symbol. 1739 U16_FWD_1_UNSAFE(text, current); 1740 } 1741 1742 // We don't combine characters in Armenian ... Limbu range for backward deletion. 1743 if ((character >= 0x0530) && (character < 0x1950)) 1744 break; 1745 1746 if (isRegionalIndicator(character)) { 1747 sawRegionalIndicator = true; 1748 continue; 1749 } 1750 1751 if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F)) 1752 break; 1753 } 1754 1755 if (current <= 0) 1756 return current; 1757 1758 // Hangul 1759 character = text.characterStartingAt(current); 1760 if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) { 1761 HangulState state; 1762 1763 if (character < HANGUL_JUNGSEONG_START) 1764 state = HangulStateL; 1765 else if (character < HANGUL_JONGSEONG_START) 1766 state = HangulStateV; 1767 else if (character < HANGUL_SYLLABLE_START) 1768 state = HangulStateT; 1769 else 1770 state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV; 1771 1772 while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) { 1773 switch (state) { 1774 case HangulStateV: 1775 if (character <= HANGUL_CHOSEONG_END) 1776 state = HangulStateL; 1777 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character)) 1778 state = HangulStateLV; 1779 else if (character > HANGUL_JUNGSEONG_END) 1780 state = HangulStateBreak; 1781 break; 1782 case HangulStateT: 1783 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END)) 1784 state = HangulStateV; 1785 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END)) 1786 state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV); 1787 else if (character < HANGUL_JUNGSEONG_START) 1788 state = HangulStateBreak; 1789 break; 1790 default: 1791 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak; 1792 break; 1793 } 1794 if (state == HangulStateBreak) 1795 break; 1796 1797 --current; 1798 } 1799 } 1800 1801 return current; 1802 #else 1803 // Platforms other than Unix-like delete by one code point. 1804 if (U16_IS_TRAIL(m_text[--current])) 1805 --current; 1806 if (current < 0) 1807 current = 0; 1808 return current; 1809 #endif 1810 } 1811 1812 int RenderText::nextOffset(int current) const 1813 { 1814 if (isAllASCII() || m_text.is8Bit()) 1815 return current + 1; 1816 1817 StringImpl* textImpl = m_text.impl(); 1818 TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length()); 1819 if (!iterator) 1820 return current + 1; 1821 1822 long result = iterator->following(current); 1823 if (result == TextBreakDone) 1824 result = current + 1; 1825 1826 return result; 1827 } 1828 1829 bool RenderText::computeCanUseSimpleFontCodePath() const 1830 { 1831 if (isAllASCII() || m_text.is8Bit()) 1832 return true; 1833 return Character::characterRangeCodePath(characters16(), length()) == SimplePath; 1834 } 1835 1836 #if ENABLE(ASSERT) 1837 1838 void RenderText::checkConsistency() const 1839 { 1840 #ifdef CHECK_CONSISTENCY 1841 const InlineTextBox* prev = 0; 1842 for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { 1843 ASSERT(child->renderer() == this); 1844 ASSERT(child->prevTextBox() == prev); 1845 prev = child; 1846 } 1847 ASSERT(prev == m_lastTextBox); 1848 #endif 1849 } 1850 1851 #endif 1852 1853 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset) 1854 { 1855 if (!gSecureTextTimers) 1856 gSecureTextTimers = new SecureTextTimerMap; 1857 1858 SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this); 1859 if (!secureTextTimer) { 1860 secureTextTimer = new SecureTextTimer(this); 1861 gSecureTextTimers->add(this, secureTextTimer); 1862 } 1863 secureTextTimer->restartWithNewText(lastTypedCharacterOffset); 1864 } 1865 1866 PassRefPtr<AbstractInlineTextBox> RenderText::firstAbstractInlineTextBox() 1867 { 1868 return AbstractInlineTextBox::getOrCreate(this, m_firstTextBox); 1869 } 1870 1871 } // namespace blink 1872