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(¢erCheck); 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(®ion); 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(®ion); 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