1 /**
2  * This file is part of the HTML widget for KDE.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5  *           (C) 2003 Apple Computer, Inc.
6  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
7  *           (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25 #include "rendering/render_canvas.h"
26 #include "rendering/render_layer.h"
27 #include "rendering/render_replaced.h"
28 #include "xml/dom_docimpl.h"
29 
30 #include "khtmlview.h"
31 #include "khtml_part.h"
32 #include "khtml_debug.h"
33 #include <QScrollBar>
34 
35 using namespace khtml;
36 
37 //#define BOX_DEBUG
38 //#define SPEED_DEBUG
39 #ifdef SPEED_DEBUG
40 #include <QTime>
41 #endif
42 
RenderCanvas(DOM::NodeImpl * node,KHTMLView * view)43 RenderCanvas::RenderCanvas(DOM::NodeImpl *node, KHTMLView *view)
44     : RenderBlock(node)
45 {
46     // init RenderObject attributes
47     setInline(false);
48     setIsAnonymous(false);
49 
50     m_view = view;
51     // try to contrain the width to the views width
52 
53     m_minWidth = 0;
54     m_height = 0;
55 
56     m_width = m_minWidth;
57     m_maxWidth = m_minWidth;
58 
59     m_rootWidth = m_rootHeight = 0;
60     m_viewportWidth = m_viewportHeight = 0;
61     m_cachedDocWidth = m_cachedDocHeight = -1;
62 
63     setPositioned(true); // to 0,0 :)
64 
65     m_staticMode = false;
66     m_pagedMode = false;
67     m_printImages = true;
68 
69     m_pageTop = 0;
70     m_pageBottom = 0;
71 
72     m_page = nullptr;
73 
74     m_maximalOutlineSize = 0;
75 
76     m_selectionStart = nullptr;
77     m_selectionEnd = nullptr;
78     m_selectionStartPos = -1;
79     m_selectionEndPos = -1;
80 
81     m_needsWidgetMasks = false;
82 
83     m_isPerformingLayout = false;
84 
85     // Create a new root layer for our layer hierarchy.
86     m_layer = new(node->document()->renderArena()) RenderLayer(this);
87 }
88 
~RenderCanvas()89 RenderCanvas::~RenderCanvas()
90 {
91     delete m_page;
92 }
93 
setStyle(RenderStyle * style)94 void RenderCanvas::setStyle(RenderStyle *style)
95 {
96     /*
97     if (m_pagedMode)
98         style->setOverflow(OHIDDEN); */
99     RenderBlock::setStyle(style);
100 }
101 
calcHeight()102 void RenderCanvas::calcHeight()
103 {
104     if (m_pagedMode || !m_view) {
105         m_height = m_rootHeight;
106     } else {
107         m_height = m_view->visibleHeight();
108     }
109 }
110 
calcWidth()111 void RenderCanvas::calcWidth()
112 {
113     // the width gets set by KHTMLView::print when printing to a printer.
114     if (m_pagedMode || !m_view) {
115         m_width = m_rootWidth;
116         return;
117     }
118 
119     m_width = m_view ? m_view->frameWidth() : m_minWidth;
120 
121     if (style()->marginLeft().isFixed()) {
122         m_marginLeft = style()->marginLeft().value();
123     } else {
124         m_marginLeft = 0;
125     }
126 
127     if (style()->marginRight().isFixed()) {
128         m_marginRight = style()->marginRight().value();
129     } else {
130         m_marginRight = 0;
131     }
132 }
133 
calcMinMaxWidth()134 void RenderCanvas::calcMinMaxWidth()
135 {
136     KHTMLAssert(!minMaxKnown());
137 
138     RenderBlock::calcMinMaxWidth();
139 
140     m_maxWidth = m_minWidth;
141 
142     setMinMaxKnown();
143 }
144 
layout()145 void RenderCanvas::layout()
146 {
147     m_isPerformingLayout = true;
148 
149     if (m_pagedMode) {
150         m_minWidth = m_width;
151 //        m_maxWidth = m_width;
152     }
153 
154     m_needsFullRepaint =  markedForRepaint() || !view() || view()->needsFullRepaint() || m_pagedMode;
155 
156     setChildNeedsLayout(true);
157     setMinMaxKnown(false);
158     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
159         c->setChildNeedsLayout(true);
160     }
161 
162     int oldWidth = m_width;
163     int oldHeight = m_height;
164 
165     m_cachedDocWidth = m_cachedDocHeight = -1;
166 
167     if (m_pagedMode || !m_view) {
168         m_width = m_rootWidth;
169         m_height = m_rootHeight;
170     } else {
171         m_viewportWidth = m_width = m_view->visibleWidth();
172         m_viewportHeight = m_height = m_view->visibleHeight();
173     }
174 
175 #ifdef SPEED_DEBUG
176     QTime qt;
177     qt.start();
178 #endif
179 
180     if (recalcMinMax()) {
181         recalcMinMaxWidths();
182     }
183 
184 #ifdef SPEED_DEBUG
185     qCDebug(KHTML_LOG) << "RenderCanvas::calcMinMax time used=" << qt.elapsed();
186     qt.start();
187 #endif
188 
189     bool relayoutChildren = (oldWidth != m_width) || (oldHeight != m_height);
190 
191     RenderBlock::layoutBlock(relayoutChildren);
192 
193 #ifdef SPEED_DEBUG
194     qCDebug(KHTML_LOG) << "RenderCanvas::layout time used=" << qt.elapsed();
195     qt.start();
196 #endif
197 
198     updateDocumentSize();
199 
200     layer()->updateLayerPositions(layer(), needsFullRepaint(), true);
201 
202     if (!m_pagedMode && m_needsWidgetMasks) {
203         layer()->updateWidgetMasks(layer());
204     }
205 
206     scheduleDeferredRepaints();
207     setNeedsLayout(false);
208 
209     m_isPerformingLayout = false;
210 #ifdef SPEED_DEBUG
211     qCDebug(KHTML_LOG) << "RenderCanvas::end time used=" << qt.elapsed();
212 #endif
213 }
214 
setNeedsWidgetMasks(bool b)215 void RenderCanvas::setNeedsWidgetMasks(bool b)
216 {
217     if (b == m_needsWidgetMasks) {
218         return;
219     }
220     m_needsWidgetMasks = b;
221     KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(m_view);
222     // ### should be reversible
223     if (k && b && k->m_kwp->isRedirected()) {
224         k->m_kwp->setIsRedirected(!b);
225         if (k->m_kwp->renderWidget()) {
226             k->m_kwp->renderWidget()->setNeedsLayout(true);
227         }
228     }
229 }
230 
updateDocumentSize()231 void RenderCanvas::updateDocumentSize()
232 {
233     // update our cached document size
234     int hDocH = m_cachedDocHeight = docHeight();
235     int hDocW = m_cachedDocWidth = docWidth();
236 
237     int zLevel = m_view ? m_view->zoomLevel() : 100;
238     hDocW = hDocW * zLevel / 100;
239     hDocH = hDocH * zLevel / 100;
240 
241     if (!m_pagedMode && m_view) {
242 
243         // we need to adjust the document's size to make sure we don't enter
244         // an endless cycle of scrollbars being added, then removed at the next layout.
245 
246         bool vss = m_view->verticalScrollBar()->isVisible();
247         bool hss = m_view->horizontalScrollBar()->isVisible();
248 
249         // calculate the extent of scrollbars
250         int vsPixSize = m_view->verticalScrollBar()->sizeHint().width();
251         int hsPixSize = m_view->horizontalScrollBar()->sizeHint().height();
252 
253         // this variable holds the size the viewport will have after the inner content is resized to
254         // the new document dimensions
255         QSize viewport = m_view->maximumViewportSize();
256 
257         // of course, if the scrollbar policy isn't auto, there's no point adjusting any value..
258         int overrideH = m_view->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded ? 0 : hDocH;
259         int overrideW = m_view->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded ? 0 : hDocW;
260 
261         if (!overrideW && hDocW > viewport.width()) {
262             viewport.setHeight(viewport.height() - hsPixSize);
263         }
264         if (!overrideH && hDocH > viewport.height()) {
265             viewport.setWidth(viewport.width() - vsPixSize);
266         }
267 
268         // if we are about to show a scrollbar, and the document is sized to the viewport w or h,
269         // then reserve the scrollbar space so that it doesn't trigger the _other_ scrollbar
270 
271         if (!vss && m_width - vsPixSize == viewport.width() &&
272                 hDocW <= m_width) {
273             hDocW = qMin(hDocW, viewport.width());
274         }
275 
276         if (!hss && m_height - hsPixSize == viewport.height() &&
277                 hDocH <= m_height) {
278             hDocH = qMin(hDocH, viewport.height());
279         }
280 
281         // likewise, if a scrollbar is shown, and we have a cunning plan to turn it off,
282         // think again if we are falling downright in the hysteresis zone
283 
284         if (vss && viewport.width() > hDocW && hDocW > m_view->visibleWidth()) {
285             hDocW = viewport.width() + 1;
286         }
287 
288         if (hss && viewport.height() > hDocH && hDocH > m_view->visibleHeight()) {
289             hDocH = viewport.height() + 1;
290         }
291 
292         m_view->resizeContents((overrideW ? overrideW : hDocW), (overrideH ? overrideH : hDocH));
293 
294     }
295     layer()->resize(qMax(m_cachedDocWidth, int(m_width)), qMax(m_cachedDocHeight, m_height));
296 }
297 
updateDocSizeAfterLayerTranslation(RenderObject * o,bool posXOffset,bool posYOffset)298 void RenderCanvas::updateDocSizeAfterLayerTranslation(RenderObject *o, bool posXOffset, bool posYOffset)
299 {
300     if (needsLayout()) {
301         return;
302     }
303     int rightmost, lowest;
304     o->absolutePosition(rightmost, lowest);
305     if (posXOffset) {
306         rightmost += o->rightmostPosition(false, true);
307         setCachedDocWidth(qMax(docWidth(), rightmost));
308     } else {
309         setCachedDocWidth(-1);
310     }
311     if (posYOffset) {
312         lowest += o->lowestPosition(false, true);
313         setCachedDocHeight(qMax(docHeight(), lowest));
314     } else {
315         setCachedDocHeight(-1);
316     }
317 //    qCDebug(KHTML_LOG) << " posXOffset: " << posXOffset << " posYOffset " << posYOffset << " m_cachedDocWidth  " <<  m_cachedDocWidth << " m_cachedDocHeight  " << m_cachedDocHeight;
318     updateDocumentSize();
319 }
320 
staticRegion() const321 QRegion RenderCanvas::staticRegion() const
322 {
323     QRegion ret = QRegion();
324 
325     // position:fixed objects
326     if (m_positionedObjects) {
327         RenderObject *obj;
328         QListIterator<RenderObject *> it(*m_positionedObjects);
329         while (it.hasNext()) {
330             obj = it.next();
331             if (obj->style()->position() == PFIXED && obj->layer()) {
332                 ret += obj->layer()->paintedRegion(layer());
333                 assert(m_fixedPosition.contains(obj));
334             }
335         }
336     }
337 
338     // background-attachment:fixed images
339     QSetIterator<RenderObject *> i(m_fixedBackground);
340     while (i.hasNext()) {
341         RenderObject *ro = i.next();
342         if (ro && ro->isBox()) {
343             int d1, d2, d3, d4;
344             const BackgroundLayer *bgLayer = ro->style()->backgroundLayers();
345             while (bgLayer) {
346                 CachedImage *bg = bgLayer->backgroundAttachment() == BGAFIXED ? bgLayer->backgroundImage() : nullptr;
347                 if (bg && bg->isComplete() && !bg->isTransparent() && !bg->isErrorImage()) {
348                     int xpos, ypos;
349                     absolutePosition(xpos, ypos);
350                     ret += static_cast<RenderBox *>(ro)->getFixedBackgroundImageRect(bgLayer, d1, d2, d3, d4)
351                            .intersected(QRect(xpos, ypos, ro->width(), ro->height()));
352                 }
353                 bgLayer = bgLayer->next();
354             }
355         }
356     }
357     return ret;
358 }
359 
needsFullRepaint() const360 bool RenderCanvas::needsFullRepaint() const
361 {
362     return m_needsFullRepaint || m_pagedMode;
363 }
364 
repaintViewRectangle(int x,int y,int w,int h,bool asap)365 void RenderCanvas::repaintViewRectangle(int x, int y, int w, int h, bool asap)
366 {
367     KHTMLAssert(view());
368     view()->scheduleRepaint(x, y, w, h, asap);
369 }
370 
absolutePosition(int & xPos,int & yPos,bool f) const371 bool RenderCanvas::absolutePosition(int &xPos, int &yPos, bool f) const
372 {
373     if (f && m_pagedMode) {
374         xPos = 0;
375         yPos = m_pageTop;
376     } else if (f && m_view) {
377         xPos = m_view->contentsX();
378         yPos = m_view->contentsY();
379     } else {
380         xPos = yPos = 0;
381     }
382     return true;
383 }
384 
paint(PaintInfo & paintInfo,int _tx,int _ty)385 void RenderCanvas::paint(PaintInfo &paintInfo, int _tx, int _ty)
386 {
387 #ifdef DEBUG_LAYOUT
388     qCDebug(KHTML_LOG) << renderName() << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")";
389 #endif
390 
391     // 1. paint background, borders etc
392     if (paintInfo.phase == PaintActionElementBackground) {
393         paintBoxDecorations(paintInfo, _tx, _ty);
394         return;
395     }
396 
397     // 2. paint contents
398     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
399         if (!child->layer() && !child->isFloating()) {
400             child->paint(paintInfo, _tx, _ty);
401         }
402 
403     // 3. paint floats.
404     if (paintInfo.phase == PaintActionFloat) {
405         paintFloats(paintInfo, _tx, _ty);
406     }
407 
408 #ifdef BOX_DEBUG
409     if (m_view) {
410         _tx += m_view->contentsX();
411         _ty += m_view->contentsY();
412     }
413 
414     outlineBox(paintInfo.p, _tx, _ty);
415 #endif
416 
417 }
418 
paintBoxDecorations(PaintInfo & paintInfo,int,int)419 void RenderCanvas::paintBoxDecorations(PaintInfo &paintInfo, int /*_tx*/, int /*_ty*/)
420 {
421     if ((firstChild() && firstChild()->style()->visibility() == VISIBLE) || !view()) {
422         return;
423     }
424 
425     paintInfo.p->fillRect(paintInfo.r, view()->palette().color(QPalette::Active, QPalette::Base));
426 }
427 
repaintRectangle(int x,int y,int w,int h,Priority p,bool f)428 void RenderCanvas::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
429 {
430     if (m_staticMode) {
431         return;
432     }
433 //    qCDebug(KHTML_LOG) << "updating views contents (" << x << "/" << y << ") (" << w << "/" << h << ")";
434 
435     if (f && m_pagedMode) {
436         y += m_pageTop;
437     } else if (f && m_view) {
438         x += m_view->contentsX();
439         y += m_view->contentsY();
440     }
441 
442     QRect vr = viewRect();
443     QRect ur(x, y, w, h);
444 
445     if (m_view && ur.intersects(vr)) {
446 
447         if (p == RealtimePriority) {
448             m_view->updateContents(ur);
449         } else if (p == HighPriority) {
450             m_view->scheduleRepaint(x, y, w, h, true /*asap*/);
451         } else {
452             m_view->scheduleRepaint(x, y, w, h);
453         }
454     }
455 }
456 
deferredRepaint(RenderObject * o)457 void RenderCanvas::deferredRepaint(RenderObject *o)
458 {
459     m_dirtyChildren.append(o);
460 }
461 
scheduleDeferredRepaints()462 void RenderCanvas::scheduleDeferredRepaints()
463 {
464     if (!needsFullRepaint()) {
465         QList<RenderObject *>::const_iterator it;
466         for (it = m_dirtyChildren.constBegin(); it != m_dirtyChildren.constEnd(); ++it) {
467             (*it)->repaint();
468         }
469     }
470     //qCDebug(KHTML_LOG) << "scheduled deferred repaints: " << m_dirtyChildren.count() << " needed full repaint: " << needsFullRepaint();
471     m_dirtyChildren.clear();
472 }
473 
repaint(Priority p)474 void RenderCanvas::repaint(Priority p)
475 {
476     if (m_view && !m_staticMode) {
477         if (p == RealtimePriority) {
478             //m_view->resizeContents(docWidth(), docHeight());
479             m_view->unscheduleRepaint();
480             if (needsLayout()) {
481                 m_view->scheduleRelayout();
482                 return;
483             }
484             // ### same as in repaintRectangle
485             m_view->updateContents(m_view->contentsX(), m_view->contentsY(),
486                                    m_view->visibleWidth(), m_view->visibleHeight());
487         } else if (p == HighPriority)
488             m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
489                                     m_view->visibleWidth(), m_view->visibleHeight(), true /*asap*/);
490         else
491             m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
492                                     m_view->visibleWidth(), m_view->visibleHeight());
493     }
494 }
495 
enclosingPositionedRect(RenderObject * n)496 static QRect enclosingPositionedRect(RenderObject *n)
497 {
498     RenderObject *enclosingParent =  n->containingBlock();
499     QRect rect(0, 0, 0, 0);
500     if (enclosingParent) {
501         int ox, oy;
502         enclosingParent->absolutePosition(ox, oy);
503         if (!enclosingParent->hasOverflowClip()) {
504             ox += enclosingParent->overflowLeft();
505             oy += enclosingParent->overflowTop();
506         }
507         rect.setX(ox);
508         rect.setY(oy);
509         rect.setWidth(enclosingParent->effectiveWidth());
510         rect.setHeight(enclosingParent->effectiveHeight());
511     }
512     return rect;
513 }
514 
addStaticObject(RenderObject * o,bool pos)515 void RenderCanvas::addStaticObject(RenderObject *o, bool pos)
516 {
517     QSet<RenderObject *> &set = pos ? m_fixedPosition : m_fixedBackground;
518     if (!o || !o->isBox() || set.contains(o)) {
519         return;
520     }
521     set.insert(o);
522     if (view()) {
523         view()->addStaticObject(pos);
524     }
525 }
526 
removeStaticObject(RenderObject * o,bool pos)527 void RenderCanvas::removeStaticObject(RenderObject *o, bool pos)
528 {
529     QSet<RenderObject *> &set = pos ? m_fixedPosition : m_fixedBackground;
530     if (!o || !o->isBox() || !set.contains(o)) {
531         return;
532     }
533     set.remove(o);
534     if (view()) {
535         view()->removeStaticObject(pos);
536     }
537 }
538 
selectionRect() const539 QRect RenderCanvas::selectionRect() const
540 {
541     RenderObject *r = m_selectionStart;
542     if (!r) {
543         return QRect();
544     }
545 
546     QRect selectionRect = enclosingPositionedRect(r);
547 
548     while (r && r != m_selectionEnd) {
549         RenderObject *n;
550         if (!(n = r->firstChild())) {
551             if (!(n = r->nextSibling())) {
552                 n = r->parent();
553                 while (n && !n->nextSibling()) {
554                     n = n->parent();
555                 }
556                 if (n) {
557                     n = n->nextSibling();
558                 }
559             }
560         }
561         r = n;
562         if (r) {
563             selectionRect = selectionRect.united(enclosingPositionedRect(r));
564         }
565     }
566 
567     return selectionRect;
568 }
569 
setSelection(RenderObject * s,int sp,RenderObject * e,int ep)570 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
571 {
572     // Check we got valid renderobjects. www.msnbc.com and clicking
573     // around, to find the case where this happened.
574     if (!s || !e) {
575         qCWarning(KHTML_LOG) << "RenderCanvas::setSelection() called with start=" << s << " end=" << e;
576         return;
577     }
578 //     qCDebug(KHTML_LOG) << "RenderCanvas::setSelection(" << s << "," << sp << "," << e << "," << ep << ")";
579 
580     bool changedSelectionBorder = (s != m_selectionStart || e != m_selectionEnd);
581 
582     // Cut out early if the selection hasn't changed.
583     if (!changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos == ep) {
584         return;
585     }
586 
587     // Record the old selected objects.  Will be used later
588     // to delta against the selected objects.
589 
590     RenderObject *oldStart = m_selectionStart;
591     int oldStartPos = m_selectionStartPos;
592     RenderObject *oldEnd = m_selectionEnd;
593     int oldEndPos = m_selectionEndPos;
594     QList<RenderObject *> oldSelectedInside;
595     QList<RenderObject *> newSelectedInside;
596     RenderObject *os = oldStart;
597 
598     while (os && os != oldEnd) {
599         RenderObject *no;
600         if (!(no = os->firstChild())) {
601             if (!(no = os->nextSibling())) {
602                 no = os->parent();
603                 while (no && !no->nextSibling()) {
604                     no = no->parent();
605                 }
606                 if (no) {
607                     no = no->nextSibling();
608                 }
609             }
610         }
611         if (os->selectionState() == SelectionInside && !oldSelectedInside.contains(os)) {
612             oldSelectedInside.append(os);
613         }
614 
615         os = no;
616     }
617     if (changedSelectionBorder) {
618         clearSelection(false);
619     }
620 
621     while (s->firstChild()) {
622         s = s->firstChild();
623     }
624     while (e->lastChild()) {
625         e = e->lastChild();
626     }
627 
628 #if 0
629     bool changedSelectionBorder = (s != m_selectionStart || e != m_selectionEnd);
630 
631     if (!changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos = ep) {
632         return;
633     }
634 #endif
635 
636     // set selection start
637     if (m_selectionStart) {
638         m_selectionStart->setIsSelectionBorder(false);
639     }
640     m_selectionStart = s;
641     if (m_selectionStart) {
642         m_selectionStart->setIsSelectionBorder(true);
643     }
644     m_selectionStartPos = sp;
645 
646     // set selection end
647     if (m_selectionEnd) {
648         m_selectionEnd->setIsSelectionBorder(false);
649     }
650     m_selectionEnd = e;
651     if (m_selectionEnd) {
652         m_selectionEnd->setIsSelectionBorder(true);
653     }
654     m_selectionEndPos = ep;
655 
656 #if 0
657     // qCDebug(KHTML_LOG) << "old selection (" << oldStart << "," << oldStartPos << "," << oldEnd << "," << oldEndPos << ")";
658     // qCDebug(KHTML_LOG) << "new selection (" << s << "," << sp << "," << e << "," << ep << ")";
659 #endif
660 
661     // update selection status of all objects between m_selectionStart and m_selectionEnd
662     RenderObject *o = s;
663 
664     while (o && o != e) {
665         o->setSelectionState(SelectionInside);
666 //      qCDebug(KHTML_LOG) << "setting selected " << o << ", " << o->isText();
667         RenderObject *no;
668         if (!(no = o->firstChild()))
669             if (!(no = o->nextSibling())) {
670                 no = o->parent();
671                 while (no && !no->nextSibling()) {
672                     no = no->parent();
673                 }
674                 if (no) {
675                     no = no->nextSibling();
676                 }
677             }
678         if (o->selectionState() == SelectionInside && !newSelectedInside.contains(o)) {
679             newSelectedInside.append(o);
680         }
681 
682         o = no;
683     }
684     s->setSelectionState(SelectionStart);
685     e->setSelectionState(SelectionEnd);
686     if (s == e) {
687         s->setSelectionState(SelectionBoth);
688     }
689 
690     if (!m_view) {
691         return;
692     }
693 
694     int i;
695     i = newSelectedInside.indexOf(s);
696     if (i != -1) {
697         newSelectedInside.removeAt(i);
698     }
699     i = newSelectedInside.indexOf(e);
700     if (i != -1) {
701         newSelectedInside.removeAt(i);
702     }
703 
704     QRect updateRect;
705 
706     // Don't use repaint() because it will cause all rects to
707     // be united (see khtmlview::scheduleRepaint()).  Instead
708     // just draw damage rects for objects that have a change
709     // in selection state.
710     // ### for Qt, updateContents will unite them, too. This has to be
711     // circumvented somehow (LS)
712 
713     // Are any of the old fully selected objects not in the new selection?
714     // If so we have to draw them.
715     // Could be faster by building list of non-intersecting rectangles rather
716     // than unioning rectangles.
717     QListIterator<RenderObject *> oldIterator(oldSelectedInside);
718     bool firstRect = true;
719     while (oldIterator.hasNext()) {
720         o = oldIterator.next();
721         if (!newSelectedInside.contains(o)) {
722             if (firstRect) {
723                 updateRect = enclosingPositionedRect(o);
724                 firstRect = false;
725             } else {
726                 updateRect = updateRect.united(enclosingPositionedRect(o));
727             }
728         }
729     }
730     if (!firstRect) {
731         m_view->updateContents(updateRect);
732     }
733 
734     // Are any of the new fully selected objects not in the previous selection?
735     // If so we have to draw them.
736     // Could be faster by building list of non-intersecting rectangles rather
737     // than unioning rectangles.
738     QListIterator<RenderObject *> newIterator(newSelectedInside);
739     firstRect = true;
740     while (newIterator.hasNext()) {
741         o = newIterator.next();
742         if (!oldSelectedInside.contains(o)) {
743             if (firstRect) {
744                 updateRect = enclosingPositionedRect(o);
745                 firstRect = false;
746             } else {
747                 updateRect = updateRect.united(enclosingPositionedRect(o));
748             }
749         }
750     }
751     if (!firstRect) {
752         m_view->updateContents(updateRect);
753     }
754 
755     // Is the new starting object different, or did the position in the starting
756     // element change?  If so we have to draw it.
757     if (oldStart != m_selectionStart ||
758             (oldStart == oldEnd && (oldStartPos != m_selectionStartPos || oldEndPos != m_selectionEndPos)) ||
759             (oldStart == m_selectionStart && oldStartPos != m_selectionStartPos)) {
760         m_view->updateContents(enclosingPositionedRect(m_selectionStart));
761     }
762 
763     // Draw the old selection start object if it's different than the new selection
764     // start object.
765     if (oldStart && oldStart != m_selectionStart) {
766         m_view->updateContents(enclosingPositionedRect(oldStart));
767     }
768 
769     // Does the selection span objects and is the new end object different, or did the position
770     // in the end element change?  If so we have to draw it.
771     if (/*(oldStart != oldEnd || !oldEnd) &&*/
772         (oldEnd != m_selectionEnd ||
773          (oldEnd == m_selectionEnd && oldEndPos != m_selectionEndPos))) {
774         m_view->updateContents(enclosingPositionedRect(m_selectionEnd));
775     }
776 
777     // Draw the old selection end object if it's different than the new selection
778     // end object.
779     if (oldEnd && oldEnd != m_selectionEnd) {
780         m_view->updateContents(enclosingPositionedRect(oldEnd));
781     }
782 }
783 
clearSelection(bool doRepaint)784 void RenderCanvas::clearSelection(bool doRepaint)
785 {
786     // update selection status of all objects between m_selectionStart and m_selectionEnd
787     RenderObject *o = m_selectionStart;
788     while (o && o != m_selectionEnd) {
789         if (o->selectionState() != SelectionNone)
790             if (doRepaint) {
791                 o->repaint();
792             }
793         o->setSelectionState(SelectionNone);
794         o->repaint();
795         RenderObject *no;
796         if (!(no = o->firstChild()))
797             if (!(no = o->nextSibling())) {
798                 no = o->parent();
799                 while (no && !no->nextSibling()) {
800                     no = no->parent();
801                 }
802                 if (no) {
803                     no = no->nextSibling();
804                 }
805             }
806         o = no;
807     }
808     if (m_selectionEnd) {
809         m_selectionEnd->setSelectionState(SelectionNone);
810         if (doRepaint) {
811             m_selectionEnd->repaint();
812         }
813     }
814 
815     // set selection start & end to 0
816     if (m_selectionStart) {
817         m_selectionStart->setIsSelectionBorder(false);
818     }
819     m_selectionStart = nullptr;
820     m_selectionStartPos = -1;
821 
822     if (m_selectionEnd) {
823         m_selectionEnd->setIsSelectionBorder(false);
824     }
825     m_selectionEnd = nullptr;
826     m_selectionEndPos = -1;
827 }
828 
selectionStartEnd(int & spos,int & epos)829 void RenderCanvas::selectionStartEnd(int &spos, int &epos)
830 {
831     spos = m_selectionStartPos;
832     epos = m_selectionEndPos;
833 }
834 
viewRect() const835 QRect RenderCanvas::viewRect() const
836 {
837     if (m_pagedMode)
838         if (m_pageTop == m_pageBottom) {
839             // qCDebug(KHTML_LOG) << "viewRect: " << QRect(0, m_pageTop, m_width, m_height);
840             return QRect(0, m_pageTop, m_width, m_height);
841         } else {
842             // qCDebug(KHTML_LOG) << "viewRect: " << QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop);
843             return QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop);
844         }
845     else if (m_view) {
846         const int z = m_view->zoomLevel() ? m_view->zoomLevel() : 100;
847         return QRect(m_view->contentsX() * 100 / z, m_view->contentsY() * 100 / z,
848                      m_view->visibleWidth(), m_view->visibleHeight());
849     } else {
850         return QRect(0, 0, m_rootWidth, m_rootHeight);
851     }
852 }
853 
docHeight() const854 int RenderCanvas::docHeight() const
855 {
856     if (m_cachedDocHeight != -1) {
857         return m_cachedDocHeight;
858     }
859 
860     int h;
861     if (m_pagedMode || !m_view) {
862         h = m_height;
863     } else {
864         h = 0;
865     }
866 
867     RenderObject *fc = firstChild();
868     if (fc) {
869         int dh = fc->overflowHeight() + fc->marginTop() + fc->marginBottom();
870         int lowestPos = fc->lowestPosition(false);
871 // qCDebug(KHTML_LOG) << "h " << h << " lowestPos " << lowestPos << " dh " << dh << " fc->rh " << fc->effectiveHeight() << " fc->height() " << fc->height();
872         if (lowestPos > dh) {
873             dh = lowestPos;
874         }
875         lowestPos = lowestAbsolutePosition();
876         if (lowestPos > dh) {
877             dh = lowestPos;
878         }
879         if (dh > h) {
880             h = dh;
881         }
882     }
883 
884     RenderLayer *layer = m_layer;
885     h = qMax(h, layer->yPos() + layer->height());
886 // qCDebug(KHTML_LOG) << "h " << h << " layer(" << layer->renderer()->renderName() << "@" << layer->renderer() << ")->height " << layer->height() << " lp " << (layer->yPos() + layer->height()) << " height() " << layer->renderer()->height() << " rh " << layer->renderer()->effectiveHeight();
887     return h;
888 }
889 
docWidth() const890 int RenderCanvas::docWidth() const
891 {
892     if (m_cachedDocWidth != -1) {
893         return m_cachedDocWidth;
894     }
895 
896     int w;
897     if (m_pagedMode || !m_view) {
898         w = m_width;
899     } else {
900         w = 0;
901     }
902 
903     RenderObject *fc = firstChild();
904     if (fc) {
905         // ow: like effectiveWidth() but without the negative
906         const int ow = fc->hasOverflowClip() ? fc->width() : fc->overflowWidth();
907         int dw = ow + fc->marginLeft() + fc->marginRight();
908         int rightmostPos = fc->rightmostPosition(false);
909 // qCDebug(KHTML_LOG) << "w " << w << " rightmostPos " << rightmostPos << " dw " << dw << " fc->rw " << fc->effectiveWidth() << " fc->width() " << fc->width();
910         if (rightmostPos > dw) {
911             dw = rightmostPos;
912         }
913         rightmostPos = rightmostAbsolutePosition();
914         if (rightmostPos > dw) {
915             dw = rightmostPos;
916         }
917         if (dw > w) {
918             w = dw;
919         }
920     }
921 
922     RenderLayer *layer = m_layer;
923     w = qMax(w, layer->xPos() + layer->width());
924 // qCDebug(KHTML_LOG) << "w " << w << " layer(" << layer->renderer()->renderName() << ")->width " << layer->width() << " rm " << (layer->xPos() + layer->width()) << " width() " << layer->renderer()->width() << " rw " << layer->renderer()->effectiveWidth();
925     return w;
926 }
927 
page()928 RenderPage *RenderCanvas::page()
929 {
930     if (!m_page) {
931         m_page = new RenderPage(this);
932     }
933     return m_page;
934 }
935 
updateInvalidatedFonts()936 void RenderCanvas::updateInvalidatedFonts()
937 {
938     for (RenderObject *o = firstChild(); o; o = o->nextRenderer()) {
939         if (o->style()->htmlFont().isInvalidated()) {
940             o->setNeedsLayoutAndMinMaxRecalc();
941 //          qCDebug(KHTML_LOG) << "updating object using invalid font" << o->style()->htmlFont().getFontDef().family << o->information();
942         }
943     }
944 }
945