1 /**
2 * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2003-2007 Apple Computer, Inc.
5  *           (C) 2006-2007 Germain Garand (germain@ebooksfrance.org)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 // -------------------------------------------------------------------------
23 
24 #include "render_line.h"
25 
26 #include "khtml_debug.h"
27 #include <assert.h>
28 #include <QPainter>
29 
30 #include "render_flow.h"
31 #include "render_text.h"
32 #include "render_table.h"
33 #include "render_inline.h"
34 #include "render_block.h"
35 #include "render_arena.h"
36 #include <xml/dom_nodeimpl.h>
37 #include <xml/dom_docimpl.h>
38 #include <html/html_formimpl.h>
39 #include <khtmlview.h>
40 
41 using namespace DOM;
42 using namespace khtml;
43 
44 #ifndef NDEBUG
45 static bool inInlineBoxDetach;
46 #endif
47 
48 class khtml::EllipsisBox : public InlineBox
49 {
50 public:
EllipsisBox(RenderObject * obj,const DOM::DOMString & ellipsisStr,InlineFlowBox * p,int w,int y,int h,int b,bool firstLine,InlineBox * markupBox)51     EllipsisBox(RenderObject *obj, const DOM::DOMString &ellipsisStr, InlineFlowBox *p,
52                 int w, int y, int h, int b, bool firstLine, InlineBox *markupBox)
53         : InlineBox(obj), m_str(ellipsisStr)
54     {
55         m_parent = p;
56         m_width = w;
57         m_y = y;
58         m_height = h;
59         m_baseline = b;
60         m_firstLine = firstLine;
61         m_constructed = true;
62         m_markupBox = markupBox;
63     }
64 
65     void paint(RenderObject::PaintInfo &i, int _tx, int _ty) override;
66     bool nodeAtPoint(RenderObject::NodeInfo &info, int _x, int _y, int _tx, int _ty) override;
67 
68 private:
69     DOM::DOMString m_str;
70     InlineBox *m_markupBox;
71 };
72 
remove()73 void InlineBox::remove()
74 {
75     if (m_parent) {
76         m_parent->removeFromLine(this);
77     }
78 }
79 
detach(RenderArena * renderArena,bool noRemove)80 void InlineBox::detach(RenderArena *renderArena, bool noRemove)
81 {
82     if (!noRemove) {
83         remove();
84     }
85 
86 #ifndef NDEBUG
87     inInlineBoxDetach = true;
88 #endif
89     delete this;
90 #ifndef NDEBUG
91     inInlineBoxDetach = false;
92 #endif
93 
94     // Recover the size left there for us by operator delete and free the memory.
95     renderArena->free(*(size_t *)this, this);
96 }
97 
operator new(size_t sz,RenderArena * renderArena)98 void *InlineBox::operator new(size_t sz, RenderArena *renderArena) throw()
99 {
100     return renderArena->allocate(sz);
101 }
102 
operator delete(void * ptr,size_t sz)103 void InlineBox::operator delete(void *ptr, size_t sz)
104 {
105     assert(inInlineBoxDetach);
106 #ifdef KHTML_USE_ARENA_ALLOCATOR
107     // Stash size where detach can find it.
108     *(size_t *)ptr = sz;
109 #endif
110 }
111 
needsOutlinePhaseRepaint(RenderObject * o,RenderObject::PaintInfo & i,int tx,int ty)112 static bool needsOutlinePhaseRepaint(RenderObject *o, RenderObject::PaintInfo &i, int tx, int ty)
113 {
114     if (o->style()->outlineWidth() <= 0) {
115         return false;
116     }
117     QRect r(tx + o->xPos(), ty + o->yPos(), o->width(), o->height());
118     if (r.intersects(i.r)) {
119         return false;
120     }
121     r.adjust(-o->style()->outlineSize(),
122              -o->style()->outlineSize(),
123              o->style()->outlineSize(),
124              o->style()->outlineSize());
125     if (!r.intersects(i.r)) {
126         return false;
127     }
128     return true;
129 }
130 
paint(RenderObject::PaintInfo & i,int tx,int ty)131 void InlineBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
132 {
133     if (i.phase == PaintActionOutline && !needsOutlinePhaseRepaint(object(), i, tx, ty)) {
134         return;
135     }
136 
137     // Paint all phases of replaced elements atomically, as though the replaced element established its
138     // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
139     // specification.)
140     bool paintSelectionOnly = i.phase == PaintActionSelection;
141     RenderObject::PaintInfo info(i.p, i.r, paintSelectionOnly ? i.phase : PaintActionElementBackground);
142     object()->paint(info, tx, ty);
143     if (!paintSelectionOnly) {
144         info.phase = PaintActionChildBackgrounds;
145         object()->paint(info, tx, ty);
146         info.phase = PaintActionFloat;
147         object()->paint(info, tx, ty);
148         info.phase = PaintActionForeground;
149         object()->paint(info, tx, ty);
150         info.phase = PaintActionOutline;
151         object()->paint(info, tx, ty);
152     }
153 }
154 
nodeAtPoint(RenderObject::NodeInfo & i,int x,int y,int tx,int ty)155 bool InlineBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty)
156 {
157     // Hit test all phases of replaced elements atomically, as though the replaced element established its
158     // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
159     // specification.)
160     bool inside = false;
161     return object()->nodeAtPoint(i, x, y, tx, ty, HitTestAll, inside); // ### port hitTest
162 }
163 
caretMinOffset() const164 long InlineBox::caretMinOffset() const
165 {
166     return 0;
167 }
168 
caretMaxOffset() const169 long InlineBox::caretMaxOffset() const
170 {
171     return 1;
172 }
173 
caretMaxRenderedOffset() const174 unsigned long InlineBox::caretMaxRenderedOffset() const
175 {
176     return 1;
177 }
178 
root()179 RootInlineBox *InlineBox::root()
180 {
181     if (m_parent) {
182         return m_parent->root();
183     }
184     assert(isRootInlineBox());
185     return static_cast<RootInlineBox *>(this);
186 }
187 
~InlineFlowBox()188 InlineFlowBox::~InlineFlowBox()
189 {
190 }
191 
extractLine()192 void InlineFlowBox::extractLine()
193 {
194     if (!m_extracted) {
195         static_cast<RenderFlow *>(m_object)->extractLineBox(this);
196     }
197     for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) {
198         child->extractLine();
199     }
200 }
201 
attachLine()202 void InlineFlowBox::attachLine()
203 {
204     if (m_extracted) {
205         static_cast<RenderFlow *>(m_object)->attachLineBox(this);
206     }
207     for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) {
208         child->attachLine();
209     }
210 }
211 
deleteLine(RenderArena * arena)212 void InlineFlowBox::deleteLine(RenderArena *arena)
213 {
214     InlineBox *child = firstChild();
215     InlineBox *next = nullptr;
216     while (child) {
217         assert(this == child->parent());
218         next = child->nextOnLine();
219 #ifndef NDEBUG
220         child->setParent(nullptr);
221 #endif
222         child->deleteLine(arena);
223         child = next;
224     }
225 #ifndef NDEBUG
226     m_firstChild = nullptr;
227     m_lastChild = nullptr;
228 #endif
229 
230     m_object->removeInlineBox(this);
231     detach(arena, true /*no remove*/);
232 }
233 
removeFromLine(InlineBox * child)234 void InlineFlowBox::removeFromLine(InlineBox *child)
235 {
236     if (!m_dirty) {
237         dirtyInlineBoxes();
238     }
239 
240     root()->childRemoved(child);
241 
242     if (child == m_firstChild) {
243         m_firstChild = child->nextOnLine();
244     }
245     if (child == m_lastChild) {
246         m_lastChild = child->prevOnLine();
247     }
248     if (child->nextOnLine()) {
249         child->nextOnLine()->m_prev = child->prevOnLine();
250     }
251     if (child->prevOnLine()) {
252         child->prevOnLine()->m_next = child->nextOnLine();
253     }
254 
255     child->setParent(nullptr);
256 }
257 
dirtyInlineBoxes()258 void InlineBox::dirtyInlineBoxes()
259 {
260     markDirty();
261     for (InlineFlowBox *curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) {
262         curr->markDirty();
263     }
264 }
265 
deleteLine(RenderArena * arena)266 void InlineBox::deleteLine(RenderArena *arena)
267 {
268     if (!m_extracted && m_object->isBox()) {
269         static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr);
270     }
271     detach(arena, true /*no remove*/);
272 }
273 
extractLine()274 void InlineBox::extractLine()
275 {
276     m_extracted = true;
277     if (m_object->isBox()) {
278         static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr);
279     }
280 }
281 
attachLine()282 void InlineBox::attachLine()
283 {
284     m_extracted = false;
285     if (m_object->isBox()) {
286         static_cast<RenderBox *>(m_object)->setPlaceHolderBox(this);
287     }
288 }
289 
adjustPosition(int dx,int dy)290 void InlineBox::adjustPosition(int dx, int dy)
291 {
292     m_x += dx;
293     m_y += dy;
294     if (m_object->isReplaced() || m_object->isBR()) {
295         m_object->setPos(m_object->xPos() + dx, m_object->yPos() + dy);
296     }
297 }
298 
canAccommodateEllipsisBox(bool ltr,int blockEdge,int ellipsisWidth)299 bool InlineBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth)
300 {
301     // Non-replaced elements can always accommodate an ellipsis.
302     if (!m_object || !m_object->isReplaced()) {
303         return true;
304     }
305 
306     QRect boxRect(m_x, 0, m_width, 10);
307     QRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
308     return !(boxRect.intersects(ellipsisRect));
309 }
310 
placeEllipsisBox(bool,int,int,bool &)311 int InlineBox::placeEllipsisBox(bool /*ltr*/, int /*blockEdge*/, int /*ellipsisWidth*/, bool &)
312 {
313     // Use -1 to mean "we didn't set the position."
314     return -1;
315 }
316 
nextOnLineExists() const317 bool InlineBox::nextOnLineExists() const
318 {
319     if (!parent()) {
320         return false;
321     }
322 
323     if (nextOnLine()) {
324         return true;
325     }
326 
327     return parent()->nextOnLineExists();
328 }
329 
prevOnLineExists() const330 bool InlineBox::prevOnLineExists() const
331 {
332     if (!parent()) {
333         return false;
334     }
335 
336     if (prevOnLine()) {
337         return true;
338     }
339 
340     return parent()->prevOnLineExists();
341 }
342 
firstLeafChild()343 InlineBox *InlineBox::firstLeafChild()
344 {
345     return this;
346 }
347 
lastLeafChild()348 InlineBox *InlineBox::lastLeafChild()
349 {
350     return this;
351 }
352 
closestLeafChildForXPos(int _x,int _tx)353 InlineBox *InlineBox::closestLeafChildForXPos(int _x, int _tx)
354 {
355     if (!isInlineFlowBox()) {
356         return this;
357     }
358 
359     InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(this);
360     if (!flowBox->firstChild()) {
361         return this;
362     }
363 
364     InlineBox *box = flowBox->closestChildForXPos(_x, _tx);
365     if (!box) {
366         return this;
367     }
368 
369     return box->closestLeafChildForXPos(_x, _tx);
370 }
371 
372 #ifdef ENABLE_DUMP
getInlineBoxType(const InlineBox * box)373 static QString getInlineBoxType(const InlineBox *box)
374 {
375     if (box->isInlineTextBox()) {
376         return "Text";
377     }
378     if (box->isRootInlineBox()) {
379         return "RootBox";
380     }
381     if (box->isInlineFlowBox()) {
382         return "FlowBox";
383     }
384     if (box->isPlaceHolderBox()) {
385         return "PlaceHolderBox";
386     }
387     return "InlineBox";
388 }
389 
information() const390 QString InlineBox::information() const
391 {
392     QString result;
393     QTextStream out(&result, QIODevice::WriteOnly);
394     out << getInlineBoxType(this) << "(" << (void *)this << ") "
395         << "Pos" << "(" << xPos() << "," << yPos() << ") "
396         << "Size" << "(" << width() << "," << height() << ") "
397         << "Overflow" << "(" << topOverflow() << "," << bottomOverflow() << ") "
398         << (object() ? object()->renderName() : "NoRenderObject") << "(" << (void *)object() << ") ";
399     if (isInlineTextBox()) {
400         const InlineTextBox *textBox = static_cast<const InlineTextBox *>(this);
401         out << "Text[" << textBox->renderText()->data().substring(textBox->start(), textBox->len()).string() << "]";
402     }
403     return result;
404 }
405 
printTree(int indent) const406 void InlineBox::printTree(int indent) const
407 {
408     QString temp;
409     temp.fill(' ', indent);
410 
411     // qCDebug(KHTML_LOG) << (temp + information());
412     if (isRootInlineBox()) {
413         // const RootInlineBox* root = static_cast<const RootInlineBox*>(this);
414     }
415     if (isInlineTextBox()) {
416         //
417     }
418     if (isInlineFlowBox()) {
419         const InlineFlowBox *flowBox = static_cast<const InlineFlowBox *>(this);
420         for (InlineBox *box = flowBox->firstChild(); box; box = box->nextOnLine()) {
421             box->printTree(indent + 2);
422         }
423     }
424 }
425 #endif
426 
marginLeft() const427 int InlineFlowBox::marginLeft() const
428 {
429     if (!includeLeftEdge()) {
430         return 0;
431     }
432 
433     RenderStyle *cstyle = object()->style();
434     Length margin = cstyle->marginLeft();
435     if (!margin.isAuto()) {
436         return (margin.isFixed() ? margin.value() : object()->marginLeft());
437     }
438     return 0;
439 }
440 
marginRight() const441 int InlineFlowBox::marginRight() const
442 {
443     if (!includeRightEdge()) {
444         return 0;
445     }
446 
447     RenderStyle *cstyle = object()->style();
448     Length margin = cstyle->marginRight();
449     if (!margin.isAuto()) {
450         return (margin.isFixed() ? margin.value() : object()->marginRight());
451     }
452     return 0;
453 }
454 
marginBorderPaddingLeft() const455 int InlineFlowBox::marginBorderPaddingLeft() const
456 {
457     return marginLeft() + borderLeft() + paddingLeft();
458 }
459 
marginBorderPaddingRight() const460 int InlineFlowBox::marginBorderPaddingRight() const
461 {
462     return marginRight() + borderRight() + paddingRight();
463 }
464 
getFlowSpacingWidth() const465 int InlineFlowBox::getFlowSpacingWidth() const
466 {
467     int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight();
468     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
469         if (curr->isInlineFlowBox()) {
470             totWidth += static_cast<InlineFlowBox *>(curr)->getFlowSpacingWidth();
471         }
472     }
473     return totWidth;
474 }
475 
nextOnLineExists()476 bool InlineFlowBox::nextOnLineExists()
477 {
478     if (!parent()) {
479         return false;
480     }
481 
482     if (nextOnLine()) {
483         return true;
484     }
485 
486     return parent()->nextOnLineExists();
487 }
488 
prevOnLineExists()489 bool InlineFlowBox::prevOnLineExists()
490 {
491     if (!parent()) {
492         return false;
493     }
494 
495     if (prevOnLine()) {
496         return true;
497     }
498 
499     return parent()->prevOnLineExists();
500 }
501 
onEndChain(RenderObject * endObject)502 bool InlineFlowBox::onEndChain(RenderObject *endObject)
503 {
504     if (!endObject) {
505         return false;
506     }
507 
508     if (endObject == object()) {
509         return true;
510     }
511 
512     RenderObject *curr = endObject;
513     RenderObject *parent = curr->parent();
514     while (parent && !parent->isRenderBlock()) {
515         if (parent->lastChild() != curr || parent == object()) {
516             return false;
517         }
518 
519         curr = parent;
520         parent = curr->parent();
521     }
522 
523     return true;
524 }
525 
determineSpacingForFlowBoxes(bool lastLine,RenderObject * endObject)526 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject *endObject)
527 {
528     // All boxes start off open.  They will not apply any margins/border/padding on
529     // any side.
530     bool includeLeftEdge = false;
531     bool includeRightEdge = false;
532 
533     RenderFlow *flow = static_cast<RenderFlow *>(object());
534 
535     // The root inline box never has borders/margins/padding.
536     if (parent()) {
537         bool ltr = flow->style()->direction() == LTR;
538 
539         // Check to see if all initial lines are unconstructed.  If so, then
540         // we know the inline began on this line.
541         if (!flow->firstLineBox()->isConstructed() && !object()->isInlineContinuation()) {
542             if (ltr && flow->firstLineBox() == this) {
543                 includeLeftEdge = true;
544             } else if (!ltr && flow->lastLineBox() == this) {
545                 includeRightEdge = true;
546             }
547         }
548 
549         // In order to determine if the inline ends on this line, we check three things:
550         // (1) If we are the last line and we don't have a continuation(), then we can
551         // close up.
552         // (2) If the last line box for the flow has an object following it on the line (ltr,
553         // reverse for rtl), then the inline has closed.
554         // (3) The line may end on the inline.  If we are the last child (climbing up
555         // the end object's chain), then we just closed as well.
556         if (!flow->lastLineBox()->isConstructed()) {
557             if (ltr) {
558                 if (!nextLineBox() &&
559                         ((lastLine && !object()->continuation()) || nextOnLineExists()
560                          || onEndChain(endObject))) {
561                     includeRightEdge = true;
562                 }
563             } else {
564                 if ((!prevLineBox() || prevLineBox()->isConstructed()) &&
565                         ((lastLine && !object()->continuation()) ||
566                          prevOnLineExists() || onEndChain(endObject))) {
567                     includeLeftEdge = true;
568                 }
569             }
570 
571         }
572     }
573 
574     setEdges(includeLeftEdge, includeRightEdge);
575 
576     // Recur into our children.
577     for (InlineBox *currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) {
578         if (currChild->isInlineFlowBox()) {
579             InlineFlowBox *currFlow = static_cast<InlineFlowBox *>(currChild);
580             currFlow->determineSpacingForFlowBoxes(lastLine, endObject);
581         }
582     }
583 }
584 
placeBoxesHorizontally(int x)585 int InlineFlowBox::placeBoxesHorizontally(int x)
586 {
587     // Set our x position.
588     setXPos(x);
589 
590     int startX = x;
591     x += borderLeft() + paddingLeft();
592 
593     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
594         if (curr->object()->isText()) {
595             InlineTextBox *text = static_cast<InlineTextBox *>(curr);
596             text->setXPos(x);
597             x += curr->width();
598         } else {
599             if (curr->object()->isPositioned()) {
600                 if (curr->object()->parent()->style()->direction() == LTR) {
601                     curr->setXPos(x);
602                 } else {
603                     // Our offset that we cache needs to be from the edge of the right border box and
604                     // not the left border box.  We have to subtract |x| from the width of the block
605                     // (which can be obtained by walking up to the root line box).
606                     InlineBox *root = this;
607                     while (!root->isRootInlineBox()) {
608                         root = root->parent();
609                     }
610                     curr->setXPos(root->object()->width() - x);
611                 }
612                 continue; // The positioned object has no effect on the width.
613             }
614             if (curr->object()->isInlineFlow()) {
615                 InlineFlowBox *flow = static_cast<InlineFlowBox *>(curr);
616                 x += flow->marginLeft();
617                 x = flow->placeBoxesHorizontally(x);
618                 x += flow->marginRight();
619             } else {
620                 x += curr->object()->marginLeft();
621                 curr->setXPos(x);
622                 x += curr->width() + curr->object()->marginRight();
623             }
624         }
625     }
626 
627     x += borderRight() + paddingRight();
628     setWidth(x - startX);
629     return x;
630 }
631 
verticallyAlignBoxes(int & heightOfBlock)632 void InlineFlowBox::verticallyAlignBoxes(int &heightOfBlock)
633 {
634     int maxPositionTop = 0;
635     int maxPositionBottom = 0;
636     int maxAscent = 0;
637     int maxDescent = 0;
638 
639     // Figure out if we're in strict mode.
640     RenderObject *curr = object();
641     while (curr && !curr->element()) {
642         curr = curr->container();
643     }
644     bool strictMode = (curr && curr->document()->inStrictMode());
645 
646     computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
647 
648     if (maxAscent + maxDescent < qMax(maxPositionTop, maxPositionBottom)) {
649         adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
650     }
651 
652     int maxHeight = maxAscent + maxDescent;
653     int topPosition = heightOfBlock;
654     int bottomPosition = heightOfBlock;
655     placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition);
656 
657     setOverflowPositions(topPosition, bottomPosition);
658 
659     // Shrink boxes with no text children in quirks and almost strict mode.
660     if (!strictMode) {
661         shrinkBoxesWithNoTextChildren(topPosition, bottomPosition);
662     }
663 
664     heightOfBlock += maxHeight;
665 }
666 
adjustMaxAscentAndDescent(int & maxAscent,int & maxDescent,int maxPositionTop,int maxPositionBottom)667 void InlineFlowBox::adjustMaxAscentAndDescent(int &maxAscent, int &maxDescent,
668         int maxPositionTop, int maxPositionBottom)
669 {
670     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
671         // The computed lineheight needs to be extended for the
672         // positioned elements
673         // see khtmltests/rendering/html_align.html
674 
675         if (curr->object()->isPositioned()) {
676             continue;    // Positioned placeholders don't affect calculations.
677         }
678         if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) {
679             if (curr->yPos() == PositionTop) {
680                 if (maxAscent + maxDescent < curr->height()) {
681                     maxDescent = curr->height() - maxAscent;
682                 }
683             } else {
684                 if (maxAscent + maxDescent < curr->height()) {
685                     maxAscent = curr->height() - maxDescent;
686                 }
687             }
688 
689             if (maxAscent + maxDescent >= qMax(maxPositionTop, maxPositionBottom)) {
690                 break;
691             }
692         }
693 
694         if (curr->isInlineFlowBox()) {
695             static_cast<InlineFlowBox *>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
696         }
697     }
698 }
699 
computeLogicalBoxHeights(int & maxPositionTop,int & maxPositionBottom,int & maxAscent,int & maxDescent,bool strictMode)700 void InlineFlowBox::computeLogicalBoxHeights(int &maxPositionTop, int &maxPositionBottom,
701         int &maxAscent, int &maxDescent, bool strictMode)
702 {
703     if (isRootInlineBox()) {
704         // Examine our root box.
705         setHeight(object()->lineHeight(m_firstLine));
706         bool isTableCell = object()->isTableCell();
707         if (isTableCell) {
708             RenderTableCell *tableCell = static_cast<RenderTableCell *>(object());
709             setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine));
710         } else {
711             setBaseline(object()->baselinePosition(m_firstLine));
712         }
713         if (hasTextChildren() || strictMode) {
714             int ascent = baseline();
715             int descent = height() - ascent;
716             if (maxAscent < ascent) {
717                 maxAscent = ascent;
718             }
719             if (maxDescent < descent) {
720                 maxDescent = descent;
721             }
722         }
723     }
724 
725     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
726         if (curr->object()->isPositioned()) {
727             continue;    // Positioned placeholders don't affect calculations.
728         }
729 
730         curr->setHeight(curr->object()->lineHeight(m_firstLine));
731         curr->setBaseline(curr->object()->baselinePosition(m_firstLine));
732         curr->setYPos(curr->object()->verticalPositionHint(m_firstLine));
733         if (curr->yPos() == PositionTop) {
734             if (maxPositionTop < curr->height()) {
735                 maxPositionTop = curr->height();
736             }
737         } else if (curr->yPos() == PositionBottom) {
738             if (maxPositionBottom < curr->height()) {
739                 maxPositionBottom = curr->height();
740             }
741         } else if (curr->hasTextChildren() || strictMode) {
742             int ascent = curr->baseline() - curr->yPos();
743             int descent = curr->height() - ascent;
744             if (maxAscent < ascent) {
745                 maxAscent = ascent;
746             }
747             if (maxDescent < descent) {
748                 maxDescent = descent;
749             }
750         }
751 
752         if (curr->isInlineFlowBox()) {
753             static_cast<InlineFlowBox *>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
754         }
755     }
756 }
757 
placeBoxesVertically(int y,int maxHeight,int maxAscent,bool strictMode,int & topPosition,int & bottomPosition)758 void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode,
759         int &topPosition, int &bottomPosition)
760 {
761     if (isRootInlineBox()) {
762         setYPos(y + maxAscent - baseline());// Place our root box.
763         // CSS2: 10.8.1 - line-height on the block level element specifies the *minimum*
764         // height of the generated line box
765         if (hasTextChildren() && maxHeight < object()->lineHeight(m_firstLine)) {
766             maxHeight = object()->lineHeight(m_firstLine);
767         }
768     }
769 
770     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
771         if (curr->object()->isPositioned()) {
772             continue;    // Positioned placeholders don't affect calculations.
773         }
774 
775         // Adjust boxes to use their real box y/height and not the logical height (as dictated by
776         // line-height).
777         if (curr->isInlineFlowBox())
778             static_cast<InlineFlowBox *>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode,
779                     topPosition, bottomPosition);
780 
781         bool childAffectsTopBottomPos = true;
782 
783         if (curr->yPos() == PositionTop) {
784             curr->setYPos(y);
785         } else if (curr->yPos() == PositionBottom) {
786             curr->setYPos(y + maxHeight - curr->height());
787         } else {
788             if (!strictMode && !curr->hasTextDescendant()) {
789                 childAffectsTopBottomPos = false;
790             }
791             curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline());
792         }
793         int newY = curr->yPos();
794         int newHeight = curr->height();
795         int newBaseline = curr->baseline();
796         int overflowTop = 0;
797         int overflowBottom = 0;
798         if (curr->isInlineTextBox() || curr->isInlineFlowBox()) {
799             const QFontMetrics &fm = curr->object()->fontMetrics(m_firstLine);
800 #ifdef APPLE_CHANGES
801             newBaseline = fm.ascent();
802             newY += curr->baseline() - newBaseline;
803             newHeight = newBaseline + fm.descent();
804 #else
805             // only adjust if the leading delta is superior to the font's natural leading
806             if (qAbs(fm.ascent() - curr->baseline()) > fm.leading() / 2) {
807                 int ascent = fm.ascent() + fm.leading() / 2;
808                 newBaseline = ascent;
809                 newY += curr->baseline() - newBaseline;
810                 newHeight = fm.lineSpacing();
811             }
812 #endif
813             for (ShadowData *shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) {
814                 overflowTop = qMin(overflowTop, shadow->y - shadow->blur);
815                 overflowBottom = qMax(overflowBottom, shadow->y + shadow->blur);
816             }
817             if (curr->isInlineFlowBox()) {
818                 newHeight += curr->object()->borderTop() + curr->object()->paddingTop() +
819                              curr->object()->borderBottom() + curr->object()->paddingBottom();
820                 newY -= curr->object()->borderTop() + curr->object()->paddingTop();
821                 newBaseline += curr->object()->borderTop() + curr->object()->paddingTop();
822             }
823         } else {
824             newY += curr->object()->marginTop();
825             newHeight = curr->height() - (curr->object()->marginTop() + curr->object()->marginBottom());
826             overflowTop = curr->object()->overflowTop();
827             overflowBottom = curr->object()->overflowHeight() - newHeight;
828         }
829         curr->setYPos(newY);
830         curr->setHeight(newHeight);
831         curr->setBaseline(newBaseline);
832 
833         if (childAffectsTopBottomPos) {
834             topPosition = qMin(topPosition, newY + overflowTop);
835             bottomPosition = qMax(bottomPosition, newY + newHeight + overflowBottom);
836         }
837     }
838 
839     if (isRootInlineBox()) {
840         const QFontMetrics &fm = object()->fontMetrics(m_firstLine);
841 #ifdef APPLE_CHANGES
842         setHeight(fm.ascent() + fm.descent());
843         setYPos(yPos() + baseline() - fm.ascent());
844         setBaseline(fm.ascent());
845 #else
846         if (qAbs(fm.ascent() - baseline()) > fm.leading() / 2) {
847             int ascent = fm.ascent() + fm.leading() / 2;
848             setHeight(fm.lineSpacing());
849             setYPos(yPos() + baseline() - ascent);
850             setBaseline(ascent);
851         }
852 #endif
853         if (hasTextDescendant() || strictMode) {
854             if (yPos() < topPosition) {
855                 topPosition = yPos();
856             }
857             if (yPos() + height() > bottomPosition) {
858                 bottomPosition = yPos() + height();
859             }
860         }
861     }
862 }
863 
shrinkBoxesWithNoTextChildren(int topPos,int bottomPos)864 void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos)
865 {
866     // First shrink our kids.
867     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
868         if (curr->object()->isPositioned()) {
869             continue;    // Positioned placeholders don't affect calculations.
870         }
871 
872         if (curr->isInlineFlowBox()) {
873             static_cast<InlineFlowBox *>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos);
874         }
875     }
876 
877     // See if we have text children. If not, then we need to shrink ourselves to fit on the line.
878     if (!hasTextDescendant()) {
879         if (yPos() < topPos) {
880             setYPos(topPos);
881         }
882         if (yPos() + height() > bottomPos) {
883             setHeight(bottomPos - yPos());
884         }
885         if (baseline() > height()) {
886             setBaseline(height());
887         }
888     }
889 }
890 
nodeAtPoint(RenderObject::NodeInfo & i,int x,int y,int tx,int ty)891 bool InlineFlowBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty)
892 {
893     // Check children first.
894     for (InlineBox *curr = lastChild(); curr; curr = curr->prevOnLine()) {
895         if (!curr->object()->layer() && curr->nodeAtPoint(i, x, y, tx, ty)) {
896             object()->setInnerNode(i);
897             return true;
898         }
899     }
900 
901     // Now check ourselves.
902     QRect rect(tx + m_x, ty + m_y, m_width, m_height);
903     if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
904         object()->setInnerNode(i);
905         return true;
906     }
907 
908     return false;
909 }
910 
paint(RenderObject::PaintInfo & i,int tx,int ty)911 void InlineFlowBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
912 {
913     bool intersectsDamageRect = true;
914     int xPos = tx + m_x - object()->maximalOutlineSize(i.phase);
915     int w = width() + 2 * object()->maximalOutlineSize(i.phase);
916     if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x())) {
917         intersectsDamageRect = false;
918     }
919 
920     if (intersectsDamageRect) {
921         if (i.phase == PaintActionOutline) {
922             // Add ourselves to the paint info struct's list of inlines that need to paint their
923             // outlines.
924             if (object()->style()->visibility() == VISIBLE && object()->style()->outlineWidth() > 0 &&
925                     !object()->isInlineContinuation() && !isRootInlineBox()) {
926                 if (!i.outlineObjects) {
927                     i.outlineObjects = new QList<RenderFlow *>;
928                 }
929                 i.outlineObjects->append(static_cast<RenderFlow *>(object()));
930             }
931         } else {
932             // 1. Paint our background and border.
933             paintBackgroundAndBorder(i, tx, ty);
934 
935             // 2. Paint our underline and overline.
936             paintDecorations(i, tx, ty, false);
937         }
938     }
939 
940     // 3. Paint our children.
941     for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
942         if (!curr->object()->layer()) {
943             curr->paint(i, tx, ty);
944         }
945     }
946 
947     // 4. Paint our strike-through
948     if (intersectsDamageRect && i.phase != PaintActionOutline) {
949         paintDecorations(i, tx, ty, true);
950     }
951 }
952 
paintAllBackgrounds(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int h)953 void InlineFlowBox::paintAllBackgrounds(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer,
954                                         QRect clipr, int _tx, int _ty, int w, int h)
955 {
956     if (!bgLayer) {
957         return;
958     }
959     paintAllBackgrounds(p, c, bgLayer->next(), clipr, _tx, _ty, w, h);
960     paintOneBackground(p, c, bgLayer, clipr, _tx, _ty, w, h);
961 }
962 
paintOneBackground(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int h)963 void InlineFlowBox::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer,
964                                        QRect clipr, int _tx, int _ty, int w, int h)
965 {
966     CachedImage *bg = bgLayer->backgroundImage();
967     bool hasBackgroundImage = bg && bg->isComplete() &&
968                               !bg->isTransparent() && !bg->isErrorImage();
969     if (!hasBackgroundImage || (!prevLineBox() && !nextLineBox()) || !parent())
970         object()->paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(),
971                                           object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom());
972     else {
973         // We have a background image that spans multiple lines.
974         // We need to adjust _tx and _ty by the width of all previous lines.
975         // Think of background painting on inlines as though you had one long line, a single continuous
976         // strip.  Even though that strip has been broken up across multiple lines, you still paint it
977         // as though you had one single line.  This means each line has to pick up the background where
978         // the previous line left off.
979         // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
980         // but it isn't even clear how this should work at all.
981         int xOffsetOnLine = 0;
982         for (InlineRunBox *curr = prevLineBox(); curr; curr = curr->prevLineBox()) {
983             xOffsetOnLine += curr->width();
984         }
985         int startX = _tx - xOffsetOnLine;
986         int totalWidth = xOffsetOnLine;
987         for (InlineRunBox *curr = this; curr; curr = curr->nextLineBox()) {
988             totalWidth += curr->width();
989         }
990         p->save();
991         p->setClipRect(QRect(_tx, _ty, width(), height()));
992         object()->paintBackgroundExtended(p, c, bgLayer, clipr, startX, _ty,
993                                           totalWidth, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(),
994                                           object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom());
995         p->restore();
996     }
997 }
998 
paintBackgroundAndBorder(RenderObject::PaintInfo & pI,int _tx,int _ty)999 void InlineFlowBox::paintBackgroundAndBorder(RenderObject::PaintInfo &pI, int _tx, int _ty)
1000 {
1001     if (object()->style()->visibility() != VISIBLE || pI.phase != PaintActionForeground) {
1002         return;
1003     }
1004 
1005     // Move x/y to our coordinates.
1006     _tx += m_x;
1007     _ty += m_y;
1008 
1009     int w = width();
1010     int h = height();
1011 
1012     QRect cr;
1013     cr.setX(qMax(_tx, pI.r.x()));
1014     cr.setY(qMax(_ty, pI.r.y()));
1015     cr.setWidth(_tx < pI.r.x() ? qMax(0, w - (pI.r.x() - _tx)) : qMin(pI.r.width(), w));
1016     cr.setHeight(_ty < pI.r.y() ? qMax(0, h - (pI.r.y() - _ty)) : qMin(pI.r.height(), h));
1017 
1018     // You can use p::first-line to specify a background. If so, the root line boxes for
1019     // a line may actually have to paint a background.
1020     RenderStyle *styleToUse = object()->style(m_firstLine);
1021     if ((!parent() && m_firstLine && styleToUse != object()->style()) ||
1022             (parent() && object()->shouldPaintBackgroundOrBorder())) {
1023         QColor c = styleToUse->backgroundColor();
1024         paintAllBackgrounds(pI.p, c, styleToUse->backgroundLayers(), cr, _tx, _ty, w, h);
1025 
1026         // ::first-line cannot be used to put borders on a line. Always paint borders with our
1027         // non-first-line style.
1028         if (parent() && object()->style()->hasBorder()) {
1029             object()->paintBorder(pI.p, _tx, _ty, w, h, object()->style(), includeLeftEdge(), includeRightEdge());
1030         }
1031     }
1032 }
1033 
shouldDrawDecoration(RenderObject * obj)1034 static bool shouldDrawDecoration(RenderObject *obj)
1035 {
1036     bool shouldDraw = false;
1037     for (RenderObject *curr = obj->firstChild();
1038             curr; curr = curr->nextSibling()) {
1039         if (curr->isInlineFlow()) {
1040             shouldDraw = true;
1041             break;
1042         } else if (curr->isText() && !curr->isBR() && (curr->style()->preserveWS() ||
1043                    !curr->element() || !curr->element()->containsOnlyWhitespace())) {
1044             shouldDraw = true;
1045             break;
1046         }
1047     }
1048     return shouldDraw;
1049 }
1050 
paintDecorations(RenderObject::PaintInfo & pI,int _tx,int _ty,bool paintedChildren)1051 void InlineFlowBox::paintDecorations(RenderObject::PaintInfo &pI, int _tx, int _ty, bool paintedChildren)
1052 {
1053     // Now paint our text decorations. We only do this if we aren't in quirks mode (i.e., in
1054     // almost-strict mode or strict mode).
1055     if (object()->style()->htmlHacks() || object()->style()->visibility() != VISIBLE) {
1056         return;
1057     }
1058 
1059     _tx += m_x;
1060     _ty += m_y;
1061     RenderStyle *styleToUse = object()->style(m_firstLine);
1062     int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect();
1063     if (deco != TDNONE &&
1064             ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) &&
1065             shouldDrawDecoration(object())) {
1066         // We must have child boxes and have decorations defined.
1067         _tx += borderLeft() + paddingLeft();
1068         int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight());
1069         if (!w) {
1070             return;
1071         }
1072         const QFontMetrics &fm = object()->fontMetrics(m_firstLine);
1073         // thick lines on small fonts look ugly
1074         int thickness = fm.height() > 20 ? fm.lineWidth() : 1;
1075         QColor underline, overline, linethrough;
1076         underline = overline = linethrough = styleToUse->color();
1077         if (!parent()) {
1078             object()->getTextDecorationColors(deco, underline, overline, linethrough);
1079         }
1080 
1081         if (styleToUse->font() != pI.p->font()) {
1082             pI.p->setFont(styleToUse->font());
1083         }
1084 
1085         if (deco & UNDERLINE && !paintedChildren) {
1086             int underlineOffset = (fm.height() + m_baseline) / 2;
1087             if (underlineOffset <= m_baseline) {
1088                 underlineOffset = m_baseline + 1;
1089             }
1090 
1091             pI.p->fillRect(_tx, _ty + underlineOffset, w, thickness, underline);
1092         }
1093         if (deco & OVERLINE && !paintedChildren) {
1094             pI.p->fillRect(_tx, _ty, w, thickness, overline);
1095         }
1096         if (deco & LINE_THROUGH && paintedChildren) {
1097             pI.p->fillRect(_tx, _ty + 2 * m_baseline / 3, w, thickness, linethrough);
1098         }
1099     }
1100 }
1101 
canAccommodateEllipsisBox(bool ltr,int blockEdge,int ellipsisWidth)1102 bool InlineFlowBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth)
1103 {
1104     for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1105         if (!box->canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth)) {
1106             return false;
1107         }
1108     }
1109     return true;
1110 }
1111 
placeEllipsisBox(bool ltr,int blockEdge,int ellipsisWidth,bool & foundBox)1112 int InlineFlowBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool &foundBox)
1113 {
1114     int result = -1;
1115     for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1116         int currResult = box->placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
1117         if (currResult != -1 && result == -1) {
1118             result = currResult;
1119         }
1120     }
1121     return result;
1122 }
1123 
clearTruncation()1124 void InlineFlowBox::clearTruncation()
1125 {
1126     for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1127         box->clearTruncation();
1128     }
1129 }
1130 
paint(RenderObject::PaintInfo & i,int _tx,int _ty)1131 void EllipsisBox::paint(RenderObject::PaintInfo &i, int _tx, int _ty)
1132 {
1133     QPainter *p = i.p;
1134     RenderStyle *_style = m_firstLine ? m_object->style(true) : m_object->style();
1135     if (_style->font() != p->font()) {
1136         p->setFont(_style->font());
1137     }
1138 
1139     const Font *font = &_style->htmlFont();
1140     QColor textColor = _style->color();
1141     if (textColor != p->pen().color()) {
1142         p->setPen(textColor);
1143     }
1144     /*
1145     bool setShadow = false;
1146     if (_style->textShadow()) {
1147         p->setShadow(_style->textShadow()->x, _style->textShadow()->y,
1148                      _style->textShadow()->blur, _style->textShadow()->color);
1149         setShadow = true;
1150     }*/
1151 
1152     const DOMString &str = m_str.string();
1153     font->drawText(p, m_x + _tx,
1154                    m_y + _ty + m_baseline,
1155                    (str.implementation())->s,
1156                    str.length(), 0, str.length(),
1157                    0,
1158                    Qt::LeftToRight, _style->visuallyOrdered());
1159 
1160     /*
1161     if (setShadow)
1162         p->clearShadow();
1163     */
1164 
1165     if (m_markupBox) {
1166         // Paint the markup box
1167         _tx += m_x + m_width - m_markupBox->xPos();
1168         _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
1169         m_markupBox->object()->paint(i, _tx, _ty);
1170     }
1171 }
1172 
nodeAtPoint(RenderObject::NodeInfo & info,int _x,int _y,int _tx,int _ty)1173 bool EllipsisBox::nodeAtPoint(RenderObject::NodeInfo &info, int _x, int _y, int _tx, int _ty)
1174 {
1175     // Hit test the markup box.
1176     if (m_markupBox) {
1177         _tx += m_x + m_width - m_markupBox->xPos();
1178         _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
1179         if (m_markupBox->nodeAtPoint(info, _x, _y, _tx, _ty)) {
1180             object()->setInnerNode(info);
1181             return true;
1182         }
1183     }
1184 
1185     QRect rect(_tx + m_x, _ty + m_y, m_width, m_height);
1186     if (object()->style()->visibility() == VISIBLE && rect.contains(_x, _y)) {
1187         object()->setInnerNode(info);
1188         return true;
1189     }
1190     return false;
1191 }
1192 
detach(RenderArena * arena,bool noRemove)1193 void RootInlineBox::detach(RenderArena *arena, bool noRemove)
1194 {
1195     if (m_lineBreakContext) {
1196         m_lineBreakContext->deref();
1197     }
1198     m_lineBreakContext = nullptr;
1199     detachEllipsisBox(arena);
1200     InlineFlowBox::detach(arena, noRemove);
1201 
1202 }
1203 
detachEllipsisBox(RenderArena * arena)1204 void RootInlineBox::detachEllipsisBox(RenderArena *arena)
1205 {
1206     if (m_ellipsisBox) {
1207         m_ellipsisBox->detach(arena);
1208         m_ellipsisBox = nullptr;
1209     }
1210 }
1211 
clearTruncation()1212 void RootInlineBox::clearTruncation()
1213 {
1214     if (m_ellipsisBox) {
1215         detachEllipsisBox(m_object->renderArena());
1216         InlineFlowBox::clearTruncation();
1217     }
1218 }
1219 
canAccommodateEllipsis(bool ltr,int blockEdge,int lineBoxEdge,int ellipsisWidth)1220 bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
1221 {
1222     // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room.
1223     int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
1224     if (width() - delta < ellipsisWidth) {
1225         return false;
1226     }
1227 
1228     // Next iterate over all the line boxes on the line.  If we find a replaced element that intersects
1229     // then we refuse to accommodate the ellipsis.  Otherwise we're ok.
1230     return InlineFlowBox::canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth);
1231 }
1232 
placeEllipsis(const DOMString & ellipsisStr,bool ltr,int blockEdge,int ellipsisWidth,InlineBox * markupBox)1233 void RootInlineBox::placeEllipsis(const DOMString &ellipsisStr,  bool ltr, int blockEdge, int ellipsisWidth, InlineBox *markupBox)
1234 {
1235     // Create an ellipsis box.
1236     m_ellipsisBox = new(m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this,
1237             ellipsisWidth - (markupBox ? markupBox->width() : 0),
1238             yPos(), height(), baseline(), !prevRootBox(),
1239             markupBox);
1240 
1241     if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) {
1242         m_ellipsisBox->m_x = xPos() + width();
1243         return;
1244     }
1245 
1246     // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
1247     // of that glyph.  Mark all of the objects that intersect the ellipsis box as not painting (as being
1248     // truncated).
1249     bool foundBox = false;
1250     m_ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
1251 }
1252 
placeEllipsisBox(bool ltr,int blockEdge,int ellipsisWidth,bool & foundBox)1253 int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool &foundBox)
1254 {
1255     int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox);
1256     if (result == -1) {
1257         result = ltr ? blockEdge - ellipsisWidth : blockEdge;
1258     }
1259     return result;
1260 }
1261 
paintEllipsisBox(RenderObject::PaintInfo & i,int _tx,int _ty) const1262 void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo &i, int _tx, int _ty) const
1263 {
1264     if (m_ellipsisBox) {
1265         m_ellipsisBox->paint(i, _tx, _ty);
1266     }
1267 }
1268 
paint(RenderObject::PaintInfo & i,int tx,int ty)1269 void RootInlineBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
1270 {
1271     InlineFlowBox::paint(i, tx, ty);
1272     paintEllipsisBox(i, tx, ty);
1273 }
1274 
nodeAtPoint(RenderObject::NodeInfo & i,int x,int y,int tx,int ty)1275 bool RootInlineBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty)
1276 {
1277     if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) {
1278         if (m_ellipsisBox->nodeAtPoint(i, x, y, tx, ty)) {
1279             object()->setInnerNode(i);
1280             return true;
1281         }
1282     }
1283     return InlineFlowBox::nodeAtPoint(i, x, y, tx, ty);
1284 }
1285 
lineBreakBidiStatus() const1286 BidiStatus RootInlineBox::lineBreakBidiStatus() const
1287 {
1288     BidiStatus st;
1289     st.eor = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusEor);
1290     st.last = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusLast);
1291     st.lastStrong = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusLastStrong);
1292     return st;
1293 }
1294 
childRemoved(InlineBox * box)1295 void RootInlineBox::childRemoved(InlineBox *box)
1296 {
1297     if (box->object() == m_lineBreakObj) {
1298         setLineBreakInfo(nullptr, 0, BidiStatus(), nullptr);
1299     }
1300 
1301     for (RootInlineBox *prev = prevRootBox(); prev && prev->lineBreakObj() == box->object(); prev = prev->prevRootBox()) {
1302         prev->setLineBreakInfo(nullptr, 0, BidiStatus(), nullptr);
1303         prev->markDirty();
1304     }
1305 }
1306 
setLineBreakInfo(RenderObject * obj,unsigned breakPos,const BidiStatus & status,BidiContext * context)1307 void RootInlineBox::setLineBreakInfo(RenderObject *obj, unsigned breakPos, const BidiStatus &status, BidiContext *context)
1308 {
1309     m_lineBreakObj = obj;
1310     m_lineBreakPos = breakPos;
1311     m_lineBreakBidiStatusEor = status.eor;
1312     m_lineBreakBidiStatusLastStrong = status.lastStrong;
1313     m_lineBreakBidiStatusLast = status.last;
1314     if (m_lineBreakContext) {
1315         m_lineBreakContext->deref();
1316     }
1317     m_lineBreakContext = context;
1318     if (m_lineBreakContext) {
1319         m_lineBreakContext->ref();
1320     }
1321 }
1322 
firstLeafChild()1323 InlineBox *InlineFlowBox::firstLeafChild()
1324 {
1325     InlineBox *box = firstChild();
1326     while (box) {
1327         InlineBox *next = nullptr;
1328         if (!box->isInlineFlowBox()) {
1329             break;
1330         }
1331         next = static_cast<InlineFlowBox *>(box)->firstChild();
1332         if (!next) {
1333             break;
1334         }
1335         box = next;
1336     }
1337     return box;
1338 }
1339 
lastLeafChild()1340 InlineBox *InlineFlowBox::lastLeafChild()
1341 {
1342     InlineBox *box = lastChild();
1343     while (box) {
1344         InlineBox *next = nullptr;
1345         if (!box->isInlineFlowBox()) {
1346             break;
1347         }
1348         next = static_cast<InlineFlowBox *>(box)->lastChild();
1349         if (!next) {
1350             break;
1351         }
1352         box = next;
1353     }
1354     return box;
1355 }
1356 
closestChildForXPos(int _x,int _tx)1357 InlineBox *InlineFlowBox::closestChildForXPos(int _x, int _tx)
1358 {
1359     if (_x < _tx + firstChild()->m_x)
1360         // if the x coordinate is to the left of the first child
1361     {
1362         return firstChild();
1363     } else if (_x >= _tx + lastChild()->m_x + lastChild()->m_width)
1364         // if the x coordinate is to the right of the last child
1365     {
1366         return lastChild();
1367     } else
1368         // look for the closest child;
1369         // check only the right edges, since the left edge of the first
1370         // box has already been checked
1371         for (InlineBox *box = firstChild(); box; box = box->nextOnLine())
1372             if (_x < _tx + box->m_x + box->m_width) {
1373                 return box;
1374             }
1375 
1376     return nullptr;
1377 }
1378 
1379