diff options
7 files changed, 123 insertions, 14 deletions
diff --git a/Source/WebCore/platform/graphics/android/Layer.cpp b/Source/WebCore/platform/graphics/android/Layer.cpp index 361cb4e..f650c52 100644 --- a/Source/WebCore/platform/graphics/android/Layer.cpp +++ b/Source/WebCore/platform/graphics/android/Layer.cpp @@ -124,23 +124,34 @@ void Layer::getLocalTransform(SkMatrix* matrix) const { matrix->preTranslate(-tx, -ty); } -void Layer::localToGlobal(SkMatrix* matrix) const { +void Layer::localToAncestor(const Layer* ancestor, SkMatrix* matrix) const { + if (this == ancestor) { + matrix->setIdentity(); + return; + } + getLocalTransform(matrix); + // Fixed position layers simply use the root layer's transform. if (shouldInheritFromRootTransform()) { + ASSERT(!ancestor); matrix->postConcat(getRootLayer()->getMatrix()); return; } - const Layer* layer = this; - while (layer->fParent != NULL) { - layer = layer->fParent; - + // Apply the local and child transforms for every layer between this layer + // and ancestor. + ASSERT(isAncestor(ancestor)); + for (const Layer* layer = this->fParent; layer != ancestor; layer = layer->fParent) { SkMatrix tmp; layer->getLocalTransform(&tmp); tmp.preConcat(layer->getChildrenMatrix()); matrix->postConcat(tmp); } + + // If ancestor is not the root layer, apply its child transformation too. + if (ancestor) + matrix->postConcat(ancestor->getChildrenMatrix()); } /////////////////////////////////////////////////////////////////////////////// @@ -204,3 +215,13 @@ void Layer::draw(SkCanvas* canvas, SkScalar opacity) { } } } + +bool Layer::isAncestor(const Layer* possibleAncestor) const { + if (!possibleAncestor) + return true; + for (const Layer* layer = getParent(); layer; layer = layer->getParent()) { + if (layer == possibleAncestor) + return true; + } + return false; +} diff --git a/Source/WebCore/platform/graphics/android/Layer.h b/Source/WebCore/platform/graphics/android/Layer.h index 7b27349..24302ce 100644 --- a/Source/WebCore/platform/graphics/android/Layer.h +++ b/Source/WebCore/platform/graphics/android/Layer.h @@ -104,7 +104,21 @@ public: from this layer's root parent to the layer itself. This is the matrix that is applied to the layer during drawing. */ - void localToGlobal(SkMatrix* matrix) const; + void localToGlobal(SkMatrix* matrix) const { localToAncestor(0, matrix); } + + /** Return, as a matrix, the transform that converts from this layer's local + space to the space of the given ancestor layer. Use NULL for ancestor to + represent the root layer. Note that this method must not be called on a + fixed position layer with ancestor != NULL. + + For non-fixed position layers, the following holds (in pseudo-code for + brevity) ... + SkMatrix localToAncestor = layer->localToAncestor(ancestor); + SkMatrix ancestorToGlobal = ancestor->localToAncestor(NULL); + SkMatrix localToGlobal = layer->localToGlobal(); + ASSERT(localToAncestor * ancestorToGlobal == localToGlobal); + */ + void localToAncestor(const Layer* ancestor, SkMatrix* matrix) const; // paint method @@ -115,12 +129,16 @@ public: void setHasOverflowChildren(bool value) { m_hasOverflowChildren = value; } + virtual bool contentIsScrollable() const { return false; } + protected: virtual void onDraw(SkCanvas*, SkScalar opacity); bool m_hasOverflowChildren; private: + bool isAncestor(const Layer*) const; + Layer* fParent; SkScalar m_opacity; SkSize m_size; diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.h b/Source/WebCore/platform/graphics/android/LayerAndroid.h index 31bb185..5bed7e5 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.h @@ -243,7 +243,6 @@ public: void bounds(SkRect*) const; - virtual bool contentIsScrollable() const { return false; } virtual LayerAndroid* copy() const { return new LayerAndroid(*this); } void needsRepaint() { m_pictureUsed++; } diff --git a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp index 2bb8b5c..bca2fd4 100644 --- a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.cpp @@ -31,6 +31,35 @@ void ScrollableLayerAndroid::getScrollRect(SkIRect* out) const out->fBottom = getSize().height() - m_scrollLimits.height(); } +bool ScrollableLayerAndroid::scrollRectIntoView(const SkIRect& rect) +{ + // Apply the local transform to the rect to get it relative to the parent + // layer. + SkMatrix localTransform; + getLocalTransform(&localTransform); + SkRect transformedRect; + transformedRect.set(rect); + localTransform.mapRect(&transformedRect); + + // Test left before right to prioritize left alignment if transformedRect is wider than + // visible area. + int x = m_scrollLimits.fLeft; + if (transformedRect.fLeft < m_scrollLimits.fLeft) + x = transformedRect.fLeft; + else if (transformedRect.fRight > m_scrollLimits.fRight) + x = transformedRect.fRight - std::max(m_scrollLimits.width(), transformedRect.width()); + + // Test top before bottom to prioritize top alignment if transformedRect is taller than + // visible area. + int y = m_scrollLimits.fTop; + if (transformedRect.fTop < m_scrollLimits.fTop) + y = transformedRect.fTop; + else if (transformedRect.fBottom > m_scrollLimits.fBottom) + y = transformedRect.fBottom - std::max(m_scrollLimits.height(), transformedRect.height()); + + return scrollTo(x - getPosition().fX, y - getPosition().fY); +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h index b59b4e1..a2486a5 100644 --- a/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/ScrollableLayerAndroid.h @@ -58,6 +58,13 @@ public: m_scrollLimits.set(x, y, x + width, y + height); } + // Given a rect in the layer, scrolls to bring the rect into view. Uses a + // lazy approach, whereby we scroll as little as possible to bring the + // entire rect into view. If the size of the rect exceeds that of the + // visible area of the layer, we favor the top and left of the rect. + // Returns whether or not any scrolling was required. + bool scrollRectIntoView(const SkIRect&); + friend void android::serializeLayer(LayerAndroid* layer, SkWStream* stream); friend LayerAndroid* android::deserializeLayer(SkStream* stream); diff --git a/Source/WebKit/android/nav/CachedRoot.h b/Source/WebKit/android/nav/CachedRoot.h index 45fc27a..65c6062 100644 --- a/Source/WebKit/android/nav/CachedRoot.h +++ b/Source/WebKit/android/nav/CachedRoot.h @@ -89,7 +89,7 @@ public: return pictureAt(xPtr, yPtr, 0); } void reset(); CachedHistory* rootHistory() const { return mHistory; } - const WebCore::LayerAndroid* rootLayer() const { return mRootLayer; } + WebCore::LayerAndroid* rootLayer() const { return mRootLayer; } bool scrollDelta(WebCore::IntRect& cursorRingBounds, Direction , int* delta); const WebCore::IntRect& scrolledBounds() const { return mScrolledBounds; } void setCursor(CachedFrame* , CachedNode* ); diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index 101e206..39a370c 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -339,13 +339,48 @@ void scrollToCurrentMatch() } SkRect matchBounds = m_findOnPage.currentMatchBounds(); - const LayerAndroid* rootLayer = getFrameCache(DontAllowNewer)->rootLayer(); - const Layer* layerContainingMatch = rootLayer->findById(m_findOnPage.currentMatchLayerId()); + LayerAndroid* rootLayer = getFrameCache(DontAllowNewer)->rootLayer(); + Layer* layerContainingMatch = rootLayer->findById(m_findOnPage.currentMatchLayerId()); ASSERT(layerContainingMatch); - // FIXME: If the match is in a scrollable layer or a child of such a layer, - // we may need to scroll these layers to make sure the match is visible. - // See http://b/5262656. + // If the match is in a fixed position layer, there's nothing to do. + if (layerContainingMatch->shouldInheritFromRootTransform()) + return; + + // If the match is in a scrollable layer or a descendant of such a layer, + // there may be a range of of scroll configurations that will make the + // current match visible. Our approach is the simplest possible. Starting at + // the layer in which the match is found, we move up the layer tree, + // scrolling any scrollable layers as little as possible to make sure that + // the current match is in view. This approach has the disadvantage that we + // may end up scrolling a larger number of elements than is necessary, which + // may be visually jarring. However, minimising the number of layers + // scrolled would complicate the code significantly. + + bool didScrollLayer = false; + for (Layer* layer = layerContainingMatch; layer; layer = layer->getParent()) { + ASSERT(layer->getParent() || layer == rootLayer); + + if (layer->contentIsScrollable()) { + // Convert the match location to layer's local space and scroll it. + // Repeatedly calling Layer::localToAncestor() is inefficient as + // each call repeats part of the calculation. It would be more + // efficient to maintain the transform here and update it on each + // iteration, but that would mean duplicating logic from + // Layer::localToAncestor() and would complicate things. + SkMatrix transform; + layerContainingMatch->localToAncestor(layer, &transform); + SkRect transformedMatchBounds; + transform.mapRect(&transformedMatchBounds, matchBounds); + SkIRect roundedTransformedMatchBounds; + transformedMatchBounds.roundOut(&roundedTransformedMatchBounds); + // Only ScrollableLayerAndroid returns true for contentIsScrollable(). + didScrollLayer |= static_cast<ScrollableLayerAndroid*>(layer)->scrollRectIntoView(roundedTransformedMatchBounds); + } + } + // Invalidate, as the call below to scroll the main page may be a no-op. + if (didScrollLayer) + viewInvalidate(); // Convert matchBounds to the global space so we can scroll the main page. SkMatrix transform; @@ -429,7 +464,7 @@ bool drawCursorPreamble(CachedRoot* root) } #if USE(ACCELERATED_COMPOSITING) if (node->isInLayer() && root->rootLayer()) { - LayerAndroid* layer = const_cast<LayerAndroid*>(root->rootLayer()); + LayerAndroid* layer = root->rootLayer(); SkRect visible; calcOurContentVisibleRect(&visible); layer->updateFixedLayersPositions(visible); |