OSDN Git Service

rewrite select text and others for layers
authorCary Clark <cary@android.com>
Tue, 26 Oct 2010 14:56:53 +0000 (10:56 -0400)
committerCary Clark <cary@android.com>
Wed, 27 Oct 2010 15:35:46 +0000 (11:35 -0400)
Layers contain pictures, and draw them offset from the top of
the page. Several readers of pictures need to account for this
displacement when computing what part of the picture intersects
a tap on the screen.

The tap may not correspond to the first layer that intersects
it, so all layers must be checked to find the best match. The
root layer usually draws everywhere, so for a match to correspond
to the root, the match must additionally intersect text.

Layers may create offscreen bitmaps when drawing to correctly
alpha blend the results to the screen, but this causes the items
in the bitmap to draw to an unexpected location when the picture
is treated as a spatial database. To get around this, call the
SkCanvas::save() from the overridden saveLayer() to push and
pop the canvas layer state without creating an offscreen.

WebCore/platform/graphics/android/LayerAndroid.cpp
WebCore/platform/graphics/android/LayerAndroid.h
- In find(), iterate through all children, instead of stopping
  on the first match.
- Check to see if the child actually draws at the desired location,
  and if it draws text there as well.
- Specify a slop factor to allow for inaccuracies in touch.
- Check the root for text before checking the children.

WebKit/android/nav/CachedFrame.cpp
WebKit/android/nav/CachedFrame.h
- Modify the (x,y) co-ordinate by the layer's offset, when
  finding the picture corresponding to a point.

WebKit/android/nav/CachedLayer.cpp
WebKit/android/nav/CachedLayer.h
- More plumbing to adjust the point if the picture is contained
  in an offset layer.

WebKit/android/nav/CachedRoot.cpp
WebKit/android/nav/CachedRoot.h
- Correct the (x,y) locations by the layer offset.
- Add some debugging (disabled by default)

WebKit/android/nav/ParsedCanvas.h
- One stop shopping that calls save() from saveLayer().
- Reset the bounder to null to balance its ref count.

WebKit/android/nav/SelectText.cpp
WebKit/android/nav/SelectText.h
- Rearrange the way pictures are tracked. Record the picture
  corresponding to the input location when the selection starts,
  requiring that the picture remain unchanged as the selection
  extends.
- Only draw adornments for when the corresponding picture is
  drawn. This fixes a Gmail specific problem, where the layers
  come and go as the page scrolls.
- Always use the supplied visible bounds instead of computing
  it from the canvas.
- Correct location points by layer offsets.
- Add to the picture ref count so it can't be deleted during
  selection.

WebKit/android/nav/WebView.cpp
- Simplify visibleRect code.
- Simplify all SelectText interfaces.

bug:3114609
Change-Id: I43dc3252fc86c4b6500edcd650126b2559f530e3

12 files changed:
WebCore/platform/graphics/android/LayerAndroid.cpp
WebCore/platform/graphics/android/LayerAndroid.h
WebKit/android/nav/CachedFrame.cpp
WebKit/android/nav/CachedFrame.h
WebKit/android/nav/CachedLayer.cpp
WebKit/android/nav/CachedLayer.h
WebKit/android/nav/CachedRoot.cpp
WebKit/android/nav/CachedRoot.h
WebKit/android/nav/ParseCanvas.h [new file with mode: 0644]
WebKit/android/nav/SelectText.cpp
WebKit/android/nav/SelectText.h
WebKit/android/nav/WebView.cpp

index 09fe135..5210171 100644 (file)
@@ -5,8 +5,9 @@
 
 #include "AndroidAnimation.h"
 #include "DrawExtra.h"
+#include "ParseCanvas.h"
 #include "SkBitmapRef.h"
-#include "SkCanvas.h"
+#include "SkBounder.h"
 #include "SkDrawFilter.h"
 #include "SkPaint.h"
 #include "SkPicture.h"
@@ -251,18 +252,112 @@ void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
         getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
 }
 
-const LayerAndroid* LayerAndroid::find(int x, int y) const
-{
-    for (int i = 0; i < countChildren(); i++) {
-        const LayerAndroid* found = getChild(i)->find(x, y);
-        if (found)
-            return found;
+class FindCheck : public SkBounder {
+public:
+    FindCheck()
+        : m_drew(false)
+        , m_drewText(false)
+    {
+    }
+
+    bool drew() const { return m_drew; }
+    bool drewText() const { return m_drewText; }
+    void reset() { m_drew = m_drewText = false; }
+
+protected:
+    virtual bool onIRect(const SkIRect& )
+    {
+        m_drew = true;
+        return false;
+    }
+
+    virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& )
+    {
+        m_drewText = true;
+        return false;
+    }
+
+    bool m_drew;
+    bool m_drewText;
+};
+
+class FindCanvas : public ParseCanvas {
+public:
+    void draw(SkPicture* picture, SkScalar offsetX, SkScalar offsetY)
+    {
+        save();
+        translate(-offsetX, -offsetY);
+        picture->draw(this);
+        restore();
+    }
+};
+
+class LayerAndroidFindState {
+public:
+    static const int TOUCH_SLOP = 10;
+
+    LayerAndroidFindState(int x, int y)
+        : m_x(x)
+        , m_y(y)
+        , m_best(0)
+    {
+        m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, TOUCH_SLOP * 2,
+             TOUCH_SLOP * 2);
+        m_checker.setBounder(&m_findCheck);
+        m_checker.setBitmapDevice(m_bitmap);
+    }
+
+    const LayerAndroid* best() const { return m_best; }
+
+    bool drew(SkPicture* picture, const SkRect& localBounds) {
+        m_findCheck.reset();
+        SkScalar localX = SkIntToScalar(m_x - TOUCH_SLOP) - localBounds.fLeft;
+        SkScalar localY = SkIntToScalar(m_y - TOUCH_SLOP) - localBounds.fTop;
+        m_checker.draw(picture, localX, localY);
+        return m_findCheck.drew();
     }
+
+    bool drewText() { return m_findCheck.drewText(); }
+
+    void setBest(const LayerAndroid* best) { m_best = best; }
+    int x() const { return m_x; }
+    int y() const { return m_y; }
+
+protected:
+    int m_x;
+    int m_y;
+    const LayerAndroid* m_best;
+    FindCheck m_findCheck;
+    SkBitmap m_bitmap;
+    FindCanvas m_checker;
+};
+
+void LayerAndroid::findInner(LayerAndroidFindState& state) const
+{
+    int x = state.x();
+    int y = state.y();
+    for (int i = 0; i < countChildren(); i++)
+        getChild(i)->findInner(state);
     SkRect localBounds;
     bounds(&localBounds);
-    if (localBounds.contains(x, y))
-        return this;
-    return 0;
+    if (!localBounds.contains(x, y))
+        return;
+    if (!m_recordingPicture)
+        return;
+    if (!state.drew(m_recordingPicture, localBounds))
+        return;
+    state.setBest(this); // set last match (presumably on top)
+}
+
+const LayerAndroid* LayerAndroid::find(int x, int y, SkPicture* root) const
+{
+    LayerAndroidFindState state(x, y);
+    SkRect rootBounds;
+    rootBounds.setEmpty();
+    if (state.drew(root, rootBounds) && state.drewText())
+        return 0; // use the root picture only if it contains the text
+    findInner(state);
+    return state.best();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
index 245d224..fb6d78c 100644 (file)
@@ -72,6 +72,7 @@ struct SkLength {
 namespace WebCore {
 
 class AndroidAnimation;
+class LayerAndroidFindState;
 
 class LayerAndroid : public SkLayer {
 
@@ -179,7 +180,7 @@ public:
     void updatePositions();
 
     void clipArea(SkTDArray<SkRect>* region) const;
-    const LayerAndroid* find(int x, int y) const;
+    const LayerAndroid* find(int x, int y, SkPicture* root) const;
     const LayerAndroid* findById(int uniqueID) const;
     LayerAndroid* getChild(int index) const {
         return static_cast<LayerAndroid*>(this->INHERITED::getChild(index));
@@ -195,6 +196,7 @@ public:
     */
     void setContentsImage(SkBitmapRef* img);
 
+    void bounds(SkRect* ) const;
 protected:
     virtual void onDraw(SkCanvas*, SkScalar opacity);
 
@@ -202,7 +204,8 @@ private:
 #if DUMP_NAV_CACHE
     friend class CachedLayer::Debug; // debugging access only
 #endif
-    void bounds(SkRect* ) const;
+
+    void findInner(LayerAndroidFindState& ) const;
     bool prepareContext(bool force = false);
     void clipInner(SkTDArray<SkRect>* region, const SkRect& local) const;
 
index 81ef299..ced87e0 100644 (file)
@@ -1015,6 +1015,19 @@ SkPicture* CachedFrame::picture(const CachedNode* node) const
     return mRoot->mPicture;
 }
 
+SkPicture* CachedFrame::picture(const CachedNode* node, int* xPtr, int* yPtr) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+    if (node->isInLayer()) {
+        const CachedLayer* cachedLayer = layer(node);
+        const LayerAndroid* rootLayer = mRoot->rootLayer();
+        cachedLayer->toLocal(rootLayer, xPtr, yPtr);
+        return cachedLayer->picture(rootLayer);
+    }
+#endif
+    return mRoot->mPicture;
+}
+
 void CachedFrame::resetClippedOut()
 {
     for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++)
index bd47421..fdee849 100644 (file)
@@ -131,6 +131,7 @@ public:
     const CachedFrame* parent() const { return mParent; }
     CachedFrame* parent() { return mParent; }
     SkPicture* picture(const CachedNode* ) const;
+    SkPicture* picture(const CachedNode* , int* xPtr, int* yPtr) const;
     void resetLayers();
     bool sameFrame(const CachedFrame* ) const;
     void removeLast() { mCachedNodes.removeLast(); }
index c4293a5..f6b1859 100644 (file)
@@ -168,6 +168,18 @@ SkPicture* CachedLayer::picture(const LayerAndroid* root) const
     return aLayer->picture();
 }
 
+void CachedLayer::toLocal(const LayerAndroid* root, int* xPtr, int* yPtr) const
+{
+    const LayerAndroid* aLayer = layer(root);
+    if (!aLayer)
+        return;
+    DBG_NAV_LOGD("root=%p aLayer=%p [%d]", root, aLayer, aLayer->uniqueId());
+    SkRect localBounds;
+    aLayer->bounds(&localBounds);
+    *xPtr -= localBounds.fLeft;
+    *yPtr -= localBounds.fTop;
+}
+
 #if DUMP_NAV_CACHE
 
 CachedLayer* CachedLayer::Debug::base() const {
index 0a56ea1..a6e4e89 100644 (file)
@@ -55,6 +55,7 @@ public:
     const LayerAndroid* layer(const LayerAndroid* root) const;
     IntRect localBounds(const LayerAndroid* root, const IntRect& bounds) const;
     SkPicture* picture(const LayerAndroid* root) const;
+    void toLocal(const LayerAndroid* root, int* xPtr, int* yPtr) const;
     void reset() { mLayer = 0; }
     void setCachedNodeIndex(int index) { mCachedNodeIndex = index; }
     void setOffset(const IntPoint& offset) { mOffset = offset; }
index e86999c..3340792 100644 (file)
@@ -31,6 +31,7 @@
 #include "FindCanvas.h"
 #include "FloatRect.h"
 #include "LayerAndroid.h"
+#include "ParseCanvas.h"
 #include "SkBitmap.h"
 #include "SkBounder.h"
 #include "SkPixelRef.h"
@@ -225,7 +226,7 @@ public:
     SkIRect mLastAll;
 };
 
-class BoundsCanvas : public SkCanvas {
+class BoundsCanvas : public ParseCanvas {
 public:
 
     BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) {
@@ -233,36 +234,32 @@ public:
         setBounder(bounder);
     }
 
-    virtual ~BoundsCanvas() {
-        setBounder(NULL);
-    }
-
     virtual void drawPaint(const SkPaint& paint) {
         mBounder.setType(CommonCheck::kDrawPaint_Type);
-        SkCanvas::drawPaint(paint);
+        INHERITED::drawPaint(paint);
     }
 
     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                             const SkPaint& paint) {
         mBounder.setType(CommonCheck::kDrawPoints_Type);
-        SkCanvas::drawPoints(mode, count, pts, paint);
+        INHERITED::drawPoints(mode, count, pts, paint);
     }
 
     virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
         mBounder.setType(CommonCheck::kDrawRect_Type);
-        SkCanvas::drawRect(rect, paint);
+        INHERITED::drawRect(rect, paint);
     }
 
     virtual void drawPath(const SkPath& path, const SkPaint& paint) {
         mBounder.setType(CommonCheck::kDrawPath_Type);
-        SkCanvas::drawPath(path, paint);
+        INHERITED::drawPath(path, paint);
     }
 
     virtual void commonDrawBitmap(const SkBitmap& bitmap,
                               const SkMatrix& matrix, const SkPaint& paint) {
         mBounder.setType(CommonCheck::kDrawBitmap_Type);
         mBounder.setIsOpaque(bitmap.isOpaque());
-        SkCanvas::commonDrawBitmap(bitmap, matrix, paint);
+        INHERITED::commonDrawBitmap(bitmap, matrix, paint);
     }
 
     virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
@@ -270,14 +267,14 @@ public:
         mBounder.setType(CommonCheck::kDrawSprite_Type);
         mBounder.setIsOpaque(bitmap.isOpaque() &&
             (!paint || paint->getAlpha() == 255));
-        SkCanvas::drawSprite(bitmap, left, top, paint);
+        INHERITED::drawSprite(bitmap, left, top, paint);
     }
 
     virtual void drawText(const void* text, size_t byteLength, SkScalar x,
                           SkScalar y, const SkPaint& paint) {
         mBounder.setEmpty();
         mBounder.setType(CommonCheck::kDrawGlyph_Type);
-        SkCanvas::drawText(text, byteLength, x, y, paint);
+        INHERITED::drawText(text, byteLength, x, y, paint);
         mBounder.doRect(CommonCheck::kDrawText_Type);
     }
 
@@ -285,7 +282,7 @@ public:
                              const SkPoint pos[], const SkPaint& paint) {
         mBounder.setEmpty();
         mBounder.setType(CommonCheck::kDrawGlyph_Type);
-        SkCanvas::drawPosText(text, byteLength, pos, paint);
+        INHERITED::drawPosText(text, byteLength, pos, paint);
         mBounder.doRect(CommonCheck::kDrawPosText_Type);
     }
 
@@ -294,7 +291,7 @@ public:
                               const SkPaint& paint) {
         mBounder.setEmpty();
         mBounder.setType(CommonCheck::kDrawGlyph_Type);
-        SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
+        INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
         if (mBounder.mUnion.isEmpty()) {
             DBG_NAV_LOGD("empty constY=%g", SkScalarToFloat(constY));
             return;
@@ -317,18 +314,18 @@ public:
                                 const SkPaint& paint) {
         mBounder.setEmpty();
         mBounder.setType(CommonCheck::kDrawGlyph_Type);
-        SkCanvas::drawTextOnPath(text, byteLength, path, matrix, paint);
+        INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
         mBounder.doRect(CommonCheck::kDrawTextOnPath_Type);
     }
 
     virtual void drawPicture(SkPicture& picture) {
         mBounder.setType(CommonCheck::kDrawPicture_Type);
-        SkCanvas::drawPicture(picture);
+        INHERITED::drawPicture(picture);
     }
 
     virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
                           SaveFlags flags) {
-        int depth = SkCanvas::save(flags);
+        int depth = INHERITED::saveLayer(bounds, paint, flags);
         if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) {
             mTransparentLayer = depth;
             mBounder.setAllOpaque(false);
@@ -343,11 +340,13 @@ public:
             mTransparentLayer = 0;
             mBounder.setAllOpaque(true);
         }
-        SkCanvas::restore();
+        INHERITED::restore();
     }
 
     int mTransparentLayer;
     CommonCheck& mBounder;
+private:
+    typedef ParseCanvas INHERITED;
 };
 
 /*
@@ -591,12 +590,15 @@ protected:
     const int mViewRight;
 };
 
-class ImageCanvas : public SkCanvas {
+class ImageCanvas : public ParseCanvas {
 public:
     ImageCanvas(SkBounder* bounder) : mURI(NULL) {
         setBounder(bounder);
     }
 
+    const char* getURI() { return mURI; }
+
+protected:
 // Currently webkit's bitmap draws always seem to be cull'd before this entry
 // point is called, so we assume that any bitmap that gets here is inside our
 // tiny clip (may not be true in the future)
@@ -608,6 +610,7 @@ public:
         }
     }
 
+private:
     const char* mURI;
 };
 
@@ -1123,6 +1126,7 @@ void CachedRoot::calcBitBounds(const IntRect& nodeBounds, IntRect* bitBounds) co
 int CachedRoot::checkForCenter(int x, int y) const
 {
     int width = mViewBounds.width();
+    SkPicture* picture = pictureAt(&x, &y);
     CenterCheck centerCheck(x + width - mViewBounds.x(), y - mViewBounds.y(),
         width);
     BoundsCanvas checker(&centerCheck);
@@ -1132,7 +1136,7 @@ int CachedRoot::checkForCenter(int x, int y) const
     checker.setBitmapDevice(bitmap);
     checker.translate(SkIntToScalar(width - mViewBounds.x()),
         SkIntToScalar(-mViewBounds.y()));
-    checker.drawPicture(*pictureAt(x, y));
+    checker.drawPicture(*picture);
     return centerCheck.center();
 }
 
@@ -1148,8 +1152,9 @@ void CachedRoot::checkForJiggle(int* xDeltaPtr) const
     checker.setBitmapDevice(bitmap);
     int x = -mViewBounds.x() - (xDelta < 0 ? xDelta : 0);
     int y = -mViewBounds.y();
+    SkPicture* picture = pictureAt(&x, &y);
     checker.translate(SkIntToScalar(x), SkIntToScalar(y));
-    checker.drawPicture(*pictureAt(x, y));
+    checker.drawPicture(*picture);
     *xDeltaPtr = jiggleCheck.jiggle();
 }
 
@@ -1262,7 +1267,7 @@ int CachedRoot::getBlockLeftEdge(int x, int y, float scale) const
             node->isTextInput() ? "text" : "plugin");
         return node->bounds(frame).x();
     }
-    SkPicture* picture = node ? frame->picture(node) : pictureAt(x, y);
+    SkPicture* picture = node ? frame->picture(node, &x, &y) : pictureAt(&x, &y);
     if (!picture)
         return x;
     int halfW = (int) (mViewBounds.width() * scale * 0.5f);
@@ -1490,14 +1495,17 @@ bool CachedRoot::innerUp(const CachedNode* test, BestData* bestData) const
 
 WTF::String CachedRoot::imageURI(int x, int y) const
 {
+    DBG_NAV_LOGD("x/y=(%d,%d)", x, y);
     ImageCheck imageCheck;
     ImageCanvas checker(&imageCheck);
     SkBitmap bitmap;
     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
     checker.setBitmapDevice(bitmap);
+    SkPicture* picture = pictureAt(&x, &y);
     checker.translate(SkIntToScalar(-x), SkIntToScalar(-y));
-    checker.drawPicture(*pictureAt(x, y));
-    return WTF::String(checker.mURI);
+    checker.drawPicture(*picture);
+    DBG_NAV_LOGD("uri=%s", checker.getURI());
+    return WTF::String(checker.getURI());
 }
 
 bool CachedRoot::maskIfHidden(BestData* best) const
@@ -1642,18 +1650,27 @@ const CachedNode* CachedRoot::nextTextField(const CachedNode* start,
     return CachedFrame::nextTextField(start, framePtr, &startFound);
 }
 
-SkPicture* CachedRoot::pictureAt(int x, int y) const
+SkPicture* CachedRoot::pictureAt(int* xPtr, int* yPtr) const
 {
 #if USE(ACCELERATED_COMPOSITING)
     if (mRootLayer) {
-        const LayerAndroid* layer = mRootLayer->find(x, y);
+        const LayerAndroid* layer = mRootLayer->find(*xPtr, *yPtr, mPicture);
         if (layer) {
             SkPicture* picture = layer->picture();
+            DBG_NAV_LOGD("layer %d picture=%p (%d,%d)", layer->uniqueId(),
+                picture, picture ? picture->width() : 0,
+                picture ? picture->height() : 0);
+            SkRect localBounds;
+            layer->bounds(&localBounds);
+            *xPtr -= localBounds.fLeft;
+            *yPtr -= localBounds.fTop;
             if (picture)
                 return picture;
         }
     }
 #endif
+    DBG_NAV_LOGD("root mPicture=%p (%d,%d)", mPicture, mPicture ?
+        mPicture->width() : 0, mPicture ? mPicture->height() : 0);
     return mPicture;
 }
 
index 8c263f8..18bace3 100644 (file)
@@ -85,7 +85,7 @@ public:
      */
     const CachedNode* nextTextField(const CachedNode* start,
         const CachedFrame** framePtr) const;
-    SkPicture* pictureAt(int x, int y) const;
+    SkPicture* pictureAt(int* xPtr, int* yPtr) const;
     void reset();
     CachedHistory* rootHistory() const { return mHistory; }
     const WebCore::LayerAndroid* rootLayer() const { return mRootLayer; }
diff --git a/WebKit/android/nav/ParseCanvas.h b/WebKit/android/nav/ParseCanvas.h
new file mode 100644 (file)
index 0000000..9b7a889
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PARSE_CANVAS_H
+#define PARSE_CANVAS_H
+
+#include "SkCanvas.h"
+
+namespace android {
+
+class ParseCanvas : public SkCanvas {
+protected:
+    virtual ~ParseCanvas() {
+        setBounder(0);
+    }
+
+    // Pictures parsed for content never want to create offscreen bitmaps.
+    // Instead, just save and restore the canvas state -- this also keeps
+    // the picture contents at their original coordinates.
+    virtual int saveLayer(const SkRect* , const SkPaint* , SaveFlags flags) {
+        return INHERITED::save(flags);
+    }
+private:
+    typedef SkCanvas INHERITED;
+};
+
+}
+
+#endif
+
index f691d47..d1f8274 100644 (file)
 #include "BidiResolver.h"
 #include "CachedRoot.h"
 #include "LayerAndroid.h"
+#include "ParseCanvas.h"
 #include "SelectText.h"
 #include "SkBitmap.h"
 #include "SkBounder.h"
-#include "SkCanvas.h"
 #include "SkGradientShader.h"
 #include "SkMatrix.h"
 #include "SkPicture.h"
@@ -158,7 +158,7 @@ public:
     SkBounder::GlyphRec mLastGlyph;
 };
 
-class SpaceCanvas : public SkCanvas {
+class SpaceCanvas : public ParseCanvas {
 public:
     SpaceCanvas(const SkIRect& area)
     {
@@ -431,7 +431,10 @@ public:
         if (mDistance > distance) {
             mBestBase = base();
             mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
-            if (distance < 100) {
+#ifndef EXTRA_NOISY_LOGGING
+            if (distance < 100) 
+#endif
+            {
                 DBG_NAV_LOGD("FirstCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
                     mBestBounds.fLeft, mBestBounds.fTop,
                     mBestBounds.fRight, mBestBounds.fBottom, distance >> 2);
@@ -992,7 +995,7 @@ private:
     typedef BuilderCheck INHERITED;
 };
 
-class TextCanvas : public SkCanvas {
+class TextCanvas : public ParseCanvas {
 public:
 
     TextCanvas(CommonCheck* bounder, const SkIRect& area)
@@ -1003,10 +1006,13 @@ public:
             area.height());
         setBitmapDevice(bitmap);
         translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
-    }
-
-    virtual ~TextCanvas() {
-        setBounder(NULL);
+#ifdef DEBUG_NAV_UI
+        const SkIRect& clip = getTotalClip().getBounds();
+        const SkMatrix& matrix = getTotalMatrix();
+        DBG_NAV_LOGD("bitmap=(%d,%d) clip=(%d,%d,%d,%d) matrix=(%g,%g)",
+            bitmap.width(), bitmap.height(), clip.fLeft, clip.fTop,
+            clip.fRight, clip.fBottom, matrix.getTranslateX(), matrix.getTranslateY());
+#endif
     }
 
     virtual void drawPaint(const SkPaint& paint) {
@@ -1033,14 +1039,14 @@ public:
     virtual void drawText(const void* text, size_t byteLength, SkScalar x, 
                           SkScalar y, const SkPaint& paint) {
         mBounder.setUp(paint, getTotalMatrix(), y, text);
-        SkCanvas::drawText(text, byteLength, x, y, paint);
+        INHERITED::drawText(text, byteLength, x, y, paint);
     }
 
     virtual void drawPosTextH(const void* text, size_t byteLength,
                               const SkScalar xpos[], SkScalar constY,
                               const SkPaint& paint) {
         mBounder.setUp(paint, getTotalMatrix(), constY, text);
-        SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
+        INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
     }
 
     virtual void drawVertices(VertexMode vmode, int vertexCount,
@@ -1051,6 +1057,8 @@ public:
     }
 
     CommonCheck& mBounder;
+private:
+    typedef ParseCanvas INHERITED;
 };
 
 static bool buildSelection(const SkPicture& picture, const SkIRect& area,
@@ -1196,6 +1204,7 @@ static WTF::String text(const SkPicture& picture, const SkIRect& area,
 
 SelectText::SelectText()
 {
+    m_picture = 0;
     reset();
     SkPaint paint;
 
@@ -1270,16 +1279,19 @@ SelectText::SelectText()
     m_endControl.endRecording();
     fillGradient->safeUnref();
     dropGradient->safeUnref();
-    m_picture = 0;
+}
+
+SelectText::~SelectText()
+{
+    m_picture->safeUnref();
 }
 
 void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer)
 {
-    // FIXME: layer may not own the original selected picture
-    m_picture = layer->picture();
-    if (!m_picture)
+    if (!m_picture || m_picture != layer->picture())
         return;
-    DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d", m_extendSelection, m_drawPointer);
+    DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d layer [%d]",
+        m_extendSelection, m_drawPointer, layer->uniqueId());
     if (m_extendSelection)
         drawSelectionRegion(canvas);
     if (m_drawPointer)
@@ -1317,16 +1329,14 @@ void SelectText::drawSelectionPointer(SkCanvas* canvas)
 void SelectText::drawSelectionRegion(SkCanvas* canvas)
 {
     m_selRegion.setEmpty();
-    SkRect visBounds;
-    if (!canvas->getClipBounds(&visBounds, SkCanvas::kAA_EdgeType))
-        return;
-    SkIRect ivisBounds;
-    visBounds.round(&ivisBounds);
+    SkIRect ivisBounds = m_visibleRect;
     ivisBounds.join(m_selStart);
     ivisBounds.join(m_selEnd);
-    DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
+    DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)"
+        " ivisBounds=(%d,%d,r=%d,b=%d)",
         m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
-        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+        m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom,
+        ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom);
     m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
         m_selEnd, m_endBase, &m_selRegion);
     SkPath path;
@@ -1348,12 +1358,15 @@ void SelectText::drawSelectionRegion(SkCanvas* canvas)
     canvas->restore();
 }
 
-void SelectText::extendSelection(const SkPicture* picture, int x, int y)
+void SelectText::extendSelection(const IntRect& vis, int x, int y)
 {
-    if (!picture)
+    if (!m_picture)
         return;
+    setVisibleRect(vis);
     SkIRect clipRect = m_visibleRect;
     int base;
+    x -= m_startOffset.fX;
+    y -= m_startOffset.fY;
     if (m_startSelection) {
         if (!clipRect.contains(x, y)
                 || !clipRect.contains(m_original.fX, m_original.fY)) {
@@ -1363,15 +1376,13 @@ void SelectText::extendSelection(const SkPicture* picture, int x, int y)
         }
         DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft,
             clipRect.fTop, clipRect.fRight, clipRect.fBottom);
-        m_picture = picture;
         FirstCheck center(m_original.fX, m_original.fY, clipRect);
-        m_selStart = m_selEnd = findClosest(center, *picture, clipRect, &base);
+        m_selStart = m_selEnd = findClosest(center, *m_picture, clipRect, &base);
         m_startBase = m_endBase = base;
         m_startSelection = false;
         m_extendSelection = true;
         m_original.fX = m_original.fY = 0;
-    } else if (picture != m_picture)
-        return;
+    }
     x -= m_original.fX;
     y -= m_original.fY;
     if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) {
@@ -1382,10 +1393,10 @@ void SelectText::extendSelection(const SkPicture* picture, int x, int y)
     DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d)", clipRect.fLeft,
         clipRect.fTop, clipRect.fRight, clipRect.fBottom);
     FirstCheck extension(x, y, clipRect);
-    SkIRect found = findClosest(extension, *picture, clipRect, &base);
-    DBG_NAV_LOGD("pic=%p x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
+    SkIRect found = findClosest(extension, *m_picture, clipRect, &base);
+    DBG_NAV_LOGD("x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
         " m_extendSelection=%s",
-        picture, x, y, m_startSelection ? "true" : "false",
+        x, y, m_startSelection ? "true" : "false",
         m_hitTopLeft ? "m_selStart" : "m_selEnd",
         found.fLeft, found.fTop, found.fRight, found.fBottom,
         m_extendSelection ? "true" : "false");
@@ -1449,6 +1460,8 @@ bool SelectText::hitCorner(int cx, int cy, int x, int y) const
 
 bool SelectText::hitSelection(int x, int y) const
 {
+    x -= m_startOffset.fX;
+    y -= m_startOffset.fY;
     int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
     int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
     if (hitCorner(left, top, x, y))
@@ -1460,18 +1473,19 @@ bool SelectText::hitSelection(int x, int y) const
     return m_selRegion.contains(x, y);
 }
 
-void SelectText::moveSelection(const SkPicture* picture, int x, int y)
+void SelectText::moveSelection(const IntRect& vis, int x, int y)
 {
-    if (!picture)
+    if (!m_picture)
         return;
+    x -= m_startOffset.fX;
+    y -= m_startOffset.fY;
+    setVisibleRect(vis);
     SkIRect clipRect = m_visibleRect;
     clipRect.join(m_selStart);
     clipRect.join(m_selEnd);
-    if (!m_extendSelection)
-        m_picture = picture;
     FirstCheck center(x, y, clipRect);
     int base;
-    SkIRect found = findClosest(center, *picture, clipRect, &base);
+    SkIRect found = findClosest(center, *m_picture, clipRect, &base);
     if (m_hitTopLeft || !m_extendSelection) {
         m_startBase = base;
         m_selStart = found;
@@ -1494,32 +1508,58 @@ void SelectText::reset()
     m_selEnd.setEmpty();
     m_extendSelection = false;
     m_startSelection = false;
+    m_picture->safeUnref();
+    m_picture = 0;
 }
 
-void SelectText::selectAll(const SkPicture* picture)
+void SelectText::selectAll()
 {
-    m_selStart = findFirst(*picture, &m_startBase);
-    m_selEnd = findLast(*picture, &m_endBase);
+    if (!m_picture)
+        return;
+    m_selStart = findFirst(*m_picture, &m_startBase);
+    m_selEnd = findLast(*m_picture, &m_endBase);
     m_extendSelection = true;
 }
 
 int SelectText::selectionX() const
 {
-    return m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight;
+    return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX;
 }
 
 int SelectText::selectionY() const
 {
     const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
-    return (rect.fTop + rect.fBottom) >> 1;
+    return ((rect.fTop + rect.fBottom) >> 1) + m_startOffset.fY;
+}
+
+void SelectText::setVisibleRect(const IntRect& vis)
+{
+    DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%g,%g)",
+        vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX,
+        m_startOffset.fY);
+    m_visibleRect = vis;
+    m_visibleRect.offset(-m_startOffset.fX, -m_startOffset.fY);
 }
 
-bool SelectText::startSelection(int x, int y)
+bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis,
+    int x, int y)
 {
+    m_startOffset.set(x, y);
+    m_picture->safeUnref();
+    m_picture = root->pictureAt(&x, &y);
+    if (!m_picture) {
+        DBG_NAV_LOG("picture==0");
+        return false;
+    }
+    m_picture->ref();
+    m_startOffset.fX -= x;
+    m_startOffset.fY -= y;
     m_original.fX = x;
     m_original.fY = y;
+    setVisibleRect(vis);
     if (m_selStart.isEmpty()) {
-        DBG_NAV_LOGD("empty start x=%d y=%d", x, y);
+        DBG_NAV_LOGD("empty start picture=(%d,%d) x=%d y=%d", 
+             m_picture->width(), m_picture->height(), x, y);
         m_startSelection = true;
         return true;
     }
@@ -1529,8 +1569,8 @@ bool SelectText::startSelection(int x, int y)
     int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
     int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
     bool hitBottomRight = hitCorner(right, bottom, x, y);
-    DBG_NAV_LOGD("left=%d top=%d right=%d bottom=%d x=%d y=%d", left, top,
-        right, bottom, x, y);
+    DBG_NAV_LOGD("picture=(%d,%d) left=%d top=%d right=%d bottom=%d x=%d y=%d",
+        m_picture->width(), m_picture->height(),left, top, right, bottom, x, y);
     if (m_hitTopLeft && (!hitBottomRight || y - top < bottom - y)) {
         DBG_NAV_LOG("hit top left");
         m_original.fX -= left;
@@ -1549,14 +1589,14 @@ bool SelectText::startSelection(int x, int y)
 * FIXME: digit find isn't implemented yet
 * returns true if a word was selected
 */
-bool SelectText::wordSelection(const SkPicture* picture)
+bool SelectText::wordSelection()
 {
     int x = m_selStart.fLeft;
     int y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
     SkIRect clipRect = m_visibleRect;
     clipRect.fLeft -= m_visibleRect.width() >> 1;
     int base;
-    SkIRect left = findLeft(*picture, clipRect, x, y, &base);
+    SkIRect left = findLeft(*m_picture, clipRect, x, y, &base);
     if (!left.isEmpty()) {
         m_startBase = base;
         m_selStart = left;
@@ -1565,7 +1605,7 @@ bool SelectText::wordSelection(const SkPicture* picture)
     y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
     clipRect = m_visibleRect;
     clipRect.fRight += m_visibleRect.width() >> 1;
-    SkIRect right = findRight(*picture, clipRect, x, y, &base);
+    SkIRect right = findRight(*m_picture, clipRect, x, y, &base);
     if (!right.isEmpty()) {
         m_endBase = base;
         m_selEnd = right;
index 404e9e7..d210117 100644 (file)
 #include "IntRect.h"
 #include "PlatformString.h"
 #include "SkPath.h"
-
-class SkPicture;
-struct SkIRect;
-class SkRegion;
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
 
 namespace android {
 
@@ -42,20 +41,20 @@ class CachedRoot;
 class SelectText : public DrawExtra {
 public:
     SelectText();
+    virtual ~SelectText();
     virtual void draw(SkCanvas* , LayerAndroid* );
-    void extendSelection(const SkPicture* , int x, int y);
+    void extendSelection(const IntRect& vis, int x, int y);
     const String getSelection();
     bool hitSelection(int x, int y) const;
-    void moveSelection(const SkPicture* , int x, int y);
+    void moveSelection(const IntRect& vis, int x, int y);
     void reset();
-    void selectAll(const SkPicture* );
+    void selectAll();
     int selectionX() const;
     int selectionY() const;
     void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; }
     void setExtendSelection(bool extend) { m_extendSelection = extend; }
-    void setVisibleRect(const IntRect& rect) { m_visibleRect = rect; }
-    bool startSelection(int x, int y);
-    bool wordSelection(const SkPicture* picture);
+    bool startSelection(const CachedRoot* , const IntRect& vis, int x, int y);
+    bool wordSelection();
 public:
     float m_inverseScale; // inverse scale, x, y used for drawing select path
     int m_selectX;
@@ -66,8 +65,10 @@ private:
     static void getSelectionArrow(SkPath* );
     void getSelectionCaret(SkPath* );
     bool hitCorner(int cx, int cy, int x, int y) const;
+    void setVisibleRect(const IntRect& );
     void swapAsNeeded();
     SkIPoint m_original; // computed start of extend selection
+    SkIPoint m_startOffset; // difference from global to layer
     SkIRect m_selStart;
     SkIRect m_selEnd;
     int m_startBase;
index 684f91c..166aa03 100644 (file)
@@ -683,26 +683,24 @@ int getScaledMaxYScroll()
     return result;
 }
 
-void getVisibleRect(WebCore::IntRect* rect)
+IntRect getVisibleRect()
 {
+    IntRect rect;
     LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
     JNIEnv* env = JSC::Bindings::getJNIEnv();
     jobject jRect = env->CallObjectMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getVisibleRect);
     checkException(env);
-    int left = (int) env->GetIntField(jRect, m_javaGlue.m_rectLeft);
+    rect.setX(env->GetIntField(jRect, m_javaGlue.m_rectLeft));
     checkException(env);
-    rect->setX(left);
-    int top = (int) env->GetIntField(jRect, m_javaGlue.m_rectTop);
+    rect.setY(env->GetIntField(jRect, m_javaGlue.m_rectTop));
     checkException(env);
-    rect->setY(top);
-    int width = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectWidth);
+    rect.setWidth(env->CallIntMethod(jRect, m_javaGlue.m_rectWidth));
     checkException(env);
-    rect->setWidth(width);
-    int height = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectHeight);
+    rect.setHeight(env->CallIntMethod(jRect, m_javaGlue.m_rectHeight));
     checkException(env);
-    rect->setHeight(height);
     env->DeleteLocalRef(jRect);
     checkException(env);
+    return rect;
 }
 
 static CachedFrame::Direction KeyToDirection(int32_t keyCode)
@@ -853,8 +851,7 @@ const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect,
 
 IntRect setVisibleRect(CachedRoot* root)
 {
-    IntRect visibleRect;
-    getVisibleRect(&visibleRect);
+    IntRect visibleRect = getVisibleRect();
     DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
         visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
     root->setVisibleRect(visibleRect);
@@ -953,10 +950,14 @@ bool motionUp(int x, int y, int slop)
 const LayerAndroid* scrollableLayer(int x, int y)
 {
 #if ENABLE(ANDROID_OVERFLOW_SCROLL) && USE(ACCELERATED_COMPOSITING)
-    const LayerAndroid* root = compositeRoot();
-    if (!root)
+    const LayerAndroid* layerRoot = compositeRoot();
+    if (!layerRoot)
+        return 0;
+    CachedRoot* cachedRoot = getFrameCache(DontAllowNewer);
+    if (!cachedRoot)
         return 0;
-    const LayerAndroid* result = root->find(x, y);
+    SkPicture* picture = cachedRoot->pictureAt(&x, &y);
+    const LayerAndroid* result = layerRoot->find(x, y, picture);
     if (result != 0 && result->contentIsScrollable())
         return result;
 #endif
@@ -1019,25 +1020,12 @@ String getSelection()
 
 void moveSelection(int x, int y)
 {
-    const CachedRoot* root = getFrameCache(DontAllowNewer);
-    if (!root)
-        return;
-    SkPicture* picture = root->pictureAt(x, y);
-    // FIXME: use the visibleRect only for the main picture
-    // for layer pictures, use the equivalent of the canvas clipping rect
-    IntRect visibleRect;
-    getVisibleRect(&visibleRect);
-    m_selectText.setVisibleRect(visibleRect);
-    m_selectText.moveSelection(picture, x, y);
+    m_selectText.moveSelection(getVisibleRect(), x, y);
 }
 
 void selectAll()
 {
-    const CachedRoot* root = getFrameCache(DontAllowNewer);
-    if (!root)
-        return;
-    SkPicture* picture = root->pictureAt(0, 0);
-    m_selectText.selectAll(picture);
+    m_selectText.selectAll();
 }
 
 int selectionX()
@@ -1057,29 +1045,24 @@ void resetSelection()
 
 bool startSelection(int x, int y)
 {
-    return m_selectText.startSelection(x, y);
+    const CachedRoot* root = getFrameCache(DontAllowNewer);
+    if (!root)
+        return false;
+    return m_selectText.startSelection(root, getVisibleRect(), x, y);
 }
 
 bool wordSelection(int x, int y)
 {
-    startSelection(x, y);
-    if (!extendSelection(x, y))
+    if (!startSelection(x, y))
         return false;
+    extendSelection(x, y);
     m_selectText.setDrawPointer(false);
-    SkPicture* picture = getFrameCache(DontAllowNewer)->pictureAt(x, y);
-    return m_selectText.wordSelection(picture);
+    return m_selectText.wordSelection();
 }
 
 bool extendSelection(int x, int y)
 {
-    const CachedRoot* root = getFrameCache(DontAllowNewer);
-    if (!root)
-        return false;
-    SkPicture* picture = root->pictureAt(x, y);
-    IntRect visibleRect;
-    getVisibleRect(&visibleRect);
-    m_selectText.setVisibleRect(visibleRect);
-    m_selectText.extendSelection(picture, x, y);
+    m_selectText.extendSelection(getVisibleRect(), x, y);
     return true;
 }