summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebKit/android/nav/CacheBuilder.cpp13
-rw-r--r--WebKit/android/nav/CacheBuilder.h2
-rw-r--r--WebKit/android/nav/CachedFrame.cpp17
-rw-r--r--WebKit/android/nav/CachedFrame.h3
-rw-r--r--WebKit/android/nav/CachedNode.cpp6
-rw-r--r--WebKit/android/nav/CachedRoot.cpp609
-rw-r--r--WebKit/android/nav/CachedRoot.h4
-rw-r--r--WebKit/android/nav/WebView.cpp2
8 files changed, 511 insertions, 145 deletions
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp
index 300f2a2..1d53721 100644
--- a/WebKit/android/nav/CacheBuilder.cpp
+++ b/WebKit/android/nav/CacheBuilder.cpp
@@ -486,7 +486,7 @@ void CacheBuilder::Debug::groups() {
const RenderBox* box = static_cast<RenderBox*>(renderer);
const IntRect& oRect = box->visibleOverflowRect();
snprintf(scratch, sizeof(scratch), "// renderBlock:"
- " columnRects=%d columnGap=%d direction=%d"
+ " columnCount=%d columnGap=%d direction=%d"
" hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)",
renderBlock->columnInfo()->columnCount(), renderBlock->columnGap(),
renderBlock->style()->direction(), renderer->hasOverflowClip(),
@@ -1026,7 +1026,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
FocusTracker* last = &tracker.last();
int lastChildIndex = cachedFrame->size() - 1;
while (node == last->mLastChild) {
- if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
cacheIndex--;
tracker.removeLast();
lastChildIndex = last->mCachedNodeIndex;
@@ -1460,14 +1460,14 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
while (tracker.size() > 1) {
FocusTracker* last = &tracker.last();
int lastChildIndex = cachedFrame->size() - 1;
- if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
cacheIndex--;
tracker.removeLast();
}
}
-bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame,
- const FocusTracker* last, int lastChildIndex)
+bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot,
+ CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex)
{
// if outer is body, disable outer
// or if there's more than one inner, disable outer
@@ -1496,9 +1496,12 @@ bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame,
HasTriggerEvent(lastNode) == false;
if (onlyChildCached->parent() == lastCached)
onlyChildCached->setParentIndex(lastCached->parentIndex());
+ bool hasFocus = lastCached->isFocus() || onlyChildCached->isFocus();
if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL))
*lastCached = *onlyChildCached;
cachedFrame->removeLast();
+ if (hasFocus)
+ cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode());
return true;
}
diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h
index cb61c9f..9832865 100644
--- a/WebKit/android/nav/CacheBuilder.h
+++ b/WebKit/android/nav/CacheBuilder.h
@@ -219,7 +219,7 @@ private:
static bool NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length);
void BuildFrame(Frame* root, Frame* frame,
CachedRoot* cachedRoot, CachedFrame* cachedFrame);
- bool CleanUpContainedNodes(CachedFrame* cachedFrame,
+ bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame,
const FocusTracker* last, int lastChildIndex);
static bool ConstructTextRect(Text* textNode,
InlineTextBox* textBox, int start, int relEnd, int x, int y,
diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp
index 4219d66..81ef299 100644
--- a/WebKit/android/nav/CachedFrame.cpp
+++ b/WebKit/android/nav/CachedFrame.cpp
@@ -132,9 +132,10 @@ bool CachedFrame::checkBetween(BestData* best, Direction direction)
bool CachedFrame::checkRings(const CachedNode* node,
const WTF::Vector<WebCore::IntRect>& rings,
- const WebCore::IntRect& bounds) const
+ const WebCore::IntRect& nodeBounds,
+ const WebCore::IntRect& testBounds) const
{
- return mRoot->checkRings(picture(node), rings, bounds);
+ return mRoot->checkRings(picture(node), rings, nodeBounds, testBounds);
}
bool CachedFrame::checkVisited(const CachedNode* node, Direction direction) const
@@ -511,8 +512,15 @@ const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect,
testData.setNodeBounds(testRect);
if (mRoot->maskIfHidden(&testData) == true)
continue;
+ DBG_NAV_LOGD("candidate %d rect=(%d,%d,r=%d,b=%d)"
+ " testRect=(%d,%d,r=%d,b=%d)",
+ test->index(), rect.x(), rect.y(), rect.right(), rect.bottom(),
+ testRect.x(), testRect.y(), testRect.right(), testRect.bottom());
for (int i = 0; i < test->navableRects(); i++) {
WebCore::IntRect cursorRect = test->ring(this, i);
+ DBG_NAV_LOGD("candidate %d cursorRect=(%d,%d,r=%d,b=%d)",
+ i, cursorRect.x(), cursorRect.y(), cursorRect.right(),
+ cursorRect.bottom());
if (cursorRect.intersects(rect)) {
WebCore::IntRect intersection(cursorRect);
intersection.intersect(rect);
@@ -523,6 +531,11 @@ const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect,
return test;
}
}
+ testRect.intersect(rect);
+ *x = testRect.x() + (testRect.width() >> 1);
+ *y = testRect.y() + (testRect.height() >> 1);
+ *framePtr = this;
+ return test;
}
return NULL;
}
diff --git a/WebKit/android/nav/CachedFrame.h b/WebKit/android/nav/CachedFrame.h
index 590c75d..bd47421 100644
--- a/WebKit/android/nav/CachedFrame.h
+++ b/WebKit/android/nav/CachedFrame.h
@@ -84,7 +84,8 @@ public:
const WebCore::IntRect& ) const;
bool checkRings(const CachedNode* node,
const WTF::Vector<WebCore::IntRect>& rings,
- const WebCore::IntRect& bounds) const;
+ const WebCore::IntRect& nodeBounds,
+ const WebCore::IntRect& testBounds) const;
bool checkVisited(const CachedNode* , CachedFrame::Direction ) const;
size_t childCount() { return mCachedFrames.size(); }
void clearCursor();
diff --git a/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp
index 47711fd..4ba7b48 100644
--- a/WebKit/android/nav/CachedNode.cpp
+++ b/WebKit/android/nav/CachedNode.cpp
@@ -110,7 +110,7 @@ void CachedNode::fixUpCursorRects(const CachedFrame* frame)
mFixedUpCursorRects = true;
// if the hit-test rect doesn't intersect any other rect, use it
if (mHitBounds != mBounds && mHitBounds.contains(mBounds) &&
- frame->checkRings(this, mCursorRing, mHitBounds)) {
+ frame->checkRings(this, mCursorRing, mBounds, mHitBounds)) {
DBG_NAV_LOGD("use mHitBounds (%d,%d,%d,%d)", mHitBounds.x(),
mHitBounds.y(), mHitBounds.width(), mHitBounds.height());
mUseHitBounds = true;
@@ -120,7 +120,9 @@ void CachedNode::fixUpCursorRects(const CachedFrame* frame)
return;
// if there is more than 1 rect, and the bounds doesn't intersect
// any other cursor ring bounds, use it
- if (frame->checkRings(this, mCursorRing, mBounds)) {
+ IntRect sloppyBounds = mBounds;
+ sloppyBounds.inflate(2); // give it a couple of extra pixels
+ if (frame->checkRings(this, mCursorRing, mBounds, sloppyBounds)) {
DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(),
mBounds.y(), mBounds.width(), mBounds.height());
mUseBounds = true;
diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp
index 15726ec..e86999c 100644
--- a/WebKit/android/nav/CachedRoot.cpp
+++ b/WebKit/android/nav/CachedRoot.cpp
@@ -38,6 +38,10 @@
#include "CachedRoot.h"
+#if DEBUG_NAV_UI
+#include "wtf/text/CString.h"
+#endif
+
using std::min;
using std::max;
@@ -62,7 +66,10 @@ public:
kDrawRect_Type,
kDrawSprite_Type,
kDrawText_Type,
- kDrawTextOnPath_Type
+ kDrawTextOnPath_Type,
+ kPopLayer_Type,
+ kPushLayer_Type,
+ kPushSave_Type
};
static bool isTextType(Type t) {
@@ -110,7 +117,10 @@ public:
"kDrawRect_Type",
"kDrawSprite_Type",
"kDrawText_Type",
- "kDrawTextOnPath_Type"
+ "kDrawTextOnPath_Type",
+ "kPopLayer_Type",
+ "kPushLayer_Type",
+ "kPushSave_Type"
};
#endif
@@ -147,8 +157,9 @@ public:
virtual bool onIRect(const SkIRect& rect) {
if (joinGlyphs(rect))
return false;
- bool interestingType = mType == kDrawBitmap_Type ||
- mType == kDrawRect_Type || isTextType(mType);
+ bool interestingType = mType == kDrawBitmap_Type
+ || mType == kDrawSprite_Type
+ || mType == kDrawRect_Type || isTextType(mType);
if (SkIRect::Intersects(mBounds, rect) == false) {
DBG_NAV_LOGD("BoundsCheck (no intersect) rect={%d,%d,%d,%d}"
" mType=%s", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
@@ -255,9 +266,10 @@ public:
}
virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
- const SkPaint* paint = NULL) {
+ const SkPaint* paint) {
mBounder.setType(CommonCheck::kDrawSprite_Type);
- mBounder.setIsOpaque(bitmap.isOpaque());
+ mBounder.setIsOpaque(bitmap.isOpaque() &&
+ (!paint || paint->getAlpha() == 255));
SkCanvas::drawSprite(bitmap, left, top, paint);
}
@@ -283,8 +295,10 @@ public:
mBounder.setEmpty();
mBounder.setType(CommonCheck::kDrawGlyph_Type);
SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
- if (mBounder.mUnion.isEmpty())
+ if (mBounder.mUnion.isEmpty()) {
+ DBG_NAV_LOGD("empty constY=%g", SkScalarToFloat(constY));
return;
+ }
SkPaint::FontMetrics metrics;
paint.getFontMetrics(&metrics);
SkPoint upDown[2] = { {xpos[0], constY + metrics.fAscent},
@@ -314,7 +328,7 @@ public:
virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags) {
- int depth = SkCanvas::saveLayer(bounds, paint, flags);
+ int depth = SkCanvas::save(flags);
if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) {
mTransparentLayer = depth;
mBounder.setAllOpaque(false);
@@ -323,6 +337,7 @@ public:
}
virtual void restore() {
+ mBounder.setType(CommonCheck::kDrawSprite_Type); // for layer draws
int depth = getSaveCount();
if (depth == mTransparentLayer) {
mTransparentLayer = 0;
@@ -653,32 +668,404 @@ public:
class RingCheck : public CommonCheck {
public:
RingCheck(const WTF::Vector<WebCore::IntRect>& rings,
- const WebCore::IntPoint& location) : mSuccess(true) {
+ const WebCore::IntRect& bitBounds, const WebCore::IntRect& testBounds)
+ : mTestBounds(testBounds)
+ , mBitBounds(bitBounds)
+ {
const WebCore::IntRect* r;
for (r = rings.begin(); r != rings.end(); r++) {
SkIRect fatter = {r->x(), r->y(), r->right(), r->bottom()};
fatter.inset(-CURSOR_RING_HIT_TEST_RADIUS, -CURSOR_RING_HIT_TEST_RADIUS);
- DBG_NAV_LOGD("fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop,
+ DBG_NAV_LOGD("RingCheck fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop,
fatter.fRight, fatter.fBottom);
- mRings.op(fatter, SkRegion::kUnion_Op);
+ mTextSlop.op(fatter, SkRegion::kUnion_Op);
+ mTextTest.op(*r, SkRegion::kUnion_Op);
}
- DBG_NAV_LOGD("translate=(%d,%d)", -location.x(), -location.y());
- mRings.translate(-location.x(), -location.y());
+ int dx = -bitBounds.x();
+ int dy = -bitBounds.y();
+ DBG_NAV_LOGD("RingCheck translate=(%d,%d)", dx, dy);
+ mTextSlop.translate(dx, dy);
+ mTextTest.translate(dx, dy);
+ mTestBounds.translate(dx, dy);
+ mEmpty.setEmpty();
}
- 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);
+ bool hiddenRings(SkRegion* clipped)
+ {
+ findBestLayer();
+ if (!mBestLayer) {
+ DBG_NAV_LOG("RingCheck empty");
+ clipped->setEmpty();
+ return true;
}
+ const SkRegion* layersEnd = mLayers.end();
+ const Type* layerTypes = &mLayerTypes[mBestLayer - mLayers.begin()];
+ bool collectGlyphs = true;
+ bool collectOvers = false;
+ SkRegion over;
+ for (const SkRegion* layers = mBestLayer; layers != layersEnd; layers++) {
+ Type layerType = *layerTypes++;
+ DBG_NAV_LOGD("RingCheck #%d %s (%d,%d,r=%d,b=%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom);
+ if (collectGlyphs && layerType == kDrawGlyph_Type) {
+ DBG_NAV_LOGD("RingCheck #%d collectOvers", layers - mLayers.begin());
+ collectOvers = true;
+ clipped->op(*layers, SkRegion::kUnion_Op);
+ }
+ collectGlyphs &= layerType != kPushLayer_Type;
+ if (collectOvers && (layerType == kDrawRect_Type
+ || (!collectGlyphs && layerType == kDrawSprite_Type)))
+ {
+ DBG_NAV_LOGD("RingCheck #%d over.op", layers - mLayers.begin());
+ over.op(*layers, SkRegion::kUnion_Op);
+ }
+ }
+ bool result = !collectOvers || clipped->intersects(over);
+ const SkIRect t = clipped->getBounds();
+ const SkIRect o = over.getBounds();
+ clipped->op(over, SkRegion::kDifference_Op);
+ clipped->translate(mBitBounds.x(), mBitBounds.y());
+ const SkIRect c = clipped->getBounds();
+ DBG_NAV_LOGD("RingCheck intersects=%s text=(%d,%d,r=%d,b=%d)"
+ " over=(%d,%d,r=%d,b=%d) clipped=(%d,%d,r=%d,b=%d)",
+ result ? "true" : "false",
+ t.fLeft, t.fTop, t.fRight, t.fBottom,
+ o.fLeft, o.fTop, o.fRight, o.fBottom,
+ c.fLeft, c.fTop, c.fRight, c.fBottom);
+ return result;
+ }
+
+ void push(Type type, const SkIRect& bounds)
+ {
+#if DEBUG_NAV_UI
+ // this caches the push string and subquently ignores if pushSave
+ // is immediately followed by popLayer. Push/pop pairs happen
+ // frequently and just add noise to the log.
+ static String lastLog;
+ String currentLog = String("RingCheck append #")
+ + String::number(mLayers.size())
+ + " type=" + TypeNames[type] + " bounds=("
+ + String::number(bounds.fLeft)
+ + "," + String::number(bounds.fTop) + ","
+ + String::number(bounds.fRight) + ","
+ + String::number(bounds.fBottom) + ")";
+ if (lastLog.length() == 0 || type != kPopLayer_Type) {
+ if (lastLog.length() != 0)
+ DBG_NAV_LOGD("%s", lastLog.latin1().data());
+ if (type == kPushSave_Type)
+ lastLog = currentLog;
+ else
+ DBG_NAV_LOGD("%s", currentLog.latin1().data());
+ } else
+ lastLog = "";
+#endif
+ popEmpty();
+ if (type == kPopLayer_Type) {
+ Type last = mLayerTypes.last();
+ // remove empty brackets
+ if (last == kPushLayer_Type || last == kPushSave_Type) {
+ mLayers.removeLast();
+ mLayerTypes.removeLast();
+ return;
+ }
+ // remove non-layer brackets
+ int stack = 0;
+ Type* types = mLayerTypes.end();
+ while (types != mLayerTypes.begin()) {
+ Type type = *--types;
+ if (type == kPopLayer_Type) {
+ stack++;
+ continue;
+ }
+ if (type != kPushLayer_Type && type != kPushSave_Type)
+ continue;
+ if (--stack >= 0)
+ continue;
+ if (type == kPushLayer_Type)
+ break;
+ int remove = types - mLayerTypes.begin();
+ DBG_NAV_LOGD("RingCheck remove=%d mLayers.size=%d"
+ " mLayerTypes.size=%d", remove, mLayers.size(),
+ mLayerTypes.size());
+ mLayers.remove(remove);
+ mLayerTypes.remove(remove);
+ return;
+ }
+ }
+ mLayers.append(bounds);
+ mLayerTypes.append(type);
+ }
+
+ void startText(const SkPaint& paint)
+ {
+ mPaint = &paint;
+ if (!mLayerTypes.isEmpty() && mLayerTypes.last() == kDrawGlyph_Type
+ && !mLayers.last().isEmpty()) {
+ push(kDrawGlyph_Type, mEmpty);
+ }
+ }
+
+ bool textOutsideRings()
+ {
+ findBestLayer();
+ if (!mBestLayer) {
+ DBG_NAV_LOG("RingCheck empty");
+ return false;
+ }
+ const SkRegion* layers = mBestLayer;
+ const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ // back up to include text drawn before the best layer
+ SkRegion active = SkRegion(mBitBounds);
+ active.translate(-mBitBounds.x(), -mBitBounds.y());
+ while (layers != mLayers.begin()) {
+ --layers;
+ Type layerType = *--layerTypes;
+ DBG_NAV_LOGD("RingCheck #%d %s"
+ " mTestBounds=(%d,%d,r=%d,b=%d) layers=(%d,%d,r=%d,b=%d)"
+ " active=(%d,%d,r=%d,b=%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
+ mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom,
+ active.getBounds().fLeft, active.getBounds().fTop,
+ active.getBounds().fRight, active.getBounds().fBottom);
+ if (layerType == kDrawRect_Type) {
+ SkRegion temp = *layers;
+ temp.op(mTestBounds, SkRegion::kIntersect_Op);
+ active.op(temp, SkRegion::kDifference_Op);
+ if (active.isEmpty()) {
+ DBG_NAV_LOGD("RingCheck #%d empty", layers - mLayers.begin());
+ break;
+ }
+ } else if (layerType == kDrawGlyph_Type) {
+ SkRegion temp = *layers;
+ temp.op(active, SkRegion::kIntersect_Op);
+ if (!mTestBounds.intersects(temp))
+ continue;
+ if (!mTestBounds.contains(temp))
+ return false;
+ } else
+ break;
+ }
+ layers = mBestLayer;
+ layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ bool foundGlyph = false;
+ bool collectGlyphs = true;
+ do {
+ Type layerType = *layerTypes++;
+ DBG_NAV_LOGD("RingCheck #%d %s mTestBounds=(%d,%d,r=%d,b=%d)"
+ " layers=(%d,%d,r=%d,b=%d) collects=%s intersects=%s contains=%s",
+ layers - mLayers.begin(), TypeNames[layerType],
+ mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
+ mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom,
+ collectGlyphs ? "true" : "false",
+ mTestBounds.intersects(*layers) ? "true" : "false",
+ mTestBounds.contains(*layers) ? "true" : "false");
+ if (collectGlyphs && layerType == kDrawGlyph_Type) {
+ if (!mTestBounds.intersects(*layers))
+ continue;
+ if (!mTestBounds.contains(*layers))
+ return false;
+ foundGlyph = true;
+ }
+ collectGlyphs &= layerType != kPushLayer_Type;
+ } while (++layers != mLayers.end());
+ DBG_NAV_LOGD("RingCheck foundGlyph=%s", foundGlyph ? "true" : "false");
+ return foundGlyph;
+ }
+
+protected:
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ joinGlyphs(rect);
+ if (mType != kDrawGlyph_Type && mType != kDrawRect_Type
+ && mType != kDrawSprite_Type)
+ return false;
+ if (mLayerTypes.isEmpty() || mLayerTypes.last() != mType)
+ push(mType, mEmpty);
+ DBG_NAV_LOGD("RingCheck join %s (%d,%d,r=%d,b=%d) '%c'",
+ TypeNames[mType], rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ mCh);
+ mLayers.last().op(rect, SkRegion::kUnion_Op);
return false;
}
- bool success() { return mSuccess; }
- SkRegion mRings;
- bool mSuccess;
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ mCh = ' ';
+ if (mPaint) {
+ SkUnichar unichar;
+ SkPaint utfPaint = *mPaint;
+ utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
+ mCh = unichar < 0x7f ? unichar : '?';
+ }
+ return onIRect(rect);
+ }
+
+private:
+ int calcOverlap(SkRegion& testRegion)
+ {
+ if (testRegion.isEmpty())
+ return INT_MAX;
+ testRegion.op(mTextTest, SkRegion::kXOR_Op);
+ SkRegion::Iterator iter(testRegion);
+ int area = 0;
+ while (!iter.done()) {
+ const SkIRect& cr = iter.rect();
+ area += cr.width() * cr.height();
+ iter.next();
+ }
+ DBG_NAV_LOGD("RingCheck area=%d", area);
+ return area;
+ }
+
+ void findBestLayer()
+ {
+ popEmpty();
+ mBestLayer = 0;
+ const SkRegion* layers = mLayers.begin();
+ const SkRegion* layersEnd = mLayers.end();
+ if (layers == layersEnd) {
+ DBG_NAV_LOG("RingCheck empty");
+ return;
+ }
+ // find text most like focus rings by xoring found with original
+ int bestArea = INT_MAX;
+ const SkRegion* testLayer = 0;
+ SkRegion testRegion;
+ const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ for (; layers != mLayers.end(); layers++) {
+ Type layerType = *layerTypes++;
+#if DEBUG_NAV_UI
+ const SkIRect& gb = layers->getBounds();
+ const SkIRect& tb = mTextSlop.getBounds();
+ DBG_NAV_LOGD("RingCheck #%d %s mTextSlop=(%d,%d,%d,%d)"
+ " contains=%s bounds=(%d,%d,%d,%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ tb.fLeft, tb.fTop, tb.fRight, tb.fBottom,
+ mTextSlop.contains(*layers) ? "true" : "false",
+ gb.fLeft, gb.fTop, gb.fRight, gb.fBottom);
+#endif
+ if (layerType == kDrawGlyph_Type) {
+ if (!testLayer)
+ testLayer = layers;
+ if (mTextSlop.contains(*layers)) {
+ testRegion.op(*layers, SkRegion::kUnion_Op);
+ continue;
+ }
+ }
+ if (testLayer) {
+ int area = calcOverlap(testRegion);
+ if (bestArea > area) {
+ bestArea = area;
+ mBestLayer = testLayer;
+ }
+ DBG_NAV_LOGD("RingCheck #%d push test=%d best=%d",
+ layers - mLayers.begin(), testLayer - mLayers.begin(),
+ mBestLayer ? mBestLayer - mLayers.begin() : -1);
+ testRegion.setEmpty();
+ testLayer = 0;
+ }
+ }
+ if (testLayer && bestArea > calcOverlap(testRegion)) {
+ DBG_NAV_LOGD("RingCheck last best=%d", testLayer - mLayers.begin());
+ mBestLayer = testLayer;
+ }
+ }
+
+ void popEmpty()
+ {
+ if (mLayerTypes.size() == 0)
+ return;
+ Type last = mLayerTypes.last();
+ if (last >= kPopLayer_Type)
+ return;
+ const SkRegion& area = mLayers.last();
+ if (!area.isEmpty())
+ return;
+ DBG_NAV_LOGD("RingCheck #%d %s", mLayers.size() - 1, TypeNames[last]);
+ mLayers.removeLast();
+ mLayerTypes.removeLast();
+ }
+
+ SkRegion mTestBounds;
+ IntRect mBitBounds;
+ SkIRect mEmpty;
+ const SkRegion* mBestLayer;
+ SkRegion mTextSlop; // outset rects for inclusion test
+ SkRegion mTextTest; // exact rects for xor area test
+ Type mLastType;
+ Vector<SkRegion> mLayers;
+ Vector<Type> mLayerTypes;
+ const SkPaint* mPaint;
+ char mCh;
+};
+
+class RingCanvas : public BoundsCanvas {
+public:
+ RingCanvas(RingCheck* bounder)
+ : INHERITED(bounder)
+ {
+ }
+
+protected:
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawText(text, byteLength, x, y, paint);
+ }
+
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawPosText(text, byteLength, pos, paint);
+ }
+
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
+ }
+
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+ }
+
+ virtual int save(SaveFlags flags)
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPushSave_Type, getTotalClip().getBounds());
+ return INHERITED::save(flags);
+ }
+
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags)
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPushLayer_Type, getTotalClip().getBounds());
+ return INHERITED::save(flags);
+ }
+
+ virtual void restore()
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPopLayer_Type, getTotalClip().getBounds());
+ INHERITED::restore();
+ }
+
+private:
+ typedef BoundsCanvas INHERITED;
};
bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction,
@@ -709,6 +1096,29 @@ bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction directio
return false;
}
+void CachedRoot::calcBitBounds(const IntRect& nodeBounds, IntRect* bitBounds) const
+{
+ IntRect contentBounds = IntRect(0, 0, mPicture->width(), mPicture->height());
+ IntRect overBounds = nodeBounds;
+ overBounds.inflate(kMargin);
+ *bitBounds = mScrolledBounds;
+ bitBounds->unite(mViewBounds);
+ bitBounds->intersect(contentBounds);
+ bitBounds->intersect(overBounds);
+ DBG_NAV_LOGD("contentBounds=(%d,%d,r=%d,b=%d) overBounds=(%d,%d,r=%d,b=%d)"
+ " mScrolledBounds=(%d,%d,r=%d,b=%d) mViewBounds=(%d,%d,r=%d,b=%d)"
+ " bitBounds=(%d,%d,r=%d,b=%d)",
+ contentBounds.x(), contentBounds.y(), contentBounds.right(),
+ contentBounds.bottom(),
+ overBounds.x(), overBounds.y(), overBounds.right(), overBounds.bottom(),
+ mScrolledBounds.x(), mScrolledBounds.y(), mScrolledBounds.right(),
+ mScrolledBounds.bottom(),
+ mViewBounds.x(), mViewBounds.y(), mViewBounds.right(),
+ mViewBounds.bottom(),
+ bitBounds->x(), bitBounds->y(), bitBounds->right(),
+ bitBounds->bottom());
+}
+
int CachedRoot::checkForCenter(int x, int y) const
{
@@ -745,22 +1155,30 @@ void CachedRoot::checkForJiggle(int* xDeltaPtr) const
bool CachedRoot::checkRings(SkPicture* picture,
const WTF::Vector<WebCore::IntRect>& rings,
- const WebCore::IntRect& bounds) const
+ const WebCore::IntRect& nodeBounds,
+ const WebCore::IntRect& testBounds) const
{
if (!picture)
return false;
- RingCheck ringCheck(rings, bounds.location());
- BoundsCanvas checker(&ringCheck);
+ IntRect bitBounds;
+ calcBitBounds(nodeBounds, &bitBounds);
+ RingCheck ringCheck(rings, bitBounds, testBounds);
+ RingCanvas checker(&ringCheck);
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
- bounds.height());
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
+ bitBounds.height());
checker.setBitmapDevice(bitmap);
- checker.translate(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y()));
+ checker.translate(SkIntToScalar(-bitBounds.x()),
+ SkIntToScalar(-bitBounds.y()));
checker.drawPicture(*picture);
- 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();
+ bool result = ringCheck.textOutsideRings();
+ DBG_NAV_LOGD("bitBounds=(%d,%d,r=%d,b=%d) nodeBounds=(%d,%d,r=%d,b=%d)"
+ " testBounds=(%d,%d,r=%d,b=%d) success=%s",
+ bitBounds.x(), bitBounds.y(), bitBounds.right(), bitBounds.bottom(),
+ nodeBounds.x(), nodeBounds.y(), nodeBounds.right(), nodeBounds.bottom(),
+ testBounds.x(), testBounds.y(), testBounds.right(), testBounds.bottom(),
+ result ? "true" : "false");
+ return result;
}
void CachedRoot::draw(FindCanvas& canvas) const
@@ -1085,7 +1503,7 @@ WTF::String CachedRoot::imageURI(int x, int y) const
bool CachedRoot::maskIfHidden(BestData* best) const
{
const CachedNode* bestNode = best->mNode;
- if (bestNode->isUnclipped() || bestNode->isTransparent())
+ if (bestNode->isUnclipped())
return false;
const CachedFrame* frame = best->mFrame;
SkPicture* picture = frame->picture(bestNode);
@@ -1093,115 +1511,42 @@ bool CachedRoot::maskIfHidden(BestData* best) const
DBG_NAV_LOG("missing picture");
return false;
}
- // given the picture matching this nav cache
- // create an SkBitmap with dimensions of the cursor intersected w/ extended view
- const WebCore::IntRect& nodeBounds = bestNode->bounds(frame);
- WebCore::IntRect bounds = nodeBounds;
- bounds.intersect(mScrolledBounds);
- int leftMargin = bounds.x() == nodeBounds.x() ? kMargin : 0;
- int topMargin = bounds.y() == nodeBounds.y() ? kMargin : 0;
- int rightMargin = bounds.right() == nodeBounds.right() ? kMargin : 0;
- int bottomMargin = bounds.bottom() == nodeBounds.bottom() ? kMargin : 0;
- bool unclipped = (leftMargin & topMargin & rightMargin & bottomMargin) != 0;
- WebCore::IntRect marginBounds = nodeBounds;
- marginBounds.inflate(kMargin);
- marginBounds.intersect(mScrolledBounds);
- SkScalar offsetX = SkIntToScalar(leftMargin - bounds.x());
- SkScalar offsetY = SkIntToScalar(topMargin - bounds.y());
-#if USE(ACCELERATED_COMPOSITING)
- // When cached nodes are constructed in CacheBuilder.cpp, their
- // unclipped attribute is set so this condition won't be reached.
- // In the future, layers may contain nodes that can be clipped.
- // So to be safe, adjust the layer picture by its offset.
- if (bestNode->isInLayer()) {
- const CachedLayer* cachedLayer = frame->layer(bestNode);
- const LayerAndroid* layer = cachedLayer->layer(mRootLayer);
- SkMatrix pictMatrix;
- layer->localToGlobal(&pictMatrix);
- // FIXME: ignore scale, rotation for now
- offsetX += pictMatrix.getTranslateX();
- offsetY += pictMatrix.getTranslateY();
- DBG_NAV_LOGD("layer picture=%p (%g,%g)", picture,
- pictMatrix.getTranslateX(), pictMatrix.getTranslateY());
- }
-#endif
- BoundsCheck boundsCheck;
- BoundsCanvas checker(&boundsCheck);
- boundsCheck.mBounds.set(leftMargin, topMargin,
- leftMargin + bounds.width(), topMargin + bounds.height());
- boundsCheck.mBoundsSlop = boundsCheck.mBounds;
- boundsCheck.mBoundsSlop.inset(-kSlop, -kSlop);
+ Vector<IntRect> rings;
+ bestNode->cursorRings(frame, &rings);
+ const WebCore::IntRect& bounds = bestNode->bounds(frame);
+ IntRect bitBounds;
+ calcBitBounds(bounds, &bitBounds);
+ RingCheck ringCheck(rings, bitBounds, bounds);
+ RingCanvas checker(&ringCheck);
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, marginBounds.width(),
- marginBounds.height());
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
+ bitBounds.height());
checker.setBitmapDevice(bitmap);
- // insert probes to be called when the data corresponding to this ring is drawn
- // need to know if ring was generated by text, image, or parent (like div)
- // ? need to know (like imdb menu bar) to give up sometimes (when?)
- checker.translate(offsetX, offsetY);
+ checker.translate(SkIntToScalar(-bitBounds.x()),
+ SkIntToScalar(-bitBounds.y()));
checker.drawPicture(*picture);
- boundsCheck.checkLast();
- // was it not drawn or clipped out?
+ SkRegion clipRgn;
+ bool clipped = ringCheck.hiddenRings(&clipRgn);
CachedNode* node = const_cast<CachedNode*>(best->mNode);
- if (boundsCheck.hidden()) { // if hidden, return false so that nav can try again
-#if DEBUG_NAV_UI
- const SkIRect& m = boundsCheck.mBounds;
- const SkIRect& s = boundsCheck.mBoundsSlop;
- DBG_NAV_LOGD("hidden node:%p (%d) mBounds={%d,%d,%d,%d} mBoundsSlop="
- "{%d,%d,%d,%d}", node, node->index(),
- m.fLeft, m.fTop, m.fRight, m.fBottom,
- s.fLeft, s.fTop, s.fRight, s.fBottom);
- const SkIRect& o = boundsCheck.mDrawnOver.getBounds();
- const SkIRect& l = boundsCheck.mLastAll;
- const SkIRect& u = boundsCheck.mUnion;
- DBG_NAV_LOGD("hidden mDrawnOver={%d,%d,%d,%d} mLastAll={%d,%d,%d,%d}"
- " mUnion={%d,%d,%d,%d}",
- o.fLeft, o.fTop, o.fRight, o.fBottom,
- l.fLeft, l.fTop, l.fRight, l.fBottom,
- u.fLeft, u.fTop, u.fRight, u.fBottom);
- const SkIRect& a = boundsCheck.mAllDrawnIn;
- const WebCore::IntRect& c = mScrolledBounds;
- const WebCore::IntRect& b = nodeBounds;
- DBG_NAV_LOGD("hidden mAllDrawnIn={%d,%d,%d,%d}"
- " mScrolledBounds={%d,%d,%d,%d} nodeBounds={%d,%d,%d,%d}",
- a.fLeft, a.fTop, a.fRight, a.fBottom,
- c.x(), c.y(), c.right(), c.bottom(),
- b.x(), b.y(), b.right(), b.bottom());
- DBG_NAV_LOGD("bits.mWidth=%d bits.mHeight=%d transX=%d transY=%d",
- marginBounds.width(),marginBounds.height(),
- kMargin - bounds.x(), kMargin - bounds.y());
-#endif
+ DBG_NAV_LOGD("clipped=%s clipRgn.isEmpty=%s", clipped ? "true" : "false",
+ clipRgn.isEmpty() ? "true" : "false");
+ if (clipped && clipRgn.isEmpty()) {
node->setDisabled(true);
- node->setClippedOut(unclipped == false);
+ IntRect clippedBounds = bounds;
+ clippedBounds.intersect(bitBounds);
+ node->setClippedOut(clippedBounds != bounds);
return true;
}
// was it partially occluded by later drawing?
// if partially occluded, modify the bounds so that the mouse click has a better x,y
- const SkIRect& over = boundsCheck.mDrawnOver.getBounds();
- if (over.isEmpty() == false) {
-#if DEBUG_NAV_UI
- SkIRect orig = boundsCheck.mBounds;
-#endif
- SkIRect& base = boundsCheck.mBounds;
- if (base.fLeft < over.fRight && base.fRight > over.fRight)
- base.fLeft = over.fRight;
- else if (base.fRight > over.fLeft && base.fLeft < over.fLeft)
- base.fRight = over.fLeft;
- if (base.fTop < over.fBottom && base.fBottom > over.fBottom)
- base.fTop = over.fBottom;
- else if (base.fBottom > over.fTop && base.fTop < over.fTop)
- base.fBottom = over.fTop;
-#if DEBUG_NAV_UI
- const SkIRect& modded = boundsCheck.mBounds;
- DBG_NAV_LOGD("partially occluded node:%p (%d) old:{%d,%d,%d,%d}"
- " new:{%d,%d,%d,%d}", node, node->index(),
- orig.fLeft, orig.fTop, orig.fRight, orig.fBottom,
- base.fLeft, base.fTop, base.fRight, base.fBottom);
-#endif
- best->setMouseBounds(WebCore::IntRect(bounds.x() + base.fLeft - kMargin,
- bounds.y() + base.fTop - kMargin, base.width(), base.height()));
+ if (clipped) {
+ DBG_NAV_LOGD("clipped clipRgn={%d,%d,r=%d,b=%d}",
+ clipRgn.getBounds().fLeft, clipRgn.getBounds().fTop,
+ clipRgn.getBounds().fRight, clipRgn.getBounds().fBottom);
+ best->setMouseBounds(clipRgn.getBounds());
node->clip(best->mouseBounds());
- }
+ } else
+ node->fixUpCursorRects(frame);
return false;
}
diff --git a/WebKit/android/nav/CachedRoot.h b/WebKit/android/nav/CachedRoot.h
index a6420f9..8c263f8 100644
--- a/WebKit/android/nav/CachedRoot.h
+++ b/WebKit/android/nav/CachedRoot.h
@@ -49,10 +49,12 @@ public:
bool adjustForScroll(BestData* , Direction , WebCore::IntPoint* scrollPtr,
bool findClosest);
const SkRegion& baseUncovered() const { return mBaseUncovered; }
+ void calcBitBounds(const IntRect& , IntRect* ) const;
int checkForCenter(int x, int y) const;
void checkForJiggle(int* ) const;
bool checkRings(SkPicture* , const WTF::Vector<WebCore::IntRect>& rings,
- const WebCore::IntRect& bounds) const;
+ const WebCore::IntRect& nodeBounds,
+ const WebCore::IntRect& testBounds) const;
WebCore::IntPoint cursorLocation() const;
int documentHeight() { return mContents.height(); }
int documentWidth() { return mContents.width(); }
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index d76c22c..f422130 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -546,7 +546,7 @@ void fixCursor()
return;
int x, y;
const CachedFrame* frame;
- const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, false);
+ const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, true);
if (!node)
return;
// require that node have approximately the same bounds (+/- 4) and the same