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
isTextType(Type t)79 static bool isTextType(Type t) {
80 return t == kDrawPosTextH_Type || t == kDrawText_Type;
81 }
82
CommonCheck()83 CommonCheck() : mType(kNo_Type), mAllOpaque(true), mIsOpaque(true) {
84 setEmpty();
85 }
86
doRect(Type type)87 bool doRect(Type type) {
88 mType = type;
89 return doIRect(mUnion);
90 }
91
isEmpty()92 bool isEmpty() { return mUnion.isEmpty(); }
93
joinGlyphs(const SkIRect & rect)94 bool joinGlyphs(const SkIRect& rect) {
95 bool isGlyph = mType == kDrawGlyph_Type;
96 if (isGlyph)
97 mUnion.join(rect);
98 return isGlyph;
99 }
100
setAllOpaque(bool opaque)101 void setAllOpaque(bool opaque) { mAllOpaque = opaque; }
setEmpty()102 void setEmpty() { mUnion.setEmpty(); }
setIsOpaque(bool opaque)103 void setIsOpaque(bool opaque) { mIsOpaque = opaque; }
setType(Type type)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
BoundsCanvas(CommonCheck * bounder)139 BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) {
140 mTransparentLayer = 0;
141 setBounder(bounder);
142 }
143
drawPaint(const SkPaint & paint)144 virtual void drawPaint(const SkPaint& paint) {
145 mBounder.setType(CommonCheck::kDrawPaint_Type);
146 INHERITED::drawPaint(paint);
147 }
148
drawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)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
drawRect(const SkRect & rect,const SkPaint & paint)155 virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
156 mBounder.setType(CommonCheck::kDrawRect_Type);
157 INHERITED::drawRect(rect, paint);
158 }
159
drawPath(const SkPath & path,const SkPaint & paint)160 virtual void drawPath(const SkPath& path, const SkPaint& paint) {
161 mBounder.setType(CommonCheck::kDrawPath_Type);
162 INHERITED::drawPath(path, paint);
163 }
164
commonDrawBitmap(const SkBitmap & bitmap,const SkIRect * rect,const SkMatrix & matrix,const SkPaint & paint)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
drawSprite(const SkBitmap & bitmap,int left,int top,const SkPaint * paint)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
drawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)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
drawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)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
drawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)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
drawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)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
drawPicture(SkPicture & picture)229 virtual void drawPicture(SkPicture& picture) {
230 mBounder.setType(CommonCheck::kDrawPicture_Type);
231 INHERITED::drawPicture(picture);
232 }
233
saveLayer(const SkRect * bounds,const SkPaint * paint,SaveFlags flags)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
restore()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:
LeftCheck(int x,int y)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
left()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
onIRect(const SkIRect & rect)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
doRect()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:
CenterCheck(int x,int y,int width)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
center()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:
onIRect(const SkIRect & rect)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
doRect()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:
ImageCanvas(SkBounder * bounder)504 ImageCanvas(SkBounder* bounder) : mURI(NULL) {
505 setBounder(bounder);
506 }
507
getURI()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)
commonDrawBitmap(const SkBitmap & bitmap,const SkIRect * rect,const SkMatrix &,const SkPaint &)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:
onIRect(const SkIRect & rect)528 virtual bool onIRect(const SkIRect& rect) {
529 return false;
530 }
531 };
532
533 class JiggleCheck : public CommonCheck {
534 public:
JiggleCheck(int delta,int width)535 JiggleCheck(int delta, int width) : mDelta(delta), mMaxX(width) {
536 mMaxJiggle = 0;
537 mMinX = mMinJiggle = abs(delta);
538 mMaxWidth = width + mMinX;
539 }
540
jiggle()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
onIRect(const SkIRect & rect)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:
RingCheck(const WTF::Vector<WebCore::IntRect> & rings,const WebCore::IntRect & bitBounds,const WebCore::IntRect & testBounds,bool singleImage)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
hiddenRings(SkRegion * clipped)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
push(Type type,const SkIRect & bounds)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
startText(const SkPaint & paint)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
textOutsideRings()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:
onIRect(const SkIRect & rect)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
onIRectGlyph(const SkIRect & rect,const SkBounder::GlyphRec & rec)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:
calcOverlap(SkRegion & testRegion)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
findBestLayer()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
joinable(const SkIRect & rect)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
popEmpty()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:
RingCanvas(RingCheck * bounder)969 RingCanvas(RingCheck* bounder)
970 : INHERITED(bounder)
971 {
972 }
973
974 protected:
drawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)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
drawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & paint)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
drawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)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
drawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)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
save(SaveFlags flags)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
saveLayer(const SkRect * bounds,const SkPaint * paint,SaveFlags flags)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
restore()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
adjustForScroll(BestData * best,CachedFrame::Direction direction,WebCore::IntPoint * scrollPtr,bool findClosest)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
calcBitBounds(const IntRect & nodeBounds,IntRect * bitBounds) const1055 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
checkForCenter(int x,int y) const1081 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
checkForJiggle(int * xDeltaPtr) const1098 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
checkRings(SkPicture * picture,const CachedNode * node,const WebCore::IntRect & testBounds) const1116 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
draw(FindCanvas & canvas) const1144 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
findAt(const WebCore::IntRect & rect,const CachedFrame ** framePtr,int * x,int * y,bool checkForHidden) const1155 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
cursorLocation() const1186 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
focusLocation() const1193 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.
getAndResetSelectionEnd()1201 int CachedRoot::getAndResetSelectionEnd()
1202 {
1203 int end = mSelectionEnd;
1204 mSelectionEnd = -1;
1205 return end;
1206 }
1207
getAndResetSelectionStart()1208 int CachedRoot::getAndResetSelectionStart()
1209 {
1210 int start = mSelectionStart;
1211 mSelectionStart = -1;
1212 return start;
1213 }
1214
getBlockLeftEdge(int x,int y,float scale) const1215 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
getSimulatedMousePosition(WebCore::IntPoint * point) const1252 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
init(WebCore::Frame * frame,CachedHistory * history)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
innerDown(const CachedNode * test,BestData * bestData) const1282 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
innerLeft(const CachedNode * test,BestData * bestData) const1308 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
innerMove(const CachedNode * node,BestData * bestData,Direction direction,WebCore::IntPoint * scroll,bool firstCall)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
innerRight(const CachedNode * test,BestData * bestData) const1406 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
innerUp(const CachedNode * test,BestData * bestData) const1432 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
imageURI(int x,int y) const1456 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
maskIfHidden(BestData * best) const1471 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
moveCursor(Direction direction,const CachedFrame ** framePtr,WebCore::IntPoint * scroll)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
nextTextField(const CachedNode * start,const CachedFrame ** framePtr) const1610 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
pictureAt(int * xPtr,int * yPtr,int * id) const1617 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
reset()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
scrollDelta(WebCore::IntRect & newOutset,Direction direction,int * delta)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
setCachedFocus(CachedFrame * frame,CachedNode * node)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
setCursor(CachedFrame * frame,CachedNode * node)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
setCursorCache(int scrollX,int scrollY) const1738 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
base() const1786 CachedRoot* CachedRoot::Debug::base() const {
1787 CachedRoot* nav = (CachedRoot*) ((char*) this - OFFSETOF(CachedRoot, mDebug));
1788 return nav;
1789 }
1790
print() const1791 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