Fix for 3810389, crash because of continuation() craziness. Revert back to the...
[WebKit-https.git] / WebCore / khtml / rendering / render_inline.cpp
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include <kglobal.h>
26 #include "render_arena.h"
27 #include "render_inline.h"
28 #include "render_block.h"
29 #include "xml/dom_docimpl.h"
30 #include "xml/dom_position.h"
31
32 using DOM::Position;
33 using namespace khtml;
34
35 RenderInline::RenderInline(DOM::NodeImpl* node)
36 :RenderFlow(node), m_isContinuation(false)
37 {}
38
39 RenderInline::~RenderInline()
40 {}
41
42 void RenderInline::setStyle(RenderStyle* _style)
43 {
44     RenderFlow::setStyle(_style);
45     setInline(true);
46
47     // Ensure that all of the split inlines pick up the new style. We
48     // only do this if we're an inline, since we don't want to propagate
49     // a block's style to the other inlines.
50     // e.g., <font>foo <h4>goo</h4> moo</font>.  The <font> inlines before
51     // and after the block share the same style, but the block doesn't
52     // need to pass its style on to anyone else.
53     RenderFlow* currCont = continuation();
54     while (currCont) {
55         if (currCont->isInline()) {
56             RenderFlow* nextCont = currCont->continuation();
57             currCont->setContinuation(0);
58             currCont->setStyle(style());
59             currCont->setContinuation(nextCont);
60         }
61         currCont = currCont->continuation();
62     }
63
64     m_lineHeight = -1;
65     
66     // Update pseudos for :before and :after now.
67     updatePseudoChild(RenderStyle::BEFORE, firstChild());
68     updatePseudoChild(RenderStyle::AFTER, lastChild());
69 }
70
71 bool RenderInline::isInlineContinuation() const
72 {
73     return m_isContinuation;
74 }
75
76 void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
77 {
78     // Make sure we don't append things after :after-generated content if we have it.
79     if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER)
80         beforeChild = lastChild();
81
82     if (!newChild->isInline() && !newChild->isFloatingOrPositioned() )
83     {
84         // We are placing a block inside an inline. We have to perform a split of this
85         // inline into continuations.  This involves creating an anonymous block box to hold
86         // |newChild|.  We then make that block box a continuation of this inline.  We take all of
87         // the children after |beforeChild| and put them in a clone of this object.
88         RenderStyle *newStyle = new (renderArena()) RenderStyle();
89         newStyle->inheritFrom(style());
90         newStyle->setDisplay(BLOCK);
91
92         RenderBlock *newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
93         newBox->setStyle(newStyle);
94         RenderFlow* oldContinuation = continuation();
95         setContinuation(newBox);
96
97         // Someone may have put a <p> inside a <q>, causing a split.  When this happens, the :after content
98         // has to move into the inline continuation.  Call updatePseudoChild to ensure that our :after
99         // content gets properly destroyed.
100         bool isLastChild = (beforeChild == lastChild());
101         updatePseudoChild(RenderStyle::AFTER, lastChild());
102         if (isLastChild && beforeChild != lastChild())
103             beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
104                              // point to be 0.  It's just a straight append now.
105         
106         splitFlow(beforeChild, newBox, newChild, oldContinuation);
107         return;
108     }
109
110     RenderContainer::addChild(newChild,beforeChild);
111
112     newChild->setNeedsLayoutAndMinMaxRecalc();
113 }
114
115 RenderInline* RenderInline::cloneInline(RenderFlow* src)
116 {
117     RenderInline *o = new (src->renderArena()) RenderInline(src->element());
118     o->m_isContinuation = true;
119     o->setStyle(src->style());
120     return o;
121 }
122
123 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
124                                 RenderBlock* middleBlock,
125                                 RenderObject* beforeChild, RenderFlow* oldCont)
126 {
127     // Create a clone of this inline.
128     RenderInline* clone = cloneInline(this);
129     clone->setContinuation(oldCont);
130     
131     // Now take all of the children from beforeChild to the end and remove
132     // them from |this| and place them in the clone.
133     RenderObject* o = beforeChild;
134     while (o) {
135         RenderObject* tmp = o;
136         o = tmp->nextSibling();
137         clone->addChildToFlow(removeChildNode(tmp), 0);
138         tmp->setNeedsLayoutAndMinMaxRecalc();
139     }
140
141     // Hook |clone| up as the continuation of the middle block.
142     middleBlock->setContinuation(clone);
143
144     // We have been reparented and are now under the fromBlock.  We need
145     // to walk up our inline parent chain until we hit the containing block.
146     // Once we hit the containing block we're done.
147     RenderFlow* curr = static_cast<RenderFlow*>(parent());
148     RenderFlow* currChild = this;
149     while (curr && curr != fromBlock) {
150         // Create a new clone.
151         RenderInline* cloneChild = clone;
152         clone = cloneInline(curr);
153
154         // Insert our child clone as the first child.
155         clone->addChildToFlow(cloneChild, 0);
156
157         // Hook the clone up as a continuation of |curr|.
158         RenderFlow* oldCont = curr->continuation();
159         curr->setContinuation(clone);
160         clone->setContinuation(oldCont);
161
162         // Someone may have indirectly caused a <q> to split.  When this happens, the :after content
163         // has to move into the inline continuation.  Call updatePseudoChild to ensure that the inline's :after
164         // content gets properly destroyed.
165         curr->updatePseudoChild(RenderStyle::AFTER, curr->lastChild());
166         
167         // Now we need to take all of the children starting from the first child
168         // *after* currChild and append them all to the clone.
169         o = currChild->nextSibling();
170         while (o) {
171             RenderObject* tmp = o;
172             o = tmp->nextSibling();
173             clone->addChildToFlow(curr->removeChildNode(tmp), 0);
174             tmp->setNeedsLayoutAndMinMaxRecalc();
175         }
176
177         // Keep walking up the chain.
178         currChild = curr;
179         curr = static_cast<RenderFlow*>(curr->parent());
180     }
181
182     // Now we are at the block level. We need to put the clone into the toBlock.
183     toBlock->appendChildNode(clone);
184
185     // Now take all the children after currChild and remove them from the fromBlock
186     // and put them in the toBlock.
187     o = currChild->nextSibling();
188     while (o) {
189         RenderObject* tmp = o;
190         o = tmp->nextSibling();
191         toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
192     }
193 }
194
195 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
196                              RenderObject* newChild, RenderFlow* oldCont)
197 {
198     RenderBlock* pre = 0;
199     RenderBlock* block = containingBlock();
200     bool madeNewBeforeBlock = false;
201     if (block->isAnonymousBlock()) {
202         // We can reuse this block and make it the preBlock of the next continuation.
203         pre = block;
204         block = block->containingBlock();
205     }
206     else {
207         // No anonymous block available for use.  Make one.
208         pre = block->createAnonymousBlock();
209         madeNewBeforeBlock = true;
210     }
211
212     RenderBlock* post = block->createAnonymousBlock();
213     
214     RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
215     if (madeNewBeforeBlock)
216         block->insertChildNode(pre, boxFirst);
217     block->insertChildNode(newBlockBox, boxFirst);
218     block->insertChildNode(post, boxFirst);
219     block->setChildrenInline(false);
220
221     if (madeNewBeforeBlock) {
222         RenderObject* o = boxFirst;
223         while (o)
224         {
225             RenderObject* no = o;
226             o = no->nextSibling();
227             pre->appendChildNode(block->removeChildNode(no));
228             no->setNeedsLayoutAndMinMaxRecalc();
229         }
230     }
231
232     splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
233
234     // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
235     // time in makeChildrenNonInline by just setting this explicitly up front.
236     newBlockBox->setChildrenInline(false);
237
238     // We don't just call addChild, since it would pass things off to the
239     // continuation, so we call addChildToFlow explicitly instead.  We delayed
240     // adding the newChild until now so that the |newBlockBox| would be fully
241     // connected, thus allowing newChild access to a renderArena should it need
242     // to wrap itself in additional boxes (e.g., table construction).
243     newBlockBox->addChildToFlow(newChild, 0);
244     
245     // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
246     // get deleted properly.  Because objects moves from the pre block into the post block, we want to
247     // make new line boxes instead of leaving the old line boxes around.
248     pre->setNeedsLayoutAndMinMaxRecalc();
249     block->setNeedsLayoutAndMinMaxRecalc();
250     post->setNeedsLayoutAndMinMaxRecalc();
251 }
252
253 void RenderInline::paint(PaintInfo& i, int _tx, int _ty)
254 {
255 #ifdef DEBUG_LAYOUT
256     //    kdDebug( 6040 ) << renderName() << "(RenderInline) " << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")" << endl;
257 #endif
258     
259     // We're done.  We don't bother painting any children.
260     if (i.phase == PaintActionElementBackground)
261         return;
262     
263     // We don't paint our own background, but we do let the kids paint their backgrounds.
264     PaintInfo paintInfo(i.p, i.r, i.phase, paintingRootForChildren(i));
265     if (i.phase == PaintActionChildBackgrounds)
266         paintInfo.phase = PaintActionChildBackground;
267
268     paintLineBoxBackgroundBorder(paintInfo, _tx, _ty);
269     
270     paintLineBoxDecorations(paintInfo, _tx, _ty); // Underline/overline
271     
272     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
273         if(!child->layer() && !child->isFloating())
274             child->paint(paintInfo, _tx, _ty);
275
276     paintLineBoxDecorations(paintInfo, _tx, _ty, true); // Strike-through
277     
278     if (style()->visibility() == VISIBLE && paintInfo.phase == PaintActionOutline) {
279 #if APPLE_CHANGES
280         if (style()->outlineStyleIsAuto())
281             paintFocusRing(paintInfo.p, _tx, _ty);
282         else
283 #endif
284         paintOutlines(paintInfo.p, _tx, _ty);
285     }
286 }
287
288 void RenderInline::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
289 {
290     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
291         rects.append(QRect(_tx + curr->xPos(), _ty + curr->yPos(), curr->width(), curr->height()));
292     
293     for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
294         if (!curr->isText())
295             curr->absoluteRects(rects, _tx + curr->xPos(), _ty + curr->yPos());
296     
297     if (continuation())
298         continuation()->absoluteRects(rects, 
299                                       _tx - containingBlock()->xPos() + continuation()->xPos(),
300                                       _ty - containingBlock()->yPos() + continuation()->yPos());
301 }
302
303 #if APPLE_CHANGES
304 void RenderInline::addFocusRingRects(QPainter *p, int _tx, int _ty)
305 {
306     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
307         p->addFocusRingRect(_tx + curr->xPos(), 
308                             _ty + curr->yPos(), 
309                             curr->width(), 
310                             curr->height());
311     }
312     
313     for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
314         if (!curr->isText())
315             curr->addFocusRingRects(p, _tx + curr->xPos(), _ty + curr->yPos());
316     }
317     
318     if (continuation())
319         continuation()->addFocusRingRects(p, 
320                                           _tx - containingBlock()->xPos() + continuation()->xPos(),
321                                           _ty - containingBlock()->yPos() + continuation()->yPos());
322 }
323
324 void RenderInline::paintFocusRing(QPainter *p, int tx, int ty)
325 {
326     int ow = style()->outlineWidth();
327     if (ow == 0 || m_isContinuation) // Continuations get painted by the original inline.
328         return;
329
330     QColor oc = style()->outlineColor();
331     if (!oc.isValid())
332         oc = style()->color();
333
334     p->initFocusRing(ow,  style()->outlineOffset(), oc);
335     addFocusRingRects(p, tx, ty);
336     p->drawFocusRing();
337     p->clearFocusRing();
338 }
339 #endif
340
341 void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty)
342 {
343     if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN)
344         return;
345     
346     QPtrList <QRect> rects;
347     rects.setAutoDelete(true);
348
349     rects.append(new QRect(0,0,0,0));
350     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
351         rects.append(new QRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()));
352     }
353     rects.append(new QRect(0,0,0,0));
354
355     for (unsigned int i = 1; i < rects.count() - 1; i++)
356         paintOutline(p, _tx, _ty, *rects.at(i-1), *rects.at(i), *rects.at(i+1));
357 }
358
359 void RenderInline::paintOutline(QPainter *p, int tx, int ty, const QRect &lastline, const QRect &thisline, const QRect &nextline)
360 {
361     int ow = style()->outlineWidth();
362     if (ow == 0 || m_isContinuation) // Continuations get painted by the original inline.
363         return;
364     
365     EBorderStyle os = style()->outlineStyle();
366     QColor oc = style()->outlineColor();
367     if (!oc.isValid())
368         oc = style()->color();
369     
370     int offset = style()->outlineOffset();
371     
372     int t = ty + thisline.top() - offset;
373     int l = tx + thisline.left() - offset;
374     int b = ty + thisline.bottom() + offset + 1;
375     int r = tx + thisline.right() + offset + 1;
376     
377     // left edge
378     drawBorder(p,
379                l - ow,
380                t - (lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : 0),
381                l,
382                b + (nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : 0),
383                BSLeft,
384                oc, style()->color(), os,
385                (lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : -ow),
386                (nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : -ow),
387                true);
388     
389     // right edge
390     drawBorder(p,
391                r,
392                t - (lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : 0),
393                r + ow,
394                b + (nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : 0),
395                BSRight,
396                oc, style()->color(), os,
397                (lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : -ow),
398                (nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : -ow),
399                true);
400     // upper edge
401     if ( thisline.left() < lastline.left())
402         drawBorder(p,
403                    l - ow,
404                    t - ow,
405                    QMIN(r+ow, (lastline.isValid()? tx+lastline.left() : 1000000)),
406                    t ,
407                    BSTop, oc, style()->color(), os,
408                    ow,
409                    (lastline.isValid() && tx+lastline.left()+1<r+ow ? -ow : ow),
410                    true);
411     
412     if (lastline.right() < thisline.right())
413         drawBorder(p,
414                    QMAX(lastline.isValid()?tx + lastline.right() + 1:-1000000, l - ow),
415                    t - ow,
416                    r + ow,
417                    t ,
418                    BSTop, oc, style()->color(), os,
419                    (lastline.isValid() && l-ow < tx+lastline.right()+1 ? -ow : ow),
420                    ow,
421                    true);
422     
423     // lower edge
424     if ( thisline.left() < nextline.left())
425         drawBorder(p,
426                    l - ow,
427                    b,
428                    QMIN(r+ow, nextline.isValid()? tx+nextline.left()+1 : 1000000),
429                    b + ow,
430                    BSBottom, oc, style()->color(), os,
431                    ow,
432                    (nextline.isValid() && tx+nextline.left()+1<r+ow? -ow : ow),
433                    true);
434     
435     if (nextline.right() < thisline.right())
436         drawBorder(p,
437                    QMAX(nextline.isValid()?tx+nextline.right()+1:-1000000 , l-ow),
438                    b,
439                    r + ow,
440                    b + ow,
441                    BSBottom, oc, style()->color(), os,
442                    (nextline.isValid() && l-ow < tx+nextline.right()+1? -ow : ow),
443                    ow,
444                    true);
445 }
446
447 void RenderInline::calcMinMaxWidth()
448 {
449     KHTMLAssert( !minMaxKnown() );
450
451 #ifdef DEBUG_LAYOUT
452     kdDebug( 6040 ) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this << endl;
453 #endif
454
455     // Irrelevant, since some enclosing block will actually measure us and our children.
456     m_minWidth = 0;
457     m_maxWidth = 0;
458
459     setMinMaxKnown();
460 }
461
462 bool RenderInline::requiresLayer() {
463     return isRoot() || isRelPositioned() || style()->opacity() < 1.0f;
464 }
465
466 int RenderInline::width() const
467 {
468     // Return the width of the minimal left side and the maximal right side.
469     int leftSide = 0;
470     int rightSide = 0;
471     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
472         if (curr == firstLineBox() || curr->xPos() < leftSide)
473             leftSide = curr->xPos();
474         if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide)
475             rightSide = curr->xPos() + curr->width();
476     }
477     
478     return rightSide - leftSide;
479 }
480
481 int RenderInline::height() const
482 {
483     int h = 0;
484     if (firstLineBox())
485         h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
486     return h;
487 }
488
489 int RenderInline::offsetLeft() const
490 {
491     int x = RenderFlow::offsetLeft();
492     if (firstLineBox())
493         x += firstLineBox()->xPos();
494     return x;
495 }
496
497 int RenderInline::offsetTop() const
498 {
499     int y = RenderFlow::offsetTop();
500     if (firstLineBox())
501         y += firstLineBox()->yPos();
502     return y;
503 }
504
505 const char *RenderInline::renderName() const
506 {
507     if (isRelPositioned())
508         return "RenderInline (relative positioned)";
509     if (isAnonymous())
510         return "RenderInline (generated)";
511     return "RenderInline";
512 }
513
514 bool RenderInline::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
515                                HitTestAction hitTestAction, bool inside)
516 {
517     // Check our kids if our HitTestAction says to.
518     if (hitTestAction != HitTestSelfOnly) {
519         for (RenderObject* child = lastChild(); child; child = child->previousSibling())
520             if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty))
521                 inside = true;
522     }
523     
524     // Check our line boxes if we're still not inside.
525     if (hitTestAction != HitTestChildrenOnly && !inside && style()->visibility() != HIDDEN) {
526         // See if we're inside one of our line boxes.
527         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
528             if((_y >=_ty + curr->m_y) && (_y < _ty + curr->m_y + curr->m_height) &&
529                (_x >= _tx + curr->m_x) && (_x <_tx + curr->m_x + curr->m_width) ) {
530                 inside = true;
531                 break;
532             }
533         }
534     }
535
536     if (inside && element()) {
537         if (info.innerNode() && info.innerNode()->renderer() &&
538             !info.innerNode()->renderer()->isInline()) {
539             // Within the same layer, inlines are ALWAYS fully above blocks.  Change inner node.
540             info.setInnerNode(element());
541
542             // Clear everything else.
543             info.setInnerNonSharedNode(0);
544             info.setURLElement(0);
545         }
546
547         if (!info.innerNode())
548             info.setInnerNode(element());
549
550         if(!info.innerNonSharedNode())
551             info.setInnerNonSharedNode(element());
552     }
553
554     return inside;
555 }
556
557 Position RenderInline::positionForCoordinates(int x, int y, EAffinity *affinity)
558 {
559     for (RenderObject *c = continuation(); c; c = c->continuation()) {
560         if (c->isInline() || c->firstChild())
561             return c->positionForCoordinates(x, y, affinity);
562     }
563
564     return RenderFlow::positionForCoordinates(x, y, affinity);
565 }