Rename InlineBox::remove() to removeFromParent
[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;
48         m_last = textBox;
49     } else {
50         m_last->setNextTextBox(textBox);
51         textBox->setPreviousTextBox(m_last);
52         m_last = textBox;
53     }
54     return textBox;
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(RenderText& renderer)
120 {
121     if (!m_first)
122         return;
123     auto& arena = renderer.renderArena();
124     InlineTextBox* next;
125     for (auto current = m_first; current; current = next) {
126         next = current->nextTextBox();
127         current->destroy(arena);
128     }
129     m_first = nullptr;
130     m_last = nullptr;
131 }
132
133 InlineTextBox* RenderTextLineBoxes::findNext(int offset, int& position) const
134 {
135     if (!m_first)
136         return nullptr;
137     // FIXME: This looks buggy. The function is only used for debugging purposes.
138     auto current = m_first;
139     int currentOffset = current->len();
140     while (offset > currentOffset && current->nextTextBox()) {
141         current = current->nextTextBox();
142         currentOffset = current->start() + current->len();
143     }
144     // we are now in the correct text run
145     position = (offset > currentOffset ? current->len() : current->len() - (currentOffset - offset));
146     return current;
147 }
148
149 IntRect RenderTextLineBoxes::boundingBox(const RenderText& renderer) const
150 {
151     if (!m_first)
152         return IntRect();
153
154     // Return the width of the minimal left side and the maximal right side.
155     float logicalLeftSide = 0;
156     float logicalRightSide = 0;
157     for (auto current = m_first; current; current = current->nextTextBox()) {
158         if (current == m_first || current->logicalLeft() < logicalLeftSide)
159             logicalLeftSide = current->logicalLeft();
160         if (current == m_first || current->logicalRight() > logicalRightSide)
161             logicalRightSide = current->logicalRight();
162     }
163     
164     bool isHorizontal = renderer.style()->isHorizontalWritingMode();
165     
166     float x = isHorizontal ? logicalLeftSide : m_first->x();
167     float y = isHorizontal ? m_first->y() : logicalLeftSide;
168     float width = isHorizontal ? logicalRightSide - logicalLeftSide : m_last->logicalBottom() - x;
169     float height = isHorizontal ? m_last->logicalBottom() - y : logicalRightSide - logicalLeftSide;
170     return enclosingIntRect(FloatRect(x, y, width, height));
171 }
172
173 LayoutRect RenderTextLineBoxes::visualOverflowBoundingBox(const RenderText& renderer) const
174 {
175     if (!m_first)
176         return LayoutRect();
177
178     // Return the width of the minimal left side and the maximal right side.
179     auto logicalLeftSide = LayoutUnit::max();
180     auto logicalRightSide = LayoutUnit::min();
181     for (auto current = m_first; current; current = current->nextTextBox()) {
182         logicalLeftSide = std::min(logicalLeftSide, current->logicalLeftVisualOverflow());
183         logicalRightSide = std::max(logicalRightSide, current->logicalRightVisualOverflow());
184     }
185     
186     auto logicalTop = m_first->logicalTopVisualOverflow();
187     auto logicalWidth = logicalRightSide - logicalLeftSide;
188     auto logicalHeight = m_last->logicalBottomVisualOverflow() - logicalTop;
189     
190     LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
191     if (!renderer.style()->isHorizontalWritingMode())
192         rect = rect.transposedRect();
193     return rect;
194 }
195
196 bool RenderTextLineBoxes::hasRenderedText() const
197 {
198     for (auto box = m_first; box; box = box->nextTextBox()) {
199         if (box->len())
200             return true;
201     }
202     return false;
203 }
204
205 int RenderTextLineBoxes::caretMinOffset() const
206 {
207     auto box = m_first;
208     if (!box)
209         return 0;
210     int minOffset = box->start();
211     for (box = box->nextTextBox(); box; box = box->nextTextBox())
212         minOffset = std::min<int>(minOffset, box->start());
213     return minOffset;
214 }
215
216 int RenderTextLineBoxes::caretMaxOffset(const RenderText& renderer) const
217 {
218     auto box = m_last;
219     if (!box)
220         return renderer.textLength();
221
222     int maxOffset = box->start() + box->len();
223     for (box = box->prevTextBox(); box; box = box->prevTextBox())
224         maxOffset = std::max<int>(maxOffset, box->start() + box->len());
225     return maxOffset;
226 }
227
228 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
229
230 static bool lineDirectionPointFitsInBox(int pointLineDirection, const InlineTextBox& box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
231 {
232     shouldAffinityBeDownstream = AlwaysDownstream;
233
234     // the x coordinate is equal to the left edge of this box
235     // the affinity must be downstream so the position doesn't jump back to the previous line
236     // except when box is the first box in the line
237     if (pointLineDirection <= box.logicalLeft()) {
238         shouldAffinityBeDownstream = !box.prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
239         return true;
240     }
241
242     // and the x coordinate is to the left of the right edge of this box
243     // check to see if position goes in this box
244     if (pointLineDirection < box.logicalRight()) {
245         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
246         return true;
247     }
248
249     // box is first on line
250     // and the x coordinate is to the left of the first text box left edge
251     if (!box.prevLeafChildIgnoringLineBreak() && pointLineDirection < box.logicalLeft())
252         return true;
253
254     if (!box.nextLeafChildIgnoringLineBreak()) {
255         // box is last on line
256         // and the x coordinate is to the right of the last text box right edge
257         // generate VisiblePosition, use UPSTREAM affinity if possible
258         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
259         return true;
260     }
261
262     return false;
263 }
264
265 static VisiblePosition createVisiblePositionForBox(const InlineBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
266 {
267     EAffinity affinity = VP_DEFAULT_AFFINITY;
268     switch (shouldAffinityBeDownstream) {
269     case AlwaysDownstream:
270         affinity = DOWNSTREAM;
271         break;
272     case AlwaysUpstream:
273         affinity = VP_UPSTREAM_IF_POSSIBLE;
274         break;
275     case UpstreamIfPositionIsNotAtStart:
276         affinity = offset > box.caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
277         break;
278     }
279     return box.renderer().createVisiblePosition(offset, affinity);
280 }
281
282 static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
283 {
284     ASSERT(offset >= 0);
285
286     if (offset && static_cast<unsigned>(offset) < box.len())
287         return createVisiblePositionForBox(box, box.start() + offset, shouldAffinityBeDownstream);
288
289     bool positionIsAtStartOfBox = !offset;
290     if (positionIsAtStartOfBox == box.isLeftToRightDirection()) {
291         // offset is on the left edge
292
293         const InlineBox* prevBox = box.prevLeafChildIgnoringLineBreak();
294         if ((prevBox && prevBox->bidiLevel() == box.bidiLevel())
295             || box.renderer().containingBlock()->style()->direction() == box.direction()) // FIXME: left on 12CBA
296             return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
297
298         if (prevBox && prevBox->bidiLevel() > box.bidiLevel()) {
299             // e.g. left of B in aDC12BAb
300             const InlineBox* leftmostBox;
301             do {
302                 leftmostBox = prevBox;
303                 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
304             } while (prevBox && prevBox->bidiLevel() > box.bidiLevel());
305             return createVisiblePositionForBox(*leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
306         }
307
308         if (!prevBox || prevBox->bidiLevel() < box.bidiLevel()) {
309             // e.g. left of D in aDC12BAb
310             const InlineBox* rightmostBox;
311             const InlineBox* nextBox = &box;
312             do {
313                 rightmostBox = nextBox;
314                 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
315             } while (nextBox && nextBox->bidiLevel() >= box.bidiLevel());
316             return createVisiblePositionForBox(*rightmostBox,
317                 box.isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
318         }
319
320         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
321     }
322
323     const InlineBox* nextBox = box.nextLeafChildIgnoringLineBreak();
324     if ((nextBox && nextBox->bidiLevel() == box.bidiLevel())
325         || box.renderer().containingBlock()->style()->direction() == box.direction())
326         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
327
328     // offset is on the right edge
329     if (nextBox && nextBox->bidiLevel() > box.bidiLevel()) {
330         // e.g. right of C in aDC12BAb
331         const InlineBox* rightmostBox;
332         do {
333             rightmostBox = nextBox;
334             nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
335         } while (nextBox && nextBox->bidiLevel() > box.bidiLevel());
336         return createVisiblePositionForBox(*rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
337     }
338
339     if (!nextBox || nextBox->bidiLevel() < box.bidiLevel()) {
340         // e.g. right of A in aDC12BAb
341         const InlineBox* leftmostBox;
342         const InlineBox* prevBox = &box;
343         do {
344             leftmostBox = prevBox;
345             prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
346         } while (prevBox && prevBox->bidiLevel() >= box.bidiLevel());
347         return createVisiblePositionForBox(*leftmostBox,
348             box.isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
349     }
350
351     return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
352 }
353
354 VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
355 {
356     if (!m_first || !renderer.textLength())
357         return renderer.createVisiblePosition(0, DOWNSTREAM);
358
359     LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
360     LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
361     bool blocksAreFlipped = renderer.style()->isFlippedBlocksWritingMode();
362
363     InlineTextBox* lastBox = nullptr;
364     for (auto box = m_first; box; box = box->nextTextBox()) {
365         if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
366             box = box->nextTextBox();
367
368         auto& rootBox = box->root();
369         LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
370         if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
371             LayoutUnit bottom = rootBox.selectionBottom();
372             if (rootBox.nextRootBox())
373                 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
374
375             if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
376                 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
377                 if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
378                     return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
379             }
380         }
381         lastBox = box;
382     }
383
384     if (lastBox) {
385         ShouldAffinityBeDownstream shouldAffinityBeDownstream;
386         lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
387         return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
388     }
389     return renderer.createVisiblePosition(0, DOWNSTREAM);
390 }
391
392 void RenderTextLineBoxes::setSelectionState(RenderText& renderer, RenderObject::SelectionState state)
393 {
394     if (state == RenderObject::SelectionInside || state == RenderObject::SelectionNone) {
395         for (auto box = m_first; box; box = box->nextTextBox())
396             box->root().setHasSelectedChildren(state == RenderObject::SelectionInside);
397         return;
398     }
399
400     int start, end;
401     renderer.selectionStartEnd(start, end);
402     if (state == RenderObject::SelectionStart) {
403         end = renderer.textLength();
404
405         // to handle selection from end of text to end of line
406         if (start && start == end)
407             start = end - 1;
408     } else if (state == RenderObject::SelectionEnd)
409         start = 0;
410
411     for (auto box = m_first; box; box = box->nextTextBox()) {
412         if (box->isSelected(start, end))
413             box->root().setHasSelectedChildren(true);
414     }
415 }
416
417 static IntRect ellipsisRectForBox(const InlineTextBox& box, unsigned start, unsigned end)
418 {
419     unsigned short truncation = box.truncation();
420     if (truncation == cNoTruncation)
421         return IntRect();
422
423     auto ellipsis = box.root().ellipsisBox();
424     if (!ellipsis)
425         return IntRect();
426     
427     IntRect rect;
428     int ellipsisStartPosition = std::max<int>(start - box.start(), 0);
429     int ellipsisEndPosition = std::min<int>(end - box.start(), box.len());
430     
431     // The ellipsis should be considered to be selected if the end of
432     // the selection is past the beginning of the truncation and the
433     // beginning of the selection is before or at the beginning of the truncation.
434     if (ellipsisEndPosition < truncation && ellipsisStartPosition > truncation)
435         return IntRect();
436     return ellipsis->selectionRect();
437 }
438
439 LayoutRect RenderTextLineBoxes::selectionRectForRange(unsigned start, unsigned end)
440 {
441     LayoutRect rect;
442     for (auto box = m_first; box; box = box->nextTextBox()) {
443         rect.unite(box->localSelectionRect(start, end));
444         rect.unite(ellipsisRectForBox(*box, start, end));
445     }
446     return rect;
447 }
448
449 Vector<IntRect> RenderTextLineBoxes::absoluteRects(const LayoutPoint& accumulatedOffset) const
450 {
451     Vector<IntRect> rects;
452     for (auto box = m_first; box; box = box->nextTextBox())
453         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + box->topLeft(), box->size())));
454     return rects;
455 }
456
457 static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight)
458 {
459     unsigned realEnd = std::min(box.end() + 1, end);
460     LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd);
461     if (!boxSelectionRect.height())
462         return FloatRect();
463     if (useSelectionHeight)
464         return boxSelectionRect;
465     // Change the height and y position (or width and x for vertical text)
466     // because selectionRect uses selection-specific values.
467     if (box.isHorizontal()) {
468         boxSelectionRect.setHeight(box.height());
469         boxSelectionRect.setY(box.y());
470     } else {
471         boxSelectionRect.setWidth(box.width());
472         boxSelectionRect.setX(box.x());
473     }
474     return boxSelectionRect;
475 }
476
477 Vector<IntRect> RenderTextLineBoxes::absoluteRectsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
478 {
479     Vector<IntRect> rects;
480     for (auto box = m_first; box; box = box->nextTextBox()) {
481         // Note: box->end() returns the index of the last character, not the index past it
482         if (start <= box->start() && box->end() < end) {
483             FloatRect boundaries = box->calculateBoundaries();
484             if (useSelectionHeight) {
485                 LayoutRect selectionRect = box->localSelectionRect(start, end);
486                 if (box->isHorizontal()) {
487                     boundaries.setHeight(selectionRect.height());
488                     boundaries.setY(selectionRect.y());
489                 } else {
490                     boundaries.setWidth(selectionRect.width());
491                     boundaries.setX(selectionRect.x());
492                 }
493             }
494             rects.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed).enclosingBoundingBox());
495             continue;
496         }
497         // FIXME: This code is wrong. It's converting local to absolute twice. http://webkit.org/b/65722
498         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
499         if (!rect.isZero())
500             rects.append(renderer.localToAbsoluteQuad(rect, 0, wasFixed).enclosingBoundingBox());
501     }
502     return rects;
503 }
504
505 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuads(const RenderText& renderer, bool* wasFixed, ClippingOption option) const
506 {
507     Vector<FloatQuad> quads;
508     for (auto box = m_first; box; box = box->nextTextBox()) {
509         FloatRect boundaries = box->calculateBoundaries();
510
511         // Shorten the width of this text box if it ends in an ellipsis.
512         // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
513         IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(*box, 0, renderer.textLength()) : IntRect();
514         if (!ellipsisRect.isEmpty()) {
515             if (renderer.style()->isHorizontalWritingMode())
516                 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
517             else
518                 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
519         }
520         quads.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed));
521     }
522     return quads;
523 }
524
525 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuadsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
526 {
527     Vector<FloatQuad> quads;
528     for (auto box = m_first; box; box = box->nextTextBox()) {
529         // Note: box->end() returns the index of the last character, not the index past it
530         if (start <= box->start() && box->end() < end) {
531             FloatRect boundaries = box->calculateBoundaries();
532             if (useSelectionHeight) {
533                 LayoutRect selectionRect = box->localSelectionRect(start, end);
534                 if (box->isHorizontal()) {
535                     boundaries.setHeight(selectionRect.height());
536                     boundaries.setY(selectionRect.y());
537                 } else {
538                     boundaries.setWidth(selectionRect.width());
539                     boundaries.setX(selectionRect.x());
540                 }
541             }
542             quads.append(renderer.localToAbsoluteQuad(boundaries, 0, wasFixed));
543             continue;
544         }
545         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
546         if (!rect.isZero())
547             quads.append(renderer.localToAbsoluteQuad(rect, 0, wasFixed));
548     }
549     return quads;
550 }
551
552 void RenderTextLineBoxes::dirtyAll()
553 {
554     for (auto box = m_first; box; box = box->nextTextBox())
555         box->dirtyLineBoxes();
556 }
557
558 bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsigned end, int lengthDelta)
559 {
560     RootInlineBox* firstRootBox = nullptr;
561     RootInlineBox* lastRootBox = nullptr;
562
563     // Dirty all text boxes that include characters in between offset and offset+len.
564     bool dirtiedLines = false;
565     for (auto current = m_first; current; current = current->nextTextBox()) {
566         // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264
567         // Text run is entirely before the affected range.
568         if (current->end() < start)
569             continue;
570         // Text run is entirely after the affected range.
571         if (current->start() > end) {
572             current->offsetRun(lengthDelta);
573             auto& rootBox = current->root();
574             if (!firstRootBox) {
575                 firstRootBox = &rootBox;
576                 if (!dirtiedLines) {
577                     // The affected area was in between two runs. Go ahead and mark the root box of
578                     // the run after the affected area as dirty.
579                     firstRootBox->markDirty();
580                     dirtiedLines = true;
581                 }
582             }
583             lastRootBox = &rootBox;
584             continue;
585         }
586         if (current->end() >= start && current->end() <= end) {
587             // Text run overlaps with the left end of the affected range.
588             current->dirtyLineBoxes();
589             dirtiedLines = true;
590             continue;
591         }
592         if (current->start() <= start && current->end() >= end) {
593             // Text run subsumes the affected range.
594             current->dirtyLineBoxes();
595             dirtiedLines = true;
596             continue;
597         }
598         if (current->start() <= end && current->end() >= end) {
599             // Text run overlaps with right end of the affected range.
600             current->dirtyLineBoxes();
601             dirtiedLines = true;
602             continue;
603         }
604     }
605
606     // Now we have to walk all of the clean lines and adjust their cached line break information
607     // to reflect our updated offsets.
608     if (lastRootBox)
609         lastRootBox = lastRootBox->nextRootBox();
610     if (firstRootBox) {
611         auto previousRootBox = firstRootBox->prevRootBox();
612         if (previousRootBox)
613             firstRootBox = previousRootBox;
614     } else if (m_last) {
615         ASSERT(!lastRootBox);
616         firstRootBox = &m_last->root();
617         firstRootBox->markDirty();
618         dirtiedLines = true;
619     }
620     for (auto current = firstRootBox; current && current != lastRootBox; current = current->nextRootBox()) {
621         if (current->lineBreakObj() == &renderer && current->lineBreakPos() > end)
622             current->setLineBreakPos(current->lineBreakPos() + lengthDelta);
623     }
624     
625     // If the text node is empty, dirty the line where new text will be inserted.
626     if (!m_first && renderer.parent()) {
627         renderer.parent()->dirtyLinesFromChangedChild(&renderer);
628         dirtiedLines = true;
629     }
630     return dirtiedLines;
631 }
632
633 inline void RenderTextLineBoxes::checkConsistency() const
634 {
635 #if !ASSERT_DISABLED
636 #ifdef CHECK_CONSISTENCY
637     const InlineTextBox* prev = nullptr;
638     for (auto child = m_first; child; child = child->nextTextBox()) {
639         ASSERT(child->renderer() == this);
640         ASSERT(child->prevTextBox() == prev);
641         prev = child;
642     }
643     ASSERT(prev == m_last);
644 #endif
645 #endif
646 }
647
648 #if !ASSERT_DISABLED
649 RenderTextLineBoxes::~RenderTextLineBoxes()
650 {
651     ASSERT(!m_first);
652     ASSERT(!m_last);
653 }
654 #endif
655
656 }