diff options
-rw-r--r-- | WebCore/config.h | 3 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp | 80 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/GraphicsLayerAndroid.h | 4 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/LayerAndroid.cpp | 66 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/LayerAndroid.h | 26 | ||||
-rw-r--r-- | WebCore/rendering/RenderLayer.cpp | 26 | ||||
-rw-r--r-- | WebCore/rendering/RenderLayerBacking.cpp | 40 | ||||
-rw-r--r-- | WebCore/rendering/RenderLayerCompositor.cpp | 15 | ||||
-rw-r--r-- | WebKit/android/nav/WebView.cpp | 70 |
9 files changed, 300 insertions, 30 deletions
diff --git a/WebCore/config.h b/WebCore/config.h index 84e50fe..df4d06b 100644 --- a/WebCore/config.h +++ b/WebCore/config.h @@ -200,6 +200,9 @@ // Enable hit test with point plus a size #define ANDROID_HITTEST_WITHSIZE +// Enable scrollable divs in separate layers. This might be upstreamed to +// webkit.org but for now, it is just an Android feature. +#define ENABLE_ANDROID_OVERFLOW_SCROLL 1 #endif /* PLATFORM(ANDROID) */ diff --git a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index 6faee17..a5ca972 100644 --- a/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -293,6 +293,9 @@ void GraphicsLayerAndroid::setSize(const FloatSize& size) MLOG("(%x) setSize (%.2f,%.2f)", this, size.width(), size.height()); GraphicsLayer::setSize(size); m_contentLayer->setSize(size.width(), size.height()); + m_contentLayer->setForegroundClip( + SkRect::MakeWH(SkFloatToScalar(size.width()), + SkFloatToScalar(size.height()))); updateFixedPosition(); askForSync(); } @@ -407,24 +410,58 @@ void GraphicsLayerAndroid::setNeedsDisplay() setNeedsDisplayInRect(rect); } +// Helper to set and clear the painting phase as well as auto restore the +// original phase. +class PaintingPhase { +public: + PaintingPhase(GraphicsLayer* layer) + : m_layer(layer) + , m_originalPhase(layer->paintingPhase()) {} + + ~PaintingPhase() { + m_layer->setPaintingPhase(m_originalPhase); + } + + void set(GraphicsLayerPaintingPhase phase) { + m_layer->setPaintingPhase(phase); + } + + void clear(GraphicsLayerPaintingPhase phase) { + m_layer->setPaintingPhase( + (GraphicsLayerPaintingPhase) (m_originalPhase & ~phase)); + } +private: + GraphicsLayer* m_layer; + GraphicsLayerPaintingPhase m_originalPhase; +}; + bool GraphicsLayerAndroid::repaint() { LOG("(%x) repaint(), gPaused(%d) m_needsRepaint(%d) m_haveContents(%d) ", this, gPaused, m_needsRepaint, m_haveContents); if (!gPaused && m_haveContents && m_needsRepaint && !m_haveImage) { - SkAutoPictureRecord arp(m_contentLayer->recordContext(), m_size.width(), m_size.height()); - SkCanvas* recordingCanvas = arp.getRecordingCanvas(); - - if (!recordingCanvas) - return false; - - PlatformGraphicsContext pgc(recordingCanvas, 0); - GraphicsContext gc(&pgc); - // with SkPicture, we request the entire layer's content. - IntRect r(0, 0, m_contentLayer->getWidth(), m_contentLayer->getHeight()); - paintGraphicsLayerContents(gc, r); + IntRect layerBounds(0, 0, m_size.width(), m_size.height()); + + if (m_contentsRect.width() > m_size.width() || + m_contentsRect.height() > m_size.height()) { + PaintingPhase phase(this); + // Paint the background into a separate context. + phase.set(GraphicsLayerPaintBackground); + if (!paintContext(m_contentLayer->recordContext(), layerBounds)) + return false; + // Paint everything else into the main recording canvas. + phase.clear(GraphicsLayerPaintBackground); + if (!paintContext(m_contentLayer->foregroundContext(), + m_contentsRect)) + return false; + } else { + // If there is no contents clip, we can draw everything into one + // picture. + if (!paintContext(m_contentLayer->recordContext(), layerBounds)) + return false; + } TLOG("(%x) repaint() on (%.2f,%.2f) contentlayer(%.2f,%.2f,%.2f,%.2f)paintGraphicsLayer called!", this, m_size.width(), m_size.height(), @@ -441,6 +478,22 @@ bool GraphicsLayerAndroid::repaint() return false; } +bool GraphicsLayerAndroid::paintContext(SkPicture* context, + const IntRect& rect) +{ + SkAutoPictureRecord arp(context, rect.width(), rect.height()); + SkCanvas* canvas = arp.getRecordingCanvas(); + + if (canvas == 0) + return false; + + PlatformGraphicsContext platformContext(canvas, 0); + GraphicsContext graphicsContext(&platformContext); + + paintGraphicsLayerContents(graphicsContext, rect); + return true; +} + void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect) { for (unsigned int i = 0; i < m_children.size(); i++) { @@ -874,6 +927,11 @@ void GraphicsLayerAndroid::notifyClientAnimationStarted() } } +void GraphicsLayerAndroid::setContentsClip(const IntRect& clip) +{ + m_contentLayer->setForegroundClip(clip); +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/android/GraphicsLayerAndroid.h b/WebCore/platform/graphics/android/GraphicsLayerAndroid.h index 54a035b..7482969 100644 --- a/WebCore/platform/graphics/android/GraphicsLayerAndroid.h +++ b/WebCore/platform/graphics/android/GraphicsLayerAndroid.h @@ -115,6 +115,8 @@ public: static int instancesCount(); + void setContentsClip(const IntRect& clip); + private: void askForSync(); @@ -129,6 +131,8 @@ private: bool repaint(); void needsNotifyClient(); + bool paintContext(SkPicture* context, const IntRect& rect); + bool m_needsSyncChildren; bool m_needsSyncMask; bool m_needsRepaint; diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index 5aaa243..b80171e 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -12,6 +12,7 @@ #include "SkPicture.h" #include <wtf/CurrentTime.h> + #define LAYER_DEBUG // Add diagonals for debugging #undef LAYER_DEBUG @@ -51,6 +52,7 @@ LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(), m_doRotation(false), m_isFixed(false), m_recordingPicture(0), + m_foregroundPicture(0), m_contentsImage(0), m_extra(0), m_uniqueId(++gUniqueId) @@ -59,6 +61,8 @@ LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(), m_translation.set(0, 0); m_scale.set(1, 1); m_backgroundColor = 0; + m_foregroundClip.setEmpty(); + m_foregroundLocation.set(0, 0); gDebugLayerAndroidInstances++; } @@ -90,7 +94,12 @@ LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer), m_fixedRect = layer.m_fixedRect; m_recordingPicture = layer.m_recordingPicture; + m_foregroundPicture = layer.m_foregroundPicture; SkSafeRef(m_recordingPicture); + SkSafeRef(m_foregroundPicture); + + m_foregroundClip = layer.m_foregroundClip; + m_foregroundLocation = layer.m_foregroundLocation; for (int i = 0; i < layer.countChildren(); i++) addChild(new LayerAndroid(*layer.getChild(i)))->unref(); @@ -102,29 +111,12 @@ LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer), gDebugLayerAndroidInstances++; } -LayerAndroid::LayerAndroid(SkPicture* picture) : SkLayer(), - m_isRootLayer(true), - m_haveClip(false), - m_doRotation(false), - m_isFixed(false), - m_recordingPicture(picture), - m_contentsImage(0), - m_extra(0), - m_uniqueId(-1) -{ - m_angleTransform = 0; - m_translation.set(0, 0); - m_scale.set(1, 1); - m_backgroundColor = 0; - SkSafeRef(m_recordingPicture); - gDebugLayerAndroidInstances++; -} - LayerAndroid::~LayerAndroid() { removeChildren(); m_contentsImage->safeUnref(); m_recordingPicture->safeUnref(); + m_foregroundPicture->safeUnref(); m_animations.clear(); gDebugLayerAndroidInstances--; } @@ -339,6 +331,14 @@ void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity) { canvas->drawBitmapRect(m_contentsImage->bitmap(), 0, dest); } else { canvas->drawPicture(*m_recordingPicture); + if (m_foregroundPicture) { + canvas->save(); + canvas->clipRect(m_foregroundClip); + canvas->translate(-m_foregroundLocation.fX, + -m_foregroundLocation.fY); + canvas->drawPicture(*m_foregroundPicture); + canvas->restore(); + } } if (m_extra) m_extra->draw(canvas, this); @@ -371,6 +371,36 @@ SkPicture* LayerAndroid::recordContext() return 0; } +SkPicture* LayerAndroid::foregroundContext() +{ + // Always create a new picture since this method is called only when + // recording the foreground picture. + m_foregroundPicture = new SkPicture(); + return m_foregroundPicture; +} + +bool LayerAndroid::contentIsScrollable() const { + return m_foregroundPicture != 0 && + (getWidth() < SkIntToScalar(m_foregroundPicture->width()) || + getHeight() < SkIntToScalar(m_foregroundPicture->height())); +} + +bool LayerAndroid::scrollBy(int dx, int dy) { + if (m_foregroundPicture == 0) + return false; + SkScalar maxScrollX = SkIntToScalar(m_foregroundPicture->width()) - getWidth(); + SkScalar maxScrollY = SkIntToScalar(m_foregroundPicture->height()) - getHeight(); + SkScalar x = m_foregroundLocation.fX + dx; + SkScalar y = m_foregroundLocation.fY + dy; + x = SkScalarClampMax(x, maxScrollX); + y = SkScalarClampMax(y, maxScrollY); + if (x != m_foregroundLocation.fX || y != m_foregroundLocation.fY) { + m_foregroundLocation.set(x, y); + return true; + } + return false; +} + bool LayerAndroid::prepareContext(bool force) { if (!m_isRootLayer) { diff --git a/WebCore/platform/graphics/android/LayerAndroid.h b/WebCore/platform/graphics/android/LayerAndroid.h index b98d4dd..2c11958 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.h +++ b/WebCore/platform/graphics/android/LayerAndroid.h @@ -77,7 +77,6 @@ class LayerAndroid : public SkLayer { public: LayerAndroid(bool isRootLayer); LayerAndroid(const LayerAndroid& layer); - LayerAndroid(SkPicture* ); virtual ~LayerAndroid(); static int instancesCount(); @@ -127,6 +126,21 @@ public: SkPicture* recordContext(); + // The foreground context is used to draw overflow scroll content. + SkPicture* foregroundContext(); + + // The foreground clip is set when there is content within the node that + // can be scrolled (i.e. a div with overflow:scroll). + void setForegroundClip(const SkRect& clip) { + m_foregroundClip = clip; + } + bool contentIsScrollable() const; + + // Returns true if the content position has changed. + bool scrollBy(int dx, int dy); + const SkPoint& scrollPosition() const { return m_foregroundLocation; } + void setScrollPosition(const SkPoint& pos) { m_foregroundLocation = pos; } + void addAnimation(PassRefPtr<AndroidAnimation> anim); void removeAnimation(const String& name); bool evaluateAnimations() const; @@ -215,6 +229,16 @@ private: // We do this as if the layer only contains an image, directly compositing // it is a much faster method than using m_recordingPicture. SkPicture* m_recordingPicture; + + // m_foregroundPicture is set only when compositing a scrollable div. It + // contains the contents minus the background and border which is drawn + // first by the rendering tree. When we draw the layer, we draw + // m_recordingPicture (containing the background + border) first and then + // clip to m_foregroundClip and draw m_foregroundPicture. + SkPicture* m_foregroundPicture; + SkRect m_foregroundClip; + SkPoint m_foregroundLocation; + SkBitmapRef* m_contentsImage; typedef HashMap<String, RefPtr<AndroidAnimation> > KeyframesMap; diff --git a/WebCore/rendering/RenderLayer.cpp b/WebCore/rendering/RenderLayer.cpp index d9d2be1..75daa94 100644 --- a/WebCore/rendering/RenderLayer.cpp +++ b/WebCore/rendering/RenderLayer.cpp @@ -3237,8 +3237,17 @@ void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& pa // Update the clip rects that will be passed to child layers. if (renderer()->hasOverflowClip() || renderer()->hasClip()) { // This layer establishes a clip of some kind. +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (renderer()->hasOverflowClip()) { + RenderBox* box = toRenderBox(renderer()); + layerBounds = backgroundRect = foregroundRect = outlineRect = + IntRect(x, y, box->borderLeft() + box->borderRight() + m_scrollWidth, + box->borderTop() + box->borderBottom() + m_scrollHeight); + } +#else if (renderer()->hasOverflowClip()) foregroundRect.intersect(toRenderBox(renderer())->overflowClipRect(x, y)); +#endif if (renderer()->hasClip()) { // Clip applies to *us* as well, so go ahead and update the damageRect. IntRect newPosClip = toRenderBox(renderer())->clipRect(x, y); @@ -3748,8 +3757,25 @@ bool RenderLayer::shouldBeNormalFlowOnly() const && !isTransparent(); } +#if ENABLE(ANDROID_OVERFLOW_SCROLL) +static bool hasOverflowScroll(const RenderLayer* l) +{ + RenderLayer* layer = const_cast<RenderLayer*>(l); + RenderBox* box = layer->renderBox(); + EOverflow x = box->style()->overflowX(); + EOverflow y = box->style()->overflowY(); + return (x == OAUTO || x == OSCROLL || y == OAUTO || y == OSCROLL) && + (layer->scrollWidth() > box->clientWidth() || + layer->scrollHeight() > box->clientHeight()); +} +#endif + bool RenderLayer::isSelfPaintingLayer() const { +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (hasOverflowScroll(this)) + return true; +#endif return !isNormalFlowOnly() || renderer()->hasReflection() || renderer()->hasMask() || renderer()->isTableRow() || renderer()->isVideo() || renderer()->isEmbeddedObject() || renderer()->isRenderIFrame(); } diff --git a/WebCore/rendering/RenderLayerBacking.cpp b/WebCore/rendering/RenderLayerBacking.cpp index ae01799..1153727 100644 --- a/WebCore/rendering/RenderLayerBacking.cpp +++ b/WebCore/rendering/RenderLayerBacking.cpp @@ -55,6 +55,10 @@ #include "RenderLayerBacking.h" +#if ENABLE(ANDROID_OVERFLOW_SCROLL) +#include "GraphicsLayerAndroid.h" +#endif + using namespace std; namespace WebCore { @@ -397,6 +401,18 @@ void RenderLayerBacking::updateGraphicsLayerGeometry() } m_graphicsLayer->setContentsRect(contentsBox()); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (m_owningLayer->hasOverflowControls()) { + RenderBoxModelObject* box = renderer(); + IntRect clip = compositedBounds(); + IntPoint location = clip.location(); + location.move(box->borderLeft(), box->borderTop()); + clip.setLocation(location); + clip.setWidth(clip.width() - box->borderLeft() - box->borderRight()); + clip.setHeight(clip.height() - box->borderTop() - box->borderBottom()); + static_cast<GraphicsLayerAndroid*>(m_graphicsLayer.get())->setContentsClip(clip); + } +#endif updateDrawsContent(); updateAfterWidgetResize(); } @@ -819,6 +835,24 @@ IntRect RenderLayerBacking::contentsBox() const } else #endif contentsRect = toRenderBox(renderer())->contentBoxRect(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (m_owningLayer->hasOverflowControls()) { + // Update the contents rect to have the width and height of the entire + // contents. This rect is only used by the platform GraphicsLayer and + // the position of the rectangle is ignored. Use the layer's scroll + // width/height (which contain the padding). + RenderBox* box = toRenderBox(renderer()); + contentsRect.setWidth(box->borderLeft() + box->borderRight() + + m_owningLayer->scrollWidth()); + contentsRect.setHeight(box->borderTop() + box->borderBottom() + + m_owningLayer->scrollHeight()); + // Move the contents rect by the padding since + // RenderBox::contentBoxRect includes the padding. The end result is + // to have a box representing the entires contents plus border and + // padding. This will be the size of the underlying picture. + contentsRect.setLocation(IntPoint(0, 0)); + } +#endif IntSize contentOffset = contentOffsetInCompostingLayer(); contentsRect.move(contentOffset); @@ -1053,6 +1087,12 @@ void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& co // We have to use the same root as for hit testing, because both methods // can compute and cache clipRects. IntRect enclosingBBox = compositedBounds(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (m_owningLayer->hasOverflowControls()) { + enclosingBBox.setSize(contentsBox().size()); + enclosingBBox.setLocation(m_compositedBounds.location()); + } +#endif IntRect clipRect(clip); diff --git a/WebCore/rendering/RenderLayerCompositor.cpp b/WebCore/rendering/RenderLayerCompositor.cpp index c485acc..1a28c37 100644 --- a/WebCore/rendering/RenderLayerCompositor.cpp +++ b/WebCore/rendering/RenderLayerCompositor.cpp @@ -1153,6 +1153,18 @@ bool RenderLayerCompositor::requiresCompositingForMobileSites(const RenderLayer* } #endif +#if ENABLE(ANDROID_OVERFLOW_SCROLL) +static bool requiresCompositingForOverflowScroll(const RenderLayer* l) { + RenderLayer* layer = const_cast<RenderLayer*>(l); + RenderBox* box = layer->renderBox(); + EOverflow x = box->style()->overflowX(); + EOverflow y = box->style()->overflowY(); + return (x == OAUTO || x == OSCROLL || y == OAUTO || y == OSCROLL) && + (layer->scrollWidth() > box->contentWidth() || + layer->scrollHeight() > box->contentHeight()); +} +#endif + // Note: this specifies whether the RL needs a compositing layer for intrinsic reasons. // Use needsToBeComposited() to determine if a RL actually needs a compositing layer. // static @@ -1167,6 +1179,9 @@ bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) c return requiresCompositingForTransform(renderer) #if PLATFORM(ANDROID) || requiresCompositingForMobileSites(layer) +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + || requiresCompositingForOverflowScroll(layer) +#endif #endif || requiresCompositingForVideo(renderer) || requiresCompositingForCanvas(renderer) diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index 5154b42..aad3501 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -920,6 +920,19 @@ bool motionUp(int x, int y, int slop) return pageScrolled; } +const LayerAndroid* scrollableLayer(int x, int y) +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) && USE(ACCELERATED_COMPOSITING) + const LayerAndroid* root = compositeRoot(); + if (!root) + return 0; + const LayerAndroid* result = root->find(x, y); + if (result != 0 && result->contentIsScrollable()) + return result; +#endif + return 0; +} + int getBlockLeftEdge(int x, int y, float scale) { CachedRoot* root = getFrameCache(AllowNewer); @@ -1200,8 +1213,27 @@ LayerAndroid* compositeRoot() const return 0; } +static void copyScrollPositionRecursive(const LayerAndroid* from, + LayerAndroid* root) +{ + if (!from || !root) + return; + for (int i = 0; i < from->countChildren(); i++) { + const LayerAndroid* l = from->getChild(i); + if (l->contentIsScrollable()) { + LayerAndroid* match = + const_cast<LayerAndroid*>(root->findById(l->uniqueId())); + if (match != 0) + match->setScrollPosition(l->scrollPosition()); + } + copyScrollPositionRecursive(l, root); + } +} + void setBaseLayer(BaseLayerAndroid* layer) { + copyScrollPositionRecursive(compositeRoot(), + static_cast<LayerAndroid*>(layer->getChild(0))); delete m_baseLayer; m_baseLayer = layer; CachedRoot* root = getFrameCache(DontAllowNewer); @@ -2064,6 +2096,40 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) #endif } +static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y) +{ + WebView* view = GET_NATIVE_VIEW(env, jwebview); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return (int) view->scrollableLayer(x, y); +} + +static bool validLayer(const LayerAndroid* root, const LayerAndroid* layer) { + if (root == layer) + return true; + for (int i = 0; i < root->countChildren(); i++) { + const LayerAndroid* l = root->getChild(i); + if (validLayer(l, layer)) + return true; + } + return false; +} + +static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint pLayer, jint dx, + jint dy) +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + WebView* view = GET_NATIVE_VIEW(env, obj); + const LayerAndroid* root = view->compositeRoot(); + LayerAndroid* layer = (LayerAndroid*) pLayer; + if (!validLayer(root, layer)) { + return false; + } + LOG_ASSERT(layer, "layer not set in %s", __FUNCTION__); + return layer->scrollBy(dx, dy); +#endif + return false; +} + /* * JNI registration */ @@ -2220,6 +2286,10 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeWordSelection }, { "nativeGetBlockLeftEdge", "(IIF)I", (void*) nativeGetBlockLeftEdge }, + { "nativeScrollableLayer", "(II)I", + (void*) nativeScrollableLayer }, + { "nativeScrollLayer", "(III)Z", + (void*) nativeScrollLayer }, }; int register_webview(JNIEnv* env) |