Home | History | Annotate | Download | only in nav
      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