Fix the crash in the layout tests caused by my recent selection changes. Simply...
[WebKit-https.git] / WebCore / khtml / rendering / render_canvas.cpp
1 /**
2  * This file is part of the HTML widget for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  * Copyright (C) 2004 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #include "rendering/render_canvas.h"
23 #include "render_layer.h"
24 #include "xml/dom_docimpl.h"
25
26 #include "khtmlview.h"
27 #include <kdebug.h>
28
29 #if APPLE_CHANGES
30 #include "khtml_part.h"
31 #endif
32
33 using namespace khtml;
34
35 //#define BOX_DEBUG
36 //#define SPEED_DEBUG
37
38 RenderCanvas::RenderCanvas(DOM::NodeImpl* node, KHTMLView *view)
39     : RenderBlock(node)
40 {
41     // Clear our anonymous bit.
42     setIsAnonymous(false);
43         
44     // init RenderObject attributes
45     setInline(false);
46
47     m_view = view;
48     // try to contrain the width to the views width
49
50     m_minWidth = 0;
51     m_height = 0;
52
53     m_width = m_minWidth;
54     m_maxWidth = m_minWidth;
55
56     m_rootWidth = m_rootHeight = 0;
57     m_viewportWidth = m_viewportHeight = 0;
58     
59     setPositioned(true); // to 0,0 :)
60
61     m_printingMode = false;
62     m_printImages = true;
63
64     m_maximalOutlineSize = 0;
65     
66     m_selectionStart = 0;
67     m_selectionEnd = 0;
68     m_selectionStartPos = -1;
69     m_selectionEndPos = -1;
70
71     // Create a new root layer for our layer hierarchy.
72     m_layer = new (node->getDocument()->renderArena()) RenderLayer(this);
73 }
74
75 RenderCanvas::~RenderCanvas()
76 {
77 }
78
79 void RenderCanvas::calcHeight()
80 {
81     if (!m_printingMode && m_view)
82     {
83         m_height = m_view->visibleHeight();
84     }
85     else if (!m_view)
86     {
87         m_height = m_rootHeight;
88     }
89 }
90
91 void RenderCanvas::calcWidth()
92 {
93     // the width gets set by KHTMLView::print when printing to a printer.
94     if(m_printingMode || !m_view)
95     {
96         m_width = m_rootWidth;
97         return;
98     }
99
100     m_width = m_view ?
101                 m_view->frameWidth() + paddingLeft() + paddingRight() + borderLeft() + borderRight()
102                 : m_minWidth;
103
104     if (style()->marginLeft().type==Fixed)
105         m_marginLeft = style()->marginLeft().value;
106     else
107         m_marginLeft = 0;
108
109     if (style()->marginRight().type==Fixed)
110         m_marginRight = style()->marginRight().value;
111     else
112         m_marginRight = 0;
113 }
114
115 void RenderCanvas::calcMinMaxWidth()
116 {
117     KHTMLAssert( !minMaxKnown() );
118
119     RenderBlock::calcMinMaxWidth();
120
121     m_maxWidth = m_minWidth;
122
123     setMinMaxKnown();
124 }
125
126 //#define SPEED_DEBUG
127
128 void RenderCanvas::layout()
129 {
130     KHTMLAssert(!view()->inLayout());
131     
132     if (m_printingMode)
133        m_minWidth = m_width;
134
135     setChildNeedsLayout(true);
136     setMinMaxKnown(false);
137     for (RenderObject *c = firstChild(); c; c = c->nextSibling())
138         c->setChildNeedsLayout(true);
139
140 #ifdef SPEED_DEBUG
141     QTime qt;
142     qt.start();
143 #endif
144     if ( recalcMinMax() )
145         recalcMinMaxWidths();
146 #ifdef SPEED_DEBUG
147     kdDebug() << "RenderCanvas::calcMinMax time used=" << qt.elapsed() << endl;
148     qt.start();
149 #endif
150
151 #ifdef SPEED_DEBUG
152     kdDebug() << "RenderCanvas::layout time used=" << qt.elapsed() << endl;
153     qt.start();
154 #endif
155     if (!m_printingMode) {
156         m_viewportWidth = m_width = m_view->visibleWidth();
157         m_viewportHeight = m_height = m_view->visibleHeight();
158     }
159     else {
160         m_width = m_rootWidth;
161         m_height = m_rootHeight;
162     }
163
164     RenderBlock::layout();
165
166     int docw = docWidth();
167     int doch = docHeight();
168
169     if (!m_printingMode) {
170         setWidth( m_viewportWidth = m_view->visibleWidth() );
171         setHeight(  m_viewportHeight = m_view->visibleHeight() );
172     }
173
174     // ### we could maybe do the call below better and only pass true if the docsize changed.
175     layoutPositionedObjects( true );
176
177 #ifdef SPEED_DEBUG
178     kdDebug() << "RenderCanvas::end time used=" << qt.elapsed() << endl;
179 #endif
180
181     layer()->setHeight(kMax(doch, m_height));
182     layer()->setWidth(kMax(docw, m_width));
183     
184     setNeedsLayout(false);
185 }
186
187 bool RenderCanvas::absolutePosition(int &xPos, int &yPos, bool f)
188 {
189     if ( f && m_view) {
190         xPos = m_view->contentsX();
191         yPos = m_view->contentsY();
192     }
193     else {
194         xPos = yPos = 0;
195     }
196     return true;
197 }
198
199 void RenderCanvas::paint(PaintInfo& i, int _tx, int _ty)
200 {
201 #ifdef DEBUG_LAYOUT
202     kdDebug( 6040 ) << renderName() << "(RenderCanvas) " << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")" << endl;
203 #endif
204     // 1. paint background, borders etc
205     if (i.phase == PaintActionElementBackground) {
206         paintBoxDecorations(i, _tx, _ty);
207         return;
208     }
209     
210     // 2. paint contents
211     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
212         if (!child->layer() && !child->isFloating())
213             child->paint(i, _tx, _ty);
214
215     if (m_view)
216     {
217         _tx += m_view->contentsX();
218         _ty += m_view->contentsY();
219     }
220     
221     // 3. paint floats.
222     if (i.phase == PaintActionFloat)
223         paintFloats(i, _tx, _ty);
224         
225 #ifdef BOX_DEBUG
226     outlineBox(i.p, _tx, _ty);
227 #endif
228 }
229
230 void RenderCanvas::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
231 {
232     // Check to see if we are enclosed by a transparent layer.  If so, we cannot blit
233     // when scrolling, and we need to use slow repaints.
234     DOM::ElementImpl* elt = element()->getDocument()->ownerElement();
235     if (view() && elt) {
236         RenderLayer* layer = elt->renderer()->enclosingLayer();
237         if (layer->isTransparent() || layer->transparentAncestor())
238             view()->useSlowRepaints();
239     }
240     
241     if ((firstChild() && firstChild()->style()->visibility() == VISIBLE) || !view())
242         return;
243
244     // This code typically only executes if the root element's visibility has been set to hidden.
245     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
246     // no background in the child document should show the parent's background.
247     if (elt || view()->isTransparent())
248         view()->useSlowRepaints(); // The parent must show behind the child.
249     else
250         i.p->fillRect(i.r.x(), i.r.y(), i.r.width(), i.r.height(), 
251                     view()->palette().active().color(QColorGroup::Base));
252 }
253
254 void RenderCanvas::repaintViewRectangle(const QRect& ur, bool immediate)
255 {
256     if (m_printingMode || ur.width() == 0 || ur.height() == 0) return;
257
258     QRect vr = viewRect();
259     if (m_view && ur.intersects(vr)) {
260         // We always just invalidate the root view, since we could be an iframe that is clipped out
261         // or even invisible.
262         QRect r = ur.intersect(vr);
263         DOM::ElementImpl* elt = element()->getDocument()->ownerElement();
264         if (!elt)
265             m_view->repaintRectangle(r, immediate);
266         else {
267             // Subtract out the contentsX and contentsY offsets to get our coords within the viewing
268             // rectangle.
269             r.setX(r.x() - m_view->contentsX());
270             r.setY(r.y() - m_view->contentsY());
271             
272             RenderObject* obj = elt->renderer();
273             int frameOffset = (m_view->frameStyle() != QFrame::NoFrame) ? 2 : 0;
274             r.setX(r.x() + obj->borderLeft()+obj->paddingLeft() + frameOffset);
275             r.setY(r.y() + obj->borderTop()+obj->paddingTop() + frameOffset);
276             obj->repaintRectangle(r, immediate);
277         }
278     }
279 }
280
281 QRect RenderCanvas::getAbsoluteRepaintRect()
282 {
283     QRect result;
284     if (m_view && !m_printingMode)
285         result = QRect(m_view->contentsX(), m_view->contentsY(),
286                        m_view->visibleWidth(), m_view->visibleHeight());
287     return result;
288 }
289
290 void RenderCanvas::computeAbsoluteRepaintRect(QRect& r, bool f)
291 {
292     if (m_printingMode) return;
293
294     if (f && m_view) {
295         r.setX(r.x() + m_view->contentsX());
296         r.setY(r.y() + m_view->contentsY());
297     }
298 }
299
300 void RenderCanvas::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
301 {
302     rects.append(QRect(_tx, _ty, m_layer->width(), m_layer->height()));
303 }
304
305 QRect RenderCanvas::selectionRect() const
306 {
307     QRect selectionRect(0,0,0,0);
308     RenderObject *r = m_selectionStart;
309     while (r) {
310         RenderObject* n = 0;
311         if (!(n = r->firstChild())) {
312             if (!(n = r->nextSibling())) {
313                 n = r->parent();
314                 while (n && !n->nextSibling())
315                     n = n->parent();
316                 if (n)
317                     n = n->nextSibling();
318             }
319         }
320         if (r) {
321             QRect selRect(r->selectionRect());
322             if (!selRect.isEmpty()) {
323                 if (selectionRect.isEmpty())
324                     selectionRect = selRect;
325                 else
326                     selectionRect = selectionRect.unite(selRect);
327             }
328         }
329         r = n;
330     }
331
332     return selectionRect;
333 }
334
335 static RenderObject::SelectionInfo getSelectionInfo(const QValueList<RenderObject::SelectionInfo>& l, RenderObject* o)
336 {
337     QValueListConstIterator<RenderObject::SelectionInfo> it;
338     for (it = l.begin(); it != l.end(); ++it) {
339         if ((*it).object() == o)
340             return *it;
341     }
342     return RenderObject::SelectionInfo();
343 }
344
345 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
346 {
347     // Make sure both our start and end objects are defined. 
348     // Check www.msnbc.com and try clicking around to find the case where this happened.
349     if ((s && !e) || (e && !s))
350         return;
351
352     // Just return if the selection hasn't changed.
353     if (m_selectionStart == s && m_selectionStartPos == sp &&
354         m_selectionEnd == e && m_selectionEndPos == ep)
355         return;
356
357     // Record the old selected objects.  These will be used later
358     // when we compare against the new selected objects.
359     int oldStartPos = m_selectionStartPos;
360     int oldEndPos = m_selectionEndPos;
361
362     QValueList<SelectionInfo> oldSelectedObjects;
363     QValueList<SelectionInfo> newSelectedObjects;
364     
365     RenderObject *os = m_selectionStart;
366     while (os) {
367         RenderObject* no = 0;
368         if (os != m_selectionEnd) {
369             if (!(no = os->firstChild())) {
370                 if (!(no = os->nextSibling())) {
371                     no = os->parent();
372                     while (no && !no->nextSibling())
373                         no = no->parent();
374                     if (no)
375                         no = no->nextSibling();
376                 }
377             }
378         }
379         
380         if (os->selectionState() != SelectionNone)
381             oldSelectedObjects.append(SelectionInfo(os));
382
383         os = no;
384     }
385
386     // Now clear the selection.
387     QValueListConstIterator<SelectionInfo> it;
388     for (it = oldSelectedObjects.begin(); it != oldSelectedObjects.end(); ++it)
389         (*it).object()->setSelectionState(SelectionNone);
390     
391     while (s && s->firstChild())
392         s = s->firstChild();
393     while (e && e->lastChild())
394         e = e->lastChild();
395         
396     // set selection start and end
397     m_selectionStart = s;
398     m_selectionStartPos = sp;
399     m_selectionEnd = e;
400     m_selectionEndPos = ep;
401
402     // Update the selection status of all objects between m_selectionStart and m_selectionEnd
403     if (s && s->style()->userSelect() != SELECT_NONE)
404         s->setSelectionState(SelectionStart);
405     if (e && e->style()->userSelect() != SELECT_NONE)
406         e->setSelectionState(SelectionEnd);
407     if (s && s == e && s->style()->userSelect() != SELECT_NONE)
408         s->setSelectionState(SelectionBoth);
409
410     RenderObject* o = s;
411     while (o) {
412         RenderObject* no = 0;
413         if (o != s && o != e && o->style()->userSelect() != SELECT_NONE)
414             o->setSelectionState(SelectionInside);
415         
416         if (o != e) {
417             if (!(no = o->firstChild())) {
418                 if ( !(no = o->nextSibling())) {
419                     no = o->parent();
420                     while (no && !no->nextSibling())
421                         no = no->parent();
422                     if (no)
423                         no = no->nextSibling();
424                 }
425             }
426         }
427         
428         o=no;
429     }
430
431     // Now that the selection state has been updated for the new objects, walk them again and
432     // put them in the new objects list.
433     o = s;
434     while (o) {
435         RenderObject* no = 0;
436         if (o != e) {
437             if (!(no = o->firstChild())) {
438                 if ( !(no = o->nextSibling())) {
439                     no = o->parent();
440                     while (no && !no->nextSibling())
441                         no = no->parent();
442                     if (no)
443                         no = no->nextSibling();
444                 }
445             }
446         }
447         
448         if (o->selectionState() != SelectionNone)
449             newSelectedObjects.append(SelectionInfo(o));
450         
451         o=no;
452     }
453     
454     if (!m_view)
455         return;
456   
457     // Are any of the old fully selected objects not in the new selection?
458     for (it = oldSelectedObjects.begin(); it != oldSelectedObjects.end(); ++it) {
459         SelectionInfo info = getSelectionInfo(newSelectedObjects, (*it).object());
460         //printf("Old Rect: %d %d %d %d\n", (*it).rect().x(), (*it).rect().y(), (*it).rect().width(), (*it).rect().height());
461         if (!info.object() || info.rect() != (*it).rect() || info.state() != (*it).state() ||
462             (info.object() == m_selectionStart && oldStartPos != m_selectionStartPos) ||
463             (info.object() == m_selectionEnd && oldEndPos != m_selectionEndPos))
464             m_view->updateContents((*it).rect());
465     }
466     
467     // Are any of the new fully selected objects not in the previous selection?
468     for (it = newSelectedObjects.begin(); it != newSelectedObjects.end(); ++it) {
469         SelectionInfo info = getSelectionInfo(oldSelectedObjects, (*it).object());
470         //printf("New Rect: %d %d %d %d\n", (*it).rect().x(), (*it).rect().y(), (*it).rect().width(), (*it).rect().height());
471         if (!info.object() || info.rect() != (*it).rect() || info.state() != (*it).state())
472             m_view->updateContents((*it).rect());
473     }
474 }
475
476 void RenderCanvas::clearSelection()
477 {
478     setSelection(0, -1, 0, -1);
479 }
480
481 void RenderCanvas::selectionStartEnd(int& spos, int& epos)
482 {
483     spos = m_selectionStartPos;
484     epos = m_selectionEndPos;
485 }
486
487 QRect RenderCanvas::viewRect() const
488 {
489     if (m_printingMode)
490         return QRect(0,0, m_width, m_height);
491     else if (m_view)
492         return QRect(m_view->contentsX(),
493             m_view->contentsY(),
494             m_view->visibleWidth(),
495             m_view->visibleHeight());
496     else return QRect(0,0,m_rootWidth,m_rootHeight);
497 }
498
499 int RenderCanvas::docHeight() const
500 {
501     int h;
502     if (m_printingMode || !m_view)
503         h = m_height;
504     else
505         h = m_view->visibleHeight();
506
507     int lowestPos = lowestPosition();
508     if( lowestPos > h )
509         h = lowestPos;
510
511     // FIXME: This doesn't do any margin collapsing.
512     // Instead of this dh computation we should keep the result
513     // when we call RenderBlock::layout.
514     int dh = 0;
515     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
516         dh += c->height() + c->marginTop() + c->marginBottom();
517     }
518     if( dh > h )
519         h = dh;
520
521     return h;
522 }
523
524 int RenderCanvas::docWidth() const
525 {
526     int w;
527     if (m_printingMode || !m_view)
528         w = m_width;
529     else
530         w = m_view->visibleWidth();
531
532     int rightmostPos = rightmostPosition();
533     if( rightmostPos > w )
534         w = rightmostPos;
535
536     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
537         int dw = c->width() + c->marginLeft() + c->marginRight();
538         if( dw > w )
539             w = dw;
540     }
541     return w;
542 }
543
544 #if APPLE_CHANGES
545 // The idea here is to take into account what object is moving the pagination point, and
546 // thus choose the best place to chop it.
547 void RenderCanvas::setBestTruncatedAt(int y, RenderObject *forRenderer, bool forcedBreak)
548 {
549     // Nobody else can set a page break once we have a forced break.
550     if (m_forcedPageBreak) return;
551     
552     // Forced breaks always win over unforced breaks.
553     if (forcedBreak) {
554         m_forcedPageBreak = true;
555         m_bestTruncatedAt = y;
556         return;
557     }
558     
559     // prefer the widest object who tries to move the pagination point
560     int width = forRenderer->width();
561     if (width > m_truncatorWidth) {
562         m_truncatorWidth = width;
563         m_bestTruncatedAt = y;
564     }
565 }
566 #endif