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