use one rectangle for browser focus ring

Check to see if the potentially larger hit-test bounds
can be used in place of the normal bounds, or if the
normal bounds can be used in place of the individual
text bounds.

Construct a region out of the individual focus ring
rectangles, then see if any text is drawn inside the
bounds but outside of the focus ring. If not, use one
rectangle instead of the rings.
diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp
index 57054e1..f68c2f2 100644
--- a/WebKit/android/nav/CachedFrame.cpp
+++ b/WebKit/android/nav/CachedFrame.cpp
@@ -305,7 +305,7 @@
     const CachedFrame* frame = hasFrame(result);
     if (frame != NULL)
         return frame->currentFocus(framePtr);
-    (const_cast<CachedNode*>(result))->fixUpFocusRects();
+    (const_cast<CachedNode*>(result))->fixUpFocusRects(mRoot);
     return result;
 }
 
diff --git a/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp
index b786677..07d3491 100644
--- a/WebKit/android/nav/CachedNode.cpp
+++ b/WebKit/android/nav/CachedNode.cpp
@@ -24,8 +24,8 @@
  */
 
 #include "CachedPrefix.h"
-#include "CachedFrame.h"
 #include "CachedHistory.h"
+#include "CachedRoot.h"
 #include "Node.h"
 #include "PlatformString.h"
 
@@ -78,13 +78,29 @@
 
 #define OVERLAP 3
 
-void CachedNode::fixUpFocusRects()
+void CachedNode::fixUpFocusRects(const CachedRoot* root)
 {
     if (mFixedUpFocusRects)
         return;
     mFixedUpFocusRects = true;
+    // if the hit-test rect doesn't intersect any other rect, use it
+    if (mHitBounds != mBounds && mHitBounds.contains(mBounds) &&
+            root->checkRings(mFocusRing, mHitBounds)) {
+        DBG_NAV_LOGD("use mHitBounds (%d,%d,%d,%d)", mHitBounds.x(),
+            mHitBounds.y(), mHitBounds.width(), mHitBounds.height());
+        mUseHitBounds = true;
+        return;
+    }
     if (mNavableRects <= 1)
         return;
+    // if there is more than 1 rect, and the bounds doesn't intersect
+    // any other focus ring bounds, use it
+    if (root->checkRings(mFocusRing, mBounds)) {
+        DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(),
+            mBounds.y(), mBounds.width(), mBounds.height());
+        mUseBounds = true;
+        return;
+    }
 #if DEBUG_NAV_UI
     {
         WebCore::IntRect* boundsPtr = mFocusRing.begin() - 1;
diff --git a/WebKit/android/nav/CachedNode.h b/WebKit/android/nav/CachedNode.h
index aa64982..bdde9e0 100644
--- a/WebKit/android/nav/CachedNode.h
+++ b/WebKit/android/nav/CachedNode.h
@@ -40,6 +40,7 @@
 namespace android {
 
 class CachedFrame;
+class CachedRoot;
 
 class CachedNode {
 public:
@@ -95,7 +96,7 @@
     bool clippedOut() { return mClippedOut; }
     bool disabled() const { return mDisabled; }
     const CachedNode* document() const { return &this[-mIndex]; }
-    void fixUpFocusRects();
+    void fixUpFocusRects(const CachedRoot* root);
     void focusRingBounds(WebCore::IntRect* ) const;
     WTF::Vector<WebCore::IntRect>& focusRings() { return mFocusRing; }
     const WTF::Vector<WebCore::IntRect>& focusRings() const { return mFocusRing; }
@@ -169,6 +170,8 @@
     const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; }
     int textSize() const { return mTextSize; }
     CachedNodeType type() const { return mType; }
+    bool useBounds() const { return mUseBounds; }
+    bool useHitBounds() const { return mUseHitBounds; }
 private:
     WebCore::String mExport;
     WebCore::String mName;
@@ -203,6 +206,8 @@
     bool mIsTransparent : 1;
     bool mIsUnclipped : 1;
     bool mLast : 1;             // true if this is the last node in a group
+    bool mUseBounds : 1;
+    bool mUseHitBounds : 1;
     bool mWantsKeyEvents : 1;   // true for nodes like plugins
 #ifdef BROWSER_DEBUG
 public:
diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp
index a123e55..3b8871a 100644
--- a/WebKit/android/nav/CachedRoot.cpp
+++ b/WebKit/android/nav/CachedRoot.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "CachedPrefix.h"
+#include "android_graphics.h"
 #include "CachedHistory.h"
 #include "CachedNode.h"
 #include "SkBitmap.h"
@@ -530,6 +531,37 @@
     int mMaxWidth;
 };
 
+class RingCheck : public CommonCheck {
+public:
+    RingCheck(const WTF::Vector<WebCore::IntRect>& rings,
+            const WebCore::IntPoint& location) : mSuccess(true) {
+        const WebCore::IntRect* r;
+        for (r = rings.begin(); r != rings.end(); r++) {
+            SkIRect fatter = {r->x(), r->y(), r->right(), r->bottom()};
+            fatter.inset(-FOCUS_RING_HIT_TEST_RADIUS, -FOCUS_RING_HIT_TEST_RADIUS);
+            DBG_NAV_LOGD("%s fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop,
+                fatter.fRight, fatter.fBottom);
+            mRings.op(fatter, SkRegion::kUnion_Op);
+        }
+        DBG_NAV_LOGD("translate=(%d,%d)", -location.x(), -location.y());
+        mRings.translate(-location.x(), -location.y());
+    }
+
+    virtual bool onIRect(const SkIRect& rect) {
+        if (mSuccess && mType == kDrawGlyph_Type) {
+            DBG_NAV_LOGD("contains (%d,%d,r=%d,b=%d) == %s", rect.fLeft, rect.fTop,
+                rect.fRight, rect.fBottom, mRings.contains(rect) ? "true" :
+                "false");
+            mSuccess &= mRings.contains(rect);
+        }
+        return false;
+    }
+
+    bool success() { return mSuccess; }
+    SkRegion mRings;
+    bool mSuccess;
+};
+
 bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction, 
     WebCore::IntPoint* scrollPtr, bool findClosest)
 {        
@@ -591,6 +623,23 @@
     *xDeltaPtr = jiggleCheck.jiggle();
 }
 
+bool CachedRoot::checkRings(const WTF::Vector<WebCore::IntRect>& rings,
+        const WebCore::IntRect& bounds) const
+{
+    RingCheck ringCheck(rings, bounds.location());
+    BoundsCanvas checker(&ringCheck);
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
+        bounds.height());
+    checker.setBitmapDevice(bitmap);
+    checker.translate(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y()));
+    checker.drawPicture(*mPicture);
+    DBG_NAV_LOGD("bounds=(%d,%d,r=%d,b=%d) success=%s",
+        bounds.x(), bounds.y(), bounds.right(), bounds.bottom(),
+        ringCheck.success() ? "true" : "false");
+    return ringCheck.success();
+}
+
 const CachedNode* CachedRoot::findAt(const WebCore::IntRect& rect,
     const CachedFrame** framePtr, int* x, int* y) const
 {
diff --git a/WebKit/android/nav/CachedRoot.h b/WebKit/android/nav/CachedRoot.h
index ab1b823..d06e46c 100644
--- a/WebKit/android/nav/CachedRoot.h
+++ b/WebKit/android/nav/CachedRoot.h
@@ -27,8 +27,9 @@
 #define CachedRoot_H
 
 #include "CachedFrame.h"
-#include "IntPoint.h"
+#include "IntRect.h"
 #include "SkPicture.h"
+#include "wtf/Vector.h"
 
 class SkRect;
 
@@ -43,6 +44,8 @@
         bool findClosest);
     int checkForCenter(int x, int y) const;
     void checkForJiggle(int* ) const;
+    bool checkRings(const WTF::Vector<WebCore::IntRect>& rings,
+        const WebCore::IntRect& bounds) const;
     int documentHeight() { return mContents.height(); }
     int documentWidth() { return mContents.width(); }
     const CachedNode* findAt(const WebCore::IntRect& , const CachedFrame** ,
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index 7b98e56..c259501 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -700,12 +700,11 @@
         DBG_NAV_LOG("!node->hasFocusRing()");
         return;
     }
-    const WTF::Vector<WebCore::IntRect>& rings = node->focusRings();
-    if (!rings.size()) {
-        DBG_NAV_LOG("!rings.size()");
+    const WTF::Vector<WebCore::IntRect>* rings = &node->focusRings();
+    if (!rings->size()) {
+        DBG_NAV_LOG("!rings->size()");
         return;
     }
-
     bool isButton = false;
     m_viewImpl->gButtonMutex.lock();
     // If this is a button drawn by us (rather than webkit) do not draw the
@@ -731,6 +730,7 @@
         return;
     }
     FocusRing::Flavor flavor = FocusRing::NORMAL_FLAVOR;
+    WTF::Vector<WebCore::IntRect> oneRing;
     if (!isButton) {
         flavor = node->type() != NORMAL_CACHEDNODETYPE ?
             FocusRing::FAKE_FLAVOR : node->nodePointer() == m_invalidNode ?
@@ -738,15 +738,20 @@
         if (flavor != FocusRing::INVALID_FLAVOR && m_followedLink) {
             flavor = (FocusRing::Flavor) (flavor + FocusRing::NORMAL_ANIMATING);
         }
+        bool useHitBounds = node->useHitBounds();
+        if (useHitBounds || node->useBounds()) {
+            oneRing.append(useHitBounds ? node->hitBounds() : node->bounds());
+            rings = &oneRing;
+        }
 #if DEBUG_NAV_UI
-        const WebCore::IntRect& ring = rings[0];
+        const WebCore::IntRect& ring = (*rings)[0];
         DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p) flavor=%s rings=%d"
             " (%d, %d, %d, %d)", node->index(), node->nodePointer(),
             flavor == FocusRing::FAKE_FLAVOR ? "FAKE_FLAVOR" :
             flavor == FocusRing::INVALID_FLAVOR ? "INVALID_FLAVOR" :
             flavor == FocusRing::NORMAL_ANIMATING ? "NORMAL_ANIMATING" :
             flavor == FocusRing::FAKE_ANIMATING ? "FAKE_ANIMATING" : "NORMAL_FLAVOR",
-            rings.size(), ring.x(), ring.y(), ring.width(), ring.height());
+            rings->size(), ring.x(), ring.y(), ring.width(), ring.height());
 #endif
     }
     if (isButton || flavor >= FocusRing::NORMAL_ANIMATING) {
@@ -761,7 +766,7 @@
         }
     }
     if (!isButton)
-        FocusRing::DrawRing(canvas, rings, flavor);
+        FocusRing::DrawRing(canvas, *rings, flavor);
 }
 
 OutOfFocusFix fixOutOfDateFocus(bool useReplay)