#include "render_layer.h"
#include <kdebug.h>
#include <assert.h>
#include "khtmlview.h"
#include "render_canvas.h"
#include "render_arena.h"
#include "xml/dom_docimpl.h"
#include "xml/dom2_eventsimpl.h"
#include "misc/htmltags.h"
#include "html/html_blockimpl.h"
#include <qscrollbar.h>
#include <qptrvector.h>
#if APPLE_CHANGES
#include "KWQKHTMLPart.h" // For Dashboard.
#endif
using namespace DOM;
using namespace khtml;
#ifdef APPLE_CHANGES
QScrollBar* RenderLayer::gScrollBar = 0;
#endif
#ifndef NDEBUG
static bool inRenderLayerDetach;
#endif
void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw()
{
return renderArena->allocate(sz);
}
void ClipRects::operator delete(void* ptr, size_t sz)
{
*(size_t *)ptr = sz;
}
void ClipRects::detach(RenderArena* renderArena)
{
delete this;
renderArena->free(*(size_t *)this, this);
}
void
RenderScrollMediator::slotValueChanged(int val)
{
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_relX( 0 ),
m_relY( 0 ),
m_x( 0 ),
m_y( 0 ),
m_width( 0 ),
m_height( 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_clipRects( 0 ) ,
m_scrollDimensionsDirty( true ),
m_zOrderListsDirty( true ),
m_usedTransparency( false ),
m_marquee( 0 )
{
}
RenderLayer::~RenderLayer()
{
delete m_hBar;
delete m_vBar;
delete m_scrollMediator;
delete m_posZOrderList;
delete m_negZOrderList;
delete m_marquee;
assert(!m_clipRects);
}
void RenderLayer::computeRepaintRects()
{
if (m_object->style()->visibility() == VISIBLE)
m_object->getAbsoluteRepaintRectIncludingFloats(m_repaintRect, m_fullRepaintRect);
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->computeRepaintRects();
}
void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint)
{
if (doFullRepaint) {
m_object->repaint();
checkForRepaint = doFullRepaint = false;
}
updateLayerPosition();
if (m_hBar || m_vBar) {
int x = 0;
int y = 0;
convertToLayerCoords(root(), x, y);
QRect layerBounds = QRect(x,y,width(),height());
positionScrollbars(layerBounds);
}
if (checkForRepaint && (m_object->style()->visibility() == VISIBLE))
m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->updateLayerPositions(doFullRepaint, checkForRepaint);
if (m_marquee)
m_marquee->updateMarqueePosition();
}
void RenderLayer::updateLayerPosition()
{
clearClipRect();
if (renderer()->isCanvas())
return;
int x = m_object->xPos();
int y = m_object->yPos();
if (!m_object->isPositioned()) {
RenderObject* curr = m_object->parent();
while (curr && !curr->layer()) {
x += curr->xPos();
y += curr->yPos();
curr = curr->parent();
}
}
m_relX = m_relY = 0;
if (m_object->isRelPositioned()) {
static_cast<RenderBox*>(m_object)->relativePositionOffset(m_relX, m_relY);
x += m_relX; y += m_relY;
}
if (m_object->isPositioned() && enclosingPositionedAncestor()) {
RenderLayer* positionedParent = enclosingPositionedAncestor();
positionedParent->subtractScrollOffset(x, y);
if (m_object->isPositioned() && positionedParent->renderer()->isRelPositioned() &&
positionedParent->renderer()->isInlineFlow()) {
RenderFlow* flow = static_cast<RenderFlow*>(positionedParent->renderer());
int sx = 0, sy = 0;
if (flow->firstLineBox()) {
sx = flow->firstLineBox()->xPos();
sy = flow->firstLineBox()->yPos();
}
else {
sx = flow->staticX();
sy = flow->staticY();
}
bool isInlineType = m_object->style()->isOriginalDisplayInlineType();
if (!m_object->hasStaticX())
x += sx;
if (m_object->hasStaticX() && !isInlineType)
x += sx - (m_object->containingBlock()->borderLeft() + m_object->containingBlock()->paddingLeft());
if (!m_object->hasStaticY())
y += sy;
}
}
else if (parent())
parent()->subtractScrollOffset(x, y);
setPos(x,y);
setWidth(m_object->width());
setHeight(m_object->height());
if (!m_object->hasOverflowClip()) {
if (m_object->overflowWidth() > m_object->width())
setWidth(m_object->overflowWidth());
if (m_object->overflowHeight() > m_object->height())
setHeight(m_object->overflowHeight());
}
}
RenderLayer *RenderLayer::stackingContext() const
{
RenderLayer* curr = parent();
for ( ; curr && !curr->m_object->isCanvas() && !curr->m_object->isRoot() &&
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->isRoot() &&
!curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
curr = curr->parent());
return curr;
}
#if 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;
}
void RenderLayer::beginTransparencyLayers(QPainter* p)
{
if (isTransparent() && m_usedTransparency)
return;
RenderLayer* ancestor = transparentAncestor();
if (ancestor)
ancestor->beginTransparencyLayers(p);
if (isTransparent()) {
m_usedTransparency = true;
p->beginTransparencyLayer(renderer()->style()->opacity());
}
}
#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);
*(size_t *)ptr = sz;
}
void RenderLayer::detach(RenderArena* renderArena)
{
#ifndef NDEBUG
inRenderLayerDetach = true;
#endif
delete this;
#ifndef NDEBUG
inRenderLayerDetach = false;
#endif
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);
RenderLayer* stackingContext = child->stackingContext();
if (stackingContext)
stackingContext->dirtyZOrderLists();
}
RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
{
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();
RenderLayer* stackingContext = oldChild->stackingContext();
if (stackingContext)
oldChild->stackingContext()->dirtyZOrderLists();
oldChild->setPreviousSibling(0);
oldChild->setNextSibling(0);
oldChild->setParent(0);
return oldChild;
}
void RenderLayer::removeOnlyThisLayer()
{
if (!m_parent)
return;
clearClipRects();
RenderLayer* parent = m_parent;
RenderLayer* nextSib = nextSibling();
parent->removeChild(this);
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()) {
RenderLayer* parentLayer = renderer()->parent()->enclosingLayer();
if (parentLayer)
parentLayer->addChild(this,
renderer()->parent()->findNextLayer(parentLayer, renderer()));
}
for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling())
curr->moveLayers(m_parent, this);
clearClipRects();
}
void
RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
{
if (ancestorLayer == this)
return;
if (m_object->style()->position() == FIXED) {
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::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
{
if (renderer()->style()->overflow() != OMARQUEE) {
if (x < 0) x = 0;
if (y < 0) y = 0;
int maxX = scrollWidth() - m_object->clientWidth();
int maxY = scrollHeight() - m_object->clientHeight();
if (x > maxX) x = maxX;
if (y > maxY) y = maxY;
}
m_scrollX = x;
m_scrollY = y;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->updateLayerPositions(false, false);
#if APPLE_CHANGES
m_object->updateWidgetPositions();
RenderCanvas *canvas = renderer()->canvas();
if (canvas)
canvas->view()->updateDashboardRegions();
#endif
m_object->element()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
if (repaint)
m_object->repaint();
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::setHasHorizontalScrollbar(bool hasScrollbar)
{
if (hasScrollbar && !m_hBar) {
QScrollView* scrollView = m_object->element()->getDocument()->view();
m_hBar = new QScrollBar(Qt::Horizontal, 0);
scrollView->addChild(m_hBar, 0, -50000);
if (!m_scrollMediator)
m_scrollMediator = new RenderScrollMediator(this);
m_scrollMediator->connect(m_hBar, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
}
else if (!hasScrollbar && m_hBar) {
QScrollView* scrollView = m_object->element()->getDocument()->view();
scrollView->removeChild (m_hBar);
m_scrollMediator->disconnect(m_hBar, SIGNAL(valueChanged(int)),
m_scrollMediator, SLOT(slotValueChanged(int)));
delete m_hBar;
m_hBar = 0;
}
}
void
RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
{
if (hasScrollbar && !m_vBar) {
QScrollView* scrollView = m_object->element()->getDocument()->view();
m_vBar = new QScrollBar(Qt::Vertical, 0);
scrollView->addChild(m_vBar, 0, -50000);
if (!m_scrollMediator)
m_scrollMediator = new RenderScrollMediator(this);
m_scrollMediator->connect(m_vBar, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
}
else if (!hasScrollbar && m_vBar) {
QScrollView* scrollView = m_object->element()->getDocument()->view();
scrollView->removeChild (m_vBar);
m_scrollMediator->disconnect(m_vBar, SIGNAL(valueChanged(int)),
m_scrollMediator, SLOT(slotValueChanged(int)));
delete m_vBar;
m_vBar = 0;
}
}
int
RenderLayer::verticalScrollbarWidth()
{
if (!m_vBar)
return 0;
return m_vBar->width();
}
int
RenderLayer::horizontalScrollbarHeight()
{
if (!m_hBar)
return 0;
return m_hBar->height();
}
void
RenderLayer::moveScrollbarsAside()
{
if (m_hBar)
m_hBar->move(0, -50000);
if (m_vBar)
m_vBar->move(0, -50000);
}
void
RenderLayer::positionScrollbars(const QRect& absBounds)
{
if (m_vBar) {
m_vBar->move(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) {
m_hBar->move(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());
}
}
#define LINE_STEP 10
#define PAGE_KEEP 40
int RenderLayer::scrollWidth()
{
if (m_scrollDimensionsDirty)
computeScrollDimensions();
return m_scrollWidth;
}
int RenderLayer::scrollHeight()
{
if (m_scrollDimensionsDirty)
computeScrollDimensions();
return m_scrollHeight;
}
void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
{
m_scrollDimensionsDirty = false;
int rightPos = m_object->rightmostPosition(true, false) - m_object->borderLeft();
int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
int clientWidth = m_object->clientWidth();
int clientHeight = m_object->clientHeight();
m_scrollWidth = kMax(rightPos, clientWidth);
m_scrollHeight = kMax(bottomPos, clientHeight);
if (needHBar)
*needHBar = rightPos > clientWidth;
if (needVBar)
*needVBar = bottomPos > clientHeight;
}
void
RenderLayer::updateScrollInfoAfterLayout()
{
m_scrollDimensionsDirty = true;
if (m_object->style()->overflow() == OHIDDEN)
return;
bool needHorizontalBar, needVerticalBar;
computeScrollDimensions(&needHorizontalBar, &needVerticalBar);
if (m_object->style()->overflow() != OMARQUEE) {
int newX = kMax(0, kMin(m_scrollX, scrollWidth() - m_object->clientWidth()));
int newY = kMax(0, kMin(m_scrollY, scrollHeight() - m_object->clientHeight()));
if (newX != m_scrollX || newY != m_scrollY)
scrollToOffset(newX, newY);
}
bool haveHorizontalBar = m_hBar;
bool haveVerticalBar = m_vBar;
if (m_object->style()->overflow() == OSCROLL) {
m_hBar->setEnabled(needHorizontalBar);
m_vBar->setEnabled(needVerticalBar);
}
bool scrollbarsChanged = (m_object->hasAutoScrollbars()) &&
(haveHorizontalBar != needHorizontalBar || haveVerticalBar != needVerticalBar);
if (scrollbarsChanged) {
setHasHorizontalScrollbar(needHorizontalBar);
setHasVerticalScrollbar(needVerticalBar);
#if APPLE_CHANGES
if (m_object->document()->hasDashboardRegions())
m_object->document()->setDashboardRegionsDirty(true);
#endif
m_object->repaint();
if (m_object->style()->overflow() == OAUTO) {
m_object->setNeedsLayout(true);
if (m_object->isRenderBlock())
static_cast<RenderBlock*>(m_object)->layoutBlock(true);
else
m_object->layout();
}
}
if (m_hBar) {
int clientWidth = m_object->clientWidth();
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, m_scrollWidth-clientWidth);
m_object->repaintRectangle(QRect(m_object->borderLeft(), m_object->borderTop() + clientHeight(),
horizontalScrollbarHeight(),
m_object->width() - m_object->borderLeft() - m_object->borderRight()));
#endif
}
if (m_vBar) {
int clientHeight = m_object->clientHeight();
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, m_scrollHeight-clientHeight);
#endif
m_object->repaintRectangle(QRect(m_object->borderLeft() + m_object->clientWidth(),
m_object->borderTop(), verticalScrollbarWidth(),
m_object->height() - m_object->borderTop() - m_object->borderBottom()));
}
#if APPLE_CHANGES
if (m_object->document()->hasDashboardRegions())
m_object->document()->setDashboardRegionsDirty(true);
#endif
m_object->repaint();
}
#if APPLE_CHANGES
void
RenderLayer::paintScrollbars(QPainter* p, const QRect& damageRect)
{
if (m_hBar || m_vBar) {
int x = 0;
int y = 0;
convertToLayerCoords(root(), x, y);
QRect layerBounds = QRect(x, y, width(), height());
positionScrollbars(layerBounds);
}
if (m_hBar)
m_hBar->paint(p, damageRect);
if (m_vBar)
m_vBar->paint(p, damageRect);
}
#endif
bool RenderLayer::scroll(KWQScrollDirection direction, KWQScrollGranularity granularity, float multiplier)
{
bool didHorizontalScroll = false;
bool didVerticalScroll = false;
if (m_hBar != 0) {
if (granularity == KWQScrollDocument) {
didHorizontalScroll = m_hBar->scroll(KWQScrollLeft, KWQScrollDocument, multiplier);
} else {
didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier);
}
}
if (m_vBar != 0) {
didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier);
}
return (didHorizontalScroll || didVerticalScroll);
}
void
RenderLayer::paint(QPainter *p, const QRect& damageRect, bool selectionOnly, RenderObject *paintingRoot)
{
paintLayer(this, p, damageRect, false, selectionOnly, paintingRoot);
}
static void setClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
{
if (paintDirtyRect == clipRect)
return;
p->save();
#if 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 haveTransparency, bool selectionOnly,
RenderObject *paintingRoot)
{
QRect layerBounds, damageRect, clipRectToApply;
calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
int x = layerBounds.x();
int y = layerBounds.y();
updateZOrderLists();
#if APPLE_CHANGES
if (isTransparent())
haveTransparency = true;
#endif
RenderObject *paintingRootForRenderer = 0;
if (paintingRoot && !m_object->hasAncestor(paintingRoot)) {
paintingRootForRenderer = paintingRoot;
}
bool shouldPaint = intersectsDamageRect(layerBounds, damageRect);
if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) {
#if APPLE_CHANGES
if (haveTransparency)
beginTransparencyLayers(p);
#endif
setClip(p, paintDirtyRect, damageRect);
RenderObject::PaintInfo info(p, damageRect, PaintActionBlockBackground, paintingRootForRenderer);
renderer()->paint(info, x - renderer()->xPos(), y - renderer()->yPos());
#if APPLE_CHANGES
paintScrollbars(p, damageRect);
#endif
restoreClip(p, paintDirtyRect, damageRect);
}
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, haveTransparency, selectionOnly, paintingRoot);
}
}
if (shouldPaint && !clipRectToApply.isEmpty()) {
#if APPLE_CHANGES
if (haveTransparency)
beginTransparencyLayers(p);
#endif
setClip(p, paintDirtyRect, clipRectToApply);
int tx = x - renderer()->xPos();
int ty = y - renderer()->yPos();
RenderObject::PaintInfo info(p, clipRectToApply,
selectionOnly ? PaintActionSelection : PaintActionChildBlockBackgrounds,
paintingRootForRenderer);
renderer()->paint(info, tx, ty);
if (!selectionOnly) {
info.phase = PaintActionFloat;
renderer()->paint(info, tx, ty);
info.phase = PaintActionForeground;
renderer()->paint(info, tx, ty);
info.phase = PaintActionOutline;
renderer()->paint(info, tx, ty);
}
restoreClip(p, paintDirtyRect, clipRectToApply);
}
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, haveTransparency, selectionOnly, paintingRoot);
}
}
#if APPLE_CHANGES
if (isTransparent() && m_usedTransparency) {
p->endTransparencyLayer();
m_usedTransparency = false;
}
#endif
}
bool
RenderLayer::hitTest(RenderObject::NodeInfo& info, int x, int y)
{
#if APPLE_CHANGES
RenderLayer::gScrollBar = 0;
#endif
QRect damageRect(m_x, m_y, width(), height());
RenderLayer* insideLayer = hitTestLayer(this, info, x, y, damageRect);
DOM::NodeImpl* node = info.innerNode();
while (node) {
if (node->hasAnchor() && !info.URLElement())
info.setURLElement(node);
node = node->parentNode();
}
updateHoverActiveState(info);
return insideLayer;
}
RenderLayer*
RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
int xMousePos, int yMousePos, const QRect& hitTestRect)
{
QRect layerBounds, bgRect, fgRect;
calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect);
updateZOrderLists();
RenderLayer* insideLayer = 0;
if (m_posZOrderList) {
uint count = m_posZOrderList->count();
for (int i = count-1; i >= 0; i--) {
RenderLayer* child = m_posZOrderList->at(i);
insideLayer = child->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
if (insideLayer)
return insideLayer;
}
}
if (containsPoint(xMousePos, yMousePos, fgRect) &&
renderer()->hitTest(info, xMousePos, yMousePos,
layerBounds.x() - renderer()->xPos(),
layerBounds.y() - renderer()->yPos(), HitTestDescendants)) {
if (!info.innerNode()) {
for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
if (r->element()) {
info.setInnerNode(r->element());
break;
}
}
}
if (!info.innerNonSharedNode()) {
for (RenderObject *r = renderer(); r != NULL; r = r->parent()) {
if (r->element()) {
info.setInnerNonSharedNode(r->element());
break;
}
}
}
return this;
}
if (m_negZOrderList) {
uint count = m_negZOrderList->count();
for (int i = count-1; i >= 0; i--) {
RenderLayer* child = m_negZOrderList->at(i);
insideLayer = child->hitTestLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
if (insideLayer)
return insideLayer;
}
}
if (containsPoint(xMousePos, yMousePos, bgRect) &&
renderer()->hitTest(info, xMousePos, yMousePos,
layerBounds.x() - renderer()->xPos(),
layerBounds.y() - renderer()->yPos(),
HitTestSelf))
return this;
return 0;
}
void RenderLayer::calculateClipRects(const RenderLayer* rootLayer)
{
if (m_clipRects)
return;
if (!parent()) {
m_clipRects = new (m_object->renderArena()) ClipRects(QRect(0,0,width(),height()));
m_clipRects->ref();
return;
}
parent()->calculateClipRects(rootLayer);
QRect posClipRect(parent()->clipRects()->posClipRect());
QRect overflowClipRect(parent()->clipRects()->overflowClipRect());
QRect fixedClipRect(parent()->clipRects()->fixedClipRect());
if (m_object->style()->position() == FIXED) {
posClipRect = fixedClipRect;
overflowClipRect = fixedClipRect;
}
else if (m_object->style()->position() == RELATIVE)
posClipRect = overflowClipRect;
if (m_object->hasOverflowClip() || m_object->hasClip()) {
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);
}
}
if (posClipRect == parent()->clipRects()->posClipRect() &&
overflowClipRect == parent()->clipRects()->overflowClipRect() &&
fixedClipRect == parent()->clipRects()->fixedClipRect())
m_clipRects = parent()->clipRects();
else
m_clipRects = new (m_object->renderArena()) ClipRects(overflowClipRect, fixedClipRect, posClipRect);
m_clipRects->ref();
}
void RenderLayer::calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
QRect& backgroundRect, QRect& foregroundRect)
{
if (parent()) {
parent()->calculateClipRects(rootLayer);
backgroundRect = m_object->style()->position() == FIXED ? parent()->clipRects()->fixedClipRect() :
(m_object->isPositioned() ? parent()->clipRects()->posClipRect() :
parent()->clipRects()->overflowClipRect());
backgroundRect = backgroundRect.intersect(paintDirtyRect);
} else
backgroundRect = paintDirtyRect;
foregroundRect = backgroundRect;
int x = 0;
int y = 0;
convertToLayerCoords(rootLayer, x, y);
layerBounds = QRect(x,y,width(),height());
if (m_object->hasOverflowClip() || m_object->hasClip()) {
if (m_object->hasOverflowClip())
foregroundRect = foregroundRect.intersect(m_object->getOverflowClipRect(x,y));
if (m_object->hasClip()) {
QRect newPosClip = m_object->getClipRect(x,y);
backgroundRect = backgroundRect.intersect(newPosClip);
foregroundRect = foregroundRect.intersect(newPosClip);
}
backgroundRect = backgroundRect.intersect(layerBounds);
}
}
static bool mustExamineRenderer(RenderObject* renderer)
{
if (renderer->isCanvas() || renderer->isRoot() || renderer->isInlineFlow())
return true;
QRect bbox = renderer->borderBox();
QRect overflowRect = renderer->overflowRect(false);
if (bbox != overflowRect)
return true;
QRect floatRect = renderer->floatRect();
if (bbox != floatRect)
return true;
return false;
}
bool RenderLayer::intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const
{
return mustExamineRenderer(renderer()) || layerBounds.intersects(damageRect);
}
bool RenderLayer::containsPoint(int x, int y, const QRect& damageRect) const
{
return mustExamineRenderer(renderer()) || damageRect.contains(x, y);
}
void RenderLayer::clearClipRects()
{
if (!m_clipRects)
return;
clearClipRect();
for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
l->clearClipRects();
}
void RenderLayer::clearClipRect()
{
if (m_clipRects) {
m_clipRects->deref(m_object->renderArena());
m_clipRects = 0;
}
}
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)
{
if (info.readonly())
return;
DOM::DocumentImpl* doc = renderer()->document();
DOM::NodeImpl* oldHoverNode = doc ? doc->hoverNode() : 0;
DOM::NodeImpl* newHoverNode = info.innerNode();
if (doc)
doc->setHoverNode(newHoverNode);
RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0;
RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj);
if (oldHoverObj != newHoverObj) {
for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = hoverAncestor(curr)) {
curr->setMouseInside(false);
if (curr->element() && !curr->isText()) {
bool oldActive = curr->element()->active();
curr->element()->setActive(false);
if (curr->style()->affectedByHoverRules() ||
(curr->style()->affectedByActiveRules() && oldActive))
curr->element()->setChanged();
}
}
}
for (RenderObject* curr = newHoverObj; curr; curr = hoverAncestor(curr)) {
bool oldInside = curr->mouseInside();
curr->setMouseInside(true);
if (curr->element() && !curr->isText()) {
bool oldActive = curr->element()->active();
curr->element()->setActive(info.active());
if ((curr->style()->affectedByHoverRules() && !oldInside) ||
(curr->style()->affectedByActiveRules() && oldActive != info.active()))
curr->element()->setChanged();
}
}
}
static void sortByZOrder(QPtrVector<RenderLayer>* buffer,
QPtrVector<RenderLayer>* mergeBuffer,
uint start, uint end)
{
if (start >= end)
return;
if (end - start <= 6) {
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 {
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);
if (elt->zIndex() <= elt2->zIndex())
return;
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::updateZOrderLists()
{
if (!isStackingContext() || !m_zOrderListsDirty)
return;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->collectLayers(m_posZOrderList, m_negZOrderList);
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::collectLayers(QPtrVector<RenderLayer>*& posBuffer, QPtrVector<RenderLayer>*& negBuffer)
{
if (renderer()->style()->visibility() != VISIBLE)
return;
QPtrVector<RenderLayer>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
if (!buffer)
buffer = new QPtrVector<RenderLayer>();
if (buffer->count() == buffer->size())
buffer->resize(2*(buffer->size()+1));
buffer->insert(buffer->count(), this);
if (!isStackingContext()) {
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->collectLayers(posBuffer, negBuffer);
}
}
void RenderLayer::repaintIncludingDescendants()
{
m_object->repaint();
for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
curr->repaintIncludingDescendants();
}
void RenderLayer::styleChanged()
{
if (m_object->style()->overflow() == 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::Marquee(RenderLayer* l)
:m_layer(l), m_currentLoop(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
{
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;
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;
m_totalLoops = s->marqueeLoopCount();
m_direction = s->marqueeDirection();
m_whiteSpace = s->whiteSpace();
if (m_layer->renderer()->isHTMLMarquee()) {
if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL))
m_totalLoops = 1;
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());
}
}
bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
if (activate && !m_timerId)
m_layer->renderer()->setNeedsLayout(true);
else if (!activate && m_timerId) {
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) {
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);
}
}