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