• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "CachedHistory.h"
28 #include "CachedNode.h"
29 #include "CachedRoot.h"
30 
31 #include "CachedFrame.h"
32 
33 #define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning
34 
35 #define MIN_OVERLAP 3 // if rects overlap by 2 pixels or fewer, treat them as non-intersecting
36 
37 namespace android {
38 
adjustBounds(const CachedNode * node,const WebCore::IntRect & rect) const39 WebCore::IntRect CachedFrame::adjustBounds(const CachedNode* node,
40     const WebCore::IntRect& rect) const
41 {
42     DBG_NAV_LOGD("node=%p [%d] rect=(%d,%d,w=%d,h=%d)",
43         node, node->index(), rect.x(), rect.y(), rect.width(), rect.height());
44 #if USE(ACCELERATED_COMPOSITING)
45     return layer(node)->adjustBounds(mRoot->rootLayer(), rect);
46 #else
47     return rect;
48 #endif
49 }
50 
CheckBetween(Direction direction,const WebCore::IntRect & bestRect,const WebCore::IntRect & prior,WebCore::IntRect * result)51 bool CachedFrame::CheckBetween(Direction direction, const WebCore::IntRect& bestRect,
52         const WebCore::IntRect& prior, WebCore::IntRect* result)
53 {
54     int left, top, width, height;
55     if (direction & UP_DOWN) {
56         top = direction == UP ? bestRect.bottom() : prior.bottom();
57         int bottom = direction == UP ? prior.y() : bestRect.y();
58         height = bottom - top;
59         if (height < 0)
60             return false;
61         left = prior.x();
62         int testLeft = bestRect.x();
63         if (left > testLeft)
64             left = testLeft;
65         int right = prior.right();
66         int testRight = bestRect.right();
67         if (right < testRight)
68             right = testRight;
69         width = right - left;
70     } else {
71         left = direction == LEFT ? bestRect.right() : prior.right();
72         int right = direction == LEFT ? prior.x() : bestRect.x();
73         width = right - left;
74         if (width < 0)
75             return false;
76         top = prior.y();
77         int testTop = bestRect.y();
78         if (top > testTop)
79             top = testTop;
80         int bottom = prior.bottom();
81         int testBottom = bestRect.bottom();
82         if (bottom < testBottom)
83             bottom = testBottom;
84         height = bottom - top;
85     }
86     *result = WebCore::IntRect(left, top, width, height);
87     return true;
88 }
89 
checkBetween(BestData * best,Direction direction)90 bool CachedFrame::checkBetween(BestData* best, Direction direction)
91 {
92     const WebCore::IntRect& bestRect = best->bounds();
93     BestData test;
94     test.mDistance = INT_MAX;
95     test.mNode = NULL;
96     int index = direction;
97     int limit = index + DIRECTION_COUNT;
98     do {
99         WebCore::IntRect edges;
100         Direction check = (Direction) (index & DIRECTION_MASK);
101         if (CheckBetween(check, bestRect,
102                 history()->priorBounds(), &edges) == false)
103             continue;
104         WebCore::IntRect clip = mRoot->scrolledBounds();
105         clip.intersect(edges);
106         if (clip.isEmpty())
107             continue;
108         findClosest(&test, direction, check, &clip);
109         if (test.mNode == NULL)
110             continue;
111         if (direction == check)
112             break;
113     } while (++index < limit);
114     if (test.mNode == NULL)
115         return false;
116     *best = test;
117     return true;
118 }
119 
checkRings(const CachedNode * node,const WTF::Vector<WebCore::IntRect> & rings,const WebCore::IntRect & bounds) const120 bool CachedFrame::checkRings(const CachedNode* node,
121         const WTF::Vector<WebCore::IntRect>& rings,
122         const WebCore::IntRect& bounds) const
123 {
124     return mRoot->checkRings(picture(node), rings, bounds);
125 }
126 
checkVisited(const CachedNode * node,Direction direction) const127 bool CachedFrame::checkVisited(const CachedNode* node, Direction direction) const
128 {
129     return history()->checkVisited(node, direction);
130 }
131 
clearCursor()132 void CachedFrame::clearCursor()
133 {
134     DBG_NAV_LOGD("mCursorIndex=%d", mCursorIndex);
135     if (mCursorIndex < CURSOR_SET)
136         return;
137     CachedNode& cursor = mCachedNodes[mCursorIndex];
138     cursor.clearCursor(this);
139     mCursorIndex = CURSOR_CLEARED; // initialized and explicitly cleared
140 }
141 
142 // returns 0 if test is preferable to best, 1 if not preferable, or -1 if unknown
compare(BestData & testData,const BestData & bestData) const143 int CachedFrame::compare(BestData& testData, const BestData& bestData) const
144 {
145     if (testData.mNode->tabIndex() != bestData.mNode->tabIndex()) {
146         if (testData.mNode->tabIndex() < bestData.mNode->tabIndex()
147                 || (mRoot->mCursor && mRoot->mCursor->tabIndex() < bestData.mNode->tabIndex())) {
148             testData.mNode->setCondition(CachedNode::HIGHER_TAB_INDEX);
149             return REJECT_TEST;
150         }
151         return TEST_IS_BEST;
152     }
153     // if the test minor axis line intersects the line segment between cursor
154     // center and best center, choose it
155     // give more weight to exact major axis alignment (rows, columns)
156     if (testData.mInNav != bestData.mInNav) {
157         if (bestData.mInNav) {
158             testData.mNode->setCondition(CachedNode::IN_CURSOR);
159             return REJECT_TEST;
160         }
161         return TEST_IS_BEST;
162     }
163     if (testData.mInNav) {
164         if (bestData.mMajorDelta < testData.mMajorDelta) {
165             testData.mNode->setCondition(CachedNode::CLOSER_IN_CURSOR);
166             return REJECT_TEST;
167         }
168         if (testData.mMajorDelta < bestData.mMajorDelta)
169             return TEST_IS_BEST;
170     }
171     if (testData.mMajorDelta < 0 && bestData.mMajorDelta >= 0) {
172         testData.mNode->setCondition(CachedNode::FURTHER);
173         return REJECT_TEST;
174     }
175     if ((testData.mMajorDelta ^ bestData.mMajorDelta) < 0) // one above, one below (or one left, one right)
176         return TEST_IS_BEST;
177     bool bestInWorking = bestData.inOrSubsumesWorking();
178     bool testInWorking = testData.inOrSubsumesWorking();
179     if (bestInWorking && testData.mWorkingOutside && testData.mNavOutside) {
180         testData.mNode->setCondition(CachedNode::IN_WORKING);
181         return REJECT_TEST;
182     }
183     if (testInWorking && bestData.mWorkingOutside && bestData.mNavOutside)
184         return TEST_IS_BEST;
185     bool bestInNav = directionChange() && bestData.inOrSubsumesNav();
186     bool testInNav = directionChange() && testData.inOrSubsumesNav();
187     if (bestInWorking == false && testInWorking == false) {
188         if (bestInNav && testData.mNavOutside) {
189             testData.mNode->setCondition(CachedNode::IN_UMBRA);
190             return REJECT_TEST;
191         }
192         if (testInNav && bestData.mNavOutside)
193             return TEST_IS_BEST;
194     }
195 #if 01 // hopefully butt test will remove need for this
196     if (testData.mCursorChild != bestData.mCursorChild) {
197         if (bestData.mCursorChild) {
198             testData.mNode->setCondition(CachedNode::IN_CURSOR_CHILDREN);
199             return REJECT_TEST;
200         }
201         return TEST_IS_BEST;
202     }
203 #endif
204     bool bestTestIn = (bestInWorking || bestInNav) && (testInWorking || testInNav);
205     bool testOverlap = bestTestIn || (testData.mWorkingOverlap != 0 && bestData.mWorkingOverlap == 0);
206     bool bestOverlap = bestTestIn || (testData.mWorkingOverlap == 0 && bestData.mWorkingOverlap != 0);
207 #if 01 // this isn't working?
208     if (testOverlap == bestOverlap) {
209         if (bestData.mMajorButt < 10 && testData.mMajorButt >= 40) {
210             testData.mNode->setCondition(CachedNode::BUTTED_UP);
211             return REJECT_TEST;
212         }
213         if (testData.mMajorButt < 10 && bestData.mMajorButt >= 40)
214             return TEST_IS_BEST;
215     }
216 #endif
217     if (bestOverlap && bestData.mMajorDelta < testData.mMajorDelta) { // choose closest major axis center
218         testData.mNode->setCondition(CachedNode::CLOSER);
219         return REJECT_TEST;
220     }
221     if (testOverlap && testData.mMajorDelta < bestData.mMajorDelta)
222         return TEST_IS_BEST;
223     if (bestOverlap && bestData.mMajorDelta2 < testData.mMajorDelta2) {
224         testData.mNode->setCondition(CachedNode::CLOSER_TOP);
225         return REJECT_TEST;
226     }
227     if (testOverlap && testData.mMajorDelta2 < bestData.mMajorDelta2)
228         return TEST_IS_BEST;
229 #if 01
230     if (bestOverlap && ((bestData.mSideDistance <= 0 && testData.mSideDistance > 0) ||
231             abs(bestData.mSideDistance) < abs(testData.mSideDistance))) {
232         testData.mNode->setCondition(CachedNode::LEFTMOST);
233         return REJECT_TEST;
234     }
235     if (testOverlap && ((testData.mSideDistance <= 0 && bestData.mSideDistance > 0) ||
236             abs(testData.mSideDistance) < abs(bestData.mSideDistance)))
237         return TEST_IS_BEST;
238 // fix me : the following ASSERT fires -- not sure if this case should be handled or not
239 //    ASSERT(bestOverlap == false && testOverlap == false);
240 #endif
241     SkFixed testMultiplier = testData.mWorkingOverlap > testData.mNavOverlap ?
242         testData.mWorkingOverlap : testData.mNavOverlap;
243     SkFixed bestMultiplier = bestData.mWorkingOverlap > bestData.mNavOverlap ?
244         bestData.mWorkingOverlap : bestData.mNavOverlap;
245     int testDistance = testData.mDistance;
246     int bestDistance = bestData.mDistance;
247 //    start here;
248     // this fails if they're off by 1
249     // try once again to implement sliding scale so that off by 1 is nearly like zero,
250     // and off by a lot causes sideDistance to have little or no effect
251         // try elliptical distance -- lengthen side contribution
252         // these ASSERTs should not fire, but do fire on mail.google.com
253         // can't debug yet, won't reproduce
254     ASSERT(testDistance >= 0);
255     ASSERT(bestDistance >= 0);
256     testDistance += testDistance; // multiply by 2
257     testDistance *= testDistance;
258     bestDistance += bestDistance; // multiply by 2
259     bestDistance *= bestDistance;
260     int side = testData.mSideDistance;
261     int negative = side < 0 && bestData.mSideDistance > 0;
262     side *= side;
263     if (negative)
264         side = -side;
265     testDistance += side;
266     side = bestData.mSideDistance;
267     negative = side < 0 && testData.mSideDistance > 0;
268     side *= side;
269     if (negative)
270         side = -side;
271     bestDistance += side;
272     if (testMultiplier > (SK_Fixed1 >> 1) || bestMultiplier > (SK_Fixed1 >> 1)) { // considerable working overlap?
273        testDistance = SkFixedMul(testDistance, bestMultiplier);
274        bestDistance = SkFixedMul(bestDistance, testMultiplier);
275     }
276     if (bestDistance < testDistance) {
277         testData.mNode->setCondition(CachedNode::CLOSER_OVERLAP);
278         return REJECT_TEST;
279     }
280     if (testDistance < bestDistance)
281         return TEST_IS_BEST;
282 #if 0
283     int distance = testData.mDistance + testData.mSideDistance;
284     int best = bestData.mDistance + bestData.mSideDistance;
285     if (distance > best) {
286         testData.mNode->setCondition(CachedNode::CLOSER_RAW_DISTANCE);
287         return REJECT_TEST;
288     }
289     else if (distance < best)
290         return TEST_IS_BEST;
291     best = bestData.mSideDistance;
292     if (testData.mSideDistance > best) {
293         testData.mNode->setCondition(CachedNode::SIDE_DISTANCE);
294         return REJECT_TEST;
295     }
296     if (testData.mSideDistance < best)
297         return TEST_IS_BEST;
298 #endif
299     if (testData.mPreferred < bestData.mPreferred) {
300         testData.mNode->setCondition(CachedNode::PREFERRED);
301         return REJECT_TEST;
302     }
303     if (testData.mPreferred > bestData.mPreferred)
304         return TEST_IS_BEST;
305     return UNDECIDED;
306 }
307 
currentCursor(const CachedFrame ** framePtr) const308 const CachedNode* CachedFrame::currentCursor(const CachedFrame** framePtr) const
309 {
310     if (framePtr)
311         *framePtr = this;
312     if (mCursorIndex < CURSOR_SET)
313         return NULL;
314     const CachedNode* result = &mCachedNodes[mCursorIndex];
315     const CachedFrame* frame = hasFrame(result);
316     if (frame != NULL)
317         return frame->currentCursor(framePtr);
318     (const_cast<CachedNode*>(result))->fixUpCursorRects(this);
319     return result;
320 }
321 
currentFocus(const CachedFrame ** framePtr) const322 const CachedNode* CachedFrame::currentFocus(const CachedFrame** framePtr) const
323 {
324     if (framePtr)
325         *framePtr = this;
326     if (mFocusIndex < 0)
327         return NULL;
328     const CachedNode* result = &mCachedNodes[mFocusIndex];
329     const CachedFrame* frame = hasFrame(result);
330     if (frame != NULL)
331         return frame->currentFocus(framePtr);
332     return result;
333 }
334 
directionChange() const335 bool CachedFrame::directionChange() const
336 {
337     return history()->directionChange();
338 }
339 
340 #ifdef BROWSER_DEBUG
find(WebCore::Node * node)341 CachedNode* CachedFrame::find(WebCore::Node* node) // !!! probably debugging only
342 {
343     for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++)
344         if (node == test->webCoreNode())
345             return test;
346     for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
347             frame++) {
348         CachedNode* result = frame->find(node);
349         if (result != NULL)
350             return result;
351     }
352     return NULL;
353 }
354 #endif
355 
findBestAt(const WebCore::IntRect & rect,int * best,bool * inside,const CachedNode ** directHit,const CachedFrame ** directHitFramePtr,const CachedFrame ** framePtr,int * x,int * y,bool checkForHiddenStart) const356 const CachedNode* CachedFrame::findBestAt(const WebCore::IntRect& rect,
357     int* best, bool* inside, const CachedNode** directHit,
358     const CachedFrame** directHitFramePtr,
359     const CachedFrame** framePtr, int* x, int* y,
360     bool checkForHiddenStart) const
361 {
362     const CachedNode* result = NULL;
363     int rectWidth = rect.width();
364     WebCore::IntPoint center = WebCore::IntPoint(rect.x() + (rectWidth >> 1),
365         rect.y() + (rect.height() >> 1));
366     mRoot->setupScrolledBounds();
367     for (const CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) {
368         if (test->disabled())
369             continue;
370         size_t parts = test->navableRects();
371         BestData testData;
372         testData.mNode = test;
373         testData.mFrame = this;
374         WebCore::IntRect bounds = test->bounds(this);
375         testData.setMouseBounds(bounds);
376         testData.setNodeBounds(bounds);
377         bool checkForHidden = checkForHiddenStart;
378         for (size_t part = 0; part < parts; part++) {
379             WebCore::IntRect testRect = test->ring(this, part);
380             if (test->isInLayer()) {
381                 DBG_NAV_LOGD("[%d] intersects=%ss testRect=(%d,%d,w=%d,h=%d)"
382                     " rect=(%d,%d,w=%d,h=%d)", test->index(),
383                     testRect.intersects(rect) ? "true" : "false",
384                     testRect.x(), testRect.y(), testRect.width(), testRect.height(),
385                     rect.x(), rect.y(), rect.width(), rect.height());
386             }
387             if (testRect.intersects(rect)) {
388                 if (checkForHidden && mRoot->maskIfHidden(&testData) == true)
389                     break;
390                 checkForHidden = false;
391                 testRect.intersect(testData.mouseBounds());
392                 if (testRect.contains(center)) {
393                     // We have a direct hit.
394                     if (*directHit == NULL) {
395                         *directHit = test;
396                         *directHitFramePtr = this;
397                         *x = center.x();
398                         *y = center.y();
399                     } else {
400                         // We have hit another one before
401                         const CachedNode* d = *directHit;
402                         if (d->bounds(this).contains(testRect)) {
403                             // This rectangle is inside the other one, so it is
404                             // the best one.
405                             *directHit = test;
406                             *directHitFramePtr = this;
407                         }
408                     }
409                 }
410                 if (NULL != *directHit) {
411                     // If we have a direct hit already, there is no need to
412                     // calculate the distances, or check the other parts
413                     break;
414                 }
415                 WebCore::IntRect both = rect;
416                 int smaller = testRect.width() < testRect.height() ?
417                     testRect.width() : testRect.height();
418                 smaller -= rectWidth;
419                 int inset = smaller < rectWidth ? smaller : rectWidth;
420                 inset >>= 1; // inflate doubles the width decrease
421                 if (inset > 1)
422                     both.inflate(1 - inset);
423                 both.intersect(testRect);
424                 if (both.isEmpty())
425                     continue;
426                 bool testInside = testRect.contains(center);
427                 if (*inside && !testInside)
428                     continue;
429                 WebCore::IntPoint testCenter = WebCore::IntPoint(testRect.x() +
430                     (testRect.width() >> 1), testRect.y() + (testRect.height() >> 1));
431                 int dx = testCenter.x() - center.x();
432                 int dy = testCenter.y() - center.y();
433                 int distance = dx * dx + dy * dy;
434                 if ((!*inside && testInside) || *best >= distance) {
435                     *best = distance;
436                     *inside = testInside;
437                     result = test;
438                     *framePtr = this;
439                     *x = both.x() + (both.width() >> 1);
440                     *y = both.y() + (both.height() >> 1);
441                 }
442             }
443         }
444     }
445     for (const CachedFrame* frame = mCachedFrames.begin();
446             frame != mCachedFrames.end(); frame++) {
447         const CachedNode* frameResult = frame->findBestAt(rect, best, inside,
448             directHit, directHitFramePtr, framePtr, x, y, checkForHiddenStart);
449         if (NULL != frameResult)
450             result = frameResult;
451     }
452     if (NULL != *directHit) {
453         result = *directHit;
454         *framePtr = *directHitFramePtr;
455     }
456     return result;
457 }
458 
findBestFrameAt(int x,int y) const459 const CachedFrame* CachedFrame::findBestFrameAt(int x, int y) const
460 {
461     if (mLocalViewBounds.contains(x, y) == false)
462         return NULL;
463     const CachedFrame* result = this;
464     for (const CachedFrame* frame = mCachedFrames.begin();
465             frame != mCachedFrames.end(); frame++) {
466         const CachedFrame* frameResult = frame->findBestFrameAt(x, y);
467         if (NULL != frameResult)
468             result = frameResult;
469     }
470     return result;
471 }
472 
findBestHitAt(const WebCore::IntRect & rect,const CachedFrame ** framePtr,int * x,int * y) const473 const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect,
474     const CachedFrame** framePtr, int* x, int* y) const
475 {
476     mRoot->setupScrolledBounds();
477     for (const CachedFrame* frame = mCachedFrames.end() - 1;
478             frame != mCachedFrames.begin() - 1; frame--) {
479         const CachedNode* frameResult = frame->findBestHitAt(rect,
480             framePtr, x, y);
481         if (NULL != frameResult)
482             return frameResult;
483     }
484     for (const CachedNode* test = mCachedNodes.end() - 1;
485             test != mCachedNodes.begin() - 1; test--) {
486         if (test->disabled())
487             continue;
488         WebCore::IntRect testRect = test->hitBounds(this);
489         if (testRect.intersects(rect) == false)
490             continue;
491         BestData testData;
492         testData.mNode = test;
493         testData.mFrame = this;
494         testData.setMouseBounds(testRect);
495         testData.setNodeBounds(testRect);
496         if (mRoot->maskIfHidden(&testData) == true)
497             continue;
498         for (int i = 0; i < test->navableRects(); i++) {
499             WebCore::IntRect cursorRect = test->ring(this, i);
500             if (cursorRect.intersects(rect)) {
501                 WebCore::IntRect intersection(cursorRect);
502                 intersection.intersect(rect);
503                 *x = intersection.x() + (intersection.width() >> 1);
504                 *y = intersection.y() + (intersection.height() >> 1);
505                 *framePtr = this;
506                 return test;
507             }
508         }
509     }
510     return NULL;
511 }
512 
findClosest(BestData * bestData,Direction originalDirection,Direction direction,WebCore::IntRect * clip) const513 void CachedFrame::findClosest(BestData* bestData, Direction originalDirection,
514     Direction direction, WebCore::IntRect* clip) const
515 {
516     const CachedNode* test = mCachedNodes.begin();
517     while ((test = test->traverseNextNode()) != NULL) {
518         const CachedFrame* child = hasFrame(test);
519         if (child != NULL) {
520             const CachedNode* childDoc = child->validDocument();
521             if (childDoc == NULL)
522                 continue;
523             child->findClosest(bestData, originalDirection, direction, clip);
524         }
525         if (test->noSecondChance())
526             continue;
527         if (test->isNavable(this, *clip) == false)
528             continue;
529         if (checkVisited(test, originalDirection) == false)
530             continue;
531         size_t partMax = test->navableRects();
532         for (size_t part = 0; part < partMax; part++) {
533             WebCore::IntRect testBounds = test->ring(this, part);
534             if (clip->intersects(testBounds) == false)
535                 continue;
536             if (clip->contains(testBounds) == false) {
537                 if (direction & UP_DOWN) {
538 //                    if (testBounds.x() > clip->x() || testBounds.right() < clip->right())
539 //                        continue;
540                     testBounds.setX(clip->x());
541                     testBounds.setWidth(clip->width());
542                 } else {
543 //                    if (testBounds.y() > clip->y() || testBounds.bottom() < clip->bottom())
544 //                        continue;
545                     testBounds.setY(clip->y());
546                     testBounds.setHeight(clip->height());
547                 }
548                 if (clip->contains(testBounds) == false)
549                     continue;
550             }
551             int distance;
552             // seems like distance for UP for instance needs to be 'test top closest to
553             // clip bottom' -- keep the old code but try this instead
554             switch (direction) {
555 #if 0
556                 case LEFT: distance = testBounds.x() - clip->x(); break;
557                 case RIGHT: distance = clip->right() - testBounds.right(); break;
558                 case UP: distance = testBounds.y() - clip->y(); break;
559                 case DOWN: distance = clip->bottom() - testBounds.bottom(); break;
560 #else
561                 case LEFT: distance = clip->right() - testBounds.x(); break;
562                 case RIGHT: distance = testBounds.right()  - clip->x(); break;
563                 case UP: distance = clip->bottom() - testBounds.y(); break;
564                 case DOWN: distance = testBounds.bottom() - clip->y(); break;
565 #endif
566                 default:
567                     distance = 0; ASSERT(0);
568             }
569             if (distance < bestData->mDistance) {
570                 bestData->mNode = test;
571                 bestData->mFrame = this;
572                 bestData->mDistance = distance;
573                 WebCore::IntRect rect = test->ring(this, part);
574                 bestData->setMouseBounds(rect);
575                 bestData->setNodeBounds(rect);
576                 CachedHistory* cachedHistory = history();
577                 switch (direction) {
578                     case LEFT:
579                         bestData->setLeftDirection(cachedHistory);
580                     break;
581                     case RIGHT:
582                         bestData->setRightDirection(cachedHistory);
583                     break;
584                     case UP:
585                         bestData->setUpDirection(cachedHistory);
586                     break;
587                     case DOWN:
588                         bestData->setDownDirection(cachedHistory);
589                     break;
590                     default:
591                         ASSERT(0);
592                 }
593             }
594         }
595     }
596 }
597 
finishInit()598 void CachedFrame::finishInit()
599 {
600     CachedNode* lastCached = lastNode();
601     lastCached->setLast();
602     CachedFrame* child = mCachedFrames.begin();
603     while (child != mCachedFrames.end()) {
604         child->mParent = this;
605         child->finishInit();
606         child++;
607     }
608     CachedFrame* frameParent;
609     if (mFocusIndex >= 0 && (frameParent = parent()))
610         frameParent->setFocusIndex(indexInParent());
611 }
612 
frameDown(const CachedNode * test,const CachedNode * limit,BestData * bestData) const613 const CachedNode* CachedFrame::frameDown(const CachedNode* test,
614     const CachedNode* limit, BestData* bestData) const
615 {
616     BestData originalData = *bestData;
617     do {
618         if (moveInFrame(&CachedFrame::frameDown, test, bestData))
619             continue;
620         BestData testData;
621         if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
622             continue;
623         if (checkVisited(test, DOWN) == false)
624             continue;
625         size_t parts = test->navableRects();
626         for (size_t part = 0; part < parts; part++) {
627             testData.setNodeBounds(test->ring(this, part));
628             if (testData.setDownDirection(history()))
629                 continue;
630             int result = framePartCommon(testData, test, bestData);
631             if (result == REJECT_TEST)
632                 continue;
633             if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
634                 BestData innerData = testData;
635                 frameDown(document(), test, &innerData);
636                 if (checkVisited(innerData.mNode, DOWN)) {
637                     *bestData = innerData;
638                     continue;
639                 }
640             }
641             if (checkVisited(test, DOWN))
642                 *bestData = testData;
643         }
644     } while ((test = test->traverseNextNode()) != limit);
645     ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
646     // does the best contain something (or, is it contained by an area which is not the cursor?)
647         // if so, is the conainer/containee should have been chosen, but wasn't -- so there's a better choice
648         // in the doc list prior to this choice
649     //
650     return bestData->mNode;
651 }
652 
frameLeft(const CachedNode * test,const CachedNode * limit,BestData * bestData) const653 const CachedNode* CachedFrame::frameLeft(const CachedNode* test,
654     const CachedNode* limit, BestData* bestData) const
655 {
656     BestData originalData = *bestData;
657     do {
658         if (moveInFrame(&CachedFrame::frameLeft, test, bestData))
659             continue;
660         BestData testData;
661         if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
662             continue;
663         if (checkVisited(test, LEFT) == false)
664             continue;
665         size_t parts = test->navableRects();
666         for (size_t part = 0; part < parts; part++) {
667             testData.setNodeBounds(test->ring(this, part));
668             if (testData.setLeftDirection(history()))
669                 continue;
670             int result = framePartCommon(testData, test, bestData);
671             if (result == REJECT_TEST)
672                 continue;
673             if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
674                 BestData innerData = testData;
675                 frameLeft(document(), test, &innerData);
676                 if (checkVisited(innerData.mNode, LEFT)) {
677                     *bestData = innerData;
678                     continue;
679                 }
680             }
681             if (checkVisited(test, LEFT))
682                 *bestData = testData;
683         }
684     } while ((test = test->traverseNextNode()) != limit);  // FIXME ??? left and up should use traversePreviousNode to choose reverse document order
685     ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
686     return bestData->mNode;
687 }
688 
frameNodeCommon(BestData & testData,const CachedNode * test,BestData * bestData,BestData * originalData) const689 int CachedFrame::frameNodeCommon(BestData& testData, const CachedNode* test,
690     BestData* bestData, BestData* originalData) const
691 {
692     testData.mFrame = this;
693     testData.mNode = test;
694     test->clearCondition();
695     if (test->disabled()) {
696         testData.mNode->setCondition(CachedNode::DISABLED);
697         return REJECT_TEST;
698     }
699     if (mRoot->scrolledBounds().intersects(test->bounds(this)) == false) {
700         testData.mNode->setCondition(CachedNode::NAVABLE);
701         return REJECT_TEST;
702     }
703     if (mRoot->rootLayer() && !test->isInLayer()
704             && !mRoot->baseUncovered().intersects(test->bounds(this))) {
705         testData.mNode->setCondition(CachedNode::UNDER_LAYER);
706         return REJECT_TEST;
707     }
708 //    if (isNavable(test, &testData.mNodeBounds, walk) == false) {
709 //        testData.mNode->setCondition(CachedNode::NAVABLE);
710 //        return REJECT_TEST;
711 //    }
712 //
713     if (test == mRoot->mCursor) {
714         testData.mNode->setCondition(CachedNode::NOT_CURSOR_NODE);
715         return REJECT_TEST;
716     }
717 //    if (test->bounds().contains(mRoot->mCursorBounds)) {
718 //        testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
719 //        return REJECT_TEST;
720 //    }
721     void* par = mRoot->mCursor ? mRoot->mCursor->parentGroup() : NULL;
722     testData.mCursorChild = par ? test->parentGroup() == par : false;
723     if (bestData->mNode == NULL)
724         return TEST_IS_BEST;
725     if (mRoot->mCursor && testData.mNode->parentIndex() != bestData->mNode->parentIndex()) {
726         int cursorParentIndex = mRoot->mCursor->parentIndex();
727         if (cursorParentIndex >= 0) {
728             if (bestData->mNode->parentIndex() == cursorParentIndex)
729                 return REJECT_TEST;
730             if (testData.mNode->parentIndex() == cursorParentIndex)
731                 return TEST_IS_BEST;
732         }
733     }
734     if (testData.mNode->parent() == bestData->mNode) {
735         testData.mNode->setCondition(CachedNode::CHILD);
736         return REJECT_TEST;
737     }
738     if (testData.mNode == bestData->mNode->parent())
739         return TEST_IS_BEST;
740     int testInBest = testData.isContainer(bestData); /* -1 pick best over test, 0 no containership, 1 pick test over best */
741     if (testInBest == 1) {
742         if (test->isArea() || bestData->mNode->isArea())
743             return UNDECIDED;
744         bestData->mNode = NULL;    // force part tests to be ignored, yet still set up remaining test data for later comparisons
745         return TEST_IS_BEST;
746     }
747     if (testInBest == -1) {
748         testData.mNode->setCondition(CachedNode::OUTSIDE_OF_BEST);
749         return REJECT_TEST;
750     }
751     if (originalData->mNode != NULL) { // test is best case
752         testInBest = testData.isContainer(originalData);
753         if (testInBest == -1) { /* test is inside best */
754             testData.mNode->setCondition(CachedNode::OUTSIDE_OF_ORIGINAL);
755             return REJECT_TEST;
756         }
757     }
758     return UNDECIDED;
759 }
760 
framePartCommon(BestData & testData,const CachedNode * test,BestData * bestData) const761 int CachedFrame::framePartCommon(BestData& testData,
762     const CachedNode* test, BestData* bestData) const
763 {
764     if (mRoot->mCursor
765             && testData.bounds().contains(mRoot->mCursorBounds)
766             && !test->wantsKeyEvents()) {
767         testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
768         return REJECT_TEST;
769     }
770     testData.setDistances();
771     if (bestData->mNode != NULL) {
772         int compared = compare(testData, *bestData);
773         if (compared == 0 && test->isArea() == false && bestData->mNode->isArea() == false)
774             goto pickTest;
775         if (compared >= 0)
776             return compared;
777     }
778 pickTest:
779     return -1; // pick test
780 }
781 
frameRight(const CachedNode * test,const CachedNode * limit,BestData * bestData) const782 const CachedNode* CachedFrame::frameRight(const CachedNode* test,
783     const CachedNode* limit, BestData* bestData) const
784 {
785     BestData originalData = *bestData;
786     do {
787         if (moveInFrame(&CachedFrame::frameRight, test, bestData))
788             continue;
789         BestData testData;
790         if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
791             continue;
792         if (checkVisited(test, RIGHT) == false)
793             continue;
794         size_t parts = test->navableRects();
795         for (size_t part = 0; part < parts; part++) {
796             testData.setNodeBounds(test->ring(this, part));
797             if (testData.setRightDirection(history()))
798                 continue;
799             int result = framePartCommon(testData, test, bestData);
800             if (result == REJECT_TEST)
801                 continue;
802             if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
803                 BestData innerData = testData;
804                 frameRight(document(), test, &innerData);
805                 if (checkVisited(innerData.mNode, RIGHT)) {
806                     *bestData = innerData;
807                     continue;
808                 }
809             }
810             if (checkVisited(test, RIGHT))
811                 *bestData = testData;
812         }
813     } while ((test = test->traverseNextNode()) != limit);
814     ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
815     return bestData->mNode;
816 }
817 
frameUp(const CachedNode * test,const CachedNode * limit,BestData * bestData) const818 const CachedNode* CachedFrame::frameUp(const CachedNode* test,
819     const CachedNode* limit, BestData* bestData) const
820 {
821     BestData originalData = *bestData;
822     do {
823         if (moveInFrame(&CachedFrame::frameUp, test, bestData))
824             continue;
825         BestData testData;
826         if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
827             continue;
828         if (checkVisited(test, UP) == false)
829             continue;
830         size_t parts = test->navableRects();
831         for (size_t part = 0; part < parts; part++) {
832             testData.setNodeBounds(test->ring(this, part));
833             if (testData.setUpDirection(history()))
834                 continue;
835             int result = framePartCommon(testData, test, bestData);
836             if (result == REJECT_TEST)
837                 continue;
838             if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
839                 BestData innerData = testData;
840                 frameUp(document(), test, &innerData);
841                 if (checkVisited(innerData.mNode, UP)) {
842                     *bestData = innerData;
843                     continue;
844                 }
845             }
846             if (checkVisited(test, UP))
847                 *bestData = testData;
848         }
849     } while ((test = test->traverseNextNode()) != limit);  // FIXME ??? left and up should use traversePreviousNode to choose reverse document order
850     ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
851     return bestData->mNode;
852 }
853 
hasFrame(const CachedNode * node) const854 const CachedFrame* CachedFrame::hasFrame(const CachedNode* node) const
855 {
856     return node->isFrame() ? &mCachedFrames[node->childFrameIndex()] : NULL;
857 }
858 
hideCursor()859 void CachedFrame::hideCursor()
860 {
861     DBG_NAV_LOGD("mCursorIndex=%d", mCursorIndex);
862     if (mCursorIndex < CURSOR_SET)
863         return;
864     CachedNode& cursor = mCachedNodes[mCursorIndex];
865     cursor.hideCursor(this);
866 }
867 
history() const868 CachedHistory* CachedFrame::history() const
869 {
870     return mRoot->rootHistory();
871 }
872 
init(const CachedRoot * root,int childFrameIndex,WebCore::Frame * frame)873 void CachedFrame::init(const CachedRoot* root, int childFrameIndex,
874     WebCore::Frame* frame)
875 {
876     mContents = WebCore::IntRect(0, 0, 0, 0); // fixed up for real in setData()
877     mLocalViewBounds = WebCore::IntRect(0, 0, 0, 0);
878     mViewBounds = WebCore::IntRect(0, 0, 0, 0);
879     mRoot = root;
880     mCursorIndex = CURSOR_UNINITIALIZED; // not explicitly cleared
881     mFocusIndex = -1;
882     mFrame = frame;
883     mParent = NULL; // set up parents after stretchy arrays are set up
884     mIndexInParent = childFrameIndex;
885 }
886 
887 #if USE(ACCELERATED_COMPOSITING)
layer(const CachedNode * node) const888 const CachedLayer* CachedFrame::layer(const CachedNode* node) const
889 {
890     if (!node->isInLayer())
891         return 0;
892     CachedLayer test;
893     test.setCachedNodeIndex(node->index());
894     return std::lower_bound(mCachedLayers.begin(), mCachedLayers.end(), test);
895 }
896 #endif
897 
localBounds(const CachedNode * node,const WebCore::IntRect & rect) const898 WebCore::IntRect CachedFrame::localBounds(const CachedNode* node,
899     const WebCore::IntRect& rect) const
900 {
901     DBG_NAV_LOGD("node=%p [%d] rect=(%d,%d,w=%d,h=%d)",
902         node, node->index(), rect.x(), rect.y(), rect.width(), rect.height());
903 #if USE(ACCELERATED_COMPOSITING)
904     return layer(node)->localBounds(rect);
905 #else
906     return rect;
907 #endif
908 }
909 
minWorkingHorizontal() const910 int CachedFrame::minWorkingHorizontal() const
911 {
912     return history()->minWorkingHorizontal();
913 }
914 
minWorkingVertical() const915 int CachedFrame::minWorkingVertical() const
916 {
917     return history()->minWorkingVertical();
918 }
919 
maxWorkingHorizontal() const920 int CachedFrame::maxWorkingHorizontal() const
921 {
922     return history()->maxWorkingHorizontal();
923 }
924 
maxWorkingVertical() const925 int CachedFrame::maxWorkingVertical() const
926 {
927     return history()->maxWorkingVertical();
928 }
929 
nextTextField(const CachedNode * start,const CachedFrame ** framePtr,bool * startFound) const930 const CachedNode* CachedFrame::nextTextField(const CachedNode* start,
931         const CachedFrame** framePtr, bool* startFound) const
932 {
933     const CachedNode* test = mCachedNodes.begin();
934     while ((test = test->traverseNextNode())) {
935         const CachedFrame* frame = hasFrame(test);
936         if (frame) {
937             if (!frame->validDocument())
938                 continue;
939             const CachedNode* node
940                     = frame->nextTextField(start, framePtr, startFound);
941             if (node)
942                 return node;
943         } else if (test->isTextInput()) {
944             if (test == start)
945                 *startFound = true;
946             else if (*startFound) {
947                 if (framePtr)
948                     *framePtr = this;
949                 return test;
950             }
951         }
952     }
953     return 0;
954 }
955 
moveInFrame(MoveInDirection moveInDirection,const CachedNode * test,BestData * bestData) const956 bool CachedFrame::moveInFrame(MoveInDirection moveInDirection,
957     const CachedNode* test, BestData* bestData) const
958 {
959     const CachedFrame* frame = hasFrame(test);
960     if (frame == NULL)
961         return false; // if it's not a frame, let the caller have another swing at it
962     const CachedNode* childDoc = frame->validDocument();
963     if (childDoc == NULL)
964         return true;
965     (frame->*moveInDirection)(childDoc, NULL, bestData);
966     return true;
967 }
968 
_navBounds() const969 const WebCore::IntRect& CachedFrame::_navBounds() const
970 {
971     return history()->navBounds();
972 }
973 
picture(const CachedNode * node) const974 SkPicture* CachedFrame::picture(const CachedNode* node) const
975 {
976 #if USE(ACCELERATED_COMPOSITING)
977     if (node->isInLayer())
978         return layer(node)->picture(mRoot->rootLayer());
979 #endif
980     return mRoot->mPicture;
981 }
982 
resetClippedOut()983 void CachedFrame::resetClippedOut()
984 {
985     for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++)
986     {
987         if (test->clippedOut()) {
988             test->setDisabled(false);
989             test->setClippedOut(false);
990         }
991     }
992     for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
993             frame++) {
994         frame->resetClippedOut();
995     }
996 }
997 
resetLayers()998 void CachedFrame::resetLayers()
999 {
1000 #if USE(ACCELERATED_COMPOSITING)
1001     for (CachedLayer* test = mCachedLayers.begin(); test != mCachedLayers.end();
1002             test++) {
1003         test->reset();
1004     }
1005     for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
1006             frame++) {
1007         frame->resetLayers();
1008     }
1009 #endif
1010 }
1011 
sameFrame(const CachedFrame * test) const1012 bool CachedFrame::sameFrame(const CachedFrame* test) const
1013 {
1014     ASSERT(test);
1015     if (mIndexInParent != test->mIndexInParent)
1016         return false;
1017     if (mIndexInParent == -1) // index within parent's array of children, or -1 if root
1018         return true;
1019     return mParent->sameFrame(test->mParent);
1020 }
1021 
setData()1022 void CachedFrame::setData()
1023 {
1024     if (this != mRoot) {
1025         mViewBounds = mLocalViewBounds;
1026         mViewBounds.intersect(mRoot->mViewBounds);
1027     }
1028     int x, y;
1029     if (parent() == NULL)
1030         x = y = 0;
1031     else {
1032         x = mLocalViewBounds.x();
1033         y = mLocalViewBounds.y();
1034     }
1035     mContents.setX(x);
1036     mContents.setY(y);
1037     CachedFrame* child = mCachedFrames.begin();
1038     while (child != mCachedFrames.end()) {
1039         child->setData();
1040         child++;
1041     }
1042 }
1043 
setCursor(WebCore::Frame * frame,WebCore::Node * node,int x,int y)1044 bool CachedFrame::setCursor(WebCore::Frame* frame, WebCore::Node* node,
1045     int x, int y)
1046 {
1047     if (NULL == node) {
1048         const_cast<CachedRoot*>(mRoot)->setCursor(NULL, NULL);
1049         return true;
1050     }
1051     if (mFrame != frame) {
1052         for (CachedFrame* testF = mCachedFrames.begin(); testF != mCachedFrames.end();
1053                 testF++) {
1054             if (testF->setCursor(frame, node, x, y))
1055                 return true;
1056         }
1057         DBG_NAV_LOGD("no frame frame=%p node=%p", frame, node);
1058         return false;
1059     }
1060     bool first = true;
1061     CachedNode const * const end = mCachedNodes.end();
1062     do {
1063         for (CachedNode* test = mCachedNodes.begin(); test != end; test++) {
1064             if (test->nodePointer() != node && first)
1065                 continue;
1066             size_t partMax = test->navableRects();
1067             for (size_t part = 0; part < partMax; part++) {
1068                 WebCore::IntRect testBounds = test->ring(this, part);
1069                 if (testBounds.contains(x, y) == false)
1070                     continue;
1071                 if (test->isCursor()) {
1072                     DBG_NAV_LOGD("already set? test=%d frame=%p node=%p x=%d y=%d",
1073                         test->index(), frame, node, x, y);
1074                     return false;
1075                 }
1076                 const_cast<CachedRoot*>(mRoot)->setCursor(this, test);
1077                 return true;
1078             }
1079         }
1080         DBG_NAV_LOGD("moved? frame=%p node=%p x=%d y=%d", frame, node, x, y);
1081     } while ((first ^= true) == false);
1082 failed:
1083     DBG_NAV_LOGD("no match frame=%p node=%p", frame, node);
1084     return false;
1085 }
1086 
validDocument() const1087 const CachedNode* CachedFrame::validDocument() const
1088 {
1089     const CachedNode* doc = document();
1090     return doc != NULL && mViewBounds.isEmpty() == false ? doc : NULL;
1091 }
1092 
canBeReachedByAnotherDirection()1093 bool CachedFrame::BestData::canBeReachedByAnotherDirection()
1094 {
1095     if (mMajorButt > -MIN_OVERLAP)
1096         return false;
1097     mMajorButt = -mMajorButt;
1098     return mNavOutside;
1099 }
1100 
isContainer(CachedFrame::BestData * other)1101 int CachedFrame::BestData::isContainer(CachedFrame::BestData* other)
1102 {
1103     int _x = x();
1104     int otherRight = other->right();
1105     if (_x >= otherRight)
1106         return 0; // does not intersect
1107     int _y = y();
1108     int otherBottom = other->bottom();
1109     if (_y >= otherBottom)
1110         return 0; // does not intersect
1111     int _right = right();
1112     int otherX = other->x();
1113     if (otherX >= _right)
1114         return 0; // does not intersect
1115     int _bottom = bottom();
1116     int otherY = other->y();
1117     if (otherY >= _bottom)
1118         return 0; // does not intersect
1119     int intoX = otherX - _x;
1120     int intoY = otherY - _y;
1121     int intoRight = otherRight - _right;
1122     int intoBottom = otherBottom - _bottom;
1123     bool contains = intoX >= 0 && intoY >= 0 && intoRight <= 0 && intoBottom <= 0;
1124     if (contains && mNode->partRectsContains(other->mNode)) {
1125 //        if (mIsArea == false && hasMouseOver())
1126 //            other->mMouseOver = mNode;
1127         return mNode->isArea() ? 1  : -1;
1128     }
1129     bool containedBy = intoX <= 0 && intoY <= 0 && intoRight >= 0 && intoBottom >= 0;
1130     if (containedBy && other->mNode->partRectsContains(mNode)) {
1131 //        if (other->mIsArea == false && other->hasMouseOver())
1132 //            mMouseOver = other->mNode;
1133         return other->mNode->isArea() ? -1  : 1;
1134     }
1135     return 0;
1136 }
1137 
1138 // distance scale factor factor as a 16.16 scalar
Overlap(int span,int left,int right)1139 SkFixed CachedFrame::BestData::Overlap(int span, int left, int right)
1140 {
1141     unsigned result;
1142     if (left > 0 && left < span && right > span)
1143         result = (unsigned) left;
1144     else if (right > 0 && right < span && left > span)
1145         result = (unsigned) right;
1146     else if (left > 0 && right > 0)
1147         return SK_Fixed1;
1148     else
1149         return 0;
1150     result = (result << 16) / (unsigned) span; // linear proportion, always less than fixed 1
1151     return (SkFixed) result;
1152 // !!! experiment with weight -- enable if overlaps are preferred too much
1153 // or reverse weighting if overlaps are preferred to little
1154 //    return (SkFixed) (result * result >> 16); // but fall off with square
1155 }
1156 
setDistances()1157 void CachedFrame::BestData::setDistances()
1158 {
1159     mDistance = abs(mMajorDelta);
1160     int sideDistance = mWorkingDelta;
1161     if (mWorkingOverlap < SK_Fixed1) {
1162         if (mPreferred > 0)
1163             sideDistance = mWorkingDelta2;
1164     } else if (sideDistance >= 0 && mWorkingDelta2 >=- 0)
1165         sideDistance = 0;
1166     else {
1167         ASSERT(sideDistance <= 0 && mWorkingDelta2 <= 0);
1168         if (sideDistance < mWorkingDelta2)
1169             sideDistance = mWorkingDelta2;
1170     }
1171     // if overlap, smaller abs mWorkingDelta is better, smaller abs majorDelta is better
1172     // if not overlap, positive mWorkingDelta is better
1173     mSideDistance = sideDistance;
1174 }
1175 
setDownDirection(const CachedHistory * history)1176 bool CachedFrame::BestData::setDownDirection(const CachedHistory* history)
1177 {
1178     const WebCore::IntRect& navBounds = history->navBounds();
1179     mMajorButt = mNodeBounds.y() - navBounds.bottom();
1180     int testX = mNodeBounds.x();
1181     int testRight = mNodeBounds.right();
1182     setNavOverlap(navBounds.width(), navBounds.right() - testX,
1183         testRight - navBounds.x());
1184     if (canBeReachedByAnotherDirection()) {
1185         mNode->setCondition(CachedNode::BEST_DIRECTION);
1186         return REJECT_TEST;
1187     }
1188     int inNavTop = mNodeBounds.y() - navBounds.y();
1189     mMajorDelta2 = inNavTop;
1190     mMajorDelta = mMajorDelta2 + ((mNodeBounds.height() -
1191         navBounds.height()) >> 1);
1192     if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
1193         mNode->setCondition(CachedNode::CENTER_FURTHER); // never move up or sideways
1194         return REJECT_TEST;
1195     }
1196     int inNavBottom = navBounds.bottom() - mNodeBounds.bottom();
1197     setNavInclusion(testRight - navBounds.right(), navBounds.x() - testX);
1198     bool subsumes = navBounds.height() > 0 && inOrSubsumesNav();
1199     if (inNavTop <= 0 && inNavBottom <= 0 && subsumes && !mNode->wantsKeyEvents()) {
1200         mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
1201         return REJECT_TEST;
1202     }
1203     int maxV = history->maxWorkingVertical();
1204     int minV = history->minWorkingVertical();
1205     setWorkingOverlap(testRight - testX, maxV - testX, testRight - minV);
1206     setWorkingInclusion(testRight - maxV, minV - testX);
1207     if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavBottom >= 0) {
1208         mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
1209         return REJECT_TEST;
1210     }
1211     mInNav = history->directionChange() && inNavTop >= 0 &&
1212         inNavBottom > 0 && subsumes;
1213     return false;
1214 }
1215 
setLeftDirection(const CachedHistory * history)1216 bool CachedFrame::BestData::setLeftDirection(const CachedHistory* history)
1217 {
1218     const WebCore::IntRect& navBounds = history->navBounds();
1219     mMajorButt = navBounds.x() - mNodeBounds.right();
1220     int testY = mNodeBounds.y();
1221     int testBottom = mNodeBounds.bottom();
1222     setNavOverlap(navBounds.height(), navBounds.bottom() - testY,
1223         testBottom - navBounds.y());
1224     if (canBeReachedByAnotherDirection()) {
1225         mNode->setCondition(CachedNode::BEST_DIRECTION);
1226         return REJECT_TEST;
1227     }
1228     int inNavRight = navBounds.right() - mNodeBounds.right();
1229     mMajorDelta2 = inNavRight;
1230     mMajorDelta = mMajorDelta2 - ((navBounds.width() -
1231         mNodeBounds.width()) >> 1);
1232     if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
1233         mNode->setCondition(CachedNode::CENTER_FURTHER); // never move right or sideways
1234         return REJECT_TEST;
1235     }
1236     int inNavLeft = mNodeBounds.x() - navBounds.x();
1237     setNavInclusion(navBounds.y() - testY, testBottom - navBounds.bottom());
1238     bool subsumes = navBounds.width() > 0 && inOrSubsumesNav();
1239     if (inNavLeft <= 0 && inNavRight <= 0 && subsumes && !mNode->wantsKeyEvents()) {
1240         mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
1241         return REJECT_TEST;
1242     }
1243     int maxH = history->maxWorkingHorizontal();
1244     int minH = history->minWorkingHorizontal();
1245     setWorkingOverlap(testBottom - testY, maxH - testY, testBottom - minH);
1246     setWorkingInclusion(minH - testY, testBottom - maxH);
1247     if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavLeft >= 0) {
1248         mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
1249         return REJECT_TEST;
1250     }
1251     mInNav = history->directionChange() && inNavLeft >= 0 &&
1252         inNavRight > 0 && subsumes; /* both L/R in or out */
1253     return false;
1254 }
1255 
setRightDirection(const CachedHistory * history)1256 bool CachedFrame::BestData::setRightDirection(const CachedHistory* history)
1257 {
1258     const WebCore::IntRect& navBounds = history->navBounds();
1259     mMajorButt = mNodeBounds.x() - navBounds.right();
1260     int testY = mNodeBounds.y();
1261     int testBottom = mNodeBounds.bottom();
1262     setNavOverlap(navBounds.height(), navBounds.bottom() - testY,
1263         testBottom - navBounds.y());
1264     if (canBeReachedByAnotherDirection()) {
1265         mNode->setCondition(CachedNode::BEST_DIRECTION);
1266         return REJECT_TEST;
1267     }
1268     int inNavLeft = mNodeBounds.x() - navBounds.x();
1269     mMajorDelta2 = inNavLeft;
1270     mMajorDelta = mMajorDelta2 + ((mNodeBounds.width() -
1271         navBounds.width()) >> 1);
1272     if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
1273         mNode->setCondition(CachedNode::CENTER_FURTHER); // never move left or sideways
1274         return REJECT_TEST;
1275     }
1276     int inNavRight = navBounds.right() - mNodeBounds.right();
1277     setNavInclusion(testBottom - navBounds.bottom(), navBounds.y() - testY);
1278     bool subsumes = navBounds.width() > 0 && inOrSubsumesNav();
1279     if (inNavLeft <= 0 && inNavRight <= 0 && subsumes && !mNode->wantsKeyEvents()) {
1280         mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
1281         return REJECT_TEST;
1282     }
1283     int maxH = history->maxWorkingHorizontal();
1284     int minH = history->minWorkingHorizontal();
1285     setWorkingOverlap(testBottom - testY, testBottom - minH, maxH - testY);
1286     setWorkingInclusion(testBottom - maxH, minH - testY);
1287     if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavRight >= 0) {
1288         mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
1289         return REJECT_TEST;
1290     }
1291     mInNav = history->directionChange() && inNavLeft >= 0 &&
1292         inNavRight > 0 && subsumes; /* both L/R in or out */
1293     return false;
1294 }
1295 
setUpDirection(const CachedHistory * history)1296 bool CachedFrame::BestData::setUpDirection(const CachedHistory* history)
1297 {
1298     const WebCore::IntRect& navBounds = history->navBounds();
1299     mMajorButt = navBounds.y() - mNodeBounds.bottom();
1300     int testX = mNodeBounds.x();
1301     int testRight = mNodeBounds.right();
1302     setNavOverlap(navBounds.width(), navBounds.right() - testX,
1303         testRight - navBounds.x());
1304     if (canBeReachedByAnotherDirection()) {
1305         mNode->setCondition(CachedNode::BEST_DIRECTION);
1306         return REJECT_TEST;
1307     }
1308     int inNavBottom = navBounds.bottom() - mNodeBounds.bottom();
1309     mMajorDelta2 = inNavBottom;
1310     mMajorDelta = mMajorDelta2 - ((navBounds.height() -
1311         mNodeBounds.height()) >> 1);
1312     if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
1313         mNode->setCondition(CachedNode::CENTER_FURTHER); // never move down or sideways
1314         return REJECT_TEST;
1315     }
1316     int inNavTop = mNodeBounds.y() - navBounds.y();
1317     setNavInclusion(navBounds.x() - testX, testRight - navBounds.right());
1318     bool subsumes = navBounds.height() > 0 && inOrSubsumesNav();
1319     if (inNavTop <= 0 && inNavBottom <= 0 && subsumes && !mNode->wantsKeyEvents()) {
1320         mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
1321         return REJECT_TEST;
1322     }
1323     int maxV = history->maxWorkingVertical();
1324     int minV = history->minWorkingVertical();
1325     setWorkingOverlap(testRight - testX, testRight - minV, maxV - testX);
1326     setWorkingInclusion(minV - testX, testRight - maxV);
1327     if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavTop >= 0) {
1328         mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
1329         return REJECT_TEST;
1330     }
1331     mInNav = history->directionChange() && inNavTop >= 0 &&
1332         inNavBottom > 0 && subsumes; /* both L/R in or out */
1333     return false;
1334 }
1335 
setNavInclusion(int left,int right)1336 void CachedFrame::BestData::setNavInclusion(int left, int right)
1337 {
1338     // if left and right <= 0, test node is completely in umbra of cursor
1339         // prefer leftmost center
1340     // if left and right > 0, test node subsumes cursor
1341     mNavDelta = left;
1342     mNavDelta2 = right;
1343 }
1344 
setNavOverlap(int span,int left,int right)1345 void CachedFrame::BestData::setNavOverlap(int span, int left, int right)
1346 {
1347     // if left or right < 0, test node is not in umbra of cursor
1348     mNavOutside = left < MIN_OVERLAP || right < MIN_OVERLAP;
1349     mNavOverlap = Overlap(span, left, right); // prefer smallest negative left
1350 }
1351 
setWorkingInclusion(int left,int right)1352 void CachedFrame::BestData::setWorkingInclusion(int left, int right)
1353 {
1354     mWorkingDelta = left;
1355     mWorkingDelta2 = right;
1356 }
1357 
1358 // distance scale factor factor as a 16.16 scalar
setWorkingOverlap(int span,int left,int right)1359 void CachedFrame::BestData::setWorkingOverlap(int span, int left, int right)
1360 {
1361     // if left or right < 0, test node is not in umbra of cursor
1362     mWorkingOutside = left < MIN_OVERLAP || right < MIN_OVERLAP;
1363     mWorkingOverlap = Overlap(span, left, right);
1364     mPreferred = left <= 0 ? 0 : left;
1365 }
1366 
1367 #if DUMP_NAV_CACHE
1368 
1369 #define DEBUG_PRINT_RECT(prefix, debugName, field) \
1370     { const WebCore::IntRect& r = b->field; \
1371     DUMP_NAV_LOGD("%s DebugTestRect TEST%s_" #debugName "={%d, %d, %d, %d}; //" #field "\n", \
1372         prefix, mFrameName, r.x(), r.y(), r.width(), r.height()); }
1373 
base() const1374 CachedFrame* CachedFrame::Debug::base() const {
1375     CachedFrame* nav = (CachedFrame*) ((char*) this - OFFSETOF(CachedFrame, mDebug));
1376     return nav;
1377 }
1378 
print() const1379 void CachedFrame::Debug::print() const
1380 {
1381     CachedFrame* b = base();
1382     DEBUG_PRINT_RECT("//", CONTENTS, mContents);
1383     DEBUG_PRINT_RECT("", BOUNDS, mLocalViewBounds);
1384     DEBUG_PRINT_RECT("//", VIEW, mViewBounds);
1385 
1386     DUMP_NAV_LOGD("// CachedNode mCachedNodes={ // count=%d\n", b->mCachedNodes.size());
1387     for (CachedNode* node = b->mCachedNodes.begin();
1388             node != b->mCachedNodes.end(); node++) {
1389         node->mDebug.print();
1390         const CachedInput* input = b->textInput(node);
1391         if (input)
1392             input->mDebug.print();
1393     }
1394     DUMP_NAV_LOGD("// }; // end of nodes\n");
1395 #if USE(ACCELERATED_COMPOSITING)
1396     DUMP_NAV_LOGD("// CachedLayer mCachedLayers={ // count=%d\n", b->mCachedLayers.size());
1397     for (CachedLayer* layer = b->mCachedLayers.begin();
1398             layer != b->mCachedLayers.end(); layer++) {
1399         layer->mDebug.print();
1400     }
1401     DUMP_NAV_LOGD("// }; // end of layers\n");
1402 #endif // USE(ACCELERATED_COMPOSITING)
1403     DUMP_NAV_LOGD("// CachedFrame mCachedFrames={ // count=%d\n", b->mCachedFrames.size());
1404     for (CachedFrame* child = b->mCachedFrames.begin();
1405             child != b->mCachedFrames.end(); child++)
1406     {
1407         child->mDebug.print();
1408     }
1409     DUMP_NAV_LOGD("// }; // end of child frames\n");
1410     DUMP_NAV_LOGD("// void* mFrame=(void*) %p;\n", b->mFrame);
1411     DUMP_NAV_LOGD("// CachedFrame* mParent=%p;\n", b->mParent);
1412     DUMP_NAV_LOGD("// int mIndexInParent=%d;\n", b->mIndexInParent);
1413     DUMP_NAV_LOGD("// const CachedRoot* mRoot=%p;\n", b->mRoot);
1414     DUMP_NAV_LOGD("// int mCursorIndex=%d;\n", b->mCursorIndex);
1415     DUMP_NAV_LOGD("// int mFocusIndex=%d;\n", b->mFocusIndex);
1416 }
1417 
validate(const CachedNode * node) const1418 bool CachedFrame::Debug::validate(const CachedNode* node) const
1419 {
1420     const CachedFrame* b = base();
1421     if (b->mCachedNodes.size() == 0)
1422         return false;
1423     if (node >= b->mCachedNodes.begin() && node < b->mCachedNodes.end())
1424         return true;
1425     for (const CachedFrame* child = b->mCachedFrames.begin();
1426             child != b->mCachedFrames.end(); child++)
1427         if (child->mDebug.validate(node))
1428             return true;
1429     return false;
1430 }
1431 
1432 #undef DEBUG_PRINT_RECT
1433 
1434 #endif
1435 
1436 }
1437