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, 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 IntRect sloppyBounds = mBounds;
124 sloppyBounds.inflate(2); // give it a couple of extra pixels
125 if (frame->checkRings(this, sloppyBounds)) {
126 DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(),
127 mBounds.y(), mBounds.width(), mBounds.height());
128 mUseBounds = true;
129 return;
130 }
131 #if DEBUG_NAV_UI
132 {
133 WebCore::IntRect* boundsPtr = mCursorRing.begin() - 1;
134 const WebCore::IntRect* const boundsEnd = mCursorRing.begin() + mCursorRing.size();
135 while (++boundsPtr < boundsEnd)
136 LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, boundsPtr - mCursorRing.begin(),
137 boundsPtr->x(), boundsPtr->y(), boundsPtr->width(), boundsPtr->height());
138 }
139 #endif
140 // q: need to know when rects are for drawing and hit-testing, but not mouse down calcs?
141 bool again;
142 do {
143 again = false;
144 size_t size = mCursorRing.size();
145 WebCore::IntRect* unitBoundsPtr = mCursorRing.begin() - 1;
146 const WebCore::IntRect* const unitBoundsEnd = mCursorRing.begin() + size;
147 while (++unitBoundsPtr < unitBoundsEnd) {
148 // any other unitBounds to the left or right of this one?
149 int unitTop = unitBoundsPtr->y();
150 int unitBottom = unitBoundsPtr->maxY();
151 int unitLeft = unitBoundsPtr->x();
152 int unitRight = unitBoundsPtr->maxX();
153 WebCore::IntRect* testBoundsPtr = mCursorRing.begin() - 1;
154 while (++testBoundsPtr < unitBoundsEnd) {
155 if (unitBoundsPtr == testBoundsPtr)
156 continue;
157 int testTop = testBoundsPtr->y();
158 int testBottom = testBoundsPtr->maxY();
159 int testLeft = testBoundsPtr->x();
160 int testRight = testBoundsPtr->maxX();
161 int candidateTop = unitTop > testTop ? unitTop : testTop;
162 int candidateBottom = unitBottom < testBottom ? unitBottom : testBottom;
163 int candidateLeft = unitRight < testLeft ? unitRight : testRight;
164 int candidateRight = unitRight > testLeft ? unitLeft : testLeft;
165 bool leftRight = true;
166 if (candidateTop + OVERLAP >= candidateBottom ||
167 candidateLeft + OVERLAP >= candidateRight) {
168 candidateTop = unitBottom < testTop ? unitBottom : testBottom;
169 candidateBottom = unitBottom > testTop ? unitTop : testTop;
170 candidateLeft = unitLeft > testLeft ? unitLeft : testLeft;
171 candidateRight = unitRight < testRight ? unitRight : testRight;
172 if (candidateTop + OVERLAP >= candidateBottom ||
173 candidateLeft + OVERLAP >= candidateRight)
174 continue;
175 leftRight = false;
176 }
177 // construct candidate to add
178 WebCore::IntRect candidate = WebCore::IntRect(candidateLeft, candidateTop,
179 candidateRight - candidateLeft, candidateBottom - candidateTop);
180 // does a different unit bounds intersect the candidate? if so, don't add
181 WebCore::IntRect* checkBoundsPtr = mCursorRing.begin() - 1;
182 while (++checkBoundsPtr < unitBoundsEnd) {
183 if (checkBoundsPtr->intersects(candidate) == false)
184 continue;
185 if (leftRight) {
186 if (candidateTop >= checkBoundsPtr->y() &&
187 candidateBottom > checkBoundsPtr->maxY())
188 candidateTop = checkBoundsPtr->maxY();
189 else if (candidateTop < checkBoundsPtr->y() &&
190 candidateBottom <= checkBoundsPtr->maxY())
191 candidateBottom = checkBoundsPtr->y();
192 else
193 goto nextCheck;
194 } else {
195 if (candidateLeft >= checkBoundsPtr->x() &&
196 candidateRight > checkBoundsPtr->maxX())
197 candidateLeft = checkBoundsPtr->maxX();
198 else if (candidateLeft < checkBoundsPtr->x() &&
199 candidateRight <= checkBoundsPtr->maxX())
200 candidateRight = checkBoundsPtr->x();
201 else
202 goto nextCheck;
203 }
204 }
205 candidate = WebCore::IntRect(candidateLeft, candidateTop,
206 candidateRight - candidateLeft, candidateBottom - candidateTop);
207 ASSERT(candidate.isEmpty() == false);
208 #if DEBUG_NAV_UI
209 LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, mCursorRing.size(),
210 candidate.x(), candidate.y(), candidate.width(), candidate.height());
211 #endif
212 mCursorRing.append(candidate);
213 again = true;
214 goto tryAgain;
215 nextCheck:
216 continue;
217 }
218 }
219 tryAgain:
220 ;
221 } while (again);
222 }
223
224
hideCursor(CachedFrame * parent)225 void CachedNode::hideCursor(CachedFrame* parent)
226 {
227 if (isFrame()) {
228 CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
229 child->hideCursor();
230 }
231 mIsHidden = true;
232 }
233
hitBounds(const CachedFrame * frame) const234 WebCore::IntRect CachedNode::hitBounds(const CachedFrame* frame) const
235 {
236 return mIsInLayer ? frame->adjustBounds(this, mHitBounds) : mHitBounds;
237 }
238
init(WebCore::Node * node)239 void CachedNode::init(WebCore::Node* node)
240 {
241 bzero(this, sizeof(CachedNode));
242 mExport = WTF::String();
243 mNode = node;
244 mParentIndex = mDataIndex = -1;
245 mType = android::NORMAL_CACHEDNODETYPE;
246 }
247
isTextField(const CachedFrame * frame) const248 bool CachedNode::isTextField(const CachedFrame* frame) const
249 {
250 const CachedInput* input = frame->textInput(this);
251 return input ? input->isTextField() : false;
252 }
253
localCursorRings(const CachedFrame * frame,WTF::Vector<WebCore::IntRect> * rings) const254 void CachedNode::localCursorRings(const CachedFrame* frame,
255 WTF::Vector<WebCore::IntRect>* rings) const
256 {
257 rings->clear();
258 for (unsigned index = 0; index < mCursorRing.size(); index++)
259 rings->append(localRing(frame, index));
260 }
261
localBounds(const CachedFrame * frame) const262 WebCore::IntRect CachedNode::localBounds(const CachedFrame* frame) const
263 {
264 return mIsInLayer ? frame->localBounds(this, mBounds) : mBounds;
265 }
266
localHitBounds(const CachedFrame * frame) const267 WebCore::IntRect CachedNode::localHitBounds(const CachedFrame* frame) const
268 {
269 return mIsInLayer ? frame->localBounds(this, mHitBounds) : mHitBounds;
270 }
271
localRing(const CachedFrame * frame,size_t part) const272 WebCore::IntRect CachedNode::localRing(const CachedFrame* frame,
273 size_t part) const
274 {
275 const WebCore::IntRect& rect = mCursorRing.at(part);
276 return mIsInLayer ? frame->localBounds(this, rect) : rect;
277 }
278
move(int x,int y)279 void CachedNode::move(int x, int y)
280 {
281 mBounds.move(x, y);
282 // mHitTestBounds will be moved by caller
283 WebCore::IntRect* first = mCursorRing.begin();
284 WebCore::IntRect* last = first + mCursorRing.size();
285 --first;
286 while (++first != last)
287 first->move(x, y);
288 }
289
partRectsContains(const CachedNode * other) const290 bool CachedNode::partRectsContains(const CachedNode* other) const
291 {
292 int outerIndex = 0;
293 int outerMax = mNavableRects;
294 int innerMax = other->mNavableRects;
295 do {
296 const WebCore::IntRect& outerBounds = mCursorRing[outerIndex];
297 int innerIndex = 0;
298 do {
299 const WebCore::IntRect& innerBounds = other->mCursorRing[innerIndex];
300 if (innerBounds.contains(outerBounds))
301 return true;
302 } while (++innerIndex < innerMax);
303 } while (++outerIndex < outerMax);
304 return false;
305 }
306
ring(const CachedFrame * frame,size_t part) const307 WebCore::IntRect CachedNode::ring(const CachedFrame* frame, size_t part) const
308 {
309 const WebCore::IntRect& rect = mCursorRing.at(part);
310 return mIsInLayer ? frame->adjustBounds(this, rect) : rect;
311 }
312
313 #if DUMP_NAV_CACHE
314
315 #define DEBUG_PRINT_BOOL(field) \
316 DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
317
318 #define DEBUG_PRINT_RECT(field) \
319 { const WebCore::IntRect& r = b->field; \
320 DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
321 r.x(), r.y(), r.width(), r.height()); }
322
base() const323 CachedNode* CachedNode::Debug::base() const {
324 CachedNode* nav = (CachedNode*) ((char*) this - OFFSETOF(CachedNode, mDebug));
325 return nav;
326 }
327
condition(Condition t) const328 const char* CachedNode::Debug::condition(Condition t) const
329 {
330 switch (t) {
331 case NOT_REJECTED: return "NOT_REJECTED"; break;
332 case BUTTED_UP: return "BUTTED_UP"; break;
333 case CENTER_FURTHER: return "CENTER_FURTHER"; break;
334 case CLOSER: return "CLOSER"; break;
335 case CLOSER_IN_CURSOR: return "CLOSER_IN_CURSOR"; break;
336 case CLOSER_OVERLAP: return "CLOSER_OVERLAP"; break;
337 case CLOSER_TOP: return "CLOSER_TOP"; break;
338 case NAVABLE: return "NAVABLE"; break;
339 case FURTHER: return "FURTHER"; break;
340 case IN_UMBRA: return "IN_UMBRA"; break;
341 case IN_WORKING: return "IN_WORKING"; break;
342 case LEFTMOST: return "LEFTMOST"; break;
343 case OVERLAP_OR_EDGE_FURTHER: return "OVERLAP_OR_EDGE_FURTHER"; break;
344 case PREFERRED: return "PREFERRED"; break;
345 case ANCHOR_IN_ANCHOR: return "ANCHOR_IN_ANCHOR"; break;
346 case BEST_DIRECTION: return "BEST_DIRECTION"; break;
347 case CHILD: return "CHILD"; break;
348 case DISABLED: return "DISABLED"; break;
349 case HIGHER_TAB_INDEX: return "HIGHER_TAB_INDEX"; break;
350 case IN_CURSOR: return "IN_CURSOR"; break;
351 case IN_CURSOR_CHILDREN: return "IN_CURSOR_CHILDREN"; break;
352 case NOT_ENCLOSING_CURSOR: return "NOT_ENCLOSING_CURSOR"; break;
353 case NOT_CURSOR_NODE: return "NOT_CURSOR_NODE"; break;
354 case OUTSIDE_OF_BEST: return "OUTSIDE_OF_BEST"; break;
355 case OUTSIDE_OF_ORIGINAL: return "OUTSIDE_OF_ORIGINAL"; break;
356 default: return "???";
357 }
358 }
359
type(android::CachedNodeType t) const360 const char* CachedNode::Debug::type(android::CachedNodeType t) const
361 {
362 switch (t) {
363 case NORMAL_CACHEDNODETYPE: return "NORMAL"; break;
364 case ADDRESS_CACHEDNODETYPE: return "ADDRESS"; break;
365 case EMAIL_CACHEDNODETYPE: return "EMAIL"; break;
366 case PHONE_CACHEDNODETYPE: return "PHONE"; break;
367 case ANCHOR_CACHEDNODETYPE: return "ANCHOR"; break;
368 case AREA_CACHEDNODETYPE: return "AREA"; break;
369 case FRAME_CACHEDNODETYPE: return "FRAME"; break;
370 case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break;
371 case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break;
372 case SELECT_CACHEDNODETYPE: return "SELECT"; break;
373 case CONTENT_EDITABLE_CACHEDNODETYPE: return "CONTENT_EDITABLE"; break;
374 default: return "???";
375 }
376 }
377
print() const378 void CachedNode::Debug::print() const
379 {
380 CachedNode* b = base();
381 char scratch[256];
382 size_t index = snprintf(scratch, sizeof(scratch), "// char* mExport=\"");
383 const UChar* ch = b->mExport.characters();
384 while (ch && *ch && index < sizeof(scratch)) {
385 UChar c = *ch++;
386 if (c < ' ' || c >= 0x7f) c = ' ';
387 scratch[index++] = c;
388 }
389 DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
390 DEBUG_PRINT_RECT(mBounds);
391 DEBUG_PRINT_RECT(mHitBounds);
392 DEBUG_PRINT_RECT(mOriginalAbsoluteBounds);
393 const WTF::Vector<WebCore::IntRect>* rects = &b->mCursorRing;
394 size_t size = rects->size();
395 DUMP_NAV_LOGD("// IntRect cursorRings={ // size=%d\n", size);
396 for (size_t i = 0; i < size; i++) {
397 const WebCore::IntRect& rect = (*rects)[i];
398 DUMP_NAV_LOGD(" // {%d, %d, %d, %d}, // %d\n", rect.x(), rect.y(),
399 rect.width(), rect.height(), i);
400 }
401 DUMP_NAV_LOGD("// };\n");
402 DUMP_NAV_LOGD("// void* mNode=%p; // (%d) \n", b->mNode, mNodeIndex);
403 DUMP_NAV_LOGD("// void* mParentGroup=%p; // (%d) \n", b->mParentGroup, mParentGroupIndex);
404 DUMP_NAV_LOGD("// int mDataIndex=%d;\n", b->mDataIndex);
405 DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex);
406 DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects);
407 DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex);
408 DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex);
409 DUMP_NAV_LOGD("// int mColorIndex=%d;\n", b->mColorIndex);
410 DUMP_NAV_LOGD("// Condition mCondition=%s;\n", condition(b->mCondition));
411 DUMP_NAV_LOGD("// Type mType=%s;\n", type(b->mType));
412 DEBUG_PRINT_BOOL(mClippedOut);
413 DEBUG_PRINT_BOOL(mDisabled);
414 DEBUG_PRINT_BOOL(mFixedUpCursorRects);
415 DEBUG_PRINT_BOOL(mHasCursorRing);
416 DEBUG_PRINT_BOOL(mHasMouseOver);
417 DEBUG_PRINT_BOOL(mIsCursor);
418 DEBUG_PRINT_BOOL(mIsFocus);
419 DEBUG_PRINT_BOOL(mIsHidden);
420 DEBUG_PRINT_BOOL(mIsInLayer);
421 DEBUG_PRINT_BOOL(mIsParentAnchor);
422 DEBUG_PRINT_BOOL(mIsTransparent);
423 DEBUG_PRINT_BOOL(mIsUnclipped);
424 DEBUG_PRINT_BOOL(mLast);
425 DEBUG_PRINT_BOOL(mUseBounds);
426 DEBUG_PRINT_BOOL(mUseHitBounds);
427 DEBUG_PRINT_BOOL(mSingleImage);
428 }
429
430 #endif
431
432 }
433