40cac1c32e909fea67e0fbc00d43a6818efa62d8
[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 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 IntPoint RenderTextLineBoxes::firstRunLocation() const
174 {
175     if (!m_first)
176         return IntPoint();
177     return IntPoint(m_first->topLeft());
178 }
179
180 LayoutRect RenderTextLineBoxes::visualOverflowBoundingBox(const RenderText& renderer) const
181 {
182     if (!m_first)
183         return LayoutRect();
184
185     // Return the width of the minimal left side and the maximal right side.
186     auto logicalLeftSide = LayoutUnit::max();
187     auto logicalRightSide = LayoutUnit::min();
188     for (auto* current = m_first; current; current = current->nextTextBox()) {
189         logicalLeftSide = std::min(logicalLeftSide, current->logicalLeftVisualOverflow());
190         logicalRightSide = std::max(logicalRightSide, current->logicalRightVisualOverflow());
191     }
192     
193     auto logicalTop = m_first->logicalTopVisualOverflow();
194     auto logicalWidth = logicalRightSide - logicalLeftSide;
195     auto logicalHeight = m_last->logicalBottomVisualOverflow() - logicalTop;
196     
197     LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
198     if (!renderer.style().isHorizontalWritingMode())
199         rect = rect.transposedRect();
200     return rect;
201 }
202
203 bool RenderTextLineBoxes::hasRenderedText() const
204 {
205     for (auto* box = m_first; box; box = box->nextTextBox()) {
206         if (box->len())
207             return true;
208     }
209     return false;
210 }
211
212 int RenderTextLineBoxes::caretMinOffset() const
213 {
214     auto box = m_first;
215     if (!box)
216         return 0;
217     int minOffset = box->start();
218     for (box = box->nextTextBox(); box; box = box->nextTextBox())
219         minOffset = std::min<int>(minOffset, box->start());
220     return minOffset;
221 }
222
223 int RenderTextLineBoxes::caretMaxOffset(const RenderText& renderer) const
224 {
225     auto box = m_last;
226     if (!box)
227         return renderer.text().length();
228
229     int maxOffset = box->start() + box->len();
230     for (box = box->prevTextBox(); box; box = box->prevTextBox())
231         maxOffset = std::max<int>(maxOffset, box->start() + box->len());
232     return maxOffset;
233 }
234
235 bool RenderTextLineBoxes::containsOffset(const RenderText& renderer, unsigned offset, OffsetType type) const
236 {
237     for (auto* box = m_first; box; box = box->nextTextBox()) {
238         if (offset < box->start() && !renderer.containsReversedText())
239             return false;
240         unsigned boxEnd = box->start() + box->len();
241         if (offset >= box->start() && offset <= boxEnd) {
242             if (offset == boxEnd && (type == CharacterOffset || box->isLineBreak()))
243                 continue;
244             if (type == CharacterOffset)
245                 return true;
246             // Return false for offsets inside composed characters.
247             return !offset || offset == static_cast<unsigned>(renderer.nextOffset(renderer.previousOffset(offset)));
248         }
249     }
250     return false;
251 }
252
253 unsigned RenderTextLineBoxes::countCharacterOffsetsUntil(unsigned offset) const
254 {
255     unsigned result = 0;
256     for (auto* box = m_first; box; box = box->nextTextBox()) {
257         if (offset < box->start())
258             return result;
259         if (offset <= box->start() + box->len()) {
260             result += offset - box->start();
261             return result;
262         }
263         result += box->len();
264     }
265     return result;
266 }
267
268 enum ShouldAffinityBeDownstream { AlwaysDownstream, AlwaysUpstream, UpstreamIfPositionIsNotAtStart };
269
270 static bool lineDirectionPointFitsInBox(int pointLineDirection, const InlineTextBox& box, ShouldAffinityBeDownstream& shouldAffinityBeDownstream)
271 {
272     shouldAffinityBeDownstream = AlwaysDownstream;
273
274     // the x coordinate is equal to the left edge of this box
275     // the affinity must be downstream so the position doesn't jump back to the previous line
276     // except when box is the first box in the line
277     if (pointLineDirection <= box.logicalLeft()) {
278         shouldAffinityBeDownstream = !box.prevLeafChild() ? UpstreamIfPositionIsNotAtStart : AlwaysDownstream;
279         return true;
280     }
281
282 #if !PLATFORM(IOS_FAMILY)
283     // and the x coordinate is to the left of the right edge of this box
284     // check to see if position goes in this box
285     if (pointLineDirection < box.logicalRight()) {
286         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
287         return true;
288     }
289 #endif
290
291     // box is first on line
292     // and the x coordinate is to the left of the first text box left edge
293     if (!box.prevLeafChildIgnoringLineBreak() && pointLineDirection < box.logicalLeft())
294         return true;
295
296     if (!box.nextLeafChildIgnoringLineBreak()) {
297         // box is last on line
298         // and the x coordinate is to the right of the last text box right edge
299         // generate VisiblePosition, use UPSTREAM affinity if possible
300         shouldAffinityBeDownstream = UpstreamIfPositionIsNotAtStart;
301         return true;
302     }
303
304     return false;
305 }
306
307 static VisiblePosition createVisiblePositionForBox(const InlineBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
308 {
309     EAffinity affinity = VP_DEFAULT_AFFINITY;
310     switch (shouldAffinityBeDownstream) {
311     case AlwaysDownstream:
312         affinity = DOWNSTREAM;
313         break;
314     case AlwaysUpstream:
315         affinity = VP_UPSTREAM_IF_POSSIBLE;
316         break;
317     case UpstreamIfPositionIsNotAtStart:
318         affinity = offset > box.caretMinOffset() ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM;
319         break;
320     }
321     return box.renderer().createVisiblePosition(offset, affinity);
322 }
323
324 static VisiblePosition createVisiblePositionAfterAdjustingOffsetForBiDi(const InlineTextBox& box, int offset, ShouldAffinityBeDownstream shouldAffinityBeDownstream)
325 {
326     ASSERT(offset >= 0);
327
328     if (offset && static_cast<unsigned>(offset) < box.len())
329         return createVisiblePositionForBox(box, box.start() + offset, shouldAffinityBeDownstream);
330
331     bool positionIsAtStartOfBox = !offset;
332     if (positionIsAtStartOfBox == box.isLeftToRightDirection()) {
333         // offset is on the left edge
334
335         const InlineBox* prevBox = box.prevLeafChildIgnoringLineBreak();
336         if ((prevBox && prevBox->bidiLevel() == box.bidiLevel())
337             || box.renderer().containingBlock()->style().direction() == box.direction()) // FIXME: left on 12CBA
338             return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
339
340         if (prevBox && prevBox->bidiLevel() > box.bidiLevel()) {
341             // e.g. left of B in aDC12BAb
342             const InlineBox* leftmostBox;
343             do {
344                 leftmostBox = prevBox;
345                 prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
346             } while (prevBox && prevBox->bidiLevel() > box.bidiLevel());
347             return createVisiblePositionForBox(*leftmostBox, leftmostBox->caretRightmostOffset(), shouldAffinityBeDownstream);
348         }
349
350         if (!prevBox || prevBox->bidiLevel() < box.bidiLevel()) {
351             // e.g. left of D in aDC12BAb
352             const InlineBox* rightmostBox;
353             const InlineBox* nextBox = &box;
354             do {
355                 rightmostBox = nextBox;
356                 nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
357             } while (nextBox && nextBox->bidiLevel() >= box.bidiLevel());
358             return createVisiblePositionForBox(*rightmostBox,
359                 box.isLeftToRightDirection() ? rightmostBox->caretMaxOffset() : rightmostBox->caretMinOffset(), shouldAffinityBeDownstream);
360         }
361
362         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
363     }
364
365     const InlineBox* nextBox = box.nextLeafChildIgnoringLineBreak();
366     if ((nextBox && nextBox->bidiLevel() == box.bidiLevel())
367         || box.renderer().containingBlock()->style().direction() == box.direction())
368         return createVisiblePositionForBox(box, box.caretRightmostOffset(), shouldAffinityBeDownstream);
369
370     // offset is on the right edge
371     if (nextBox && nextBox->bidiLevel() > box.bidiLevel()) {
372         // e.g. right of C in aDC12BAb
373         const InlineBox* rightmostBox;
374         do {
375             rightmostBox = nextBox;
376             nextBox = rightmostBox->nextLeafChildIgnoringLineBreak();
377         } while (nextBox && nextBox->bidiLevel() > box.bidiLevel());
378         return createVisiblePositionForBox(*rightmostBox, rightmostBox->caretLeftmostOffset(), shouldAffinityBeDownstream);
379     }
380
381     if (!nextBox || nextBox->bidiLevel() < box.bidiLevel()) {
382         // e.g. right of A in aDC12BAb
383         const InlineBox* leftmostBox;
384         const InlineBox* prevBox = &box;
385         do {
386             leftmostBox = prevBox;
387             prevBox = leftmostBox->prevLeafChildIgnoringLineBreak();
388         } while (prevBox && prevBox->bidiLevel() >= box.bidiLevel());
389         return createVisiblePositionForBox(*leftmostBox,
390             box.isLeftToRightDirection() ? leftmostBox->caretMinOffset() : leftmostBox->caretMaxOffset(), shouldAffinityBeDownstream);
391     }
392
393     return createVisiblePositionForBox(box, box.caretLeftmostOffset(), shouldAffinityBeDownstream);
394 }
395
396 VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const
397 {
398     if (!m_first || !renderer.text().length())
399         return renderer.createVisiblePosition(0, DOWNSTREAM);
400
401     LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y();
402     LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x();
403     bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode();
404
405     InlineTextBox* lastBox = nullptr;
406     for (auto* box = m_first; box; box = box->nextTextBox()) {
407         if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak())
408             box = box->nextTextBox();
409
410         auto& rootBox = box->root();
411         LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop());
412         if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) {
413             LayoutUnit bottom = rootBox.selectionBottom();
414             if (rootBox.nextRootBox())
415                 bottom = std::min(bottom, rootBox.nextRootBox()->lineTop());
416
417             if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) {
418                 ShouldAffinityBeDownstream shouldAffinityBeDownstream;
419 #if PLATFORM(IOS_FAMILY)
420                 if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) {
421                     int half = box->x() + box->logicalWidth() / 2;
422                     EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE;
423                     return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity);
424                 }
425 #endif
426                 if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream))
427                     return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream);
428             }
429         }
430         lastBox = box;
431     }
432
433     if (lastBox) {
434         ShouldAffinityBeDownstream shouldAffinityBeDownstream;
435         lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream);
436         return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream);
437     }
438     return renderer.createVisiblePosition(0, DOWNSTREAM);
439 }
440
441 void RenderTextLineBoxes::setSelectionState(RenderText& renderer, RenderObject::SelectionState state)
442 {
443     if (state == RenderObject::SelectionInside || state == RenderObject::SelectionNone) {
444         for (auto* box = m_first; box; box = box->nextTextBox())
445             box->root().setHasSelectedChildren(state == RenderObject::SelectionInside);
446         return;
447     }
448
449     auto start = renderer.view().selection().startPosition();
450     auto end = renderer.view().selection().endPosition();
451     if (state == RenderObject::SelectionStart) {
452         end = renderer.text().length();
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 short 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     int ellipsisStartPosition = std::max<int>(start - box.start(), 0);
477     int ellipsisEndPosition = std::min<int>(end - box.start(), box.len());
478     
479     // The ellipsis should be considered to be selected if the end of
480     // the selection is past the beginning of the truncation and the
481     // beginning of the selection is before or at the beginning of the truncation.
482     if (ellipsisEndPosition < truncation && ellipsisStartPosition > truncation)
483         return IntRect();
484     return ellipsis->selectionRect();
485 }
486
487 LayoutRect RenderTextLineBoxes::selectionRectForRange(unsigned start, unsigned end)
488 {
489     LayoutRect rect;
490     for (auto* box = m_first; box; box = box->nextTextBox()) {
491         rect.unite(box->localSelectionRect(start, end));
492         rect.unite(ellipsisRectForBox(*box, start, end));
493     }
494     return rect;
495 }
496
497 void RenderTextLineBoxes::collectSelectionRectsForRange(unsigned start, unsigned end, Vector<LayoutRect>& rects)
498 {
499     for (auto* box = m_first; box; box = box->nextTextBox()) {
500         LayoutRect rect;
501         rect.unite(box->localSelectionRect(start, end));
502         rect.unite(ellipsisRectForBox(*box, start, end));
503         if (!rect.size().isEmpty())
504             rects.append(rect);
505     }
506 }
507
508 Vector<IntRect> RenderTextLineBoxes::absoluteRects(const LayoutPoint& accumulatedOffset) const
509 {
510     Vector<IntRect> rects;
511     for (auto* box = m_first; box; box = box->nextTextBox())
512         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + box->topLeft(), box->size())));
513     return rects;
514 }
515
516 static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight)
517 {
518     unsigned realEnd = std::min(box.end() + 1, end);
519     LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd);
520     if (!boxSelectionRect.height())
521         return FloatRect();
522     if (useSelectionHeight)
523         return boxSelectionRect;
524     // Change the height and y position (or width and x for vertical text)
525     // because selectionRect uses selection-specific values.
526     if (box.isHorizontal()) {
527         boxSelectionRect.setHeight(box.height());
528         boxSelectionRect.setY(box.y());
529     } else {
530         boxSelectionRect.setWidth(box.width());
531         boxSelectionRect.setX(box.x());
532     }
533     return boxSelectionRect;
534 }
535
536 Vector<IntRect> RenderTextLineBoxes::absoluteRectsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
537 {
538     Vector<IntRect> rects;
539     for (auto* box = m_first; box; box = box->nextTextBox()) {
540         // Note: box->end() returns the index of the last character, not the index past it
541         if (start <= box->start() && box->end() < end) {
542             FloatRect boundaries = box->calculateBoundaries();
543             if (useSelectionHeight) {
544                 LayoutRect selectionRect = box->localSelectionRect(start, end);
545                 if (box->isHorizontal()) {
546                     boundaries.setHeight(selectionRect.height());
547                     boundaries.setY(selectionRect.y());
548                 } else {
549                     boundaries.setWidth(selectionRect.width());
550                     boundaries.setX(selectionRect.x());
551                 }
552             }
553             rects.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed).enclosingBoundingBox());
554             continue;
555         }
556         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
557         if (!rect.isZero())
558             rects.append(renderer.localToAbsoluteQuad(rect, UseTransforms, wasFixed).enclosingBoundingBox());
559     }
560     return rects;
561 }
562
563 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuads(const RenderText& renderer, bool* wasFixed, ClippingOption option) const
564 {
565     Vector<FloatQuad> quads;
566     for (auto* box = m_first; box; box = box->nextTextBox()) {
567         FloatRect boundaries = box->calculateBoundaries();
568
569         // Shorten the width of this text box if it ends in an ellipsis.
570         // FIXME: ellipsisRectForBox should switch to return FloatRect soon with the subpixellayout branch.
571         IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(*box, 0, renderer.text().length()) : IntRect();
572         if (!ellipsisRect.isEmpty()) {
573             if (renderer.style().isHorizontalWritingMode())
574                 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
575             else
576                 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
577         }
578         quads.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed));
579     }
580     return quads;
581 }
582
583 Vector<FloatQuad> RenderTextLineBoxes::absoluteQuadsForRange(const RenderText& renderer, unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
584 {
585     Vector<FloatQuad> quads;
586     for (auto* box = m_first; box; box = box->nextTextBox()) {
587         // Note: box->end() returns the index of the last character, not the index past it
588         if (start <= box->start() && box->end() < end) {
589             FloatRect boundaries = box->calculateBoundaries();
590             if (useSelectionHeight) {
591                 LayoutRect selectionRect = box->localSelectionRect(start, end);
592                 if (box->isHorizontal()) {
593                     boundaries.setHeight(selectionRect.height());
594                     boundaries.setY(selectionRect.y());
595                 } else {
596                     boundaries.setWidth(selectionRect.width());
597                     boundaries.setX(selectionRect.x());
598                 }
599             }
600             quads.append(renderer.localToAbsoluteQuad(boundaries, UseTransforms, wasFixed));
601             continue;
602         }
603         FloatRect rect = localQuadForTextBox(*box, start, end, useSelectionHeight);
604         if (!rect.isZero())
605             quads.append(renderer.localToAbsoluteQuad(rect, UseTransforms, wasFixed));
606     }
607     return quads;
608 }
609
610 void RenderTextLineBoxes::dirtyAll()
611 {
612     for (auto* box = m_first; box; box = box->nextTextBox())
613         box->dirtyLineBoxes();
614 }
615
616 bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsigned end, int lengthDelta)
617 {
618     RootInlineBox* firstRootBox = nullptr;
619     RootInlineBox* lastRootBox = nullptr;
620
621     // Dirty all text boxes that include characters in between offset and offset+len.
622     bool dirtiedLines = false;
623     for (auto* current = m_first; current; current = current->nextTextBox()) {
624         // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264
625         // Text run is entirely before the affected range.
626         if (current->end() < start)
627             continue;
628         // Text run is entirely after the affected range.
629         if (current->start() > end) {
630             current->offsetRun(lengthDelta);
631             auto& rootBox = current->root();
632             if (!firstRootBox) {
633                 firstRootBox = &rootBox;
634                 if (!dirtiedLines) {
635                     // The affected area was in between two runs. Mark the root box of the run after the affected area as dirty.
636                     firstRootBox->markDirty();
637                     dirtiedLines = true;
638                 }
639             }
640             lastRootBox = &rootBox;
641             continue;
642         }
643         if (current->end() >= start && current->end() <= end) {
644             // Text run overlaps with the left end of the affected range.
645             current->dirtyLineBoxes();
646             dirtiedLines = true;
647             continue;
648         }
649         if (current->start() <= start && current->end() >= end) {
650             // Text run subsumes the affected range.
651             current->dirtyLineBoxes();
652             dirtiedLines = true;
653             continue;
654         }
655         if (current->start() <= end && current->end() >= end) {
656             // Text run overlaps with right end of the affected range.
657             current->dirtyLineBoxes();
658             dirtiedLines = true;
659             continue;
660         }
661     }
662
663     // Now we have to walk all of the clean lines and adjust their cached line break information
664     // to reflect our updated offsets.
665     if (lastRootBox)
666         lastRootBox = lastRootBox->nextRootBox();
667     if (firstRootBox) {
668         auto previousRootBox = firstRootBox->prevRootBox();
669         if (previousRootBox)
670             firstRootBox = previousRootBox;
671     } else if (m_last) {
672         ASSERT(!lastRootBox);
673         firstRootBox = &m_last->root();
674         firstRootBox->markDirty();
675         dirtiedLines = true;
676     }
677     for (auto* current = firstRootBox; current && current != lastRootBox; current = current->nextRootBox()) {
678         if (current->lineBreakObj() == &renderer && current->lineBreakPos() > end)
679             current->setLineBreakPos(current->lineBreakPos() + lengthDelta);
680     }
681     
682     // If the text node is empty, dirty the line where new text will be inserted.
683     if (!m_first && renderer.parent()) {
684         renderer.parent()->dirtyLinesFromChangedChild(renderer);
685         dirtiedLines = true;
686     }
687     return dirtiedLines;
688 }
689
690 inline void RenderTextLineBoxes::checkConsistency() const
691 {
692 #if !ASSERT_DISABLED
693 #ifdef CHECK_CONSISTENCY
694     const InlineTextBox* prev = nullptr;
695     for (auto* child = m_first; child; child = child->nextTextBox()) {
696         ASSERT(child->renderer() == this);
697         ASSERT(child->prevTextBox() == prev);
698         prev = child;
699     }
700     ASSERT(prev == m_last);
701 #endif
702 #endif
703 }
704
705 #if !ASSERT_DISABLED
706 RenderTextLineBoxes::~RenderTextLineBoxes()
707 {
708     ASSERT(!m_first);
709     ASSERT(!m_last);
710 }
711 #endif
712
713 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
714 void RenderTextLineBoxes::invalidateParentChildLists()
715 {
716     for (auto* box = m_first; box; box = box->nextTextBox())
717         box->invalidateParentChildList();
718 }
719 #endif
720
721 }