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