Use LineLayoutTraversal for RenderText functions
[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 "RenderView.h"
34 #include "RootInlineBox.h"
35
36 namespace WebCore {
37
38 RenderTextLineBoxes::RenderTextLineBoxes()
39     : m_first(nullptr)
40     , m_last(nullptr)
41 {
42 }
43
44 InlineTextBox* RenderTextLineBoxes::createAndAppendLineBox(RenderText& renderText)
45 {
46     auto textBox = renderText.createTextBox();
47     if (!m_first) {
48         m_first = textBox.get();
49         m_last = textBox.get();
50     } else {
51         m_last->setNextTextBox(textBox.get());
52         textBox->setPreviousTextBox(m_last);
53         m_last = textBox.get();
54     }
55     return textBox.release();
56 }
57
58 void RenderTextLineBoxes::extract(InlineTextBox& box)
59 {
60     checkConsistency();
61
62     m_last = box.prevTextBox();
63     if (&box == m_first)
64         m_first = nullptr;
65     if (box.prevTextBox())
66         box.prevTextBox()->setNextTextBox(nullptr);
67     box.setPreviousTextBox(nullptr);
68     for (auto* current = &box; current; current = current->nextTextBox())
69         current->setExtracted();
70
71     checkConsistency();
72 }
73
74 void RenderTextLineBoxes::attach(InlineTextBox& box)
75 {
76     checkConsistency();
77
78     if (m_last) {
79         m_last->setNextTextBox(&box);
80         box.setPreviousTextBox(m_last);
81     } else
82         m_first = &box;
83     InlineTextBox* last = nullptr;
84     for (auto* current = &box; current; current = current->nextTextBox()) {
85         current->setExtracted(false);
86         last = current;
87     }
88     m_last = last;
89
90     checkConsistency();
91 }
92
93 void RenderTextLineBoxes::remove(InlineTextBox& box)
94 {
95     checkConsistency();
96
97     if (&box == m_first)
98         m_first = box.nextTextBox();
99     if (&box == m_last)
100         m_last = box.prevTextBox();
101     if (box.nextTextBox())
102         box.nextTextBox()->setPreviousTextBox(box.prevTextBox());
103     if (box.prevTextBox())
104         box.prevTextBox()->setNextTextBox(box.nextTextBox());
105
106     checkConsistency();
107 }
108
109 void RenderTextLineBoxes::removeAllFromParent(RenderText& renderer)
110 {
111     if (!m_first) {
112         if (renderer.parent())
113             renderer.parent()->dirtyLinesFromChangedChild(renderer);
114         return;
115     }
116     for (auto* box = m_first; box; box = box->nextTextBox())
117         box->removeFromParent();
118 }
119
120 void RenderTextLineBoxes::deleteAll()
121 {
122     if (!m_first)
123         return;
124     InlineTextBox* next;
125     for (auto* current = m_first; current; current = next) {
126         next = current->nextTextBox();
127         delete current;
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 LayoutRect RenderTextLineBoxes::visualOverflowBoundingBox(const RenderText& renderer) const
150 {
151     if (!m_first)
152         return LayoutRect();
153
154     // Return the width of the minimal left side and the maximal right side.
155     auto logicalLeftSide = LayoutUnit::max();
156     auto logicalRightSide = LayoutUnit::min();
157     for (auto* current = m_first; current; current = current->nextTextBox()) {
158         logicalLeftSide = std::min(logicalLeftSide, current->logicalLeftVisualOverflow());
159         logicalRightSide = std::max(logicalRightSide, current->logicalRightVisualOverflow());
160     }
161     
162     auto logicalTop = m_first->logicalTopVisualOverflow();
163     auto logicalWidth = logicalRightSide - logicalLeftSide;
164     auto logicalHeight = m_last->logicalBottomVisualOverflow() - logicalTop;
165     
166     LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
167     if (!renderer.style().isHorizontalWritingMode())
168         rect = rect.transposedRect();
169     return rect;
170 }
171
172 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
173
174 static bool lineDirectionPointFitsInBox(int pointLineDirection, const InlineTextBox& box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
175 {
176     shouldAffinityBeDownstream = AlwaysDownstream;
177
178     // the x coordinate is equal to the left edge of this box
179     // the affinity must be downstream so the position doesn't jump back to the previous line
180     // except when box is the first box in the line
181     if (pointLineDirection <= box.logicalLeft()) {
182         shouldAffinityBeDownstream = !box.previousLeafOnLine() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
183         return true;
184     }
185
186 #if !PLATFORM(IOS_FAMILY)
187     // and the x coordinate is to the left of the right edge of this box
188     // check to see if position goes in this box
189     if (pointLineDirection < box.logicalRight()) {
190         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
191         return true;
192     }
193 #endif
194
195     // box is first on line
196     // and the x coordinate is to the left of the first text box left edge
197     if (!box.previousLeafOnLineIgnoringLineBreak() && pointLineDirection < box.logicalLeft())
198         return true;
199
200     if (!box.nextLeafOnLineIgnoringLineBreak()) {
201         // box is last on line
202         // and the x coordinate is to the right of the last text box right edge
203         // generate VisiblePosition, use UPSTREAM affinity if possible
204         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
205         return true;
206     }
207
208     return false;
209 }
210
211 static VisiblePosition createVisiblePositionForBox(const InlineBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
212 {
213     EAffinity affinity = VP_DEFAULT_AFFINITY;
214     switch (shouldAffinityBeDownstream) {
215     case AlwaysDownstream:
216         affinity = DOWNSTREAM;
217         break;
218     case AlwaysUpstream:
219         affinity = VP_UPSTREAM_IF_POSSIBLE;
220         break;
221     case UpstreamIfPositionIsNotAtStart:
222         affinity = offset > box.caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
223         break;
224     }
225     return box.renderer().createVisiblePosition(offset, affinity);
226 }
227
228 static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
229 {
230     ASSERT(offset >= 0);
231
232     if (offset && static_cast<unsigned>(offset) < box.len())
233         return createVisiblePositionForBox(box, box.start() + offset, shouldAffinityBeDownstream);
234
235     bool positionIsAtStartOfBox = !offset;
236     if (positionIsAtStartOfBox == box.isLeftToRightDirection()) {
237         // offset is on the left edge
238
239         const InlineBox* prevBox = box.previousLeafOnLineIgnoringLineBreak();
240         if ((prevBox && prevBox->bidiLevel() == box.bidiLevel())
241             || box.renderer().containingBlock()->style().direction() == box.direction()) // FIXME: left on 12CBA
242             return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
243
244         if (prevBox && prevBox->bidiLevel() > box.bidiLevel()) {
245             // e.g. left of B in aDC12BAb
246             const InlineBox* leftmostBox;
247             do {
248                 leftmostBox = prevBox;
249                 prevBox = leftmostBox->previousLeafOnLineIgnoringLineBreak();
250             } while (prevBox && prevBox->bidiLevel() > box.bidiLevel());
251             return createVisiblePositionForBox(*leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
252         }
253
254         if (!prevBox || prevBox->bidiLevel() < box.bidiLevel()) {
255             // e.g. left of D in aDC12BAb
256             const InlineBox* rightmostBox;
257             const InlineBox* nextBox = &box;
258             do {
259                 rightmostBox = nextBox;
260                 nextBox = rightmostBox->nextLeafOnLineIgnoringLineBreak();
261             } while (nextBox && nextBox->bidiLevel() >= box.bidiLevel());
262             return createVisiblePositionForBox(*rightmostBox,
263                 box.isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
264         }
265
266         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
267     }
268
269     const InlineBox* nextBox = box.nextLeafOnLineIgnoringLineBreak();
270     if ((nextBox && nextBox->bidiLevel() == box.bidiLevel())
271         || box.renderer().containingBlock()->style().direction() == box.direction())
272         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
273
274     // offset is on the right edge
275     if (nextBox && nextBox->bidiLevel() > box.bidiLevel()) {
276         // e.g. right of C in aDC12BAb
277         const InlineBox* rightmostBox;
278         do {
279             rightmostBox = nextBox;
280             nextBox = rightmostBox->nextLeafOnLineIgnoringLineBreak();
281         } while (nextBox && nextBox->bidiLevel() > box.bidiLevel());
282         return createVisiblePositionForBox(*rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
283     }
284
285     if (!nextBox || nextBox->bidiLevel() < box.bidiLevel()) {
286         // e.g. right of A in aDC12BAb
287         const InlineBox* leftmostBox;
288         const InlineBox* prevBox = &box;
289         do {
290             leftmostBox = prevBox;
291             prevBox = leftmostBox->previousLeafOnLineIgnoringLineBreak();
292         } while (prevBox && prevBox->bidiLevel() >= box.bidiLevel());
293         return createVisiblePositionForBox(*leftmostBox,
294             box.isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
295     }
296
297     return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
298 }
299
300 VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
301 {
302     if (!m_first || !renderer.text().length())
303         return renderer.createVisiblePosition(0, DOWNSTREAM);
304
305     LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
306     LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
307     bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode();
308
309     InlineTextBox* lastBox = nullptr;
310     for (auto* box = m_first; box; box = box->nextTextBox()) {
311         if (box->isLineBreak() && !box->previousLeafOnLine() && box->nextLeafOnLine() && !box->nextLeafOnLine()->isLineBreak())
312             box = box->nextTextBox();
313
314         auto& rootBox = box->root();
315         LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
316         if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
317             LayoutUnit bottom = rootBox.selectionBottom();
318             if (rootBox.nextRootBox())
319                 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
320
321             if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
322                 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
323 #if PLATFORM(IOS_FAMILY)
324                 if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) {
325                     int half = box->x() + box->logicalWidth() / 2;
326                     EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE;
327                     return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity);
328                 }
329 #endif
330                 if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
331                     return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
332             }
333         }
334         lastBox = box;
335     }
336
337     if (lastBox) {
338         ShouldAffinityBeDownstream shouldAffinityBeDownstream;
339         lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
340         return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
341     }
342     return renderer.createVisiblePosition(0, DOWNSTREAM);
343 }
344
345 void RenderTextLineBoxes::setSelectionState(RenderText& renderer, RenderObject::SelectionState state)
346 {
347     if (state == RenderObject::SelectionInside || state == RenderObject::SelectionNone) {
348         for (auto* box = m_first; box; box = box->nextTextBox())
349             box->root().setHasSelectedChildren(state == RenderObject::SelectionInside);
350         return;
351     }
352
353     auto start = renderer.view().selection().startPosition();
354     auto end = renderer.view().selection().endPosition();
355     if (state == RenderObject::SelectionStart) {
356         end = renderer.text().length();
357         // to handle selection from end of text to end of line
358         if (start && start == end)
359             start = end - 1;
360     } else if (state == RenderObject::SelectionEnd)
361         start = 0;
362
363     for (auto* box = m_first; box; box = box->nextTextBox()) {
364         if (box->isSelected(start, end))
365             box->root().setHasSelectedChildren(true);
366     }
367 }
368
369 static IntRect ellipsisRectForBox(const InlineTextBox& box, unsigned start, unsigned end)
370 {
371     unsigned short truncation = box.truncation();
372     if (truncation == cNoTruncation)
373         return IntRect();
374
375     auto ellipsis = box.root().ellipsisBox();
376     if (!ellipsis)
377         return IntRect();
378     
379     IntRect rect;
380     int ellipsisStartPosition = std::max<int>(start - box.start(), 0);
381     int ellipsisEndPosition = std::min<int>(end - box.start(), box.len());
382     
383     // The ellipsis should be considered to be selected if the end of
384     // the selection is past the beginning of the truncation and the
385     // beginning of the selection is before or at the beginning of the truncation.
386     if (ellipsisEndPosition < truncation && ellipsisStartPosition > truncation)
387         return IntRect();
388     return ellipsis->selectionRect();
389 }
390
391 LayoutRect RenderTextLineBoxes::selectionRectForRange(unsigned start, unsigned end)
392 {
393     LayoutRect rect;
394     for (auto* box = m_first; box; box = box->nextTextBox()) {
395         rect.unite(box->localSelectionRect(start, end));
396         rect.unite(ellipsisRectForBox(*box, start, end));
397     }
398     return rect;
399 }
400
401 void RenderTextLineBoxes::collectSelectionRectsForRange(unsigned start, unsigned end, Vector<LayoutRect>& rects)
402 {
403     for (auto* box = m_first; box; box = box->nextTextBox()) {
404         LayoutRect rect;
405         rect.unite(box->localSelectionRect(start, end));
406         rect.unite(ellipsisRectForBox(*box, start, end));
407         if (!rect.size().isEmpty())
408             rects.append(rect);
409     }
410 }
411
412 static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight)
413 {
414     unsigned realEnd = std::min(box.end(), end);
415     LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd);
416     if (!boxSelectionRect.height())
417         return FloatRect();
418     if (useSelectionHeight)
419         return boxSelectionRect;
420     // Change the height and y position (or width and x for vertical text)
421     // because selectionRect uses selection-specific values.
422     if (box.isHorizontal()) {
423         boxSelectionRect.setHeight(box.height());
424         boxSelectionRect.setY(box.y());
425     } else {
426         boxSelectionRect.setWidth(box.width());
427         boxSelectionRect.setX(box.x());
428     }
429     return boxSelectionRect;
430 }
431
432 Vector<IntRect> RenderTextLineBoxes::absoluteRectsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
433 {
434     Vector<IntRect> rects;
435     for (auto* box = m_first; box; box = box->nextTextBox()) {
436         if (start <= box->start() && box->end() <= end) {
437             FloatRect boundaries = box->calculateBoundaries();
438             if (useSelectionHeight) {
439                 LayoutRect selectionRect = box->localSelectionRect(start, end);
440                 if (box->isHorizontal()) {
441                     boundaries.setHeight(selectionRect.height());
442                     boundaries.setY(selectionRect.y());
443                 } else {
444                     boundaries.setWidth(selectionRect.width());
445                     boundaries.setX(selectionRect.x());
446                 }
447             }
448             rects.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed).enclosingBoundingBox());
449             continue;
450         }
451         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
452         if (!rect.isZero())
453             rects.append(renderer.localToAbsoluteQuad(rect, UseTransforms, wasFixed).enclosingBoundingBox());
454     }
455     return rects;
456 }
457
458 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuads(const RenderText& renderer, bool* wasFixed, ClippingOption option) const
459 {
460     Vector<FloatQuad> quads;
461     for (auto* box = m_first; box; box = box->nextTextBox()) {
462         FloatRect boundaries = box->calculateBoundaries();
463
464         // Shorten the width of this text box if it ends in an ellipsis.
465         // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
466         IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(*box, 0, renderer.text().length()) : IntRect();
467         if (!ellipsisRect.isEmpty()) {
468             if (renderer.style().isHorizontalWritingMode())
469                 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
470             else
471                 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
472         }
473         quads.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed));
474     }
475     return quads;
476 }
477
478 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuadsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
479 {
480     Vector<FloatQuad> quads;
481     for (auto* box = m_first; box; box = box->nextTextBox()) {
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             quads.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed));
495             continue;
496         }
497         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
498         if (!rect.isZero())
499             quads.append(renderer.localToAbsoluteQuad(rect, UseTransforms, wasFixed));
500     }
501     return quads;
502 }
503
504 void RenderTextLineBoxes::dirtyAll()
505 {
506     for (auto* box = m_first; box; box = box->nextTextBox())
507         box->dirtyLineBoxes();
508 }
509
510 bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsigned end, int lengthDelta)
511 {
512     RootInlineBox* firstRootBox = nullptr;
513     RootInlineBox* lastRootBox = nullptr;
514
515     // Dirty all text boxes that include characters in between offset and offset+len.
516     bool dirtiedLines = false;
517     for (auto* current = m_first; current; current = current->nextTextBox()) {
518         // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264
519         // Text run is entirely before the affected range.
520         if (current->end() <= start)
521             continue;
522         // Text run is entirely after the affected range.
523         if (current->start() >= end) {
524             current->offsetRun(lengthDelta);
525             auto& rootBox = current->root();
526             if (!firstRootBox) {
527                 firstRootBox = &rootBox;
528                 if (!dirtiedLines) {
529                     // The affected area was in between two runs. Mark the root box of the run after the affected area as dirty.
530                     firstRootBox->markDirty();
531                     dirtiedLines = true;
532                 }
533             }
534             lastRootBox = &rootBox;
535             continue;
536         }
537         if (current->end() > start && current->end() <= end) {
538             // Text run overlaps with the left end of the affected range.
539             current->dirtyLineBoxes();
540             dirtiedLines = true;
541             continue;
542         }
543         if (current->start() <= start && current->end() >= end) {
544             // Text run subsumes the affected range.
545             current->dirtyLineBoxes();
546             dirtiedLines = true;
547             continue;
548         }
549         if (current->start() < end && current->end() >= end) {
550             // Text run overlaps with right end of the affected range.
551             current->dirtyLineBoxes();
552             dirtiedLines = true;
553             continue;
554         }
555     }
556
557     // Now we have to walk all of the clean lines and adjust their cached line break information
558     // to reflect our updated offsets.
559     if (lastRootBox)
560         lastRootBox = lastRootBox->nextRootBox();
561     if (firstRootBox) {
562         auto previousRootBox = firstRootBox->prevRootBox();
563         if (previousRootBox)
564             firstRootBox = previousRootBox;
565     } else if (m_last) {
566         ASSERT(!lastRootBox);
567         firstRootBox = &m_last->root();
568         firstRootBox->markDirty();
569         dirtiedLines = true;
570     }
571     for (auto* current = firstRootBox; current && current != lastRootBox; current = current->nextRootBox()) {
572         if (current->lineBreakObj() == &renderer && current->lineBreakPos() > end)
573             current->setLineBreakPos(current->lineBreakPos() + lengthDelta);
574     }
575     
576     // If the text node is empty, dirty the line where new text will be inserted.
577     if (!m_first && renderer.parent()) {
578         renderer.parent()->dirtyLinesFromChangedChild(renderer);
579         dirtiedLines = true;
580     }
581     return dirtiedLines;
582 }
583
584 inline void RenderTextLineBoxes::checkConsistency() const
585 {
586 #if !ASSERT_DISABLED
587 #ifdef CHECK_CONSISTENCY
588     const InlineTextBox* prev = nullptr;
589     for (auto* child = m_first; child; child = child->nextTextBox()) {
590         ASSERT(child->renderer() == this);
591         ASSERT(child->prevTextBox() == prev);
592         prev = child;
593     }
594     ASSERT(prev == m_last);
595 #endif
596 #endif
597 }
598
599 #if !ASSERT_DISABLED
600 RenderTextLineBoxes::~RenderTextLineBoxes()
601 {
602     ASSERT(!m_first);
603     ASSERT(!m_last);
604 }
605 #endif
606
607 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
608 void RenderTextLineBoxes::invalidateParentChildLists()
609 {
610     for (auto* box = m_first; box; box = box->nextTextBox())
611         box->invalidateParentChildList();
612 }
613 #endif
614
615 }