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