summaryrefslogtreecommitdiffstats
path: root/khtml/rendering/render_layer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/rendering/render_layer.cpp')
-rw-r--r--khtml/rendering/render_layer.cpp1830
1 files changed, 1830 insertions, 0 deletions
diff --git a/khtml/rendering/render_layer.cpp b/khtml/rendering/render_layer.cpp
new file mode 100644
index 000000000..b4af3536c
--- /dev/null
+++ b/khtml/rendering/render_layer.cpp
@@ -0,0 +1,1830 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc.
+ * (C) 2006 Germain Garand <germain@ebooksfrance.org>
+ * (C) 2006 Allan Sandfeld Jense <kde@carewolf.com>
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+//#define BOX_DEBUG
+
+#include "render_layer.h"
+#include <kdebug.h>
+#include <assert.h>
+#include "khtmlview.h"
+#include "render_canvas.h"
+#include "render_arena.h"
+#include "render_replaced.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom2_eventsimpl.h"
+#include "misc/htmltags.h"
+#include "html/html_blockimpl.h"
+#include "xml/dom_restyler.h"
+
+#include <qscrollbar.h>
+#include <qptrvector.h>
+#include <qstyle.h>
+
+using namespace DOM;
+using namespace khtml;
+
+#ifdef APPLE_CHANGES
+QScrollBar* RenderLayer::gScrollBar = 0;
+#endif
+
+#ifndef NDEBUG
+static bool inRenderLayerDetach;
+#endif
+
+void
+RenderScrollMediator::slotValueChanged()
+{
+ m_layer->updateScrollPositionFromScrollbars();
+}
+
+RenderLayer::RenderLayer(RenderObject* object)
+: m_object( object ),
+m_parent( 0 ),
+m_previous( 0 ),
+m_next( 0 ),
+m_first( 0 ),
+m_last( 0 ),
+m_x( 0 ),
+m_y( 0 ),
+m_scrollX( 0 ),
+m_scrollY( 0 ),
+m_scrollWidth( 0 ),
+m_scrollHeight( 0 ),
+m_hBar( 0 ),
+m_vBar( 0 ),
+m_scrollMediator( 0 ),
+m_posZOrderList( 0 ),
+m_negZOrderList( 0 ),
+m_overflowList(0),
+m_zOrderListsDirty( true ),
+m_overflowListDirty(true),
+m_isOverflowOnly( shouldBeOverflowOnly() ),
+m_markedForRepaint( false ),
+m_hasOverlaidWidgets( false ),
+m_marquee( 0 )
+{
+}
+
+RenderLayer::~RenderLayer()
+{
+ // Child layers will be deleted by their corresponding render objects, so
+ // our destructor doesn't have to do anything.
+ delete m_hBar;
+ delete m_vBar;
+ delete m_scrollMediator;
+ delete m_posZOrderList;
+ delete m_negZOrderList;
+ delete m_overflowList;
+ delete m_marquee;
+}
+
+void RenderLayer::updateLayerPosition()
+{
+
+ // The canvas is sized to the docWidth/Height over in RenderCanvas::layout, so we
+ // don't need to ever update our layer position here.
+ if (renderer()->isCanvas())
+ return;
+
+ int x = m_object->xPos();
+ int y = m_object->yPos() - m_object->borderTopExtra();
+
+ if (!m_object->isPositioned()) {
+ // We must adjust our position by walking up the render tree looking for the
+ // nearest enclosing object with a layer.
+ RenderObject* curr = m_object->parent();
+ while (curr && !curr->layer()) {
+ x += curr->xPos();
+ y += curr->yPos();
+ curr = curr->parent();
+ }
+ if (curr)
+ y += curr->borderTopExtra();
+ }
+
+ if (m_object->isRelPositioned())
+ static_cast<RenderBox*>(m_object)->relativePositionOffset(x, y);
+
+ // Subtract our parent's scroll offset.
+ if (m_object->isPositioned() && enclosingPositionedAncestor()) {
+ RenderLayer* positionedParent = enclosingPositionedAncestor();
+
+ // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
+ positionedParent->subtractScrollOffset(x, y);
+ positionedParent->checkInlineRelOffset(m_object, x, y);
+ }
+ else if (parent())
+ parent()->subtractScrollOffset(x, y);
+
+ setPos(x,y);
+}
+
+QRegion RenderLayer::paintedRegion(RenderLayer* rootLayer)
+{
+ updateZOrderLists();
+ QRegion r;
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ r += child->paintedRegion(rootLayer);
+ }
+ }
+ const RenderStyle *s= renderer()->style();
+ if (s->visibility() == VISIBLE) {
+ int x = 0; int y = 0;
+ convertToLayerCoords(rootLayer,x,y);
+ QRect cr(x,y,width(),height());
+ if ( s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() ||
+ renderer()->scrollsOverflow() || renderer()->isReplaced() ) {
+ r += cr;
+ } else {
+ r += renderer()->visibleFlowRegion(x, y);
+ }
+ }
+
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ r += child->paintedRegion(rootLayer);
+ }
+ }
+ return r;
+}
+
+void RenderLayer::repaint( Priority p, bool markForRepaint )
+{
+ if (markForRepaint && m_markedForRepaint)
+ return;
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->repaint( p, markForRepaint );
+ QRect layerBounds, damageRect, fgrect;
+ calculateRects(renderer()->canvas()->layer(), renderer()->viewRect(), layerBounds, damageRect, fgrect);
+ m_visibleRect = damageRect.intersect( layerBounds );
+ if (m_visibleRect.isValid())
+ renderer()->canvas()->repaintViewRectangle( m_visibleRect.x(), m_visibleRect.y(), m_visibleRect.width(), m_visibleRect.height(), (p > NormalPriority) );
+ if (markForRepaint)
+ m_markedForRepaint = true;
+}
+
+void RenderLayer::updateLayerPositions(RenderLayer* rootLayer, bool doFullRepaint, bool checkForRepaint)
+{
+ if (doFullRepaint) {
+ m_object->repaint();
+ checkForRepaint = doFullRepaint = false;
+ }
+
+ updateLayerPosition(); // For relpositioned layers or non-positioned layers,
+ // we need to keep in sync, since we may have shifted relative
+ // to our parent layer.
+
+ if (m_hBar || m_vBar) {
+ // Need to position the scrollbars.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ QRect layerBounds = QRect(x,y,width(),height());
+ positionScrollbars(layerBounds);
+ }
+
+#ifdef APPLE_CHANGES
+ // FIXME: Child object could override visibility.
+ if (checkForRepaint && (m_object->style()->visibility() == VISIBLE))
+ m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
+#else
+ if (checkForRepaint && m_markedForRepaint) {
+ QRect layerBounds, damageRect, fgrect;
+ calculateRects(rootLayer, renderer()->viewRect(), layerBounds, damageRect, fgrect);
+ QRect vr = damageRect.intersect( layerBounds );
+ if (vr != m_visibleRect && vr.isValid()) {
+ renderer()->canvas()->repaintViewRectangle( vr.x(), vr.y(), vr.width(), vr.height() );
+ m_visibleRect = vr;
+ }
+ }
+ m_markedForRepaint = false;
+#endif
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(rootLayer, doFullRepaint, checkForRepaint);
+
+ // With all our children positioned, now update our marquee if we need to.
+ if (m_marquee)
+ m_marquee->updateMarqueePosition();
+}
+
+void RenderLayer::updateWidgetMasks(RenderLayer* rootLayer)
+{
+ if (hasOverlaidWidgets() && !renderer()->canvas()->pagedMode()) {
+ updateZOrderLists();
+ uint count = m_posZOrderList ? m_posZOrderList->count() : 0;
+ bool needUpdate = (count || !m_region.isNull());
+ if (count) {
+ QScrollView* sv = m_object->document()->view();
+ m_region = QRect(0,0,sv->contentsWidth(),sv->contentsHeight());
+
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ if (child->zIndex() == 0 && child->renderer()->style()->position() == STATIC)
+ continue; // we don't know the widget's exact stacking position within flow
+ m_region -= child->paintedRegion(rootLayer);
+ }
+ } else {
+ m_region = QRegion();
+ }
+ if (needUpdate)
+ renderer()->updateWidgetMasks();
+ }
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateWidgetMasks(rootLayer);
+}
+
+short RenderLayer::width() const
+{
+ int w = m_object->width();
+ if (!m_object->hasOverflowClip())
+ w = kMax(m_object->overflowWidth(), w);
+ return w;
+}
+
+int RenderLayer::height() const
+{
+ int h = m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra();
+ if (!m_object->hasOverflowClip())
+ h = kMax(m_object->overflowHeight(), h);
+ return h;
+}
+
+
+RenderLayer *RenderLayer::stackingContext() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isCanvas() &&
+ curr->m_object->style()->hasAutoZIndex();
+ curr = curr->parent());
+ return curr;
+}
+
+RenderLayer* RenderLayer::enclosingPositionedAncestor() const
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && !curr->m_object->isCanvas() &&
+ !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
+ curr = curr->parent());
+
+ return curr;
+}
+
+#ifdef APPLE_CHANGES
+bool RenderLayer::isTransparent()
+{
+ return m_object->style()->opacity() < 1.0f;
+}
+
+RenderLayer* RenderLayer::transparentAncestor()
+{
+ RenderLayer* curr = parent();
+ for ( ; curr && curr->m_object->style()->opacity() == 1.0f; curr = curr->parent());
+ return curr;
+}
+#endif
+
+void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void RenderLayer::operator delete(void* ptr, size_t sz)
+{
+ assert(inRenderLayerDetach);
+
+ // Stash size where detach can find it.
+ *(size_t *)ptr = sz;
+}
+
+void RenderLayer::detach(RenderArena* renderArena)
+{
+#ifndef NDEBUG
+ inRenderLayerDetach = true;
+#endif
+ delete this;
+#ifndef NDEBUG
+ inRenderLayerDetach = false;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ renderArena->free(*(size_t *)this, this);
+}
+
+void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild)
+{
+ RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
+ if (prevSibling) {
+ child->setPreviousSibling(prevSibling);
+ prevSibling->setNextSibling(child);
+ }
+ else
+ setFirstChild(child);
+
+ if (beforeChild) {
+ beforeChild->setPreviousSibling(child);
+ child->setNextSibling(beforeChild);
+ }
+ else
+ setLastChild(child);
+
+ child->setParent(this);
+
+ if (child->isOverflowOnly())
+ dirtyOverflowList();
+ else {
+ // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
+ // case where we're building up generated content layers. This is ok, since the lists will start
+ // off dirty in that case anyway.
+ RenderLayer* stackingContext = child->stackingContext();
+ if (stackingContext)
+ stackingContext->dirtyZOrderLists();
+ }
+}
+
+RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
+{
+ // remove the child
+ if (oldChild->previousSibling())
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
+ if (oldChild->nextSibling())
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
+
+ if (m_first == oldChild)
+ m_first = oldChild->nextSibling();
+ if (m_last == oldChild)
+ m_last = oldChild->previousSibling();
+
+ if (oldChild->isOverflowOnly())
+ dirtyOverflowList();
+ else {
+ // Dirty the z-order list in which we are contained. When called via the
+ // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
+ // from the main layer tree, so we need to null-check the |stackingContext| value.
+ RenderLayer* stackingContext = oldChild->stackingContext();
+ if (stackingContext)
+ stackingContext->dirtyZOrderLists();
+ }
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ return oldChild;
+}
+
+void RenderLayer::removeOnlyThisLayer()
+{
+ if (!m_parent)
+ return;
+
+ // Remove us from the parent.
+ RenderLayer* parent = m_parent;
+ RenderLayer* nextSib = nextSibling();
+ parent->removeChild(this);
+
+ // Now walk our kids and reattach them to our parent.
+ RenderLayer* current = m_first;
+ while (current) {
+ RenderLayer* next = current->nextSibling();
+ removeChild(current);
+ parent->addChild(current, nextSib);
+ current = next;
+ }
+
+ detach(renderer()->renderArena());
+}
+
+void RenderLayer::insertOnlyThisLayer()
+{
+ if (!m_parent && renderer()->parent()) {
+ // We need to connect ourselves when our renderer() has a parent.
+ // Find our enclosingLayer and add ourselves.
+ RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
+ if (parentLayer)
+ parentLayer->addChild(this,
+ renderer()->parent()->findNextLayer(parentLayer, renderer()));
+ }
+
+ // Remove all descendant layers from the hierarchy and add them to the new position.
+ for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
+ curr->moveLayers(m_parent, this);
+}
+
+void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
+{
+ if (ancestorLayer == this)
+ return;
+
+ if (m_object->style()->position() == FIXED) {
+ // Add in the offset of the view. We can obtain this by calling
+ // absolutePosition() on the RenderCanvas.
+ int xOff, yOff;
+ m_object->absolutePosition(xOff, yOff, true);
+ x += xOff;
+ y += yOff;
+ return;
+ }
+
+ RenderLayer* parentLayer;
+ if (m_object->style()->position() == ABSOLUTE)
+ parentLayer = enclosingPositionedAncestor();
+ else
+ parentLayer = parent();
+
+ if (!parentLayer) return;
+
+ parentLayer->convertToLayerCoords(ancestorLayer, x, y);
+
+ x += xPos();
+ y += yPos();
+}
+
+void RenderLayer::scrollOffset(int& x, int& y)
+{
+ x += scrollXOffset();
+ y += scrollYOffset();
+}
+
+void RenderLayer::subtractScrollOffset(int& x, int& y)
+{
+ x -= scrollXOffset();
+ y -= scrollYOffset();
+}
+
+void RenderLayer::checkInlineRelOffset(const RenderObject* o, int& x, int& y)
+{
+ if(o->style()->position() != ABSOLUTE || !renderer()->isRelPositioned() || !renderer()->isInlineFlow())
+ return;
+
+ // Our renderer is an enclosing relpositioned inline, we need to add in the offset of the first line
+ // box from the rest of the content, but only in the cases where we know our descendant is positioned
+ // relative to the inline itself.
+ assert( o->container() == m_object );
+
+ RenderFlow* flow = static_cast<RenderFlow*>(m_object);
+ int sx = 0, sy = 0;
+ if (flow->firstLineBox()) {
+ if (flow->style()->direction() == LTR)
+ sx = flow->firstLineBox()->xPos();
+ else
+ sx = flow->lastLineBox()->xPos();
+ sy = flow->firstLineBox()->yPos();
+ } else {
+ sx = flow->staticX(); // ###
+ sy = flow->staticY();
+ }
+ bool isInlineType = o->style()->isOriginalDisplayInlineType();
+
+ if (!o->hasStaticX())
+ x += sx;
+
+ // Despite the positioned child being a block display type inside an inline, we still keep
+ // its x locked to our left. Arguably the correct behavior would be to go flush left to
+ // the block that contains us, but that isn't what other browsers do.
+ if (o->hasStaticX() && !isInlineType)
+ // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
+ x += sx - (o->containingBlock()->borderLeft() + o->containingBlock()->paddingLeft());
+
+ if (!o->hasStaticY())
+ y += sy;
+}
+
+void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
+{
+ if (renderer()->style()->overflowX() != OMARQUEE || !renderer()->hasOverflowClip()) {
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
+ // to be (for overflow:hidden blocks).
+ // ### merge the scrollWidth()/scrollHeight() methods
+ int maxX = m_scrollWidth - m_object->clientWidth();
+ int maxY = m_scrollHeight - m_object->clientHeight();
+
+ if (x > maxX) x = maxX;
+ if (y > maxY) y = maxY;
+ }
+
+ // FIXME: Eventually, we will want to perform a blit. For now never
+ // blit, since the check for blitting is going to be very
+ // complicated (since it will involve testing whether our layer
+ // is either occluded by another layer or clipped by an enclosing
+ // layer or contains fixed backgrounds, etc.).
+ m_scrollX = x;
+ m_scrollY = y;
+
+ // Update the positions of our child layers.
+ RenderLayer* rootLayer = root();
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->updateLayerPositions(rootLayer);
+
+ // Fire the scroll DOM event.
+ m_object->element()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
+
+ // Just schedule a full repaint of our object.
+ if (repaint)
+ m_object->repaint(RealtimePriority);
+
+ if (updateScrollbars) {
+ if (m_hBar)
+ m_hBar->setValue(m_scrollX);
+ if (m_vBar)
+ m_vBar->setValue(m_scrollY);
+ }
+}
+
+void RenderLayer::updateScrollPositionFromScrollbars()
+{
+ bool needUpdate = false;
+ int newX = m_scrollX;
+ int newY = m_scrollY;
+
+ if (m_hBar) {
+ newX = m_hBar->value();
+ if (newX != m_scrollX)
+ needUpdate = true;
+ }
+
+ if (m_vBar) {
+ newY = m_vBar->value();
+ if (newY != m_scrollY)
+ needUpdate = true;
+ }
+
+ if (needUpdate)
+ scrollToOffset(newX, newY, false);
+}
+
+void
+RenderLayer::showScrollbar(Qt::Orientation o, bool show)
+{
+ QScrollBar *sb = (o == Qt::Horizontal) ? m_hBar : m_vBar;
+
+ if (show && !sb) {
+ QScrollView* scrollView = m_object->document()->view();
+ sb = new QScrollBar(o, scrollView, "__khtml");
+ scrollView->addChild(sb, 0, -50000);
+ sb->setBackgroundMode(QWidget::NoBackground);
+ sb->show();
+ if (!m_scrollMediator)
+ m_scrollMediator = new RenderScrollMediator(this);
+ m_scrollMediator->connect(sb, SIGNAL(valueChanged(int)), SLOT(slotValueChanged()));
+ }
+ else if (!show && sb) {
+ delete sb;
+ sb = 0;
+ }
+
+ if (o == Qt::Horizontal)
+ m_hBar = sb;
+ else
+ m_vBar = sb;
+}
+
+int RenderLayer::verticalScrollbarWidth()
+{
+ if (!m_vBar)
+ return 0;
+
+#ifdef APPLE_CHANGES
+ return m_vBar->width();
+#else
+ return m_vBar->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+#endif
+
+}
+
+int RenderLayer::horizontalScrollbarHeight()
+{
+ if (!m_hBar)
+ return 0;
+
+#ifdef APPLE_CHANGES
+ return m_hBar->height();
+#else
+ return m_hBar->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+#endif
+
+}
+
+void RenderLayer::positionScrollbars(const QRect& absBounds)
+{
+#ifdef APPLE_CHANGES
+ if (m_vBar) {
+ scrollView->addChild(m_vBar, absBounds.x()+absBounds.width()-m_object->borderRight()-m_vBar->width(),
+ absBounds.y()+m_object->borderTop());
+ m_vBar->resize(m_vBar->width(), absBounds.height() -
+ (m_object->borderTop()+m_object->borderBottom()) -
+ (m_hBar ? m_hBar->height()-1 : 0));
+ }
+
+ if (m_hBar) {
+ scrollView->addChild(m_hBar, absBounds.x()+m_object->borderLeft(),
+ absBounds.y()+absBounds.height()-m_object->borderBottom()-m_hBar->height());
+ m_hBar->resize(absBounds.width() - (m_object->borderLeft()+m_object->borderRight()) -
+ (m_vBar ? m_vBar->width()-1 : 0), m_hBar->height());
+ }
+#else
+ int tx = absBounds.x();
+ int ty = absBounds.y();
+ int bl = m_object->borderLeft();
+ int bt = m_object->borderTop();
+ int w = width() - bl - m_object->borderRight();
+ int h = height() - bt - m_object->borderBottom();
+
+ if (w <= 0 || h <= 0 || (!m_vBar && !m_hBar))
+ return;
+
+ QScrollView* scrollView = m_object->document()->view();
+
+ tx += bl;
+ ty += bt;
+
+ QScrollBar *b = m_hBar;
+ if (!m_hBar)
+ b = m_vBar;
+ int sw = b->style().pixelMetric(QStyle::PM_ScrollBarExtent);
+
+ if (m_vBar) {
+ QRect vBarRect = QRect(tx + w - sw + 1, ty, sw, h - (m_hBar ? sw : 0) + 1);
+ m_vBar->resize(vBarRect.width(), vBarRect.height());
+ scrollView->addChild(m_vBar, vBarRect.x(), vBarRect.y());
+ }
+
+ if (m_hBar) {
+ QRect hBarRect = QRect(tx, ty + h - sw + 1, w - (m_vBar ? sw : 0) + 1, sw);
+ m_hBar->resize(hBarRect.width(), hBarRect.height());
+ scrollView->addChild(m_hBar, hBarRect.x(), hBarRect.y());
+ }
+#endif
+}
+
+#define LINE_STEP 10
+#define PAGE_KEEP 40
+
+void RenderLayer::checkScrollbarsAfterLayout()
+{
+ int rightPos = m_object->rightmostPosition(true);
+ int bottomPos = m_object->lowestPosition(true);
+
+/* TODO
+ m_scrollLeft = m_object->leftmostPosition(true);
+ m_scrollTop = m_object->highestPosition(true);
+*/
+
+ int clientWidth = m_object->clientWidth();
+ int clientHeight = m_object->clientHeight();
+ m_scrollWidth = clientWidth;
+ m_scrollHeight = clientHeight;
+
+ if (rightPos - m_object->borderLeft() > m_scrollWidth)
+ m_scrollWidth = rightPos - m_object->borderLeft();
+ if (bottomPos - m_object->borderTop() > m_scrollHeight)
+ m_scrollHeight = bottomPos - m_object->borderTop();
+
+ bool needHorizontalBar = rightPos > width();
+ bool needVerticalBar = bottomPos > height();
+
+ bool haveHorizontalBar = m_hBar && m_hBar->isEnabled();
+ bool haveVerticalBar = m_vBar && m_vBar->isEnabled();
+
+ bool hasOvf = m_object->hasOverflowClip();
+
+ // overflow:scroll should just enable/disable.
+ if (hasOvf && m_object->style()->overflowX() == OSCROLL)
+ m_hBar->setEnabled(needHorizontalBar);
+ if (hasOvf && m_object->style()->overflowY() == OSCROLL)
+ m_vBar->setEnabled(needVerticalBar);
+
+ // overflow:auto may need to lay out again if scrollbars got added/removed.
+ bool scrollbarsChanged = (hasOvf && m_object->style()->overflowX() == OAUTO && haveHorizontalBar != needHorizontalBar)
+ || (hasOvf && m_object->style()->overflowY() == OAUTO && haveVerticalBar != needVerticalBar);
+ if (scrollbarsChanged) {
+ if (m_object->style()->overflowX() == OAUTO) {
+ showScrollbar(Qt::Horizontal, needHorizontalBar);
+ if (m_hBar)
+ m_hBar->setEnabled(true);
+ }
+ if (m_object->style()->overflowY() == OAUTO) {
+ showScrollbar(Qt::Vertical, needVerticalBar);
+ if (m_vBar)
+ m_vBar->setEnabled(true);
+ }
+
+ m_object->setNeedsLayout(true);
+ if (m_object->isRenderBlock())
+ static_cast<RenderBlock*>(m_object)->layoutBlock(true);
+ else
+ m_object->layout();
+ return;
+ }
+
+ // Set up the range (and page step/line step).
+ if (m_hBar) {
+ int pageStep = (clientWidth-PAGE_KEEP);
+ if (pageStep < 0) pageStep = clientWidth;
+ m_hBar->setSteps(LINE_STEP, pageStep);
+#ifdef APPLE_CHANGES
+ m_hBar->setKnobProportion(clientWidth, m_scrollWidth);
+#else
+ m_hBar->setRange(0, needHorizontalBar ? m_scrollWidth-clientWidth : 0);
+#endif
+ }
+ if (m_vBar) {
+ int pageStep = (clientHeight-PAGE_KEEP);
+ if (pageStep < 0) pageStep = clientHeight;
+ m_vBar->setSteps(LINE_STEP, pageStep);
+#ifdef APPLE_CHANGES
+ m_vBar->setKnobProportion(clientHeight, m_scrollHeight);
+#else
+ m_vBar->setRange(0, needVerticalBar ? m_scrollHeight-clientHeight : 0);
+#endif
+ }
+}
+
+void RenderLayer::paintScrollbars(RenderObject::PaintInfo& pI)
+{
+#ifdef APPLE_CHANGES
+ if (m_hBar)
+ m_hBar->paint(p, damageRect);
+ if (m_vBar)
+ m_vBar->paint(p, damageRect);
+#else
+ if (!m_object->element())
+ return;
+
+ QScrollView* scrollView = m_object->document()->view();
+ if (m_hBar) {
+ int x = m_hBar->x();
+ int y = m_hBar->y();
+ scrollView->viewportToContents(x, y, x, y);
+ RenderWidget::paintWidget(pI, m_hBar, x, y);
+ }
+ if (m_vBar) {
+ int x = m_vBar->x();
+ int y = m_vBar->y();
+ scrollView->viewportToContents(x, y, x, y);
+ RenderWidget::paintWidget(pI, m_vBar, x, y);
+ }
+#endif
+}
+
+void RenderLayer::paint(QPainter *p, const QRect& damageRect, bool selectionOnly)
+{
+ paintLayer(this, p, damageRect, selectionOnly);
+}
+
+static void setClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->save();
+
+#ifdef APPLE_CHANGES
+ p->addClip(clipRect);
+#else
+
+ QRect clippedRect = p->xForm(clipRect);
+ QRegion creg(clippedRect);
+ QRegion old = p->clipRegion();
+ if (!old.isNull())
+ creg = old.intersect(creg);
+ p->setClipRegion(creg);
+#endif
+
+}
+
+static void restoreClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
+ return;
+ p->restore();
+}
+
+void RenderLayer::paintLayer(RenderLayer* rootLayer, QPainter *p,
+ const QRect& paintDirtyRect, bool selectionOnly)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, damageRect, clipRectToApply;
+ calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
+ int x = layerBounds.x();
+ int y = layerBounds.y();
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+#ifdef APPLE_CHANGES
+ // Set our transparency if we need to.
+ if (isTransparent())
+ p->beginTransparencyLayer(renderer()->style()->opacity());
+#endif
+
+ // We want to paint our layer, but only if we intersect the damage rect.
+ bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
+ if (shouldPaint && !selectionOnly) {
+ // Paint our background first, before painting any child layers.
+ if (!damageRect.isEmpty()) {
+ // Establish the clip used to paint our background.
+ setClip(p, paintDirtyRect, damageRect);
+
+ // Paint the background.
+ RenderObject::PaintInfo paintInfo(p, damageRect, PaintActionElementBackground);
+ renderer()->paint(paintInfo,
+ x - renderer()->xPos(), y - renderer()->yPos() + renderer()->borderTopExtra());
+
+ // Position our scrollbars.
+ positionScrollbars(layerBounds);
+
+ // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
+ // z-index. We paint after we painted the background/border, so that the scrollbars will
+ // sit above the background/border.
+ paintScrollbars(paintInfo);
+
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, damageRect);
+ }
+ }
+
+ // Now walk the sorted list of children with negative z-indices.
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+ }
+ }
+
+ // Now establish the appropriate clip and paint our child RenderObjects.
+ if (shouldPaint && !clipRectToApply.isEmpty()) {
+ // Set up the clip used when painting our children.
+ setClip(p, paintDirtyRect, clipRectToApply);
+
+ RenderObject::PaintInfo paintInfo(p, clipRectToApply, PaintActionSelection);
+
+ int tx = x - renderer()->xPos();
+ int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
+
+ if (selectionOnly)
+ renderer()->paint(paintInfo, tx, ty);
+ else {
+ paintInfo.phase = PaintActionChildBackgrounds;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintActionFloat;
+ renderer()->paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintActionForeground;
+ renderer()->paint(paintInfo, tx, ty);
+ RenderCanvas *rc = static_cast<RenderCanvas*>(renderer()->document()->renderer());
+ if (rc->maximalOutlineSize()) {
+ paintInfo.phase = PaintActionOutline;
+ renderer()->paint(paintInfo, tx, ty);
+ }
+ if (rc->selectionStart() && rc->selectionEnd()) {
+ paintInfo.phase = PaintActionSelection;
+ renderer()->paint(paintInfo, tx, ty);
+ }
+ }
+
+ // Now restore our clip.
+ restoreClip(p, paintDirtyRect, clipRectToApply);
+ }
+
+ // Paint any child layers that have overflow.
+ if (m_overflowList)
+ for (QValueList<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it)
+ (*it)->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+
+ // Now walk the sorted list of children with positive z-indices.
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (uint i = 0; i < count; i++) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
+ }
+ }
+
+#ifdef BOX_DEBUG
+ {
+ int ax=0;
+ int ay=0;
+ renderer()->absolutePosition( ax, ay );
+ p->setPen(QPen(QColor("yellow"), 1, Qt::DotLine));
+ p->setBrush( Qt::NoBrush );
+ p->drawRect(ax, ay, width(), height());
+ }
+#endif
+
+#ifdef APPLE_CHANGES
+ // End our transparency layer
+ if (isTransparent())
+ p->endTransparencyLayer();
+#endif
+}
+
+bool RenderLayer::nodeAtPoint(RenderObject::NodeInfo& info, int x, int y)
+{
+#ifdef APPLE_CHANGES
+ // Clear our our scrollbar variable
+ RenderLayer::gScrollBar = 0;
+#endif
+
+ int stx = m_x;
+ int sty = m_y;
+
+#ifdef __GNUC__
+#warning HACK
+#endif
+ if (renderer()->isCanvas()) {
+ stx += static_cast<RenderCanvas*>(renderer())->view()->contentsX();
+ sty += static_cast<RenderCanvas*>(renderer())->view()->contentsY();
+ }
+
+ QRect damageRect(stx,sty, width(), height());
+ RenderLayer* insideLayer = nodeAtPointForLayer(this, info, x, y, damageRect);
+
+ // Now determine if the result is inside an anchor.
+ DOM::NodeImpl* node = info.innerNode();
+ while (node) {
+ if (node->hasAnchor() && !info.URLElement())
+ info.setURLElement(node);
+ node = node->parentNode();
+ }
+
+ // Next set up the correct :hover/:active state along the new chain.
+ updateHoverActiveState(info);
+
+ // Now return whether we were inside this layer (this will always be true for the root
+ // layer).
+ return insideLayer;
+}
+
+RenderLayer* RenderLayer::nodeAtPointForLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
+ int xMousePos, int yMousePos, const QRect& hitTestRect)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, bgRect, fgRect;
+ calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect);
+
+ // Ensure our lists are up-to-date.
+ updateZOrderLists();
+ updateOverflowList();
+
+ // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
+ // we are done and can return it.
+ RenderLayer* insideLayer = 0;
+
+ // Begin by walking our list of positive layers from highest z-index down to the lowest
+ // z-index.
+ if (m_posZOrderList) {
+ uint count = m_posZOrderList->count();
+ for (int i = count-1; i >= 0; i--) {
+ RenderLayer* child = m_posZOrderList->at(i);
+ insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Now check our overflow objects.
+ if (m_overflowList) {
+ QValueList<RenderLayer*>::iterator it = m_overflowList->end();
+ for (--it; it != m_overflowList->end(); --it) {
+ insideLayer = (*it)->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
+ if (containsPoint(xMousePos, yMousePos, fgRect) &&
+ renderer()->nodeAtPoint(info, xMousePos, yMousePos,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestChildrenOnly)) {
+ if (info.innerNode() != m_object->element())
+ return this;
+ }
+
+ // Now check our negative z-index children.
+ if (m_negZOrderList) {
+ uint count = m_negZOrderList->count();
+ for (int i = count-1; i >= 0; i--) {
+ RenderLayer* child = m_negZOrderList->at(i);
+ insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
+ if (insideLayer)
+ return insideLayer;
+ }
+ }
+
+ // Next we want to see if the mouse pos is inside this layer but not any of its children.
+ if (containsPoint(xMousePos, yMousePos, bgRect) &&
+ renderer()->nodeAtPoint(info, xMousePos, yMousePos,
+ layerBounds.x() - renderer()->xPos(),
+ layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
+ HitTestSelfOnly))
+ return this;
+
+ // No luck.
+ return 0;
+}
+
+void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
+ QRect& posClipRect, QRect& fixedClipRect)
+{
+ if (parent())
+ parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
+
+ switch (m_object->style()->position()) {
+ // A fixed object is essentially the root of its containing block hierarchy, so when
+ // we encounter such an object, we reset our clip rects to the fixedClipRect.
+ case FIXED:
+ posClipRect = fixedClipRect;
+ overflowClipRect = fixedClipRect;
+ break;
+ case ABSOLUTE:
+ overflowClipRect = posClipRect;
+ break;
+ case RELATIVE:
+ posClipRect = overflowClipRect;
+ break;
+ default:
+ break;
+ }
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+
+ if (m_object->hasOverflowClip()) {
+ QRect newOverflowClip = m_object->getOverflowClipRect(x,y);
+ overflowClipRect = newOverflowClip.intersect(overflowClipRect);
+ if (m_object->isPositioned() || m_object->isRelPositioned())
+ posClipRect = newOverflowClip.intersect(posClipRect);
+ }
+ if (m_object->hasClip()) {
+ QRect newPosClip = m_object->getClipRect(x,y);
+ posClipRect = posClipRect.intersect(newPosClip);
+ overflowClipRect = overflowClipRect.intersect(newPosClip);
+ fixedClipRect = fixedClipRect.intersect(newPosClip);
+ }
+ }
+}
+
+void RenderLayer::calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
+ QRect& backgroundRect, QRect& foregroundRect)
+{
+ QRect overflowClipRect = paintDirtyRect;
+ QRect posClipRect = paintDirtyRect;
+ QRect fixedClipRect = paintDirtyRect;
+ if (parent())
+ parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
+
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ layerBounds = QRect(x,y,width(),height());
+
+ backgroundRect = m_object->style()->position() == FIXED ? fixedClipRect :
+ (m_object->isPositioned() ? posClipRect : overflowClipRect);
+ foregroundRect = backgroundRect;
+
+ // Update the clip rects that will be passed to child layers.
+ if (m_object->hasOverflowClip() || m_object->hasClip()) {
+ // This layer establishes a clip of some kind.
+ if (m_object->hasOverflowClip())
+ foregroundRect = foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
+
+ if (m_object->hasClip()) {
+ // Clip applies to *us* as well, so go ahead and update the damageRect.
+ QRect newPosClip = m_object->getClipRect(x,y);
+ backgroundRect = backgroundRect.intersect(newPosClip);
+ foregroundRect = foregroundRect.intersect(newPosClip);
+ }
+
+ // If we establish a clip at all, then go ahead and make sure our background
+ // rect is intersected with our layer's bounds.
+ backgroundRect = backgroundRect.intersect(layerBounds);
+ }
+}
+
+bool RenderLayer::intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const
+{
+ return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
+ (renderer()->hasOverhangingFloats() && !renderer()->hasOverflowClip()) ||
+ (renderer()->isInline() && !renderer()->isReplaced()) ||
+ layerBounds.intersects(damageRect));
+}
+
+bool RenderLayer::containsPoint(int x, int y, const QRect& damageRect) const
+{
+ return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
+ renderer()->hasOverhangingFloats() ||
+ (renderer()->isInline() && !renderer()->isReplaced()) ||
+ damageRect.contains(x, y));
+}
+
+// This code has been written to anticipate the addition of CSS3-::outside and ::inside generated
+// content (and perhaps XBL). That's why it uses the render tree and not the DOM tree.
+static RenderObject* hoverAncestor(RenderObject* obj)
+{
+ return (!obj->isInline() && obj->continuation()) ? obj->continuation() : obj->parent();
+}
+
+static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2)
+{
+ if (!obj1 || !obj2)
+ return 0;
+
+ for (RenderObject* currObj1 = obj1; currObj1; currObj1 = hoverAncestor(currObj1))
+ for (RenderObject* currObj2 = obj2; currObj2; currObj2 = hoverAncestor(currObj2))
+ if (currObj1 == currObj2)
+ return currObj1;
+
+ return 0;
+}
+
+
+void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo& info)
+{
+ // We don't update :hover/:active state when the info is marked as readonly.
+ if (info.readonly())
+ return;
+
+ DOM::NodeImpl *e = m_object->element();
+ DOM::DocumentImpl *doc = e ? e->getDocument() : 0;
+ if (!doc) return;
+
+ // Check to see if the hovered node has changed. If not, then we don't need to
+ // do anything.
+ DOM::NodeImpl* oldHoverNode = doc->hoverNode();
+ DOM::NodeImpl* newHoverNode = info.innerNode();
+
+ if (oldHoverNode == newHoverNode && (!oldHoverNode || oldHoverNode->active() == info.active()))
+ return;
+
+ // Update our current hover node.
+ doc->setHoverNode(newHoverNode);
+ if (info.active())
+ doc->setActiveNode(newHoverNode);
+ else
+ doc->setActiveNode(0);
+
+ // We have two different objects. Fetch their renderers.
+ RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
+ RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
+
+ // Locate the common ancestor render object for the two renderers.
+ RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
+
+ // The old hover path only needs to be cleared up to (and not including) the common ancestor;
+ for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = hoverAncestor(curr)) {
+ curr->setMouseInside(false);
+ if (curr->element()) {
+ curr->element()->setActive(false);
+ curr->element()->setHovered(false);
+ }
+ }
+
+ // Now set the hover state for our new object up to the root.
+ for (RenderObject* curr = newHoverObj; curr; curr = hoverAncestor(curr)) {
+ curr->setMouseInside(true);
+ if (curr->element()) {
+ curr->element()->setActive(info.active());
+ curr->element()->setHovered(true);
+ }
+ }
+}
+
+// Sort the buffer from lowest z-index to highest. The common scenario will have
+// most z-indices equal, so we optimize for that case (i.e., the list will be mostly
+// sorted already).
+static void sortByZOrder(QPtrVector<RenderLayer>* buffer,
+ QPtrVector<RenderLayer>* mergeBuffer,
+ uint start, uint end)
+{
+ if (start >= end)
+ return; // Sanity check.
+
+ if (end - start <= 6) {
+ // Apply a bubble sort for smaller lists.
+ for (uint i = end-1; i > start; i--) {
+ bool sorted = true;
+ for (uint j = start; j < i; j++) {
+ RenderLayer* elt = buffer->at(j);
+ RenderLayer* elt2 = buffer->at(j+1);
+ if (elt->zIndex() > elt2->zIndex()) {
+ sorted = false;
+ buffer->insert(j, elt2);
+ buffer->insert(j+1, elt);
+ }
+ }
+ if (sorted)
+ return;
+ }
+ }
+ else {
+ // Peform a merge sort for larger lists.
+ uint mid = (start+end)/2;
+ sortByZOrder(buffer, mergeBuffer, start, mid);
+ sortByZOrder(buffer, mergeBuffer, mid, end);
+
+ RenderLayer* elt = buffer->at(mid-1);
+ RenderLayer* elt2 = buffer->at(mid);
+
+ // Handle the fast common case (of equal z-indices). The list may already
+ // be completely sorted.
+ if (elt->zIndex() <= elt2->zIndex())
+ return;
+
+ // We have to merge sort. Ensure our merge buffer is big enough to hold
+ // all the items.
+ mergeBuffer->resize(end - start);
+ uint i1 = start;
+ uint i2 = mid;
+
+ elt = buffer->at(i1);
+ elt2 = buffer->at(i2);
+
+ while (i1 < mid || i2 < end) {
+ if (i1 < mid && (i2 == end || elt->zIndex() <= elt2->zIndex())) {
+ mergeBuffer->insert(mergeBuffer->count(), elt);
+ i1++;
+ if (i1 < mid)
+ elt = buffer->at(i1);
+ }
+ else {
+ mergeBuffer->insert(mergeBuffer->count(), elt2);
+ i2++;
+ if (i2 < end)
+ elt2 = buffer->at(i2);
+ }
+ }
+
+ for (uint i = start; i < end; i++)
+ buffer->insert(i, mergeBuffer->at(i-start));
+
+ mergeBuffer->clear();
+ }
+}
+
+void RenderLayer::dirtyZOrderLists()
+{
+ if (m_posZOrderList)
+ m_posZOrderList->clear();
+ if (m_negZOrderList)
+ m_negZOrderList->clear();
+ m_zOrderListsDirty = true;
+}
+
+void RenderLayer::dirtyOverflowList()
+{
+ if (m_overflowList)
+ m_overflowList->clear();
+ m_overflowListDirty = true;
+}
+
+void RenderLayer::updateZOrderLists()
+{
+ if (!isStackingContext() || !m_zOrderListsDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->collectLayers(m_posZOrderList, m_negZOrderList);
+
+ // Sort the two lists.
+ if (m_posZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_posZOrderList, &mergeBuffer, 0, m_posZOrderList->count());
+ }
+ if (m_negZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_negZOrderList, &mergeBuffer, 0, m_negZOrderList->count());
+ }
+
+ m_zOrderListsDirty = false;
+}
+
+void RenderLayer::updateOverflowList()
+{
+ if (!m_overflowListDirty)
+ return;
+
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isOverflowOnly()) {
+ if (!m_overflowList)
+ m_overflowList = new QValueList<RenderLayer*>;
+ m_overflowList->append(child);
+ }
+ }
+
+ m_overflowListDirty = false;
+}
+
+void RenderLayer::collectLayers(QPtrVector<RenderLayer>*& posBuffer, QPtrVector<RenderLayer>*& negBuffer)
+{
+ // FIXME: A child render object or layer could override visibility. Don't remove this
+ // optimization though until RenderObject's nodeAtPoint is patched to understand what to do
+ // when visibility is overridden by a child.
+ if (renderer()->style()->visibility() != VISIBLE)
+ return;
+
+ // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
+ if (!isOverflowOnly()) {
+
+ // Determine which buffer the child should be in.
+ QPtrVector<RenderLayer>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
+
+ // Create the buffer if it doesn't exist yet.
+ if (!buffer)
+ buffer = new QPtrVector<RenderLayer>();
+
+ // Resize by a power of 2 when our buffer fills up.
+ if (buffer->count() == buffer->size())
+ buffer->resize(2*(buffer->size()+1));
+
+ // Append ourselves at the end of the appropriate buffer.
+ buffer->insert(buffer->count(), this);
+ }
+
+ // Recur into our children to collect more layers, but only if we don't establish
+ // a stacking context.
+ if (!isStackingContext()) {
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->collectLayers(posBuffer, negBuffer);
+ }
+}
+
+#ifdef ENABLE_DUMP
+#ifndef KDE_USE_FINAL
+static QTextStream &operator<<(QTextStream &ts, const QRect &r)
+{
+ return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
+}
+#endif
+
+static void write(QTextStream &ts, RenderObject& o, const QString& indent )
+{
+ o.dump(ts, indent);
+
+ for (RenderObject *child = o.firstChild(); child; child = child->nextSibling()) {
+ if (child->layer()) continue;
+ write( ts, *child, indent + " " );
+ }
+}
+
+static void write(QTextStream &ts, const RenderLayer &l,
+ const QRect& layerBounds, const QRect& backgroundClipRect, const QRect& clipRect,
+ int layerType = 0, const QString& indent = QString::null)
+
+{
+ ts << indent << "layer";
+
+ ts << " at (" << l.xPos() << "," << l.yPos() << ") size " << l.width() << "x" << l.height();
+
+ if (layerBounds != layerBounds.intersect(backgroundClipRect)) {
+ ts << " backgroundClip " << backgroundClipRect;
+ }
+ if (layerBounds != layerBounds.intersect(clipRect)) {
+ ts << " clip " << clipRect;
+ }
+
+ if (layerType == -1)
+ ts << " layerType: background only";
+ else if (layerType == 1)
+ ts << " layerType: foreground only";
+
+ ts << "\n";
+
+ if (layerType != -1)
+ write( ts, *l.renderer(), indent + " " );
+
+ ts << "\n";
+}
+
+static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
+ const QRect& paintDirtyRect, const QString& indent)
+{
+ // Calculate the clip rects we should use.
+ QRect layerBounds, damageRect, clipRectToApply;
+ l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
+
+ // Ensure our lists are up-to-date.
+ l->updateZOrderLists();
+ l->updateOverflowList();
+
+ bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
+ QPtrVector<RenderLayer>* negList = l->negZOrderList();
+ QValueList<RenderLayer*>* ovfList = l->overflowList();
+ if (shouldPaint && negList && negList->count() > 0)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, -1, indent);
+
+ if (negList) {
+ for (unsigned i = 0; i != negList->count(); ++i)
+ writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent );
+ }
+
+ if (shouldPaint)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, negList && negList->count() > 0, indent);
+
+ if (ovfList) {
+ for (QValueList<RenderLayer*>::iterator it = ovfList->begin(); it != ovfList->end(); ++it)
+ writeLayers(ts, rootLayer, *it, paintDirtyRect, indent);
+ }
+
+ QPtrVector<RenderLayer>* posList = l->posZOrderList();
+ if (posList) {
+ for (unsigned i = 0; i != posList->count(); ++i)
+ writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
+ }
+}
+
+
+void RenderLayer::dump(QTextStream &ts, const QString &ind)
+{
+ assert( renderer()->isCanvas() );
+
+ writeLayers(ts, this, this, QRect(xPos(), yPos(), width(), height()), ind);
+}
+
+
+#endif
+
+bool RenderLayer::shouldBeOverflowOnly() const
+{
+ return renderer()->style() && renderer()->hasOverflowClip() &&
+ !renderer()->isPositioned() && !renderer()->isRelPositioned();
+ /* && !isTransparent(); */
+}
+
+void RenderLayer::styleChanged()
+{
+ bool isOverflowOnly = shouldBeOverflowOnly();
+ if (isOverflowOnly != m_isOverflowOnly) {
+ m_isOverflowOnly = isOverflowOnly;
+ RenderLayer* p = parent();
+ RenderLayer* sc = stackingContext();
+ if (p)
+ p->dirtyOverflowList();
+ if (sc)
+ sc->dirtyZOrderLists();
+ }
+
+ if (m_object->hasOverflowClip() &&
+ m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
+ if (!m_marquee)
+ m_marquee = new Marquee(this);
+ m_marquee->updateMarqueeStyle();
+ }
+ else if (m_marquee) {
+ delete m_marquee;
+ m_marquee = 0;
+ }
+}
+
+void RenderLayer::suspendMarquees()
+{
+ if (m_marquee)
+ m_marquee->suspend();
+
+ for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->suspendMarquees();
+}
+
+// --------------------------------------------------------------------------
+// Marquee implementation
+
+Marquee::Marquee(RenderLayer* l)
+:m_layer(l), m_currentLoop(0), m_totalLoops(0), m_timerId(0), m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false),
+ m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
+{
+}
+
+int Marquee::marqueeSpeed() const
+{
+ int result = m_layer->renderer()->style()->marqueeSpeed();
+ DOM::NodeImpl* elt = m_layer->renderer()->element();
+ if (elt && elt->id() == ID_MARQUEE) {
+ HTMLMarqueeElementImpl* marqueeElt = static_cast<HTMLMarqueeElementImpl*>(elt);
+ result = kMax(result, marqueeElt->minimumDelay());
+ }
+ return result;
+}
+
+EMarqueeDirection Marquee::direction() const
+{
+ // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
+ // For now just map MAUTO to MBACKWARD
+ EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
+ EDirection dir = m_layer->renderer()->style()->direction();
+ if (result == MAUTO)
+ result = MBACKWARD;
+ if (result == MFORWARD)
+ result = (dir == LTR) ? MRIGHT : MLEFT;
+ if (result == MBACKWARD)
+ result = (dir == LTR) ? MLEFT : MRIGHT;
+
+ // Now we have the real direction. Next we check to see if the increment is negative.
+ // If so, then we reverse the direction.
+ Length increment = m_layer->renderer()->style()->marqueeIncrement();
+ if (increment.value() < 0)
+ result = static_cast<EMarqueeDirection>(-result);
+
+ return result;
+}
+
+bool Marquee::isHorizontal() const
+{
+ return direction() == MLEFT || direction() == MRIGHT;
+}
+
+bool Marquee::isUnfurlMarquee() const
+{
+ EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+ return (behavior == MUNFURL);
+}
+
+int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
+{
+ RenderObject* o = m_layer->renderer();
+ RenderStyle* s = o->style();
+ if (isHorizontal()) {
+ bool ltr = s->direction() == LTR;
+ int clientWidth = o->clientWidth();
+ int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
+ if (ltr)
+ contentWidth += (o->paddingRight() - o->borderLeft());
+ else {
+ contentWidth = o->width() - contentWidth;
+ contentWidth += (o->paddingLeft() - o->borderRight());
+ }
+ if (dir == MRIGHT) {
+ if (stopAtContentEdge)
+ return kMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? contentWidth : clientWidth;
+ }
+ else {
+ if (stopAtContentEdge)
+ return kMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? -clientWidth : -contentWidth;
+ }
+ }
+ else {
+ int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
+ m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
+ int clientHeight = m_layer->renderer()->clientHeight();
+ if (dir == MUP) {
+ if (stopAtContentEdge)
+ return kMin(contentHeight - clientHeight, 0);
+ else
+ return -clientHeight;
+ }
+ else {
+ if (stopAtContentEdge)
+ return kMax(contentHeight - clientHeight, 0);
+ else
+ return contentHeight;
+ }
+ }
+}
+
+void Marquee::start()
+{
+ if (m_timerId || m_layer->renderer()->style()->marqueeIncrement().value() == 0)
+ return;
+
+ if (!m_suspended && !m_stopped) {
+ if (isUnfurlMarquee()) {
+ bool forward = direction() == MDOWN || direction() == MRIGHT;
+ bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
+ m_unfurlPos = isReversed ? m_end : m_start;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else {
+ if (isHorizontal())
+ m_layer->scrollToOffset(m_start, 0, false, false);
+ else
+ m_layer->scrollToOffset(0, m_start, false, false);
+ }
+ }
+ else
+ m_suspended = false;
+
+ m_stopped = false;
+ m_timerId = startTimer(speed());
+}
+
+void Marquee::suspend()
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+
+ m_suspended = true;
+}
+
+void Marquee::stop()
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+
+ m_stopped = true;
+}
+
+void Marquee::updateMarqueePosition()
+{
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate) {
+ if (isUnfurlMarquee()) {
+ if (m_unfurlPos < m_start) {
+ m_unfurlPos = m_start;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else if (m_unfurlPos > m_end) {
+ m_unfurlPos = m_end;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ }
+ else {
+ EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+ m_start = computePosition(direction(), behavior == MALTERNATE);
+ m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
+ }
+ if (!m_stopped) start();
+ }
+}
+
+void Marquee::updateMarqueeStyle()
+{
+ RenderStyle* s = m_layer->renderer()->style();
+
+ if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
+ m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
+
+ m_totalLoops = s->marqueeLoopCount();
+ m_direction = s->marqueeDirection();
+ m_whiteSpace = s->whiteSpace();
+
+ if (m_layer->renderer()->isHTMLMarquee()) {
+ // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
+ // one loop.
+ if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
+ m_totalLoops = 1;
+
+ // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
+ // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
+ // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
+ // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
+ // marquee element.
+ // FIXME: Bring these up with the CSS WG.
+ if (isHorizontal() && m_layer->renderer()->childrenInline()) {
+ s->setWhiteSpace(NOWRAP);
+ s->setTextAlign(TAAUTO);
+ }
+ }
+
+ if (speed() != marqueeSpeed()) {
+ m_speed = marqueeSpeed();
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = startTimer(speed());
+ }
+ }
+
+ // Check the loop count to see if we should now stop.
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate && !m_timerId)
+ m_layer->renderer()->setNeedsLayout(true);
+ else if (!activate && m_timerId) {
+ // Destroy the timer.
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+}
+
+void Marquee::timerEvent(QTimerEvent* /*evt*/)
+{
+ if (m_layer->renderer()->needsLayout())
+ return;
+
+ if (m_reset) {
+ m_reset = false;
+ if (isHorizontal())
+ m_layer->scrollToXOffset(m_start);
+ else
+ m_layer->scrollToYOffset(m_start);
+ return;
+ }
+
+ RenderStyle* s = m_layer->renderer()->style();
+
+ int endPoint = m_end;
+ int range = m_end - m_start;
+ int newPos;
+ if (range == 0)
+ newPos = m_end;
+ else {
+ bool addIncrement = direction() == MUP || direction() == MLEFT;
+ bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
+ if (isUnfurlMarquee()) {
+ isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
+ addIncrement = !isReversed;
+ }
+ if (isReversed) {
+ // We're going in the reverse direction.
+ endPoint = m_start;
+ range = -range;
+ if (!isUnfurlMarquee())
+ addIncrement = !addIncrement;
+ }
+ bool positive = range > 0;
+ int clientSize = isUnfurlMarquee() ? abs(range) :
+ (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
+ int increment = kMax(1, abs(m_layer->renderer()->style()->marqueeIncrement().width(clientSize)));
+ int currentPos = isUnfurlMarquee() ? m_unfurlPos :
+ (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
+ newPos = currentPos + (addIncrement ? increment : -increment);
+ if (positive)
+ newPos = kMin(newPos, endPoint);
+ else
+ newPos = kMax(newPos, endPoint);
+ }
+
+ if (newPos == endPoint) {
+ m_currentLoop++;
+ if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+ else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL)
+ m_reset = true;
+ }
+
+ if (isUnfurlMarquee()) {
+ m_unfurlPos = newPos;
+ m_layer->renderer()->setChildNeedsLayout(true);
+ }
+ else {
+ if (isHorizontal())
+ m_layer->scrollToXOffset(newPos);
+ else
+ m_layer->scrollToYOffset(newPos);
+ }
+}
+
+#include "render_layer.moc"