summaryrefslogtreecommitdiffstats
path: root/WebCore
diff options
context:
space:
mode:
authorPatrick Scott <phanna@android.com>2010-07-16 12:43:07 -0400
committerPatrick Scott <phanna@android.com>2010-07-22 08:16:26 -0400
commita8d0e5a36718ee59b84a577053458bded49e369a (patch)
tree510917186d198758676037c96296405d0a1b83f1 /WebCore
parent0286594a8b68c879fc5c11deb6f14c4ccadeef2a (diff)
downloadexternal_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.h3
-rw-r--r--WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp80
-rw-r--r--WebCore/platform/graphics/android/GraphicsLayerAndroid.h4
-rw-r--r--WebCore/platform/graphics/android/LayerAndroid.cpp66
-rw-r--r--WebCore/platform/graphics/android/LayerAndroid.h26
-rw-r--r--WebCore/rendering/RenderLayer.cpp26
-rw-r--r--WebCore/rendering/RenderLayerBacking.cpp40
-rw-r--r--WebCore/rendering/RenderLayerCompositor.cpp15
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)