Reviewed by John.
[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
301 static QRect enclosingPositionedRect (RenderObject *n)
302 {
303     RenderObject *enclosingParent =  n->containingBlock();
304     QRect rect(0,0,0,0);
305     if (enclosingParent) {
306         int ox, oy;
307         enclosingParent->absolutePosition(ox, oy);
308         rect.setX(ox);
309         rect.setY(oy);
310         rect.setWidth (enclosingParent->width());
311         rect.setHeight (enclosingParent->height());
312     }
313     return rect;
314 }
315
316 void RenderCanvas::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
317 {
318     rects.append(QRect(_tx, _ty, m_layer->width(), m_layer->height()));
319 }
320
321 QRect RenderCanvas::selectionRect() const
322 {
323     RenderObject *r = m_selectionStart;
324     if (!r)
325         return QRect();
326     
327     QRect selectionRect = enclosingPositionedRect(r);
328
329     while (r && r != m_selectionEnd)
330     {
331         RenderObject* n;
332         if ( !(n = r->firstChild()) ){
333             if ( !(n = r->nextSibling()) )
334             {
335                 n = r->parent();
336                 while (n && !n->nextSibling())
337                     n = n->parent();
338                 if (n)
339                     n = n->nextSibling();
340             }
341         }
342         r = n;
343         if (r) {
344             selectionRect = selectionRect.unite(enclosingPositionedRect(r));
345         }
346     }
347
348     return selectionRect;
349 }
350
351 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
352 {
353     // Check we got valid renderobjects. www.msnbc.com and clicking around, to find the case where this happened.
354     if ( !s || !e )
355     {
356         kdWarning(6040) << "RenderCanvas::setSelection() called with start=" << s << " end=" << e << endl;
357         return;
358     }
359     //kdDebug( 6040 ) << "RenderCanvas::setSelection(" << s << "," << sp << "," << e << "," << ep << ")" << endl;
360
361 #if APPLE_CHANGES
362     // Cut out early if the selection hasn't changed.
363     if (m_selectionStart == s && m_selectionStartPos == sp &&
364         m_selectionEnd == e && m_selectionEndPos == ep){
365         return;
366     }
367
368     // Record the old selected objects.  Will be used later
369     // to delta again the selected objects.
370     
371     RenderObject *oldStart = m_selectionStart;
372     int oldStartPos = m_selectionStartPos;
373     RenderObject *oldEnd = m_selectionEnd;
374     int oldEndPos = m_selectionEndPos;
375     QPtrList<RenderObject> oldSelectedInside;
376     QPtrList<RenderObject> newSelectedInside;
377     RenderObject *os = oldStart;
378
379     while (os && os != oldEnd)
380     {
381         RenderObject* no;
382         if (!(no = os->firstChild())) {
383             if ( !(no = os->nextSibling()) )
384             {
385                 no = os->parent();
386                 while (no && !no->nextSibling())
387                     no = no->parent();
388                 if (no)
389                     no = no->nextSibling();
390             }
391         }
392         if (os->selectionState() == SelectionInside && !oldSelectedInside.containsRef(os))
393             oldSelectedInside.append(os);
394             
395         os = no;
396     }
397     clearSelection(false);
398 #else
399     clearSelection();
400 #endif
401
402     while (s->firstChild())
403         s = s->firstChild();
404     while (e->lastChild())
405         e = e->lastChild();
406
407     // set selection start
408     if (m_selectionStart)
409         m_selectionStart->setIsSelectionBorder(false);
410     m_selectionStart = s;
411     if (m_selectionStart)
412         m_selectionStart->setIsSelectionBorder(true);
413     m_selectionStartPos = sp;
414
415     // set selection end
416     if (m_selectionEnd)
417         m_selectionEnd->setIsSelectionBorder(false);
418     m_selectionEnd = e;
419     if (m_selectionEnd)
420         m_selectionEnd->setIsSelectionBorder(true);
421     m_selectionEndPos = ep;
422
423     // update selection status of all objects between m_selectionStart and m_selectionEnd
424     RenderObject* o = s;
425     
426     while (o && o!=e)
427     {
428         if (o->style()->userSelect() != SELECT_NONE)
429             o->setSelectionState(SelectionInside);
430 //      kdDebug( 6040 ) << "setting selected " << o << ", " << o->isText() << endl;
431         RenderObject* no = 0;
432         if (!(no = o->firstChild()))
433             if ( !(no = o->nextSibling()) )
434             {
435                 no = o->parent();
436                 while (no && !no->nextSibling())
437                     no = no->parent();
438                 if (no)
439                     no = no->nextSibling();
440             }
441 #if APPLE_CHANGES
442         if (o->selectionState() == SelectionInside && !newSelectedInside.containsRef(o))
443             newSelectedInside.append(o);
444 #endif
445             
446         o=no;
447     }
448     
449     if (s->style()->userSelect() != SELECT_NONE)
450         s->setSelectionState(SelectionStart);
451     if (e->style()->userSelect() != SELECT_NONE)
452         e->setSelectionState(SelectionEnd);
453     if (s == e && s->style()->userSelect() != SELECT_NONE)
454         s->setSelectionState(SelectionBoth);
455
456 #if APPLE_CHANGES
457     if (!m_view)
458         return;
459
460     newSelectedInside.remove (s);
461     newSelectedInside.remove (e);
462     
463     QRect updateRect;
464
465     // Don't use repaint() because it will cause all rects to
466     // be united (see khtmlview::scheduleRepaint()).  Instead
467     // just draw damage rects for objects that have a change
468     // in selection state.
469     
470     // Are any of the old fully selected objects not in the new selection?
471     // If so we have to draw them.
472     // Could be faster by building list of non-intersecting rectangles rather
473     // than unioning rectangles.
474     QPtrListIterator<RenderObject> oldIterator(oldSelectedInside);
475     bool firstRect = true;
476     for (; oldIterator.current(); ++oldIterator){
477         if (!newSelectedInside.containsRef(oldIterator.current())){
478             if (firstRect){
479                 updateRect = enclosingPositionedRect(oldIterator.current());
480                 firstRect = false;
481             }
482             else
483                 updateRect = updateRect.unite(enclosingPositionedRect(oldIterator.current()));
484         }
485     }
486     if (!firstRect){
487         m_view->updateContents( updateRect );
488     }
489
490     // Are any of the new fully selected objects not in the previous selection?
491     // If so we have to draw them.
492     // Could be faster by building list of non-intersecting rectangles rather
493     // than unioning rectangles.
494     QPtrListIterator<RenderObject> newIterator(newSelectedInside);
495     firstRect = true;
496     for (; newIterator.current(); ++newIterator){
497         if (!oldSelectedInside.containsRef(newIterator.current())){
498             if (firstRect){
499                 updateRect = enclosingPositionedRect(newIterator.current());
500                 firstRect = false;
501             }
502             else
503                 updateRect = updateRect.unite(enclosingPositionedRect(newIterator.current()));
504         }
505     }
506     if (!firstRect) {
507         m_view->updateContents( updateRect );
508     }
509     
510     // Is the new starting object different, or did the position in the starting
511     // element change?  If so we have to draw it.
512     if (oldStart != m_selectionStart || 
513         (oldStart == oldEnd && (oldStartPos != m_selectionStartPos || oldEndPos != m_selectionEndPos)) ||
514         (oldStart == m_selectionStart && oldStartPos != m_selectionStartPos)){
515         m_view->updateContents( enclosingPositionedRect(m_selectionStart) );
516     }
517
518     // Draw the old selection start object if it's different than the new selection
519     // start object.
520     if (oldStart && oldStart != m_selectionStart){
521         m_view->updateContents( enclosingPositionedRect(oldStart) );
522     }
523     
524     // Does the selection span objects and is the new end object different, or did the position
525     // in the end element change?  If so we have to draw it.
526     if (oldStart != oldEnd && 
527         (oldEnd != m_selectionEnd ||
528         (oldEnd == m_selectionEnd && oldEndPos != m_selectionEndPos))){
529         m_view->updateContents( enclosingPositionedRect(m_selectionEnd) );
530     }
531     
532     // Draw the old selection end object if it's different than the new selection
533     // end object.
534     if (oldEnd && oldEnd != m_selectionEnd){
535         m_view->updateContents( enclosingPositionedRect(oldEnd) );
536     }
537 #else
538     repaint();
539 #endif
540 }
541
542
543 #if APPLE_CHANGES
544 void RenderCanvas::clearSelection(bool doRepaint)
545 #else
546 void RenderCanvas::clearSelection()
547 #endif
548 {
549     // update selection status of all objects between m_selectionStart and m_selectionEnd
550     RenderObject* o = m_selectionStart;
551     while (o && o!=m_selectionEnd)
552     {
553         if (o->selectionState()!=SelectionNone)
554 #if APPLE_CHANGES
555             if (doRepaint)
556 #endif
557                 o->repaint();
558         o->setSelectionState(SelectionNone);
559         RenderObject* no;
560         if ( !(no = o->firstChild()) )
561             if ( !(no = o->nextSibling()) )
562             {
563                 no = o->parent();
564                 while (no && !no->nextSibling())
565                     no = no->parent();
566                 if (no)
567                     no = no->nextSibling();
568             }
569         o=no;
570     }
571     if (m_selectionEnd)
572     {
573         m_selectionEnd->setSelectionState(SelectionNone);
574         // check if selection is collapsed
575         if (m_selectionStart != m_selectionEnd || m_selectionStartPos != m_selectionEndPos)
576 #if APPLE_CHANGES
577             if (doRepaint)
578 #endif
579                 m_selectionEnd->repaint();
580     }
581
582     // set selection start & end to 0
583     if (m_selectionStart)
584         m_selectionStart->setIsSelectionBorder(false);
585     m_selectionStart = 0;
586     m_selectionStartPos = -1;
587
588     if (m_selectionEnd)
589         m_selectionEnd->setIsSelectionBorder(false);
590     m_selectionEnd = 0;
591     m_selectionEndPos = -1;
592 }
593
594 void RenderCanvas::selectionStartEnd(int& spos, int& epos)
595 {
596     spos = m_selectionStartPos;
597     epos = m_selectionEndPos;
598 }
599
600 QRect RenderCanvas::viewRect() const
601 {
602     if (m_printingMode)
603         return QRect(0,0, m_width, m_height);
604     else if (m_view)
605         return QRect(m_view->contentsX(),
606             m_view->contentsY(),
607             m_view->visibleWidth(),
608             m_view->visibleHeight());
609     else return QRect(0,0,m_rootWidth,m_rootHeight);
610 }
611
612 int RenderCanvas::docHeight() const
613 {
614     int h;
615     if (m_printingMode || !m_view)
616         h = m_height;
617     else
618         h = m_view->visibleHeight();
619
620     int lowestPos = lowestPosition();
621     if( lowestPos > h )
622         h = lowestPos;
623
624     // FIXME: This doesn't do any margin collapsing.
625     // Instead of this dh computation we should keep the result
626     // when we call RenderBlock::layout.
627     int dh = 0;
628     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
629         dh += c->height() + c->marginTop() + c->marginBottom();
630     }
631     if( dh > h )
632         h = dh;
633
634     return h;
635 }
636
637 int RenderCanvas::docWidth() const
638 {
639     int w;
640     if (m_printingMode || !m_view)
641         w = m_width;
642     else
643         w = m_view->visibleWidth();
644
645     int rightmostPos = rightmostPosition();
646     if( rightmostPos > w )
647         w = rightmostPos;
648
649     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
650         int dw = c->width() + c->marginLeft() + c->marginRight();
651         if( dw > w )
652             w = dw;
653     }
654     return w;
655 }
656
657 #if APPLE_CHANGES
658 // The idea here is to take into account what object is moving the pagination point, and
659 // thus choose the best place to chop it.
660 void RenderCanvas::setBestTruncatedAt(int y, RenderObject *forRenderer, bool forcedBreak)
661 {
662     // Nobody else can set a page break once we have a forced break.
663     if (m_forcedPageBreak) return;
664     
665     // Forced breaks always win over unforced breaks.
666     if (forcedBreak) {
667         m_forcedPageBreak = true;
668         m_bestTruncatedAt = y;
669         return;
670     }
671     
672     // prefer the widest object who tries to move the pagination point
673     int width = forRenderer->width();
674     if (width > m_truncatorWidth) {
675         m_truncatorWidth = width;
676         m_bestTruncatedAt = y;
677     }
678 }
679 #endif