File indexing completed on 2024-04-28 15:24:03

0001 /*
0002  * This file is part of the render object implementation for KHTML.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2002-2003 Dirk Mueller (mueller@kde.org)
0007  *           (C) 2003 Apple Computer, Inc.
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  *
0024  */
0025 
0026 #include "render_inline.h"
0027 
0028 #include "render_arena.h"
0029 #include "render_block.h"
0030 #include "rendering/render_position.h"
0031 
0032 #include <xml/dom_docimpl.h>
0033 
0034 using namespace khtml;
0035 
0036 void RenderInline::setStyle(RenderStyle *_style)
0037 {
0038     RenderFlow::setStyle(_style);
0039     setInline(true);
0040 
0041     // Ensure that all of the split inlines pick up the new style. We
0042     // only do this if we're an inline, since we don't want to propagate
0043     // a block's style to the other inlines.
0044     // e.g., <font>foo <h4>goo</h4> moo</font>.  The <font> inlines before
0045     // and after the block share the same style, but the block doesn't
0046     // need to pass its style on to anyone else.
0047     RenderFlow *currCont = continuation();
0048     while (currCont) {
0049         if (currCont->isInline()) {
0050             RenderFlow *nextCont = currCont->continuation();
0051             currCont->setContinuation(nullptr);
0052             currCont->setStyle(style());
0053             currCont->setContinuation(nextCont);
0054         }
0055         currCont = currCont->continuation();
0056     }
0057 
0058     if (attached()) {
0059         // Update replaced content
0060         updateReplacedContent();
0061         // Update pseudos for ::before and ::after
0062         updatePseudoChildren();
0063     }
0064 }
0065 
0066 // Attach handles initial setStyle that requires parent nodes
0067 void RenderInline::attach()
0068 {
0069     RenderFlow::attach();
0070 
0071     updateReplacedContent();
0072     updatePseudoChildren();
0073 }
0074 
0075 bool RenderInline::isInlineContinuation() const
0076 {
0077     return m_isContinuation;
0078 }
0079 
0080 void RenderInline::addChildToFlow(RenderObject *newChild, RenderObject *beforeChild)
0081 {
0082     // Make sure we don't append things after :after-generated content if we have it.
0083     if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER) {
0084         beforeChild = lastChild();
0085     }
0086 
0087     if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
0088         // We are placing a block inside an inline. We have to perform a split of this
0089         // inline into continuations.  This involves creating an anonymous block box to hold
0090         // |newChild|.  We then make that block box a continuation of this inline.  We take all of
0091         // the children after |beforeChild| and put them in a clone of this object.
0092 
0093         RenderBlock *newBox = createAnonymousBlock();
0094         RenderFlow *oldContinuation = continuation();
0095         setContinuation(newBox);
0096 
0097         splitFlow(beforeChild, newBox, newChild, oldContinuation);
0098         return;
0099     }
0100 
0101     RenderBox::addChild(newChild, beforeChild);
0102 
0103     newChild->setNeedsLayoutAndMinMaxRecalc();
0104 }
0105 
0106 RenderInline *RenderInline::cloneInline(RenderFlow *src)
0107 {
0108     RenderInline *o = new(src->renderArena()) RenderInline(src->element());
0109     o->m_isContinuation = true;
0110     o->setStyle(src->style());
0111     return o;
0112 }
0113 
0114 void RenderInline::splitInlines(RenderBlock *fromBlock, RenderBlock *toBlock,
0115                                 RenderBlock *middleBlock,
0116                                 RenderObject *beforeChild, RenderFlow *oldCont)
0117 {
0118     // Create a clone of this inline.
0119     RenderInline *clone = cloneInline(this);
0120     clone->setContinuation(oldCont);
0121 
0122     // Now take all of the children from beforeChild to the end and remove
0123     // then from |this| and place them in the clone.
0124     RenderObject *o = beforeChild;
0125     while (o) {
0126         RenderObject *tmp = o;
0127         o = tmp->nextSibling();
0128         clone->addChildToFlow(removeChildNode(tmp), nullptr);
0129         tmp->setNeedsLayoutAndMinMaxRecalc();
0130     }
0131 
0132     // Hook |clone| up as the continuation of the middle block.
0133     middleBlock->setContinuation(clone);
0134 
0135     // We have been reparented and are now under the fromBlock.  We need
0136     // to walk up our inline parent chain until we hit the containing block.
0137     // Once we hit the containing block we're done.
0138     RenderFlow *curr = static_cast<RenderFlow *>(parent());
0139     RenderFlow *currChild = this;
0140     while (curr && curr != fromBlock) {
0141         // Create a new clone.
0142         RenderInline *cloneChild = clone;
0143         clone = cloneInline(curr);
0144 
0145         // Insert our child clone as the first child.
0146         clone->addChildToFlow(cloneChild, nullptr);
0147 
0148         // Hook the clone up as a continuation of |curr|.
0149         RenderFlow *oldCont = curr->continuation();
0150         curr->setContinuation(clone);
0151         clone->setContinuation(oldCont);
0152 
0153         // Now we need to take all of the children starting from the first child
0154         // *after* currChild and append them all to the clone.
0155         o = currChild->nextSibling();
0156         while (o) {
0157             RenderObject *tmp = o;
0158             o = tmp->nextSibling();
0159             clone->appendChildNode(curr->removeChildNode(tmp));
0160             tmp->setNeedsLayoutAndMinMaxRecalc();
0161         }
0162 
0163         // Keep walking up the chain.
0164         currChild = curr;
0165         curr = static_cast<RenderFlow *>(curr->parent());
0166     }
0167 
0168     // Now we are at the block level. We need to put the clone into the toBlock.
0169     toBlock->appendChildNode(clone);
0170 
0171     // Now take all the children after currChild and remove them from the fromBlock
0172     // and put them in the toBlock.
0173     o = currChild->nextSibling();
0174     while (o) {
0175         RenderObject *tmp = o;
0176         o = tmp->nextSibling();
0177         toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
0178     }
0179 }
0180 
0181 void RenderInline::splitFlow(RenderObject *beforeChild, RenderBlock *newBlockBox,
0182                              RenderObject *newChild, RenderFlow *oldCont)
0183 {
0184     RenderBlock *pre = nullptr;
0185     RenderBlock *block = containingBlock();
0186     bool madeNewBeforeBlock = false;
0187 
0188     // Delete our line boxes before we do the inline split into continuations.
0189     block->deleteLineBoxTree();
0190 
0191     if (block->isAnonymousBlock()) {
0192         // We can reuse this block and make it the preBlock of the next continuation.
0193         pre = block;
0194         block = block->containingBlock();
0195     } else {
0196         // No anonymous block available for use.  Make one.
0197         pre = block->createAnonymousBlock();
0198         madeNewBeforeBlock = true;
0199     }
0200 
0201     RenderBlock *post = block->createAnonymousBlock();
0202 
0203     RenderObject *boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
0204     if (madeNewBeforeBlock) {
0205         block->insertChildNode(pre, boxFirst);
0206     }
0207     block->insertChildNode(newBlockBox, boxFirst);
0208     block->insertChildNode(post, boxFirst);
0209     block->setChildrenInline(false);
0210 
0211     if (madeNewBeforeBlock) {
0212         RenderObject *o = boxFirst;
0213         while (o) {
0214             RenderObject *no = o;
0215             o = no->nextSibling();
0216             pre->appendChildNode(block->removeChildNode(no));
0217             no->setNeedsLayoutAndMinMaxRecalc();
0218         }
0219     }
0220 
0221     splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
0222 
0223     // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
0224     // time in makeChildrenNonInline by just setting this explicitly up front.
0225     newBlockBox->setChildrenInline(false);
0226 
0227     // We don't just call addChild, since it would pass things off to the
0228     // continuation, so we call addChildToFlow explicitly instead.  We delayed
0229     // adding the newChild until now so that the |newBlockBox| would be fully
0230     // connected, thus allowing newChild access to a renderArena should it need
0231     // to wrap itself in additional boxes (e.g., table construction).
0232     newBlockBox->addChildToFlow(newChild, nullptr);
0233 
0234     // XXXdwh is any of this even necessary? I don't think it is.
0235     pre->close();
0236     pre->setPos(0, -500000);
0237     pre->setNeedsLayoutAndMinMaxRecalc();
0238     newBlockBox->close();
0239     newBlockBox->setPos(0, -500000);
0240     newBlockBox->setNeedsLayout(true);
0241     post->close();
0242     post->setPos(0, -500000);
0243     post->setNeedsLayoutAndMinMaxRecalc();
0244 
0245     updatePseudoChildren();
0246 
0247     block->setNeedsLayoutAndMinMaxRecalc();
0248 }
0249 
0250 void RenderInline::paint(PaintInfo &i, int _tx, int _ty)
0251 {
0252     paintLines(i, _tx, _ty);
0253 }
0254 
0255 /**
0256  * Appends the given coordinate-pair to the point-array if it is not
0257  * equal to the last element.
0258  * @param pointArray point-array
0259  * @param pnt point to append
0260  * @return \c true if \c pnt has actually been appended
0261  */
0262 inline static bool appendIfNew(QVector<QPoint> &pointArray, const QPoint &pnt)
0263 {
0264 //   if (!pointArray.isEmpty()) qCDebug(KHTML_LOG) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt);
0265 //   else qCDebug(KHTML_LOG) << "appifnew: " << pnt << " (unconditional)";
0266     if (!pointArray.isEmpty() && pointArray.back() == pnt) {
0267         return false;
0268     }
0269     pointArray.append(pnt);
0270     return true;
0271 }
0272 
0273 /**
0274  * Does spike-reduction on the given point-array's stack-top.
0275  *
0276  * Spikes are path segments of which one goes forward, and the sucessor
0277  * goes backward on the predecessor's segment:
0278  *
0279  * 2      0      1
0280  * x------x<-----x
0281  * (0 is stack-top in point-array)
0282  *
0283  * This will be reduced to
0284  * 1      0
0285  * x------x
0286  *
0287  * Preconditions:
0288  * - No other spikes exist in the whole point-array except at most
0289  *   one at the end
0290  * - No two succeeding points are ever equal
0291  * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
0292  *   true
0293  * - No such spike exists where 2 is situated between 0 and 1.
0294  *
0295  * Postcondition:
0296  * - No spikes exist in the whole point-array
0297  *
0298  * If no spike is found, the point-array is left unchanged.
0299  * @return \c true if an actual reduction was done
0300  */
0301 inline static bool reduceSpike(QVector<QPoint> &pointArray)
0302 {
0303     if (pointArray.size() < 3) {
0304         return false;
0305     }
0306     QVector<QPoint>::Iterator it = pointArray.end();
0307     QPoint p0 = *--it;
0308     QPoint p1 = *--it;
0309     QPoint p2 = *--it;
0310 
0311     bool elide = false;
0312 
0313     if ((p0.x() == p1.x() && p1.x() == p2.x()
0314             && ((p1.y() < p0.y() && p0.y() < p2.y())
0315                 || (p2.y() < p0.y() && p0.y() < p1.y())
0316                 || (p1.y() < p2.y() && p2.y() < p0.y())
0317                 || (p0.y() < p2.y() && p2.y() < p1.y())
0318                 || (elide = p2.y() == p0.y() && p0.y() < p1.y())
0319                 || (elide = p1.y() < p0.y() && p0.y() == p2.y())))
0320             || (p0.y() == p1.y() && p1.y() == p2.y()
0321                 && ((p1.x() < p0.x() && p0.x() < p2.x())
0322                     || (p2.x() < p0.x() && p0.x() < p1.x())
0323                     || (p1.x() < p2.x() && p2.x() < p0.x())
0324                     || (p0.x() < p2.x() && p2.x() < p1.x())
0325                     || (elide = p2.x() == p0.x() && p0.x() < p1.x())
0326                     || (elide = p1.x() < p0.x() && p0.x() == p2.x())))) {
0327 //     qCDebug(KHTML_LOG) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0;
0328         pointArray.pop_back(); pointArray.pop_back();
0329         if (!elide) {
0330             pointArray.push_back(p0);
0331         }
0332         return true;
0333     }
0334     return false;
0335 }
0336 
0337 /**
0338  * Reduces segment separators.
0339  *
0340  * A segment separator separates a segment into two segments, thus causing
0341  * two adjacent segment with the same orientation.
0342  *
0343  * 2       1     0
0344  * x-------x---->x
0345  * (0 means stack-top)
0346  *
0347  * Here, 1 is a segment separator. As segment separators not only make
0348  * the line drawing algorithm inefficient, but also make the spike-reduction
0349  * fail, they must be eliminated:
0350  *
0351  * 1             0
0352  * x------------>x
0353  *
0354  * Preconditions:
0355  * - No other segment separators exist in the whole point-array except
0356  *   at most one at the end
0357  * - No two succeeding points are ever equal
0358  * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
0359  *   true
0360  * - No such spike exists where 2 is situated between 0 and 1.
0361  *
0362  * Postcondition:
0363  * - No segment separators exist in the whole point-array
0364  *
0365  * If no segment separator is found at the end of the point-array, it is
0366  * left unchanged.
0367  * @return \c true if a segment separator was actually reduced.
0368  */
0369 inline static bool reduceSegmentSeparator(QVector<QPoint> &pointArray)
0370 {
0371     if (pointArray.size() < 3) {
0372         return false;
0373     }
0374     QVector<QPoint>::Iterator it = pointArray.end();
0375     QPoint p0 = *--it;
0376     QPoint p1 = *--it;
0377     QPoint p2 = *--it;
0378 //     qCDebug(KHTML_LOG) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0;
0379 
0380     if ((p0.x() == p1.x() && p1.x() == p2.x()
0381             && ((p2.y() < p1.y() && p1.y() < p0.y())
0382                 || (p0.y() < p1.y() && p1.y() < p2.y())))
0383             || (p0.y() == p1.y() && p1.y() == p2.y()
0384                 && ((p2.x() < p1.x() && p1.x() < p0.x())
0385                     || (p0.x() < p1.x() && p1.x() < p2.x())))) {
0386 //     qCDebug(KHTML_LOG) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0;
0387         pointArray.pop_back(); pointArray.pop_back();
0388         pointArray.push_back(p0);
0389         return true;
0390     }
0391     return false;
0392 }
0393 
0394 /**
0395  * Appends the given point to the point-array, doing necessary reductions to
0396  * produce a path without spikes and segment separators.
0397  */
0398 static void appendPoint(QVector<QPoint> &pointArray, const QPoint &pnt)
0399 {
0400     if (!appendIfNew(pointArray, pnt)) {
0401         return;
0402     }
0403 //   qCDebug(KHTML_LOG) << "appendPoint: appended " << pnt;
0404     reduceSegmentSeparator(pointArray)
0405     || reduceSpike(pointArray);
0406 }
0407 
0408 /**
0409  * Traverses the horizontal inline boxes and appends the point coordinates to
0410  * the given array.
0411  * @param box inline box
0412  * @param pointArray array collecting coordinates
0413  * @param bottom \c true, collect bottom coordinates, \c false, collect top
0414  *  coordinates.
0415  * @param limit lower limit that an y-coordinate must at least reach. Note
0416  *  that limit designates the highest y-coordinate for \c bottom, and
0417  *  the lowest for !\c bottom.
0418  */
0419 static void collectHorizontalBoxCoordinates(InlineBox *box,
0420         QVector<QPoint> &pointArray,
0421         bool bottom, int offset, int limit = -500000)
0422 {
0423 //   qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: ";
0424     offset = bottom ? offset : -offset;
0425     int y = box->yPos() + bottom * box->height() + offset;
0426     if (limit != -500000 && (bottom ? y < limit : y > limit)) {
0427         y = limit;
0428     }
0429     int x = box->xPos() + bottom * box->width() + offset;
0430     QPoint newPnt(x, y);
0431     // Add intersection point if point-array not empty.
0432     if (!pointArray.isEmpty()) {
0433         QPoint lastPnt = pointArray.back();
0434         QPoint insPnt(newPnt.x(), lastPnt.y());
0435 
0436         if (offset && ((bottom && lastPnt.y() > y) || (!bottom && lastPnt.y() < y))) {
0437             insPnt.rx() = lastPnt.x();
0438             insPnt.ry() = y;
0439         }
0440 //         qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
0441         appendPoint(pointArray, insPnt);
0442     }
0443     // Insert starting point of box
0444     appendPoint(pointArray, newPnt);
0445 
0446     newPnt.rx() += (bottom ? -box->width() : box->width()) - 2 * offset;
0447 
0448     if (box->isInlineFlowBox()) {
0449         InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
0450         for (InlineBox *b = bottom ? flowBox->lastChild() : flowBox->firstChild(); b; b = bottom ? b->prevOnLine() : b->nextOnLine()) {
0451             // Don't let boxes smaller than this flow box' height influence
0452             // the vertical position of the outline if they have a different
0453             // x-coordinate
0454             int l2;
0455             if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width()) {
0456                 l2 = y;
0457             } else {
0458                 l2 = limit;
0459             }
0460             collectHorizontalBoxCoordinates(b, pointArray, bottom, qAbs(offset), l2);
0461         }
0462 
0463         // Add intersection point if flow box contained any children
0464         if (flowBox->firstChild()) {
0465             QPoint lastPnt = pointArray.back();
0466             QPoint insPnt(lastPnt.x(), newPnt.y());
0467 //             qCDebug(KHTML_LOG) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
0468             appendPoint(pointArray, insPnt);
0469         }
0470     }
0471 
0472     // Insert ending point of box
0473     appendPoint(pointArray, newPnt);
0474 
0475 //     qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: " << "ende";
0476 }
0477 
0478 /**
0479  * Checks whether the given line box' extents and the following line box'
0480  * extents are disjount (i. e. do not share the same x-coordinate range).
0481  * @param line line box
0482  * @param toBegin \c true, compare with preceding line box, \c false, with
0483  *  succeeding
0484  * @return \c true if this and the next box are disjoint
0485  */
0486 inline static bool lineBoxesDisjoint(InlineRunBox *line, int offset, bool toBegin)
0487 {
0488     InlineRunBox *next = toBegin ? line->prevLineBox() : line->nextLineBox();
0489     return !next || next->xPos() + next->width() + 2 * offset < line->xPos()
0490            || next->xPos() > line->xPos() + line->width() + 2 * offset;
0491 }
0492 
0493 /**
0494  * Traverses the vertical outer borders of the given render flow's line
0495  * boxes and appends the point coordinates to the given point array.
0496  * @param line line box to begin traversal
0497  * @param pointArray point array
0498  * @param left \c true, traverse the left vertical coordinates,
0499  *  \c false, traverse the right vertical coordinates.
0500  * @param lastline if not 0, returns the pointer to the last line box traversed
0501  */
0502 static void collectVerticalBoxCoordinates(InlineRunBox *line,
0503         QVector<QPoint> &pointArray,
0504         bool left, int offset, InlineRunBox **lastline = nullptr)
0505 {
0506     InlineRunBox *last = nullptr;
0507     offset = left ? -offset : offset;
0508     for (InlineRunBox *curr = line; curr && !last; curr = left ? curr->prevLineBox() : curr->nextLineBox()) {
0509         InlineBox *root = curr;
0510 
0511         bool isLast = lineBoxesDisjoint(curr, qAbs(offset), left);
0512         if (isLast) {
0513             last = curr;
0514         }
0515 
0516         if (root != line && !isLast)
0517             while (root->parent()) {
0518                 root = root->parent();
0519             }
0520         QPoint newPnt(curr->xPos() + !left * curr->width() + offset,
0521                       (left ? root->topOverflow() : root->bottomOverflow()) + offset);
0522         if (!pointArray.isEmpty()) {
0523             QPoint lastPnt = pointArray.back();
0524             if (newPnt.x() > lastPnt.x() && !left) {
0525                 pointArray.back().setY(qMin(lastPnt.y(), root->topOverflow() - offset));
0526             } else if (newPnt.x() < lastPnt.x() && left) {
0527                 pointArray.back().setY(qMax(lastPnt.y(), root->bottomOverflow() + offset));
0528             }
0529             QPoint insPnt(newPnt.x(), pointArray.back().y());
0530 //         qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
0531             appendPoint(pointArray, insPnt);
0532         }
0533         appendPoint(pointArray, newPnt);
0534     }
0535     if (lastline) {
0536         *lastline = last;
0537     }
0538 }
0539 
0540 /**
0541  * Links up the end of the given point-array such that the starting point
0542  * is not a segment separator.
0543  *
0544  * To achieve this, improper points are removed from the beginning of
0545  * the point-array (by changing the array's starting iterator), and
0546  * proper ones appended to the point-array's back.
0547  *
0548  *   X---------------+               X------------------+
0549  *   ^               |               ^                  |
0550  *   |               |      ==>      |                  |
0551  *   +.....       ...+               +.....          ...+
0552  *
0553  *   +----->X--------+               +----------------->X
0554  *   |               |      ==>      |                  |
0555  *   +.....       ...+               +.....          ...+
0556  *
0557  *         ^X
0558  *         ||
0559  *   +-----++--------+               +----------------->X
0560  *   |               |      ==>      |                  |
0561  *   +.....       ...+               +.....          ...+
0562  *
0563  * @param pointArray point-array
0564  * @return actual begin of point array
0565  */
0566 static QPoint *linkEndToBegin(QVector<QPoint> &pointArray)
0567 {
0568     uint index = 0;
0569     // ### BUG: outlines with zero width aren't treated correctly
0570     // this is not the right fix
0571     if (pointArray.size() < 3) {
0572         return pointArray.data();
0573     }
0574 
0575     // if first and last points match, ignore the last one.
0576     bool linkup = false; QPoint linkupPnt;
0577     if (pointArray.front() == pointArray.back()) {
0578         linkupPnt = pointArray.back();
0579         pointArray.pop_back();
0580         linkup = true;
0581     }
0582 
0583     const QPoint *it = pointArray.data() + index;
0584     QPoint pfirst = *it;
0585     QPoint pnext = *++it;
0586     QPoint plast = pointArray.back();
0587 //     qCDebug(KHTML_LOG) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext;
0588 
0589     if ((plast.x() == pfirst.x() && pfirst.x() == pnext.x())
0590             || (plast.y() == pfirst.y() && pfirst.y() == pnext.y())) {
0591 
0592         ++index;
0593         appendPoint(pointArray, pfirst); // ### do we really need this point?
0594         appendPoint(pointArray, pnext);
0595         // ended up at a segment separator? move one point forward
0596         if (plast == pnext) {
0597             ++index;
0598             appendPoint(pointArray, *++it);
0599         }
0600     } else if (linkup) {
0601         pointArray.push_back(linkupPnt);
0602     }
0603     return pointArray.data() + index;
0604 }
0605 
0606 // assumes clock-wise orientation
0607 static RenderObject::BorderSide borderSide(const QPoint &first,
0608         const QPoint &second)
0609 {
0610     if (second.x() > first.x()) {
0611         return RenderObject::BSTop;
0612     } else if (second.x() < first.x()) {
0613         return RenderObject::BSBottom;
0614     } else if (second.y() > first.y()) {
0615         return RenderObject::BSRight;
0616     } else { // second.y() < first.y()
0617         return RenderObject::BSLeft;
0618     }
0619 }
0620 
0621 void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty)
0622 {
0623     if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN) {
0624         return;
0625     }
0626     int offset = style()->outlineOffset();
0627 
0628     // We may have to draw more than one outline path as they may be
0629     // disjoint.
0630     for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
0631         QVector<QPoint> path;
0632 
0633         // collect topmost outline
0634         collectHorizontalBoxCoordinates(curr, path, false, offset);
0635         // collect right outline
0636         collectVerticalBoxCoordinates(curr, path, false, offset, &curr);
0637         // collect bottommost outline
0638         collectHorizontalBoxCoordinates(curr, path, true, offset);
0639         // collect left outline
0640         collectVerticalBoxCoordinates(curr, path, true, offset);
0641 
0642         if (path.size() < 3) {
0643             continue;
0644         }
0645 
0646         const QPoint *begin = linkEndToBegin(path);
0647 
0648         // initial borderside and direction values
0649         QPoint pstart = *begin;
0650         QPoint pprev = *(path.end() - 2);
0651         RenderObject::BorderSide bs = borderSide(pprev, pstart);
0652         QPoint diff = pstart - pprev;
0653         int direction = diff.x() + diff.y();
0654         RenderObject::BorderSide endingBS = borderSide(*begin, *(begin + 1));
0655 
0656         // paint the outline
0657         paintOutlinePath(p, _tx, _ty, begin, path.data() + path.size(),
0658                          bs, direction, endingBS);
0659     }
0660 }
0661 
0662 template<class T> inline void kSwap(T &a1, T &a2)
0663 {
0664     T tmp = a2;
0665     a2 = a1;
0666     a1 = tmp;
0667 }
0668 
0669 enum BSOrientation { BSHorizontal, BSVertical };
0670 
0671 /**
0672  * Returns the orientation of the given border side.
0673  */
0674 inline BSOrientation bsOrientation(RenderObject::BorderSide bs)
0675 {
0676     switch (bs) {
0677     case RenderObject::BSTop:
0678     case RenderObject::BSBottom:
0679         return BSHorizontal;
0680     case RenderObject::BSLeft:
0681     case RenderObject::BSRight:
0682         return BSVertical;
0683     }
0684     return BSHorizontal;    // make gcc happy (sigh)
0685 }
0686 
0687 /**
0688  * Determines the new border side by evaluating the new direction as determined
0689  * by the given coordinates, the old border side, and the relative direction.
0690  *
0691  * The relative direction specifies whether the old border side meets with the
0692  * straight given by the coordinates from below/right (negative), or
0693  * above/left (positive).
0694  */
0695 inline RenderObject::BorderSide newBorderSide(RenderObject::BorderSide oldBS, int direction, const QPoint &last, const QPoint &cur)
0696 {
0697     bool below = direction < 0;
0698     if (last.x() == cur.x()) {  // new segment is vertical
0699         bool t = oldBS == RenderObject::BSTop;
0700         bool b = oldBS == RenderObject::BSBottom;
0701         if ((t || b) && last.y() != cur.y())
0702             return (cur.y() < last.y()) ^ ((t && below) || (b && !below))
0703                    ? RenderObject::BSLeft : RenderObject::BSRight;
0704     } else { /*if (last.y() == cur.y())*/    // new segment is horizontal
0705         bool l = oldBS == RenderObject::BSLeft;
0706         bool r = oldBS == RenderObject::BSRight;
0707         if ((l || r) && last.x() != cur.x())
0708             return (cur.x() < last.x()) ^ ((l && below) || (r && !below))
0709                    ? RenderObject::BSTop : RenderObject::BSBottom;
0710     }
0711     return oldBS;           // same direction
0712 }
0713 
0714 /**
0715  * Draws an outline segment between the given two points.
0716  * @param o render object
0717  * @param p painter
0718  * @param tx absolute x-coordinate of containing block
0719  * @param ty absolute y-coordinate of containing block
0720  * @param p1 starting point
0721  * @param p2 end point
0722  * @param prevBS border side of previous segment
0723  * @param curBS border side of this segment
0724  * @param nextBS border side of next segment
0725  */
0726 static void paintOutlineSegment(RenderObject *o, QPainter *p, int tx, int ty,
0727                                 const QPoint &p1, const QPoint &p2,
0728                                 RenderObject::BorderSide prevBS,
0729                                 RenderObject::BorderSide curBS,
0730                                 RenderObject::BorderSide nextBS)
0731 {
0732     int ow = o->style()->outlineWidth();
0733     EBorderStyle os = o->style()->outlineStyle();
0734     QColor oc = o->style()->outlineColor();
0735 
0736     int x1 = tx + p1.x();
0737     int y1 = ty + p1.y();
0738     int x2 = tx + p2.x();
0739     int y2 = ty + p2.y();
0740     if (x1 > x2) {
0741         kSwap(x1, x2);
0742         if (bsOrientation(curBS) == BSHorizontal) {
0743             kSwap(prevBS, nextBS);
0744         }
0745     }
0746     if (y1 > y2) {
0747         kSwap(y1, y2);
0748         if (bsOrientation(curBS) == BSVertical) {
0749             kSwap(prevBS, nextBS);
0750         }
0751     }
0752 
0753 //     qCDebug(KHTML_LOG) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")";
0754     /*    p->setPen(Qt::gray);
0755         p->drawLine(x1,y1,x2,y2);*/
0756     switch (curBS) {
0757     case RenderObject::BSLeft:
0758     case RenderObject::BSRight:
0759         /*        p->setPen(QColor("#ffe4dd"));
0760                 p->drawLine(
0761                               x1 - (curBS == RenderObject::BSLeft ? ow : 0),
0762                               y1 - (prevBS == RenderObject::BSTop ? ow : 0),
0763                               x2 + (curBS == RenderObject::BSRight ? ow : 0),
0764                               y2 + (nextBS == RenderObject::BSBottom ? ow : 0)
0765                               );*/
0766         o->drawBorder(p,
0767                       x1 - (curBS == RenderObject::BSLeft ? ow : 0),
0768                       y1 - (prevBS == RenderObject::BSTop ? ow : 0),
0769                       x2 + (curBS == RenderObject::BSRight ? ow : 0),
0770                       y2 + (nextBS == RenderObject::BSBottom ? ow : 0),
0771                       curBS, oc, o->style()->color(), os,
0772                       prevBS == RenderObject::BSTop ? ow
0773                       : prevBS == RenderObject::BSBottom ? -ow : 0,
0774                       nextBS == RenderObject::BSTop ? -ow
0775                       : nextBS == RenderObject::BSBottom ? ow : 0,
0776                       true);
0777         break;
0778     case RenderObject::BSBottom:
0779     case RenderObject::BSTop:
0780 //       qCDebug(KHTML_LOG) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS;
0781         o->drawBorder(p,
0782                       x1 - (prevBS == RenderObject::BSLeft ? ow : 0),
0783                       y1 - (curBS == RenderObject::BSTop ? ow : 0),
0784                       x2 + (nextBS == RenderObject::BSRight ? ow : 0),
0785                       y2 + (curBS == RenderObject::BSBottom ? ow : 0),
0786                       curBS, oc, o->style()->color(), os,
0787                       prevBS == RenderObject::BSLeft ? ow
0788                       : prevBS == RenderObject::BSRight ? -ow : 0,
0789                       nextBS == RenderObject::BSLeft ? -ow
0790                       : nextBS == RenderObject::BSRight ? ow : 0,
0791                       true);
0792         break;
0793     }
0794 }
0795 
0796 void RenderInline::paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide bs, int direction, BorderSide endingBS)
0797 {
0798     int ow = style()->outlineWidth();
0799     if (ow == 0 || m_isContinuation) { // Continuations get painted by the original inline.
0800         return;
0801     }
0802 
0803     QPoint last = *begin;
0804     BorderSide lastBS = bs;
0805     Q_ASSERT(begin != end);
0806     ++begin;
0807 
0808 //     qCDebug(KHTML_LOG) << "last: " << last;
0809 
0810     bs = newBorderSide(bs, direction, last, *begin);
0811 //     qCDebug(KHTML_LOG) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs;
0812 
0813     for (const QPoint *it = begin; it != end; ++it) {
0814         QPoint cur = *it;
0815 //         qCDebug(KHTML_LOG) << "cur: " << cur;
0816         BorderSide nextBS;
0817         if (it + 1 != end) {
0818             QPoint diff = cur - last;
0819             direction = diff.x() + diff.y();
0820             nextBS = newBorderSide(bs, direction, cur, *(it + 1));
0821 //             qCDebug(KHTML_LOG) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS;
0822         } else {
0823             nextBS = endingBS;
0824         }
0825 
0826         Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS));
0827         paintOutlineSegment(this, p, tx, ty, last, cur,
0828                             lastBS, bs, nextBS);
0829         lastBS = bs;
0830         last = cur;
0831         bs = nextBS;
0832     }
0833 
0834 }
0835 
0836 void RenderInline::calcMinMaxWidth()
0837 {
0838     KHTMLAssert(!minMaxKnown());
0839 
0840 #ifdef DEBUG_LAYOUT
0841     // qCDebug(KHTML_LOG) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this;
0842 #endif
0843 
0844     // Irrelevant, since some enclosing block will actually measure us and our children.
0845     m_minWidth = 0;
0846     m_maxWidth = 0;
0847 
0848     setMinMaxKnown();
0849 }
0850 
0851 short RenderInline::width() const
0852 {
0853     // Return the width of the minimal left side and the maximal right side.
0854     short leftSide = 0;
0855     short rightSide = 0;
0856     for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
0857         if (curr == firstLineBox() || curr->xPos() < leftSide) {
0858             leftSide = curr->xPos();
0859         }
0860         if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide) {
0861             rightSide = curr->xPos() + curr->width();
0862         }
0863     }
0864 
0865     return rightSide - leftSide;
0866 }
0867 
0868 int RenderInline::height() const
0869 {
0870     int h = 0;
0871     if (firstLineBox()) {
0872         h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
0873     }
0874     return h;
0875 }
0876 
0877 int RenderInline::offsetLeft() const
0878 {
0879     int x = RenderFlow::offsetLeft();
0880     if (firstLineBox()) {
0881         x += firstLineBox()->xPos();
0882     }
0883     return x;
0884 }
0885 
0886 int RenderInline::offsetTop() const
0887 {
0888     int y = RenderFlow::offsetTop();
0889     if (firstLineBox()) {
0890         y += firstLineBox()->yPos();
0891     }
0892     return y;
0893 }
0894 
0895 const char *RenderInline::renderName() const
0896 {
0897     if (isRelPositioned()) {
0898         return "RenderInline (relative positioned)";
0899     }
0900     if (isAnonymous()) {
0901         return "RenderInline (anonymous)";
0902     }
0903     return "RenderInline";
0904 }
0905 
0906 bool RenderInline::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
0907 {
0908 
0909     // Check our line boxes if we're still not inside.
0910     if (!inside) {
0911         // See if we're inside one of our line boxes.
0912         inside = hitTestLines(info, _x, _y, _tx, _ty, hitTestAction);
0913     }
0914 
0915     if (inside && element() && style()->visibility() != HIDDEN) {
0916         if (info.innerNode() && info.innerNode()->renderer() &&
0917                 !info.innerNode()->renderer()->isInline()) {
0918             // Within the same layer, inlines are ALWAYS fully above blocks.  Change inner node.
0919             info.setInnerNode(element());
0920 
0921             // Clear everything else.
0922             info.setInnerNonSharedNode(nullptr);
0923             info.setURLElement(nullptr);
0924         }
0925 
0926         if (!info.innerNode()) {
0927             info.setInnerNode(element());
0928         }
0929 
0930         if (!info.innerNonSharedNode()) {
0931             info.setInnerNonSharedNode(element());
0932         }
0933     }
0934 
0935     return inside;
0936 }
0937 
0938 RenderPosition RenderInline::positionForCoordinates(int x, int y)
0939 {
0940     for (RenderObject *c = continuation(); c; c = c->continuation()) {
0941         if (c->isInline() || c->firstChild()) {
0942             return c->positionForCoordinates(x, y);
0943         }
0944     }
0945 
0946     return RenderFlow::positionForCoordinates(x, y);
0947 }
0948 
0949 void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) const
0950 {
0951     _x = -1;
0952 
0953     RenderBlock *cb = containingBlock();
0954     bool rtl = cb->style()->direction() == RTL;
0955     bool outsideEnd = flags & CFOutsideEnd;
0956     // I need to explain that: outsideEnd contains a meaningful value if
0957     // and only if flags & CFOutside is set. If it is not, then randomly
0958     // either the first or the last line box is returned.
0959     // This doesn't matter because the only case this can happen is on an
0960     // empty inline element, whose first and last line boxes are actually
0961     // the same.
0962     InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox();
0963 
0964     if (!line) {        // umpf, handle "gracefully"
0965         RenderFlow::caretPos(offset, flags, _x, _y, width, height);
0966         return;
0967     }
0968 
0969     _x = line->xPos();
0970     width = 1;      // ### regard CFOverride
0971 
0972     // Place caret outside the border
0973     if (flags & CFOutside) {
0974         RenderStyle *s = element() && element()->parent()
0975                          && element()->parent()->renderer()
0976                          ? element()->parent()->renderer()->style()
0977                          : style();
0978         const QFontMetrics &fm = s->fontMetrics();
0979         _y = line->yPos() + line->baseline() - fm.ascent();
0980         height = fm.height();
0981 
0982         if (!outsideEnd ^ rtl) {
0983             _x -= line->marginBorderPaddingLeft();
0984         } else {
0985             _x += line->width() + line->marginBorderPaddingRight();
0986         }
0987 
0988     } else {
0989         const QFontMetrics &fm = style()->fontMetrics();
0990         _y = line->yPos() + line->baseline() - fm.ascent();
0991         height = fm.height();
0992     }
0993 
0994     int absx, absy;
0995     if (cb && cb->absolutePosition(absx, absy)) {
0996         //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
0997         _x += absx;
0998         _y += absy;
0999     } else {
1000         // we don't know our absolute position, and there is no point returning
1001         // just a relative one
1002         _x = _y = -1;
1003     }
1004 }
1005 
1006 inline int minXPos(const RenderInline *o)
1007 {
1008     int retval = 6666666;
1009     if (!o->firstLineBox()) {
1010         return 0;
1011     }
1012     for (InlineRunBox *curr = o->firstLineBox(); curr; curr = curr->nextLineBox()) {
1013         retval = qMin(retval, int(curr->m_x));
1014     }
1015     return retval;
1016 }
1017 
1018 int RenderInline::inlineXPos() const
1019 {
1020     return minXPos(this);
1021 }
1022 
1023 int RenderInline::inlineYPos() const
1024 {
1025     return firstLineBox() ? firstLineBox()->yPos() : 0;
1026 }
1027