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