Fix for 3718697, crash clicking on JS tab at alaskaair.com.
[WebKit-https.git] / WebCore / khtml / rendering / render_flow.cpp
1 /**
2  * This file is part of the html renderer for KDE.
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 <kdebug.h>
26 #include <assert.h>
27 #include <qpainter.h>
28 #include <kglobal.h>
29
30 #include "rendering/render_flow.h"
31 #include "rendering/render_text.h"
32 #include "rendering/render_table.h"
33 #include "rendering/render_canvas.h"
34 #include "xml/dom_nodeimpl.h"
35 #include "xml/dom_docimpl.h"
36 #include "html/html_formimpl.h"
37 #include "render_inline.h"
38 #include "render_block.h"
39 #include "render_arena.h"
40 #include "render_line.h"
41
42 #include "khtmlview.h"
43 #include "htmltags.h"
44
45 using namespace DOM;
46 using namespace khtml;
47
48 RenderFlow* RenderFlow::createAnonymousFlow(DOM::DocumentImpl* doc, RenderStyle* style)
49 {
50     RenderFlow* result;
51     if (style->display() == INLINE)
52         result = new (doc->renderArena()) RenderInline(doc);
53     else
54         result = new (doc->renderArena()) RenderBlock(doc);
55     result->setStyle(style);
56     return result;
57 }
58
59 RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
60 {
61     if (beforeChild && beforeChild->parent() == this)
62         return this;
63     
64     RenderFlow* curr = continuation();
65     RenderFlow* nextToLast = this;
66     RenderFlow* last = this;
67     while (curr) {
68         if (beforeChild && beforeChild->parent() == curr) {
69             if (curr->firstChild() == beforeChild)
70                 return last;
71             return curr;
72         }
73         
74         nextToLast = last;
75         last = curr;
76         curr = curr->continuation();
77     }
78     
79     if (!beforeChild && !last->firstChild())
80         return nextToLast;
81     return last;
82 }
83
84 void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
85 {
86     RenderFlow* flow = continuationBefore(beforeChild);
87     KHTMLAssert(!beforeChild || beforeChild->parent()->isRenderBlock() ||
88                 beforeChild->parent()->isRenderInline());
89     RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) : 
90                                     (flow->continuation() ? flow->continuation() : flow);
91     
92     if (newChild->isFloatingOrPositioned())
93         return beforeChildParent->addChildToFlow(newChild, beforeChild);
94     
95     // A continuation always consists of two potential candidates: an inline or an anonymous
96     // block box holding block children.
97     bool childInline = newChild->isInline();
98     bool bcpInline = beforeChildParent->isInline();
99     bool flowInline = flow->isInline();
100     
101     if (flow == beforeChildParent)
102         return flow->addChildToFlow(newChild, beforeChild);
103     else {
104         // The goal here is to match up if we can, so that we can coalesce and create the
105         // minimal # of continuations needed for the inline.
106         if (childInline == bcpInline)
107             return beforeChildParent->addChildToFlow(newChild, beforeChild);
108         else if (flowInline == childInline)
109             return flow->addChildToFlow(newChild, 0); // Just treat like an append.
110         else 
111             return beforeChildParent->addChildToFlow(newChild, beforeChild);
112     }
113 }
114
115 void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
116 {
117 #ifdef DEBUG_LAYOUT
118     kdDebug( 6040 ) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
119                        ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
120     kdDebug( 6040 ) << "current height = " << m_height << endl;
121 #endif
122
123     if (continuation())
124         return addChildWithContinuation(newChild, beforeChild);
125     return addChildToFlow(newChild, beforeChild);
126 }
127
128 void RenderFlow::extractLineBox(InlineFlowBox* box)
129 {
130     m_lastLineBox = box->prevFlowBox();
131     if (box == m_firstLineBox)
132         m_firstLineBox = 0;
133     if (box->prevLineBox())
134         box->prevLineBox()->setNextLineBox(0);
135     box->setPreviousLineBox(0);
136     for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
137         curr->setExtracted();
138 }
139
140 void RenderFlow::attachLineBox(InlineFlowBox* box)
141 {
142     if (m_lastLineBox) {
143         m_lastLineBox->setNextLineBox(box);
144         box->setPreviousLineBox(m_lastLineBox);
145     }
146     else
147         m_firstLineBox = box;
148     InlineFlowBox* last = box;
149     for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) {
150         curr->setExtracted(false);
151         last = curr;
152     }
153     m_lastLineBox = last;
154 }
155
156 void RenderFlow::removeLineBox(InlineFlowBox* box)
157 {
158     if (box == m_firstLineBox)
159         m_firstLineBox = box->nextFlowBox();
160     if (box == m_lastLineBox)
161         m_lastLineBox = box->prevFlowBox();
162     if (box->nextLineBox())
163         box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
164     if (box->prevLineBox())
165         box->prevLineBox()->setNextLineBox(box->nextLineBox());
166 }
167
168 void RenderFlow::deleteLineBoxes()
169 {
170     if (m_firstLineBox) {
171         RenderArena* arena = renderArena();
172         InlineRunBox *curr=m_firstLineBox, *next=0;
173         while (curr) {
174             next = curr->nextLineBox();
175             curr->detach(arena);
176             curr = next;
177         }
178         m_firstLineBox = 0;
179         m_lastLineBox = 0;
180     }
181 }
182
183 void RenderFlow::detach()
184 {
185     if (!documentBeingDestroyed()) {
186         if (m_firstLineBox) {
187             if (m_firstLineBox->parent()) {
188                 for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
189                     box->parent()->removeChild(box);
190             }
191         }
192         else if (isInline() && parent())
193             parent()->dirtyLinesFromChangedChild(this, false);
194     }
195
196     deleteLineBoxes();
197     RenderBox::detach();
198 }
199
200 void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child, bool adding)
201 {
202     if (!parent() || selfNeedsLayout() || isTable())
203         return;
204     
205     if (adding && !isInline() && (!child->nextSibling() || !firstLineBox())) {
206         // An append onto the end of a block or we don't have any lines anyway.  
207         // In this case we don't have to dirty any specific lines.
208         static_cast<RenderBlock*>(this)->setLinesAppended();
209         return;
210     }
211     
212     // For an empty inline, go ahead and propagate the check up to our parent.
213     if (isInline() && !firstLineBox())
214         return parent()->dirtyLinesFromChangedChild(this);
215     
216     // Try to figure out which line box we belong in.  First try to find a previous
217     // line box by examining our siblings.  If we didn't find a line box, then use our 
218     // parent's first line box.
219     RootInlineBox* box = 0;
220     for (RenderObject* curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
221         if (curr->isFloatingOrPositioned())
222             continue;
223         
224         if (curr->isReplaced()) {
225             InlineBox* wrapper = curr->inlineBoxWrapper();
226             if (wrapper)
227                 box = wrapper->root();
228         }
229         else if (curr->isText()) {
230             InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox();
231             if (textBox)
232                 box = textBox->root();
233         }
234         else if (curr->isInlineFlow()) {
235             InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox();
236             if (runBox)
237                 box = runBox->root();
238         }
239         
240         if (box)
241             break;
242     }
243     if (!box)
244         box = lastLineBox()->root();
245
246     // If we found a line box, then dirty it.
247     if (box) {
248         box->markDirty();
249         if (child->isBR()) {
250             RootInlineBox* next = box->nextRootBox();
251             if (next)
252                 next->markDirty();
253         }
254     }
255 }
256
257 short RenderFlow::lineHeight(bool firstLine, bool isRootLineBox) const
258 {
259     if (firstLine) {
260         RenderStyle* s = style(firstLine);
261         Length lh = s->lineHeight();
262         if (lh.value < 0) {
263             if (s == style()) {
264                 if (m_lineHeight == -1)
265                     m_lineHeight = RenderObject::lineHeight(false);
266                 return m_lineHeight;
267             }
268             return s->fontMetrics().lineSpacing();
269         }
270         if (lh.isPercent())
271             return lh.minWidth(s->font().pixelSize());
272         return lh.value;
273     }
274
275     if (m_lineHeight == -1)
276         m_lineHeight = RenderObject::lineHeight(false);
277     return m_lineHeight;
278 }
279
280 void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox)
281 {
282     if (!isRootLineBox && isReplaced())
283         return RenderBox::dirtyLineBoxes(isRootLineBox);
284     
285     if (fullLayout)
286         deleteLineBoxes();
287     else {
288         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
289             curr->dirtyLineBoxes();
290     }
291 }
292
293 InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun)
294 {
295     if (!isRootLineBox &&
296         (isReplaced() || makePlaceHolderBox))                     // Inline tables and inline blocks
297         return RenderBox::createInlineBox(false, isRootLineBox);  // (or positioned element placeholders).
298
299     InlineFlowBox* flowBox = 0;
300     if (isInlineFlow())
301         flowBox = new (renderArena()) InlineFlowBox(this);
302     else
303         flowBox = new (renderArena()) RootInlineBox(this);
304     
305     if (!m_firstLineBox)
306         m_firstLineBox = m_lastLineBox = flowBox;
307     else {
308         m_lastLineBox->setNextLineBox(flowBox);
309         flowBox->setPreviousLineBox(m_lastLineBox);
310         m_lastLineBox = flowBox;
311     }
312
313     return flowBox;
314 }
315
316 void RenderFlow::paintLineBoxBackgroundBorder(PaintInfo& i, int _tx, int _ty)
317 {
318     if (!shouldPaintWithinRoot(i))
319         return;
320
321     if (!firstLineBox())
322         return;
323  
324     if (style()->visibility() == VISIBLE && i.phase == PaintActionForeground) {
325         // We can check the first box and last box and avoid painting if we don't
326         // intersect.
327         int yPos = _ty + firstLineBox()->yPos();
328         int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
329         if( (yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
330             return;
331
332         // See if our boxes intersect with the dirty rect.  If so, then we paint
333         // them.  Note that boxes can easily overlap, so we can't make any assumptions
334         // based off positions of our first line box or our last line box.
335         int xOffsetWithinLineBoxes = 0;
336         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
337             yPos = _ty + curr->yPos();
338             h = curr->height();
339             if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
340                 curr->paintBackgroundAndBorder(i, _tx, _ty, xOffsetWithinLineBoxes);
341             xOffsetWithinLineBoxes += curr->width();
342         }
343     }
344 }
345
346 void RenderFlow::paintLineBoxDecorations(PaintInfo& i, int _tx, int _ty, bool paintedChildren)
347 {
348     if (!shouldPaintWithinRoot(i))
349         return;
350
351     // We only paint line box decorations in strict or almost strict mode.
352     // Otherwise we let the InlineTextBoxes paint their own decorations.
353     if (style()->htmlHacks() || !firstLineBox())
354         return;
355
356     if (style()->visibility() == VISIBLE && i.phase == PaintActionForeground) {
357         // We can check the first box and last box and avoid painting if we don't
358         // intersect.
359         int yPos = _ty + firstLineBox()->yPos();;
360         int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
361         if( (yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
362             return;
363
364         // See if our boxes intersect with the dirty rect.  If so, then we paint
365         // them.  Note that boxes can easily overlap, so we can't make any assumptions
366         // based off positions of our first line box or our last line box.
367         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
368             yPos = _ty + curr->yPos();
369             h = curr->height();
370             if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
371                 curr->paintDecorations(i, _tx, _ty, paintedChildren);
372         }
373     }
374 }
375
376 QRect RenderFlow::getAbsoluteRepaintRect()
377 {
378     if (isInlineFlow()) {
379         // Find our leftmost position.
380         int left = 0;
381         int top = firstLineBox() ? firstLineBox()->yPos() : 0;
382         for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
383             if (curr == firstLineBox() || curr->xPos() < left)
384                 left = curr->xPos();
385
386         // Now invalidate a rectangle.
387         int ow = style() ? style()->outlineSize() : 0;
388         if (isCompact())
389             left -= m_x;
390         if (style()->position() == RELATIVE && m_layer)
391             m_layer->relativePositionOffset(left, top);
392
393         QRect r(-ow+left, -ow+top, width()+ow*2, height()+ow*2);
394         containingBlock()->computeAbsoluteRepaintRect(r);
395         if (ow) {
396             for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
397                 if (!curr->isText()) {
398                     QRect childRect = curr->getAbsoluteRepaintRectWithOutline(ow);
399                     r = r.unite(childRect);
400                 }
401             }
402             
403             if (continuation() && !continuation()->isInline()) {
404                 QRect contRect = continuation()->getAbsoluteRepaintRectWithOutline(ow);
405                 r = r.unite(contRect);
406             }
407         }
408
409         return r;
410     }
411     else {
412         if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
413             int ow = style() ? style()->outlineSize() : 0;
414             QRect r(-ow, -ow+firstLineBox()->topOverflow(),
415                     overflowWidth(false)+ow*2,
416                     overflowHeight(false)+ow*2-firstLineBox()->topOverflow());
417             computeAbsoluteRepaintRect(r);
418             return r;
419         }
420     }
421
422     return RenderBox::getAbsoluteRepaintRect();
423 }
424
425 int
426 RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
427 {
428     int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf);
429     if (!includeOverflowInterior && hasOverflowClip())
430         return bottom;
431
432     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
433     // For now, we have to descend into all the children, since we may have a huge abs div inside
434     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
435     // the abs div.
436     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
437         if (!c->isFloatingOrPositioned() && !c->isText()) {
438             int lp = c->yPos() + c->lowestPosition(false);
439             bottom = kMax(bottom, lp);
440         }
441     }
442     
443     return bottom;
444 }
445
446 int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
447 {
448     int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf);
449     if (!includeOverflowInterior && hasOverflowClip())
450         return right;
451
452     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
453     // For now, we have to descend into all the children, since we may have a huge abs div inside
454     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
455     // the abs div.
456     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
457         if (!c->isFloatingOrPositioned() && !c->isText()) {
458             int rp = c->xPos() + c->rightmostPosition(false);
459             right = kMax(right, rp);
460         }
461     }
462     
463     return right;
464 }
465
466 int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
467 {
468     int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf);
469     if (!includeOverflowInterior && hasOverflowClip())
470         return left;
471     
472     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
473     // For now, we have to descend into all the children, since we may have a huge abs div inside
474     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
475     // the abs div.
476     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
477         if (!c->isFloatingOrPositioned() && !c->isText()) {
478             int lp = c->xPos() + c->leftmostPosition(false);
479             left = kMin(left, lp);
480         }
481     }
482     
483     return left;
484 }
485
486 void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &width, int &height)
487 {
488     if (firstChild() || style()->display() == INLINE) {
489         // Do the normal calculation
490         RenderBox::caretPos(offset, override, _x, _y, width, height);
491         return;
492     }
493
494     // This is a special case:
495     // The element is not an inline element, and it's empty. So we have to
496     // calculate a fake position to indicate where objects are to be inserted.
497     
498     // EDIT FIXME: this does neither take into regard :first-line nor :first-letter
499     // However, as soon as some content is entered, the line boxes will be
500     // constructed properly and this kludge is not called any more. So only
501     // the caret size of an empty :first-line'd block is wrong, but I think we
502     // can live with that.
503     RenderStyle *currentStyle = style(true);
504     //height = currentStyle->fontMetrics().height();
505     height = lineHeight(true);
506     width = 1;
507
508     // EDIT FIXME: This needs to account for text direction
509     int w = this->width();
510     switch (currentStyle->textAlign()) {
511         case LEFT:
512         case KHTML_LEFT:
513         case TAAUTO:
514         case JUSTIFY:
515             _x = 0;
516             break;
517         case CENTER:
518         case KHTML_CENTER:
519             _x = w / 2;
520         break;
521         case RIGHT:
522         case KHTML_RIGHT:
523             _x = w;
524         break;
525     }
526     
527     _y = 0;
528     
529     int absx, absy;
530     absolutePosition(absx, absy, false);
531     _x += absx + paddingLeft() + borderLeft();
532     _y += absy + paddingTop() + borderTop();
533 }