diff options
author | Patrick Scott <phanna@android.com> | 2010-07-16 12:43:07 -0400 |
---|---|---|
committer | Patrick Scott <phanna@android.com> | 2010-07-22 08:16:26 -0400 |
commit | a8d0e5a36718ee59b84a577053458bded49e369a (patch) | |
tree | 510917186d198758676037c96296405d0a1b83f1 /WebCore | |
parent | 0286594a8b68c879fc5c11deb6f14c4ccadeef2a (diff) | |
download | external_webkit-a8d0e5a36718ee59b84a577053458bded49e369a.zip external_webkit-a8d0e5a36718ee59b84a577053458bded49e369a.tar.gz external_webkit-a8d0e5a36718ee59b84a577053458bded49e369a.tar.bz2 |
Enable scrollable divs.
Force a composite layer when the style says the content is scrollable.
Record the border and background in the main content picture. When
the contents of the layer are bigger than the size, record the
foreground contents in a separate picture which is clipped by the
border and size.
When updating the base layer, remember the scroll position of each
layer and update the new layer with the same position.
Bug: 1566791
Change-Id: If440e4f215db6bda9df32a781d754d1f5a238162
Diffstat (limited to 'WebCore')
-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 |
8 files changed, 230 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) |