From fa26a8dd531dff44d6cad0700ff32c0bb949392c Mon Sep 17 00:00:00 2001 From: Nicolas Roard Date: Fri, 5 Mar 2010 14:41:28 +0000 Subject: Fix bug 'Children of fixed elements do not always remain fixed themselves' The problem was twofold: - webkit didn't create composited layers of the children div unless they were intersecting with the fixed layer - the children divs layers are siblings, not children of the fixed layer The solution is to: 1/ mark layers as needed to be composited if their ancestor is a fixed element (in RenderLayerCompositor) 2/ as the GraphicsLayer/LayerAndroid hierarchy is based on the RenderLayer hierarchy (z-order..) and not the display hierarchy, we need to a way of updating the position of the contained layers when a fixed layer move. We do that by: - marking layers contained in a fixed layer as being linked to the fixed layer (GraphicsLayerAndroid::syncFixedDescendants), and set the offset between the layer and the fixed layer. - when pushing the layers tree to the UI side, we ensure that such layers are linked to their corresponding fixed layer (LayerAndroid::ensureFixedLayersForDescendants) - when we draw, we do a first pass to update the fixed layers position (LayerAndroid::updateFixedLayersPositions) then update the rest of the layers (LayerAndroid::updatePositions). The layers that are linked to the fixed layers will then update their position relative to it, using the original offset between the fixed layer and the layer. Bug:2470701 Change-Id: I512966df94de6a5f84aff335c5d09b3f027bc2c3 --- .../graphics/android/GraphicsLayerAndroid.cpp | 75 ++++++++++++++++------ .../graphics/android/GraphicsLayerAndroid.h | 1 + WebCore/platform/graphics/android/LayerAndroid.cpp | 69 +++++++++++++++++--- WebCore/platform/graphics/android/LayerAndroid.h | 38 +++++++++-- 4 files changed, 151 insertions(+), 32 deletions(-) (limited to 'WebCore/platform') diff --git a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index fd1c91a..b48ef49 100644 --- a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -218,26 +218,63 @@ void GraphicsLayerAndroid::needsSyncChildren() askForSync(); } +void GraphicsLayerAndroid::syncFixedDescendants() +{ + for (unsigned int i = 0; i < m_children.size(); i++) + (static_cast(m_children[i]))->syncFixedDescendants(); + + if (!m_client) + return; + + RenderLayerBacking* backing = static_cast(m_client); + RenderLayer* renderLayer = backing->owningLayer(); + RenderView* view = static_cast(renderLayer->renderer()); + + // If we have an ancestor that is a fixed position layer, we need to + // mark ourselve as a child of it, as the RenderLayer hierarchy only + // keeps track of the z-order. + // By calling setRelativeTo() we ensure that we will keep track of + // the fixed layer we are relative to, and will be able to update + // our position accordingly. + RenderLayer* positionedParent = renderLayer->parent(); + while (positionedParent && + !(positionedParent->renderer()->isPositioned() && + positionedParent->renderer()->style()->position() == FixedPosition)) + positionedParent = positionedParent->parent(); + + if (positionedParent && positionedParent->isComposited()) { + RenderLayerBacking* positionedParentBacking = positionedParent->backing(); + GraphicsLayerAndroid* positionedParentLayer = + static_cast(positionedParentBacking->graphicsLayer()); + LayerAndroid* parentLayer = positionedParentLayer->contentLayer(); + m_contentLayer->setRelativeTo(parentLayer); + } +} + void GraphicsLayerAndroid::updateFixedPosition() { - if (m_client) { - RenderLayerBacking* backing = static_cast(m_client); - RenderLayer* renderLayer = backing->owningLayer(); - RenderView* view = static_cast(renderLayer->renderer()); - if (view->isPositioned() && view->style()->position() == FixedPosition) { - SkLength left, top, right, bottom; - left = convertLength(view->style()->left()); - top = convertLength(view->style()->top()); - right = convertLength(view->style()->right()); - bottom = convertLength(view->style()->bottom()); - // We need to pass the size of the element to compute the final fixed - // position -- we can't use the layer's size as it could possibly differs. - // We also have to use the visible overflow and not just the size, - // as some child elements could be overflowing. - int w = view->rightVisibleOverflow() - view->leftVisibleOverflow(); - int h = view->bottomVisibleOverflow() - view->topVisibleOverflow(); - m_contentLayer->setFixedPosition(left, top, right, bottom, w, h); - } + if (!m_client) + return; + + RenderLayerBacking* backing = static_cast(m_client); + RenderLayer* renderLayer = backing->owningLayer(); + RenderView* view = static_cast(renderLayer->renderer()); + + // If we are a fixed position layer, just set it + if (view->isPositioned() && view->style()->position() == FixedPosition) { + SkLength left, top, right, bottom; + left = convertLength(view->style()->left()); + top = convertLength(view->style()->top()); + right = convertLength(view->style()->right()); + bottom = convertLength(view->style()->bottom()); + // We need to pass the size of the element to compute the final fixed + // position -- we can't use the layer's size as it could possibly differs. + // We also have to use the visible overflow and not just the size, + // as some child elements could be overflowing. + int w = view->rightVisibleOverflow() - view->leftVisibleOverflow(); + int h = view->bottomVisibleOverflow() - view->topVisibleOverflow(); + + m_contentLayer->setFixedPosition(left, top, right, bottom, w, h); } } @@ -419,6 +456,7 @@ void GraphicsLayerAndroid::sendImmediateRepaint() if (rootGraphicsLayer->m_frame && rootGraphicsLayer->m_frame->view()) { LayerAndroid* copyLayer = new LayerAndroid(*m_contentLayer); + copyLayer->ensureFixedLayersForDescendants(copyLayer); TLOG("(%x) sendImmediateRepaint, copy the layer, (%.2f,%.2f => %.2f,%.2f)", this, m_contentLayer->getSize().width(), m_contentLayer->getSize().height(), copyLayer->getSize().width(), copyLayer->getSize().height()); @@ -882,6 +920,7 @@ void GraphicsLayerAndroid::syncCompositingState() syncChildren(); syncMask(); syncPositionState(); + syncFixedDescendants(); if (!gPaused || WTF::currentTime() >= gPausedDelay) repaintAll(); diff --git a/WebCore/platform/graphics/android/GraphicsLayerAndroid.h b/WebCore/platform/graphics/android/GraphicsLayerAndroid.h index 25f70b4..09f4181 100644 --- a/WebCore/platform/graphics/android/GraphicsLayerAndroid.h +++ b/WebCore/platform/graphics/android/GraphicsLayerAndroid.h @@ -110,6 +110,7 @@ public: virtual void setZPosition(float); void askForSync(); + void syncFixedDescendants(); void syncPositionState(); void needsSyncChildren(); void syncChildren(); diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index b0c2629..26372f0 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -49,10 +49,14 @@ LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(), m_haveClip(false), m_doRotation(false), m_isFixed(false), + m_isRelativeTo(false), + m_relativeFixedLayerID(0), m_recordingPicture(0), m_extra(0), - m_uniqueId(++gUniqueId) + m_uniqueId(++gUniqueId), + m_relativeFixedLayer(0) { + m_deltaPosition.set(0, 0); m_angleTransform = 0; m_translation.set(0, 0); m_scale.set(1, 1); @@ -65,10 +69,14 @@ LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer), m_isRootLayer(layer.m_isRootLayer), m_haveClip(layer.m_haveClip), m_extra(0), // deliberately not copied - m_uniqueId(layer.m_uniqueId) + m_uniqueId(layer.m_uniqueId), + m_relativeFixedLayer(0) { m_doRotation = layer.m_doRotation; m_isFixed = layer.m_isFixed; + m_isRelativeTo = layer.m_isRelativeTo; + m_relativeFixedLayerID = layer.m_relativeFixedLayerID; + m_deltaPosition = layer.m_deltaPosition; m_angleTransform = layer.m_angleTransform; m_translation = layer.m_translation; @@ -100,10 +108,14 @@ LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(), m_haveClip(false), m_doRotation(false), m_isFixed(false), + m_isRelativeTo(false), + m_relativeFixedLayerID(0), m_recordingPicture(picture), m_extra(0), - m_uniqueId(-1) + m_uniqueId(-1), + m_relativeFixedLayer(0) { + m_deltaPosition.set(0, 0); m_angleTransform = 0; m_translation.set(0, 0); m_scale.set(1, 1); @@ -245,9 +257,29 @@ const LayerAndroid* LayerAndroid::find(int x, int y) const /////////////////////////////////////////////////////////////////////////////// -void LayerAndroid::updatePositions(const SkRect& viewport) { - // apply the viewport to us - SkMatrix matrix; +void LayerAndroid::setRelativeTo(LayerAndroid* container) { + ASSERT(container->m_isFixed); + m_relativeFixedLayerID = container->m_uniqueId; + m_isRelativeTo = true; + m_deltaPosition = getPosition() - container->getPosition(); +} + +void LayerAndroid::ensureFixedLayersForDescendants(const LayerAndroid* rootLayer) { + if (m_isRelativeTo && !m_relativeFixedLayer) { + LayerAndroid* containerLayer = const_cast( + rootLayer->findById(m_relativeFixedLayerID)); + if (containerLayer) + m_relativeFixedLayer = containerLayer; + } + + int count = countChildren(); + for (int i = 0; i < count; i++) { + getChild(i)->ensureFixedLayersForDescendants(rootLayer); + } +} + +void LayerAndroid::updateFixedLayersPositions(const SkRect& viewport) { + if (m_isFixed) { float x = 0; float y = 0; @@ -267,8 +299,25 @@ void LayerAndroid::updatePositions(const SkRect& viewport) { y = dy + h - m_fixedBottom.calcFloatValue(h) - m_fixedHeight; this->setPosition(x, y); - matrix.reset(); - } else { + } + + int count = this->countChildren(); + for (int i = 0; i < count; i++) { + this->getChild(i)->updateFixedLayersPositions(viewport); + } +} + +void LayerAndroid::updatePositions() { + // apply the viewport to us + SkMatrix matrix; + if (!m_isFixed) { + // If we are defined as being relative to a fixed + // layer, we need to update our position... + if (m_isRelativeTo && m_relativeFixedLayer) { + this->setPosition(m_relativeFixedLayer->getPosition().fX + m_deltaPosition.fX, + m_relativeFixedLayer->getPosition().fY + m_deltaPosition.fY); + } + // turn our fields into a matrix. // // TODO: this should happen in the caller, and we should remove these @@ -278,13 +327,13 @@ void LayerAndroid::updatePositions(const SkRect& viewport) { matrix.preRotate(m_angleTransform); } matrix.preScale(m_scale.fX, m_scale.fY); + this->setMatrix(matrix); } - this->setMatrix(matrix); // now apply it to our children int count = this->countChildren(); for (int i = 0; i < count; i++) { - this->getChild(i)->updatePositions(viewport); + this->getChild(i)->updatePositions(); } } diff --git a/WebCore/platform/graphics/android/LayerAndroid.h b/WebCore/platform/graphics/android/LayerAndroid.h index 2554fb9..005ef3d 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.h +++ b/WebCore/platform/graphics/android/LayerAndroid.h @@ -103,6 +103,14 @@ public: m_isFixed = true; } + /** Call that method to position the layer relative to another one. + We can only be set relative to a fixed layer. + The parameter is not refcounted -- we only save its ID as we + need to reconnect with the correct layer once we copy the tree + to the UI. + */ + void setRelativeTo(LayerAndroid* container); + void setBackgroundColor(SkColor color); void setMaskLayer(LayerAndroid*); void setMasksToBounds(bool); @@ -126,14 +134,31 @@ public: void dumpLayers(FILE*, int indentLevel) const; void dumpToLog() const; - /** Call this with the current viewport (scrolling, zoom) to update its - position attribute, so that later calls like bounds() will report the - corrected position (assuming the layer had fixed-positioning). + /** Call this to be sure all fixed descendants correctly have + a pointer to their container layer before we try + to update the positions. The fixed layer we point to is + not refcounted (no need, it's already in the layers' tree). + + This call is recursive, so it should be called on the root of the + hierarchy. + */ + void ensureFixedLayersForDescendants(const LayerAndroid* rootLayer); + + /** Call this with the current viewport (scrolling, zoom) to update + the position of the fixed layers. + + This call is recursive, so it should be called on the root of the + hierarchy. + */ + void updateFixedLayersPositions(const SkRect& viewPort); + + /** Call this to update the position attribute, so that later calls + like bounds() will report the corrected position. This call is recursive, so it should be called on the root of the hierarchy. */ - void updatePositions(const SkRect& viewPort); + void updatePositions(); void clipArea(SkTDArray* region) const; const LayerAndroid* find(int x, int y) const; @@ -160,6 +185,7 @@ private: bool m_haveClip; bool m_doRotation; bool m_isFixed; + bool m_isRelativeTo; bool m_backgroundColorSet; SkLength m_fixedLeft; @@ -168,6 +194,8 @@ private: SkLength m_fixedBottom; int m_fixedWidth; int m_fixedHeight; + int m_relativeFixedLayerID; + SkPoint m_deltaPosition; SkPoint m_translation; SkPoint m_scale; @@ -181,6 +209,8 @@ private: DrawExtra* m_extra; int m_uniqueId; + LayerAndroid* m_relativeFixedLayer; + typedef SkLayer INHERITED; }; -- cgit v1.1