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 = 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 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 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 234 WebCore::IntRect CachedNode::hitBounds(const CachedFrame* frame) const 235 { 236 return mIsInLayer ? frame->adjustBounds(this, mHitBounds) : mHitBounds; 237 } 238 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 248 bool CachedNode::isTextField(const CachedFrame* frame) const 249 { 250 const CachedInput* input = frame->textInput(this); 251 return input ? input->isTextField() : false; 252 } 253 254 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 262 WebCore::IntRect CachedNode::localBounds(const CachedFrame* frame) const 263 { 264 return mIsInLayer ? frame->localBounds(this, mBounds) : mBounds; 265 } 266 267 WebCore::IntRect CachedNode::localHitBounds(const CachedFrame* frame) const 268 { 269 return mIsInLayer ? frame->localBounds(this, mHitBounds) : mHitBounds; 270 } 271 272 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 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 290 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 307 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 323 CachedNode* CachedNode::Debug::base() const { 324 CachedNode* nav = (CachedNode*) ((char*) this - OFFSETOF(CachedNode, mDebug)); 325 return nav; 326 } 327 328 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 360 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 378 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