InlineTextBox's m_len can be an unsigned (rather than an unsigned short)
[WebKit-https.git] / Source / WebCore / rendering / RenderTextLineBoxes.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "RenderTextLineBoxes.h"
28
29 #include "EllipsisBox.h"
30 #include "InlineTextBox.h"
31 #include "RenderBlock.h"
32 #include "RenderStyle.h"
33 #include "RootInlineBox.h"
34
35 namespace WebCore {
36
37 RenderTextLineBoxes::RenderTextLineBoxes()
38     : m_first(nullptr)
39     , m_last(nullptr)
40 {
41 }
42
43 InlineTextBox* RenderTextLineBoxes::createAndAppendLineBox(RenderText& renderText)
44 {
45     auto textBox = renderText.createTextBox();
46     if (!m_first) {
47         m_first = textBox.get();
48         m_last = textBox.get();
49     } else {
50         m_last->setNextTextBox(textBox.get());
51         textBox->setPreviousTextBox(m_last);
52         m_last = textBox.get();
53     }
54     return textBox.release();
55 }
56
57 void RenderTextLineBoxes::extract(InlineTextBox& box)
58 {
59     checkConsistency();
60
61     m_last = box.prevTextBox();
62     if (&box == m_first)
63         m_first = nullptr;
64     if (box.prevTextBox())
65         box.prevTextBox()->setNextTextBox(nullptr);
66     box.setPreviousTextBox(nullptr);
67     for (auto current = &box; current; current = current->nextTextBox())
68         current->setExtracted();
69
70     checkConsistency();
71 }
72
73 void RenderTextLineBoxes::attach(InlineTextBox& box)
74 {
75     checkConsistency();
76
77     if (m_last) {
78         m_last->setNextTextBox(&box);
79         box.setPreviousTextBox(m_last);
80     } else
81         m_first = &box;
82     InlineTextBox* last = nullptr;
83     for (auto current = &box; current; current = current->nextTextBox()) {
84         current->setExtracted(false);
85         last = current;
86     }
87     m_last = last;
88
89     checkConsistency();
90 }
91
92 void RenderTextLineBoxes::remove(InlineTextBox& box)
93 {
94     checkConsistency();
95
96     if (&box == m_first)
97         m_first = box.nextTextBox();
98     if (&box == m_last)
99         m_last = box.prevTextBox();
100     if (box.nextTextBox())
101         box.nextTextBox()->setPreviousTextBox(box.prevTextBox());
102     if (box.prevTextBox())
103         box.prevTextBox()->setNextTextBox(box.nextTextBox());
104
105     checkConsistency();
106 }
107
108 void RenderTextLineBoxes::removeAllFromParent(RenderText& renderer)
109 {
110     if (!m_first) {
111         if (renderer.parent())
112             renderer.parent()->dirtyLinesFromChangedChild(&renderer);
113         return;
114     }
115     for (auto box = m_first; box; box = box->nextTextBox())
116         box->removeFromParent();
117 }
118
119 void RenderTextLineBoxes::deleteAll()
120 {
121     if (!m_first)
122         return;
123     InlineTextBox* next;
124     for (auto current = m_first; current; current = next) {
125         next = current->nextTextBox();
126         delete current;
127     }
128     m_first = nullptr;
129     m_last = nullptr;
130 }
131
132 InlineTextBox* RenderTextLineBoxes::findNext(int offset, int& position) const
133 {
134     if (!m_first)
135         return nullptr;
136     // FIXME: This looks buggy. The function is only used for debugging purposes.
137     auto current = m_first;
138     int currentOffset = current->len();
139     while (offset > currentOffset && current->nextTextBox()) {
140         current = current->nextTextBox();
141         currentOffset = current->start() + current->len();
142     }
143     // we are now in the correct text run
144     position = (offset > currentOffset ? current->len() : current->len() - (currentOffset - offset));
145     return current;
146 }
147
148 IntRect RenderTextLineBoxes::boundingBox(const RenderText& renderer) const
149 {
150     if (!m_first)
151         return IntRect();
152
153     // Return the width of the minimal left side and the maximal right side.
154     float logicalLeftSide = 0;
155     float logicalRightSide = 0;
156     for (auto current = m_first; current; current = current->nextTextBox()) {
157         if (current == m_first || current->logicalLeft() < logicalLeftSide)
158             logicalLeftSide = current->logicalLeft();
159         if (current == m_first || current->logicalRight() > logicalRightSide)
160             logicalRightSide = current->logicalRight();
161     }
162     
163     bool isHorizontal = renderer.style().isHorizontalWritingMode();
164     
165     float x = isHorizontal ? logicalLeftSide : m_first->x();
166     float y = isHorizontal ? m_first->y() : logicalLeftSide;
167     float width = isHorizontal ? logicalRightSide - logicalLeftSide : m_last->logicalBottom() - x;
168     float height = isHorizontal ? m_last->logicalBottom() - y : logicalRightSide - logicalLeftSide;
169     return enclosingIntRect(FloatRect(x, y, width, height));
170 }
171
172 IntPoint RenderTextLineBoxes::firstRunLocation() const
173 {
174     if (!m_first)
175         return IntPoint();
176     return IntPoint(m_first->topLeft());
177 }
178
179 LayoutRect RenderTextLineBoxes::visualOverflowBoundingBox(const RenderText& renderer) const
180 {
181     if (!m_first)
182         return LayoutRect();
183
184     // Return the width of the minimal left side and the maximal right side.
185     auto logicalLeftSide = LayoutUnit::max();
186     auto logicalRightSide = LayoutUnit::min();
187     for (auto current = m_first; current; current = current->nextTextBox()) {
188         logicalLeftSide = std::min(logicalLeftSide, current->logicalLeftVisualOverflow());
189         logicalRightSide = std::max(logicalRightSide, current->logicalRightVisualOverflow());
190     }
191     
192     auto logicalTop = m_first->logicalTopVisualOverflow();
193     auto logicalWidth = logicalRightSide - logicalLeftSide;
194     auto logicalHeight = m_last->logicalBottomVisualOverflow() - logicalTop;
195     
196     LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
197     if (!renderer.style().isHorizontalWritingMode())
198         rect = rect.transposedRect();
199     return rect;
200 }
201
202 bool RenderTextLineBoxes::hasRenderedText() const
203 {
204     for (auto box = m_first; box; box = box->nextTextBox()) {
205         if (box->len())
206             return true;
207     }
208     return false;
209 }
210
211 int RenderTextLineBoxes::caretMinOffset() const
212 {
213     auto box = m_first;
214     if (!box)
215         return 0;
216     int minOffset = box->start();
217     for (box = box->nextTextBox(); box; box = box->nextTextBox())
218         minOffset = std::min<int>(minOffset, box->start());
219     return minOffset;
220 }
221
222 int RenderTextLineBoxes::caretMaxOffset(const RenderText& renderer) const
223 {
224     auto box = m_last;
225     if (!box)
226         return renderer.textLength();
227
228     int maxOffset = box->start() + box->len();
229     for (box = box->prevTextBox(); box; box = box->prevTextBox())
230         maxOffset = std::max<int>(maxOffset, box->start() + box->len());
231     return maxOffset;
232 }
233
234 bool RenderTextLineBoxes::containsOffset(const RenderText& renderer, unsigned offset, OffsetType type) const
235 {
236     for (auto box = m_first; box; box = box->nextTextBox()) {
237         if (offset < box->start() && !renderer.containsReversedText())
238             return false;
239         unsigned boxEnd = box->start() + box->len();
240         if (offset >= box->start() && offset <= boxEnd) {
241             if (offset == boxEnd && (type == CharacterOffset || box->isLineBreak()))
242                 continue;
243             if (type == CharacterOffset)
244                 return true;
245             // Return false for offsets inside composed characters.
246             return !offset || offset == static_cast<unsigned>(renderer.nextOffset(renderer.previousOffset(offset)));
247         }
248     }
249     return false;
250 }
251
252 unsigned RenderTextLineBoxes::countCharacterOffsetsUntil(unsigned offset) const
253 {
254     unsigned result = 0;
255     for (auto box = m_first; box; box = box->nextTextBox()) {
256         if (offset < box->start())
257             return result;
258         if (offset <= box->start() + box->len()) {
259             result += offset - box->start();
260             return result;
261         }
262         result += box->len();
263     }
264     return result;
265 }
266
267 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
268
269 static bool lineDirectionPointFitsInBox(int pointLineDirection, const InlineTextBox& box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
270 {
271     shouldAffinityBeDownstream = AlwaysDownstream;
272
273     // the x coordinate is equal to the left edge of this box
274     // the affinity must be downstream so the position doesn't jump back to the previous line
275     // except when box is the first box in the line
276     if (pointLineDirection <= box.logicalLeft()) {
277         shouldAffinityBeDownstream = !box.prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
278         return true;
279     }
280
281 #if !PLATFORM(IOS)
282     // and the x coordinate is to the left of the right edge of this box
283     // check to see if position goes in this box
284     if (pointLineDirection < box.logicalRight()) {
285         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
286         return true;
287     }
288 #endif
289
290     // box is first on line
291     // and the x coordinate is to the left of the first text box left edge
292     if (!box.prevLeafChildIgnoringLineBreak() && pointLineDirection < box.logicalLeft())
293         return true;
294
295     if (!box.nextLeafChildIgnoringLineBreak()) {
296         // box is last on line
297         // and the x coordinate is to the right of the last text box right edge
298         // generate VisiblePosition, use UPSTREAM affinity if possible
299         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
300         return true;
301     }
302
303     return false;
304 }
305
306 static VisiblePosition createVisiblePositionForBox(const InlineBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
307 {
308     EAffinity affinity = VP_DEFAULT_AFFINITY;
309     switch (shouldAffinityBeDownstream) {
310     case AlwaysDownstream:
311         affinity = DOWNSTREAM;
312         break;
313     case AlwaysUpstream:
314         affinity = VP_UPSTREAM_IF_POSSIBLE;
315         break;
316     case UpstreamIfPositionIsNotAtStart:
317         affinity = offset > box.caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
318         break;
319     }
320     return box.renderer().createVisiblePosition(offset, affinity);
321 }
322
323 static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
324 {
325     ASSERT(offset >= 0);
326
327     if (offset && static_cast<unsigned>(offset) < box.len())
328         return createVisiblePositionForBox(box, box.start() + offset, shouldAffinityBeDownstream);
329
330     bool positionIsAtStartOfBox = !offset;
331     if (positionIsAtStartOfBox == box.isLeftToRightDirection()) {
332         // offset is on the left edge
333
334         const InlineBox* prevBox = box.prevLeafChildIgnoringLineBreak();
335         if ((prevBox && prevBox->bidiLevel() == box.bidiLevel())
336             || box.renderer().containingBlock()->style().direction() == box.direction()) // FIXME: left on 12CBA
337             return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
338
339         if (prevBox && prevBox->bidiLevel() > box.bidiLevel()) {
340             // e.g. left of B in aDC12BAb
341             const InlineBox* leftmostBox;
342             do {
343                 leftmostBox = prevBox;
344                 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
345             } while (prevBox && prevBox->bidiLevel() > box.bidiLevel());
346             return createVisiblePositionForBox(*leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
347         }
348
349         if (!prevBox || prevBox->bidiLevel() < box.bidiLevel()) {
350             // e.g. left of D in aDC12BAb
351             const InlineBox* rightmostBox;
352             const InlineBox* nextBox = &box;
353             do {
354                 rightmostBox = nextBox;
355                 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
356             } while (nextBox && nextBox->bidiLevel() >= box.bidiLevel());
357             return createVisiblePositionForBox(*rightmostBox,
358                 box.isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
359         }
360
361         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
362     }
363
364     const InlineBox* nextBox = box.nextLeafChildIgnoringLineBreak();
365     if ((nextBox && nextBox->bidiLevel() == box.bidiLevel())
366         || box.renderer().containingBlock()->style().direction() == box.direction())
367         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
368
369     // offset is on the right edge
370     if (nextBox && nextBox->bidiLevel() > box.bidiLevel()) {
371         // e.g. right of C in aDC12BAb
372         const InlineBox* rightmostBox;
373         do {
374             rightmostBox = nextBox;
375             nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
376         } while (nextBox && nextBox->bidiLevel() > box.bidiLevel());
377         return createVisiblePositionForBox(*rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
378     }
379
380     if (!nextBox || nextBox->bidiLevel() < box.bidiLevel()) {
381         // e.g. right of A in aDC12BAb
382         const InlineBox* leftmostBox;
383         const InlineBox* prevBox = &box;
384         do {
385             leftmostBox = prevBox;
386             prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
387         } while (prevBox && prevBox->bidiLevel() >= box.bidiLevel());
388         return createVisiblePositionForBox(*leftmostBox,
389             box.isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
390     }
391
392     return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
393 }
394
395 VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
396 {
397     if (!m_first || !renderer.textLength())
398         return renderer.createVisiblePosition(0, DOWNSTREAM);
399
400     LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
401     LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
402     bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode();
403
404     InlineTextBox* lastBox = nullptr;
405     for (auto box = m_first; box; box = box->nextTextBox()) {
406         if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
407             box = box->nextTextBox();
408
409         auto& rootBox = box->root();
410         LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
411         if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
412             LayoutUnit bottom = rootBox.selectionBottom();
413             if (rootBox.nextRootBox())
414                 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
415
416             if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
417                 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
418 #if PLATFORM(IOS)
419                 if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) {
420                     int half = box->x() + box->logicalWidth() / 2;
421                     EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE;
422                     return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity);
423                 }
424 #endif
425                 if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
426                     return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
427             }
428         }
429         lastBox = box;
430     }
431
432     if (lastBox) {
433         ShouldAffinityBeDownstream shouldAffinityBeDownstream;
434         lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
435         return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
436     }
437     return renderer.createVisiblePosition(0, DOWNSTREAM);
438 }
439
440 void RenderTextLineBoxes::setSelectionState(RenderText& renderer, RenderObject::SelectionState state)
441 {
442     if (state == RenderObject::SelectionInside || state == RenderObject::SelectionNone) {
443         for (auto box = m_first; box; box = box->nextTextBox())
444             box->root().setHasSelectedChildren(state == RenderObject::SelectionInside);
445         return;
446     }
447
448     int start, end;
449     renderer.selectionStartEnd(start, end);
450     if (state == RenderObject::SelectionStart) {
451         end = renderer.textLength();
452
453         // to handle selection from end of text to end of line
454         if (start && start == end)
455             start = end - 1;
456     } else if (state == RenderObject::SelectionEnd)
457         start = 0;
458
459     for (auto box = m_first; box; box = box->nextTextBox()) {
460         if (box->isSelected(start, end))
461             box->root().setHasSelectedChildren(true);
462     }
463 }
464
465 static IntRect ellipsisRectForBox(const InlineTextBox& box, unsigned start, unsigned end)
466 {
467     unsigned truncation = box.truncation();
468     if (truncation == cNoTruncation)
469         return IntRect();
470
471     auto ellipsis = box.root().ellipsisBox();
472     if (!ellipsis)
473         return IntRect();
474     
475     IntRect rect;
476     unsigned ellipsisStartPosition = start > box.start() ? start - box.start() : 0;
477     ASSERT(end >= box.start());
478     unsigned ellipsisEndPosition = std::min(end - box.start(), box.len());
479     
480     // The ellipsis should be considered to be selected if the end of
481     // the selection is past the beginning of the truncation and the
482     // beginning of the selection is before or at the beginning of the truncation.
483     if (ellipsisEndPosition < truncation && ellipsisStartPosition > truncation)
484         return IntRect();
485     return ellipsis->selectionRect();
486 }
487
488 LayoutRect RenderTextLineBoxes::selectionRectForRange(unsigned start, unsigned end)
489 {
490     LayoutRect rect;
491     for (auto box = m_first; box; box = box->nextTextBox()) {
492         rect.unite(box->localSelectionRect(start, end));
493         rect.unite(ellipsisRectForBox(*box, start, end));
494     }
495     return rect;
496 }
497
498 Vector<IntRect> RenderTextLineBoxes::absoluteRects(const LayoutPoint& accumulatedOffset) const
499 {
500     Vector<IntRect> rects;
501     for (auto box = m_first; box; box = box->nextTextBox())
502         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + box->topLeft(), box->size())));
503     return rects;
504 }
505
506 static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight)
507 {
508     unsigned realEnd = std::min(box.end() + 1, end);
509     LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd);
510     if (!boxSelectionRect.height())
511         return FloatRect();
512     if (useSelectionHeight)
513         return boxSelectionRect;
514     // Change the height and y position (or width and x for vertical text)
515     // because selectionRect uses selection-specific values.
516     if (box.isHorizontal()) {
517         boxSelectionRect.setHeight(box.height());
518         boxSelectionRect.setY(box.y());
519     } else {
520         boxSelectionRect.setWidth(box.width());
521         boxSelectionRect.setX(box.x());
522     }
523     return boxSelectionRect;
524 }
525
526 Vector<IntRect> RenderTextLineBoxes::absoluteRectsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
527 {
528     Vector<IntRect> rects;
529     for (auto box = m_first; box; box = box->nextTextBox()) {
530         // Note: box->end() returns the index of the last character, not the index past it
531         if (start <= box->start() && box->end() < end) {
532             FloatRect boundaries = box->calculateBoundaries();
533             if (useSelectionHeight) {
534                 LayoutRect selectionRect = box->localSelectionRect(start, end);
535                 if (box->isHorizontal()) {
536                     boundaries.setHeight(selectionRect.height());
537                     boundaries.setY(selectionRect.y());
538                 } else {
539                     boundaries.setWidth(selectionRect.width());
540                     boundaries.setX(selectionRect.x());
541                 }
542             }
543             rects.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed).enclosingBoundingBox());
544             continue;
545         }
546         // FIXME: This code is wrong. It's converting local to absolute twice. http://webkit.org/b/65722
547         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
548         if (!rect.isZero())
549             rects.append(renderer.localToAbsoluteQuad(rect, 0, wasFixed).enclosingBoundingBox());
550     }
551     return rects;
552 }
553
554 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuads(const RenderText& renderer, bool* wasFixed, ClippingOption option) const
555 {
556     Vector<FloatQuad> quads;
557     for (auto box = m_first; box; box = box->nextTextBox()) {
558         FloatRect boundaries = box->calculateBoundaries();
559
560         // Shorten the width of this text box if it ends in an ellipsis.
561         // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
562         IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(*box, 0, renderer.textLength()) : IntRect();
563         if (!ellipsisRect.isEmpty()) {
564             if (renderer.style().isHorizontalWritingMode())
565                 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
566             else
567                 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
568         }
569         quads.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed));
570     }
571     return quads;
572 }
573
574 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuadsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
575 {
576     Vector<FloatQuad> quads;
577     for (auto box = m_first; box; box = box->nextTextBox()) {
578         // Note: box->end() returns the index of the last character, not the index past it
579         if (start <= box->start() && box->end() < end) {
580             FloatRect boundaries = box->calculateBoundaries();
581             if (useSelectionHeight) {
582                 LayoutRect selectionRect = box->localSelectionRect(start, end);
583                 if (box->isHorizontal()) {
584                     boundaries.setHeight(selectionRect.height());
585                     boundaries.setY(selectionRect.y());
586                 } else {
587                     boundaries.setWidth(selectionRect.width());
588                     boundaries.setX(selectionRect.x());
589                 }
590             }
591             quads.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed));
592             continue;
593         }
594         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
595         if (!rect.isZero())
596             quads.append(renderer.localToAbsoluteQuad(rect, 0, wasFixed));
597     }
598     return quads;
599 }
600
601 void RenderTextLineBoxes::dirtyAll()
602 {
603     for (auto box = m_first; box; box = box->nextTextBox())
604         box->dirtyLineBoxes();
605 }
606
607 bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsigned end, int lengthDelta)
608 {
609     RootInlineBox* firstRootBox = nullptr;
610     RootInlineBox* lastRootBox = nullptr;
611
612     // Dirty all text boxes that include characters in between offset and offset+len.
613     bool dirtiedLines = false;
614     for (auto current = m_first; current; current = current->nextTextBox()) {
615         // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264
616         // Text run is entirely before the affected range.
617         if (current->end() < start)
618             continue;
619         // Text run is entirely after the affected range.
620         if (current->start() > end) {
621             current->offsetRun(lengthDelta);
622             auto& rootBox = current->root();
623             if (!firstRootBox) {
624                 firstRootBox = &rootBox;
625                 if (!dirtiedLines) {
626                     // The affected area was in between two runs. Go ahead and mark the root box of
627                     // the run after the affected area as dirty.
628                     firstRootBox->markDirty();
629                     dirtiedLines = true;
630                 }
631             }
632             lastRootBox = &rootBox;
633             continue;
634         }
635         if (current->end() >= start && current->end() <= end) {
636             // Text run overlaps with the left end of the affected range.
637             current->dirtyLineBoxes();
638             dirtiedLines = true;
639             continue;
640         }
641         if (current->start() <= start && current->end() >= end) {
642             // Text run subsumes the affected range.
643             current->dirtyLineBoxes();
644             dirtiedLines = true;
645             continue;
646         }
647         if (current->start() <= end && current->end() >= end) {
648             // Text run overlaps with right end of the affected range.
649             current->dirtyLineBoxes();
650             dirtiedLines = true;
651             continue;
652         }
653     }
654
655     // Now we have to walk all of the clean lines and adjust their cached line break information
656     // to reflect our updated offsets.
657     if (lastRootBox)
658         lastRootBox = lastRootBox->nextRootBox();
659     if (firstRootBox) {
660         auto previousRootBox = firstRootBox->prevRootBox();
661         if (previousRootBox)
662             firstRootBox = previousRootBox;
663     } else if (m_last) {
664         ASSERT(!lastRootBox);
665         firstRootBox = &m_last->root();
666         firstRootBox->markDirty();
667         dirtiedLines = true;
668     }
669     for (auto current = firstRootBox; current && current != lastRootBox; current = current->nextRootBox()) {
670         if (current->lineBreakObj() == &renderer && current->lineBreakPos() > end)
671             current->setLineBreakPos(current->lineBreakPos() + lengthDelta);
672     }
673     
674     // If the text node is empty, dirty the line where new text will be inserted.
675     if (!m_first && renderer.parent()) {
676         renderer.parent()->dirtyLinesFromChangedChild(&renderer);
677         dirtiedLines = true;
678     }
679     return dirtiedLines;
680 }
681
682 inline void RenderTextLineBoxes::checkConsistency() const
683 {
684 #if !ASSERT_DISABLED
685 #ifdef CHECK_CONSISTENCY
686     const InlineTextBox* prev = nullptr;
687     for (auto child = m_first; child; child = child->nextTextBox()) {
688         ASSERT(child->renderer() == this);
689         ASSERT(child->prevTextBox() == prev);
690         prev = child;
691     }
692     ASSERT(prev == m_last);
693 #endif
694 #endif
695 }
696
697 #if !ASSERT_DISABLED
698 RenderTextLineBoxes::~RenderTextLineBoxes()
699 {
700     ASSERT(!m_first);
701     ASSERT(!m_last);
702 }
703 #endif
704
705 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
706 void RenderTextLineBoxes::invalidateParentChildLists()
707 {
708     for (auto box = m_first; box; box = box->nextTextBox())
709         box->invalidateParentChildList();
710 }
711 #endif
712
713 }