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