Home | History | Annotate | Download | only in nav
      1 /*
      2  * Copyright 2007, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "CachedPrefix.h"
     27 #include "android_graphics.h"
     28 #include "CachedHistory.h"
     29 #include "CachedInput.h"
     30 #include "CachedLayer.h"
     31 #include "CachedNode.h"
     32 #include "FindCanvas.h"
     33 #include "FloatRect.h"
     34 #include "LayerAndroid.h"
     35 #include "ParseCanvas.h"
     36 #include "SkBitmap.h"
     37 #include "SkBounder.h"
     38 #include "SkPixelRef.h"
     39 #include "SkRegion.h"
     40 
     41 #include "CachedRoot.h"
     42 
     43 #if DEBUG_NAV_UI
     44 #include "wtf/text/CString.h"
     45 #endif
     46 
     47 #define DONT_CENTER_IF_ALREADY_VISIBLE
     48 
     49 using std::min;
     50 using std::max;
     51 
     52 #ifdef DUMP_NAV_CACHE_USING_PRINTF
     53     extern android::Mutex gWriteLogMutex;
     54 #endif
     55 
     56 namespace android {
     57 
     58 class CommonCheck : public SkBounder {
     59 public:
     60     enum Type {
     61         kNo_Type,
     62         kDrawBitmap_Type,
     63         kDrawGlyph_Type,
     64         kDrawPaint_Type,
     65         kDrawPath_Type,
     66         kDrawPicture_Type,
     67         kDrawPoints_Type,
     68         kDrawPosText_Type,
     69         kDrawPosTextH_Type,
     70         kDrawRect_Type,
     71         kDrawSprite_Type,
     72         kDrawText_Type,
     73         kDrawTextOnPath_Type,
     74         kPopLayer_Type,
     75         kPushLayer_Type,
     76         kPushSave_Type
     77     };
     78 
     79     static bool isTextType(Type t) {
     80         return t == kDrawPosTextH_Type || t == kDrawText_Type;
     81     }
     82 
     83     CommonCheck() : mType(kNo_Type), mAllOpaque(true), mIsOpaque(true) {
     84         setEmpty();
     85     }
     86 
     87     bool doRect(Type type) {
     88         mType = type;
     89         return doIRect(mUnion);
     90     }
     91 
     92     bool isEmpty() { return mUnion.isEmpty(); }
     93 
     94     bool joinGlyphs(const SkIRect& rect) {
     95         bool isGlyph = mType == kDrawGlyph_Type;
     96         if (isGlyph)
     97             mUnion.join(rect);
     98         return isGlyph;
     99     }
    100 
    101     void setAllOpaque(bool opaque) { mAllOpaque = opaque; }
    102     void setEmpty() { mUnion.setEmpty(); }
    103     void setIsOpaque(bool opaque) { mIsOpaque = opaque; }
    104     void setType(Type type) { mType = type; }
    105 
    106     Type mType;
    107     SkIRect mUnion;
    108     bool mAllOpaque;
    109     bool mIsOpaque;
    110 };
    111 
    112 #if DEBUG_NAV_UI
    113     static const char* TypeNames[] = {
    114         "kNo_Type",
    115         "kDrawBitmap_Type",
    116         "kDrawGlyph_Type",
    117         "kDrawPaint_Type",
    118         "kDrawPath_Type",
    119         "kDrawPicture_Type",
    120         "kDrawPoints_Type",
    121         "kDrawPosText_Type",
    122         "kDrawPosTextH_Type",
    123         "kDrawRect_Type",
    124         "kDrawSprite_Type",
    125         "kDrawText_Type",
    126         "kDrawTextOnPath_Type",
    127         "kPopLayer_Type",
    128         "kPushLayer_Type",
    129         "kPushSave_Type"
    130     };
    131 #endif
    132 
    133 #define kMargin 16
    134 #define kSlop 2
    135 
    136 class BoundsCanvas : public ParseCanvas {
    137 public:
    138 
    139     BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) {
    140         mTransparentLayer = 0;
    141         setBounder(bounder);
    142     }
    143 
    144     virtual void drawPaint(const SkPaint& paint) {
    145         mBounder.setType(CommonCheck::kDrawPaint_Type);
    146         INHERITED::drawPaint(paint);
    147     }
    148 
    149     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
    150                             const SkPaint& paint) {
    151         mBounder.setType(CommonCheck::kDrawPoints_Type);
    152         INHERITED::drawPoints(mode, count, pts, paint);
    153     }
    154 
    155     virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
    156         mBounder.setType(CommonCheck::kDrawRect_Type);
    157         INHERITED::drawRect(rect, paint);
    158     }
    159 
    160     virtual void drawPath(const SkPath& path, const SkPaint& paint) {
    161         mBounder.setType(CommonCheck::kDrawPath_Type);
    162         INHERITED::drawPath(path, paint);
    163     }
    164 
    165     virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
    166                               const SkMatrix& matrix, const SkPaint& paint) {
    167         mBounder.setType(CommonCheck::kDrawBitmap_Type);
    168         mBounder.setIsOpaque(bitmap.isOpaque());
    169         INHERITED::commonDrawBitmap(bitmap, rect, matrix, paint);
    170     }
    171 
    172     virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
    173                             const SkPaint* paint) {
    174         mBounder.setType(CommonCheck::kDrawSprite_Type);
    175         mBounder.setIsOpaque(bitmap.isOpaque() &&
    176             (!paint || paint->getAlpha() == 255));
    177         INHERITED::drawSprite(bitmap, left, top, paint);
    178     }
    179 
    180     virtual void drawText(const void* text, size_t byteLength, SkScalar x,
    181                           SkScalar y, const SkPaint& paint) {
    182         mBounder.setEmpty();
    183         mBounder.setType(CommonCheck::kDrawGlyph_Type);
    184         INHERITED::drawText(text, byteLength, x, y, paint);
    185         mBounder.doRect(CommonCheck::kDrawText_Type);
    186     }
    187 
    188     virtual void drawPosText(const void* text, size_t byteLength,
    189                              const SkPoint pos[], const SkPaint& paint) {
    190         mBounder.setEmpty();
    191         mBounder.setType(CommonCheck::kDrawGlyph_Type);
    192         INHERITED::drawPosText(text, byteLength, pos, paint);
    193         if (!mBounder.isEmpty())
    194             mBounder.doRect(CommonCheck::kDrawPosText_Type);
    195     }
    196 
    197     virtual void drawPosTextH(const void* text, size_t byteLength,
    198                               const SkScalar xpos[], SkScalar constY,
    199                               const SkPaint& paint) {
    200         mBounder.setEmpty();
    201         mBounder.setType(CommonCheck::kDrawGlyph_Type);
    202         INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
    203         if (mBounder.mUnion.isEmpty()) {
    204             DBG_NAV_LOGD("empty constY=%g", SkScalarToFloat(constY));
    205             return;
    206         }
    207         SkPaint::FontMetrics metrics;
    208         paint.getFontMetrics(&metrics);
    209         SkPoint upDown[2] = { {xpos[0], constY + metrics.fAscent},
    210             {xpos[0], constY + metrics.fDescent} };
    211         const SkMatrix& matrix = getTotalMatrix();
    212         matrix.mapPoints(upDown, 2);
    213         if (upDown[0].fX == upDown[1].fX) {
    214             mBounder.mUnion.fTop = SkScalarFloor(upDown[0].fY);
    215             mBounder.mUnion.fBottom = SkScalarFloor(upDown[1].fY);
    216         }
    217         mBounder.doRect(CommonCheck::kDrawPosTextH_Type);
    218     }
    219 
    220     virtual void drawTextOnPath(const void* text, size_t byteLength,
    221                                 const SkPath& path, const SkMatrix* matrix,
    222                                 const SkPaint& paint) {
    223         mBounder.setEmpty();
    224         mBounder.setType(CommonCheck::kDrawGlyph_Type);
    225         INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
    226         mBounder.doRect(CommonCheck::kDrawTextOnPath_Type);
    227     }
    228 
    229     virtual void drawPicture(SkPicture& picture) {
    230         mBounder.setType(CommonCheck::kDrawPicture_Type);
    231         INHERITED::drawPicture(picture);
    232     }
    233 
    234     virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
    235                           SaveFlags flags) {
    236         int depth = INHERITED::saveLayer(bounds, paint, flags);
    237         if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) {
    238             mTransparentLayer = depth;
    239             mBounder.setAllOpaque(false);
    240         }
    241         return depth;
    242     }
    243 
    244     virtual void restore() {
    245         mBounder.setType(CommonCheck::kDrawSprite_Type); // for layer draws
    246         int depth = getSaveCount();
    247         if (depth == mTransparentLayer) {
    248             mTransparentLayer = 0;
    249             mBounder.setAllOpaque(true);
    250         }
    251         INHERITED::restore();
    252     }
    253 
    254     int mTransparentLayer;
    255     CommonCheck& mBounder;
    256 private:
    257     typedef ParseCanvas INHERITED;
    258 };
    259 
    260 /*
    261 LeftCheck examines the text in a picture, within a viewable rectangle,
    262 and returns via left() the position of the left edge of the paragraph.
    263 It first looks at the left edge of the test point, then looks above and below
    264 it for more lines of text to determine the div's left edge.
    265 */
    266 class LeftCheck : public CommonCheck {
    267 public:
    268     LeftCheck(int x, int y) : mX(x), mY(y), mHitLeft(INT_MAX),
    269             mMostLeft(INT_MAX) {
    270         mHit.set(x - (HIT_SLOP << 1), y - HIT_SLOP, x, y + HIT_SLOP);
    271         mPartial.setEmpty();
    272         mBounds.setEmpty();
    273         mPartialType = kNo_Type;
    274     }
    275 
    276     int left() {
    277         if (isTextType(mType))
    278             doRect(); // process the final line of text
    279         return mMostLeft != INT_MAX ? mMostLeft : mX >> 1;
    280     }
    281 
    282     // FIXME: this is identical to CenterCheck::onIRect()
    283     // refactor so that LeftCheck and CenterCheck inherit common functions
    284     virtual bool onIRect(const SkIRect& rect) {
    285         bool opaqueBitmap = mType == kDrawBitmap_Type && mIsOpaque;
    286         if (opaqueBitmap && rect.contains(mX, mY)) {
    287             mMostLeft = rect.fLeft;
    288             return false;
    289         }
    290         if (joinGlyphs(rect)) // assembles glyphs into a text string
    291             return false;
    292         if (!isTextType(mType) && !opaqueBitmap)
    293             return false;
    294         /* Text on one line may be broken into several parts. Reassemble
    295            the text into a rectangle before considering it. */
    296         if (rect.fTop < mPartial.fBottom
    297                 && rect.fBottom > mPartial.fTop
    298                 && mPartial.fRight + JOIN_SLOP_X >= rect.fLeft
    299                 && (mPartialType != kDrawBitmap_Type
    300                 || mPartial.height() <= rect.height() + JOIN_SLOP_Y)) {
    301             DBG_NAV_LOGD("LeftCheck join mPartial=(%d, %d, %d, %d)"
    302                 " rect=(%d, %d, %d, %d)",
    303                 mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
    304                 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
    305             mPartial.join(rect);
    306             return false;
    307         }
    308         if (mPartial.isEmpty() == false) {
    309             doRect(); // process the previous line of text
    310 #if DEBUG_NAV_UI
    311             if (mHitLeft == INT_MAX)
    312                 DBG_NAV_LOGD("LeftCheck disabled rect=(%d, %d, %d, %d)",
    313                     rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
    314 #endif
    315         }
    316         mPartial = rect;
    317         mPartialType = mType;
    318         return false;
    319     }
    320 
    321     void doRect()
    322     {
    323         /* Record the outer bounds of the lines of text that intersect the
    324            touch coordinates, given some slop */
    325         if (SkIRect::Intersects(mPartial, mHit)) {
    326             if (mHitLeft > mPartial.fLeft)
    327                 mHitLeft = mPartial.fLeft;
    328             DBG_NAV_LOGD("LeftCheck mHitLeft=%d", mHitLeft);
    329         } else if (mHitLeft == INT_MAX)
    330             return; // wait for intersect success
    331         /* If text is too far away vertically, don't consider it */
    332         if (!mBounds.isEmpty() && (mPartial.fTop > mBounds.fBottom + HIT_SLOP
    333                 || mPartial.fBottom < mBounds.fTop - HIT_SLOP)) {
    334             DBG_NAV_LOGD("LeftCheck stop mPartial=(%d, %d, %d, %d)"
    335                 " mBounds=(%d, %d, %d, %d)",
    336                 mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
    337                 mBounds.fLeft, mBounds.fTop, mBounds.fRight, mBounds.fBottom);
    338             mHitLeft = INT_MAX; // and disable future comparisons
    339             return;
    340         }
    341         /* If the considered text is completely to the left or right of the
    342            touch coordinates, skip it, turn off further detection */
    343         if (mPartial.fLeft > mX || mPartial.fRight < mX) {
    344             DBG_NAV_LOGD("LeftCheck stop mX=%d mPartial=(%d, %d, %d, %d)", mX,
    345                 mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom);
    346             mHitLeft = INT_MAX;
    347             return;
    348         }
    349         /* record the smallest margins on the left and right */
    350         if (mMostLeft > mPartial.fLeft) {
    351             DBG_NAV_LOGD("LeftCheck new mMostLeft=%d (old=%d)", mPartial.fLeft,
    352                 mMostLeft);
    353             mMostLeft = mPartial.fLeft;
    354         }
    355         if (mBounds.isEmpty())
    356             mBounds = mPartial;
    357         else if (mPartial.fBottom > mBounds.fBottom) {
    358             DBG_NAV_LOGD("LeftCheck new bottom=%d (old=%d)", mPartial.fBottom,
    359                 mBounds.fBottom);
    360             mBounds.fBottom = mPartial.fBottom;
    361         }
    362     }
    363 
    364     static const int JOIN_SLOP_X = 30; // horizontal space between text parts
    365     static const int JOIN_SLOP_Y = 5; // vertical space between text lines
    366     static const int HIT_SLOP = 30; // diameter allowing for tap size
    367     /* const */ SkIRect mHit; // sloppy hit rectangle
    368     SkIRect mBounds; // reference bounds
    369     SkIRect mPartial; // accumulated text bounds, per line
    370     const int mX; // touch location
    371     const int mY;
    372     int mHitLeft; // touched text extremes
    373     int mMostLeft; // paragraph extremes
    374     Type mPartialType;
    375 };
    376 
    377 /*
    378 CenterCheck examines the text in a picture, within a viewable rectangle,
    379 and returns via center() the optimal amount to scroll in x to display the
    380 paragraph of text.
    381 
    382 The caller of CenterCheck has configured (but not allocated) a bitmap
    383 the height and three times the width of the view. The picture is drawn centered
    384 in the bitmap, so text that would be revealed, if the view was scrolled up to
    385 a view-width to the left or right, is considered.
    386 */
    387 class CenterCheck : public CommonCheck {
    388 public:
    389     CenterCheck(int x, int y, int width) : mX(x), mY(y),
    390             mHitLeft(x), mHitRight(x), mMostLeft(INT_MAX), mMostRight(-INT_MAX),
    391             mViewLeft(width), mViewRight(width << 1) {
    392         mHit.set(x - CENTER_SLOP, y - CENTER_SLOP,
    393             x + CENTER_SLOP, y + CENTER_SLOP);
    394         mPartial.setEmpty();
    395     }
    396 
    397     int center() {
    398         doRect(); // process the final line of text
    399         /* If the touch coordinates aren't near any text, return 0 */
    400         if (mHitLeft == mHitRight) {
    401             DBG_NAV_LOGD("abort: mHitLeft=%d ==mHitRight", mHitLeft);
    402             return 0;
    403         }
    404         int leftOver = mHitLeft - mViewLeft;
    405         int rightOver = mHitRight - mViewRight;
    406         int center;
    407         /* If the touched text is too large to entirely fit on the screen,
    408            center it. */
    409         if (leftOver < 0 && rightOver > 0) {
    410             center = (leftOver + rightOver) >> 1;
    411             DBG_NAV_LOGD("overlap: leftOver=%d rightOver=%d center=%d",
    412                 leftOver, rightOver, center);
    413             return center;
    414         }
    415         center = (mMostLeft + mMostRight) >> 1; // the paragraph center
    416         if (leftOver > 0 && rightOver >= 0) { // off to the right
    417             if (center > mMostLeft) // move to center loses left-most text?
    418                 center = mMostLeft;
    419         } else if (rightOver < 0 && leftOver <= 0) { // off to the left
    420             if (center < mMostRight) // move to center loses right-most text?
    421                 center = mMostRight;
    422         } else {
    423 #ifdef DONT_CENTER_IF_ALREADY_VISIBLE
    424             center = 0; // paragraph is already fully visible
    425 #endif
    426         }
    427         DBG_NAV_LOGD("scroll: leftOver=%d rightOver=%d center=%d",
    428             leftOver, rightOver, center);
    429         return center;
    430     }
    431 
    432 protected:
    433     virtual bool onIRect(const SkIRect& rect) {
    434         if (joinGlyphs(rect)) // assembles glyphs into a text string
    435             return false;
    436         if (!isTextType(mType))
    437             return false;
    438         /* Text on one line may be broken into several parts. Reassemble
    439            the text into a rectangle before considering it. */
    440         if (rect.fTop < mPartial.fBottom && rect.fBottom >
    441                 mPartial.fTop && mPartial.fRight + CENTER_SLOP >= rect.fLeft) {
    442             DBG_NAV_LOGD("join mPartial=(%d, %d, %d, %d) rect=(%d, %d, %d, %d)",
    443                 mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
    444                 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
    445             mPartial.join(rect);
    446             return false;
    447         }
    448         if (mPartial.isEmpty() == false)
    449             doRect(); // process the previous line of text
    450         mPartial = rect;
    451         return false;
    452     }
    453 
    454     void doRect()
    455     {
    456         /* Record the outer bounds of the lines of text that was 'hit' by the
    457            touch coordinates, given some slop */
    458         if (SkIRect::Intersects(mPartial, mHit)) {
    459             if (mHitLeft > mPartial.fLeft)
    460                 mHitLeft = mPartial.fLeft;
    461             if (mHitRight < mPartial.fRight)
    462                 mHitRight = mPartial.fRight;
    463             DBG_NAV_LOGD("mHitLeft=%d mHitRight=%d", mHitLeft, mHitRight);
    464         }
    465         /* If the considered text is completely to the left or right of the
    466            touch coordinates, skip it */
    467         if (mPartial.fLeft > mX || mPartial.fRight < mX)
    468             return;
    469         int leftOver = mPartial.fLeft - mViewLeft;
    470         int rightOver = mPartial.fRight - mViewRight;
    471         /* If leftOver <= 0, the text starts off the screen.
    472            If rightOver >= 0, the text ends off the screen.
    473         */
    474         if (leftOver <= 0 && rightOver >= 0) // discard wider than screen
    475             return;
    476 #ifdef DONT_CENTER_IF_ALREADY_VISIBLE
    477         if (leftOver > 0 && rightOver < 0)   // discard already visible
    478             return;
    479 #endif
    480         /* record the smallest margins on the left and right */
    481         if (mMostLeft > leftOver)
    482             mMostLeft = leftOver;
    483         if (mMostRight < rightOver)
    484             mMostRight = rightOver;
    485         DBG_NAV_LOGD("leftOver=%d rightOver=%d mMostLeft=%d mMostRight=%d",
    486             leftOver, rightOver, mMostLeft, mMostRight);
    487     }
    488 
    489     static const int CENTER_SLOP = 10; // space between text parts and lines
    490     /* const */ SkIRect mHit; // sloppy hit rectangle
    491     SkIRect mPartial; // accumulated text bounds, per line
    492     const int mX; // touch location
    493     const int mY;
    494     int mHitLeft; // touched text extremes
    495     int mHitRight;
    496     int mMostLeft; // paragraph extremes
    497     int mMostRight;
    498     const int mViewLeft; // middle third of 3x-wide view
    499     const int mViewRight;
    500 };
    501 
    502 class ImageCanvas : public ParseCanvas {
    503 public:
    504     ImageCanvas(SkBounder* bounder) : mURI(NULL) {
    505         setBounder(bounder);
    506     }
    507 
    508     const char* getURI() { return mURI; }
    509 
    510 protected:
    511 // Currently webkit's bitmap draws always seem to be cull'd before this entry
    512 // point is called, so we assume that any bitmap that gets here is inside our
    513 // tiny clip (may not be true in the future)
    514     virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
    515                               const SkMatrix& , const SkPaint& ) {
    516         SkPixelRef* pixelRef = bitmap.pixelRef();
    517         if (pixelRef != NULL) {
    518             mURI = pixelRef->getURI();
    519         }
    520     }
    521 
    522 private:
    523     const char* mURI;
    524 };
    525 
    526 class ImageCheck : public SkBounder {
    527 public:
    528     virtual bool onIRect(const SkIRect& rect) {
    529         return false;
    530     }
    531 };
    532 
    533 class JiggleCheck : public CommonCheck {
    534 public:
    535     JiggleCheck(int delta, int width) : mDelta(delta), mMaxX(width) {
    536         mMaxJiggle = 0;
    537         mMinX = mMinJiggle = abs(delta);
    538         mMaxWidth = width + mMinX;
    539     }
    540 
    541     int jiggle() {
    542         if (mMinJiggle > mMaxJiggle)
    543             return mDelta;
    544         int avg = (mMinJiggle + mMaxJiggle + 1) >> 1;
    545         return mDelta < 0 ? -avg : avg;
    546     }
    547 
    548     virtual bool onIRect(const SkIRect& rect) {
    549         if (joinGlyphs(rect))
    550             return false;
    551         if (mType != kDrawBitmap_Type && !isTextType(mType))
    552             return false;
    553         int min, max;
    554         if (mDelta < 0) {
    555             min = mMinX - rect.fLeft;
    556             max = mMaxWidth - rect.fRight;
    557         } else {
    558             min = rect.fRight - mMaxX;
    559             max = rect.fLeft;
    560         }
    561         if (min <= 0)
    562             return false;
    563         if (max >= mMinX)
    564             return false;
    565         if (mMinJiggle > min)
    566             mMinJiggle = min;
    567         if (mMaxJiggle < max)
    568             mMaxJiggle = max;
    569         return false;
    570     }
    571 
    572     int mDelta;
    573     int mMaxJiggle;
    574     int mMaxX;
    575     int mMinJiggle;
    576     int mMinX;
    577     int mMaxWidth;
    578 };
    579 
    580 class RingCheck : public CommonCheck {
    581 public:
    582     RingCheck(const WTF::Vector<WebCore::IntRect>& rings,
    583         const WebCore::IntRect& bitBounds, const WebCore::IntRect& testBounds,
    584         bool singleImage)
    585         : mTestBounds(testBounds)
    586         , mBitBounds(bitBounds)
    587         , mPushPop(false)
    588         , mSingleImage(singleImage)
    589     {
    590         const WebCore::IntRect* r;
    591         for (r = rings.begin(); r != rings.end(); r++) {
    592             SkIRect fatter = {r->x(), r->y(), r->maxX(), r->maxY()};
    593             fatter.inset(-CURSOR_RING_HIT_TEST_RADIUS, -CURSOR_RING_HIT_TEST_RADIUS);
    594             DBG_NAV_LOGD("RingCheck fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop,
    595                 fatter.fRight, fatter.fBottom);
    596             mTextSlop.op(fatter, SkRegion::kUnion_Op);
    597             mTextTest.op(*r, SkRegion::kUnion_Op);
    598         }
    599         int dx = -bitBounds.x();
    600         int dy = -bitBounds.y();
    601         DBG_NAV_LOGD("RingCheck translate=(%d,%d)", dx, dy);
    602         mTextSlop.translate(dx, dy);
    603         mTextTest.translate(dx, dy);
    604         mTestBounds.translate(dx, dy);
    605         mEmpty.setEmpty();
    606     }
    607 
    608     bool hiddenRings(SkRegion* clipped)
    609     {
    610         findBestLayer();
    611         if (!mBestLayer) {
    612             DBG_NAV_LOG("RingCheck empty");
    613             clipped->setEmpty();
    614             return true;
    615         }
    616         const SkRegion* layersEnd = mLayers.end();
    617         const Type* layerTypes = &mLayerTypes[mBestLayer - mLayers.begin()];
    618         bool collectGlyphs = true;
    619         bool collectOvers = false;
    620         SkRegion over;
    621         for (const SkRegion* layers = mBestLayer; layers != layersEnd; layers++) {
    622             Type layerType = *layerTypes++;
    623             DBG_NAV_LOGD("RingCheck #%d %s (%d,%d,r=%d,b=%d)",
    624                 layers - mLayers.begin(), TypeNames[layerType],
    625                 layers->getBounds().fLeft, layers->getBounds().fTop,
    626                 layers->getBounds().fRight, layers->getBounds().fBottom);
    627             if (collectGlyphs && (layerType == kDrawGlyph_Type
    628                 || ((layerType == kDrawRect_Type && mTextTest.contains(*layers))
    629                 || (layerType == kDrawBitmap_Type && mTextSlop.contains(*layers))))) {
    630                 DBG_NAV_LOGD("RingCheck #%d collectOvers", layers - mLayers.begin());
    631                 collectOvers = true;
    632                 clipped->op(*layers, SkRegion::kUnion_Op);
    633                 continue;
    634             }
    635             collectGlyphs &= layerType != kPushLayer_Type;
    636             if (collectOvers && (layerType == kDrawRect_Type
    637                 || layerType == kDrawBitmap_Type
    638                 || (!collectGlyphs && layerType == kDrawSprite_Type))) {
    639                 DBG_NAV_LOGD("RingCheck #%d over.op", layers - mLayers.begin());
    640                 over.op(*layers, SkRegion::kUnion_Op);
    641             }
    642         }
    643         bool result = !collectOvers || clipped->intersects(over);
    644         const SkIRect t = clipped->getBounds();
    645         const SkIRect o = over.getBounds();
    646         clipped->op(over, SkRegion::kDifference_Op);
    647         clipped->translate(mBitBounds.x(), mBitBounds.y());
    648         const SkIRect c = clipped->getBounds();
    649         DBG_NAV_LOGD("RingCheck intersects=%s text=(%d,%d,r=%d,b=%d)"
    650             " over=(%d,%d,r=%d,b=%d) clipped=(%d,%d,r=%d,b=%d)",
    651             result ? "true" : "false",
    652             t.fLeft, t.fTop, t.fRight, t.fBottom,
    653             o.fLeft, o.fTop, o.fRight, o.fBottom,
    654             c.fLeft, c.fTop, c.fRight, c.fBottom);
    655         return result;
    656     }
    657 
    658     void push(Type type, const SkIRect& bounds)
    659     {
    660 #if DEBUG_NAV_UI
    661         // this caches the push string and subquently ignores if pushSave
    662         // is immediately followed by popLayer. Push/pop pairs happen
    663         // frequently and just add noise to the log.
    664         static String lastLog;
    665         String currentLog = String("RingCheck append #")
    666             + String::number(mLayers.size())
    667             + " type=" + TypeNames[type] + " bounds=("
    668             + String::number(bounds.fLeft)
    669             + "," + String::number(bounds.fTop) + ","
    670             + String::number(bounds.fRight) + ","
    671             + String::number(bounds.fBottom) + ")";
    672         if (lastLog.length() == 0 || type != kPopLayer_Type) {
    673             if (lastLog.length() != 0)
    674                 DBG_NAV_LOGD("%s", lastLog.latin1().data());
    675             if (type == kPushSave_Type)
    676                 lastLog = currentLog;
    677             else
    678                 DBG_NAV_LOGD("%s", currentLog.latin1().data());
    679         } else
    680             lastLog = "";
    681 #endif
    682         popEmpty();
    683         mPushPop |= type >= kPopLayer_Type;
    684         if (type == kPopLayer_Type) {
    685             Type last = mLayerTypes.last();
    686             // remove empty brackets
    687             if (last == kPushLayer_Type || last == kPushSave_Type) {
    688                 mLayers.removeLast();
    689                 mLayerTypes.removeLast();
    690                 return;
    691             }
    692             // remove push/pop from push/bitmap/pop
    693             size_t pushIndex = mLayerTypes.size() - 2;
    694             if (last == kDrawBitmap_Type
    695                 && mLayerTypes.at(pushIndex) == kPushLayer_Type) {
    696                 mLayers.at(pushIndex) = mLayers.last();
    697                 mLayerTypes.at(pushIndex) = kDrawBitmap_Type;
    698                 mLayers.removeLast();
    699                 mLayerTypes.removeLast();
    700                 return;
    701             }
    702             // remove non-layer brackets
    703             int stack = 0;
    704             Type* types = mLayerTypes.end();
    705             while (types != mLayerTypes.begin()) {
    706                 Type type = *--types;
    707                 if (type == kPopLayer_Type) {
    708                     stack++;
    709                     continue;
    710                 }
    711                 if (type != kPushLayer_Type && type != kPushSave_Type)
    712                     continue;
    713                 if (--stack >= 0)
    714                     continue;
    715                 if (type == kPushLayer_Type)
    716                     break;
    717                 int remove = types - mLayerTypes.begin();
    718                 DBG_NAV_LOGD("RingCheck remove=%d mLayers.size=%d"
    719                     " mLayerTypes.size=%d", remove, mLayers.size(),
    720                     mLayerTypes.size());
    721                 mLayers.remove(remove);
    722                 mLayerTypes.remove(remove);
    723                 mAppendLikeTypes = false;
    724                 return;
    725             }
    726         }
    727         mLayers.append(bounds);
    728         mLayerTypes.append(type);
    729     }
    730 
    731     void startText(const SkPaint& paint)
    732     {
    733         mPaint = &paint;
    734         if (!mLayerTypes.isEmpty() && mLayerTypes.last() == kDrawGlyph_Type
    735             && !mLayers.last().isEmpty()) {
    736             push(kDrawGlyph_Type, mEmpty);
    737         }
    738     }
    739 
    740     bool textOutsideRings()
    741     {
    742         findBestLayer();
    743         if (!mBestLayer) {
    744             DBG_NAV_LOG("RingCheck empty");
    745             return false;
    746         }
    747         const SkRegion* layers = mBestLayer;
    748         const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
    749         // back up to include text drawn before the best layer
    750         SkRegion active = SkRegion(mBitBounds);
    751         active.translate(-mBitBounds.x(), -mBitBounds.y());
    752         while (layers != mLayers.begin()) {
    753             --layers;
    754             Type layerType = *--layerTypes;
    755             DBG_NAV_LOGD("RingCheck #%d %s"
    756                 " mTestBounds=(%d,%d,r=%d,b=%d) layers=(%d,%d,r=%d,b=%d)"
    757                 " active=(%d,%d,r=%d,b=%d)",
    758                 layers - mLayers.begin(), TypeNames[layerType],
    759                 mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
    760                 mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
    761                 layers->getBounds().fLeft, layers->getBounds().fTop,
    762                 layers->getBounds().fRight, layers->getBounds().fBottom,
    763                 active.getBounds().fLeft, active.getBounds().fTop,
    764                 active.getBounds().fRight, active.getBounds().fBottom);
    765             if (layerType == kDrawRect_Type || layerType == kDrawBitmap_Type) {
    766                 SkRegion temp = *layers;
    767                 temp.op(mTestBounds, SkRegion::kIntersect_Op);
    768                 active.op(temp, SkRegion::kDifference_Op);
    769                 if (active.isEmpty()) {
    770                     DBG_NAV_LOGD("RingCheck #%d empty", layers - mLayers.begin());
    771                     break;
    772                 }
    773             } else if (layerType == kDrawGlyph_Type) {
    774                 SkRegion temp = *layers;
    775                 temp.op(active, SkRegion::kIntersect_Op);
    776                 if (!mTestBounds.intersects(temp))
    777                     continue;
    778                 if (!mTestBounds.contains(temp))
    779                     return false;
    780             } else
    781                 break;
    782         }
    783         layers = mBestLayer;
    784         layerTypes = &mLayerTypes[layers - mLayers.begin()];
    785         bool foundGlyph = false;
    786         bool collectGlyphs = true;
    787         do {
    788             Type layerType = *layerTypes++;
    789             DBG_NAV_LOGD("RingCheck #%d %s mTestBounds=(%d,%d,r=%d,b=%d)"
    790             " layers=(%d,%d,r=%d,b=%d) collects=%s intersects=%s contains=%s",
    791                 layers - mLayers.begin(), TypeNames[layerType],
    792                 mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
    793                 mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
    794                 layers->getBounds().fLeft, layers->getBounds().fTop,
    795                 layers->getBounds().fRight, layers->getBounds().fBottom,
    796                 collectGlyphs ? "true" : "false",
    797                 mTestBounds.intersects(*layers) ? "true" : "false",
    798                 mTextSlop.contains(*layers) ? "true" : "false");
    799             if (collectGlyphs && layerType == kDrawGlyph_Type) {
    800                 if (!mTestBounds.intersects(*layers))
    801                     continue;
    802                 if (!mTextSlop.contains(*layers))
    803                     return false;
    804                 foundGlyph = true;
    805             }
    806             collectGlyphs &= layerType != kPushLayer_Type;
    807         } while (++layers != mLayers.end());
    808         DBG_NAV_LOGD("RingCheck foundGlyph=%s", foundGlyph ? "true" : "false");
    809         return foundGlyph;
    810     }
    811 
    812 protected:
    813     virtual bool onIRect(const SkIRect& rect)
    814     {
    815         joinGlyphs(rect);
    816         if (mType != kDrawGlyph_Type && mType != kDrawRect_Type
    817                 && mType != kDrawSprite_Type && mType != kDrawBitmap_Type)
    818             return false;
    819         if (mLayerTypes.isEmpty() || mLayerTypes.last() != mType
    820             || !mAppendLikeTypes || mPushPop || mSingleImage
    821             // if the last and current were not glyphs,
    822             // and the two bounds have a gap between, don't join them -- push
    823             // an empty between them
    824             || (mType != kDrawGlyph_Type && !joinable(rect))) {
    825             push(mType, mEmpty);
    826         }
    827         DBG_NAV_LOGD("RingCheck join %s (%d,%d,r=%d,b=%d) '%c'",
    828             TypeNames[mType], rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    829             mCh);
    830         mLayers.last().op(rect, SkRegion::kUnion_Op);
    831         mAppendLikeTypes = true;
    832         mPushPop = false;
    833         return false;
    834     }
    835 
    836     virtual bool onIRectGlyph(const SkIRect& rect,
    837         const SkBounder::GlyphRec& rec)
    838     {
    839         mCh = ' ';
    840         if (mPaint) {
    841             SkUnichar unichar;
    842             SkPaint utfPaint = *mPaint;
    843             utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    844             utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
    845             mCh = unichar < 0x7f ? unichar : '?';
    846         }
    847         return onIRect(rect);
    848     }
    849 
    850 private:
    851     int calcOverlap(SkRegion& testRegion)
    852     {
    853         if (testRegion.isEmpty())
    854             return INT_MAX;
    855         testRegion.op(mTextTest, SkRegion::kXOR_Op);
    856         SkRegion::Iterator iter(testRegion);
    857         int area = 0;
    858         while (!iter.done()) {
    859             const SkIRect& cr = iter.rect();
    860             area += cr.width() * cr.height();
    861             iter.next();
    862         }
    863         DBG_NAV_LOGD("RingCheck area=%d", area);
    864         return area;
    865     }
    866 
    867     void findBestLayer()
    868     {
    869         popEmpty();
    870         mBestLayer = 0;
    871         const SkRegion* layers = mLayers.begin();
    872         const SkRegion* layersEnd = mLayers.end();
    873         if (layers == layersEnd) {
    874             DBG_NAV_LOG("RingCheck empty");
    875             return;
    876         }
    877         // find text most like focus rings by xoring found with original
    878         int bestArea = INT_MAX;
    879         const SkRegion* testLayer = 0;
    880         SkRegion testRegion;
    881         const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
    882         for (; layers != mLayers.end(); layers++) {
    883             Type layerType = *layerTypes++;
    884 #if DEBUG_NAV_UI
    885             const SkIRect& gb = layers->getBounds();
    886             const SkIRect& tb = mTextSlop.getBounds();
    887             DBG_NAV_LOGD("RingCheck #%d %s mTextSlop=(%d,%d,%d,%d)"
    888                 " contains=%s bounds=(%d,%d,%d,%d)",
    889                 layers - mLayers.begin(), TypeNames[layerType],
    890                 tb.fLeft, tb.fTop, tb.fRight, tb.fBottom,
    891                 mTextSlop.contains(*layers) ? "true" : "false",
    892                 gb.fLeft, gb.fTop, gb.fRight, gb.fBottom);
    893 #endif
    894             if (((layerType == kDrawGlyph_Type || layerType == kDrawBitmap_Type)
    895                 && mTextSlop.contains(*layers))
    896                 || (layerType == kDrawRect_Type
    897                 && mTextTest.contains(*layers))) {
    898                 if (!testLayer)
    899                     testLayer = layers;
    900                 testRegion.op(*layers, SkRegion::kUnion_Op);
    901                 continue;
    902             }
    903             if (testLayer) {
    904                 int area = calcOverlap(testRegion);
    905                 if (bestArea > area) {
    906                     bestArea = area;
    907                     mBestLayer = testLayer;
    908                 }
    909                 DBG_NAV_LOGD("RingCheck #%d push test=%d best=%d",
    910                     layers - mLayers.begin(), testLayer - mLayers.begin(),
    911                     mBestLayer ? mBestLayer - mLayers.begin() : -1);
    912                 testRegion.setEmpty();
    913                 testLayer = 0;
    914             }
    915         }
    916         if (testLayer && bestArea > calcOverlap(testRegion)) {
    917             DBG_NAV_LOGD("RingCheck last best=%d", testLayer - mLayers.begin());
    918             mBestLayer = testLayer;
    919         }
    920     }
    921 
    922     bool joinable(const SkIRect& rect)
    923     {
    924         SkRegion region = mLayers.last();
    925         if (!region.isRect())
    926             return false;
    927         const SkIRect& bounds1 = region.getBounds();
    928         int area1 = bounds1.width() * bounds1.height();
    929         area1 += rect.width() * rect.height();
    930         region.op(rect, SkRegion::kUnion_Op);
    931         const SkIRect& bounds2 = region.getBounds();
    932         int area2 = bounds2.width() * bounds2.height();
    933         return area2 <= area1;
    934     }
    935 
    936     void popEmpty()
    937     {
    938         if (mLayerTypes.size() == 0)
    939             return;
    940         Type last = mLayerTypes.last();
    941         if (last >= kPopLayer_Type)
    942             return;
    943         const SkRegion& area = mLayers.last();
    944         if (!area.isEmpty())
    945             return;
    946         DBG_NAV_LOGD("RingCheck #%d %s", mLayers.size() - 1, TypeNames[last]);
    947         mLayers.removeLast();
    948         mLayerTypes.removeLast();
    949     }
    950 
    951     SkRegion mTestBounds;
    952     IntRect mBitBounds;
    953     SkIRect mEmpty;
    954     const SkRegion* mBestLayer;
    955     SkRegion mTextSlop; // outset rects for inclusion test
    956     SkRegion mTextTest; // exact rects for xor area test
    957     Type mLastType;
    958     Vector<SkRegion> mLayers;
    959     Vector<Type> mLayerTypes;
    960     const SkPaint* mPaint;
    961     char mCh;
    962     bool mAppendLikeTypes;
    963     bool mPushPop;
    964     bool mSingleImage;
    965 };
    966 
    967 class RingCanvas : public BoundsCanvas {
    968 public:
    969     RingCanvas(RingCheck* bounder)
    970         : INHERITED(bounder)
    971     {
    972     }
    973 
    974 protected:
    975     virtual void drawText(const void* text, size_t byteLength, SkScalar x,
    976                           SkScalar y, const SkPaint& paint) {
    977         static_cast<RingCheck&>(mBounder).startText(paint);
    978         INHERITED::drawText(text, byteLength, x, y, paint);
    979     }
    980 
    981     virtual void drawPosText(const void* text, size_t byteLength,
    982                              const SkPoint pos[], const SkPaint& paint) {
    983         static_cast<RingCheck&>(mBounder).startText(paint);
    984         INHERITED::drawPosText(text, byteLength, pos, paint);
    985     }
    986 
    987     virtual void drawTextOnPath(const void* text, size_t byteLength,
    988                                 const SkPath& path, const SkMatrix* matrix,
    989                                 const SkPaint& paint) {
    990         static_cast<RingCheck&>(mBounder).startText(paint);
    991         INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
    992     }
    993 
    994     virtual void drawPosTextH(const void* text, size_t byteLength,
    995                               const SkScalar xpos[], SkScalar constY,
    996                               const SkPaint& paint) {
    997         static_cast<RingCheck&>(mBounder).startText(paint);
    998         INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
    999     }
   1000 
   1001     virtual int save(SaveFlags flags)
   1002     {
   1003         RingCheck& bounder = static_cast<RingCheck&>(mBounder);
   1004         bounder.push(CommonCheck::kPushSave_Type, getTotalClip().getBounds());
   1005         return INHERITED::save(flags);
   1006     }
   1007 
   1008     virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
   1009         SaveFlags flags)
   1010     {
   1011         RingCheck& bounder = static_cast<RingCheck&>(mBounder);
   1012         bounder.push(CommonCheck::kPushLayer_Type, getTotalClip().getBounds());
   1013         return INHERITED::save(flags);
   1014     }
   1015 
   1016     virtual void restore()
   1017     {
   1018         RingCheck& bounder = static_cast<RingCheck&>(mBounder);
   1019         bounder.push(CommonCheck::kPopLayer_Type, getTotalClip().getBounds());
   1020         INHERITED::restore();
   1021     }
   1022 
   1023 private:
   1024     typedef BoundsCanvas INHERITED;
   1025 };
   1026 
   1027 bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction,
   1028     WebCore::IntPoint* scrollPtr, bool findClosest)
   1029 {
   1030     WebCore::IntRect newOutset;
   1031     const CachedNode* newNode = best->mNode;
   1032     // see if there's a middle node
   1033         // if the middle node is in the visited list,
   1034         // or if none was computed and the newNode is in the visited list,
   1035         // treat result as NULL
   1036     if (newNode != NULL && findClosest) {
   1037         if (best->bounds().intersects(mHistory->mPriorBounds) == false &&
   1038                 checkBetween(best, direction))
   1039             newNode = best->mNode;
   1040         if (findClosest && maskIfHidden(best)) {
   1041             innerMove(document(), best, direction, scrollPtr, false);
   1042             return true;
   1043         }
   1044         newOutset = newNode->cursorRingBounds(best->mFrame);
   1045     }
   1046     int delta;
   1047     bool newNodeInView = scrollDelta(newOutset, direction, &delta);
   1048     if (delta && scrollPtr && (newNode == NULL || newNodeInView == false ||
   1049             (best->mNavOutside && best->mWorkingOutside)))
   1050         *scrollPtr = WebCore::IntPoint(direction & UP_DOWN ? 0 : delta,
   1051             direction & UP_DOWN ? delta : 0);
   1052     return false;
   1053 }
   1054 
   1055 void CachedRoot::calcBitBounds(const IntRect& nodeBounds, IntRect* bitBounds) const
   1056 {
   1057     IntRect contentBounds = IntRect(0, 0, mPicture->width(), mPicture->height());
   1058     IntRect overBounds = nodeBounds;
   1059     overBounds.inflate(kMargin);
   1060     IntRect viewableBounds = mScrolledBounds;
   1061     viewableBounds.unite(mViewBounds);
   1062     *bitBounds = contentBounds;
   1063     bitBounds->intersect(overBounds);
   1064     if (!bitBounds->intersects(viewableBounds))
   1065         *bitBounds = IntRect(0, 0, 0, 0);
   1066     DBG_NAV_LOGD("contentBounds=(%d,%d,r=%d,b=%d) overBounds=(%d,%d,r=%d,b=%d)"
   1067         " mScrolledBounds=(%d,%d,r=%d,b=%d) mViewBounds=(%d,%d,r=%d,b=%d)"
   1068         " bitBounds=(%d,%d,r=%d,b=%d)",
   1069         contentBounds.x(), contentBounds.y(), contentBounds.maxX(),
   1070         contentBounds.maxY(),
   1071         overBounds.x(), overBounds.y(), overBounds.maxX(), overBounds.maxY(),
   1072         mScrolledBounds.x(), mScrolledBounds.y(), mScrolledBounds.maxX(),
   1073         mScrolledBounds.maxY(),
   1074         mViewBounds.x(), mViewBounds.y(), mViewBounds.maxX(),
   1075         mViewBounds.maxY(),
   1076         bitBounds->x(), bitBounds->y(), bitBounds->maxX(),
   1077         bitBounds->maxY());
   1078 }
   1079 
   1080 
   1081 int CachedRoot::checkForCenter(int x, int y) const
   1082 {
   1083     int width = mViewBounds.width();
   1084     SkPicture* picture = pictureAt(&x, &y);
   1085     CenterCheck centerCheck(x + width - mViewBounds.x(), y - mViewBounds.y(),
   1086         width);
   1087     BoundsCanvas checker(&centerCheck);
   1088     SkBitmap bitmap;
   1089     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width * 3,
   1090         mViewBounds.height());
   1091     checker.setBitmapDevice(bitmap);
   1092     checker.translate(SkIntToScalar(width - mViewBounds.x()),
   1093         SkIntToScalar(-mViewBounds.y()));
   1094     checker.drawPicture(*picture);
   1095     return centerCheck.center();
   1096 }
   1097 
   1098 void CachedRoot::checkForJiggle(int* xDeltaPtr) const
   1099 {
   1100     int xDelta = *xDeltaPtr;
   1101     JiggleCheck jiggleCheck(xDelta, mViewBounds.width());
   1102     BoundsCanvas checker(&jiggleCheck);
   1103     SkBitmap bitmap;
   1104     int absDelta = abs(xDelta);
   1105     bitmap.setConfig(SkBitmap::kARGB_8888_Config, mViewBounds.width() +
   1106         absDelta, mViewBounds.height());
   1107     checker.setBitmapDevice(bitmap);
   1108     int x = -mViewBounds.x() - (xDelta < 0 ? xDelta : 0);
   1109     int y = -mViewBounds.y();
   1110     SkPicture* picture = pictureAt(&x, &y);
   1111     checker.translate(SkIntToScalar(x), SkIntToScalar(y));
   1112     checker.drawPicture(*picture);
   1113     *xDeltaPtr = jiggleCheck.jiggle();
   1114 }
   1115 
   1116 bool CachedRoot::checkRings(SkPicture* picture, const CachedNode* node,
   1117         const WebCore::IntRect& testBounds) const
   1118 {
   1119     if (!picture)
   1120         return false;
   1121     const WTF::Vector<WebCore::IntRect>& rings = node->rings();
   1122     const WebCore::IntRect& nodeBounds = node->rawBounds();
   1123     IntRect bitBounds;
   1124     calcBitBounds(nodeBounds, &bitBounds);
   1125     RingCheck ringCheck(rings, bitBounds, testBounds, node->singleImage());
   1126     RingCanvas checker(&ringCheck);
   1127     SkBitmap bitmap;
   1128     bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
   1129         bitBounds.height());
   1130     checker.setBitmapDevice(bitmap);
   1131     checker.translate(SkIntToScalar(-bitBounds.x()),
   1132         SkIntToScalar(-bitBounds.y()));
   1133     checker.drawPicture(*picture);
   1134     bool result = ringCheck.textOutsideRings();
   1135     DBG_NAV_LOGD("bitBounds=(%d,%d,r=%d,b=%d) nodeBounds=(%d,%d,r=%d,b=%d)"
   1136         " testBounds=(%d,%d,r=%d,b=%d) success=%s",
   1137         bitBounds.x(), bitBounds.y(), bitBounds.maxX(), bitBounds.maxY(),
   1138         nodeBounds.x(), nodeBounds.y(), nodeBounds.maxX(), nodeBounds.maxY(),
   1139         testBounds.x(), testBounds.y(), testBounds.maxX(), testBounds.maxY(),
   1140         result ? "true" : "false");
   1141     return result;
   1142 }
   1143 
   1144 void CachedRoot::draw(FindCanvas& canvas) const
   1145 {
   1146     canvas.setLayerId(-1); // overlays change the ID as their pictures draw
   1147     canvas.drawPicture(*mPicture);
   1148 #if USE(ACCELERATED_COMPOSITING)
   1149     if (!mRootLayer)
   1150         return;
   1151     canvas.drawLayers(mRootLayer);
   1152 #endif
   1153 }
   1154 
   1155 const CachedNode* CachedRoot::findAt(const WebCore::IntRect& rect,
   1156     const CachedFrame** framePtr, int* x, int* y, bool checkForHidden) const
   1157 {
   1158 #if DEBUG_NAV_UI
   1159     DBG_NAV_LOGD("rect=(%d,%d,w=%d,h=%d) xy=(%d,%d)", rect.x(), rect.y(),
   1160         rect.width(), rect.height(), *x, *y);
   1161 #if DUMP_NAV_CACHE
   1162     if (mRootLayer) CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
   1163 #endif
   1164 #endif
   1165     int best = INT_MAX;
   1166     bool inside = false;
   1167     (const_cast<CachedRoot*>(this))->resetClippedOut();
   1168     const CachedFrame* directHitFramePtr;
   1169     const CachedNode* directHit = NULL;
   1170     const CachedNode* node = findBestAt(rect, &best, &inside, &directHit,
   1171         &directHitFramePtr, framePtr, x, y, checkForHidden);
   1172     DBG_NAV_LOGD("node=%d (%p) xy=(%d,%d)", node == NULL ? 0 : node->index(),
   1173         node == NULL ? NULL : node->nodePointer(), *x, *y);
   1174     if (node == NULL) {
   1175         node = findBestHitAt(rect, framePtr, x, y);
   1176         DBG_NAV_LOGD("node=%d (%p)", node == NULL ? 0 : node->index(),
   1177             node == NULL ? NULL : node->nodePointer());
   1178     }
   1179     if (node == NULL) {
   1180         *framePtr = findBestFrameAt(rect.x() + (rect.width() >> 1),
   1181             rect.y() + (rect.height() >> 1));
   1182     }
   1183     return node;
   1184 }
   1185 
   1186 WebCore::IntPoint CachedRoot::cursorLocation() const
   1187 {
   1188     const WebCore::IntRect& bounds = mHistory->mNavBounds;
   1189     return WebCore::IntPoint(bounds.x() + (bounds.width() >> 1),
   1190         bounds.y() + (bounds.height() >> 1));
   1191 }
   1192 
   1193 WebCore::IntPoint CachedRoot::focusLocation() const
   1194 {
   1195     return WebCore::IntPoint(mFocusBounds.x() + (mFocusBounds.width() >> 1),
   1196         mFocusBounds.y() + (mFocusBounds.height() >> 1));
   1197 }
   1198 
   1199 // These reset the values because we only want to get the selection the first time.
   1200 // After that, the selection is no longer accurate.
   1201 int CachedRoot::getAndResetSelectionEnd()
   1202 {
   1203     int end = mSelectionEnd;
   1204     mSelectionEnd = -1;
   1205     return end;
   1206 }
   1207 
   1208 int CachedRoot::getAndResetSelectionStart()
   1209 {
   1210     int start = mSelectionStart;
   1211     mSelectionStart = -1;
   1212     return start;
   1213 }
   1214 
   1215 int CachedRoot::getBlockLeftEdge(int x, int y, float scale) const
   1216 {
   1217     DBG_NAV_LOGD("x=%d y=%d scale=%g mViewBounds=(%d,%d,%d,%d)", x, y, scale,
   1218         mViewBounds.x(), mViewBounds.y(), mViewBounds.width(),
   1219         mViewBounds.height());
   1220     // if (x, y) is in a textArea or textField, return that
   1221     const int slop = 1;
   1222     WebCore::IntRect rect = WebCore::IntRect(x - slop, y - slop,
   1223         slop * 2, slop * 2);
   1224     const CachedFrame* frame;
   1225     int fx, fy;
   1226     const CachedNode* node = findAt(rect, &frame, &fx, &fy, true);
   1227     if (node && node->wantsKeyEvents()) {
   1228         DBG_NAV_LOGD("x=%d (%s)", node->bounds(frame).x(),
   1229             node->isTextInput() ? "text" : "plugin");
   1230         return node->bounds(frame).x();
   1231     }
   1232     SkPicture* picture = node ? frame->picture(node, &x, &y) : pictureAt(&x, &y);
   1233     if (!picture)
   1234         return x;
   1235     int halfW = (int) (mViewBounds.width() * scale * 0.5f);
   1236     int fullW = halfW << 1;
   1237     int halfH = (int) (mViewBounds.height() * scale * 0.5f);
   1238     int fullH = halfH << 1;
   1239     LeftCheck leftCheck(fullW, halfH);
   1240     BoundsCanvas checker(&leftCheck);
   1241     SkBitmap bitmap;
   1242     bitmap.setConfig(SkBitmap::kARGB_8888_Config, fullW, fullH);
   1243     checker.setBitmapDevice(bitmap);
   1244     checker.translate(SkIntToScalar(fullW - x), SkIntToScalar(halfH - y));
   1245     checker.drawPicture(*picture);
   1246     int result = x + leftCheck.left() - fullW;
   1247     DBG_NAV_LOGD("halfW=%d halfH=%d mMostLeft=%d x=%d",
   1248         halfW, halfH, leftCheck.mMostLeft, result);
   1249     return result;
   1250 }
   1251 
   1252 void CachedRoot::getSimulatedMousePosition(WebCore::IntPoint* point) const
   1253 {
   1254 #ifndef NDEBUG
   1255     ASSERT(CachedFrame::mDebug.mInUse);
   1256 #endif
   1257     const WebCore::IntRect& mouseBounds = mHistory->mMouseBounds;
   1258     int x = mouseBounds.x();
   1259     int y = mouseBounds.y();
   1260     int width = mouseBounds.width();
   1261     int height = mouseBounds.height();
   1262     point->setX(x + (width >> 1)); // default to box center
   1263     point->setY(y + (height >> 1));
   1264 #if DEBUG_NAV_UI
   1265     const WebCore::IntRect& navBounds = mHistory->mNavBounds;
   1266     DBG_NAV_LOGD("mHistory->mNavBounds={%d,%d,%d,%d} "
   1267         "mHistory->mMouseBounds={%d,%d,%d,%d} point={%d,%d}",
   1268         navBounds.x(), navBounds.y(), navBounds.width(), navBounds.height(),
   1269         mouseBounds.x(), mouseBounds.y(), mouseBounds.width(),
   1270         mouseBounds.height(), point->x(), point->y());
   1271 #endif
   1272 }
   1273 
   1274 void CachedRoot::init(WebCore::Frame* frame, CachedHistory* history)
   1275 {
   1276     CachedFrame::init(this, -1, frame);
   1277     reset();
   1278     mHistory = history;
   1279     mPicture = NULL;
   1280 }
   1281 
   1282 bool CachedRoot::innerDown(const CachedNode* test, BestData* bestData) const
   1283 {
   1284     ASSERT(minWorkingVertical() >= mViewBounds.x());
   1285     ASSERT(maxWorkingVertical() <= mViewBounds.maxX());
   1286     setupScrolledBounds();
   1287     // (line up)
   1288     mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
   1289     int testTop = mScrolledBounds.y();
   1290     int viewBottom = mViewBounds.maxY();
   1291     const WebCore::IntRect& navBounds = mHistory->mNavBounds;
   1292     if (navBounds.isEmpty() == false &&
   1293             navBounds.maxY() > viewBottom && viewBottom < mContents.height())
   1294         return false;
   1295     if (navBounds.isEmpty() == false) {
   1296         int navTop = navBounds.y();
   1297         int scrollBottom;
   1298         if (testTop < navTop && navTop < (scrollBottom = mScrolledBounds.maxY())) {
   1299             mScrolledBounds.setHeight(scrollBottom - navTop);
   1300             mScrolledBounds.setY(navTop);
   1301         }
   1302     }
   1303     setCursorCache(0, mMaxYScroll);
   1304     frameDown(test, NULL, bestData);
   1305     return true;
   1306 }
   1307 
   1308 bool CachedRoot::innerLeft(const CachedNode* test, BestData* bestData) const
   1309 {
   1310     ASSERT(minWorkingHorizontal() >= mViewBounds.y());
   1311     ASSERT(maxWorkingHorizontal() <= mViewBounds.maxY());
   1312     setupScrolledBounds();
   1313     mScrolledBounds.setX(mScrolledBounds.x() - mMaxXScroll);
   1314     mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
   1315     int testRight = mScrolledBounds.maxX();
   1316     int viewLeft = mViewBounds.x();
   1317     const WebCore::IntRect& navBounds = mHistory->mNavBounds;
   1318     if (navBounds.isEmpty() == false &&
   1319             navBounds.x() < viewLeft && viewLeft > mContents.x())
   1320         return false;
   1321     if (navBounds.isEmpty() == false) {
   1322         int navRight = navBounds.maxX();
   1323         int scrollLeft;
   1324         if (testRight > navRight && navRight > (scrollLeft = mScrolledBounds.x()))
   1325             mScrolledBounds.setWidth(navRight - scrollLeft);
   1326     }
   1327     setCursorCache(-mMaxXScroll, 0);
   1328     frameLeft(test, NULL, bestData);
   1329     return true;
   1330 }
   1331 
   1332 
   1333 void CachedRoot::innerMove(const CachedNode* node, BestData* bestData,
   1334     Direction direction, WebCore::IntPoint* scroll, bool firstCall)
   1335 {
   1336     bestData->reset();
   1337     bool outOfCursor = mCursorIndex == CURSOR_CLEARED;
   1338     DBG_NAV_LOGD("mHistory->didFirstLayout()=%s && mCursorIndex=%d",
   1339         mHistory->didFirstLayout() ? "true" : "false", mCursorIndex);
   1340     if (mHistory->didFirstLayout() && mCursorIndex < CURSOR_SET) {
   1341         mHistory->reset();
   1342         outOfCursor = true;
   1343     }
   1344     const CachedFrame* cursorFrame;
   1345     const CachedNode* cursor = currentCursor(&cursorFrame);
   1346     mHistory->setWorking(direction, cursorFrame, cursor, mViewBounds);
   1347     bool findClosest = false;
   1348     if (mScrollOnly == false) {
   1349         switch (direction) {
   1350             case LEFT:
   1351                 if (outOfCursor)
   1352                     mHistory->mNavBounds = WebCore::IntRect(mViewBounds.maxX(),
   1353                         mViewBounds.y(), 1, mViewBounds.height());
   1354                 findClosest = innerLeft(node, bestData);
   1355                 break;
   1356             case RIGHT:
   1357                 if (outOfCursor)
   1358                     mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x() - 1,
   1359                         mViewBounds.y(), 1, mViewBounds.height());
   1360                 findClosest = innerRight(node, bestData);
   1361                 break;
   1362             case UP:
   1363                 if (outOfCursor)
   1364                     mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(),
   1365                         mViewBounds.maxY(), mViewBounds.width(), 1);
   1366                 findClosest = innerUp(node, bestData);
   1367                 break;
   1368             case DOWN:
   1369                 if (outOfCursor)
   1370                     mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(),
   1371                         mViewBounds.y() - 1, mViewBounds.width(), 1);
   1372                 findClosest = innerDown(node, bestData);
   1373                 break;
   1374             case UNINITIALIZED:
   1375             default:
   1376                 ASSERT(0);
   1377         }
   1378     }
   1379     if (firstCall)
   1380         mHistory->mPriorBounds = mHistory->mNavBounds; // bounds always advances, even if new node is ultimately NULL
   1381     bestData->setMouseBounds(bestData->bounds());
   1382     if (adjustForScroll(bestData, direction, scroll, findClosest))
   1383         return;
   1384     if (bestData->mNode != NULL) {
   1385         mHistory->addToVisited(bestData->mNode, direction);
   1386         mHistory->mNavBounds = bestData->bounds();
   1387         mHistory->mMouseBounds = bestData->mouseBounds();
   1388     } else if (scroll->x() != 0 || scroll->y() != 0) {
   1389         WebCore::IntRect newBounds = mHistory->mNavBounds;
   1390         int offsetX = scroll->x();
   1391         int offsetY = scroll->y();
   1392         newBounds.move(offsetX, offsetY);
   1393         if (mViewBounds.x() > newBounds.x())
   1394             offsetX = mViewBounds.x() - mHistory->mNavBounds.x();
   1395         else if (mViewBounds.maxX() < newBounds.maxX())
   1396             offsetX = mViewBounds.maxX() - mHistory->mNavBounds.maxX();
   1397         if (mViewBounds.y() > newBounds.y())
   1398             offsetY = mViewBounds.y() - mHistory->mNavBounds.y();
   1399         else if (mViewBounds.maxY() < newBounds.maxY())
   1400             offsetY = mViewBounds.maxY() - mHistory->mNavBounds.maxY();
   1401         mHistory->mNavBounds.move(offsetX, offsetY);
   1402     }
   1403     mHistory->setDidFirstLayout(false);
   1404 }
   1405 
   1406 bool CachedRoot::innerRight(const CachedNode* test, BestData* bestData) const
   1407 {
   1408     ASSERT(minWorkingHorizontal() >= mViewBounds.y());
   1409     ASSERT(maxWorkingHorizontal() <= mViewBounds.maxY());
   1410     setupScrolledBounds();
   1411     // (align)
   1412     mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
   1413     int testLeft = mScrolledBounds.x();
   1414     int viewRight = mViewBounds.maxX();
   1415     const WebCore::IntRect& navBounds = mHistory->mNavBounds;
   1416     if (navBounds.isEmpty() == false &&
   1417             navBounds.maxX() > viewRight && viewRight < mContents.width())
   1418         return false;
   1419     if (navBounds.isEmpty() == false) {
   1420         int navLeft = navBounds.x();
   1421         int scrollRight;
   1422         if (testLeft < navLeft && navLeft < (scrollRight = mScrolledBounds.maxX())) {
   1423             mScrolledBounds.setWidth(scrollRight - navLeft);
   1424             mScrolledBounds.setX(navLeft);
   1425         }
   1426     }
   1427     setCursorCache(mMaxXScroll, 0);
   1428     frameRight(test, NULL, bestData);
   1429     return true;
   1430 }
   1431 
   1432 bool CachedRoot::innerUp(const CachedNode* test, BestData* bestData) const
   1433 {
   1434     ASSERT(minWorkingVertical() >= mViewBounds.x());
   1435     ASSERT(maxWorkingVertical() <= mViewBounds.maxX());
   1436     setupScrolledBounds();
   1437     mScrolledBounds.setY(mScrolledBounds.y() - mMaxYScroll);
   1438     mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
   1439     int testBottom = mScrolledBounds.maxY();
   1440     int viewTop = mViewBounds.y();
   1441     const WebCore::IntRect& navBounds = mHistory->mNavBounds;
   1442     if (navBounds.isEmpty() == false &&
   1443             navBounds.y() < viewTop && viewTop > mContents.y())
   1444         return false;
   1445     if (navBounds.isEmpty() == false) {
   1446         int navBottom = navBounds.maxY();
   1447         int scrollTop;
   1448         if (testBottom > navBottom && navBottom > (scrollTop = mScrolledBounds.y()))
   1449             mScrolledBounds.setHeight(navBottom - scrollTop);
   1450     }
   1451     setCursorCache(0, -mMaxYScroll);
   1452     frameUp(test, NULL, bestData);
   1453     return true;
   1454 }
   1455 
   1456 WTF::String CachedRoot::imageURI(int x, int y) const
   1457 {
   1458     DBG_NAV_LOGD("x/y=(%d,%d)", x, y);
   1459     ImageCheck imageCheck;
   1460     ImageCanvas checker(&imageCheck);
   1461     SkBitmap bitmap;
   1462     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
   1463     checker.setBitmapDevice(bitmap);
   1464     SkPicture* picture = pictureAt(&x, &y);
   1465     checker.translate(SkIntToScalar(-x), SkIntToScalar(-y));
   1466     checker.drawPicture(*picture);
   1467     DBG_NAV_LOGD("uri=%s", checker.getURI());
   1468     return WTF::String(checker.getURI());
   1469 }
   1470 
   1471 bool CachedRoot::maskIfHidden(BestData* best) const
   1472 {
   1473     const CachedNode* bestNode = best->mNode;
   1474     if (bestNode->isUnclipped())
   1475         return false;
   1476     const CachedFrame* frame = best->mFrame;
   1477     SkPicture* picture = frame->picture(bestNode);
   1478     if (picture == NULL) {
   1479         DBG_NAV_LOG("missing picture");
   1480         return false;
   1481     }
   1482     Vector<IntRect> rings;
   1483     bestNode->cursorRings(frame, &rings);
   1484     const WebCore::IntRect& bounds = bestNode->bounds(frame);
   1485     IntRect bitBounds;
   1486     calcBitBounds(bounds, &bitBounds);
   1487     RingCheck ringCheck(rings, bitBounds, bounds, bestNode->singleImage());
   1488     RingCanvas checker(&ringCheck);
   1489     SkBitmap bitmap;
   1490     bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
   1491         bitBounds.height());
   1492     checker.setBitmapDevice(bitmap);
   1493     checker.translate(SkIntToScalar(-bitBounds.x()),
   1494         SkIntToScalar(-bitBounds.y()));
   1495     checker.drawPicture(*picture);
   1496     SkRegion clipRgn;
   1497     bool clipped = ringCheck.hiddenRings(&clipRgn);
   1498     CachedNode* node = const_cast<CachedNode*>(best->mNode);
   1499     DBG_NAV_LOGD("clipped=%s clipRgn.isEmpty=%s", clipped ? "true" : "false",
   1500         clipRgn.isEmpty() ? "true" : "false");
   1501     if (clipped && clipRgn.isEmpty()) {
   1502         node->setDisabled(true);
   1503         IntRect clippedBounds = bounds;
   1504         clippedBounds.intersect(bitBounds);
   1505         node->setClippedOut(clippedBounds != bounds);
   1506         return true;
   1507     }
   1508     // was it partially occluded by later drawing?
   1509     // if partially occluded, modify the bounds so that the mouse click has a better x,y
   1510     if (clipped) {
   1511         DBG_NAV_LOGD("clipped clipRgn={%d,%d,r=%d,b=%d}",
   1512             clipRgn.getBounds().fLeft, clipRgn.getBounds().fTop,
   1513             clipRgn.getBounds().fRight, clipRgn.getBounds().fBottom);
   1514         best->setMouseBounds(clipRgn.getBounds());
   1515         if (!node->clip(best->mouseBounds())) {
   1516             node->setDisabled(true);
   1517             node->setClippedOut(true);
   1518             return true;
   1519         }
   1520     } else
   1521         node->fixUpCursorRects(frame);
   1522     return false;
   1523 }
   1524 
   1525 const CachedNode* CachedRoot::moveCursor(Direction direction, const CachedFrame** framePtr,
   1526     WebCore::IntPoint* scroll)
   1527 {
   1528 #ifndef NDEBUG
   1529     ASSERT(CachedFrame::mDebug.mInUse);
   1530 #endif
   1531     CachedRoot* frame = this;
   1532     const CachedNode* node = frame->document();
   1533     if (node == NULL)
   1534         return NULL;
   1535     if (mViewBounds.isEmpty())
   1536         return NULL;
   1537     resetClippedOut();
   1538     setData();
   1539     BestData bestData;
   1540     innerMove(node, &bestData, direction, scroll, true);
   1541     // if node is partially or fully concealed by layer, scroll it into view
   1542     if (mRootLayer && bestData.mNode && !bestData.mNode->isInLayer()) {
   1543 #if USE(ACCELERATED_COMPOSITING)
   1544 #if DUMP_NAV_CACHE
   1545         CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
   1546 #endif
   1547         SkIRect original = bestData.mNode->cursorRingBounds(bestData.mFrame);
   1548         DBG_NAV_LOGD("original=(%d,%d,w=%d,h=%d) scroll=(%d,%d)",
   1549             original.fLeft, original.fTop, original.width(), original.height(),
   1550             scroll->x(), scroll->y());
   1551         original.offset(-scroll->x(), -scroll->y());
   1552         SkRegion rings(original);
   1553         SkTDArray<SkRect> region;
   1554         mRootLayer->clipArea(&region);
   1555         SkRegion layers;
   1556         for (int index = 0; index < region.count(); index++) {
   1557             SkIRect enclosing;
   1558             region[index].round(&enclosing);
   1559             rings.op(enclosing, SkRegion::kDifference_Op);
   1560             layers.op(enclosing, SkRegion::kUnion_Op);
   1561         }
   1562         SkIRect layerBounds(layers.getBounds());
   1563         SkIRect ringBounds(rings.getBounds());
   1564         int scrollX = scroll->x();
   1565         int scrollY = scroll->y();
   1566         if (rings.getBounds() != original) {
   1567             int topOverlap = layerBounds.fBottom - original.fTop;
   1568             int bottomOverlap = original.fBottom - layerBounds.fTop;
   1569             int leftOverlap = layerBounds.fRight - original.fLeft;
   1570             int rightOverlap = original.fRight - layerBounds.fLeft;
   1571             if (direction & UP_DOWN) {
   1572                 if (layerBounds.fLeft < original.fLeft && leftOverlap < 0)
   1573                     scroll->setX(leftOverlap);
   1574                 if (original.fRight < layerBounds.fRight && rightOverlap > 0
   1575                         && -leftOverlap > rightOverlap)
   1576                     scroll->setX(rightOverlap);
   1577                 bool topSet = scrollY > topOverlap && (direction == UP
   1578                     || !scrollY);
   1579                 if (topSet)
   1580                     scroll->setY(topOverlap);
   1581                 if (scrollY < bottomOverlap && (direction == DOWN || (!scrollY
   1582                         && (!topSet || -topOverlap > bottomOverlap))))
   1583                     scroll->setY(bottomOverlap);
   1584             } else {
   1585                 if (layerBounds.fTop < original.fTop && topOverlap < 0)
   1586                     scroll->setY(topOverlap);
   1587                 if (original.fBottom < layerBounds.fBottom && bottomOverlap > 0
   1588                         && -topOverlap > bottomOverlap)
   1589                     scroll->setY(bottomOverlap);
   1590                 bool leftSet = scrollX > leftOverlap && (direction == LEFT
   1591                     || !scrollX);
   1592                 if (leftSet)
   1593                     scroll->setX(leftOverlap);
   1594                 if (scrollX < rightOverlap && (direction == RIGHT || (!scrollX
   1595                         && (!leftSet || -leftOverlap > rightOverlap))))
   1596                     scroll->setX(rightOverlap);
   1597            }
   1598             DBG_NAV_LOGD("rings=(%d,%d,w=%d,h=%d) layers=(%d,%d,w=%d,h=%d)"
   1599                 " scroll=(%d,%d)",
   1600                 ringBounds.fLeft, ringBounds.fTop, ringBounds.width(), ringBounds.height(),
   1601                 layerBounds.fLeft, layerBounds.fTop, layerBounds.width(), layerBounds.height(),
   1602                 scroll->x(), scroll->y());
   1603         }
   1604 #endif
   1605     }
   1606     *framePtr = bestData.mFrame;
   1607     return const_cast<CachedNode*>(bestData.mNode);
   1608 }
   1609 
   1610 const CachedNode* CachedRoot::nextTextField(const CachedNode* start,
   1611         const CachedFrame** framePtr) const
   1612 {
   1613     bool startFound = false;
   1614     return CachedFrame::nextTextField(start, framePtr, &startFound);
   1615 }
   1616 
   1617 SkPicture* CachedRoot::pictureAt(int* xPtr, int* yPtr, int* id) const
   1618 {
   1619 #if USE(ACCELERATED_COMPOSITING)
   1620     if (mRootLayer) {
   1621         const LayerAndroid* layer = mRootLayer->find(xPtr, yPtr, mPicture);
   1622         if (layer) {
   1623             SkPicture* picture = layer->picture();
   1624             DBG_NAV_LOGD("layer %d picture=%p (%d,%d)", layer->uniqueId(),
   1625                 picture, picture ? picture->width() : 0,
   1626                 picture ? picture->height() : 0);
   1627             if (picture) {
   1628                 if (id)
   1629                     *id = layer->uniqueId();
   1630                 return picture;
   1631             }
   1632         }
   1633     }
   1634 #endif
   1635     DBG_NAV_LOGD("root mPicture=%p (%d,%d)", mPicture, mPicture ?
   1636         mPicture->width() : 0, mPicture ? mPicture->height() : 0);
   1637     if (id)
   1638         *id = -1;
   1639     return mPicture;
   1640 }
   1641 
   1642 void CachedRoot::reset()
   1643 {
   1644 #ifndef NDEBUG
   1645     ASSERT(CachedFrame::mDebug.mInUse);
   1646 #endif
   1647     mContents = mViewBounds = WebCore::IntRect(0, 0, 0, 0);
   1648     mMaxXScroll = mMaxYScroll = 0;
   1649     mRootLayer = 0;
   1650     mSelectionStart = mSelectionEnd = -1;
   1651     mScrollOnly = false;
   1652 }
   1653 
   1654 bool CachedRoot::scrollDelta(WebCore::IntRect& newOutset, Direction direction, int* delta)
   1655 {
   1656     switch (direction) {
   1657         case LEFT:
   1658             *delta = -mMaxXScroll;
   1659             return newOutset.x() >= mViewBounds.x();
   1660         case RIGHT:
   1661             *delta = mMaxXScroll;
   1662             return newOutset.maxX() <= mViewBounds.maxX();
   1663         case UP:
   1664             *delta = -mMaxYScroll;
   1665             return newOutset.y() >= mViewBounds.y();
   1666         case DOWN:
   1667             *delta = mMaxYScroll;
   1668             return newOutset.maxY() <= mViewBounds.maxY();
   1669         default:
   1670             *delta = 0;
   1671             ASSERT(0);
   1672     }
   1673     return false;
   1674 }
   1675 
   1676 void CachedRoot::setCachedFocus(CachedFrame* frame, CachedNode* node)
   1677 {
   1678     mFocusBounds = WebCore::IntRect(0, 0, 0, 0);
   1679     if (node == NULL)
   1680         return;
   1681     node->setIsFocus(true);
   1682     mFocusBounds = node->bounds(frame);
   1683     frame->setFocusIndex(node - frame->document());
   1684     CachedFrame* parent;
   1685     while ((parent = frame->parent()) != NULL) {
   1686         parent->setFocusIndex(frame->indexInParent());
   1687         frame = parent;
   1688     }
   1689 #if DEBUG_NAV_UI
   1690     const CachedFrame* focusFrame;
   1691     const CachedNode* focus = currentFocus(&focusFrame);
   1692     WebCore::IntRect bounds = WebCore::IntRect(0, 0, 0, 0);
   1693     if (focus)
   1694         bounds = focus->bounds(focusFrame);
   1695     DBG_NAV_LOGD("new focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
   1696         focus ? focus->index() : 0,
   1697         focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(),
   1698         bounds.width(), bounds.height());
   1699 #endif
   1700 }
   1701 
   1702 void CachedRoot::setCursor(CachedFrame* frame, CachedNode* node)
   1703 {
   1704 #if DEBUG_NAV_UI
   1705     const CachedFrame* cursorFrame;
   1706     const CachedNode* cursor = currentCursor(&cursorFrame);
   1707     WebCore::IntRect bounds;
   1708     if (cursor)
   1709         bounds = cursor->bounds(cursorFrame);
   1710     DBG_NAV_LOGD("old cursor %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
   1711         cursor ? cursor->index() : 0,
   1712         cursor ? cursor->nodePointer() : NULL, bounds.x(), bounds.y(),
   1713         bounds.width(), bounds.height());
   1714 #endif
   1715     clearCursor();
   1716     if (node == NULL)
   1717         return;
   1718     node->setIsCursor(true);
   1719     node->show();
   1720     frame->setCursorIndex(node - frame->document());
   1721     CachedFrame* parent;
   1722     while ((parent = frame->parent()) != NULL) {
   1723         parent->setCursorIndex(frame->indexInParent());
   1724         frame = parent;
   1725     }
   1726 #if DEBUG_NAV_UI
   1727     cursor = currentCursor(&cursorFrame);
   1728     bounds = WebCore::IntRect(0, 0, 0, 0);
   1729     if (cursor)
   1730         bounds = cursor->bounds(cursorFrame);
   1731     DBG_NAV_LOGD("new cursor %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
   1732         cursor ? cursor->index() : 0,
   1733         cursor ? cursor->nodePointer() : NULL, bounds.x(), bounds.y(),
   1734         bounds.width(), bounds.height());
   1735 #endif
   1736 }
   1737 
   1738 void CachedRoot::setCursorCache(int scrollX, int scrollY) const
   1739 {
   1740     mCursor = currentCursor();
   1741     if (mCursor)
   1742         mCursorBounds = mCursor->bounds(this);
   1743     if (!mRootLayer)
   1744         return;
   1745     SkRegion baseScrolled(mScrolledBounds);
   1746     mBaseUncovered = SkRegion(mScrolledBounds);
   1747 #if USE(ACCELERATED_COMPOSITING)
   1748 #if DUMP_NAV_CACHE
   1749     CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
   1750 #endif
   1751     SkTDArray<SkRect> region;
   1752     mRootLayer->clipArea(&region);
   1753     WebCore::IntSize offset(
   1754         copysign(min(max(0, mContents.width() - mScrolledBounds.width()),
   1755         abs(scrollX)), scrollX),
   1756         copysign(min(max(0, mContents.height() - mScrolledBounds.height()),
   1757         abs(scrollY)), scrollY));
   1758     bool hasOffset = offset.width() || offset.height();
   1759     // restrict scrollBounds to that which is not under layer
   1760     for (int index = 0; index < region.count(); index++) {
   1761         SkIRect less;
   1762         region[index].round(&less);
   1763         DBG_NAV_LOGD("less=(%d,%d,w=%d,h=%d)", less.fLeft, less.fTop,
   1764             less.width(), less.height());
   1765         mBaseUncovered.op(less, SkRegion::kDifference_Op);
   1766         if (!hasOffset)
   1767             continue;
   1768         less.offset(offset.width(), offset.height());
   1769         baseScrolled.op(less, SkRegion::kDifference_Op);
   1770     }
   1771     if (hasOffset)
   1772         mBaseUncovered.op(baseScrolled, SkRegion::kUnion_Op);
   1773 #endif
   1774 }
   1775 
   1776 #if DUMP_NAV_CACHE
   1777 
   1778 #define DEBUG_PRINT_BOOL(field) \
   1779     DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
   1780 
   1781 #define DEBUG_PRINT_RECT(field) \
   1782     { const WebCore::IntRect& r = b->field; \
   1783     DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
   1784         r.x(), r.y(), r.width(), r.height()); }
   1785 
   1786 CachedRoot* CachedRoot::Debug::base() const {
   1787     CachedRoot* nav = (CachedRoot*) ((char*) this - OFFSETOF(CachedRoot, mDebug));
   1788     return nav;
   1789 }
   1790 
   1791 void CachedRoot::Debug::print() const
   1792 {
   1793 #ifdef DUMP_NAV_CACHE_USING_PRINTF
   1794     gWriteLogMutex.lock();
   1795     ASSERT(gNavCacheLogFile == NULL);
   1796     gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a");
   1797 #endif
   1798     CachedRoot* b = base();
   1799     b->CachedFrame::mDebug.print();
   1800     b->mHistory->mDebug.print(b);
   1801     DUMP_NAV_LOGD("// int mMaxXScroll=%d, mMaxYScroll=%d;\n",
   1802         b->mMaxXScroll, b->mMaxYScroll);
   1803     if (b->mRootLayer)
   1804         CachedLayer::Debug::printRootLayerAndroid(b->mRootLayer);
   1805 #ifdef DUMP_NAV_CACHE_USING_PRINTF
   1806     if (gNavCacheLogFile)
   1807         fclose(gNavCacheLogFile);
   1808     gNavCacheLogFile = NULL;
   1809     gWriteLogMutex.unlock();
   1810 #endif
   1811 }
   1812 
   1813 #endif
   1814 
   1815 }
   1816