• 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 "android_graphics.h"
28 #include "CachedFrame.h"
29 #include "CachedHistory.h"
30 #include "Node.h"
31 #include "PlatformString.h"
32 
33 #include "CachedNode.h"
34 
35 namespace android {
36 
bounds(const CachedFrame * frame) const37 WebCore::IntRect CachedNode::bounds(const CachedFrame* frame) const
38 {
39     return mIsInLayer ? frame->adjustBounds(this, mBounds) : mBounds;
40 }
41 
clearCursor(CachedFrame * parent)42 void CachedNode::clearCursor(CachedFrame* parent)
43 {
44     if (isFrame()) {
45         CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
46         child->clearCursor();
47     }
48     mIsCursor = false;
49 }
50 
Clip(const WebCore::IntRect & outer,WebCore::IntRect * inner,WTF::Vector<WebCore::IntRect> * rings)51 bool CachedNode::Clip(const WebCore::IntRect& outer, WebCore::IntRect* inner,
52     WTF::Vector<WebCore::IntRect>* rings)
53 {
54     if (outer.contains(*inner))
55         return true;
56 //    DBG_NAV_LOGD("outer:{%d,%d,%d,%d} does not contain inner:{%d,%d,%d,%d}",
57 //        outer.x(), outer.y(), outer.width(), outer.height(),
58 //        inner->x(), inner->y(), inner->width(), inner->height());
59     bool intersects = outer.intersects(*inner);
60     size_t size = intersects ? rings->size() : 0;
61     *inner = WebCore::IntRect(0, 0, 0, 0);
62     if (intersects) {
63         WebCore::IntRect * const start = rings->begin();
64         WebCore::IntRect* ring = start + size - 1;
65         do {
66             ring->intersect(outer);
67             if (ring->isEmpty()) {
68                 if ((size_t) (ring - start) != --size)
69                     *ring = start[size];
70             } else
71                 inner->unite(*ring);
72         } while (ring-- != start);
73     }
74     rings->shrink(size);
75 //    DBG_NAV_LOGD("size:%d", size);
76     return size != 0;
77 }
78 
clip(const WebCore::IntRect & bounds)79 bool CachedNode::clip(const WebCore::IntRect& bounds)
80 {
81     return Clip(bounds, &mBounds, &mCursorRing);
82 }
83 
84 
cursorRings(const CachedFrame * frame,WTF::Vector<WebCore::IntRect> * rings) const85 void CachedNode::cursorRings(const CachedFrame* frame,
86     WTF::Vector<WebCore::IntRect>* rings) const
87 {
88     rings->clear();
89     for (unsigned index = 0; index < mCursorRing.size(); index++)
90         rings->append(ring(frame, index));
91 }
92 
cursorRingBounds(const CachedFrame * frame) const93 WebCore::IntRect CachedNode::cursorRingBounds(const CachedFrame* frame) const
94 {
95     int partMax = mNavableRects;
96     ASSERT(partMax > 0);
97     WebCore::IntRect bounds = mCursorRing[0];
98     for (int partIndex = 1; partIndex < partMax; partIndex++)
99         bounds.unite(mCursorRing[partIndex]);
100     bounds.inflate(CURSOR_RING_HIT_TEST_RADIUS);
101     return mIsInLayer ? frame->adjustBounds(this, bounds) : bounds;
102 }
103 
104 #define OVERLAP 3
105 
fixUpCursorRects(const CachedFrame * frame)106 void CachedNode::fixUpCursorRects(const CachedFrame* frame)
107 {
108     if (mFixedUpCursorRects)
109         return;
110     mFixedUpCursorRects = true;
111     // if the hit-test rect doesn't intersect any other rect, use it
112     if (mHitBounds != mBounds && mHitBounds.contains(mBounds) &&
113             frame->checkRings(this, mCursorRing, mHitBounds)) {
114         DBG_NAV_LOGD("use mHitBounds (%d,%d,%d,%d)", mHitBounds.x(),
115             mHitBounds.y(), mHitBounds.width(), mHitBounds.height());
116         mUseHitBounds = true;
117         return;
118     }
119     if (mNavableRects <= 1)
120         return;
121     // if there is more than 1 rect, and the bounds doesn't intersect
122     // any other cursor ring bounds, use it
123     if (frame->checkRings(this, mCursorRing, mBounds)) {
124         DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(),
125             mBounds.y(), mBounds.width(), mBounds.height());
126         mUseBounds = true;
127         return;
128     }
129 #if DEBUG_NAV_UI
130     {
131         WebCore::IntRect* boundsPtr = mCursorRing.begin() - 1;
132         const WebCore::IntRect* const boundsEnd = mCursorRing.begin() + mCursorRing.size();
133         while (++boundsPtr < boundsEnd)
134             LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, boundsPtr - mCursorRing.begin(),
135                 boundsPtr->x(), boundsPtr->y(), boundsPtr->width(), boundsPtr->height());
136     }
137 #endif
138     // q: need to know when rects are for drawing and hit-testing, but not mouse down calcs?
139     bool again;
140     do {
141         again = false;
142         size_t size = mCursorRing.size();
143         WebCore::IntRect* unitBoundsPtr = mCursorRing.begin() - 1;
144         const WebCore::IntRect* const unitBoundsEnd = mCursorRing.begin() + size;
145         while (++unitBoundsPtr < unitBoundsEnd) {
146             // any other unitBounds to the left or right of this one?
147             int unitTop = unitBoundsPtr->y();
148             int unitBottom = unitBoundsPtr->bottom();
149             int unitLeft = unitBoundsPtr->x();
150             int unitRight = unitBoundsPtr->right();
151             WebCore::IntRect* testBoundsPtr = mCursorRing.begin() - 1;
152             while (++testBoundsPtr < unitBoundsEnd) {
153                 if (unitBoundsPtr == testBoundsPtr)
154                     continue;
155                 int testTop = testBoundsPtr->y();
156                 int testBottom = testBoundsPtr->bottom();
157                 int testLeft = testBoundsPtr->x();
158                 int testRight = testBoundsPtr->right();
159                 int candidateTop = unitTop > testTop ? unitTop : testTop;
160                 int candidateBottom = unitBottom < testBottom ? unitBottom : testBottom;
161                 int candidateLeft = unitRight < testLeft ? unitRight : testRight;
162                 int candidateRight = unitRight > testLeft ? unitLeft : testLeft;
163                 bool leftRight = true;
164                 if (candidateTop + OVERLAP >= candidateBottom ||
165                         candidateLeft + OVERLAP >= candidateRight) {
166                     candidateTop = unitBottom < testTop ? unitBottom : testBottom;
167                     candidateBottom = unitBottom > testTop ? unitTop : testTop;
168                     candidateLeft = unitLeft > testLeft ? unitLeft : testLeft;
169                     candidateRight = unitRight < testRight ? unitRight : testRight;
170                     if (candidateTop + OVERLAP >= candidateBottom ||
171                             candidateLeft + OVERLAP >= candidateRight)
172                         continue;
173                     leftRight = false;
174                 }
175                 // construct candidate to add
176                 WebCore::IntRect candidate = WebCore::IntRect(candidateLeft, candidateTop,
177                     candidateRight - candidateLeft, candidateBottom - candidateTop);
178                 // does a different unit bounds intersect the candidate? if so, don't add
179                 WebCore::IntRect* checkBoundsPtr = mCursorRing.begin() - 1;
180                 while (++checkBoundsPtr < unitBoundsEnd) {
181                     if (checkBoundsPtr->intersects(candidate) == false)
182                         continue;
183                     if (leftRight) {
184                         if (candidateTop >= checkBoundsPtr->y() &&
185                                 candidateBottom > checkBoundsPtr->bottom())
186                             candidateTop = checkBoundsPtr->bottom();
187                         else if (candidateTop < checkBoundsPtr->y() &&
188                                 candidateBottom <= checkBoundsPtr->bottom())
189                             candidateBottom = checkBoundsPtr->y();
190                         else
191                             goto nextCheck;
192                     } else {
193                         if (candidateLeft >= checkBoundsPtr->x() &&
194                                 candidateRight > checkBoundsPtr->right())
195                             candidateLeft = checkBoundsPtr->right();
196                         else if (candidateLeft < checkBoundsPtr->x() &&
197                                 candidateRight <= checkBoundsPtr->right())
198                             candidateRight = checkBoundsPtr->x();
199                         else
200                             goto nextCheck;
201                     }
202                  }
203                  candidate = WebCore::IntRect(candidateLeft, candidateTop,
204                     candidateRight - candidateLeft, candidateBottom - candidateTop);
205                  ASSERT(candidate.isEmpty() == false);
206 #if DEBUG_NAV_UI
207                 LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, mCursorRing.size(),
208                     candidate.x(), candidate.y(), candidate.width(), candidate.height());
209 #endif
210                 mCursorRing.append(candidate);
211                 again = true;
212                 goto tryAgain;
213         nextCheck:
214                 continue;
215             }
216         }
217 tryAgain:
218         ;
219     } while (again);
220 }
221 
222 
hideCursor(CachedFrame * parent)223 void CachedNode::hideCursor(CachedFrame* parent)
224 {
225     if (isFrame()) {
226         CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
227         child->hideCursor();
228     }
229     mIsHidden = true;
230 }
231 
hitBounds(const CachedFrame * frame) const232 WebCore::IntRect CachedNode::hitBounds(const CachedFrame* frame) const
233 {
234     return mIsInLayer ? frame->adjustBounds(this, mHitBounds) : mHitBounds;
235 }
236 
init(WebCore::Node * node)237 void CachedNode::init(WebCore::Node* node)
238 {
239     bzero(this, sizeof(CachedNode));
240     mExport = WebCore::String();
241     mNode = node;
242     mParentIndex = mDataIndex = -1;
243     mType = android::NORMAL_CACHEDNODETYPE;
244 }
245 
isTextField(const CachedFrame * frame) const246 bool CachedNode::isTextField(const CachedFrame* frame) const
247 {
248     const CachedInput* input = frame->textInput(this);
249     return input ? input->isTextField() : false;
250 }
251 
localCursorRings(const CachedFrame * frame,WTF::Vector<WebCore::IntRect> * rings) const252 void CachedNode::localCursorRings(const CachedFrame* frame,
253     WTF::Vector<WebCore::IntRect>* rings) const
254 {
255     rings->clear();
256     for (unsigned index = 0; index < mCursorRing.size(); index++)
257         rings->append(localRing(frame, index));
258 }
259 
localBounds(const CachedFrame * frame) const260 WebCore::IntRect CachedNode::localBounds(const CachedFrame* frame) const
261 {
262     return mIsInLayer ? frame->localBounds(this, mBounds) : mBounds;
263 }
264 
localHitBounds(const CachedFrame * frame) const265 WebCore::IntRect CachedNode::localHitBounds(const CachedFrame* frame) const
266 {
267     return mIsInLayer ? frame->localBounds(this, mHitBounds) : mHitBounds;
268 }
269 
localRing(const CachedFrame * frame,size_t part) const270 WebCore::IntRect CachedNode::localRing(const CachedFrame* frame,
271     size_t part) const
272 {
273     const WebCore::IntRect& rect = mCursorRing.at(part);
274     return mIsInLayer ? frame->localBounds(this, rect) : rect;
275 }
276 
move(int x,int y)277 void CachedNode::move(int x, int y)
278 {
279     mBounds.move(x, y);
280     // mHitTestBounds will be moved by caller
281     WebCore::IntRect* first = mCursorRing.begin();
282     WebCore::IntRect* last = first + mCursorRing.size();
283     --first;
284     while (++first != last)
285         first->move(x, y);
286 }
287 
partRectsContains(const CachedNode * other) const288 bool CachedNode::partRectsContains(const CachedNode* other) const
289 {
290     int outerIndex = 0;
291     int outerMax = mNavableRects;
292     int innerMax = other->mNavableRects;
293     do {
294         const WebCore::IntRect& outerBounds = mCursorRing[outerIndex];
295         int innerIndex = 0;
296         do {
297             const WebCore::IntRect& innerBounds = other->mCursorRing[innerIndex];
298             if (innerBounds.contains(outerBounds))
299                 return true;
300         } while (++innerIndex < innerMax);
301     } while (++outerIndex < outerMax);
302     return false;
303 }
304 
ring(const CachedFrame * frame,size_t part) const305 WebCore::IntRect CachedNode::ring(const CachedFrame* frame, size_t part) const
306 {
307     const WebCore::IntRect& rect = mCursorRing.at(part);
308     return mIsInLayer ? frame->adjustBounds(this, rect) : rect;
309 }
310 
311 #if DUMP_NAV_CACHE
312 
313 #define DEBUG_PRINT_BOOL(field) \
314     DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
315 
316 #define DEBUG_PRINT_RECT(field) \
317     { const WebCore::IntRect& r = b->field; \
318     DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
319         r.x(), r.y(), r.width(), r.height()); }
320 
base() const321 CachedNode* CachedNode::Debug::base() const {
322     CachedNode* nav = (CachedNode*) ((char*) this - OFFSETOF(CachedNode, mDebug));
323     return nav;
324 }
325 
condition(Condition t) const326 const char* CachedNode::Debug::condition(Condition t) const
327 {
328     switch (t) {
329         case NOT_REJECTED: return "NOT_REJECTED"; break;
330         case BUTTED_UP: return "BUTTED_UP"; break;
331         case CENTER_FURTHER: return "CENTER_FURTHER"; break;
332         case CLOSER: return "CLOSER"; break;
333         case CLOSER_IN_CURSOR: return "CLOSER_IN_CURSOR"; break;
334         case CLOSER_OVERLAP: return "CLOSER_OVERLAP"; break;
335         case CLOSER_TOP: return "CLOSER_TOP"; break;
336         case NAVABLE: return "NAVABLE"; break;
337         case FURTHER: return "FURTHER"; break;
338         case IN_UMBRA: return "IN_UMBRA"; break;
339         case IN_WORKING: return "IN_WORKING"; break;
340         case LEFTMOST: return "LEFTMOST"; break;
341         case OVERLAP_OR_EDGE_FURTHER: return "OVERLAP_OR_EDGE_FURTHER"; break;
342         case PREFERRED: return "PREFERRED"; break;
343         case ANCHOR_IN_ANCHOR: return "ANCHOR_IN_ANCHOR"; break;
344         case BEST_DIRECTION: return "BEST_DIRECTION"; break;
345         case CHILD: return "CHILD"; break;
346         case DISABLED: return "DISABLED"; break;
347         case HIGHER_TAB_INDEX: return "HIGHER_TAB_INDEX"; break;
348         case IN_CURSOR: return "IN_CURSOR"; break;
349         case IN_CURSOR_CHILDREN: return "IN_CURSOR_CHILDREN"; break;
350         case NOT_ENCLOSING_CURSOR: return "NOT_ENCLOSING_CURSOR"; break;
351         case NOT_CURSOR_NODE: return "NOT_CURSOR_NODE"; break;
352         case OUTSIDE_OF_BEST: return "OUTSIDE_OF_BEST"; break;
353         case OUTSIDE_OF_ORIGINAL: return "OUTSIDE_OF_ORIGINAL"; break;
354         default: return "???";
355     }
356 }
357 
type(android::CachedNodeType t) const358 const char* CachedNode::Debug::type(android::CachedNodeType t) const
359 {
360     switch (t) {
361         case NORMAL_CACHEDNODETYPE: return "NORMAL"; break;
362         case ADDRESS_CACHEDNODETYPE: return "ADDRESS"; break;
363         case EMAIL_CACHEDNODETYPE: return "EMAIL"; break;
364         case PHONE_CACHEDNODETYPE: return "PHONE"; break;
365         case ANCHOR_CACHEDNODETYPE: return "ANCHOR"; break;
366         case AREA_CACHEDNODETYPE: return "AREA"; break;
367         case FRAME_CACHEDNODETYPE: return "FRAME"; break;
368         case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break;
369         case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break;
370         default: return "???";
371     }
372 }
373 
print() const374 void CachedNode::Debug::print() const
375 {
376     CachedNode* b = base();
377     char scratch[256];
378     size_t index = snprintf(scratch, sizeof(scratch), "// char* mExport=\"");
379     const UChar* ch = b->mExport.characters();
380     while (ch && *ch && index < sizeof(scratch)) {
381         UChar c = *ch++;
382         if (c < ' ' || c >= 0x7f) c = ' ';
383         scratch[index++] = c;
384     }
385     DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
386     DEBUG_PRINT_RECT(mBounds);
387     DEBUG_PRINT_RECT(mHitBounds);
388     DEBUG_PRINT_RECT(mOriginalAbsoluteBounds);
389     const WTF::Vector<WebCore::IntRect>* rects = &b->mCursorRing;
390     size_t size = rects->size();
391     DUMP_NAV_LOGD("// IntRect cursorRings={ // size=%d\n", size);
392     for (size_t i = 0; i < size; i++) {
393         const WebCore::IntRect& rect = (*rects)[i];
394         DUMP_NAV_LOGD("    // {%d, %d, %d, %d}, // %d\n", rect.x(), rect.y(),
395             rect.width(), rect.height(), i);
396     }
397     DUMP_NAV_LOGD("// };\n");
398     DUMP_NAV_LOGD("// void* mNode=%p; // (%d) \n", b->mNode, mNodeIndex);
399     DUMP_NAV_LOGD("// void* mParentGroup=%p; // (%d) \n", b->mParentGroup, mParentGroupIndex);
400     DUMP_NAV_LOGD("// int mDataIndex=%d;\n", b->mDataIndex);
401     DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex);
402     DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects);
403     DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex);
404     DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex);
405     DUMP_NAV_LOGD("// Condition mCondition=%s;\n", condition(b->mCondition));
406     DUMP_NAV_LOGD("// Type mType=%s;\n", type(b->mType));
407     DEBUG_PRINT_BOOL(mClippedOut);
408     DEBUG_PRINT_BOOL(mDisabled);
409     DEBUG_PRINT_BOOL(mFixedUpCursorRects);
410     DEBUG_PRINT_BOOL(mHasCursorRing);
411     DEBUG_PRINT_BOOL(mHasMouseOver);
412     DEBUG_PRINT_BOOL(mIsCursor);
413     DEBUG_PRINT_BOOL(mIsFocus);
414     DEBUG_PRINT_BOOL(mIsHidden);
415     DEBUG_PRINT_BOOL(mIsInLayer);
416     DEBUG_PRINT_BOOL(mIsParentAnchor);
417     DEBUG_PRINT_BOOL(mIsTransparent);
418     DEBUG_PRINT_BOOL(mIsUnclipped);
419     DEBUG_PRINT_BOOL(mLast);
420     DEBUG_PRINT_BOOL(mUseBounds);
421     DEBUG_PRINT_BOOL(mUseHitBounds);
422     DUMP_NAV_LOGD("\n");
423 }
424 
425 #endif
426 
427 }
428