Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: Source/core/rendering/RenderText.cpp

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

Powered by Google App Engine
This is Rietveld 408576698