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