diff options
author | Feng Qian <> | 2009-04-10 18:11:29 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-10 18:11:29 -0700 |
commit | 8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch) | |
tree | 181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/rendering/RenderLayer.cpp | |
parent | 7ed56f225e0ade046e1c2178977f72b2d896f196 (diff) | |
download | external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2 |
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/rendering/RenderLayer.cpp')
-rw-r--r-- | WebCore/rendering/RenderLayer.cpp | 1214 |
1 files changed, 880 insertions, 334 deletions
diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index 1a538c6..fd88120 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -44,10 +44,13 @@ #include "config.h" #include "RenderLayer.h" +#include "CString.h" #include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" #include "Document.h" #include "EventHandler.h" #include "EventNames.h" +#include "FloatPoint3D.h" #include "FloatRect.h" #include "FocusController.h" #include "Frame.h" @@ -73,8 +76,16 @@ #include "Scrollbar.h" #include "ScrollbarTheme.h" #include "SelectionController.h" +#include "TransformationMatrix.h" +#include "TransformState.h" #include "TranslateTransformOperation.h" #include <wtf/StdLibExtras.h> +#include <wtf/UnusedParam.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#endif #if ENABLE(SVG) #include "SVGNames.h" @@ -88,12 +99,6 @@ namespace WebCore { using namespace HTMLNames; -const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop }; -const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom }; - const int MinimumWidthWhileResizing = 100; const int MinimumHeightWhileResizing = 40; @@ -116,7 +121,7 @@ void ClipRects::destroy(RenderArena* renderArena) renderArena->free(*(size_t *)this, this); } -RenderLayer::RenderLayer(RenderBox* renderer) +RenderLayer::RenderLayer(RenderBoxModelObject* renderer) : m_renderer(renderer) , m_parent(0) , m_previous(0) @@ -138,15 +143,15 @@ RenderLayer::RenderLayer(RenderBox* renderer) , m_inResizeMode(false) , m_posZOrderList(0) , m_negZOrderList(0) - , m_overflowList(0) + , m_normalFlowList(0) , m_clipRects(0) #ifndef NDEBUG , m_clipRectsRoot(0) #endif , m_scrollDimensionsDirty(true) , m_zOrderListsDirty(true) - , m_overflowListDirty(true) - , m_isOverflowOnly(shouldBeOverflowOnly()) + , m_normalFlowListDirty(true) + , m_isNormalFlowOnly(shouldBeNormalFlowOnly()) , m_usedTransparency(false) , m_paintingInsideReflection(false) , m_inOverflowRelayout(false) @@ -156,10 +161,15 @@ RenderLayer::RenderLayer(RenderBox* renderer) , m_hasVisibleContent(false) , m_visibleDescendantStatusDirty(false) , m_hasVisibleDescendant(false) + , m_3DTransformedDescendantStatusDirty(true) + , m_has3DTransformedDescendant(false) +#if USE(ACCELERATED_COMPOSITING) + , m_hasCompositingDescendant(false) + , m_mustOverlayCompositedLayers(false) +#endif , m_marquee(0) , m_staticX(0) , m_staticY(0) - , m_transform(0) , m_reflection(0) , m_scrollCorner(0) , m_resizer(0) @@ -185,9 +195,13 @@ RenderLayer::~RenderLayer() delete m_posZOrderList; delete m_negZOrderList; - delete m_overflowList; + delete m_normalFlowList; delete m_marquee; +#if USE(ACCELERATED_COMPOSITING) + clearBacking(); +#endif + // Make sure we have no lingering clip rects. ASSERT(!m_clipRects); @@ -204,11 +218,40 @@ RenderLayer::~RenderLayer() m_resizer->destroy(); } +#if USE(ACCELERATED_COMPOSITING) +RenderLayerCompositor* RenderLayer::compositor() const +{ + ASSERT(renderer()->view()); + return renderer()->view()->compositor(); +} + +void RenderLayer::rendererContentChanged() +{ + if (m_backing) + m_backing->rendererContentChanged(); +} +#endif // USE(ACCELERATED_COMPOSITING) + +void RenderLayer::setStaticY(int staticY) +{ + if (m_staticY == staticY) + return; + m_staticY = staticY; + renderer()->setChildNeedsLayout(true, false); +} + void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) { if (doFullRepaint) { renderer()->repaint(); +#if USE(ACCELERATED_COMPOSITING) + checkForRepaint = false; + // We need the full repaint to propagate to child layers if we are hardware compositing. + if (!compositor()->inCompositingMode()) + doFullRepaint = false; +#else checkForRepaint = doFullRepaint = false; +#endif } updateLayerPosition(); // For relpositioned layers or non-positioned layers, @@ -231,16 +274,17 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) // from updateScrollInfoAfterLayout(). ASSERT(!view->layoutStateEnabled()); - IntRect newRect = renderer()->absoluteClippedOverflowRect(); - IntRect newOutlineBox = renderer()->absoluteOutlineBounds(); + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + IntRect newRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + IntRect newOutlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); if (checkForRepaint) { if (view && !view->printing()) { if (m_needsFullRepaint) { - view->repaintViewRectangle(m_repaintRect); + renderer()->repaintUsingContainer(repaintContainer, m_repaintRect); if (newRect != m_repaintRect) - view->repaintViewRectangle(newRect); + renderer()->repaintUsingContainer(repaintContainer, newRect); } else - renderer()->repaintAfterLayoutIfNeeded(m_repaintRect, m_outlineBox); + renderer()->repaintAfterLayoutIfNeeded(repaintContainer, m_repaintRect, m_outlineBox); } } m_repaintRect = newRect; @@ -258,6 +302,14 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(doFullRepaint, checkForRepaint); + +#if USE(ACCELERATED_COMPOSITING) + if (!parent()) + compositor()->updateRootLayerPosition(); + + if (isComposited()) + backing()->updateAfterLayout(); +#endif // With all our children positioned, now update our marquee if we need to. if (m_marquee) @@ -266,7 +318,11 @@ void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) void RenderLayer::updateTransform() { - bool hasTransform = renderer()->hasTransform(); + // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, + // so check style too. + bool hasTransform = renderer()->hasTransform() && renderer()->style()->hasTransform(); + bool had3DTransform = has3DTransform(); + bool hadTransform = m_transform; if (hasTransform != hadTransform) { if (hasTransform) @@ -276,9 +332,33 @@ void RenderLayer::updateTransform() } if (hasTransform) { - m_transform->reset(); - renderer()->style()->applyTransform(*m_transform, renderer()->borderBoxRect().size()); + RenderBox* box = renderBox(); + ASSERT(box); + m_transform->makeIdentity(); + box->style()->applyTransform(*m_transform, box->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(*m_transform); } + + if (had3DTransform != has3DTransform()) + dirty3DTransformedDescendantStatus(); +} + +TransformationMatrix RenderLayer::currentTransform() const +{ + if (!m_transform) + return TransformationMatrix(); + +#if USE(ACCELERATED_COMPOSITING) + if (renderer()->style()->isRunningAcceleratedAnimation()) { + TransformationMatrix currTransform; + RefPtr<RenderStyle> style = renderer()->animation()->getAnimatedStyleForRenderer(renderer()); + style->applyTransform(currTransform, renderBox()->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(currTransform); + return currTransform; + } +#endif + + return *m_transform; } void RenderLayer::setHasVisibleContent(bool b) @@ -288,9 +368,10 @@ void RenderLayer::setHasVisibleContent(bool b) m_visibleContentStatusDirty = false; m_hasVisibleContent = b; if (m_hasVisibleContent) { - m_repaintRect = renderer()->absoluteClippedOverflowRect(); - m_outlineBox = renderer()->absoluteOutlineBounds(); - if (!isOverflowOnly()) + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + m_repaintRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + m_outlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); + if (!isNormalFlowOnly()) dirtyStackingContextZOrderLists(); } if (parent()) @@ -372,38 +453,84 @@ void RenderLayer::updateVisibilityStatus() } } +void RenderLayer::dirty3DTransformedDescendantStatus() +{ + RenderLayer* curr = stackingContext(); + if (curr) + curr->m_3DTransformedDescendantStatusDirty = true; + + // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. + // Note that preserves3D() creates stacking context, so we can just run up the stacking contexts. + while (curr && curr->preserves3D()) { + curr->m_3DTransformedDescendantStatusDirty = true; + curr = curr->stackingContext(); + } +} + +// Return true if this layer or any preserve-3d descendants have 3d. +bool RenderLayer::update3DTransformedDescendantStatus() +{ + if (m_3DTransformedDescendantStatusDirty) { + m_has3DTransformedDescendant = false; + + // Transformed or preserve-3d descendants can only be in the z-order lists, not + // in the normal flow list, so we only need to check those. + if (m_posZOrderList) { + for (unsigned i = 0; i < m_posZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_posZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + + // Now check our negative z-index children. + if (m_negZOrderList) { + for (unsigned i = 0; i < m_negZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_negZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + } + + // If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs + // the m_has3DTransformedDescendant set. + if (preserves3D()) + return has3DTransform() || m_has3DTransformedDescendant; + + return has3DTransform(); +} + void RenderLayer::updateLayerPosition() { // Clear our cached clip rect information. clearClipRects(); - int x = renderer()->x(); - int y = renderer()->y(); + RenderBox* rendererBox = renderBox(); + + int x = rendererBox ? rendererBox->x() : 0; + int y = rendererBox ? rendererBox->y() : 0; if (!renderer()->isPositioned() && renderer()->parent()) { // We must adjust our position by walking up the render tree looking for the // nearest enclosing object with a layer. - RenderBox* curr = renderer()->parentBox(); + RenderObject* curr = renderer()->parent(); while (curr && !curr->hasLayer()) { - if (!curr->isTableRow()) { + if (curr->isBox() && !curr->isTableRow()) { // Rows and cells share the same coordinate space (that of the section). // Omit them when computing our xpos/ypos. - x += curr->x(); - y += curr->y(); + RenderBox* currBox = toRenderBox(curr); + x += currBox->x(); + y += currBox->y(); } - curr = curr->parentBox(); + curr = curr->parent(); } - if (curr->isTableRow()) { + if (curr->isBox() && curr->isTableRow()) { // Put ourselves into the row coordinate space. - x -= curr->x(); - y -= curr->y(); + RenderBox* currBox = toRenderBox(curr); + x -= currBox->x(); + y -= currBox->y(); } } m_relX = m_relY = 0; if (renderer()->isRelPositioned()) { - m_relX = toRenderBox(renderer())->relativePositionOffsetX(); - m_relY = toRenderBox(renderer())->relativePositionOffsetY(); + m_relX = renderer()->relativePositionOffsetX(); + m_relY = renderer()->relativePositionOffsetY(); x += m_relX; y += m_relY; } @@ -414,8 +541,8 @@ void RenderLayer::updateLayerPosition() // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. positionedParent->subtractScrolledContentOffset(x, y); - if (renderer()->isPositioned()) { - IntSize offset = toRenderBox(renderer())->offsetForPositionedInContainer(positionedParent->renderer()); + if (renderer()->isPositioned() && positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) { + IntSize offset = toRenderInline(positionedParent->renderer())->relativePositionedInlineOffset(toRenderBox(renderer())); x += offset.width(); y += offset.height(); } @@ -424,33 +551,74 @@ void RenderLayer::updateLayerPosition() // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. - setPos(x, y); + setLocation(x, y); if (renderer()->isRenderInline()) { - RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); + RenderInline* inlineFlow = toRenderInline(renderer()); IntRect lineBox = inlineFlow->linesBoundingBox(); setWidth(lineBox.width()); setHeight(lineBox.height()); - } else { - setWidth(renderer()->width()); - setHeight(renderer()->height()); - - if (!renderer()->hasOverflowClip()) { - if (renderer()->overflowWidth() > renderer()->width()) - setWidth(renderer()->overflowWidth()); - if (renderer()->overflowHeight() > renderer()->height()) - setHeight(renderer()->overflowHeight()); + } else if (RenderBox* box = renderBox()) { + setWidth(box->width()); + setHeight(box->height()); + + if (!box->hasOverflowClip()) { + if (box->overflowWidth() > box->width()) + setWidth(box->overflowWidth()); + if (box->overflowHeight() > box->height()) + setHeight(box->overflowHeight()); } } } -RenderLayer *RenderLayer::stackingContext() const +TransformationMatrix RenderLayer::perspectiveTransform() const { - RenderLayer* curr = parent(); - for ( ; curr && !curr->renderer()->isRenderView() && !curr->renderer()->isRoot() && - curr->renderer()->style()->hasAutoZIndex(); - curr = curr->parent()) { } - return curr; + if (!renderer()->hasTransform()) + return TransformationMatrix(); + + RenderStyle* style = renderer()->style(); + if (!style->hasPerspective()) + return TransformationMatrix(); + + // Maybe fetch the perspective from the backing? + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + const float boxWidth = borderBox.width(); + const float boxHeight = borderBox.height(); + + float perspectiveOriginX = style->perspectiveOriginX().calcFloatValue(boxWidth); + float perspectiveOriginY = style->perspectiveOriginY().calcFloatValue(boxHeight); + + // A perspective origin of 0,0 makes the vanishing point in the center of the element. + // We want it to be in the top-left, so subtract half the height and width. + perspectiveOriginX -= boxWidth / 2.0f; + perspectiveOriginY -= boxHeight / 2.0f; + + TransformationMatrix t; + t.translate(perspectiveOriginX, perspectiveOriginY); + t.applyPerspective(style->perspective()); + t.translate(-perspectiveOriginX, -perspectiveOriginY); + + return t; +} + +FloatPoint RenderLayer::perspectiveOrigin() const +{ + if (!renderer()->hasTransform()) + return FloatPoint(); + + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + RenderStyle* style = renderer()->style(); + + return FloatPoint(style->perspectiveOriginX().calcFloatValue(borderBox.width()), + style->perspectiveOriginY().calcFloatValue(borderBox.height())); +} + +RenderLayer* RenderLayer::stackingContext() const +{ + RenderLayer* layer = parent(); + while (layer && !layer->renderer()->isRenderView() && !layer->renderer()->isRoot() && layer->renderer()->style()->hasAutoZIndex()) + layer = layer->parent(); + return layer; } RenderLayer* RenderLayer::enclosingPositionedAncestor() const @@ -469,6 +637,27 @@ RenderLayer* RenderLayer::enclosingTransformedAncestor() const return curr; } +#if USE(ACCELERATED_COMPOSITING) +RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const +{ + if (includeSelf && isComposited()) + return const_cast<RenderLayer*>(this); + + // Compositing layers are parented according to stacking order and overflow list, + // so we have to check whether the parent is a stacking context, or whether + // the child is overflow-only. + bool inNormalFlowList = isNormalFlowOnly(); + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->isComposited() && (inNormalFlowList || curr->isStackingContext())) + return curr; + + inNormalFlowList = curr->isNormalFlowOnly(); + } + + return 0; +} +#endif + IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const { // We don't use convertToLayerCoords because it doesn't know about transforms @@ -487,18 +676,24 @@ bool RenderLayer::requiresSlowRepaints() const bool RenderLayer::isTransparent() const { #if ENABLE(SVG) - if (renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) + if (renderer()->node() && renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) return false; #endif return renderer()->isTransparent() || renderer()->hasMask(); } -RenderLayer* -RenderLayer::transparentAncestor() +RenderLayer* RenderLayer::transparentPaintingAncestor() { - RenderLayer* curr = parent(); - for ( ; curr && !curr->isTransparent(); curr = curr->parent()) { } - return curr; + if (isComposited()) + return 0; + + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->isComposited()) + return 0; + if (curr->isTransparent()) + return curr; + } + return 0; } static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransform, const RenderLayer* l, const RenderLayer* rootLayer) @@ -507,16 +702,16 @@ static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransfor // paintDirtyRect, and that should cut down on the amount we have to paint. Still it // would be better to respect clips. - TransformationMatrix* t = l->transform(); - if (t && rootLayer != l) { + if (rootLayer != l && l->paintsWithTransform()) { // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass // the transformed layer and all of its children. int x = 0; int y = 0; l->convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform; transform.translate(x, y); - transform = *t * transform; + transform = *l->transform() * transform; transform = transform * enclosingTransform; // We now have a transform that will produce a rectangle in our view's space. @@ -550,14 +745,14 @@ static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransfor void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer) { - if (p->paintingDisabled() || (isTransparent() && m_usedTransparency)) + if (p->paintingDisabled() || (paintsWithTransparency() && m_usedTransparency)) return; - RenderLayer* ancestor = transparentAncestor(); + RenderLayer* ancestor = transparentPaintingAncestor(); if (ancestor) ancestor->beginTransparencyLayers(p, rootLayer); - if (isTransparent()) { + if (paintsWithTransparency()) { m_usedTransparency = true; p->save(); p->clip(transparencyClipBox(TransformationMatrix(), this, rootLayer)); @@ -577,7 +772,7 @@ void RenderLayer::operator delete(void* ptr, size_t sz) } void RenderLayer::destroy(RenderArena* renderArena) -{ +{ delete this; // Recover the size left there for us by operator delete and free the memory. @@ -601,10 +796,10 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) child->setParent(this); - if (child->isOverflowOnly()) - dirtyOverflowList(); + if (child->isNormalFlowOnly()) + dirtyNormalFlowList(); - if (!child->isOverflowOnly() || child->firstChild()) { + if (!child->isNormalFlowOnly() || child->firstChild()) { // Dirty the z-order list in which we are contained. The stackingContext() can be null in the // case where we're building up generated content layers. This is ok, since the lists will start // off dirty in that case anyway. @@ -614,10 +809,19 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) child->updateVisibilityStatus(); if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) childVisibilityChanged(true); + +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWasAdded(this, child); +#endif } RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) { +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(this, oldChild); +#endif + // remove the child if (oldChild->previousSibling()) oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); @@ -629,9 +833,9 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) if (m_last == oldChild) m_last = oldChild->previousSibling(); - if (oldChild->isOverflowOnly()) - dirtyOverflowList(); - if (!oldChild->isOverflowOnly() || oldChild->firstChild()) { + if (oldChild->isNormalFlowOnly()) + dirtyNormalFlowList(); + if (!oldChild->isNormalFlowOnly() || oldChild->firstChild()) { // 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. @@ -654,6 +858,10 @@ void RenderLayer::removeOnlyThisLayer() if (!m_parent) return; +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + // Dirty the clip rects. clearClipRectsIncludingDescendants(); @@ -674,8 +882,8 @@ void RenderLayer::removeOnlyThisLayer() current->updateLayerPositions(); current = next; } - - destroy(renderer()->renderArena()); + + m_renderer->destroyLayer(); } void RenderLayer::insertOnlyThisLayer() @@ -684,21 +892,21 @@ void RenderLayer::insertOnlyThisLayer() // We need to connect ourselves when our renderer() has a parent. // Find our enclosingLayer and add ourselves. RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); + ASSERT(parentLayer); RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; - if (parentLayer) - parentLayer->addChild(this, beforeChild); + parentLayer->addChild(this, beforeChild); } - + // 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); - + // Clear out all the clip rects. clearClipRectsIncludingDescendants(); } void -RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const +RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, int& yPos) const { if (ancestorLayer == this) return; @@ -707,8 +915,8 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& // Add in the offset of the view. We can obtain this by calling // localToAbsolute() on the RenderView. FloatPoint absPos = renderer()->localToAbsolute(FloatPoint(), true); - x += absPos.x(); - y += absPos.y(); + xPos += absPos.x(); + yPos += absPos.y(); return; } @@ -720,10 +928,10 @@ RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& if (!parentLayer) return; - parentLayer->convertToLayerCoords(ancestorLayer, x, y); + parentLayer->convertToLayerCoords(ancestorLayer, xPos, yPos); - x += xPos(); - y += yPos(); + xPos += x(); + yPos += y(); } void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) @@ -804,14 +1012,18 @@ RenderLayer::subtractScrolledContentOffset(int& x, int& y) const void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint) { - if (renderer()->style()->overflowX() != OMARQUEE) { + RenderBox* box = renderBox(); + if (!box) + return; + + if (box->style()->overflowX() != OMARQUEE) { if (x < 0) x = 0; if (y < 0) y = 0; // Call the scrollWidth/Height functions so that the dimensions will be computed if they need // to be (for overflow:hidden blocks). - int maxX = scrollWidth() - renderer()->clientWidth(); - int maxY = scrollHeight() - renderer()->clientHeight(); + int maxX = scrollWidth() - box->clientWidth(); + int maxY = scrollHeight() - box->clientHeight(); if (x > maxX) x = maxX; if (y > maxY) y = maxY; @@ -831,6 +1043,11 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // Update the positions of our child layers. for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(false, false); + +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) + m_backing->updateGraphicsLayerGeometry(); +#endif RenderView* view = renderer()->view(); @@ -865,7 +1082,7 @@ void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repai // Schedule the scroll DOM event. if (view) { if (FrameView* frameView = view->frameView()) - frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), EventTargetNodeCast(renderer()->element())); + frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), renderer()->node()); } } @@ -890,10 +1107,12 @@ void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. // This will prevent us from revealing text hidden by the slider in Safari RSS. - FloatPoint absPos = renderer()->localToAbsolute(); - absPos.move(renderer()->borderLeft(), renderer()->borderTop()); + RenderBox* box = renderBox(); + ASSERT(box); + FloatPoint absPos = box->localToAbsolute(); + absPos.move(box->borderLeft(), box->borderTop()); - IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), renderer()->clientWidth(), renderer()->clientHeight()); + IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), box->clientWidth(), box->clientHeight()); IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); @@ -912,7 +1131,7 @@ void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, newRect.setX(rect.x() - diffX); newRect.setY(rect.y() - diffY); } - } else if (!parentLayer && renderer()->canBeProgramaticallyScrolled(scrollToAnchor)) { + } else if (!parentLayer && renderer()->isBox() && renderBox()->canBeProgramaticallyScrolled(scrollToAnchor)) { if (frameView) { if (renderer()->document() && renderer()->document()->ownerElement() && renderer()->document()->ownerElement()->renderer()) { IntRect viewRect = frameView->visibleContentRect(); @@ -956,17 +1175,17 @@ IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect & // If the rectangle is fully visible, use the specified visible behavior. // If the rectangle is partially visible, but over a certain threshold, // then treat it as fully visible to avoid unnecessary horizontal scrolling - scrollX = getVisibleBehavior(alignX); + scrollX = ScrollAlignment::getVisibleBehavior(alignX); else if (intersectWidth == visibleRect.width()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. - scrollX = getVisibleBehavior(alignX); + scrollX = ScrollAlignment::getVisibleBehavior(alignX); if (scrollX == alignCenter) scrollX = noScroll; } else if (intersectWidth > 0) // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior - scrollX = getPartialBehavior(alignX); + scrollX = ScrollAlignment::getPartialBehavior(alignX); else - scrollX = getHiddenBehavior(alignX); + scrollX = ScrollAlignment::getHiddenBehavior(alignX); // If we're trying to align to the closest edge, and the exposeRect is further right // than the visibleRect, and not bigger than the visible area, then align with the right. if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width()) @@ -989,17 +1208,17 @@ IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect & int intersectHeight = intersection(visibleRect, exposeRectY).height(); if (intersectHeight == exposeRect.height()) // If the rectangle is fully visible, use the specified visible behavior. - scrollY = getVisibleBehavior(alignY); + scrollY = ScrollAlignment::getVisibleBehavior(alignY); else if (intersectHeight == visibleRect.height()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. - scrollY = getVisibleBehavior(alignY); + scrollY = ScrollAlignment::getVisibleBehavior(alignY); if (scrollY == alignCenter) scrollY = noScroll; } else if (intersectHeight > 0) // If the rectangle is partially visible, use the specified partial behavior - scrollY = getPartialBehavior(alignY); + scrollY = ScrollAlignment::getPartialBehavior(alignY); else - scrollY = getHiddenBehavior(alignY); + scrollY = ScrollAlignment::getHiddenBehavior(alignY); // If we're trying to align to the closest edge, and the exposeRect is further down // than the visibleRect, and not bigger than the visible area, then align with the bottom. if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height()) @@ -1032,12 +1251,13 @@ void RenderLayer::autoscroll() frame->eventHandler()->updateSelectionForMouseDrag(); IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); - scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, gAlignToEdgeIfNeeded, gAlignToEdgeIfNeeded); + scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); } void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset) { - if (!inResizeMode() || !renderer()->hasOverflowClip()) + // FIXME: This should be possible on generated content but is not right now. + if (!inResizeMode() || !renderer()->hasOverflowClip() || !renderer()->node()) return; // Set the width and height of the shadow ancestor node if there is one. @@ -1073,7 +1293,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset ExceptionCode ec; if (difference.width()) { - if (element && element->isControl()) { + if (element->isFormControlElement()) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). style->setProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false, ec); style->setProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false, ec); @@ -1085,7 +1305,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset } if (difference.height()) { - if (element && element->isControl()) { + if (element->isFormControlElement()) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). style->setProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false, ec); style->setProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false, ec); @@ -1172,6 +1392,7 @@ static IntRect scrollCornerRect(const RenderLayer* layer, const IntRect& bounds) static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds) { + ASSERT(layer->renderer()->isBox()); if (layer->renderer()->style()->resize() == RESIZE_NONE) return IntRect(); return cornerRect(layer, bounds); @@ -1179,25 +1400,29 @@ static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds bool RenderLayer::scrollbarCornerPresent() const { - return !scrollCornerRect(this, renderer()->borderBoxRect()).isEmpty(); + ASSERT(renderer()->isBox()); + return !scrollCornerRect(this, renderBox()->borderBoxRect()).isEmpty(); } void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) { IntRect scrollRect = rect; + RenderBox* box = renderBox(); + ASSERT(box); if (scrollbar == m_vBar.get()) - scrollRect.move(renderer()->width() - renderer()->borderRight() - scrollbar->width(), renderer()->borderTop()); + scrollRect.move(box->width() - box->borderRight() - scrollbar->width(), box->borderTop()); else - scrollRect.move(renderer()->borderLeft(), renderer()->height() - renderer()->borderBottom() - scrollbar->height()); + scrollRect.move(box->borderLeft(), box->height() - box->borderBottom() - scrollbar->height()); renderer()->repaintRectangle(scrollRect); } PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) { RefPtr<Scrollbar> widget; - bool hasCustomScrollbarStyle = renderer()->node()->shadowAncestorNode()->renderer()->style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) - widget = RenderScrollbar::createCustomScrollbar(this, orientation, renderer()->node()->shadowAncestorNode()->renderBox()); + widget = RenderScrollbar::createCustomScrollbar(this, orientation, toRenderBox(actualRenderer)); else widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); renderer()->document()->view()->addChild(widget.get()); @@ -1287,19 +1512,23 @@ void RenderLayer::positionOverflowControls(int tx, int ty) if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) return; - IntRect borderBox = renderer()->borderBoxRect(); + RenderBox* box = renderBox(); + if (!box) + return; + + IntRect borderBox = box->borderBoxRect(); IntRect scrollCorner(scrollCornerRect(this, borderBox)); IntRect absBounds(borderBox.x() + tx, borderBox.y() + ty, borderBox.width(), borderBox.height()); if (m_vBar) - m_vBar->setFrameRect(IntRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), - absBounds.y() + renderer()->borderTop(), + m_vBar->setFrameRect(IntRect(absBounds.right() - box->borderRight() - m_vBar->width(), + absBounds.y() + box->borderTop(), m_vBar->width(), - absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - scrollCorner.height())); + absBounds.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height())); if (m_hBar) - m_hBar->setFrameRect(IntRect(absBounds.x() + renderer()->borderLeft(), - absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), - absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - scrollCorner.width(), + m_hBar->setFrameRect(IntRect(absBounds.x() + box->borderLeft(), + absBounds.bottom() - box->borderBottom() - m_hBar->height(), + absBounds.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(), m_hBar->height())); if (m_scrollCorner) @@ -1324,19 +1553,22 @@ int RenderLayer::scrollHeight() void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) { + RenderBox* box = renderBox(); + ASSERT(box); + m_scrollDimensionsDirty = false; bool ltr = renderer()->style()->direction() == LTR; - int clientWidth = renderer()->clientWidth(); - int clientHeight = renderer()->clientHeight(); + int clientWidth = box->clientWidth(); + int clientHeight = box->clientHeight(); - m_scrollLeftOverflow = ltr ? 0 : min(0, renderer()->leftmostPosition(true, false) - renderer()->borderLeft()); + m_scrollLeftOverflow = ltr ? 0 : min(0, box->leftmostPosition(true, false) - box->borderLeft()); int rightPos = ltr ? - renderer()->rightmostPosition(true, false) - renderer()->borderLeft() : + box->rightmostPosition(true, false) - box->borderLeft() : clientWidth - m_scrollLeftOverflow; - int bottomPos = renderer()->lowestPosition(true, false) - renderer()->borderTop(); + int bottomPos = box->lowestPosition(true, false) - box->borderTop(); m_scrollWidth = max(rightPos, clientWidth); m_scrollHeight = max(bottomPos, clientHeight); @@ -1368,7 +1600,7 @@ void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOve if (FrameView* frameView = renderer()->document()->view()) { frameView->scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), - EventTargetNodeCast(renderer()->element())); + renderer()->node()); } } } @@ -1376,16 +1608,20 @@ void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOve void RenderLayer::updateScrollInfoAfterLayout() { + RenderBox* box = renderBox(); + if (!box) + return; + m_scrollDimensionsDirty = true; bool horizontalOverflow, verticalOverflow; computeScrollDimensions(&horizontalOverflow, &verticalOverflow); - if (renderer()->style()->overflowX() != OMARQUEE) { + if (box->style()->overflowX() != OMARQUEE) { // Layout may cause us to be in an invalid scroll position. In this case we need // to pull our scroll offsets back to the max (or push them up to the min). - int newX = max(0, min(scrollXOffset(), scrollWidth() - renderer()->clientWidth())); - int newY = max(0, min(m_scrollY, scrollHeight() - renderer()->clientHeight())); + int newX = max(0, min(scrollXOffset(), scrollWidth() - box->clientWidth())); + int newY = max(0, min(m_scrollY, scrollHeight() - box->clientHeight())); if (newX != scrollXOffset() || newY != m_scrollY) { RenderView* view = renderer()->view(); ASSERT(view); @@ -1417,12 +1653,12 @@ RenderLayer::updateScrollInfoAfterLayout() setHasVerticalScrollbar(false); // overflow:auto may need to lay out again if scrollbars got added/removed. - bool scrollbarsChanged = (renderer()->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || - (renderer()->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); + bool scrollbarsChanged = (box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || + (box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); if (scrollbarsChanged) { - if (renderer()->hasAutoHorizontalScrollbar()) + if (box->hasAutoHorizontalScrollbar()) setHasHorizontalScrollbar(horizontalOverflow); - if (renderer()->hasAutoVerticalScrollbar()) + if (box->hasAutoVerticalScrollbar()) setHasVerticalScrollbar(verticalOverflow); #if ENABLE(DASHBOARD_SUPPORT) @@ -1439,7 +1675,7 @@ RenderLayer::updateScrollInfoAfterLayout() m_inOverflowRelayout = true; renderer()->setNeedsLayout(true, false); if (renderer()->isRenderBlock()) - static_cast<RenderBlock*>(renderer())->layoutBlock(true); + toRenderBlock(renderer())->layoutBlock(true); else renderer()->layout(); m_inOverflowRelayout = false; @@ -1448,14 +1684,14 @@ RenderLayer::updateScrollInfoAfterLayout() } // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). - if (m_hBar && renderer()->hasAutoHorizontalScrollbar()) + if (m_hBar && box->hasAutoHorizontalScrollbar()) m_hBar->setEnabled(true); - if (m_vBar && renderer()->hasAutoVerticalScrollbar()) + if (m_vBar && box->hasAutoVerticalScrollbar()) m_vBar->setEnabled(true); // Set up the range (and page step/line step). if (m_hBar) { - int clientWidth = renderer()->clientWidth(); + int clientWidth = box->clientWidth(); int pageStep = (clientWidth - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientWidth; m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); @@ -1463,14 +1699,14 @@ RenderLayer::updateScrollInfoAfterLayout() m_hBar->setValue(scrollXOffset()); } if (m_vBar) { - int clientHeight = renderer()->clientHeight(); + int clientHeight = box->clientHeight(); int pageStep = (clientHeight - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientHeight; m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_vBar->setProportion(clientHeight, m_scrollHeight); } - if (renderer()->element() && renderer()->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) + if (renderer()->node() && renderer()->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) updateOverflowStatus(horizontalOverflow, verticalOverflow); } @@ -1501,7 +1737,10 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) { - IntRect cornerRect = scrollCornerRect(this, renderer()->borderBoxRect()); + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = scrollCornerRect(this, box->borderBoxRect()); IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); if (!absRect.intersects(damageRect)) return; @@ -1524,7 +1763,10 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I if (renderer()->style()->resize() == RESIZE_NONE) return; - IntRect cornerRect = resizerCornerRect(this, renderer()->borderBoxRect()); + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = resizerCornerRect(this, box->borderBoxRect()); IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); if (!absRect.intersects(damageRect)) return; @@ -1548,6 +1790,7 @@ void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const I // Clipping will exclude the right and bottom edges of this frame. if (m_hBar || m_vBar) { context->save(); + context->clip(absRect); IntRect largerCorner = absRect; largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); context->setStrokeColor(Color(makeRGB(217, 217, 217))); @@ -1563,9 +1806,12 @@ bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const if (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE) return false; + RenderBox* box = renderBox(); + ASSERT(box); + IntPoint localPoint = absoluteToContents(absolutePoint); - IntRect localBounds(0, 0, renderer()->width(), renderer()->height()); + IntRect localBounds(0, 0, box->width(), box->height()); return resizerCornerRect(this, localBounds).contains(localPoint); } @@ -1574,10 +1820,13 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) return false; + RenderBox* box = renderBox(); + ASSERT(box); + int x = 0; int y = 0; convertToLayerCoords(root(), x, y); - IntRect absBounds(x, y, renderer()->width(), renderer()->height()); + IntRect absBounds(x, y, box->width(), box->height()); IntRect resizeControlRect; if (renderer()->style()->resize() != RESIZE_NONE) { @@ -1589,7 +1838,10 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) int resizeControlSize = max(resizeControlRect.height(), 0); if (m_vBar) { - IntRect vBarRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), absBounds.y() + renderer()->borderTop(), m_vBar->width(), absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); + IntRect vBarRect(absBounds.right() - box->borderRight() - m_vBar->width(), + absBounds.y() + box->borderTop(), + m_vBar->width(), + absBounds.height() - (box->borderTop() + box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); if (vBarRect.contains(result.point())) { result.setScrollbar(m_vBar.get()); return true; @@ -1598,7 +1850,10 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result) resizeControlSize = max(resizeControlRect.width(), 0); if (m_hBar) { - IntRect hBarRect(absBounds.x() + renderer()->borderLeft(), absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), m_hBar->height()); + IntRect hBarRect(absBounds.x() + box->borderLeft(), + absBounds.bottom() - box->borderBottom() - m_hBar->height(), + absBounds.width() - (box->borderLeft() + box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), + m_hBar->height()); if (hBarRect.contains(result.point())) { result.setScrollbar(m_hBar.get()); return true; @@ -1654,6 +1909,12 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction, RenderObject* paintingRoot, bool appliedTransform, bool temporaryClipRects) { +#if USE(ACCELERATED_COMPOSITING) + // Composited RenderLayers are painted via the backing's paintIntoLayer(). + if (isComposited() && !backing()->paintingGoesToWindow()) + return; +#endif + // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC. // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document // will do a full repaint(). @@ -1664,11 +1925,11 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, if (!renderer()->opacity()) return; - if (isTransparent()) + if (paintsWithTransparency()) haveTransparency = true; // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate. - if (m_transform && !appliedTransform) { + if (paintsWithTransform() && !appliedTransform) { // If the transform can't be inverted, then don't paint anything. if (!m_transform->isInvertible()) return; @@ -1681,14 +1942,9 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Make sure the parent's clip rects have been calculated. IntRect clipRect = paintDirtyRect; if (parent()) { - if (temporaryClipRects) { - ClipRects parentClipRects; - parent()->calculateClipRects(rootLayer, parentClipRects); - clipRect = parentClipRects.overflowClipRect(); - } else { - parent()->updateClipRects(rootLayer); - clipRect = parent()->clipRects()->overflowClipRect(); - } + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, temporaryClipRects); + clipRect = parentRects.overflowClipRect(); clipRect.intersect(paintDirtyRect); } @@ -1710,7 +1966,7 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, // Now do a paint with the root layer shifted to be us. paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), haveTransparency, paintRestriction, paintingRoot, true, temporaryClipRects); - + p->restore(); // Restore the clip. @@ -1732,12 +1988,11 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, temporaryClipRects); int x = layerBounds.x(); int y = layerBounds.y(); - int tx = x - renderer()->x(); - int ty = y - renderer()->y(); + int tx = x - renderBoxX(); + int ty = y - renderBoxY(); // Ensure our lists are up-to-date. - updateZOrderLists(); - updateOverflowList(); + updateLayerListsIfNeeded(); bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; @@ -1765,11 +2020,6 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); renderer()->paint(paintInfo, tx, ty); - // 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. - paintOverflowControls(p, x, y, damageRect); - // Restore the clip. restoreClip(p, paintDirtyRect, damageRect); } @@ -1813,10 +2063,12 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, } // Paint any child layers that have overflow. - if (m_overflowList) - for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it) - it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); - + if (m_normalFlowList) + for (Vector<RenderLayer*>::iterator it = m_normalFlowList->begin(); it != m_normalFlowList->end(); ++it) { + if (it[0]->isSelfPaintingLayer()) + it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + } + // Now walk the sorted list of children with positive z-indices. if (m_posZOrderList) for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it) @@ -1834,7 +2086,7 @@ RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, } // End our transparency layer - if (isTransparent() && m_usedTransparency) { + if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { p->endTransparencyLayer(); p->restore(); m_usedTransparency = false; @@ -1855,9 +2107,19 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) renderer()->document()->updateLayout(); IntRect boundsRect(m_x, m_y, width(), height()); - boundsRect.intersect(frameVisibleRect(renderer())); - - RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect, result.point()); + if (!request.ignoreClipping()) + boundsRect.intersect(frameVisibleRect(renderer())); + + RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, boundsRect, result.point(), false); + if (!insideLayer) { + // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, + // return ourselves. We do this so mouse events continue getting delivered after a drag has + // exited the WebView, and so hit testing over a scrollbar hits the content document. + if ((request.active() || request.mouseUp()) && renderer()->isRenderView()) { + renderer()->updateHitTestResult(result, result.point()); + insideLayer = this; + } + } // Now determine if the result is inside an anchor; make sure an image map wins if // it already set URLElement and only use the innermost. @@ -1880,135 +2142,302 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) Node* RenderLayer::enclosingElement() const { for (RenderObject* r = renderer(); r; r = r->parent()) { - if (Node* e = r->element()) + if (Node* e = r->node()) return e; } ASSERT_NOT_REACHED(); return 0; } -RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, - const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform) +// Compute the z-offset of the point in the transformState. +// This is effectively projecting a ray normal to the plane of ancestor, finding where that +// ray intersects target, and computing the z delta between those two points. +static double computeZOffset(const HitTestingTransformState& transformState) +{ + // We got an affine transform, so no z-offset + if (transformState.m_accumulatedTransform.isAffine()) + return 0; + + // Flatten the point into the target plane + FloatPoint targetPoint = transformState.mappedPoint(); + + // Now map the point back through the transform, which computes Z. + FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); + return backmappedPoint.z(); +} + +PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* containerTransformState) const +{ + RefPtr<HitTestingTransformState> transformState; + int offsetX = 0; + int offsetY = 0; + if (containerTransformState) { + // If we're already computing transform state, then it's relative to the container (which we know is non-null). + transformState = HitTestingTransformState::create(*containerTransformState); + convertToLayerCoords(containerLayer, offsetX, offsetY); + } else { + // If this is the first time we need to make transform state, then base it off of hitTestPoint, + // which is relative to rootLayer. + transformState = HitTestingTransformState::create(hitTestPoint, FloatQuad(hitTestRect)); + convertToLayerCoords(rootLayer, offsetX, offsetY); + } + + TransformationMatrix containerTransform = renderer()->transformFromContainer(containerLayer ? containerLayer->renderer() : 0, IntSize(offsetX, offsetY)); + transformState->applyTransform(containerTransform, true); + return transformState; +} + + +static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState) +{ + if (!hitLayer) + return false; + + // The hit layer is depth-sorting with other layers, so just say that it was hit. + if (canDepthSort) + return true; + + // We need to look at z-depth to decide if this layer was hit. + if (zOffset) { + ASSERT(transformState); + // This is actually computing our z, but that's OK because the hitLayer is coplanar with us. + double childZOffset = computeZOffset(*transformState); + if (childZOffset > *zOffset) { + *zOffset = childZOffset; + return true; + } + return false; + } + + return true; +} + +// hitTestPoint and hitTestRect are relative to rootLayer. +// A 'flattening' layer is one preserves3D() == false. +// transformState.m_accumulatedTransform holds the transform from the containing flattening layer. +// transformState.m_lastPlanarPoint is the hitTestPoint in the plane of the containing flattening layer. +// transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer. +// +// If zOffset is non-null (which indicates that the caller wants z offset information), +// *zOffset on return is the z offset of the hit point relative to the containing flattening layer. +RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform, + const HitTestingTransformState* transformState, double* zOffset) { + // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. + + bool useTemporaryClipRects = false; +#if USE(ACCELERATED_COMPOSITING) + useTemporaryClipRects = compositor()->inCompositingMode(); +#endif + // Apply a transform if we have one. - if (m_transform && !appliedTransform) { - // If the transform can't be inverted, then don't hit test this layer at all. - if (!m_transform->isInvertible()) - return 0; - + if (transform() && !appliedTransform) { // Make sure the parent's clip rects have been calculated. if (parent()) { - parent()->updateClipRects(rootLayer); - + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, useTemporaryClipRects); + IntRect clipRect = parentRects.overflowClipRect(); // Go ahead and test the enclosing clip now. - IntRect clipRect = parent()->clipRects()->overflowClipRect(); if (!clipRect.contains(hitTestPoint)) return 0; } - // Adjust the transform such that the renderer's upper left corner is at (0,0) in user space. - // This involves subtracting out the position of the layer in our current coordinate space. - int x = 0; - int y = 0; - convertToLayerCoords(rootLayer, x, y); - TransformationMatrix transform; - transform.translate(x, y); - transform = *m_transform * transform; - - // Map the hit test point into the transformed space and then do a hit test with the root layer shifted to be us. - return hitTestLayer(this, request, result, transform.inverse().mapRect(hitTestRect), transform.inverse().mapPoint(hitTestPoint), true); + // Create a transform state to accumulate this transform. + RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + + // If the transform can't be inverted, then don't hit test this layer at all. + if (!newTransformState->m_accumulatedTransform.isInvertible()) + return 0; + + // Compute the point and the hit test rect in the coords of this layer by using the values + // from the transformState, which store the point and quad in the coords of the last flattened + // layer, and the accumulated transform which lets up map through preserve-3d layers. + // + // We can't just map hitTestPoint and hitTestRect because they may have been flattened (losing z) + // by our container. + IntPoint localPoint = roundedIntPoint(newTransformState->mappedPoint()); + IntRect localHitTestRect; +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) { + // It doesn't make sense to project hitTestRect into the plane of this layer, so use the same bounds we use for painting. + localHitTestRect = compositor()->calculateCompositedBounds(this, this); + } else +#endif + localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox(); + + // Now do a hit test with the root layer shifted to be us. + return hitTestLayer(this, containerLayer, request, result, localHitTestRect, localPoint, true, newTransformState.get(), zOffset); } + // Ensure our lists and 3d status are up-to-date. + updateLayerListsIfNeeded(); + update3DTransformedDescendantStatus(); + + RefPtr<HitTestingTransformState> localTransformState; + if (appliedTransform) { + // We computed the correct state in the caller (above code), so just reference it. + ASSERT(transformState); + localTransformState = const_cast<HitTestingTransformState*>(transformState); + } else if (transformState || m_has3DTransformedDescendant || preserves3D()) { + // We need transform state for the first time, or to offset the container state, so create it here. + localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + } + + // Check for hit test on backface if backface-visibility is 'hidden' + if (localTransformState && renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden) { + TransformationMatrix invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); + // If the z-vector of the matrix is negative, the back is facing towards the viewer. + if (invertedMatrix.m33() < 0) + return 0; + } + + RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState; + if (localTransformState && !preserves3D()) { + // Keep a copy of the pre-flattening state, for computing z-offsets for the container + unflattenedTransformState = HitTestingTransformState::create(*localTransformState); + // This layer is flattening, so flatten the state passed to descendants. + localTransformState->flatten(); + } + // Calculate the clip rects we should use. IntRect layerBounds; IntRect bgRect; IntRect fgRect; IntRect outlineRect; - calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect); - - // Ensure our lists are up-to-date. - updateZOrderLists(); - updateOverflowList(); - - // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer, - // we are done and can return it. - RenderLayer* insideLayer = 0; - - // Begin by walking our list of positive layers from highest z-index down to the lowest - // z-index. + calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect, useTemporaryClipRects); + + // The following are used for keeping track of the z-depth of the hit point of 3d-transformed + // descendants. + double localZOffset = -numeric_limits<double>::infinity(); + double* zOffsetForDescendantsPtr = 0; + double* zOffsetForContentsPtr = 0; + + bool depthSortDescendants = false; + if (preserves3D()) { + depthSortDescendants = true; + // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down. + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (m_has3DTransformedDescendant) { + // Flattening layer with 3d children; use a local zOffset pointer to depth-test children and foreground. + depthSortDescendants = true; + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (zOffset) { + zOffsetForDescendantsPtr = 0; + // Container needs us to give back a z offset for the hit layer. + zOffsetForContentsPtr = zOffset; + } + + // This variable tracks which layer the mouse ends up being inside. + RenderLayer* candidateLayer = 0; + + // Begin by walking our list of positive layers from highest z-index down to the lowest z-index. if (m_posZOrderList) { for (int i = m_posZOrderList->size() - 1; i >= 0; --i) { - insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } // Now check our overflow objects. - if (m_overflowList) { - for (int i = m_overflowList->size() - 1; i >= 0; --i) { - insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + if (m_normalFlowList) { + for (int i = m_normalFlowList->size() - 1; i >= 0; --i) { + RenderLayer* currLayer = m_normalFlowList->at(i); + if (!currLayer->isSelfPaintingLayer()) + continue; + + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = currLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. - if (fgRect.contains(hitTestPoint) && - renderer()->hitTest(request, result, hitTestPoint, - layerBounds.x() - renderer()->x(), - layerBounds.y() - renderer()->y(), - HitTestDescendants)) { - // For positioned generated content, we might still not have a - // node by the time we get to the layer level, since none of - // the content in the layer has an element. So just walk up - // the tree. - if (!result.innerNode() || !result.innerNonSharedNode()) { - Node* e = enclosingElement(); - if (!result.innerNode()) - result.setInnerNode(e); - if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(e); + if (fgRect.contains(hitTestPoint)) { + // Hit test with a temporary HitTestResult, because we onlyl want to commit to 'result' if we know we're frontmost. + HitTestResult tempResult(result.point()); + if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestDescendants) && + isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return this; + // Foreground can depth-sort with descendant layers, so keep this as a candidate. + candidateLayer = this; } - - return this; } - + // Now check our negative z-index children. if (m_negZOrderList) { for (int i = m_negZOrderList->size() - 1; i >= 0; --i) { - insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); - if (insideLayer) - return insideLayer; + HitTestResult tempResult(result.point()); + RenderLayer* hitLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, localTransformState.get(), zOffsetForDescendantsPtr); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState.get())) { + result = tempResult; + if (!depthSortDescendants) + return hitLayer; + + candidateLayer = hitLayer; + } } } + + // If we found a layer, return. Child layers, and foreground always render in front of background. + if (candidateLayer) + return candidateLayer; - // Next we want to see if the mouse is inside this layer but not any of its children. - if (bgRect.contains(hitTestPoint) && - renderer()->hitTest(request, result, hitTestPoint, - layerBounds.x() - renderer()->x(), - layerBounds.y() - renderer()->y(), - HitTestSelf)) { - if (!result.innerNode() || !result.innerNonSharedNode()) { - Node* e = enclosingElement(); - if (!result.innerNode()) - result.setInnerNode(e); - if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(e); + if (bgRect.contains(hitTestPoint)) { + HitTestResult tempResult(result.point()); + if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestSelf) && + isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { + result = tempResult; + return this; } - - return this; } + + return 0; +} - // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, - // return ourselves. We do this so mouse events continue getting delivered after a drag has - // exited the WebView, and so hit testing over a scrollbar hits the content document. - if ((request.active || request.mouseUp) && renderer()->isRenderView()) { - renderer()->updateHitTestResult(result, hitTestPoint); - return this; +bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const IntRect& layerBounds, const IntPoint& hitTestPoint, HitTestFilter hitTestFilter) const +{ + if (!renderer()->hitTest(request, result, hitTestPoint, + layerBounds.x() - renderBoxX(), + layerBounds.y() - renderBoxY(), + hitTestFilter)) { + // It's wrong to set innerNode, but then claim that you didn't hit anything. + ASSERT(!result.innerNode()); + return false; } - return 0; + // For positioned generated content, we might still not have a + // node by the time we get to the layer level, since none of + // the content in the layer has an element. So just walk up + // the tree. + if (!result.innerNode() || !result.innerNonSharedNode()) { + Node* e = enclosingElement(); + if (!result.innerNode()) + result.setInnerNode(e); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(e); + } + + return true; } void RenderLayer::updateClipRects(const RenderLayer* rootLayer) @@ -2039,10 +2468,9 @@ void RenderLayer::updateClipRects(const RenderLayer* rootLayer) void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool useCached) const { - IntRect infiniteRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX); if (!parent()) { // The root layer's clip rect is always infinite. - clipRects.reset(infiniteRect); + clipRects.reset(ClipRects::infiniteRect()); return; } @@ -2058,7 +2486,7 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl parentLayer->calculateClipRects(rootLayer, clipRects); } else - clipRects.reset(infiniteRect); + clipRects.reset(ClipRects::infiniteRect()); // 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. @@ -2086,13 +2514,13 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl } if (renderer()->hasOverflowClip()) { - IntRect newOverflowClip = renderer()->getOverflowClipRect(x,y); + IntRect newOverflowClip = toRenderBox(renderer())->overflowClipRect(x,y); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); if (renderer()->isPositioned() || renderer()->isRelPositioned()) clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); } if (renderer()->hasClip()) { - IntRect newPosClip = renderer()->getClipRect(x,y); + IntRect newPosClip = toRenderBox(renderer())->clipRect(x,y); clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); @@ -2100,24 +2528,30 @@ void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& cl } } +void RenderLayer::parentClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool temporaryClipRects) const +{ + ASSERT(parent()); + if (temporaryClipRects) { + parent()->calculateClipRects(rootLayer, clipRects); + return; + } + + parent()->updateClipRects(rootLayer); + clipRects = *parent()->clipRects(); +} + void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const { if (rootLayer != this && parent()) { - ClipRects parentClipRects; - if (temporaryClipRects) - parent()->calculateClipRects(rootLayer, parentClipRects); - else { - parent()->updateClipRects(rootLayer); - parentClipRects = *parent()->clipRects(); - } - - backgroundRect = renderer()->style()->position() == FixedPosition ? parentClipRects.fixedClipRect() : - (renderer()->isPositioned() ? parentClipRects.posClipRect() : - parentClipRects.overflowClipRect()); + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, temporaryClipRects); + backgroundRect = renderer()->style()->position() == FixedPosition ? parentRects.fixedClipRect() : + (renderer()->isPositioned() ? parentRects.posClipRect() : + parentRects.overflowClipRect()); RenderView* view = renderer()->view(); ASSERT(view); - if (view && parentClipRects.fixed() && rootLayer->renderer() == view) + if (view && parentRects.fixed() && rootLayer->renderer() == view) backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY()); backgroundRect.intersect(paintDirtyRect); @@ -2136,10 +2570,10 @@ void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& pa if (renderer()->hasOverflowClip() || renderer()->hasClip()) { // This layer establishes a clip of some kind. if (renderer()->hasOverflowClip()) - foregroundRect.intersect(renderer()->getOverflowClipRect(x,y)); + foregroundRect.intersect(toRenderBox(renderer())->overflowClipRect(x,y)); if (renderer()->hasClip()) { // Clip applies to *us* as well, so go ahead and update the damageRect. - IntRect newPosClip = renderer()->getClipRect(x,y); + IntRect newPosClip = toRenderBox(renderer())->clipRect(x,y); backgroundRect.intersect(newPosClip); foregroundRect.intersect(newPosClip); outlineRect.intersect(newPosClip); @@ -2202,7 +2636,7 @@ bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect return boundingBox(rootLayer).intersects(damageRect); } -IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const +IntRect RenderLayer::localBoundingBox() const { // There are three special cases we need to consider. // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the @@ -2216,56 +2650,64 @@ IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const IntRect result; if (renderer()->isRenderInline()) { // Go from our first line box to our last line box. - RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); + RenderInline* inlineFlow = toRenderInline(renderer()); InlineFlowBox* firstBox = inlineFlow->firstLineBox(); if (!firstBox) return result; int top = firstBox->root()->topOverflow(); int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow(); - int left = firstBox->xPos(); + int left = firstBox->x(); for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) - left = min(left, curr->xPos()); - result = IntRect(m_x + left, m_y + (top - renderer()->y()), width(), bottom - top); + left = min(left, curr->x()); + result = IntRect(left, top, width(), bottom - top); } else if (renderer()->isTableRow()) { // Our bounding box is just the union of all of our cells' border/overflow rects. for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { if (child->isTableCell()) { IntRect bbox = toRenderBox(child)->borderBoxRect(); result.unite(bbox); - IntRect overflowRect = renderer()->overflowRect(false); + IntRect overflowRect = renderBox()->overflowRect(false); if (bbox != overflowRect) result.unite(overflowRect); } } - result.move(m_x, m_y); } else { - if (renderer()->hasMask()) - result = renderer()->maskClipRect(); + RenderBox* box = renderBox(); + ASSERT(box); + if (box->hasMask()) + result = box->maskClipRect(); else { - IntRect bbox = renderer()->borderBoxRect(); + IntRect bbox = box->borderBoxRect(); result = bbox; - IntRect overflowRect = renderer()->overflowRect(false); + IntRect overflowRect = box->overflowRect(false); if (bbox != overflowRect) result.unite(overflowRect); } - - // We have to adjust the x/y of this result so that it is in the coordinate space of the layer. - result.move(m_x, m_y); } - - // Convert the bounding box to an absolute position. We can do this easily by looking at the delta - // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds - // passed in. - int absX = 0, absY = 0; - convertToLayerCoords(rootLayer, absX, absY); - result.move(absX - m_x, absY - m_y); + RenderView* view = renderer()->view(); ASSERT(view); if (view) - result.inflate(view->maximalOutlineSize()); + result.inflate(view->maximalOutlineSize()); // Used to apply a fudge factor to dirty-rect checks on blocks/tables. + return result; } +IntRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer) const +{ + IntRect result = localBoundingBox(); + + int deltaX = 0, deltaY = 0; + convertToLayerCoords(ancestorLayer, deltaX, deltaY); + result.move(deltaX, deltaY); + return result; +} + +IntRect RenderLayer::absoluteBoundingBox() const +{ + return boundingBox(root()); +} + void RenderLayer::clearClipRectsIncludingDescendants() { if (!m_clipRects) @@ -2288,6 +2730,38 @@ void RenderLayer::clearClipRects() } } +#if USE(ACCELERATED_COMPOSITING) +RenderLayerBacking* RenderLayer::ensureBacking() +{ + if (!m_backing) + m_backing.set(new RenderLayerBacking(this)); + return m_backing.get(); +} + +void RenderLayer::clearBacking() +{ + m_backing.clear(); +} +#endif + +void RenderLayer::setParent(RenderLayer* parent) +{ + if (parent == m_parent) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + + m_parent = parent; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWasAdded(m_parent, this); +#endif +} + static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) { if (!obj1 || !obj2) @@ -2303,28 +2777,28 @@ static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result) { - // We don't update :hover/:active state when the result is marked as readonly. - if (request.readonly) + // We don't update :hover/:active state when the result is marked as readOnly. + if (request.readOnly()) return; Document* doc = renderer()->document(); Node* activeNode = doc->activeNode(); - if (activeNode && !request.active) { + if (activeNode && !request.active()) { // We are clearing the :active chain because the mouse has been released. for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) { - if (curr->element() && !curr->isText()) - curr->element()->setInActiveChain(false); + if (curr->node() && !curr->isText()) + curr->node()->setInActiveChain(false); } doc->setActiveNode(0); } else { Node* newActiveNode = result.innerNode(); - if (!activeNode && newActiveNode && request.active) { + if (!activeNode && newActiveNode && request.active()) { // We are setting the :active chain and freezing it. If future moves happen, they // will need to reference this chain. for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) { - if (curr->element() && !curr->isText()) { - curr->element()->setInActiveChain(true); + if (curr->node() && !curr->isText()) { + curr->node()->setInActiveChain(true); } } doc->setActiveNode(newActiveNode); @@ -2334,7 +2808,7 @@ void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestR // If the mouse is down and if this is a mouse move event, we want to restrict changes in // :hover/:active to only apply to elements that are in the :active chain that we froze // at the time the mouse went down. - bool mustBeInActiveChain = request.active && request.mouseMove; + bool mustBeInActiveChain = request.active() && request.mouseMove(); // Check to see if the hovered node has changed. If not, then we don't need to // do anything. @@ -2354,18 +2828,18 @@ void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestR if (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 = curr->hoverAncestor()) { - if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { - curr->element()->setActive(false); - curr->element()->setHovered(false); + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) { + curr->node()->setActive(false); + curr->node()->setHovered(false); } } } // Now set the hover state for our new object up to the root. for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) { - if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { - curr->element()->setActive(request.active); - curr->element()->setHovered(true); + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) { + curr->node()->setActive(request.active()); + curr->node()->setHovered(true); } } } @@ -2383,6 +2857,11 @@ void RenderLayer::dirtyZOrderLists() if (m_negZOrderList) m_negZOrderList->clear(); m_zOrderListsDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedUpdate(); +#endif } void RenderLayer::dirtyStackingContextZOrderLists() @@ -2392,18 +2871,23 @@ void RenderLayer::dirtyStackingContextZOrderLists() sc->dirtyZOrderLists(); } -void RenderLayer::dirtyOverflowList() +void RenderLayer::dirtyNormalFlowList() { - if (m_overflowList) - m_overflowList->clear(); - m_overflowListDirty = true; + if (m_normalFlowList) + m_normalFlowList->clear(); + m_normalFlowListDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedUpdate(); +#endif } void RenderLayer::updateZOrderLists() { if (!isStackingContext() || !m_zOrderListsDirty) return; - + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) if (!m_reflection || reflectionLayer() != child) child->collectLayers(m_posZOrderList, m_negZOrderList); @@ -2411,27 +2895,28 @@ void RenderLayer::updateZOrderLists() // Sort the two lists. if (m_posZOrderList) std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); + if (m_negZOrderList) std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); m_zOrderListsDirty = false; } -void RenderLayer::updateOverflowList() +void RenderLayer::updateNormalFlowList() { - if (!m_overflowListDirty) + if (!m_normalFlowListDirty) return; for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { // Ignore non-overflow layers and reflections. - if (child->isOverflowOnly() && (!m_reflection || reflectionLayer() != child)) { - if (!m_overflowList) - m_overflowList = new Vector<RenderLayer*>; - m_overflowList->append(child); + if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) { + if (!m_normalFlowList) + m_normalFlowList = new Vector<RenderLayer*>; + m_normalFlowList->append(child); } } - m_overflowListDirty = false; + m_normalFlowListDirty = false; } void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer) @@ -2439,7 +2924,7 @@ void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderL updateVisibilityStatus(); // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. - if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isOverflowOnly()) { + if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isNormalFlowOnly()) { // Determine which buffer the child should be in. Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; @@ -2462,6 +2947,19 @@ void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderL } } +void RenderLayer::updateLayerListsIfNeeded() +{ +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode()) { + if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) + compositor()->updateCompositingLayers(this); + return; + } +#endif + updateZOrderLists(); + updateNormalFlowList(); +} + void RenderLayer::repaintIncludingDescendants() { renderer()->repaint(); @@ -2469,23 +2967,62 @@ void RenderLayer::repaintIncludingDescendants() curr->repaintIncludingDescendants(); } -bool RenderLayer::shouldBeOverflowOnly() const +#if USE(ACCELERATED_COMPOSITING) +void RenderLayer::setBackingNeedsRepaint() +{ + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absoluteBoundingBox()); + } else + backing()->setContentsNeedDisplay(); +} + +void RenderLayer::setBackingNeedsRepaintInRect(const IntRect& r) { - return (renderer()->hasOverflowClip() || renderer()->hasReflection()) && + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + IntRect absRect(r); + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + absRect.move(x, y); + + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absRect); + } else + backing()->setContentsNeedDisplayInRect(r); +} +#endif + +bool RenderLayer::shouldBeNormalFlowOnly() const +{ + return (renderer()->hasOverflowClip() || renderer()->hasReflection() || renderer()->hasMask()) && !renderer()->isPositioned() && !renderer()->isRelPositioned() && !renderer()->hasTransform() && !isTransparent(); } -void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*) +bool RenderLayer::isSelfPaintingLayer() const +{ + return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow(); +} + +void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) { - bool isOverflowOnly = shouldBeOverflowOnly(); - if (isOverflowOnly != m_isOverflowOnly) { - m_isOverflowOnly = isOverflowOnly; + bool isNormalFlowOnly = shouldBeNormalFlowOnly(); + if (isNormalFlowOnly != m_isNormalFlowOnly) { + m_isNormalFlowOnly = isNormalFlowOnly; RenderLayer* p = parent(); if (p) - p->dirtyOverflowList(); + p->dirtyNormalFlowList(); dirtyStackingContextZOrderLists(); } @@ -2516,12 +3053,21 @@ void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*) updateScrollCornerStyle(); updateResizerStyle(); + +#if USE(ACCELERATED_COMPOSITING) + updateTransform(); + + if (compositor()->updateLayerCompositingState(this, diff)) + compositor()->setCompositingLayersNeedUpdate(); +#else + UNUSED_PARAM(diff); +#endif } void RenderLayer::updateScrollCornerStyle() { - RenderObject* actualRenderer = renderer()->node()->isElementNode() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::SCROLLBAR_CORNER, actualRenderer->style()) : 0; + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr<RenderStyle> corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(SCROLLBAR_CORNER, actualRenderer->style()) : 0; if (corner) { if (!m_scrollCorner) { m_scrollCorner = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); @@ -2536,8 +3082,8 @@ void RenderLayer::updateScrollCornerStyle() void RenderLayer::updateResizerStyle() { - RenderObject* actualRenderer = renderer()->node()->isElementNode() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); - RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::RESIZER, actualRenderer->style()) : 0; + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr<RenderStyle> resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RESIZER, actualRenderer->style()) : 0; if (resizer) { if (!m_resizer) { m_resizer = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); |