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