Latest layer patch
David Hyatt
hyatt at apple.com
Tue Sep 2 11:52:45 CEST 2003
Here's the latest layer patch. I fixed another clipping bug with
relative positioning, and I optimized relpositioning to only do a
repaint when left/top/right/bottom changes and not a layout. This
patch also fixes a couple of hover-related crashes introduced by the
last revision of the patch.
I've also attached the complete clipping test suite I'm using. The
tests are pretty self-explanatory.
Let me know if you have any feedback on the patch itself, as this patch
has not yet landed in the Safari tree.
Dave
(hyatt at apple.com)
-------------- next part --------------
Index: khtml/css/cssstyleselector.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssstyleselector.cpp,v
retrieving revision 1.95
diff -u -p -r1.95 khtml/css/cssstyleselector.cpp
--- khtml/css/cssstyleselector.cpp 2003/08/26 22:19:17 1.95
+++ khtml/css/cssstyleselector.cpp 2003/09/02 17:44:09
@@ -527,6 +527,21 @@ void CSSStyleSelector::adjustRenderStyle
}
}
+ // Make sure our z-index value is only applied if the object is positioned,
+ // relatively positioned, or transparent.
+ if (style->position() == STATIC && style->opacity() == 1.0f) {
+ if (e && e->getDocument()->documentElement() == e)
+ style->setZIndex(0); // The root has a z-index of 0 if not positioned or transparent.
+ else
+ style->setHasAutoZIndex(); // Everyone else gets an auto z-index.
+ }
+
+ // Auto z-index becomes 0 for transparent objects. This prevents cases where
+ // objects that should be blended as a single unit end up with a non-transparent object
+ // wedged in between them.
+ if (style->opacity() < 1.0f && style->hasAutoZIndex())
+ style->setZIndex(0);
+
// Finally update our text decorations in effect, but don't allow text-decoration to percolate through
// tables, inline blocks, inline tables, or run-ins.
if (style->display() == TABLE || style->display() == INLINE_TABLE || style->display() == RUN_IN
Index: khtml/rendering/render_block.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_block.cpp,v
retrieving revision 1.55
diff -u -p -r1.55 khtml/rendering/render_block.cpp
--- khtml/rendering/render_block.cpp 2003/08/28 21:24:07 1.55
+++ khtml/rendering/render_block.cpp 2003/09/02 17:44:13
@@ -1853,13 +1853,14 @@ bool RenderBlock::isPointInScrollbar(int
return false;
}
-bool RenderBlock::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inBox)
+bool RenderBlock::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inBox)
{
bool inScrollbar = isPointInScrollbar(_x, _y, _tx+xPos(), _ty+yPos());
- if (inScrollbar)
+ if (inScrollbar && hitTestAction != HitTestChildrenOnly)
inBox = true;
- if (m_floatingObjects && !inScrollbar) {
+ if (hitTestAction != HitTestSelfOnly && m_floatingObjects && !inScrollbar) {
int stx = _tx + xPos();
int sty = _ty + yPos();
if (style()->hidesOverflow() && m_layer)
@@ -1877,7 +1878,7 @@ bool RenderBlock::nodeAtPoint(NodeInfo&
sty+o->startY + o->node->marginTop() - o->node->yPos());
}
- inBox |= RenderFlow::nodeAtPoint(info, _x, _y, _tx, _ty, inBox);
+ inBox |= RenderFlow::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inBox);
return inBox;
}
Index: khtml/rendering/render_block.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_block.h,v
retrieving revision 1.21
diff -u -p -r1.21 khtml/rendering/render_block.h
--- khtml/rendering/render_block.h 2003/08/27 01:09:18 1.21
+++ khtml/rendering/render_block.h 2003/09/02 17:44:14
@@ -158,7 +158,8 @@ public:
int *heightRemaining = 0) const;
int leftOffset(int y) const { return leftRelOffset(y, leftOffset(), true); }
- virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, bool inside=false);
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
bool isPointInScrollbar(int x, int y, int tx, int ty);
Index: khtml/rendering/render_box.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_box.cpp,v
retrieving revision 1.80
diff -u -p -r1.80 khtml/rendering/render_box.cpp
--- khtml/rendering/render_box.cpp 2003/08/21 18:52:06 1.80
+++ khtml/rendering/render_box.cpp 2003/09/02 17:44:15
@@ -108,34 +108,11 @@ void RenderBox::setStyle(RenderStyle *_s
m_layer = 0;
}
- adjustZIndex();
-
// Set the text color if we're the body.
if (isBody())
element()->getDocument()->setTextColor(_style->color());
}
-void RenderBox::adjustZIndex()
-{
- if (m_layer) {
- // Make sure our z-index values are only applied if we're positioned or
- // relpositioned or transparent.
- if (!isPositioned() && !isRelPositioned() && style()->opacity() == 1.0f) {
- // Set the auto z-index flag.
- if (isRoot())
- style()->setZIndex(0);
- else
- style()->setHasAutoZIndex();
- }
-
- // Auto z-index becomes 0 for transparent objects. This prevents cases where
- // objects that should be blended as a single unit end up with a non-transparent object
- // wedged in between them.
- if (style()->opacity() < 1.0f && style()->hasAutoZIndex())
- style()->setZIndex(0);
- }
-}
-
RenderBox::~RenderBox()
{
//kdDebug( 6040 ) << "Element destructor: this=" << nodeName().string() << endl;
@@ -612,8 +589,9 @@ void RenderBox::repaintRectangle(int x,
// Apply the relative position offset when invalidating a rectangle. The layer
// is translated, but the render box isn't, so we need to do this to get the
- // right dirty rect.
- if (isRelPositioned())
+ // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
+ // flag on the RenderObject has been cleared, so use the one on the style().
+ if (style()->position() == RELATIVE)
relativePositionOffset(x,y);
if (style()->position()==FIXED) f=true;
Index: khtml/rendering/render_box.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_box.h,v
retrieving revision 1.26
diff -u -p -r1.26 khtml/rendering/render_box.h
--- khtml/rendering/render_box.h 2003/07/11 17:55:42 1.26
+++ khtml/rendering/render_box.h 2003/09/02 17:44:15
@@ -137,9 +137,6 @@ protected:
virtual QRect getOverflowClipRect(int tx, int ty);
virtual QRect getClipRect(int tx, int ty);
- // Called to correct the z-index after the style has been changed.
- void adjustZIndex();
-
// the actual height of the contents + borders + padding
int m_height;
Index: khtml/rendering/render_frames.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_frames.cpp,v
retrieving revision 1.42
diff -u -p -r1.42 khtml/rendering/render_frames.cpp
--- khtml/rendering/render_frames.cpp 2003/08/19 21:49:53 1.42
+++ khtml/rendering/render_frames.cpp 2003/09/02 17:44:17
@@ -84,9 +84,10 @@ RenderFrameSet::~RenderFrameSet()
delete [] m_vSplitVar;
}
-bool RenderFrameSet::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside)
+bool RenderFrameSet::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inside)
{
- RenderBox::nodeAtPoint(info, _x, _y, _tx, _ty, inside);
+ RenderBox::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside);
inside = m_resizing || canResize(_x, _y);
Index: khtml/rendering/render_frames.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_frames.h,v
retrieving revision 1.9
diff -u -p -r1.9 khtml/rendering/render_frames.h
--- khtml/rendering/render_frames.h 2003/05/17 01:40:18 1.9
+++ khtml/rendering/render_frames.h 2003/09/02 17:44:17
@@ -60,7 +60,8 @@ public:
bool canResize( int _x, int _y);
void setResizing(bool e);
- bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, bool inside=false);
+ bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
DOM::HTMLFrameSetElementImpl *element() const
{ return static_cast<DOM::HTMLFrameSetElementImpl*>(RenderObject::element()); }
Index: khtml/rendering/render_image.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_image.cpp,v
retrieving revision 1.38
diff -u -p -r1.38 khtml/rendering/render_image.cpp
--- khtml/rendering/render_image.cpp 2003/07/30 21:03:32 1.38
+++ khtml/rendering/render_image.cpp 2003/09/02 17:44:18
@@ -439,9 +439,10 @@ void RenderImage::detach(RenderArena *ar
RenderReplaced::detach(arena);
}
-bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside)
+bool RenderImage::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inside)
{
- inside |= RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty, inside);
+ inside |= RenderReplaced::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside);
if (inside && element()) {
int tx = _tx + m_x;
Index: khtml/rendering/render_image.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_image.h,v
retrieving revision 1.11
diff -u -p -r1.11 khtml/rendering/render_image.h
--- khtml/rendering/render_image.h 2003/05/05 17:31:40 1.11
+++ khtml/rendering/render_image.h 2003/09/02 17:44:18
@@ -64,7 +64,8 @@ public:
virtual void notifyFinished(CachedObject *finishedObj);
void dispatchLoadEvent();
- virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, bool inside=false);
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
virtual short calcReplacedWidth() const;
virtual int calcReplacedHeight() const;
Index: khtml/rendering/render_inline.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_inline.cpp,v
retrieving revision 1.16
diff -u -p -r1.16 khtml/rendering/render_inline.cpp
--- khtml/rendering/render_inline.cpp 2003/08/11 23:41:02 1.16
+++ khtml/rendering/render_inline.cpp 2003/09/02 17:44:18
@@ -366,15 +366,18 @@ const char *RenderInline::renderName() c
return "RenderInline";
}
-bool RenderInline::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside)
+bool RenderInline::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inside)
{
- // Always check our kids.
- for (RenderObject* child = lastChild(); child; child = child->previousSibling())
- if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty))
- inside = true;
-
+ // Check our kids if our HitTestAction says to.
+ if (hitTestAction != HitTestSelfOnly) {
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling())
+ if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty))
+ inside = true;
+ }
+
// Check our line boxes if we're still not inside.
- if (!inside && style()->visibility() != HIDDEN) {
+ if (hitTestAction != HitTestChildrenOnly && !inside && style()->visibility() != HIDDEN) {
// See if we're inside one of our line boxes.
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
if((_y >=_ty + curr->m_y) && (_y < _ty + curr->m_y + curr->m_height) &&
@@ -401,31 +404,8 @@ bool RenderInline::nodeAtPoint(NodeInfo&
if(!info.innerNonSharedNode())
info.setInnerNonSharedNode(element());
-
- if (!info.URLElement()) {
- RenderObject* p = this;
- while (p) {
- if (p->element() && p->element()->hasAnchor()) {
- info.setURLElement(p->element());
- break;
- }
- if (!isFloatingOrPositioned()) break;
- p = p->parent();
- }
- }
-
}
- if (!info.readonly()) {
- // lets see if we need a new style
- bool oldinside = mouseInside();
- setMouseInside(inside);
-
- setHoverAndActive(info, oldinside, inside);
- if (!isInline() && continuation())
- continuation()->setHoverAndActive(info, oldinside, inside);
- }
-
return inside;
}
Index: khtml/rendering/render_inline.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_inline.h,v
retrieving revision 1.11
diff -u -p -r1.11 khtml/rendering/render_inline.h
--- khtml/rendering/render_inline.h 2003/08/11 23:41:02 1.11
+++ khtml/rendering/render_inline.h 2003/09/02 17:44:19
@@ -58,7 +58,8 @@ public:
virtual void paintObject(QPainter *, int x, int y, int w, int h,
int tx, int ty, PaintAction paintAction);
- virtual bool nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside);
+ virtual bool nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
virtual void calcMinMaxWidth();
Index: khtml/rendering/render_layer.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_layer.cpp,v
retrieving revision 1.49
diff -u -p -r1.49 khtml/rendering/render_layer.cpp
--- khtml/rendering/render_layer.cpp 2003/07/24 01:51:17 1.49
+++ khtml/rendering/render_layer.cpp 2003/09/02 17:44:21
@@ -49,15 +49,18 @@
#include "render_arena.h"
#include "xml/dom_docimpl.h"
+#include <qscrollbar.h>
+#include <qptrvector.h>
+
using namespace DOM;
using namespace khtml;
-QWidget* RenderLayer::gScrollBar = 0;
+#ifdef APPLE_CHANGES
+QScrollBar* RenderLayer::gScrollBar = 0;
+#endif
#ifndef NDEBUG
static bool inRenderLayerDetach;
-static bool inRenderLayerElementDetach;
-static bool inRenderZTreeNodeDetach;
#endif
void
@@ -73,17 +76,20 @@ m_previous( 0 ),
m_next( 0 ),
m_first( 0 ),
m_last( 0 ),
-m_height( 0 ),
-m_y( 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_scrollMediator( 0 ),
+m_posZOrderList( 0 ),
+m_negZOrderList( 0 ),
+m_zOrderListsDirty( true )
{
}
@@ -95,6 +101,8 @@ RenderLayer::~RenderLayer()
delete m_hBar;
delete m_vBar;
delete m_scrollMediator;
+ delete m_posZOrderList;
+ delete m_negZOrderList;
}
void RenderLayer::updateLayerPosition()
@@ -135,94 +143,44 @@ void RenderLayer::updateLayerPosition()
setWidth(m_object->overflowWidth());
if (m_object->overflowHeight() > m_object->height())
setHeight(m_object->overflowHeight());
- }
+ }
}
-RenderLayer*
-RenderLayer::enclosingPositionedAncestor()
+RenderLayer *RenderLayer::stackingContext() 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());
-
+ curr->m_object->style()->hasAutoZIndex();
+ curr = curr->parent());
return curr;
}
RenderLayer*
-RenderLayer::transparentAncestor()
+RenderLayer::enclosingPositionedAncestor() const
{
RenderLayer* curr = parent();
- for ( ; curr && curr->m_object->style()->opacity() == 1.0f; curr = 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;
}
-
-static RenderLayer* commonTransparentAncestor(RenderLayer* layer1, RenderLayer* layer2)
-{
- if (!layer1 || !layer2)
- return 0;
-
- for (RenderLayer* currLayer1 = layer1; currLayer1; currLayer1 = currLayer1->transparentAncestor())
- for (RenderLayer* currLayer2 = layer2; currLayer2; currLayer2 = currLayer2->transparentAncestor())
- if (currLayer1 == currLayer2)
- return currLayer1;
-
- return 0;
-}
-
-void RenderLayer::updateTransparentState(QPainter* painter, RenderLayer* newLayer, RenderLayer*& currLayer)
-{
- RenderLayer* transparentLayer = (!newLayer || newLayer->isTransparent()) ? newLayer :
- newLayer->transparentAncestor();
- if (transparentLayer == currLayer)
- return;
-
- RenderLayer* commonAncestor = commonTransparentAncestor(currLayer, transparentLayer);
- endTransparencyLayers(painter, currLayer, commonAncestor);
- beginTransparencyLayers(painter, transparentLayer, commonAncestor);
-
- // Update our current layer.
- currLayer = transparentLayer;
-}
-void RenderLayer::beginTransparencyLayers(QPainter* painter, RenderLayer* newLayer, RenderLayer* ancestorLayer)
+RenderLayer*
+RenderLayer::transparentAncestor()
{
- if (!newLayer || newLayer == ancestorLayer)
- return;
-
- // We need to open from the outside in, so begin ancestor layers first.
- beginTransparencyLayers(painter, newLayer->transparentAncestor(), ancestorLayer);
-
- // Konqueror should add its own code for setting up the opacity layer here.
- // Safari uses a custom extension to QPainter to communicate with CoreGraphics.
-#ifdef APPLE_CHANGES
- // Begin the layer
- painter->beginTransparencyLayer(newLayer->renderer()->style()->opacity());
-#endif
+ RenderLayer* curr = parent();
+ for ( ; curr && curr->m_object->style()->opacity() == 1.0f; curr = curr->parent());
+ return curr;
}
-
-void RenderLayer::endTransparencyLayers(QPainter* painter, RenderLayer* newLayer, RenderLayer* ancestorLayer)
-{
- if (!newLayer || newLayer == ancestorLayer)
- return;
-
- // We need to close from the inside out.
-
- // Konqueror should add its own code for popping opacity layers here.
- // Safari uses a custom extension to QPainter to communicate with CoreGraphics.
-#ifdef APPLE_CHANGES
- // End the layer
- painter->endTransparencyLayer();
#endif
-
- endTransparencyLayers(painter, newLayer->transparentAncestor(), ancestorLayer);
-}
void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw()
{
@@ -269,6 +227,9 @@ void RenderLayer::addChild(RenderLayer *
setLastChild(child);
child->setParent(this);
+
+ // Dirty the z-order list in which we are contained.
+ child->stackingContext()->dirtyZOrderLists();
}
RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
@@ -284,6 +245,13 @@ RenderLayer* RenderLayer::removeChild(Re
if (m_last == oldChild)
m_last = oldChild->previousSibling();
+ // 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)
+ oldChild->stackingContext()->dirtyZOrderLists();
+
oldChild->setPreviousSibling(0);
oldChild->setNextSibling(0);
oldChild->setParent(0);
@@ -330,7 +298,7 @@ void RenderLayer::insertOnlyThisLayer()
}
void
-RenderLayer::convertToLayerCoords(RenderLayer* ancestorLayer, int& x, int& y)
+RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const
{
if (ancestorLayer == this)
return;
@@ -533,8 +501,8 @@ RenderLayer::checkScrollbarsAfterLayout(
if (bottomPos - m_object->borderTop() > m_scrollHeight)
m_scrollHeight = bottomPos - m_object->borderTop();
- bool needHorizontalBar = rightPos > m_width;
- bool needVerticalBar = bottomPos > m_height;
+ bool needHorizontalBar = rightPos > width();
+ bool needVerticalBar = bottomPos > height();
bool haveHorizontalBar = m_hBar;
bool haveVerticalBar = m_vBar;
@@ -583,425 +551,405 @@ RenderLayer::checkScrollbarsAfterLayout(
}
}
+#if APPLE_CHANGES
void
-RenderLayer::paintScrollbars(QPainter* p, int x, int y, int w, int h)
+RenderLayer::paintScrollbars(QPainter* p, const QRect& damageRect)
{
-#if APPLE_CHANGES
if (m_hBar)
- m_hBar->paint(p, QRect(x, y, w, h));
+ m_hBar->paint(p, damageRect);
if (m_vBar)
- m_vBar->paint(p, QRect(x, y, w, h));
-#endif
+ m_vBar->paint(p, damageRect);
}
+#endif
void
-RenderLayer::paint(QPainter *p, int x, int y, int w, int h, bool selectionOnly)
+RenderLayer::paint(QPainter *p, const QRect& damageRect, bool selectionOnly)
{
- // Create the z-tree of layers that should be displayed.
- QRect damageRect(x,y,w,h);
- RenderZTreeNode* node = constructZTree(damageRect, damageRect, this);
- if (!node)
+ paintLayer(this, p, damageRect, selectionOnly);
+}
+
+static void setClip(QPainter* p, const QRect& paintDirtyRect, const QRect& clipRect)
+{
+ if (paintDirtyRect == clipRect)
return;
- // Flatten the tree into a back-to-front list for painting.
- QPtrVector<RenderLayerElement> layerList;
- constructLayerList(node, &layerList);
-
- // Walk the list and paint each layer, adding in the appropriate offset.
- QRect paintRect(x, y, w, h);
- QRect currRect(paintRect);
- RenderLayer* currentTransparentLayer = 0;
-
- uint count = layerList.count();
- for (uint i = 0; i < count; i++) {
- RenderLayerElement* elt = layerList.at(i);
-
- // Elements add in their own positions as a translation factor. This forces
- // us to subtract that out, so that when it's added back in, we get the right
- // bounds. This is really disgusting (that paint only sets up the right paint
- // position after you call into it). -dwh
- //printf("Painting layer at %d %d\n", elt->absBounds.x(), elt->absBounds.y());
-
- bool updatedTransparentState = false;
- if (elt->clipOriginator) {
- // We originated a clip (we're either positioned or an element with
- // overflow: hidden). We need to paint our background and border, subject
- // to clip regions established by our parent layers.
- if (elt->backgroundClipRect != currRect) {
- if (currRect != paintRect)
- p->restore(); // Pop the clip.
-
- // This is called to update our transparency state.
- updateTransparentState(p, elt->layer, currentTransparentLayer);
- updatedTransparentState = true;
-
- currRect = elt->backgroundClipRect;
-
- // Now apply the clip rect.
- QRect clippedRect = p->xForm(currRect);
+ p->save();
+
#if APPLE_CHANGES
- p->save();
- p->addClip(clippedRect);
+ p->addClip(clipRect);
#else
- QRegion creg(cr);
- QRegion old = p->clipRegion();
- if (!old.isNull())
- creg = old.intersect(creg);
-
- p->save();
- p->setClipRegion(creg);
+ QRect clippedRect = p->xForm(clipRect);
+ QRegion creg(clippedRect);
+ QRegion old = p->clipRegion();
+ if (!old.isNull())
+ creg = old.intersect(creg);
+ p->setClipRegion(creg);
#endif
- }
-
- // A clip is in effect. The clip is never allowed to clip our render object's
- // background, borders or scrollbars. Go ahead and draw those now without our clip (that will
- // be used for our children) in effect.
- elt->layer->renderer()->paintBoxDecorations(p, x, y, w, h,
- elt->absBounds.x(),
- elt->absBounds.y());
+
+}
- // Position our scrollbars prior to painting.
- elt->layer->positionScrollbars(elt->absBounds);
+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 z-order lists are up-to-date.
+ updateZOrderLists();
+
#if APPLE_CHANGES
- // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
- // z-index.
- elt->layer->paintScrollbars(p, x, y, w, h);
+ // Set our transparency if we need to.
+ if (isTransparent())
+ p->beginTransparencyLayer(renderer()->style()->opacity());
#endif
- }
-
- if (elt->clipRect != currRect) {
- if (currRect != paintRect)
- p->restore(); // Pop the clip.
-
- if (!updatedTransparentState) {
- // This is called to update our transparency state.
- updateTransparentState(p, elt->layer, currentTransparentLayer);
- updatedTransparentState = true;
- }
-
- currRect = elt->clipRect;
- if (currRect != paintRect) {
-
- // Now apply the clip rect.
- QRect clippedRect = p->xForm(currRect);
+
+ // 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.
+ renderer()->paint(p, damageRect.x(), damageRect.y(),
+ damageRect.width(), damageRect.height(),
+ x - renderer()->xPos(), y - renderer()->yPos(),
+ PaintActionElementBackground);
+
+ // Position our scrollbars.
+ positionScrollbars(layerBounds);
+
#if APPLE_CHANGES
- p->save();
- p->addClip(clippedRect);
-#else
- QRegion creg(cr);
- QRegion old = p->clipRegion();
- if (!old.isNull())
- creg = old.intersect(creg);
-
- p->save();
- p->setClipRegion(creg);
+ // 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(p, damageRect);
#endif
- }
+ // Restore the clip.
+ restoreClip(p, paintDirtyRect, damageRect);
}
+ }
- if (!updatedTransparentState) {
- // This is called to update our transparency state.
- updateTransparentState(p, elt->layer, currentTransparentLayer);
- updatedTransparentState = true;
+ // 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);
}
-
- if (currRect.isEmpty())
- continue;
-
- if (selectionOnly) {
- if (elt->layerElementType == RenderLayerElement::Normal ||
- elt->layerElementType == RenderLayerElement::Foreground)
- elt->layer->renderer()->paint(p, x, y, w, h,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos(),
- PaintActionSelection);
- } else {
- if (elt->layerElementType == RenderLayerElement::Normal ||
- elt->layerElementType == RenderLayerElement::Background)
- elt->layer->renderer()->paint(p, x, y, w, h,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos(),
- PaintActionElementBackground);
-
- if (elt->layerElementType == RenderLayerElement::Normal ||
- elt->layerElementType == RenderLayerElement::Foreground) {
- elt->layer->renderer()->paint(p, x, y, w, h,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos(),
- PaintActionChildBackgrounds);
- elt->layer->renderer()->paint(p, x, y, w, h,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos(),
- PaintActionFloat);
- elt->layer->renderer()->paint(p, x, y, w, h,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos(),
- PaintActionForeground);
- }
- }
}
-
- if (currRect != paintRect)
- p->restore(); // Pop the clip.
-
- updateTransparentState(p, 0, currentTransparentLayer); // End any open transparency layers.
- node->detach(renderer()->renderArena());
-}
+ // 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);
+
+ if (selectionOnly)
+ renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
+ clipRectToApply.width(), clipRectToApply.height(),
+ x - renderer()->xPos(), y - renderer()->yPos(), PaintActionSelection);
+ else {
+ renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
+ clipRectToApply.width(), clipRectToApply.height(),
+ x - renderer()->xPos(), y - renderer()->yPos(), PaintActionChildBackgrounds);
+ renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
+ clipRectToApply.width(), clipRectToApply.height(),
+ x - renderer()->xPos(), y - renderer()->yPos(), PaintActionFloat);
+ renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
+ clipRectToApply.width(), clipRectToApply.height(),
+ x - renderer()->xPos(), y - renderer()->yPos(), PaintActionForeground);
+ }
-void
-RenderLayer::clearOtherLayersHoverActiveState()
-{
- if (!m_parent)
- return;
-
- for (RenderLayer* curr = m_parent->firstChild(); curr; curr = curr->nextSibling()) {
- if (curr == this)
- continue;
- curr->clearHoverAndActiveState(curr->renderer());
+ // Now restore our clip.
+ restoreClip(p, paintDirtyRect, clipRectToApply);
}
- m_parent->clearOtherLayersHoverActiveState();
-}
-
-void
-RenderLayer::clearHoverAndActiveState(RenderObject* obj)
-{
- if (!obj->mouseInside())
- return;
-
- obj->setMouseInside(false);
- if (obj->element()) {
- obj->element()->setActive(false);
- if (obj->style()->affectedByHoverRules() || obj->style()->affectedByActiveRules())
- obj->element()->setChanged(true);
+ // 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);
+ }
}
- for (RenderObject* child = obj->firstChild(); child; child = child->nextSibling())
- if (child->mouseInside())
- clearHoverAndActiveState(child);
+#if APPLE_CHANGES
+ // End our transparency layer
+ if (isTransparent())
+ p->endTransparencyLayer();
+#endif
}
bool
RenderLayer::nodeAtPoint(RenderObject::NodeInfo& info, int x, int y)
{
- // Clear out our global scrollbar tracking variable.
- gScrollBar = 0;
+#if APPLE_CHANGES
+ // Clear our our scrollbar variable
+ RenderLayer::gScrollBar = 0;
+#endif
- bool inside = false;
+ QRect damageRect(m_x, m_y, 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.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 z-order lists are up-to-date.
+ updateZOrderLists();
+
+ // 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;
- QRect damageRect(m_x, m_y, m_width, m_height);
- RenderZTreeNode* node = constructZTree(damageRect, damageRect, this, true, x, y);
- if (!node)
- return false;
-
- // Flatten the tree into a back-to-front list for painting.
- QPtrVector<RenderLayerElement> layerList;
- constructLayerList(node, &layerList);
-
- // Walk the list and test each layer, adding in the appropriate offset.
- uint count = layerList.count();
- for (int i = count-1; i >= 0; i--) {
- RenderLayerElement* elt = layerList.at(i);
-
- // Elements add in their own positions as a translation factor. This forces
- // us to subtract that out, so that when it's added back in, we get the right
- // bounds. This is really disgusting (that paint only sets up the right paint
- // position after you call into it). -dwh
- //printf("Painting layer at %d %d\n", elt->absBounds.x(), elt->absBounds.y());
-
- inside = elt->layer->renderer()->nodeAtPoint(info, x, y,
- elt->absBounds.x() - elt->layer->renderer()->xPos(),
- elt->absBounds.y() - elt->layer->renderer()->yPos());
- if (inside) {
- // For foreground layer elements where the layer has been split into two, we
- // are only considered to be inside the foreground layer if we hit content other
- // than ourselves.
- if (elt->layerElementType == RenderLayerElement::Foreground &&
- info.innerNode() == elt->layer->renderer()->element()) {
- inside = false;
- info.setInnerNode(0);
- info.setInnerNonSharedNode(0);
- info.setURLElement(0);
- continue;
- }
- // Otherwise the mouse is inside this layer, and we can stop looking.
- insideLayer = elt->layer;
- break;
+
+ // 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;
}
}
- node->detach(renderer()->renderArena());
- if (insideLayer) {
- // Clear out the other layers' hover/active state
- insideLayer->clearOtherLayersHoverActiveState();
-
- // Now clear out our descendant layers
- for (RenderLayer* child = insideLayer->firstChild();
- child; child = child->nextSibling())
- child->clearHoverAndActiveState(child->renderer());
+ // 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(),
+ HitTestChildrenOnly))
+ 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;
+ }
}
-
- return inside;
+
+ // 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(),
+ HitTestSelfOnly))
+ return this;
+
+ // No luck.
+ return 0;
}
-RenderLayer::RenderZTreeNode*
-RenderLayer::constructZTree(QRect overflowClipRect, QRect posClipRect,
- RenderLayer* rootLayer,
- bool eventProcessing, int xMousePos, int yMousePos)
+void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
+ QRect& posClipRect, QRect& fixedClipRect)
{
- // The arena we use for allocating our temporary ztree elements.
- RenderArena* renderArena = renderer()->renderArena();
-
- // This variable stores the result we will hand back.
- RenderZTreeNode* returnNode = 0;
-
- // FIXME: A child render object or layer could override visibility. Don't remove this
- // optimization though until nodeAtPoint is patched as well.
- //
- // If a layer isn't visible, then none of its child layers are visible either.
- // Don't build this branch of the z-tree, since these layers should not be painted.
- if (renderer()->style()->visibility() != VISIBLE)
- return 0;
-
- // Compute this layer's absolute position, so that we can compare it with our
- // damage rect and avoid repainting the layer if it falls outside that rect.
- // An exception to this rule is the root layer, which always paints (hence the
- // m_parent null check below).
- updateLayerPosition(); // For relpositioned layers or non-positioned layers,
- // we need to keep in sync, since we may have shifted relative
- // to our parent layer.
-
- int x = 0;
- int y = 0;
- convertToLayerCoords(rootLayer, x, y);
- QRect layerBounds(x, y, width(), height());
-
- returnNode = new (renderArena) RenderZTreeNode(this);
-
- // Positioned elements are clipped according to the posClipRect. All other
- // layers are clipped according to the overflowClipRect.
- QRect clipRectToApply = m_object->isPositioned() ? posClipRect : overflowClipRect;
- QRect damageRect = clipRectToApply.intersect(layerBounds);
-
- // Clip applies to *us* as well, so go ahead and update the damageRect.
- if (m_object->hasClip())
- damageRect = damageRect.intersect(m_object->getClipRect(x,y));
+ if (parent())
+ parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
- // If we establish a clip rect, then we want to intersect that rect
- // with the damage rect to form a new damage rect.
- bool clipOriginator = 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.
+
+ // 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.
+ if (m_object->style()->position() == FIXED) {
+ posClipRect = fixedClipRect;
+ overflowClipRect = fixedClipRect;
+ }
+ else if (m_object->style()->position() == RELATIVE)
+ posClipRect = overflowClipRect;
- // Update the clip rects that will be passed to children layers.
+ // 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.
- clipOriginator = true;
+ 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);
- clipRectToApply = clipRectToApply.intersect(newOverflowClip);
if (m_object->isPositioned() || m_object->isRelPositioned())
posClipRect = newOverflowClip.intersect(posClipRect);
}
if (m_object->hasClip()) {
QRect newPosClip = m_object->getClipRect(x,y);
- posClipRect = newPosClip.intersect(posClipRect);
- overflowClipRect = overflowClipRect.intersect(posClipRect);
- clipRectToApply = clipRectToApply.intersect(newPosClip);
+ 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);
+
+ updateLayerPosition();
- // Walk our list of child layers looking only for those layers that have a
- // non-negative z-index (a z-index >= 0).
- RenderZTreeNode* lastChildNode = 0;
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
- if (child->zIndex() < 0)
- continue; // Ignore negative z-indices in this first pass.
-
- RenderZTreeNode* childNode = child->constructZTree(overflowClipRect, posClipRect,
- rootLayer, eventProcessing,
- xMousePos, yMousePos);
- if (childNode) {
- // Put the new node into the tree at the front of the parent's list.
- if (lastChildNode)
- lastChildNode->next = childNode;
- else
- returnNode->child = childNode;
- lastChildNode = childNode;
- }
- }
+ int x = 0;
+ int y = 0;
+ convertToLayerCoords(rootLayer, x, y);
+ layerBounds = QRect(x,y,width(),height());
- // Now add a leaf node for ourselves, but only if we intersect the damage
- // rect. This intersection test is valid only for replaced elements or
- // block elements, since inline non-replaced elements have a width of 0 (and
- // thus the layer does too). We also exclude the root from this test, since
- // the HTML can be much taller than the root (because of scrolling).
- if (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
- renderer()->hasOverhangingFloats() ||
- (renderer()->isInline() && !renderer()->isReplaced()) ||
- (eventProcessing && damageRect.contains(xMousePos,yMousePos)) ||
- (!eventProcessing && layerBounds.intersects(damageRect))) {
- RenderLayerElement* layerElt = new (renderArena) RenderLayerElement(this, layerBounds,
- damageRect, clipRectToApply,
- clipOriginator, x, y);
- if (returnNode->child) {
- RenderZTreeNode* leaf = new (renderArena) RenderZTreeNode(layerElt);
- leaf->next = returnNode->child;
- returnNode->child = leaf;
-
- // We are an interior node and have other child layers. Our layer
- // will need to be sorted with the other layers as though it has
- // a z-index of 0.
- if (!layerElt->zauto)
- layerElt->zindex = 0;
- }
- else
- returnNode->layerElement = layerElt;
- }
+ backgroundRect = m_object->style()->position() == FIXED ? fixedClipRect :
+ (m_object->isPositioned() ? posClipRect : overflowClipRect);
+ foregroundRect = backgroundRect;
- // Now look for children that have a negative z-index.
- for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
- if (child->zIndex() >= 0)
- continue; // Ignore non-negative z-indices in this second pass.
-
- RenderZTreeNode* childNode = child->constructZTree(overflowClipRect, posClipRect,
- rootLayer, eventProcessing,
- xMousePos, yMousePos);
- if (childNode) {
- // Deal with the case where all our children views had negative z-indices.
- // Demote our leaf node and make a new interior node that can hold these
- // children.
- if (returnNode->layerElement) {
- RenderZTreeNode* leaf = returnNode;
- returnNode = new (renderArena) RenderZTreeNode(this);
- returnNode->child = leaf;
- }
-
- // Put the new node into the tree at the front of the parent's list.
- childNode->next = returnNode->child;
- returnNode->child = childNode;
+ // 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);
}
-
- return returnNode;
}
-void
-RenderLayer::constructLayerList(RenderZTreeNode* ztree, QPtrVector<RenderLayerElement>* result)
+bool RenderLayer::intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const
+{
+ return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
+ renderer()->hasOverhangingFloats() ||
+ (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)
{
- // This merge buffer is just a temporary used during computation as we do merge sorting.
- QPtrVector<RenderLayerElement> mergeBuffer;
- ztree->constructLayerList(&mergeBuffer, result);
+ 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;
+
+ // Check to see if the hovered node has changed. If not, then we don't need to
+ // do anything. An exception is if we just went from :hover into :hover:active,
+ // in which case we need to update to get the new :active state.
+ DOM::DocumentImpl* doc = renderer()->document();
+ DOM::NodeImpl* oldHoverNode = doc ? doc->hoverNode() : 0;
+ DOM::NodeImpl* newHoverNode = info.innerNode();
+
+ if (oldHoverNode == newHoverNode && (!oldHoverNode || oldHoverNode->active() == info.active()))
+ return;
+
+ // Update our current hover node.
+ info.innerNode()->getDocument()->setHoverNode(newHoverNode);
+
+ // We have two different objects. Fetch their renderers.
+ RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
+ RenderObject* newHoverObj = info.innerNode() ? info.innerNode()->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->isText()) {
+ bool oldActive = curr->element()->active();
+ curr->element()->setActive(false);
+ if (curr->style()->affectedByHoverRules() ||
+ (curr->style()->affectedByActiveRules() && oldActive))
+ curr->element()->setChanged();
+ }
+ }
+
+ // Now set the hover state for our new object up to the root.
+ 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();
+ }
+ }
}
// 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::RenderLayerElement>* buffer,
- QPtrVector<RenderLayer::RenderLayerElement>* mergeBuffer,
- uint start,
- uint end)
+static void sortByZOrder(QPtrVector<RenderLayer::RenderLayer>* buffer,
+ QPtrVector<RenderLayer::RenderLayer>* mergeBuffer,
+ uint start, uint end)
{
if (start >= end)
return; // Sanity check.
@@ -1011,9 +959,9 @@ static void sortByZOrder(QPtrVector<Rend
for (uint i = end-1; i > start; i--) {
bool sorted = true;
for (uint j = start; j < i; j++) {
- RenderLayer::RenderLayerElement* elt = buffer->at(j);
- RenderLayer::RenderLayerElement* elt2 = buffer->at(j+1);
- if (elt->zindex > elt2->zindex) {
+ 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);
@@ -1029,12 +977,12 @@ static void sortByZOrder(QPtrVector<Rend
sortByZOrder(buffer, mergeBuffer, start, mid);
sortByZOrder(buffer, mergeBuffer, mid, end);
- RenderLayer::RenderLayerElement* elt = buffer->at(mid-1);
- RenderLayer::RenderLayerElement* elt2 = buffer->at(mid);
+ 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)
+ if (elt->zIndex() <= elt2->zIndex())
return;
// We have to merge sort. Ensure our merge buffer is big enough to hold
@@ -1047,7 +995,7 @@ static void sortByZOrder(QPtrVector<Rend
elt2 = buffer->at(i2);
while (i1 < mid || i2 < end) {
- if (i1 < mid && (i2 == end || elt->zindex <= elt2->zindex)) {
+ if (i1 < mid && (i2 == end || elt->zIndex() <= elt2->zIndex())) {
mergeBuffer->insert(mergeBuffer->count(), elt);
i1++;
if (i1 < mid)
@@ -1067,161 +1015,63 @@ static void sortByZOrder(QPtrVector<Rend
mergeBuffer->clear();
}
}
-
-void RenderLayer::RenderZTreeNode::constructLayerList(QPtrVector<RenderLayerElement>* mergeTmpBuffer,
- QPtrVector<RenderLayerElement>* buffer)
-{
- // The root always establishes a stacking context. We could add a rule for this
- // to the UA sheet, but this code guarantees that nobody can do anything wacky
- // in CSS to prevent the root from establishing a stacking context.
- bool autoZIndex = layer->parent() ? layer->hasAutoZIndex() : false;
- int explicitZIndex = layer->zIndex();
-
- if (layerElement) {
- // We are a leaf node of the ztree, and so we just place our layer element into
- // the buffer.
- if (buffer->count() == buffer->size())
- // Resize by a power of 2.
- buffer->resize(2*(buffer->size()+1));
-
- buffer->insert(buffer->count(), layerElement);
- return;
- }
-
- uint startIndex = buffer->count();
- for (RenderZTreeNode* current = child; current; current = current->next)
- current->constructLayerList(mergeTmpBuffer, buffer);
- uint endIndex = buffer->count();
-
- if (autoZIndex || !(endIndex-startIndex))
- return; // We just had to collect the kids. We don't apply a sort to them, since
- // they will actually be layered in some ancestor layer's stacking context.
-
- sortByZOrder(buffer, mergeTmpBuffer, startIndex, endIndex);
-
- // Find out if we have any elements with negative z-indices in this stacking context.
- // If so, then we need to split our layer in two (a background layer and a foreground
- // layer). We then put the background layer before the negative z-index objects, and
- // leave the foreground layer in the position previously occupied by the unsplit original.
- RenderLayerElement* elt = buffer->at(startIndex);
- if (elt->zindex < 0) {
- // Locate our layer in the layer list.
- for (uint i = startIndex; i < endIndex; i++) {
- elt = buffer->at(i);
- if (elt->layer == layer) {
- // Clone the layer element.
- RenderLayerElement* bgLayer =
- new (layer->renderer()->renderArena()) RenderLayerElement(*elt);
-
- // Set the layer types (foreground and background) on the two layer elements.
- elt->layerElementType = RenderLayerElement::Foreground;
- bgLayer->layerElementType = RenderLayerElement::Background;
-
- // Ensure our buffer is big enough to hold a new layer element.
- if (buffer->count() == buffer->size())
- // Resize by a power of 2.
- buffer->resize(2*(buffer->size()+1));
-
- // Insert the background layer element at the front of our sorted list.
- for (uint j = buffer->count(); j > startIndex; j--)
- buffer->insert(j, buffer->at(j-1));
- buffer->insert(startIndex, bgLayer);
-
- // Augment endIndex since we added a layer element.
- endIndex++;
- break;
- }
- }
- }
-
- // Now set all of the elements' z-indices to match the parent's explicit z-index, so that
- // they will be layered properly in the ancestor layer's stacking context.
- for (uint i = startIndex; i < endIndex; i++) {
- elt = buffer->at(i);
- elt->zindex = explicitZIndex;
- }
-}
-void* RenderLayer::RenderLayerElement::operator new(size_t sz, RenderArena* renderArena) throw()
+void RenderLayer::dirtyZOrderLists()
{
- void* result = renderArena->allocate(sz);
- if (result)
- memset(result, 0, sz);
- return result;
+ if (m_posZOrderList)
+ m_posZOrderList->clear();
+ if (m_negZOrderList)
+ m_negZOrderList->clear();
+ m_zOrderListsDirty = true;
}
-
-void RenderLayer::RenderLayerElement::operator delete(void* ptr, size_t sz)
-{
- assert(inRenderLayerElementDetach);
- // Stash size where detach can find it.
- *(size_t *)ptr = sz;
-}
-
-void RenderLayer::RenderLayerElement::detach(RenderArena* renderArena)
+void RenderLayer::updateZOrderLists()
{
-#ifndef NDEBUG
- inRenderLayerElementDetach = true;
-#endif
- delete this;
-#ifndef NDEBUG
- inRenderLayerElementDetach = false;
-#endif
-
- // Recover the size left there for us by operator delete and free the memory.
- renderArena->free(*(size_t *)this, this);
-}
-
-void* RenderLayer::RenderZTreeNode::operator new(size_t sz, RenderArena* renderArena) throw()
-{
- void* result = renderArena->allocate(sz);
- if (result)
- memset(result, 0, sz);
- return result;
-}
-
-void RenderLayer::RenderZTreeNode::operator delete(void* ptr, size_t sz)
-{
- assert(inRenderZTreeNodeDetach);
+ if (!isStackingContext() || !m_zOrderListsDirty)
+ return;
- // Stash size where detach can find it.
- *(size_t *)ptr = sz;
-}
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
+ child->collectLayers(m_posZOrderList, m_negZOrderList);
-void RenderLayer::RenderZTreeNode::detach(RenderArena* renderArena)
-{
- assert(!next);
-
- RenderZTreeNode *n;
- for (RenderZTreeNode *c = child; c; c = n) {
- n = c->next;
- c->next = 0;
- c->detach(renderArena);
+ // Sort the two lists.
+ if (m_posZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_posZOrderList, &mergeBuffer, 0, m_posZOrderList->count());
}
- if (layerElement)
- layerElement->detach(renderArena);
+ if (m_negZOrderList) {
+ QPtrVector<RenderLayer> mergeBuffer;
+ sortByZOrder(m_negZOrderList, &mergeBuffer, 0, m_negZOrderList->count());
+ }
-#ifndef NDEBUG
- inRenderZTreeNodeDetach = true;
-#endif
- delete this;
-#ifndef NDEBUG
- inRenderZTreeNodeDetach = false;
-#endif
-
- // Recover the size left there for us by operator delete and free the memory.
- renderArena->free(*(size_t *)this, this);
+ m_zOrderListsDirty = false;
}
-QPtrVector<RenderLayer::RenderLayerElement> RenderLayer::elementList(RenderZTreeNode *&node)
+void RenderLayer::collectLayers(QPtrVector<RenderLayer>*& posBuffer, QPtrVector<RenderLayer>*& negBuffer)
{
- QPtrVector<RenderLayerElement> list;
+ // 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;
- QRect damageRect(m_x, m_y, m_width, m_height);
- node = constructZTree(damageRect, damageRect, this);
- if (node) {
- constructLayerList(node, &list);
+ // 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);
}
-
- return list;
}
Index: khtml/rendering/render_layer.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_layer.h,v
retrieving revision 1.27
diff -u -p -r1.27 khtml/rendering/render_layer.h
--- khtml/rendering/render_layer.h 2003/07/11 17:55:42 1.27
+++ khtml/rendering/render_layer.h 2003/09/02 17:44:21
@@ -49,9 +49,10 @@
#include <assert.h>
#include "render_object.h"
-#include <qvector.h>
-#include <qscrollbar.h>
+class QScrollBar;
+template <class T> class QPtrVector;
+
namespace khtml {
class RenderStyle;
class RenderTable;
@@ -60,7 +61,8 @@ namespace khtml {
class RenderText;
class RenderFrameSet;
class RenderObject;
-
+ class RenderScrollMediator;
+
class RenderScrollMediator: public QObject
{
public:
@@ -76,6 +78,10 @@ private:
class RenderLayer
{
public:
+#ifdef APPLE_CHANGES
+ static QScrollBar* gScrollBar;
+#endif
+
RenderLayer(RenderObject* object);
~RenderLayer();
@@ -90,14 +96,13 @@ public:
void addChild(RenderLayer *newChild, RenderLayer* beforeChild = 0);
RenderLayer* removeChild(RenderLayer *oldChild);
- RenderLayer* transparentAncestor();
- bool isTransparent();
- void updateTransparentState(QPainter* painter, RenderLayer* newLayer, RenderLayer*& currLayer);
- void beginTransparencyLayers(QPainter* painter, RenderLayer* newLayer, RenderLayer* ancestorLayer);
- void endTransparencyLayers(QPainter* painter, RenderLayer* newLayer, RenderLayer* ancestorLayer);
-
void removeOnlyThisLayer();
void insertOnlyThisLayer();
+
+#if APPLE_CHANGES
+ bool isTransparent();
+ RenderLayer* transparentAncestor();
+#endif
RenderLayer* root() {
RenderLayer* curr = this;
@@ -109,15 +114,13 @@ public:
int yPos() const { return m_y; }
short width() const { return m_width; }
int height() const { return m_height; }
+
+ void setWidth(short w) { m_width = w; }
+ void setHeight(int h) { m_height = h; }
+
short scrollWidth() const { return m_scrollWidth; }
int scrollHeight() const { return m_scrollHeight; }
- void setWidth( int width ) {
- m_width = width;
- }
- void setHeight( int height ) {
- m_height = height;
- }
void setPos( int xPos, int yPos ) {
m_x = xPos;
m_y = yPos;
@@ -133,40 +136,62 @@ public:
void scrollToYOffset(int y) { scrollToOffset(m_scrollX, y); }
void setHasHorizontalScrollbar(bool hasScrollbar);
void setHasVerticalScrollbar(bool hasScrollbar);
- QWidget* horizontalScrollbar() { return m_hBar; }
- QWidget* verticalScrollbar() { return m_vBar; }
+ QScrollBar* horizontalScrollbar() { return m_hBar; }
+ QScrollBar* verticalScrollbar() { return m_vBar; }
int verticalScrollbarWidth();
int horizontalScrollbarHeight();
void moveScrollbarsAside();
void positionScrollbars(const QRect& absBounds);
- void paintScrollbars(QPainter* p, int x, int y, int w, int h);
+#ifdef APPLE_CHANGES
+ void paintScrollbars(QPainter* p, const QRect& damageRect);
+#endif
void checkScrollbarsAfterLayout();
void slotValueChanged(int);
void updateScrollPositionFromScrollbars();
-
+
void updateLayerPosition();
+
+ // Get the enclosing stacking context for this layer. A stacking context is a layer
+ // that has a non-auto z-index.
+ RenderLayer* stackingContext() const;
+ bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isCanvas(); }
+
+ void dirtyZOrderLists();
+ void updateZOrderLists();
+ QPtrVector<RenderLayer>* posZOrderList() const { return m_posZOrderList; }
+ QPtrVector<RenderLayer>* negZOrderList() const { return m_negZOrderList; }
// Gets the nearest enclosing positioned ancestor layer (also includes
// the <html> layer and the root layer).
- RenderLayer* enclosingPositionedAncestor();
+ RenderLayer* enclosingPositionedAncestor() const;
- void convertToLayerCoords(RenderLayer* ancestorLayer, int& x, int& y);
+ void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const;
- bool hasAutoZIndex() { return renderer()->style()->hasAutoZIndex(); }
- int zIndex() { return renderer()->style()->zIndex(); }
+ bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); }
+ int zIndex() const { return renderer()->style()->zIndex(); }
// The two main functions that use the layer system. The paint method
// paints the layers that intersect the damage rect from back to
// front. The nodeAtPoint method looks for mouse events by walking
// layers that intersect the point from front to back.
- void paint(QPainter *p, int x, int y, int w, int h, bool selectionOnly=false);
+ void paint(QPainter *p, const QRect& damageRect, bool selectionOnly=false);
bool nodeAtPoint(RenderObject::NodeInfo& info, int x, int y);
+
+ // This method figures out our layerBounds in coordinates relative to
+ // |rootLayer}. It also computes our background and foreground clip rects
+ // for painting/event handling.
+ void calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
+ QRect& backgroundRect, QRect& foregroundRect);
+ void calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
+ QRect& posClipRect, QRect& fixedClipRect);
+
+ bool intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const;
+ bool containsPoint(int x, int y, const QRect& damageRect) const;
- void clearOtherLayersHoverActiveState();
- void clearHoverAndActiveState(RenderObject* obj);
+ void updateHoverActiveState(RenderObject::NodeInfo& info);
void detach(RenderArena* renderArena);
-
+
// Overloaded new operator. Derived classes must override operator new
// in order to allocate out of the RenderArena.
void* operator new(size_t sz, RenderArena* renderArena) throw();
@@ -178,179 +203,6 @@ private:
// The normal operator new is disallowed on all render objects.
void* operator new(size_t sz) throw();
-public:
- // Z-Index Implementation Notes
- //
- // In order to properly handle mouse events as well as painting,
- // we must compute a correct list of layers that should be painted
- // from back to front (and for mouse events walked from front to
- // back).
- //
- // Positioned elements in the render tree (e.g., relative positioned
- // divs and absolute positioned divs) have a corresponding layer
- // that holds them and all children that reside in the same layer.
- //
- // When painting is performed on a layer, all render objects in that
- // layer are painted. If the render object has descendants in another
- // layer, those will be dealt with separately.
- //
- // A RenderLayerElement represents a single entry in our list of
- // layers that should be painted. We perform computations as we
- // build up this list so that we have the correct translation factor
- // for painting. We also use a temporary z-index variable for storage
- // (more on this below).
- //
- struct RenderLayerElement {
- enum LayerElementType { Normal, Background, Foreground };
-
- RenderLayer* layer;
- QRect absBounds; // Our bounds in absolute coordinates relative to the root.
- QRect backgroundClipRect; // Clip rect used for our background/borders.
- QRect clipRect; // Clip rect used for our children.
- int zindex; // Temporary z-index used for processing and sorting.
- bool zauto : 1; // Whether or not we are using auto z-indexing.
- bool clipOriginator : 1; // Whether or not we established a clip.
- int x; // The coords relative to the layer that will be using this list
- // to paint.
- int y;
- LayerElementType layerElementType; // For negative z-indices, we have to split a single layer into two
- // RenderLayerElements, one that sits beneath the negative content, and
- // another that sits above (denoted with values of Background and Foreground,
- // respectively). A normal layer that fully paints is denoted with the value Normal.
-
- RenderLayerElement(RenderLayer* l, const QRect& rect, const QRect& bgclip,
- const QRect& clip, bool clipOrig, int xpos, int ypos,
- LayerElementType lType = Normal)
- :layer(l), absBounds(rect), backgroundClipRect(bgclip), clipRect(clip),
- zindex(l->zIndex()), zauto(l->hasAutoZIndex()), clipOriginator(clipOrig),
- x(xpos), y(ypos), layerElementType(lType) {}
-
- void detach(RenderArena* renderArena);
-
- // Overloaded new operator. Derived classes must override operator new
- // in order to allocate out of the RenderArena.
- void* operator new(size_t sz, RenderArena* renderArena) throw();
-
- // Overridden to prevent the normal delete from being called.
- void operator delete(void* ptr, size_t sz);
-
- // The normal operator new is disallowed.
- void* operator new(size_t sz) throw();
- };
-
- // The list of layer elements is built through a recursive examination
- // of a tree of z nodes. This tree structure mimics the layer
- // hierarchy itself, but only leaf nodes represent items that will
- // end up in the layer list for painting.
- //
- // Every leaf layer in the layer hierarchy will have a corresponding
- // leaf node in the z-tree. Layers with children have an
- // interior z-tree node that contains the tree nodes for the child
- // layers as well as a leaf node that represents the containing layer.
- //
- // Sibling z-tree nodes match the same order as the layers in the
- // layer hierarchy, which will have been arranged in document order
- // when the render tree was constructed (since the render tree
- // constructed the layers). An exception is if a negative z-index
- // is specified on a child (see below).
-
- struct RenderZTreeNode {
- RenderLayer* layer;
- RenderZTreeNode* next;
-
- // Only one of these will ever be defined.
- RenderZTreeNode* child; // Defined for interior nodes.
- RenderLayerElement* layerElement; // Defined for leaf nodes.
-
- RenderZTreeNode(RenderLayer* l)
- :layer(l), next(0), child(0), layerElement(0) {}
-
- RenderZTreeNode(RenderLayerElement* layerElt)
- :layer(layerElt->layer), next(0), child(0), layerElement(layerElt) {}
-
- ~RenderZTreeNode() {}
-
- void constructLayerList(QPtrVector<RenderLayerElement>* mergeTmpBuffer,
- QPtrVector<RenderLayerElement>* finalBuffer);
-
- void detach(RenderArena* renderArena);
-
- // Overloaded new operator. Derived classes must override operator new
- // in order to allocate out of the RenderArena.
- void* operator new(size_t sz, RenderArena* renderArena) throw();
-
- // Overridden to prevent the normal delete from being called.
- void operator delete(void* ptr, size_t sz);
-
- // The normal operator new is disallowed.
- void* operator new(size_t sz) throw();
- };
-
- static QWidget* gScrollBar;
-
- // For debugging.
- QPtrVector<RenderLayerElement> elementList(RenderZTreeNode *&node);
-
-private:
- // The constructZTree function creates a z-tree for a given layer hierarchy
- // rooted on this layer. It will ensure that immediate child
- // elements of a given z-tree node are at least initially sorted
- // into <negative z-index children>, <this layer>, <non-negative z-index
- // children>.
- //
- // Here is a concrete example (lifted from Gecko's view system,
- // which is analogous to our layer system and works the same way):
- // z-index values as specified by CSS are shown in parentheses.
- //
- // L0(auto) --> L1(0) --> L2(auto) --> L3(0)
- // | | +------> L4(2)
- // | +-----------> L5(1)
- // +--------------------> L6(1)
- //
- // The corresponding z-tree for this layer hierarchy will be
- // the following, where |I| represents an interior node, and |L|
- // represents a leaf RenderLayerElement.
- //
- // I(L0) --> L(L0)
- // +-------> I(L1) --------> L(L1)
- // | | +-------> I(L2) ------> L(L2)
- // | | +---------> L(L3)
- // | +-----------> L(L4)
- // +-------> L(L5)
- // +-------> L(L6)
- //
- RenderZTreeNode* constructZTree(QRect overflowClipRect,
- QRect clipRect,
- RenderLayer* rootLayer,
- bool eventProcessing = false, int x=0, int y=0);
-
- // Once the z-tree has been constructed, we call constructLayerList
- // to produce a flattened layer list for rendering/event handling.
- // This function recursively computes a layer list for each z-tree
- // node by computing lists for each child node. It then concatenates
- // them and sorts them by z-index.
- //
- // Z-indices are updated during this computation. After a list is
- // computed for one z-tree node, the elements of the layer list are
- // all changed so that their z-indices match the specified z-index
- // of the tree node's layer (unless that layer doesn't establish
- // a z-index, e.g., it just has z-index: auto).
- //
- // Continuing the above example, the computation of the list for
- // L0 would be as follows:
- //
- // I(L2) has a list [ L(L2)(0), L(L3)(0), L(L4)(2) ]
- // I(L2) is auto so the z-indices of the child layer elements remain
- // unaltered.
- // I(L1) has a list [ L(L1)(0), L(L2)(0), L(L3)(0), L(L4)(2), L(L5)(1) ]
- // The nodes are sorted and then reassigned a z-index of 0, so this
- // list becomes:
- // [ L(L1)(0), L(L2)(0), L(L3)(0), L(L5)(0), L(L4)(0) ]
- // Finally we end up with the list for L0, which sorted becomes:
- // [ L(L0)(0), L(L1)(0), L(L2)(0), L(L3)(0), L(L5)(0), L(L4)(0), L(L6)(1) ]
- void constructLayerList(RenderZTreeNode* ztree,
- QPtrVector<RenderLayerElement>* result);
-
private:
void setNextSibling(RenderLayer* next) { m_next = next; }
void setPreviousSibling(RenderLayer* prev) { m_previous = prev; }
@@ -358,34 +210,50 @@ private:
void setFirstChild(RenderLayer* first) { m_first = first; }
void setLastChild(RenderLayer* last) { m_last = last; }
+ void collectLayers(QPtrVector<RenderLayer>*&, QPtrVector<RenderLayer>*&);
+
+ void paintLayer(RenderLayer* rootLayer, QPainter *p, const QRect& paintDirtyRect, bool selectionOnly=false);
+ RenderLayer* nodeAtPointForLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
+ int x, int y, const QRect& hitTestRect);
+
protected:
RenderObject* m_object;
- RenderLayer *m_parent;
- RenderLayer *m_previous;
- RenderLayer *m_next;
+ RenderLayer* m_parent;
+ RenderLayer* m_previous;
+ RenderLayer* m_next;
- RenderLayer *m_first;
- RenderLayer *m_last;
+ RenderLayer* m_first;
+ RenderLayer* m_last;
// Our (x,y) coordinates are in our parent layer's coordinate space.
- int m_height;
- int m_y;
short m_x;
- short m_width;
+ int m_y;
+ // The layer's width/height
+ short m_width;
+ int m_height;
+
// Our scroll offsets if the view is scrolled.
short m_scrollX;
int m_scrollY;
-
+
// The width/height of our scrolled area.
short m_scrollWidth;
- short m_scrollHeight;
+ int m_scrollHeight;
// For layers with overflow, we have a pair of scrollbars.
QScrollBar* m_hBar;
QScrollBar* m_vBar;
RenderScrollMediator* m_scrollMediator;
+
+ // For layers that establish stacking contexts, m_posZOrderList holds a sorted list of all the
+ // descendant layers within the stacking context that have z-indices of 0 or greater
+ // (auto will count as 0). m_negZOrderList holds descendants within our stacking context with negative
+ // z-indices.
+ QPtrVector<RenderLayer>* m_posZOrderList;
+ QPtrVector<RenderLayer>* m_negZOrderList;
+ bool m_zOrderListsDirty;
};
}; // namespace
Index: khtml/rendering/render_object.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.cpp,v
retrieving revision 1.97
diff -u -p -r1.97 khtml/rendering/render_object.cpp
--- khtml/rendering/render_object.cpp 2003/08/20 22:03:24 1.97
+++ khtml/rendering/render_object.cpp 2003/09/02 17:44:22
@@ -983,6 +983,19 @@ void RenderObject::setStyle(RenderStyle
if (m_style == style)
return;
+ // If our z-index changes value or our visibility changes,
+ // we need to dirty our stacking context's z-order list.
+ if (m_style && style) {
+ if ((m_style->hasAutoZIndex() != style->hasAutoZIndex() ||
+ m_style->zIndex() != style->zIndex() ||
+ m_style->visibility() != style->visibility()) && layer()) {
+ layer()->stackingContext()->dirtyZOrderLists();
+ if (m_style->hasAutoZIndex() != style->hasAutoZIndex() ||
+ m_style->visibility() != style->visibility())
+ layer()->dirtyZOrderLists();
+ }
+ }
+
RenderStyle::Diff d = m_style ? m_style->diff( style ) : RenderStyle::Layout;
if (m_style && m_parent && d == RenderStyle::Visible && !isText())
@@ -1220,7 +1233,7 @@ void RenderObject::removeFromObjectLists
}
}
-RenderArena* RenderObject::renderArena() const
+DOM::DocumentImpl* RenderObject::document() const
{
DOM::NodeImpl* elt = element();
RenderObject* current = parent();
@@ -1228,11 +1241,15 @@ RenderArena* RenderObject::renderArena()
elt = current->element();
current = current->parent();
}
- if (!elt)
- return 0;
- return elt->getDocument()->renderArena();
+ return elt ? elt->getDocument() : 0;
}
+RenderArena* RenderObject::renderArena() const
+{
+ DOM::DocumentImpl* doc = document();
+ return doc ? doc->renderArena() : 0;
+}
+
void RenderObject::detach(RenderArena* renderArena)
{
@@ -1336,21 +1353,9 @@ bool RenderObject::mouseInside() const
return m_mouseInside;
}
-void RenderObject::setHoverAndActive(NodeInfo& info, bool oldinside, bool inside)
+bool RenderObject::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inside)
{
- DOM::NodeImpl* elt = element();
- if (elt) {
- bool oldactive = elt->active();
- if (oldactive != (inside && info.active()))
- elt->setActive(inside && info.active());
- if ((oldinside != mouseInside() && style()->affectedByHoverRules()) ||
- (oldactive != elt->active() && style()->affectedByActiveRules()))
- elt->setChanged();
- }
-}
-
-bool RenderObject::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside)
-{
int tx = _tx + xPos();
int ty = _ty + yPos();
@@ -1363,11 +1368,14 @@ bool RenderObject::nodeAtPoint(NodeInfo&
}
// ### table should have its own, more performant method
- if ((!isRenderBlock() ||
+ if (hitTestAction != HitTestSelfOnly &&
+ ((!isRenderBlock() ||
!static_cast<RenderBlock*>(this)->isPointInScrollbar(_x, _y, _tx, _ty)) &&
(overhangingContents() || inOverflowRect || isInline() || isCanvas() ||
isTableRow() || isTableSection() || inside || mouseInside() ||
- (childrenInline() && firstChild() && firstChild()->isCompact()))) {
+ (childrenInline() && firstChild() && firstChild()->isCompact())))) {
+ if (hitTestAction == HitTestChildrenOnly)
+ inside = false;
int stx = _tx + xPos();
int sty = _ty + yPos();
if (style()->hidesOverflow() && layer())
@@ -1379,6 +1387,15 @@ bool RenderObject::nodeAtPoint(NodeInfo&
}
if (inside) {
+ if (!info.innerNode() && !isInline() && continuation()) {
+ // We are in the margins of block elements that are part of a continuation. In
+ // this case we're actually still inside the enclosing inline element that was
+ // split. Go ahead and set our inner node accordingly.
+ info.setInnerNode(continuation()->element());
+ if (!info.innerNonSharedNode())
+ info.setInnerNonSharedNode(continuation()->element());
+ }
+
if (info.innerNode() && info.innerNode()->renderer() &&
!info.innerNode()->renderer()->isInline() && element() && isInline()) {
// Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
@@ -1394,28 +1411,6 @@ bool RenderObject::nodeAtPoint(NodeInfo&
if(!info.innerNonSharedNode() && element())
info.setInnerNonSharedNode(element());
-
- if (!info.URLElement()) {
- RenderObject* p = (!isInline() && continuation()) ? continuation() : this;
- while (p) {
- if (p->element() && p->element()->hasAnchor()) {
- info.setURLElement(p->element());
- break;
- }
- if (!isFloatingOrPositioned()) break;
- p = p->parent();
- }
- }
- }
-
- if (!info.readonly()) {
- // lets see if we need a new style
- bool oldinside = mouseInside();
- setMouseInside(inside);
-
- setHoverAndActive(info, oldinside, inside);
- if (!isInline() && continuation())
- continuation()->setHoverAndActive(info, oldinside, inside);
}
return inside;
Index: khtml/rendering/render_object.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.h,v
retrieving revision 1.79
diff -u -p -r1.79 khtml/rendering/render_object.h
--- khtml/rendering/render_object.h 2003/08/11 23:41:02 1.79
+++ khtml/rendering/render_object.h 2003/09/02 17:44:23
@@ -70,10 +70,17 @@ typedef enum {
PaintActionSelection
} PaintAction;
+typedef enum {
+ HitTestAll = 0,
+ HitTestSelfOnly = 1,
+ HitTestChildrenOnly = 2
+} HitTestAction;
+
namespace DOM {
class HTMLAreaElementImpl;
class DOMString;
class NodeImpl;
+ class DocumentImpl;
class ElementImpl;
class EventImpl;
};
@@ -241,7 +248,8 @@ public:
// don't even think about making this method virtual!
DOM::NodeImpl* element() const { return m_node; }
-
+ DOM::DocumentImpl* document() const;
+
/**
* returns the object containing this one. can be different from parent for
* positioned elements
@@ -405,8 +413,8 @@ public:
FindSelectionResult checkSelectionPoint(int x, int y, int tx, int ty, DOM::NodeImpl*&, int& offset);
virtual FindSelectionResult checkSelectionPointIgnoringContinuations(int x, int y, int tx, int ty, DOM::NodeImpl*&, int& offset);
- virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, bool inside=false);
- void setHoverAndActive(NodeInfo& info, bool oldinside, bool inside);
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
// set the style of the object.
virtual void setStyle(RenderStyle *style);
Index: khtml/rendering/render_style.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_style.cpp,v
retrieving revision 1.27
diff -u -p -r1.27 khtml/rendering/render_style.cpp
--- khtml/rendering/render_style.cpp 2003/07/15 22:32:42 1.27
+++ khtml/rendering/render_style.cpp 2003/09/02 17:44:24
@@ -420,7 +420,9 @@ RenderStyle::Diff RenderStyle::diff( con
// DataRef<StyleInheritedData> inherited;
if ( *box.get() != *other->box.get() ||
- *surround.get() != *other->surround.get() ||
+ !(surround->margin == other->surround->margin) ||
+ !(surround->border == other->surround->border) || // FIXME: Width -> layout, but other styles -> repaint
+ !(surround->padding == other->surround->padding) ||
*css3NonInheritedData->flexibleBox.get() != *other->css3NonInheritedData->flexibleBox.get() ||
!(inherited->indent == other->inherited->indent) ||
!(inherited->line_height == other->inherited->line_height) ||
@@ -490,6 +492,15 @@ RenderStyle::Diff RenderStyle::diff( con
if ( !(noninherited_flags._effectiveDisplay == INLINE) &&
!(noninherited_flags._vertical_align == other->noninherited_flags._vertical_align))
return Layout;
+
+ // Make sure these left/top/right/bottom checks stay below all layout checks and above
+ // all visible checks.
+ if (other->position() != STATIC && !(surround->offset == other->surround->offset)) {
+ if (other->position() == RELATIVE)
+ return Visible;
+ else
+ return Layout;
+ }
// Visible:
// EVisibility _visibility : 2;
Index: khtml/rendering/render_text.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_text.cpp,v
retrieving revision 1.78
diff -u -p -r1.78 khtml/rendering/render_text.cpp
--- khtml/rendering/render_text.cpp 2003/08/27 22:57:14 1.78
+++ khtml/rendering/render_text.cpp 2003/09/02 17:44:25
@@ -377,7 +377,8 @@ TextRun * RenderText::findTextRun( int o
return s;
}
-bool RenderText::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, bool inside)
+bool RenderText::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
+ HitTestAction hitTestAction, bool inside)
{
assert(parent());
@@ -393,8 +394,6 @@ bool RenderText::nodeAtPoint(NodeInfo& i
s = si < (int) m_lines.count()-1 ? m_lines[++si] : 0;
}
- setMouseInside(inside);
-
if (inside && element()) {
if (info.innerNode() && info.innerNode()->renderer() &&
!info.innerNode()->renderer()->isInline()) {
@@ -409,7 +408,7 @@ bool RenderText::nodeAtPoint(NodeInfo& i
if (!info.innerNode())
info.setInnerNode(element());
- if(!info.innerNonSharedNode())
+ if (!info.innerNonSharedNode())
info.setInnerNonSharedNode(element());
}
Index: khtml/rendering/render_text.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_text.h,v
retrieving revision 1.34
diff -u -p -r1.34 khtml/rendering/render_text.h
--- khtml/rendering/render_text.h 2003/07/25 20:22:34 1.34
+++ khtml/rendering/render_text.h 2003/09/02 17:44:26
@@ -141,7 +141,8 @@ public:
virtual void layout() {assert(false);}
- virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, bool inside = false);
+ virtual bool nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty,
+ HitTestAction hitTestAction = HitTestAll, bool inside=false);
// Return before, after (offset set to max), or inside the text, at @p offset
virtual FindSelectionResult checkSelectionPointIgnoringContinuations
Index: khtml/xml/dom_docimpl.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/xml/dom_docimpl.cpp,v
retrieving revision 1.75
diff -u -p -r1.75 khtml/xml/dom_docimpl.cpp
--- khtml/xml/dom_docimpl.cpp 2003/07/30 00:39:43 1.75
+++ khtml/xml/dom_docimpl.cpp 2003/09/02 17:44:30
@@ -284,6 +284,7 @@ DocumentImpl::DocumentImpl(DOMImplementa
m_namespaceURIs[0] = new DOMStringImpl(xhtml.unicode(), xhtml.length());
m_namespaceURIs[0]->ref();
m_focusNode = 0;
+ m_hoverNode = 0;
m_defaultView = new AbstractViewImpl(this);
m_defaultView->ref();
m_listenerTypes = 0;
@@ -336,9 +337,12 @@ DocumentImpl::~DocumentImpl()
delete [] m_namespaceURIs;
m_defaultView->deref();
m_styleSheets->deref();
+
if (m_focusNode)
m_focusNode->deref();
-
+ if (m_hoverNode)
+ m_hoverNode->deref();
+
if (m_renderArena){
delete m_renderArena;
m_renderArena = 0;
@@ -2014,6 +2018,17 @@ void DocumentImpl::recalcStyleSelector()
!inCompatMode() );
m_styleSelectorDirty = false;
+}
+
+void DocumentImpl::setHoverNode(NodeImpl* newHoverNode)
+{
+ if (m_hoverNode != newHoverNode) {
+ if (m_hoverNode)
+ m_hoverNode->deref();
+ m_hoverNode = newHoverNode;
+ if (m_hoverNode)
+ m_hoverNode->ref();
+ }
}
void DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
Index: khtml/xml/dom_docimpl.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/xml/dom_docimpl.h,v
retrieving revision 1.38
diff -u -p -r1.38 khtml/xml/dom_docimpl.h
--- khtml/xml/dom_docimpl.h 2003/07/30 00:39:43 1.38
+++ khtml/xml/dom_docimpl.h 2003/09/02 17:44:30
@@ -344,9 +344,13 @@ public:
void setSelectedStylesheetSet(const DOMString& aString);
QStringList availableStyleSheets() const;
+
NodeImpl *focusNode() const { return m_focusNode; }
void setFocusNode(NodeImpl *newFocusNode);
+ NodeImpl *hoverNode() const { return m_hoverNode; }
+ void setHoverNode(NodeImpl *newHoverNode);
+
// Updates for :target (CSS3 selector).
void setCSSTarget(NodeImpl* n);
NodeImpl* getCSSTarget();
@@ -478,8 +482,10 @@ protected:
HTMLMode hMode;
QColor m_textColor;
- NodeImpl *m_focusNode;
+ NodeImpl *m_focusNode;
+ NodeImpl *m_hoverNode;
+
// ### replace me with something more efficient
// in lookup and insertion.
DOMStringImpl **m_elementNames;
Index: kwq/KWQKHTMLPart.mm
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/kwq/KWQKHTMLPart.mm,v
retrieving revision 1.391
diff -u -p -r1.391 kwq/KWQKHTMLPart.mm
--- kwq/KWQKHTMLPart.mm 2003/08/22 22:02:56 1.391
+++ kwq/KWQKHTMLPart.mm 2003/09/02 17:44:33
@@ -56,6 +56,8 @@
#import <JavaScriptCore/property_map.h>
+#import <qscrollbar.h>
+
#undef _KWQ_TIMING
using DOM::DocumentImpl;
@@ -776,7 +778,7 @@ void KWQKHTMLPart::paint(QPainter *p, co
#endif
if (renderer()) {
- renderer()->layer()->paint(p, rect.x(), rect.y(), rect.width(), rect.height(), false);
+ renderer()->layer()->paint(p, rect);
} else {
ERROR("called KWQKHTMLPart::paint with nil renderer");
}
@@ -785,7 +787,7 @@ void KWQKHTMLPart::paint(QPainter *p, co
void KWQKHTMLPart::paintSelectionOnly(QPainter *p, const QRect &rect)
{
if (renderer()) {
- renderer()->layer()->paint(p, rect.x(), rect.y(), rect.width(), rect.height(), true);
+ renderer()->layer()->paint(p, rect, true);
} else {
ERROR("called KWQKHTMLPart::paintSelectionOnly with nil renderer");
}
@@ -800,8 +802,9 @@ void KWQKHTMLPart::adjustPageHeight(floa
painter.setPaintingDisabled(true);
root->setTruncatedAt((int)floor(oldBottom));
- root->layer()->paint(&painter, 0, (int)floor(oldTop),
- root->docWidth(), (int)ceil(oldBottom-oldTop), false);
+ QRect dirtyRect(0, (int)floor(oldTop),
+ root->docWidth(), (int)ceil(oldBottom-oldTop));
+ root->layer()->paint(&painter, dirtyRect);
*newBottom = root->bestTruncatedAt();
if (*newBottom == 0) {
*newBottom = oldBottom;
Index: kwq/KWQRect.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/kwq/KWQRect.h,v
retrieving revision 1.27
diff -u -p -r1.27 kwq/KWQRect.h
--- kwq/KWQRect.h 2003/01/22 00:12:35 1.27
+++ kwq/KWQRect.h 2003/09/02 17:44:33
@@ -61,7 +61,7 @@ public:
bool intersects(const QRect &) const;
QRect unite(const QRect &) const;
- bool contains(int x, int y, bool proper = false) {
+ bool contains(int x, int y, bool proper = false) const {
if (proper)
return x > xp && (x < (xp + w - 1)) && y > yp && y < (yp + h - 1);
return x >= xp && x < (xp + w) && y >= yp && y < (yp + h);
Index: kwq/KWQRenderTreeDebug.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/kwq/KWQRenderTreeDebug.cpp,v
retrieving revision 1.10
diff -u -p -r1.10 kwq/KWQRenderTreeDebug.cpp
--- kwq/KWQRenderTreeDebug.cpp 2003/05/06 22:26:07 1.10
+++ kwq/KWQRenderTreeDebug.cpp 2003/09/02 17:44:33
@@ -42,11 +42,9 @@ using khtml::RenderText;
using khtml::TextRun;
using khtml::TextRunArray;
-typedef khtml::RenderLayer::RenderLayerElement RenderLayerElement;
-typedef khtml::RenderLayer::RenderZTreeNode RenderZTreeNode;
+static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
+ const QRect& paintDirtyRect, int indent=0);
-static void writeLayers(QTextStream &ts, const RenderObject &o, int indent = 0);
-
static QTextStream &operator<<(QTextStream &ts, const QRect &r)
{
return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
@@ -149,16 +147,18 @@ static void write(QTextStream &ts, const
if (view) {
RenderObject *root = KWQ(view->part())->renderer();
if (root) {
- writeLayers(ts, *root, indent + 1);
+ RenderLayer* l = root->layer();
+ if (l)
+ writeLayers(ts, l, l, QRect(l->xPos(), l->yPos(), l->width(), l->height()), indent+1);
}
}
}
}
-static void write(QTextStream &ts, const RenderLayerElement &e, int indent = 0)
+static void write(QTextStream &ts, const RenderLayer &l,
+ const QRect& layerBounds, const QRect& backgroundClipRect, const QRect& clipRect,
+ int layerType = 0, int indent = 0)
{
- RenderLayer &l = *e.layer;
-
writeIndent(ts, indent);
ts << "layer";
@@ -166,34 +166,52 @@ static void write(QTextStream &ts, const
QRect r(l.xPos(), l.yPos(), l.width(), l.height());
ts << " " << r;
-
- if (r != r.intersect(e.backgroundClipRect)) {
- ts << " backgroundClip " << e.backgroundClipRect;
+
+ if (layerBounds != layerBounds.intersect(backgroundClipRect)) {
+ ts << " backgroundClip " << backgroundClipRect;
}
- if (r != r.intersect(e.clipRect)) {
- ts << " clip " << e.clipRect;
+ if (layerBounds != layerBounds.intersect(clipRect)) {
+ ts << " clip " << clipRect;
}
- if (e.layerElementType == RenderLayerElement::Background)
+ if (layerType == -1)
ts << " layerType: background only";
- else if (e.layerElementType == RenderLayerElement::Foreground)
+ else if (layerType == 1)
ts << " layerType: foreground only";
ts << "\n";
- if (e.layerElementType != RenderLayerElement::Background)
+ if (layerType != -1)
write(ts, *l.renderer(), indent + 1);
}
-
-static void writeLayers(QTextStream &ts, const RenderObject &o, int indent)
+
+static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
+ const QRect& paintDirtyRect, int indent)
{
- RenderZTreeNode *node;
- QPtrVector<RenderLayerElement> list = o.layer()->elementList(node);
- for (unsigned i = 0; i != list.count(); ++i) {
- write(ts, *list[i], indent);
+ // Calculate the clip rects we should use.
+ QRect layerBounds, damageRect, clipRectToApply;
+ l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
+
+ // Ensure our z-order lists are up-to-date.
+ l->updateZOrderLists();
+
+ bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
+ QPtrVector<RenderLayer>* negList = l->negZOrderList();
+ 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 (node) {
- node->detach(o.renderArena());
+
+ if (shouldPaint)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, negList && negList->count() > 0, indent);
+
+ QPtrVector<RenderLayer>* posList = l->posZOrderList();
+ if (posList) {
+ for (unsigned i = 0; i != posList->count(); ++i)
+ writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
}
}
@@ -203,7 +221,9 @@ QString externalRepresentation(const Ren
{
QTextStream ts(&s);
if (o) {
- writeLayers(ts, *o);
+ RenderLayer* l = o->layer();
+ if (l)
+ writeLayers(ts, l, l, QRect(l->xPos(), l->yPos(), l->width(), l->height()));
}
}
return s;
-------------- next part --------------
-------------- next part --------------
/*
* Copyright (C) 2003 Apple Computer, Inc.
*
* Portions are Copyright (C) 1998 Netscape Communications Corporation.
*
* Other contributors:
* Robert O'Callahan <roc+ at cs.cmu.edu>
* David Baron <dbaron at fas.harvard.edu>
* Christian Biesinger <cbiesinger at web.de>
* Randall Jesup <rjesup at wgate.com>
* Roland Mainz <roland.mainz at informatik.med.uni-giessen.de>
* Josh Soref <timeless at mac.com>
* Boris Zbarsky <bzbarsky at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
*/
#include "render_layer.h"
#include <kdebug.h>
#include <assert.h>
#include "khtmlview.h"
#include "render_block.h"
#include "render_arena.h"
#include "xml/dom_docimpl.h"
#include <qscrollbar.h>
#include <qptrvector.h>
using namespace DOM;
using namespace khtml;
#ifdef APPLE_CHANGES
QScrollBar* RenderLayer::gScrollBar = 0;
#endif
#ifndef NDEBUG
static bool inRenderLayerDetach;
#endif
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_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_zOrderListsDirty( true )
{
}
RenderLayer::~RenderLayer()
{
// Child layers will be deleted by their corresponding render objects, so
// our destructor doesn't have to do anything.
m_parent = m_previous = m_next = m_first = m_last = 0;
delete m_hBar;
delete m_vBar;
delete m_scrollMediator;
delete m_posZOrderList;
delete m_negZOrderList;
}
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();
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 (m_object->isRelPositioned())
static_cast<RenderBox*>(m_object)->relativePositionOffset(x, y);
// Subtract our parent's scroll offset.
if (parent())
parent()->subtractScrollOffset(x, y);
setPos(x,y);
setWidth(m_object->width());
setHeight(m_object->height());
if (!m_object->style()->hidesOverflow()) {
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;
}
#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);
// Dirty the z-order list in which we are contained.
child->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();
// 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)
oldChild->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::scrollToOffset(int x, int y, bool updateScrollbars)
{
if (x < 0) x = 0;
if (y < 0) y = 0;
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;
// FIXME: Fire the onscroll DOM event.
// Just schedule a full repaint of our object.
m_object->repaint(true);
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, scrollView);
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) {
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, scrollView);
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) {
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
void
RenderLayer::checkScrollbarsAfterLayout()
{
updateLayerPosition();
int rightPos = m_object->rightmostPosition();
int bottomPos = m_object->lowestPosition();
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;
bool haveVerticalBar = m_vBar;
// overflow:scroll should just enable/disable.
if (m_object->style()->overflow() == OSCROLL) {
m_hBar->setEnabled(needHorizontalBar);
m_vBar->setEnabled(needVerticalBar);
}
// overflow:auto may need to lay out again if scrollbars got added/removed.
bool scrollbarsChanged = (m_object->style()->overflow() == OAUTO) &&
(haveHorizontalBar != needHorizontalBar || haveVerticalBar != needVerticalBar);
if (scrollbarsChanged) {
setHasHorizontalScrollbar(needHorizontalBar);
setHasVerticalScrollbar(needVerticalBar);
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, m_scrollWidth-clientWidth);
#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, m_scrollHeight-clientHeight);
#endif
}
}
#if APPLE_CHANGES
void
RenderLayer::paintScrollbars(QPainter* p, const QRect& damageRect)
{
if (m_hBar)
m_hBar->paint(p, damageRect);
if (m_vBar)
m_vBar->paint(p, damageRect);
}
#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();
#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 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 z-order lists are up-to-date.
updateZOrderLists();
#if 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.
renderer()->paint(p, damageRect.x(), damageRect.y(),
damageRect.width(), damageRect.height(),
x - renderer()->xPos(), y - renderer()->yPos(),
PaintActionElementBackground);
// Position our scrollbars.
positionScrollbars(layerBounds);
#if APPLE_CHANGES
// 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(p, damageRect);
#endif
// 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);
if (selectionOnly)
renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
clipRectToApply.width(), clipRectToApply.height(),
x - renderer()->xPos(), y - renderer()->yPos(), PaintActionSelection);
else {
renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
clipRectToApply.width(), clipRectToApply.height(),
x - renderer()->xPos(), y - renderer()->yPos(), PaintActionChildBackgrounds);
renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
clipRectToApply.width(), clipRectToApply.height(),
x - renderer()->xPos(), y - renderer()->yPos(), PaintActionFloat);
renderer()->paint(p, clipRectToApply.x(), clipRectToApply.y(),
clipRectToApply.width(), clipRectToApply.height(),
x - renderer()->xPos(), y - renderer()->yPos(), PaintActionForeground);
}
// Now restore our clip.
restoreClip(p, paintDirtyRect, clipRectToApply);
}
// 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);
}
}
#if APPLE_CHANGES
// End our transparency layer
if (isTransparent())
p->endTransparencyLayer();
#endif
}
bool
RenderLayer::nodeAtPoint(RenderObject::NodeInfo& info, int x, int y)
{
#if APPLE_CHANGES
// Clear our our scrollbar variable
RenderLayer::gScrollBar = 0;
#endif
QRect damageRect(m_x, m_y, 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.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 z-order lists are up-to-date.
updateZOrderLists();
// 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;
}
}
// 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(),
HitTestChildrenOnly))
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(),
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);
updateLayerPosition(); // For relpositioned layers or non-positioned layers,
// we need to keep in sync, since we may have shifted relative
// to our parent layer.
// 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.
if (m_object->style()->position() == FIXED) {
posClipRect = fixedClipRect;
overflowClipRect = fixedClipRect;
}
else if (m_object->style()->position() == RELATIVE)
posClipRect = overflowClipRect;
// 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);
updateLayerPosition();
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()->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;
// Check to see if the hovered node has changed. If not, then we don't need to
// do anything. An exception is if we just went from :hover into :hover:active,
// in which case we need to update to get the new :active state.
DOM::DocumentImpl* doc = renderer()->document();
DOM::NodeImpl* oldHoverNode = doc ? doc->hoverNode() : 0;
DOM::NodeImpl* newHoverNode = info.innerNode();
if (oldHoverNode == newHoverNode && (!oldHoverNode || oldHoverNode->active() == info.active()))
return;
// Update our current hover node.
info.innerNode()->getDocument()->setHoverNode(newHoverNode);
// We have two different objects. Fetch their renderers.
RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0;
RenderObject* newHoverObj = info.innerNode() ? info.innerNode()->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->isText()) {
bool oldActive = curr->element()->active();
curr->element()->setActive(false);
if (curr->style()->affectedByHoverRules() ||
(curr->style()->affectedByActiveRules() && oldActive))
curr->element()->setChanged();
}
}
// Now set the hover state for our new object up to the root.
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();
}
}
}
// 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::RenderLayer>* buffer,
QPtrVector<RenderLayer::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::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::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;
// 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);
}
}
-------------- next part --------------
/*
* Copyright (C) 2003 Apple Computer, Inc.
*
* Portions are Copyright (C) 1998 Netscape Communications Corporation.
*
* Other contributors:
* Robert O'Callahan <roc+ at cs.cmu.edu>
* David Baron <dbaron at fas.harvard.edu>
* Christian Biesinger <cbiesinger at web.de>
* Randall Jesup <rjesup at wgate.com>
* Roland Mainz <roland.mainz at informatik.med.uni-giessen.de>
* Josh Soref <timeless at mac.com>
* Boris Zbarsky <bzbarsky at 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
*/
#ifndef render_layer_h
#define render_layer_h
#include <qcolor.h>
#include <qrect.h>
#include <assert.h>
#include "render_object.h"
class QScrollBar;
template <class T> class QPtrVector;
namespace khtml {
class RenderStyle;
class RenderTable;
class CachedObject;
class RenderCanvas;
class RenderText;
class RenderFrameSet;
class RenderObject;
class RenderScrollMediator;
class RenderScrollMediator: public QObject
{
public:
RenderScrollMediator(RenderLayer* layer)
:m_layer(layer) {}
void slotValueChanged(int);
private:
RenderLayer* m_layer;
};
class RenderLayer
{
public:
#ifdef APPLE_CHANGES
static QScrollBar* gScrollBar;
#endif
RenderLayer(RenderObject* object);
~RenderLayer();
RenderObject* renderer() const { return m_object; }
RenderLayer *parent() const { return m_parent; }
RenderLayer *previousSibling() const { return m_previous; }
RenderLayer *nextSibling() const { return m_next; }
RenderLayer *firstChild() const { return m_first; }
RenderLayer *lastChild() const { return m_last; }
void addChild(RenderLayer *newChild, RenderLayer* beforeChild = 0);
RenderLayer* removeChild(RenderLayer *oldChild);
void removeOnlyThisLayer();
void insertOnlyThisLayer();
#if APPLE_CHANGES
bool isTransparent();
RenderLayer* transparentAncestor();
#endif
RenderLayer* root() {
RenderLayer* curr = this;
while (curr->parent()) curr = curr->parent();
return curr;
}
int xPos() const { return m_x; }
int yPos() const { return m_y; }
short width() const { return m_width; }
int height() const { return m_height; }
void setWidth(short w) { m_width = w; }
void setHeight(int h) { m_height = h; }
short scrollWidth() const { return m_scrollWidth; }
int scrollHeight() const { return m_scrollHeight; }
void setPos( int xPos, int yPos ) {
m_x = xPos;
m_y = yPos;
}
// Scrolling methods for layers that can scroll their overflow.
void scrollOffset(int& x, int& y);
void subtractScrollOffset(int& x, int& y);
short scrollXOffset() { return m_scrollX; }
int scrollYOffset() { return m_scrollY; }
void scrollToOffset(int x, int y, bool updateScrollbars = true);
void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY); }
void scrollToYOffset(int y) { scrollToOffset(m_scrollX, y); }
void setHasHorizontalScrollbar(bool hasScrollbar);
void setHasVerticalScrollbar(bool hasScrollbar);
QScrollBar* horizontalScrollbar() { return m_hBar; }
QScrollBar* verticalScrollbar() { return m_vBar; }
int verticalScrollbarWidth();
int horizontalScrollbarHeight();
void moveScrollbarsAside();
void positionScrollbars(const QRect& absBounds);
#ifdef APPLE_CHANGES
void paintScrollbars(QPainter* p, const QRect& damageRect);
#endif
void checkScrollbarsAfterLayout();
void slotValueChanged(int);
void updateScrollPositionFromScrollbars();
void updateLayerPosition();
// Get the enclosing stacking context for this layer. A stacking context is a layer
// that has a non-auto z-index.
RenderLayer* stackingContext() const;
bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isCanvas(); }
void dirtyZOrderLists();
void updateZOrderLists();
QPtrVector<RenderLayer>* posZOrderList() const { return m_posZOrderList; }
QPtrVector<RenderLayer>* negZOrderList() const { return m_negZOrderList; }
// Gets the nearest enclosing positioned ancestor layer (also includes
// the <html> layer and the root layer).
RenderLayer* enclosingPositionedAncestor() const;
void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const;
bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); }
int zIndex() const { return renderer()->style()->zIndex(); }
// The two main functions that use the layer system. The paint method
// paints the layers that intersect the damage rect from back to
// front. The nodeAtPoint method looks for mouse events by walking
// layers that intersect the point from front to back.
void paint(QPainter *p, const QRect& damageRect, bool selectionOnly=false);
bool nodeAtPoint(RenderObject::NodeInfo& info, int x, int y);
// This method figures out our layerBounds in coordinates relative to
// |rootLayer}. It also computes our background and foreground clip rects
// for painting/event handling.
void calculateRects(const RenderLayer* rootLayer, const QRect& paintDirtyRect, QRect& layerBounds,
QRect& backgroundRect, QRect& foregroundRect);
void calculateClipRects(const RenderLayer* rootLayer, QRect& overflowClipRect,
QRect& posClipRect, QRect& fixedClipRect);
bool intersectsDamageRect(const QRect& layerBounds, const QRect& damageRect) const;
bool containsPoint(int x, int y, const QRect& damageRect) const;
void updateHoverActiveState(RenderObject::NodeInfo& info);
void detach(RenderArena* renderArena);
// Overloaded new operator. Derived classes must override operator new
// in order to allocate out of the RenderArena.
void* operator new(size_t sz, RenderArena* renderArena) throw();
// Overridden to prevent the normal delete from being called.
void operator delete(void* ptr, size_t sz);
private:
// The normal operator new is disallowed on all render objects.
void* operator new(size_t sz) throw();
private:
void setNextSibling(RenderLayer* next) { m_next = next; }
void setPreviousSibling(RenderLayer* prev) { m_previous = prev; }
void setParent(RenderLayer* parent) { m_parent = parent; }
void setFirstChild(RenderLayer* first) { m_first = first; }
void setLastChild(RenderLayer* last) { m_last = last; }
void collectLayers(QPtrVector<RenderLayer>*&, QPtrVector<RenderLayer>*&);
void paintLayer(RenderLayer* rootLayer, QPainter *p, const QRect& paintDirtyRect, bool selectionOnly=false);
RenderLayer* nodeAtPointForLayer(RenderLayer* rootLayer, RenderObject::NodeInfo& info,
int x, int y, const QRect& hitTestRect);
protected:
RenderObject* m_object;
RenderLayer* m_parent;
RenderLayer* m_previous;
RenderLayer* m_next;
RenderLayer* m_first;
RenderLayer* m_last;
// Our (x,y) coordinates are in our parent layer's coordinate space.
short m_x;
int m_y;
// The layer's width/height
short m_width;
int m_height;
// Our scroll offsets if the view is scrolled.
short m_scrollX;
int m_scrollY;
// The width/height of our scrolled area.
short m_scrollWidth;
int m_scrollHeight;
// For layers with overflow, we have a pair of scrollbars.
QScrollBar* m_hBar;
QScrollBar* m_vBar;
RenderScrollMediator* m_scrollMediator;
// For layers that establish stacking contexts, m_posZOrderList holds a sorted list of all the
// descendant layers within the stacking context that have z-indices of 0 or greater
// (auto will count as 0). m_negZOrderList holds descendants within our stacking context with negative
// z-indices.
QPtrVector<RenderLayer>* m_posZOrderList;
QPtrVector<RenderLayer>* m_negZOrderList;
bool m_zOrderListsDirty;
};
}; // namespace
#endif
-------------- next part --------------
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/001-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/002-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/003-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/004-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/005-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/006-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/007-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/008-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/009-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/010-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/011-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/012-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/013-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/014-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/mailman/private/khtml-devel/attachments/20030902/4c49d262/015-0001.html
-------------- next part --------------
More information about the Khtml-devel
mailing list