diff options
Diffstat (limited to 'WebKit/android/nav')
| -rw-r--r-- | WebKit/android/nav/CacheBuilder.cpp | 209 | ||||
| -rw-r--r-- | WebKit/android/nav/CacheBuilder.h | 17 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedColor.cpp | 58 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedColor.h | 87 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedFrame.cpp | 56 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedFrame.h | 11 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedInput.cpp | 9 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedInput.h | 17 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedLayer.cpp | 94 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedLayer.h | 13 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedNode.cpp | 12 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedNode.h | 18 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedNodeType.h | 4 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedPrefix.h | 7 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedRoot.cpp | 617 | ||||
| -rw-r--r-- | WebKit/android/nav/CachedRoot.h | 11 | ||||
| -rw-r--r-- | WebKit/android/nav/FindCanvas.cpp | 82 | ||||
| -rw-r--r-- | WebKit/android/nav/FindCanvas.h | 1 | ||||
| -rw-r--r-- | WebKit/android/nav/SelectText.cpp | 19 | ||||
| -rw-r--r-- | WebKit/android/nav/WebView.cpp | 507 |
20 files changed, 1416 insertions, 433 deletions
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp index f69b23d..1d53721 100644 --- a/WebKit/android/nav/CacheBuilder.cpp +++ b/WebKit/android/nav/CacheBuilder.cpp @@ -26,6 +26,7 @@ #include "CachedPrefix.h" #include "CachedNode.h" #include "CachedRoot.h" +#include "ColumnInfo.h" #include "Document.h" #include "EventListener.h" #include "EventNames.h" @@ -412,7 +413,7 @@ void CacheBuilder::Debug::groups() { comma(scratch); Element* element = static_cast<Element*>(node); if (node->isElementNode() && element->hasID()) - wideString(element->getIDAttribute()); + wideString(element->getIdAttribute()); else if (node->isTextNode()) { #if 01 // set to one to abbreviate text that can be omitted from the address detection code if (rect.isEmpty() && node->textContent().length() > 100) { @@ -469,22 +470,50 @@ void CacheBuilder::Debug::groups() { } } } - count++; - newLine(); -#if USE(ACCELERATED_COMPOSITING) - if (renderer && layer) { + if (renderer) { + RenderStyle* style = renderer->style(); + snprintf(scratch, sizeof(scratch), "// renderStyle:" + " visibility=%s hasBackGround=%d" + " tapHighlightColor().alpha()=0x%02x" + " isTransparent()=%s", + style->visibility() == HIDDEN ? "HIDDEN" : "VISIBLE", + renderer->hasBackground(), style->tapHighlightColor().alpha(), + renderer->isTransparent() ? "true" : "false"); + newLine(); + print(scratch); + RenderBlock* renderBlock = static_cast<RenderBlock*>(renderer); + if (renderer->isRenderBlock() && renderBlock->hasColumns()) { + const RenderBox* box = static_cast<RenderBox*>(renderer); + const IntRect& oRect = box->visibleOverflowRect(); + snprintf(scratch, sizeof(scratch), "// renderBlock:" + " columnCount=%d columnGap=%d direction=%d" + " hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)", + renderBlock->columnInfo()->columnCount(), renderBlock->columnGap(), + renderBlock->style()->direction(), renderer->hasOverflowClip(), + oRect.x(), oRect.y(), oRect.width(), oRect.height()); + newLine(); + print(scratch); + } + } + #if USE(ACCELERATED_COMPOSITING) + if (renderer && renderer->hasLayer()) { + RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); RenderLayerBacking* back = layer->backing(); GraphicsLayerAndroid* grLayer = static_cast <GraphicsLayerAndroid*>(back ? back->graphicsLayer() : 0); LayerAndroid* aLayer = grLayer ? grLayer->contentLayer() : 0; const SkPicture* pict = aLayer ? aLayer->picture() : 0; + const IntRect& r = renderer->absoluteBoundingBoxRect(); snprintf(scratch, sizeof(scratch), "// layer:%p back:%p" - " gLayer:%p aLayer:%p pict:%p", layer, back, grLayer, - aLayer, pict); - print(scratch); + " gLayer:%p aLayer:%p pict:%p r:(%d,%d,w=%d,h=%d)", + layer, back, grLayer, aLayer, pict, r.x(), r.y(), + r.width(), r.height()); newLine(); - } -#endif + print(scratch); + } + #endif + count++; + newLine(); } while ((node = node->traverseNextNode()) != NULL); DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1); DUMP_NAV_LOGD("\n"); @@ -553,7 +582,7 @@ void CacheBuilder::Debug::groups() { mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", 0 /*textBox->spaceAdd()*/, textBox->start(), 0 /*textBox->textPos()*/); mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d", - textBox->x(), textBox->y(), textBox->width(), textBox->height()); + textBox->x(), textBox->y(), textBox->logicalWidth(), textBox->logicalHeight()); int baseline = textBox->renderer()->style(textBox->isFirstLineStyle())->font().ascent(); mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d }, // %d ", baseline, ++rectIndex); @@ -754,16 +783,18 @@ CacheBuilder::CacheBuilder() } void CacheBuilder::adjustForColumns(const ClipColumnTracker& track, - CachedNode* node, IntRect* bounds) + CachedNode* node, IntRect* bounds, RenderBlock* renderer) { + if (!renderer->hasColumns()) + return; int x = 0; int y = 0; int tx = track.mBounds.x(); int ty = track.mBounds.y(); int columnGap = track.mColumnGap; - size_t limit = track.mColumns->size(); + size_t limit = track.mColumnInfo->columnCount(); for (size_t index = 0; index < limit; index++) { - IntRect column = track.mColumns->at(index); + IntRect column = renderer->columnRectAt(track.mColumnInfo, index); column.move(tx, ty); IntRect test = *bounds; test.move(x, y); @@ -841,6 +872,7 @@ bool CacheBuilder::AnyIsClick(Node* node) void CacheBuilder::buildCache(CachedRoot* root) { Frame* frame = FrameAnd(this); + mPictureSetDisabled = false; BuildFrame(frame, frame, root, (CachedFrame*) root); root->finishInit(); // set up frame parent pointers, child pointers setData((CachedFrame*) root); @@ -878,7 +910,7 @@ static Node* OneAfter(Node* node) static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) { if (renderer->isWidget()) { Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); - if (widget && (widget->isPluginView() || widget->isPluginWidget())) { + if (widget && (widget->isPluginView() || widget->isPluginViewBase())) { // check if this plugin really wants key events (TODO) return true; } @@ -888,7 +920,7 @@ static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) { #if USE(ACCELERATED_COMPOSITING) static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, - int id) + const IntPoint& scroll, int id) { DBG_NAV_LOGD("frame=%p index=%d loc=(%d,%d) id=%d", frame, index, location.x(), location.y(), id); @@ -896,11 +928,41 @@ static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, cachedLayer.reset(); cachedLayer.setCachedNodeIndex(index); cachedLayer.setOffset(location); + cachedLayer.setScrollOffset(scroll); cachedLayer.setUniqueId(id); frame->add(cachedLayer); } #endif +static int FindColorIndex(WTF::Vector<CachedColor>& colorTracker, + const CachedColor& cachedColor) +{ + CachedColor* work = colorTracker.begin() - 1; + CachedColor* end = colorTracker.end(); + while (++work < end) { + if (*work == cachedColor) + return work - colorTracker.begin(); + } + int result = colorTracker.size(); + colorTracker.grow(result + 1); + CachedColor& newColor = colorTracker.last(); + newColor = cachedColor; + return result; +} + +static void InitColor(CachedColor* color) +{ + color->setFillColor(RenderStyle::initialRingFillColor()); + color->setInnerWidth(RenderStyle::initialRingInnerWidth()); + color->setOuterWidth(RenderStyle::initialRingOuterWidth()); + color->setOutset(RenderStyle::initialRingOutset()); + color->setPressedInnerColor(RenderStyle::initialRingPressedInnerColor()); + color->setPressedOuterColor(RenderStyle::initialRingPressedOuterColor()); + color->setRadius(RenderStyle::initialRingRadius()); + color->setSelectedInnerColor(RenderStyle::initialRingSelectedInnerColor()); + color->setSelectedOuterColor(RenderStyle::initialRingSelectedOuterColor()); +} + // when new focus is found, push it's parent on a stack // as long as more focii are found with the same (grand) parent, note it // (which only requires retrieving the last parent on the stack) @@ -928,6 +990,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, bzero(clipTracker.data(), sizeof(ClipColumnTracker)); WTF::Vector<TabIndexTracker> tabIndexTracker(1); // sentinel bzero(tabIndexTracker.data(), sizeof(TabIndexTracker)); + WTF::Vector<CachedColor> colorTracker(1); + InitColor(colorTracker.data()); #if DUMP_NAV_CACHE char* frameNamePtr = cachedFrame->mDebug.mFrameName; Builder(frame)->mDebug.frameName(frameNamePtr, frameNamePtr + @@ -943,16 +1007,18 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, #if DUMP_NAV_CACHE cachedParentNode.mDebug.mNodeIndex = nodeIndex; #endif + cachedFrame->add(colorTracker[0]); cachedFrame->add(cachedParentNode); Node* node = parent; int cacheIndex = 1; + int colorIndex = 0; // assume no special css ring colors + const void* lastStyleDataPtr = 0; int textInputIndex = 0; Node* focused = doc->focusedNode(); if (focused) cachedRoot->setFocusBounds(focused->getRect()); int globalOffsetX, globalOffsetY; GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); - IntPoint bodyPos = IntPoint(0, 0); while (walk.mMore || (node = node->traverseNextNode()) != NULL) { #if DUMP_NAV_CACHE nodeIndex++; @@ -1028,21 +1094,25 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, RenderStyle* style = nodeRenderer->style(); if (style->visibility() == HIDDEN) continue; - isTransparent = style->hasBackground() == false; + isTransparent = nodeRenderer->hasBackground() == false; #ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR hasCursorRing = style->tapHighlightColor().alpha() > 0; #endif #if USE(ACCELERATED_COMPOSITING) if (nodeRenderer->hasLayer()) { TrackLayer(layerTracker, nodeRenderer, lastChild, - globalOffsetX - bodyPos.x(), globalOffsetY - bodyPos.y()); + globalOffsetX, globalOffsetY); size_t size = tracker.size(); - const LayerAndroid* layer = layerTracker.last().mLayer; + LayerAndroid* layer = layerTracker.last().mLayer; if (layer) { int id = layer->uniqueId(); - IntPoint loc = nodeRenderer-> - absoluteBoundingBoxRect().location(); + const RenderLayer* renderLayer = + layerTracker.last().mRenderLayer; + IntPoint loc(SkScalarRound(layer->getPosition().fX), + SkScalarRound(layer->getPosition().fY)); loc.move(globalOffsetX, globalOffsetY); + IntPoint scroll(renderLayer->scrollXOffset(), + renderLayer->scrollYOffset()); // if this is a child of a CachedNode, add a layer size_t limit = cachedFrame->layerCount() == 0 ? 0 : cachedFrame->lastLayer()->cachedNodeIndex(); @@ -1058,7 +1128,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, CachedNode* trackedNode = cachedFrame->getIndex(index); trackedNode->setIsInLayer(true); trackedNode->setIsUnclipped(true); - AddLayer(cachedFrame, index, loc, id); + AddLayer(cachedFrame, index, loc, scroll, id); } } } @@ -1077,11 +1147,12 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, TextDirection direction = LTR; String exported; CachedNodeType type = NORMAL_CACHEDNODETYPE; + CachedColor cachedColor; CachedInput cachedInput; IntRect bounds; IntRect absBounds; IntRect originalAbsBounds; - WTF::Vector<IntRect>* columns = NULL; + ColumnInfo* columnInfo = NULL; if (node->hasTagName(HTMLNames::areaTag)) { type = AREA_CACHEDNODETYPE; HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node); @@ -1102,9 +1173,16 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, originalAbsBounds = absBounds; absBounds.move(globalOffsetX, globalOffsetY); hasClip = nodeRenderer->hasOverflowClip(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (nodeRenderer->hasLayer()) { + const LayerAndroid* layer = layerTracker.last().mLayer; + if (layer && layer->contentIsScrollable()) + hasClip = false; + } +#endif - if (node->hasTagName(HTMLNames::bodyTag)) - bodyPos = originalAbsBounds.location(); + if (node->hasTagName(HTMLNames::canvasTag)) + mPictureSetDisabled = true; if (checkForPluginViewThatWantsFocus(nodeRenderer)) { bounds = absBounds; isUnclipped = true; @@ -1112,27 +1190,32 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, type = PLUGIN_CACHEDNODETYPE; goto keepNode; } + // Only use the root contentEditable element + if (node->isContentEditable() && !node->parent()->isContentEditable()) { + bounds = absBounds; + takesFocus = true; + type = CONTENT_EDITABLE_CACHEDNODETYPE; + goto keepNode; + } if (nodeRenderer->isRenderBlock()) { RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; if (renderBlock->hasColumns()) { - columns = renderBlock->columnRects(); -#ifdef ANDROID_EXPOSE_COLUMN_GAP + columnInfo = renderBlock->columnInfo(); columnGap = renderBlock->columnGap(); -#endif direction = renderBlock->style()->direction(); } } - if ((hasClip != false || columns != NULL) && lastChild) { + if ((hasClip != false || columnInfo != NULL) && lastChild) { clipTracker.grow(clipTracker.size() + 1); ClipColumnTracker& clip = clipTracker.last(); clip.mBounds = absBounds; clip.mLastChild = OneAfter(lastChild); clip.mNode = node; - clip.mColumns = columns; + clip.mColumnInfo = columnInfo; clip.mColumnGap = columnGap; clip.mHasClip = hasClip; clip.mDirection = direction; - if (columns != NULL) { + if (columnInfo != NULL) { const IntRect& oRect = ((RenderBox*)nodeRenderer)->visibleOverflowRect(); clip.mBounds.move(oRect.x(), oRect.y()); } @@ -1177,7 +1260,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, } if (node->hasTagName(WebCore::HTMLNames::inputTag)) { HTMLInputElement* input = static_cast<HTMLInputElement*>(node); - HTMLInputElement::InputType inputType = input->inputType(); + HTMLInputElement::DeprecatedInputType inputType = input->deprecatedInputType(); if (input->isTextField()) { type = TEXT_INPUT_CACHEDNODETYPE; cachedInput.init(); @@ -1216,6 +1299,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string())) // Set the exported string for all non-javascript anchors. exported = href.string().threadsafeCopy(); + } else if (node->hasTagName(HTMLNames::selectTag)) { + type = SELECT_CACHEDNODETYPE; } if (type == TEXT_INPUT_CACHEDNODETYPE) { RenderTextControl* renderText = @@ -1262,6 +1347,28 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, globalOffsetX, globalOffsetY, &cachedNode.mCursorRing) == false) continue; keepTextNode: + if (nodeRenderer) { // area tags' node->renderer() == 0 + RenderStyle* style = nodeRenderer->style(); + const void* styleDataPtr = style->ringData(); + // to save time, see if we're pointing to the same style data as before + if (lastStyleDataPtr != styleDataPtr) { + lastStyleDataPtr = styleDataPtr; + cachedColor.setFillColor(style->ringFillColor()); + cachedColor.setInnerWidth(style->ringInnerWidth()); + cachedColor.setOuterWidth(style->ringOuterWidth()); + cachedColor.setOutset(style->ringOutset()); + cachedColor.setPressedInnerColor(style->ringPressedInnerColor()); + cachedColor.setPressedOuterColor(style->ringPressedOuterColor()); + cachedColor.setRadius(style->ringRadius()); + cachedColor.setSelectedInnerColor(style->ringSelectedInnerColor()); + cachedColor.setSelectedOuterColor(style->ringSelectedOuterColor()); + int oldSize = colorTracker.size(); + colorIndex = FindColorIndex(colorTracker, cachedColor); + if (colorIndex == oldSize) + cachedFrame->add(cachedColor); + } + } else + colorIndex = 0; IntRect clip = hasClip ? bounds : absBounds; size_t clipIndex = clipTracker.size(); if (clipTracker.last().mNode == node) @@ -1269,7 +1376,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, while (--clipIndex > 0) { const ClipColumnTracker& clipTrack = clipTracker.at(clipIndex); if (clipTrack.mHasClip == false) { - adjustForColumns(clipTrack, &cachedNode, &absBounds); + adjustForColumns(clipTrack, &cachedNode, &absBounds, static_cast<RenderBlock*>(nodeRenderer)); continue; } const IntRect& parentClip = clipTrack.mBounds; @@ -1291,17 +1398,22 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, LayerAndroid* layer = layerTracker.last().mLayer; if (layer) { const IntRect& layerClip = layerTracker.last().mBounds; - if (!layerClip.isEmpty() && !cachedNode.clip(layerClip)) { + const RenderLayer* renderLayer = layerTracker.last().mRenderLayer; + if (!layer->contentIsScrollable() && !layerClip.isEmpty() && + !cachedNode.clip(layerClip)) { DBG_NAV_LOGD("skipped on layer clip %d", cacheIndex); continue; // skip this node if outside of the clip } isInLayer = true; isUnclipped = true; // assume that layers do not have occluded nodes + IntPoint scroll(renderLayer->scrollXOffset(), + renderLayer->scrollYOffset()); AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(), - layer->uniqueId()); + scroll, layer->uniqueId()); } #endif cachedNode.setNavableRects(); + cachedNode.setColorIndex(colorIndex); cachedNode.setExport(exported); cachedNode.setHasCursorRing(hasCursorRing); cachedNode.setHasMouseOver(hasMouseOver); @@ -2680,7 +2792,8 @@ bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, do { do { do { - node = node->traverseNextNode(); + if (node) + node = node->traverseNextNode(); if (node == NULL || node->hasTagName(HTMLNames::aTag) || node->hasTagName(HTMLNames::inputTag) || node->hasTagName(HTMLNames::textareaTag)) { @@ -2727,10 +2840,10 @@ tryNextCheckType: exported->replace(index, 1, escapedComma); } break; case EMAIL_CACHEDNODETYPE: - exported->insert(WebCore::String("mailto:"), 0); + exported->insert(WTF::String("mailto:"), 0); break; case PHONE_CACHEDNODETYPE: - exported->insert(WebCore::String("tel:"), 0); + exported->insert(WTF::String("tel:"), 0); break; default: break; @@ -2803,7 +2916,8 @@ void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker, layerTracker.grow(layerTracker.size() + 1); LayerTracker& indexTracker = layerTracker.last(); indexTracker.mLayer = aLayer; - indexTracker.mBounds = nodeRenderer->absoluteBoundingBoxRect(); + indexTracker.mRenderLayer = layer; + indexTracker.mBounds = IntRect(FloatRect(aLayer->bounds())); indexTracker.mBounds.move(offsetX, offsetY); indexTracker.mLastChild = lastChild ? OneAfter(lastChild) : 0; DBG_NAV_LOGD("layer=%p [%d] bounds=(%d,%d,w=%d,h=%d)", aLayer, @@ -2921,18 +3035,18 @@ bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds, EVisibility vis = renderer->style()->visibility(); if (vis == HIDDEN) continue; + bool hasClip = renderer->hasOverflowClip(); + size_t clipIndex = clipTracker.size(); + IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); + if (hasClip || --clipIndex > 0) { + clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() : + clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect + } if (test->isTextNode()) { RenderText* renderText = (RenderText*) renderer; InlineTextBox *textBox = renderText->firstTextBox(); if (textBox == NULL) continue; - bool hasClip = renderer->hasOverflowClip(); - size_t clipIndex = clipTracker.size(); - IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); - if (hasClip || --clipIndex > 0) { - clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() : - clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect - } if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX, x, y, focusBounds, clipBounds, result) == false) { return false; @@ -2941,6 +3055,7 @@ bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds, } if (test->hasTagName(HTMLNames::imgTag)) { IntRect bounds = test->getRect(); + bounds.intersect(clipBounds); if (AddPartRect(bounds, x, y, result, focusBounds) == false) return false; continue; diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h index 407d590..9832865 100644 --- a/WebKit/android/nav/CacheBuilder.h +++ b/WebKit/android/nav/CacheBuilder.h @@ -31,7 +31,8 @@ #include "IntRect.h" #include "PlatformString.h" #include "TextDirection.h" -#include "wtf/Vector.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> #define NAVIGATION_MAX_PHONE_LENGTH 14 @@ -39,7 +40,7 @@ using namespace WebCore; namespace WebCore { -class AtomicString; +class ColumnInfo; class Document; class Frame; class HTMLAreaElement; @@ -47,9 +48,10 @@ class InlineTextBox; class LayerAndroid; class Node; class PlatformGraphicsContext; +class RenderBlock; class RenderFlow; -class RenderObject; class RenderLayer; +class RenderObject; class Text; } @@ -94,6 +96,7 @@ public: static IntRect getAreaRect(const HTMLAreaElement* area); static void GetGlobalOffset(Frame* , int* x, int * y); static void GetGlobalOffset(Node* , int* x, int * y); + bool pictureSetDisabled() { return mPictureSetDisabled; } static bool validNode(Frame* startFrame, void* framePtr, void* nodePtr); private: enum AddressProgress { @@ -190,13 +193,14 @@ private: struct ClipColumnTracker : Tracker { Node* mNode; IntRect mBounds; - WTF::Vector<IntRect>* mColumns; + ColumnInfo* mColumnInfo; int mColumnGap; TextDirection mDirection; bool mHasClip; }; struct LayerTracker : Tracker { LayerAndroid* mLayer; + RenderLayer* mRenderLayer; IntRect mBounds; }; struct TabIndexTracker : Tracker { @@ -207,7 +211,7 @@ private: bool mSomeParentTakesFocus; }; void adjustForColumns(const ClipColumnTracker& track, - CachedNode* node, IntRect* bounds); + CachedNode* node, IntRect* bounds, RenderBlock*); static bool AddPartRect(IntRect& bounds, int x, int y, WTF::Vector<IntRect>* result, IntRect* focusBounds); static bool AnyIsClick(Node* node); @@ -215,7 +219,7 @@ private: static bool NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length); void BuildFrame(Frame* root, Frame* frame, CachedRoot* cachedRoot, CachedFrame* cachedFrame); - bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame, + bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex); static bool ConstructTextRect(Text* textNode, InlineTextBox* textBox, int start, int relEnd, int x, int y, @@ -249,6 +253,7 @@ private: Node* tryFocus(Direction direction); Node* trySegment(Direction direction, int mainStart, int mainEnd); CachedNodeBits mAllowableTypes; + bool mPictureSetDisabled; #if DUMP_NAV_CACHE public: class Debug { diff --git a/WebKit/android/nav/CachedColor.cpp b/WebKit/android/nav/CachedColor.cpp new file mode 100644 index 0000000..c610022 --- /dev/null +++ b/WebKit/android/nav/CachedColor.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CachedPrefix.h" +#include "CachedColor.h" + +namespace android { + +#if DUMP_NAV_CACHE + +#define DEBUG_PRINT_COLOR(field) \ + DUMP_NAV_LOGD("// SkColor " #field "=0x%08x;\n", b->field) + +CachedColor* CachedColor::Debug::base() const { + CachedColor* nav = (CachedColor*) ((char*) this - OFFSETOF(CachedColor, mDebug)); + return nav; +} + +void CachedColor::Debug::print() const +{ + CachedColor* b = base(); + DEBUG_PRINT_COLOR(mFillColor); + DUMP_NAV_LOGD("// int mInnerWidth=%d;\n", b->mInnerWidth); + DUMP_NAV_LOGD("// int mOuterWidth=%d;\n", b->mOuterWidth); + DUMP_NAV_LOGD("// int mOutset=%d;\n", b->mOutset); + DEBUG_PRINT_COLOR(mPressedInnerColor); + DEBUG_PRINT_COLOR(mPressedOuterColor); + DUMP_NAV_LOGD("// int mRadius=%d;\n", b->mRadius); + DEBUG_PRINT_COLOR(mSelectedInnerColor); + DEBUG_PRINT_COLOR(mSelectedOuterColor); +} + +#endif + +} + diff --git a/WebKit/android/nav/CachedColor.h b/WebKit/android/nav/CachedColor.h new file mode 100644 index 0000000..4b39810 --- /dev/null +++ b/WebKit/android/nav/CachedColor.h @@ -0,0 +1,87 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CachedColor_H +#define CachedColor_H + +#include "CachedDebug.h" +#include "Color.h" +#include "Length.h" +#include "SkColor.h" + +using namespace WebCore; + +namespace android { + +class CachedColor { +public: + CachedColor() { + // Initiaized to 0 in its array, so nothing to do in the + // constructor + } + bool operator==(const CachedColor& o) const { + return memcmp(&o, this, sizeof(this)) == 0; } + SkColor fillColor() const { return mFillColor; } + void init(); + int innerWidth() const { return mInnerWidth; } + int outerWidth() const { return mOuterWidth; } + int outset() const { return mOutset; } + SkColor pressedInnerColor() const { return mPressedInnerColor; } + SkColor pressedOuterColor() const { return mPressedOuterColor; } + int radius() const { return mRadius; } + SkColor selectedInnerColor() const { return mSelectedInnerColor; } + SkColor selectedOuterColor() const { return mSelectedOuterColor; } + void setFillColor(const Color& c) { mFillColor = c.rgb(); } + void setInnerWidth(Length l) { mInnerWidth = l.value(); } + void setOuterWidth(Length l) { mOuterWidth = l.value(); } + void setOutset(Length l) { mOutset = l.value(); } + void setPressedInnerColor(const Color& c) { mPressedInnerColor = c.rgb(); } + void setPressedOuterColor(const Color& c) { mPressedOuterColor = c.rgb(); } + void setRadius(Length l) { mRadius = l.value(); } + void setSelectedInnerColor(const Color& c) { mSelectedInnerColor = c.rgb(); } + void setSelectedOuterColor(const Color& c) { mSelectedOuterColor = c.rgb(); } +private: + SkColor mFillColor; + int mInnerWidth; + int mOuterWidth; + int mOutset; + SkColor mPressedInnerColor; + SkColor mPressedOuterColor; + int mRadius; + SkColor mSelectedInnerColor; + SkColor mSelectedOuterColor; +#if DUMP_NAV_CACHE +public: + class Debug { +public: + CachedColor* base() const; + void print() const; + } mDebug; +#endif +}; + +} + +#endif diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp index ce5600b..81ef299 100644 --- a/WebKit/android/nav/CachedFrame.cpp +++ b/WebKit/android/nav/CachedFrame.cpp @@ -48,6 +48,19 @@ WebCore::IntRect CachedFrame::adjustBounds(const CachedNode* node, #endif } +// This is for nodes inside a layer. It takes an IntRect that has been +// adjusted by the layer's position and removes the adjustment made by the +// layer. +WebCore::IntRect CachedFrame::unadjustBounds(const CachedNode* node, + const WebCore::IntRect& rect) const +{ +#if USE(ACCELERATED_COMPOSITING) + if (node->isInLayer()) + return layer(node)->unadjustBounds(mRoot->rootLayer(), rect); +#endif + return rect; +} + bool CachedFrame::CheckBetween(Direction direction, const WebCore::IntRect& bestRect, const WebCore::IntRect& prior, WebCore::IntRect* result) { @@ -119,9 +132,10 @@ bool CachedFrame::checkBetween(BestData* best, Direction direction) bool CachedFrame::checkRings(const CachedNode* node, const WTF::Vector<WebCore::IntRect>& rings, - const WebCore::IntRect& bounds) const + const WebCore::IntRect& nodeBounds, + const WebCore::IntRect& testBounds) const { - return mRoot->checkRings(picture(node), rings, bounds); + return mRoot->checkRings(picture(node), rings, nodeBounds, testBounds); } bool CachedFrame::checkVisited(const CachedNode* node, Direction direction) const @@ -394,8 +408,10 @@ const CachedNode* CachedFrame::findBestAt(const WebCore::IntRect& rect, if (*directHit == NULL) { *directHit = test; *directHitFramePtr = this; - *x = center.x(); - *y = center.y(); + IntRect r(center, IntSize(0, 0)); + r = unadjustBounds(test, r); + *x = r.x(); + *y = r.y(); } else { // We have hit another one before const CachedNode* d = *directHit; @@ -436,6 +452,7 @@ const CachedNode* CachedFrame::findBestAt(const WebCore::IntRect& rect, *inside = testInside; result = test; *framePtr = this; + both = unadjustBounds(test, both); *x = both.x() + (both.width() >> 1); *y = both.y() + (both.height() >> 1); } @@ -495,17 +512,30 @@ const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect, testData.setNodeBounds(testRect); if (mRoot->maskIfHidden(&testData) == true) continue; + DBG_NAV_LOGD("candidate %d rect=(%d,%d,r=%d,b=%d)" + " testRect=(%d,%d,r=%d,b=%d)", + test->index(), rect.x(), rect.y(), rect.right(), rect.bottom(), + testRect.x(), testRect.y(), testRect.right(), testRect.bottom()); for (int i = 0; i < test->navableRects(); i++) { WebCore::IntRect cursorRect = test->ring(this, i); + DBG_NAV_LOGD("candidate %d cursorRect=(%d,%d,r=%d,b=%d)", + i, cursorRect.x(), cursorRect.y(), cursorRect.right(), + cursorRect.bottom()); if (cursorRect.intersects(rect)) { WebCore::IntRect intersection(cursorRect); intersection.intersect(rect); + intersection = unadjustBounds(test, intersection); *x = intersection.x() + (intersection.width() >> 1); *y = intersection.y() + (intersection.height() >> 1); *framePtr = this; return test; } } + testRect.intersect(rect); + *x = testRect.x() + (testRect.width() >> 1); + *y = testRect.y() + (testRect.height() >> 1); + *framePtr = this; + return test; } return NULL; } @@ -696,12 +726,17 @@ int CachedFrame::frameNodeCommon(BestData& testData, const CachedNode* test, testData.mNode->setCondition(CachedNode::DISABLED); return REJECT_TEST; } - if (mRoot->scrolledBounds().intersects(test->bounds(this)) == false) { + WebCore::IntRect bounds = test->bounds(this); + if (bounds.isEmpty()) { + testData.mNode->setCondition(CachedNode::NAVABLE); + return REJECT_TEST; + } + if (mRoot->scrolledBounds().intersects(bounds) == false) { testData.mNode->setCondition(CachedNode::NAVABLE); return REJECT_TEST; } if (mRoot->rootLayer() && !test->isInLayer() - && !mRoot->baseUncovered().intersects(test->bounds(this))) { + && !mRoot->baseUncovered().intersects(bounds)) { testData.mNode->setCondition(CachedNode::UNDER_LAYER); return REJECT_TEST; } @@ -901,7 +936,7 @@ WebCore::IntRect CachedFrame::localBounds(const CachedNode* node, DBG_NAV_LOGD("node=%p [%d] rect=(%d,%d,w=%d,h=%d)", node, node->index(), rect.x(), rect.y(), rect.width(), rect.height()); #if USE(ACCELERATED_COMPOSITING) - return layer(node)->localBounds(rect); + return layer(node)->localBounds(mRoot->rootLayer(), rect); #else return rect; #endif @@ -1390,6 +1425,7 @@ void CachedFrame::Debug::print() const const CachedInput* input = b->textInput(node); if (input) input->mDebug.print(); + DUMP_NAV_LOGD("\n"); } DUMP_NAV_LOGD("// }; // end of nodes\n"); #if USE(ACCELERATED_COMPOSITING) @@ -1400,6 +1436,12 @@ void CachedFrame::Debug::print() const } DUMP_NAV_LOGD("// }; // end of layers\n"); #endif // USE(ACCELERATED_COMPOSITING) + DUMP_NAV_LOGD("// CachedColor mCachedColors={ // count=%d\n", b->mCachedColors.size()); + for (CachedColor* color = b->mCachedColors.begin(); + color != b->mCachedColors.end(); color++) { + color->mDebug.print(); + } + DUMP_NAV_LOGD("// }; // end of colors\n"); DUMP_NAV_LOGD("// CachedFrame mCachedFrames={ // count=%d\n", b->mCachedFrames.size()); for (CachedFrame* child = b->mCachedFrames.begin(); child != b->mCachedFrames.end(); child++) diff --git a/WebKit/android/nav/CachedFrame.h b/WebKit/android/nav/CachedFrame.h index 9334707..bd47421 100644 --- a/WebKit/android/nav/CachedFrame.h +++ b/WebKit/android/nav/CachedFrame.h @@ -26,6 +26,7 @@ #ifndef CachedFrame_H #define CachedFrame_H +#include "CachedColor.h" #include "CachedInput.h" #include "CachedLayer.h" #include "CachedNode.h" @@ -70,6 +71,7 @@ public: CURSOR_SET = 0 }; CachedFrame() {} + void add(CachedColor& color) { mCachedColors.append(color); } void add(CachedInput& input) { mCachedTextInputs.append(input); } #if USE(ACCELERATED_COMPOSITING) void add(CachedLayer& layer) { mCachedLayers.append(layer); } @@ -78,12 +80,18 @@ public: void addFrame(CachedFrame& child) { mCachedFrames.append(child); } WebCore::IntRect adjustBounds(const CachedNode* , const WebCore::IntRect& ) const; + WebCore::IntRect unadjustBounds(const CachedNode*, + const WebCore::IntRect& ) const; bool checkRings(const CachedNode* node, const WTF::Vector<WebCore::IntRect>& rings, - const WebCore::IntRect& bounds) const; + const WebCore::IntRect& nodeBounds, + const WebCore::IntRect& testBounds) const; bool checkVisited(const CachedNode* , CachedFrame::Direction ) const; size_t childCount() { return mCachedFrames.size(); } void clearCursor(); + const CachedColor& color(const CachedNode* node) const { + return mCachedColors[node->colorIndex()]; + } const CachedNode* currentCursor() const { return currentCursor(NULL); } const CachedNode* currentCursor(const CachedFrame** ) const; const CachedNode* currentFocus() const { return currentFocus(NULL); } @@ -224,6 +232,7 @@ private: // since computing these is complicated, protect them so that the WebCore::IntRect mContents; WebCore::IntRect mLocalViewBounds; WebCore::IntRect mViewBounds; + WTF::Vector<CachedColor> mCachedColors; WTF::Vector<CachedNode> mCachedNodes; WTF::Vector<CachedFrame> mCachedFrames; WTF::Vector<CachedInput> mCachedTextInputs; diff --git a/WebKit/android/nav/CachedInput.cpp b/WebKit/android/nav/CachedInput.cpp index cba5820..7c9beba 100644 --- a/WebKit/android/nav/CachedInput.cpp +++ b/WebKit/android/nav/CachedInput.cpp @@ -28,6 +28,11 @@ namespace android { +void CachedInput::init() { + bzero(this, sizeof(CachedInput)); + mName = WTF::String(); +} + #if DUMP_NAV_CACHE #define DEBUG_PRINT_BOOL(field) \ @@ -39,7 +44,7 @@ CachedInput* CachedInput::Debug::base() const { } static void printWebCoreString(const char* label, - const WebCore::String& string) { + const WTF::String& string) { char scratch[256]; size_t index = snprintf(scratch, sizeof(scratch), label); const UChar* ch = string.characters(); @@ -55,7 +60,7 @@ void CachedInput::Debug::print() const { CachedInput* b = base(); printWebCoreString("// char* mName=\"", b->mName); - DUMP_NAV_LOGD("// void* mForm=%p;", b->mForm); + DUMP_NAV_LOGD("// void* mForm=%p;\n", b->mForm); DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength); DUMP_NAV_LOGD("// int mTextSize=%d;\n", b->mTextSize); DUMP_NAV_LOGD("// int mInputType=%d;\n", b->mInputType); diff --git a/WebKit/android/nav/CachedInput.h b/WebKit/android/nav/CachedInput.h index 01b40f9..0809208 100644 --- a/WebKit/android/nav/CachedInput.h +++ b/WebKit/android/nav/CachedInput.h @@ -39,25 +39,22 @@ public: // constructor } void* formPointer() const { return mForm; } - void init() { - bzero(this, sizeof(CachedInput)); - mName = WebCore::String(); - } - WebCore::HTMLInputElement::InputType inputType() const { return mInputType; } + void init(); + WebCore::HTMLInputElement::DeprecatedInputType inputType() const { return mInputType; } bool isRtlText() const { return mIsRtlText; } bool isTextField() const { return mIsTextField; } int maxLength() const { return mMaxLength; }; - const WebCore::String& name() const { return mName; } + const WTF::String& name() const { return mName; } int paddingBottom() const { return mPaddingBottom; } int paddingLeft() const { return mPaddingLeft; } int paddingRight() const { return mPaddingRight; } int paddingTop() const { return mPaddingTop; } void setFormPointer(void* form) { mForm = form; } - void setInputType(WebCore::HTMLInputElement::InputType type) { mInputType = type; } + void setInputType(WebCore::HTMLInputElement::DeprecatedInputType type) { mInputType = type; } void setIsRtlText(bool isRtlText) { mIsRtlText = isRtlText; } void setIsTextField(bool isTextField) { mIsTextField = isTextField; } void setMaxLength(int maxLength) { mMaxLength = maxLength; } - void setName(const WebCore::String& name) { mName = name; } + void setName(const WTF::String& name) { mName = name; } void setPaddingBottom(int bottom) { mPaddingBottom = bottom; } void setPaddingLeft(int left) { mPaddingLeft = left; } void setPaddingRight(int right) { mPaddingRight = right; } @@ -66,14 +63,14 @@ public: int textSize() const { return mTextSize; } private: void* mForm; - WebCore::HTMLInputElement::InputType mInputType; + WTF::String mName; int mMaxLength; - WebCore::String mName; int mPaddingBottom; int mPaddingLeft; int mPaddingRight; int mPaddingTop; int mTextSize; + WebCore::HTMLInputElement::DeprecatedInputType mInputType; bool mIsRtlText : 1; bool mIsTextField : 1; #if DUMP_NAV_CACHE diff --git a/WebKit/android/nav/CachedLayer.cpp b/WebKit/android/nav/CachedLayer.cpp index 2f6e20a..c4293a5 100644 --- a/WebKit/android/nav/CachedLayer.cpp +++ b/WebKit/android/nav/CachedLayer.cpp @@ -46,23 +46,79 @@ IntRect CachedLayer::adjustBounds(const LayerAndroid* root, return bounds; } FloatRect temp = bounds; + // First, remove the original offset from the bounds. + temp.move(-mOffset.x(), -mOffset.y()); + + // Now, add in the original scroll position. This moves the node to the + // original location within the layer. + temp.move(mScrollOffset.x(), mScrollOffset.y()); + + // Next, add in the new position of the layer (could be different due to a + // fixed position layer). const FloatPoint& position = aLayer->getPosition(); temp.move(position.x(), position.y()); + + // Add in any layer translation. const FloatPoint& translation = aLayer->translation(); temp.move(translation.x(), translation.y()); + + // Move the bounds by the layer's internal scroll position. + const SkPoint& scroll = aLayer->scrollPosition(); + temp.move(SkScalarToFloat(-scroll.fX), SkScalarToFloat(-scroll.fY)); + IntRect result = enclosingIntRect(temp); + + // Finally, clip the result to the foreground (this includes the object's + // border which does not scroll). + IntRect clip(aLayer->foregroundClip()); + clip.move(position.x(), position.y()); + result.intersect(clip); + DBG_NAV_LOGD("root=%p aLayer=%p [%d]" - " bounds=(%d,%d,w=%d,h=%d) pos=(%g,%g) trans=(%g,%g)" - " result=(%d,%d,w=%d,h=%d) offset=(%d,%d)", + " bounds=(%d,%d,w=%d,h=%d) trans=(%g,%g)" + " pos=(%f,%f)" + " offset=(%d,%d) clip=(%d,%d,w=%d,h=%d)" + " scroll=(%d,%d) origScroll=(%d,%d)" + " result=(%d,%d,w=%d,h=%d)", root, aLayer, aLayer->uniqueId(), bounds.x(), bounds.y(), bounds.width(), bounds.height(), - position.x(), position.y(), translation.x(), translation.y(), - result.x(), result.y(), result.width(), result.height(), - mOffset.x(), mOffset.y()); - result.move(-mOffset.x(), -mOffset.y()); + translation.x(), translation.y(), position.x(), position.y(), + mOffset.x(), mOffset.y(), + clip.x(), clip.y(), clip.width(), clip.height(), + SkScalarRound(scroll.fX), SkScalarRound(scroll.fY), + mScrollOffset.x(), mScrollOffset.y(), + result.x(), result.y(), result.width(), result.height()); return result; } +IntRect CachedLayer::unadjustBounds(const LayerAndroid* root, + const IntRect& bounds) const +{ + const LayerAndroid* aLayer = layer(root); + if (!aLayer) + return bounds; + + IntRect temp = bounds; + // Remove the new position (i.e. fixed position elements). + const FloatPoint& position = aLayer->getPosition(); + temp.move(-position.x(), -position.y()); + + // Remove any layer translation. + const FloatPoint& translation = aLayer->translation(); + temp.move(-translation.x(), -translation.y()); + + // Move the bounds by the internal scroll position. + const SkPoint& scroll = aLayer->scrollPosition(); + temp.move(SkScalarRound(scroll.fX), SkScalarRound(scroll.fY)); + + // Move it back to the original offset. + temp.move(mOffset.x(), mOffset.y()); + + // Move the bounds by the original scroll. + temp.move(-mScrollOffset.x(), -mScrollOffset.y()); + return temp; +} + const LayerAndroid* CachedLayer::layer(const LayerAndroid* root) const { if (!root || mLayer) @@ -71,10 +127,34 @@ const LayerAndroid* CachedLayer::layer(const LayerAndroid* root) const } // return bounds relative to enclosing layer as recorded when walking the dom -IntRect CachedLayer::localBounds(const IntRect& bounds) const +IntRect CachedLayer::localBounds(const LayerAndroid* root, + const IntRect& bounds) const { IntRect temp = bounds; + // Remove the original offset from the bounds. temp.move(-mOffset.x(), -mOffset.y()); + + // We add in the original scroll position in order to position the node + // relative to the current internal scroll position. + temp.move(mScrollOffset.x(), mScrollOffset.y()); + + const LayerAndroid* aLayer = layer(root); + if (aLayer) { + // Move the bounds by the scroll position of the layer. + const SkPoint& scroll = aLayer->scrollPosition(); + temp.move(SkScalarToFloat(-scroll.fX), SkScalarToFloat(-scroll.fY)); + + // Clip by the layer's foreground bounds. Since the bounds have + // already be moved local to the layer, no need to move the foreground + // clip. + temp.intersect(IntRect(aLayer->foregroundClip())); + } + + DBG_NAV_LOGD("bounds=(%d,%d,w=%d,h=%d) offset=(%d,%d)" + " result=(%d,%d,w=%d,h=%d)", bounds.x(), bounds.y(), + bounds.width(), bounds.height(), mOffset.x(), mOffset.y(), + temp.x(), temp.y(), temp.width(), temp.height()); + return temp; } diff --git a/WebKit/android/nav/CachedLayer.h b/WebKit/android/nav/CachedLayer.h index c802407..0a56ea1 100644 --- a/WebKit/android/nav/CachedLayer.h +++ b/WebKit/android/nav/CachedLayer.h @@ -47,20 +47,29 @@ public: } // FIXME: adjustBounds should be renamed globalBounds or toGlobal IntRect adjustBounds(const LayerAndroid* root, const IntRect& bounds) const; + // Moves the bounds by the layer's scroll position. Assumes the incoming + // bounds have been adjusted by adjustBounds. + IntRect unadjustBounds(const LayerAndroid* root, + const IntRect& bounds) const; int cachedNodeIndex() const { return mCachedNodeIndex; } - const IntPoint& getOffset() const { return mOffset; } const LayerAndroid* layer(const LayerAndroid* root) const; - IntRect localBounds(const IntRect& bounds) const; + IntRect localBounds(const LayerAndroid* root, const IntRect& bounds) const; SkPicture* picture(const LayerAndroid* root) const; void reset() { mLayer = 0; } void setCachedNodeIndex(int index) { mCachedNodeIndex = index; } void setOffset(const IntPoint& offset) { mOffset = offset; } + void setScrollOffset(const IntPoint& scrollOffset) { + mScrollOffset = scrollOffset; + } void setUniqueId(int uniqueId) { mUniqueId = uniqueId; } int uniqueId() const { return mUniqueId; } private: int mCachedNodeIndex; mutable const LayerAndroid* mLayer; + // mOffset and mScrollOffset are the position and scroll offset of the + // layer when recorded by the nav cache. IntPoint mOffset; + IntPoint mScrollOffset; int mUniqueId; #if DUMP_NAV_CACHE diff --git a/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp index 0c9d541..4ba7b48 100644 --- a/WebKit/android/nav/CachedNode.cpp +++ b/WebKit/android/nav/CachedNode.cpp @@ -110,7 +110,7 @@ void CachedNode::fixUpCursorRects(const CachedFrame* frame) mFixedUpCursorRects = true; // if the hit-test rect doesn't intersect any other rect, use it if (mHitBounds != mBounds && mHitBounds.contains(mBounds) && - frame->checkRings(this, mCursorRing, mHitBounds)) { + frame->checkRings(this, mCursorRing, mBounds, mHitBounds)) { DBG_NAV_LOGD("use mHitBounds (%d,%d,%d,%d)", mHitBounds.x(), mHitBounds.y(), mHitBounds.width(), mHitBounds.height()); mUseHitBounds = true; @@ -120,7 +120,9 @@ void CachedNode::fixUpCursorRects(const CachedFrame* frame) return; // if there is more than 1 rect, and the bounds doesn't intersect // any other cursor ring bounds, use it - if (frame->checkRings(this, mCursorRing, mBounds)) { + IntRect sloppyBounds = mBounds; + sloppyBounds.inflate(2); // give it a couple of extra pixels + if (frame->checkRings(this, mCursorRing, mBounds, sloppyBounds)) { DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(), mBounds.y(), mBounds.width(), mBounds.height()); mUseBounds = true; @@ -237,7 +239,7 @@ WebCore::IntRect CachedNode::hitBounds(const CachedFrame* frame) const void CachedNode::init(WebCore::Node* node) { bzero(this, sizeof(CachedNode)); - mExport = WebCore::String(); + mExport = WTF::String(); mNode = node; mParentIndex = mDataIndex = -1; mType = android::NORMAL_CACHEDNODETYPE; @@ -367,6 +369,8 @@ const char* CachedNode::Debug::type(android::CachedNodeType t) const case FRAME_CACHEDNODETYPE: return "FRAME"; break; case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break; case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break; + case SELECT_CACHEDNODETYPE: return "SELECT"; break; + case CONTENT_EDITABLE_CACHEDNODETYPE: return "CONTENT_EDITABLE"; break; default: return "???"; } } @@ -402,6 +406,7 @@ void CachedNode::Debug::print() const DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects); DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex); DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex); + DUMP_NAV_LOGD("// int mColorIndex=%d;\n", b->mColorIndex); DUMP_NAV_LOGD("// Condition mCondition=%s;\n", condition(b->mCondition)); DUMP_NAV_LOGD("// Type mType=%s;\n", type(b->mType)); DEBUG_PRINT_BOOL(mClippedOut); @@ -419,7 +424,6 @@ void CachedNode::Debug::print() const DEBUG_PRINT_BOOL(mLast); DEBUG_PRINT_BOOL(mUseBounds); DEBUG_PRINT_BOOL(mUseHitBounds); - DUMP_NAV_LOGD("\n"); } #endif diff --git a/WebKit/android/nav/CachedNode.h b/WebKit/android/nav/CachedNode.h index f3cfd98..db48a66 100644 --- a/WebKit/android/nav/CachedNode.h +++ b/WebKit/android/nav/CachedNode.h @@ -26,12 +26,13 @@ #ifndef CachedNode_H #define CachedNode_H -#include "AtomicString.h" #include "CachedDebug.h" #include "CachedNodeType.h" #include "IntRect.h" #include "PlatformString.h" -#include "wtf/Vector.h" + +#include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> class SkPicture; @@ -95,12 +96,13 @@ public: WTF::Vector<WebCore::IntRect>* rings); bool clip(const WebCore::IntRect& ); bool clippedOut() { return mClippedOut; } + int colorIndex() const { return mColorIndex; } WebCore::IntRect cursorRingBounds(const CachedFrame* ) const; void cursorRings(const CachedFrame* , WTF::Vector<WebCore::IntRect>* ) const; bool disabled() const { return mDisabled; } const CachedNode* document() const { return &this[-mIndex]; } void fixUpCursorRects(const CachedFrame* frame); - const WebCore::String& getExport() const { return mExport; } + const WTF::String& getExport() const { return mExport; } bool hasCursorRing() const { return mHasCursorRing; } bool hasMouseOver() const { return mHasMouseOver; } void hideCursor(CachedFrame* ); @@ -108,6 +110,7 @@ public: int index() const { return mIndex; } void init(WebCore::Node* node); bool isAnchor() const { return mType == ANCHOR_CACHEDNODETYPE; } + bool isContentEditable() const { return mType == CONTENT_EDITABLE_CACHEDNODETYPE; } bool isCursor() const { return mIsCursor; } bool isArea() const { return mType == AREA_CACHEDNODETYPE; } bool isFocus() const { return mIsFocus; } @@ -118,6 +121,7 @@ public: return clip.intersects(bounds(frame)); } bool isPlugin() const { return mType == PLUGIN_CACHEDNODETYPE; } + bool isSelect() const { return mType == SELECT_CACHEDNODETYPE; } bool isSyntheticLink() const { return mType >= ADDRESS_CACHEDNODETYPE && mType <= PHONE_CACHEDNODETYPE; } @@ -145,10 +149,11 @@ public: WebCore::IntRect ring(const CachedFrame* , size_t part) const; void setBounds(const WebCore::IntRect& bounds) { mBounds = bounds; } void setClippedOut(bool clipped) { mClippedOut = clipped; } + void setColorIndex(int index) { mColorIndex = index; } void setCondition(Condition condition) const { mCondition = condition; } void setDataIndex(int index) { mDataIndex = index; } void setDisabled(bool disabled) { mDisabled = disabled; } - void setExport(const WebCore::String& exported) { mExport = exported; } + void setExport(const WTF::String& exported) { mExport = exported; } void setHasCursorRing(bool hasRing) { mHasCursorRing = hasRing; } void setHasMouseOver(bool hasMouseOver) { mHasMouseOver = hasMouseOver; } void setHitBounds(const WebCore::IntRect& bounds) { mHitBounds = bounds; } @@ -173,10 +178,10 @@ public: const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; } bool useBounds() const { return mUseBounds; } bool useHitBounds() const { return mUseHitBounds; } - bool wantsKeyEvents() const { return isTextInput() || isPlugin(); } + bool wantsKeyEvents() const { return isTextInput() || isPlugin() || isContentEditable(); } private: friend class CacheBuilder; - WebCore::String mExport; + WTF::String mExport; WebCore::IntRect mBounds; WebCore::IntRect mHitBounds; WebCore::IntRect mOriginalAbsoluteBounds; @@ -188,6 +193,7 @@ private: int mNavableRects; // FIXME: could be bitfield once I limit max number of rects int mParentIndex; int mTabIndex; + int mColorIndex; // index to ring color and other stylable properties mutable Condition mCondition : 5; // why the node was not chosen on the first pass CachedNodeType mType : 4; bool mClippedOut : 1; diff --git a/WebKit/android/nav/CachedNodeType.h b/WebKit/android/nav/CachedNodeType.h index 21e2d40..8bc9328 100644 --- a/WebKit/android/nav/CachedNodeType.h +++ b/WebKit/android/nav/CachedNodeType.h @@ -37,7 +37,9 @@ enum CachedNodeType { AREA_CACHEDNODETYPE, FRAME_CACHEDNODETYPE, PLUGIN_CACHEDNODETYPE, - TEXT_INPUT_CACHEDNODETYPE + TEXT_INPUT_CACHEDNODETYPE, + SELECT_CACHEDNODETYPE, + CONTENT_EDITABLE_CACHEDNODETYPE }; enum CachedNodeBits { diff --git a/WebKit/android/nav/CachedPrefix.h b/WebKit/android/nav/CachedPrefix.h index b682288..73a5c2c 100644 --- a/WebKit/android/nav/CachedPrefix.h +++ b/WebKit/android/nav/CachedPrefix.h @@ -43,4 +43,11 @@ #define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning +#ifndef BZERO_DEFINED +#define BZERO_DEFINED +// http://www.opengroup.org/onlinepubs/000095399/functions/bzero.html +// For maximum portability, it is recommended to replace the function call to bzero() as follows: +#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) +#endif + #endif diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp index 2d37a2a..e86999c 100644 --- a/WebKit/android/nav/CachedRoot.cpp +++ b/WebKit/android/nav/CachedRoot.cpp @@ -38,6 +38,10 @@ #include "CachedRoot.h" +#if DEBUG_NAV_UI +#include "wtf/text/CString.h" +#endif + using std::min; using std::max; @@ -62,7 +66,10 @@ public: kDrawRect_Type, kDrawSprite_Type, kDrawText_Type, - kDrawTextOnPath_Type + kDrawTextOnPath_Type, + kPopLayer_Type, + kPushLayer_Type, + kPushSave_Type }; static bool isTextType(Type t) { @@ -110,7 +117,10 @@ public: "kDrawRect_Type", "kDrawSprite_Type", "kDrawText_Type", - "kDrawTextOnPath_Type" + "kDrawTextOnPath_Type", + "kPopLayer_Type", + "kPushLayer_Type", + "kPushSave_Type" }; #endif @@ -147,8 +157,9 @@ public: virtual bool onIRect(const SkIRect& rect) { if (joinGlyphs(rect)) return false; - bool interestingType = mType == kDrawBitmap_Type || - mType == kDrawRect_Type || isTextType(mType); + bool interestingType = mType == kDrawBitmap_Type + || mType == kDrawSprite_Type + || mType == kDrawRect_Type || isTextType(mType); if (SkIRect::Intersects(mBounds, rect) == false) { DBG_NAV_LOGD("BoundsCheck (no intersect) rect={%d,%d,%d,%d}" " mType=%s", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, @@ -255,9 +266,10 @@ public: } virtual void drawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint = NULL) { + const SkPaint* paint) { mBounder.setType(CommonCheck::kDrawSprite_Type); - mBounder.setIsOpaque(bitmap.isOpaque()); + mBounder.setIsOpaque(bitmap.isOpaque() && + (!paint || paint->getAlpha() == 255)); SkCanvas::drawSprite(bitmap, left, top, paint); } @@ -283,8 +295,10 @@ public: mBounder.setEmpty(); mBounder.setType(CommonCheck::kDrawGlyph_Type); SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint); - if (mBounder.mUnion.isEmpty()) + if (mBounder.mUnion.isEmpty()) { + DBG_NAV_LOGD("empty constY=%g", SkScalarToFloat(constY)); return; + } SkPaint::FontMetrics metrics; paint.getFontMetrics(&metrics); SkPoint upDown[2] = { {xpos[0], constY + metrics.fAscent}, @@ -314,7 +328,7 @@ public: virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) { - int depth = SkCanvas::saveLayer(bounds, paint, flags); + int depth = SkCanvas::save(flags); if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) { mTransparentLayer = depth; mBounder.setAllOpaque(false); @@ -323,6 +337,7 @@ public: } virtual void restore() { + mBounder.setType(CommonCheck::kDrawSprite_Type); // for layer draws int depth = getSaveCount(); if (depth == mTransparentLayer) { mTransparentLayer = 0; @@ -653,32 +668,404 @@ public: class RingCheck : public CommonCheck { public: RingCheck(const WTF::Vector<WebCore::IntRect>& rings, - const WebCore::IntPoint& location) : mSuccess(true) { + const WebCore::IntRect& bitBounds, const WebCore::IntRect& testBounds) + : mTestBounds(testBounds) + , mBitBounds(bitBounds) + { const WebCore::IntRect* r; for (r = rings.begin(); r != rings.end(); r++) { SkIRect fatter = {r->x(), r->y(), r->right(), r->bottom()}; fatter.inset(-CURSOR_RING_HIT_TEST_RADIUS, -CURSOR_RING_HIT_TEST_RADIUS); - DBG_NAV_LOGD("fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop, + DBG_NAV_LOGD("RingCheck fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop, fatter.fRight, fatter.fBottom); - mRings.op(fatter, SkRegion::kUnion_Op); + mTextSlop.op(fatter, SkRegion::kUnion_Op); + mTextTest.op(*r, SkRegion::kUnion_Op); } - DBG_NAV_LOGD("translate=(%d,%d)", -location.x(), -location.y()); - mRings.translate(-location.x(), -location.y()); + int dx = -bitBounds.x(); + int dy = -bitBounds.y(); + DBG_NAV_LOGD("RingCheck translate=(%d,%d)", dx, dy); + mTextSlop.translate(dx, dy); + mTextTest.translate(dx, dy); + mTestBounds.translate(dx, dy); + mEmpty.setEmpty(); } - virtual bool onIRect(const SkIRect& rect) { - if (mSuccess && mType == kDrawGlyph_Type) { - DBG_NAV_LOGD("contains (%d,%d,r=%d,b=%d) == %s", - rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - mRings.contains(rect) ? "true" : "false"); - mSuccess &= mRings.contains(rect); + bool hiddenRings(SkRegion* clipped) + { + findBestLayer(); + if (!mBestLayer) { + DBG_NAV_LOG("RingCheck empty"); + clipped->setEmpty(); + return true; } + const SkRegion* layersEnd = mLayers.end(); + const Type* layerTypes = &mLayerTypes[mBestLayer - mLayers.begin()]; + bool collectGlyphs = true; + bool collectOvers = false; + SkRegion over; + for (const SkRegion* layers = mBestLayer; layers != layersEnd; layers++) { + Type layerType = *layerTypes++; + DBG_NAV_LOGD("RingCheck #%d %s (%d,%d,r=%d,b=%d)", + layers - mLayers.begin(), TypeNames[layerType], + layers->getBounds().fLeft, layers->getBounds().fTop, + layers->getBounds().fRight, layers->getBounds().fBottom); + if (collectGlyphs && layerType == kDrawGlyph_Type) { + DBG_NAV_LOGD("RingCheck #%d collectOvers", layers - mLayers.begin()); + collectOvers = true; + clipped->op(*layers, SkRegion::kUnion_Op); + } + collectGlyphs &= layerType != kPushLayer_Type; + if (collectOvers && (layerType == kDrawRect_Type + || (!collectGlyphs && layerType == kDrawSprite_Type))) + { + DBG_NAV_LOGD("RingCheck #%d over.op", layers - mLayers.begin()); + over.op(*layers, SkRegion::kUnion_Op); + } + } + bool result = !collectOvers || clipped->intersects(over); + const SkIRect t = clipped->getBounds(); + const SkIRect o = over.getBounds(); + clipped->op(over, SkRegion::kDifference_Op); + clipped->translate(mBitBounds.x(), mBitBounds.y()); + const SkIRect c = clipped->getBounds(); + DBG_NAV_LOGD("RingCheck intersects=%s text=(%d,%d,r=%d,b=%d)" + " over=(%d,%d,r=%d,b=%d) clipped=(%d,%d,r=%d,b=%d)", + result ? "true" : "false", + t.fLeft, t.fTop, t.fRight, t.fBottom, + o.fLeft, o.fTop, o.fRight, o.fBottom, + c.fLeft, c.fTop, c.fRight, c.fBottom); + return result; + } + + void push(Type type, const SkIRect& bounds) + { +#if DEBUG_NAV_UI + // this caches the push string and subquently ignores if pushSave + // is immediately followed by popLayer. Push/pop pairs happen + // frequently and just add noise to the log. + static String lastLog; + String currentLog = String("RingCheck append #") + + String::number(mLayers.size()) + + " type=" + TypeNames[type] + " bounds=(" + + String::number(bounds.fLeft) + + "," + String::number(bounds.fTop) + "," + + String::number(bounds.fRight) + "," + + String::number(bounds.fBottom) + ")"; + if (lastLog.length() == 0 || type != kPopLayer_Type) { + if (lastLog.length() != 0) + DBG_NAV_LOGD("%s", lastLog.latin1().data()); + if (type == kPushSave_Type) + lastLog = currentLog; + else + DBG_NAV_LOGD("%s", currentLog.latin1().data()); + } else + lastLog = ""; +#endif + popEmpty(); + if (type == kPopLayer_Type) { + Type last = mLayerTypes.last(); + // remove empty brackets + if (last == kPushLayer_Type || last == kPushSave_Type) { + mLayers.removeLast(); + mLayerTypes.removeLast(); + return; + } + // remove non-layer brackets + int stack = 0; + Type* types = mLayerTypes.end(); + while (types != mLayerTypes.begin()) { + Type type = *--types; + if (type == kPopLayer_Type) { + stack++; + continue; + } + if (type != kPushLayer_Type && type != kPushSave_Type) + continue; + if (--stack >= 0) + continue; + if (type == kPushLayer_Type) + break; + int remove = types - mLayerTypes.begin(); + DBG_NAV_LOGD("RingCheck remove=%d mLayers.size=%d" + " mLayerTypes.size=%d", remove, mLayers.size(), + mLayerTypes.size()); + mLayers.remove(remove); + mLayerTypes.remove(remove); + return; + } + } + mLayers.append(bounds); + mLayerTypes.append(type); + } + + void startText(const SkPaint& paint) + { + mPaint = &paint; + if (!mLayerTypes.isEmpty() && mLayerTypes.last() == kDrawGlyph_Type + && !mLayers.last().isEmpty()) { + push(kDrawGlyph_Type, mEmpty); + } + } + + bool textOutsideRings() + { + findBestLayer(); + if (!mBestLayer) { + DBG_NAV_LOG("RingCheck empty"); + return false; + } + const SkRegion* layers = mBestLayer; + const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()]; + // back up to include text drawn before the best layer + SkRegion active = SkRegion(mBitBounds); + active.translate(-mBitBounds.x(), -mBitBounds.y()); + while (layers != mLayers.begin()) { + --layers; + Type layerType = *--layerTypes; + DBG_NAV_LOGD("RingCheck #%d %s" + " mTestBounds=(%d,%d,r=%d,b=%d) layers=(%d,%d,r=%d,b=%d)" + " active=(%d,%d,r=%d,b=%d)", + layers - mLayers.begin(), TypeNames[layerType], + mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop, + mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom, + layers->getBounds().fLeft, layers->getBounds().fTop, + layers->getBounds().fRight, layers->getBounds().fBottom, + active.getBounds().fLeft, active.getBounds().fTop, + active.getBounds().fRight, active.getBounds().fBottom); + if (layerType == kDrawRect_Type) { + SkRegion temp = *layers; + temp.op(mTestBounds, SkRegion::kIntersect_Op); + active.op(temp, SkRegion::kDifference_Op); + if (active.isEmpty()) { + DBG_NAV_LOGD("RingCheck #%d empty", layers - mLayers.begin()); + break; + } + } else if (layerType == kDrawGlyph_Type) { + SkRegion temp = *layers; + temp.op(active, SkRegion::kIntersect_Op); + if (!mTestBounds.intersects(temp)) + continue; + if (!mTestBounds.contains(temp)) + return false; + } else + break; + } + layers = mBestLayer; + layerTypes = &mLayerTypes[layers - mLayers.begin()]; + bool foundGlyph = false; + bool collectGlyphs = true; + do { + Type layerType = *layerTypes++; + DBG_NAV_LOGD("RingCheck #%d %s mTestBounds=(%d,%d,r=%d,b=%d)" + " layers=(%d,%d,r=%d,b=%d) collects=%s intersects=%s contains=%s", + layers - mLayers.begin(), TypeNames[layerType], + mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop, + mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom, + layers->getBounds().fLeft, layers->getBounds().fTop, + layers->getBounds().fRight, layers->getBounds().fBottom, + collectGlyphs ? "true" : "false", + mTestBounds.intersects(*layers) ? "true" : "false", + mTestBounds.contains(*layers) ? "true" : "false"); + if (collectGlyphs && layerType == kDrawGlyph_Type) { + if (!mTestBounds.intersects(*layers)) + continue; + if (!mTestBounds.contains(*layers)) + return false; + foundGlyph = true; + } + collectGlyphs &= layerType != kPushLayer_Type; + } while (++layers != mLayers.end()); + DBG_NAV_LOGD("RingCheck foundGlyph=%s", foundGlyph ? "true" : "false"); + return foundGlyph; + } + +protected: + virtual bool onIRect(const SkIRect& rect) + { + joinGlyphs(rect); + if (mType != kDrawGlyph_Type && mType != kDrawRect_Type + && mType != kDrawSprite_Type) + return false; + if (mLayerTypes.isEmpty() || mLayerTypes.last() != mType) + push(mType, mEmpty); + DBG_NAV_LOGD("RingCheck join %s (%d,%d,r=%d,b=%d) '%c'", + TypeNames[mType], rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, + mCh); + mLayers.last().op(rect, SkRegion::kUnion_Op); return false; } - bool success() { return mSuccess; } - SkRegion mRings; - bool mSuccess; + virtual bool onIRectGlyph(const SkIRect& rect, + const SkBounder::GlyphRec& rec) + { + mCh = ' '; + if (mPaint) { + SkUnichar unichar; + SkPaint utfPaint = *mPaint; + utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar); + mCh = unichar < 0x7f ? unichar : '?'; + } + return onIRect(rect); + } + +private: + int calcOverlap(SkRegion& testRegion) + { + if (testRegion.isEmpty()) + return INT_MAX; + testRegion.op(mTextTest, SkRegion::kXOR_Op); + SkRegion::Iterator iter(testRegion); + int area = 0; + while (!iter.done()) { + const SkIRect& cr = iter.rect(); + area += cr.width() * cr.height(); + iter.next(); + } + DBG_NAV_LOGD("RingCheck area=%d", area); + return area; + } + + void findBestLayer() + { + popEmpty(); + mBestLayer = 0; + const SkRegion* layers = mLayers.begin(); + const SkRegion* layersEnd = mLayers.end(); + if (layers == layersEnd) { + DBG_NAV_LOG("RingCheck empty"); + return; + } + // find text most like focus rings by xoring found with original + int bestArea = INT_MAX; + const SkRegion* testLayer = 0; + SkRegion testRegion; + const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()]; + for (; layers != mLayers.end(); layers++) { + Type layerType = *layerTypes++; +#if DEBUG_NAV_UI + const SkIRect& gb = layers->getBounds(); + const SkIRect& tb = mTextSlop.getBounds(); + DBG_NAV_LOGD("RingCheck #%d %s mTextSlop=(%d,%d,%d,%d)" + " contains=%s bounds=(%d,%d,%d,%d)", + layers - mLayers.begin(), TypeNames[layerType], + tb.fLeft, tb.fTop, tb.fRight, tb.fBottom, + mTextSlop.contains(*layers) ? "true" : "false", + gb.fLeft, gb.fTop, gb.fRight, gb.fBottom); +#endif + if (layerType == kDrawGlyph_Type) { + if (!testLayer) + testLayer = layers; + if (mTextSlop.contains(*layers)) { + testRegion.op(*layers, SkRegion::kUnion_Op); + continue; + } + } + if (testLayer) { + int area = calcOverlap(testRegion); + if (bestArea > area) { + bestArea = area; + mBestLayer = testLayer; + } + DBG_NAV_LOGD("RingCheck #%d push test=%d best=%d", + layers - mLayers.begin(), testLayer - mLayers.begin(), + mBestLayer ? mBestLayer - mLayers.begin() : -1); + testRegion.setEmpty(); + testLayer = 0; + } + } + if (testLayer && bestArea > calcOverlap(testRegion)) { + DBG_NAV_LOGD("RingCheck last best=%d", testLayer - mLayers.begin()); + mBestLayer = testLayer; + } + } + + void popEmpty() + { + if (mLayerTypes.size() == 0) + return; + Type last = mLayerTypes.last(); + if (last >= kPopLayer_Type) + return; + const SkRegion& area = mLayers.last(); + if (!area.isEmpty()) + return; + DBG_NAV_LOGD("RingCheck #%d %s", mLayers.size() - 1, TypeNames[last]); + mLayers.removeLast(); + mLayerTypes.removeLast(); + } + + SkRegion mTestBounds; + IntRect mBitBounds; + SkIRect mEmpty; + const SkRegion* mBestLayer; + SkRegion mTextSlop; // outset rects for inclusion test + SkRegion mTextTest; // exact rects for xor area test + Type mLastType; + Vector<SkRegion> mLayers; + Vector<Type> mLayerTypes; + const SkPaint* mPaint; + char mCh; +}; + +class RingCanvas : public BoundsCanvas { +public: + RingCanvas(RingCheck* bounder) + : INHERITED(bounder) + { + } + +protected: + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) { + static_cast<RingCheck&>(mBounder).startText(paint); + INHERITED::drawText(text, byteLength, x, y, paint); + } + + virtual void drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + static_cast<RingCheck&>(mBounder).startText(paint); + INHERITED::drawPosText(text, byteLength, pos, paint); + } + + virtual void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + static_cast<RingCheck&>(mBounder).startText(paint); + INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint); + } + + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + static_cast<RingCheck&>(mBounder).startText(paint); + INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint); + } + + virtual int save(SaveFlags flags) + { + RingCheck& bounder = static_cast<RingCheck&>(mBounder); + bounder.push(CommonCheck::kPushSave_Type, getTotalClip().getBounds()); + return INHERITED::save(flags); + } + + virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags) + { + RingCheck& bounder = static_cast<RingCheck&>(mBounder); + bounder.push(CommonCheck::kPushLayer_Type, getTotalClip().getBounds()); + return INHERITED::save(flags); + } + + virtual void restore() + { + RingCheck& bounder = static_cast<RingCheck&>(mBounder); + bounder.push(CommonCheck::kPopLayer_Type, getTotalClip().getBounds()); + INHERITED::restore(); + } + +private: + typedef BoundsCanvas INHERITED; }; bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction, @@ -709,6 +1096,29 @@ bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction directio return false; } +void CachedRoot::calcBitBounds(const IntRect& nodeBounds, IntRect* bitBounds) const +{ + IntRect contentBounds = IntRect(0, 0, mPicture->width(), mPicture->height()); + IntRect overBounds = nodeBounds; + overBounds.inflate(kMargin); + *bitBounds = mScrolledBounds; + bitBounds->unite(mViewBounds); + bitBounds->intersect(contentBounds); + bitBounds->intersect(overBounds); + DBG_NAV_LOGD("contentBounds=(%d,%d,r=%d,b=%d) overBounds=(%d,%d,r=%d,b=%d)" + " mScrolledBounds=(%d,%d,r=%d,b=%d) mViewBounds=(%d,%d,r=%d,b=%d)" + " bitBounds=(%d,%d,r=%d,b=%d)", + contentBounds.x(), contentBounds.y(), contentBounds.right(), + contentBounds.bottom(), + overBounds.x(), overBounds.y(), overBounds.right(), overBounds.bottom(), + mScrolledBounds.x(), mScrolledBounds.y(), mScrolledBounds.right(), + mScrolledBounds.bottom(), + mViewBounds.x(), mViewBounds.y(), mViewBounds.right(), + mViewBounds.bottom(), + bitBounds->x(), bitBounds->y(), bitBounds->right(), + bitBounds->bottom()); +} + int CachedRoot::checkForCenter(int x, int y) const { @@ -745,22 +1155,30 @@ void CachedRoot::checkForJiggle(int* xDeltaPtr) const bool CachedRoot::checkRings(SkPicture* picture, const WTF::Vector<WebCore::IntRect>& rings, - const WebCore::IntRect& bounds) const + const WebCore::IntRect& nodeBounds, + const WebCore::IntRect& testBounds) const { if (!picture) return false; - RingCheck ringCheck(rings, bounds.location()); - BoundsCanvas checker(&ringCheck); + IntRect bitBounds; + calcBitBounds(nodeBounds, &bitBounds); + RingCheck ringCheck(rings, bitBounds, testBounds); + RingCanvas checker(&ringCheck); SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), - bounds.height()); + bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(), + bitBounds.height()); checker.setBitmapDevice(bitmap); - checker.translate(SkIntToScalar(-bounds.x()), SkIntToScalar(-bounds.y())); + checker.translate(SkIntToScalar(-bitBounds.x()), + SkIntToScalar(-bitBounds.y())); checker.drawPicture(*picture); - DBG_NAV_LOGD("bounds=(%d,%d,r=%d,b=%d) success=%s", - bounds.x(), bounds.y(), bounds.right(), bounds.bottom(), - ringCheck.success() ? "true" : "false"); - return ringCheck.success(); + bool result = ringCheck.textOutsideRings(); + DBG_NAV_LOGD("bitBounds=(%d,%d,r=%d,b=%d) nodeBounds=(%d,%d,r=%d,b=%d)" + " testBounds=(%d,%d,r=%d,b=%d) success=%s", + bitBounds.x(), bitBounds.y(), bitBounds.right(), bitBounds.bottom(), + nodeBounds.x(), nodeBounds.y(), nodeBounds.right(), nodeBounds.bottom(), + testBounds.x(), testBounds.y(), testBounds.right(), testBounds.bottom(), + result ? "true" : "false"); + return result; } void CachedRoot::draw(FindCanvas& canvas) const @@ -999,7 +1417,9 @@ void CachedRoot::innerMove(const CachedNode* node, BestData* bestData, if (bestData->mNode != NULL) { mHistory->addToVisited(bestData->mNode, direction); mHistory->mNavBounds = bestData->bounds(); - mHistory->mMouseBounds = bestData->mouseBounds(); + mHistory->mMouseBounds = + cursorFrame->unadjustBounds(bestData->mNode, + bestData->mouseBounds()); } else if (scroll->x() != 0 || scroll->y() != 0) { WebCore::IntRect newBounds = mHistory->mNavBounds; int offsetX = scroll->x(); @@ -1068,7 +1488,7 @@ bool CachedRoot::innerUp(const CachedNode* test, BestData* bestData) const return true; } -WebCore::String CachedRoot::imageURI(int x, int y) const +WTF::String CachedRoot::imageURI(int x, int y) const { ImageCheck imageCheck; ImageCanvas checker(&imageCheck); @@ -1077,13 +1497,13 @@ WebCore::String CachedRoot::imageURI(int x, int y) const checker.setBitmapDevice(bitmap); checker.translate(SkIntToScalar(-x), SkIntToScalar(-y)); checker.drawPicture(*pictureAt(x, y)); - return WebCore::String(checker.mURI); + return WTF::String(checker.mURI); } bool CachedRoot::maskIfHidden(BestData* best) const { const CachedNode* bestNode = best->mNode; - if (bestNode->isUnclipped() || bestNode->isTransparent()) + if (bestNode->isUnclipped()) return false; const CachedFrame* frame = best->mFrame; SkPicture* picture = frame->picture(bestNode); @@ -1091,115 +1511,42 @@ bool CachedRoot::maskIfHidden(BestData* best) const DBG_NAV_LOG("missing picture"); return false; } - // given the picture matching this nav cache - // create an SkBitmap with dimensions of the cursor intersected w/ extended view - const WebCore::IntRect& nodeBounds = bestNode->bounds(frame); - WebCore::IntRect bounds = nodeBounds; - bounds.intersect(mScrolledBounds); - int leftMargin = bounds.x() == nodeBounds.x() ? kMargin : 0; - int topMargin = bounds.y() == nodeBounds.y() ? kMargin : 0; - int rightMargin = bounds.right() == nodeBounds.right() ? kMargin : 0; - int bottomMargin = bounds.bottom() == nodeBounds.bottom() ? kMargin : 0; - bool unclipped = (leftMargin & topMargin & rightMargin & bottomMargin) != 0; - WebCore::IntRect marginBounds = nodeBounds; - marginBounds.inflate(kMargin); - marginBounds.intersect(mScrolledBounds); - SkScalar offsetX = SkIntToScalar(leftMargin - bounds.x()); - SkScalar offsetY = SkIntToScalar(topMargin - bounds.y()); -#if USE(ACCELERATED_COMPOSITING) - // When cached nodes are constructed in CacheBuilder.cpp, their - // unclipped attribute is set so this condition won't be reached. - // In the future, layers may contain nodes that can be clipped. - // So to be safe, adjust the layer picture by its offset. - if (bestNode->isInLayer()) { - const CachedLayer* cachedLayer = frame->layer(bestNode); - const LayerAndroid* layer = cachedLayer->layer(mRootLayer); - SkMatrix pictMatrix; - layer->localToGlobal(&pictMatrix); - // FIXME: ignore scale, rotation for now - offsetX += pictMatrix.getTranslateX(); - offsetY += pictMatrix.getTranslateY(); - DBG_NAV_LOGD("layer picture=%p (%g,%g)", picture, - pictMatrix.getTranslateX(), pictMatrix.getTranslateY()); - } -#endif - BoundsCheck boundsCheck; - BoundsCanvas checker(&boundsCheck); - boundsCheck.mBounds.set(leftMargin, topMargin, - leftMargin + bounds.width(), topMargin + bounds.height()); - boundsCheck.mBoundsSlop = boundsCheck.mBounds; - boundsCheck.mBoundsSlop.inset(-kSlop, -kSlop); + Vector<IntRect> rings; + bestNode->cursorRings(frame, &rings); + const WebCore::IntRect& bounds = bestNode->bounds(frame); + IntRect bitBounds; + calcBitBounds(bounds, &bitBounds); + RingCheck ringCheck(rings, bitBounds, bounds); + RingCanvas checker(&ringCheck); SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, marginBounds.width(), - marginBounds.height()); + bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(), + bitBounds.height()); checker.setBitmapDevice(bitmap); - // insert probes to be called when the data corresponding to this ring is drawn - // need to know if ring was generated by text, image, or parent (like div) - // ? need to know (like imdb menu bar) to give up sometimes (when?) - checker.translate(offsetX, offsetY); + checker.translate(SkIntToScalar(-bitBounds.x()), + SkIntToScalar(-bitBounds.y())); checker.drawPicture(*picture); - boundsCheck.checkLast(); - // was it not drawn or clipped out? + SkRegion clipRgn; + bool clipped = ringCheck.hiddenRings(&clipRgn); CachedNode* node = const_cast<CachedNode*>(best->mNode); - if (boundsCheck.hidden()) { // if hidden, return false so that nav can try again -#if DEBUG_NAV_UI - const SkIRect& m = boundsCheck.mBounds; - const SkIRect& s = boundsCheck.mBoundsSlop; - DBG_NAV_LOGD("hidden node:%p (%d) mBounds={%d,%d,%d,%d} mBoundsSlop=" - "{%d,%d,%d,%d}", node, node->index(), - m.fLeft, m.fTop, m.fRight, m.fBottom, - s.fLeft, s.fTop, s.fRight, s.fBottom); - const SkIRect& o = boundsCheck.mDrawnOver.getBounds(); - const SkIRect& l = boundsCheck.mLastAll; - const SkIRect& u = boundsCheck.mUnion; - DBG_NAV_LOGD("hidden mDrawnOver={%d,%d,%d,%d} mLastAll={%d,%d,%d,%d}" - " mUnion={%d,%d,%d,%d}", - o.fLeft, o.fTop, o.fRight, o.fBottom, - l.fLeft, l.fTop, l.fRight, l.fBottom, - u.fLeft, u.fTop, u.fRight, u.fBottom); - const SkIRect& a = boundsCheck.mAllDrawnIn; - const WebCore::IntRect& c = mScrolledBounds; - const WebCore::IntRect& b = nodeBounds; - DBG_NAV_LOGD("hidden mAllDrawnIn={%d,%d,%d,%d}" - " mScrolledBounds={%d,%d,%d,%d} nodeBounds={%d,%d,%d,%d}", - a.fLeft, a.fTop, a.fRight, a.fBottom, - c.x(), c.y(), c.right(), c.bottom(), - b.x(), b.y(), b.right(), b.bottom()); - DBG_NAV_LOGD("bits.mWidth=%d bits.mHeight=%d transX=%d transY=%d", - marginBounds.width(),marginBounds.height(), - kMargin - bounds.x(), kMargin - bounds.y()); -#endif + DBG_NAV_LOGD("clipped=%s clipRgn.isEmpty=%s", clipped ? "true" : "false", + clipRgn.isEmpty() ? "true" : "false"); + if (clipped && clipRgn.isEmpty()) { node->setDisabled(true); - node->setClippedOut(unclipped == false); + IntRect clippedBounds = bounds; + clippedBounds.intersect(bitBounds); + node->setClippedOut(clippedBounds != bounds); return true; } // was it partially occluded by later drawing? // if partially occluded, modify the bounds so that the mouse click has a better x,y - const SkIRect& over = boundsCheck.mDrawnOver.getBounds(); - if (over.isEmpty() == false) { -#if DEBUG_NAV_UI - SkIRect orig = boundsCheck.mBounds; -#endif - SkIRect& base = boundsCheck.mBounds; - if (base.fLeft < over.fRight && base.fRight > over.fRight) - base.fLeft = over.fRight; - else if (base.fRight > over.fLeft && base.fLeft < over.fLeft) - base.fRight = over.fLeft; - if (base.fTop < over.fBottom && base.fBottom > over.fBottom) - base.fTop = over.fBottom; - else if (base.fBottom > over.fTop && base.fTop < over.fTop) - base.fBottom = over.fTop; -#if DEBUG_NAV_UI - const SkIRect& modded = boundsCheck.mBounds; - DBG_NAV_LOGD("partially occluded node:%p (%d) old:{%d,%d,%d,%d}" - " new:{%d,%d,%d,%d}", node, node->index(), - orig.fLeft, orig.fTop, orig.fRight, orig.fBottom, - base.fLeft, base.fTop, base.fRight, base.fBottom); -#endif - best->setMouseBounds(WebCore::IntRect(bounds.x() + base.fLeft - kMargin, - bounds.y() + base.fTop - kMargin, base.width(), base.height())); + if (clipped) { + DBG_NAV_LOGD("clipped clipRgn={%d,%d,r=%d,b=%d}", + clipRgn.getBounds().fLeft, clipRgn.getBounds().fTop, + clipRgn.getBounds().fRight, clipRgn.getBounds().fBottom); + best->setMouseBounds(clipRgn.getBounds()); node->clip(best->mouseBounds()); - } + } else + node->fixUpCursorRects(frame); return false; } diff --git a/WebKit/android/nav/CachedRoot.h b/WebKit/android/nav/CachedRoot.h index 6e9fff0..8c263f8 100644 --- a/WebKit/android/nav/CachedRoot.h +++ b/WebKit/android/nav/CachedRoot.h @@ -49,10 +49,12 @@ public: bool adjustForScroll(BestData* , Direction , WebCore::IntPoint* scrollPtr, bool findClosest); const SkRegion& baseUncovered() const { return mBaseUncovered; } + void calcBitBounds(const IntRect& , IntRect* ) const; int checkForCenter(int x, int y) const; void checkForJiggle(int* ) const; bool checkRings(SkPicture* , const WTF::Vector<WebCore::IntRect>& rings, - const WebCore::IntRect& bounds) const; + const WebCore::IntRect& nodeBounds, + const WebCore::IntRect& testBounds) const; WebCore::IntPoint cursorLocation() const; int documentHeight() { return mContents.height(); } int documentWidth() { return mContents.width(); } @@ -72,7 +74,7 @@ public: WebCore::IntPoint* scroll, bool firstCall); bool innerRight(const CachedNode* , BestData* ) const; bool innerUp(const CachedNode* , BestData* ) const; - WebCore::String imageURI(int x, int y) const; + WTF::String imageURI(int x, int y) const; bool maskIfHidden(BestData* ) const; const CachedNode* moveCursor(Direction , const CachedFrame** , WebCore::IntPoint* scroll); /** @@ -96,7 +98,10 @@ public: void setTextGeneration(int textGeneration) { mTextGeneration = textGeneration; } void setMaxScroll(int x, int y) { mMaxXScroll = x; mMaxYScroll = y; } void setPicture(SkPicture* picture) { mPicture = picture; } - void setRootLayer(WebCore::LayerAndroid* layer) { mRootLayer = layer; } + void setRootLayer(WebCore::LayerAndroid* layer) { + mRootLayer = layer; + resetLayers(); + } void setScrollOnly(bool state) { mScrollOnly = state; } void setSelection(int start, int end) { mSelectionStart = start; mSelectionEnd = end; } void setupScrolledBounds() const { mScrolledBounds = mViewBounds; } diff --git a/WebKit/android/nav/FindCanvas.cpp b/WebKit/android/nav/FindCanvas.cpp index d8e908b..d60fffd 100644 --- a/WebKit/android/nav/FindCanvas.cpp +++ b/WebKit/android/nav/FindCanvas.cpp @@ -98,7 +98,7 @@ GlyphSet::~GlyphSet() { // part of mLowerGlyphs } -GlyphSet::GlyphSet& GlyphSet::operator=(GlyphSet& src) { +GlyphSet& GlyphSet::operator=(GlyphSet& src) { mTypeface = src.mTypeface; mCount = src.mCount; if (mCount > MAX_STORAGE_COUNT) { @@ -361,48 +361,40 @@ void FindCanvas::findHelper(const void* text, size_t byteLength, // We need an SkIRect for SkRegion operations. SkIRect iRect; rect.roundOut(&iRect); - // If the rectangle is partially clipped, assume that the text is - // not visible, so skip this match. - if (getTotalClip().contains(iRect)) { - // Want to outset the drawn rectangle by the same amount as - // mOutset - iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET); - SkRegion regionToAdd(iRect); - if (!mWorkingRegion.isEmpty()) { - // If this is on the same line as our working region, make - // sure that they are close enough together that they are - // supposed to be part of the same text string. - // The width of two spaces has arbitrarily been chosen. - const SkIRect& workingBounds = mWorkingRegion.getBounds(); - if (workingBounds.fTop <= iRect.fBottom && - workingBounds.fBottom >= iRect.fTop && - SkIntToScalar(iRect.fLeft - workingBounds.fRight) > - approximateSpaceWidth(paint)) { - index = -1; // Will increase to 0 on next run - // In this case, we need to start from the beginning of - // the text being searched and our search term. - j = 0; - mWorkingIndex = 0; - mWorkingRegion.setEmpty(); - continue; - } - // Add the mWorkingRegion, which contains rectangles from - // the previous line(s). - regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op); + // Want to outset the drawn rectangle by the same amount as + // mOutset + iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET); + SkRegion regionToAdd(iRect); + if (!mWorkingRegion.isEmpty()) { + // If this is on the same line as our working region, make + // sure that they are close enough together that they are + // supposed to be part of the same text string. + // The width of two spaces has arbitrarily been chosen. + const SkIRect& workingBounds = mWorkingRegion.getBounds(); + if (workingBounds.fTop <= iRect.fBottom && + workingBounds.fBottom >= iRect.fTop && + SkIntToScalar(iRect.fLeft - workingBounds.fRight) > + approximateSpaceWidth(paint)) { + index = -1; // Will increase to 0 on next run + // In this case, we need to start from the beginning of + // the text being searched and our search term. + j = 0; + mWorkingIndex = 0; + mWorkingRegion.setEmpty(); + continue; } - insertMatchInfo(regionToAdd); + // Add the mWorkingRegion, which contains rectangles from + // the previous line(s). + regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op); + } + insertMatchInfo(regionToAdd); #if INCLUDE_SUBSTRING_MATCHES - // Reset index to the location of the match and reset j to the - // beginning, so that on the next iteration of the loop, index - // will advance by 1 and we will compare the next character in - // chars to the first character in the GlyphSet. - index = matchIndex; + // Reset index to the location of the match and reset j to the + // beginning, so that on the next iteration of the loop, index + // will advance by 1 and we will compare the next character in + // chars to the first character in the GlyphSet. + index = matchIndex; #endif - } else { - // This match was clipped out, so begin looking at the next - // character from our hidden match - index = matchIndex; - } // Whether the clip contained it or not, we need to start over // with our recording canvas resetWorkingCanvas(); @@ -443,12 +435,9 @@ void FindCanvas::findHelper(const void* text, size_t byteLength, partial.inset(mOutset, mOutset); SkIRect dest; partial.roundOut(&dest); - // Only save a partial if it is in the current clip. - if (getTotalClip().contains(dest)) { - mWorkingRegion.op(dest, SkRegion::kUnion_Op); - mWorkingIndex = j; - return; - } + mWorkingRegion.op(dest, SkRegion::kUnion_Op); + mWorkingIndex = j; + return; } // This string doesn't go into the next drawText, so reset our working // variables @@ -675,4 +664,3 @@ void FindOnPage::setMatches(WTF::Vector<MatchInfo>* matches) } } - diff --git a/WebKit/android/nav/FindCanvas.h b/WebKit/android/nav/FindCanvas.h index 34929ec..903279c 100644 --- a/WebKit/android/nav/FindCanvas.h +++ b/WebKit/android/nav/FindCanvas.h @@ -224,6 +224,7 @@ public: bool currentMatchIsInLayer() const; virtual void draw(SkCanvas* , LayerAndroid* ); void findNext(bool forward); + bool isCurrentLocationValid() { return m_hasCurrentLocation; } void setMatches(WTF::Vector<MatchInfo>* matches); private: void drawMatch(const SkRegion& region, SkCanvas* canvas, bool focused); diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp index 25f9482..f691d47 100644 --- a/WebKit/android/nav/SelectText.cpp +++ b/WebKit/android/nav/SelectText.cpp @@ -44,7 +44,7 @@ #include "TextRun.h" #ifdef DEBUG_NAV_UI -#include "CString.h" +#include <wtf/text/CString.h> #endif #define VERBOSE_LOGGING 0 @@ -961,7 +961,7 @@ public: return false; } - WebCore::String text() { + WTF::String text() { if (mFlipped) finish(); // the text has been copied in visual order. Reverse as needed if @@ -977,7 +977,7 @@ public: break; } } - return WebCore::String(mSelectText.begin(), mSelectText.count()); + return WTF::String(mSelectText.begin(), mSelectText.count()); } protected: @@ -1166,7 +1166,7 @@ static SkIRect findRight(const SkPicture& picture, const SkIRect& area, return findEdge(picture, area, x, y, false, base); } -static WebCore::String text(const SkPicture& picture, const SkIRect& area, +static WTF::String text(const SkPicture& picture, const SkIRect& area, const SkIRect& start, int startBase, const SkIRect& end, int endBase, bool flipped) { @@ -1275,12 +1275,6 @@ SelectText::SelectText() void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer) { - // Gmail makes layers appear dynamically the page scrolls. The picture - // recorded when the selection begins is confused by the pictures seen - // in subsequent layers. To work around this, only allow text selection - // in the main picture. - if (layer->uniqueId() != -1) - return; // FIXME: layer may not own the original selected picture m_picture = layer->picture(); if (!m_picture) @@ -1463,10 +1457,7 @@ bool SelectText::hitSelection(int x, int y) const int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2; if (hitCorner(right, bottom, x, y)) return true; - SkIRect test; - test.set(x - CONTROL_WIDTH, y - CONTROL_HEIGHT, x + CONTROL_WIDTH, - y + CONTROL_HEIGHT); - return m_selRegion.intersects(test); + return m_selRegion.contains(x, y); } void SelectText::moveSelection(const SkPicture* picture, int x, int y) diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index 913c4ba..a9e5e10 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -29,11 +29,10 @@ #include "AndroidAnimation.h" #include "AndroidLog.h" -#include "AtomicString.h" +#include "BaseLayerAndroid.h" #include "CachedFrame.h" #include "CachedNode.h" #include "CachedRoot.h" -#include "CString.h" #include "DrawExtra.h" #include "FindCanvas.h" #include "Frame.h" @@ -54,7 +53,9 @@ #ifdef ANDROID_INSTRUMENT #include "TimeCounter.h" #endif +#include "TilesManager.h" #include "WebCoreJni.h" +#include "WebRequestContext.h" #include "WebViewCore.h" #include "android_graphics.h" @@ -68,6 +69,8 @@ #include <JNIHelp.h> #include <jni.h> #include <ui/KeycodeLabels.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/CString.h> namespace android { @@ -104,7 +107,6 @@ enum DrawExtras { // keep this in sync with WebView.java struct JavaGlue { jweak m_obj; jmethodID m_calcOurContentVisibleRectF; - jmethodID m_clearTextEntry; jmethodID m_overrideLoading; jmethodID m_scrollBy; jmethodID m_sendMoveFocus; @@ -140,7 +142,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView); m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z"); m_javaGlue.m_calcOurContentVisibleRectF = GetJMethod(env, clazz, "calcOurContentVisibleRectF", "(Landroid/graphics/RectF;)V"); - m_javaGlue.m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "(Z)V"); m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V"); m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V"); m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V"); @@ -173,11 +174,10 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : m_navPictureUI = 0; m_generation = 0; m_heightCanMeasure = false; - m_ring.m_followedLink = false; m_lastDx = 0; m_lastDxTime = 0; m_ringAnimationEnd = 0; - m_rootLayer = 0; + m_baseLayer = 0; } ~WebView() @@ -190,7 +190,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : } delete m_frameCacheUI; delete m_navPictureUI; - delete m_rootLayer; + delete m_baseLayer; } WebViewCore* getWebViewCore() const { @@ -221,15 +221,6 @@ void hideCursor() viewInvalidate(); } -void clearTextEntry() -{ - DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue.object(env).get(), - m_javaGlue.m_clearTextEntry, true); - checkException(env); -} - #if DUMP_NAV_CACHE void debugDump() { @@ -276,7 +267,7 @@ void nativeRecordButtons(bool hasFocus, bool pressed, bool invalidate) // button if (!hasFocus) { state = WebCore::RenderSkinAndroid::kNormal; - } else if (m_ring.m_followedLink || pressed) { + } else if (pressed || m_ring.m_isPressed) { state = WebCore::RenderSkinAndroid::kPressed; } else { state = WebCore::RenderSkinAndroid::kFocused; @@ -303,10 +294,11 @@ void scrollRectOnScreen(const IntRect& rect) SkRect visible; calcOurContentVisibleRect(&visible); #if USE(ACCELERATED_COMPOSITING) - if (m_rootLayer) { - m_rootLayer->updateFixedLayersPositions(visible); - m_rootLayer->updatePositions(); - visible = m_rootLayer->subtractLayers(visible); + LayerAndroid* root = compositeRoot(); + if (root) { + root->updateFixedLayersPositions(visible); + root->updatePositions(); + visible = root->subtractLayers(visible); } #endif int dx = 0; @@ -350,7 +342,7 @@ void calcOurContentVisibleRect(SkRect* r) void resetCursorRing() { - m_ring.m_followedLink = false; + m_ringAnimationEnd = 0; m_viewImpl->m_hasCursorBounds = false; } @@ -372,36 +364,101 @@ bool drawCursorPreamble(CachedRoot* root) m_ring.m_root = root; m_ring.m_frame = frame; m_ring.m_node = node; + SkMSec time = SkTime::GetMSecs(); + m_ring.m_isPressed = time < m_ringAnimationEnd + && m_ringAnimationEnd != UINT_MAX; return true; } void drawCursorPostamble() { - if (!m_ring.m_isButton && m_ring.m_flavor < CursorRing::NORMAL_ANIMATING) + if (m_ringAnimationEnd == UINT_MAX) return; SkMSec time = SkTime::GetMSecs(); if (time < m_ringAnimationEnd) { // views assume that inval bounds coordinates are non-negative WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX); - invalBounds.intersect(m_ring.m_bounds); + invalBounds.intersect(m_ring.m_absBounds); postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds); } else { - if (m_ring.m_followedLink) - hideCursor(); - m_ring.m_followedLink = false; - m_ring.m_flavor = static_cast<CursorRing::Flavor> - (m_ring.m_flavor - CursorRing::NORMAL_ANIMATING); + hideCursor(); } } -void drawExtras(SkCanvas* canvas, int extras) +bool drawGL(WebCore::IntRect& viewRect, float scale, int extras) { +#if USE(ACCELERATED_COMPOSITING) + if (!m_baseLayer) + return false; + + m_glWebViewState.resetExtra(false); CachedRoot* root = getFrameCache(AllowNewer); if (!root) { DBG_NAV_LOG("!root"); if (extras == DrawExtrasCursorRing) resetCursorRing(); - return; + return false; + } + DrawExtra* extra = 0; + switch (extras) { + case DrawExtrasFind: + extra = &m_findOnPage; + break; + case DrawExtrasSelection: + extra = &m_selectText; + break; + case DrawExtrasCursorRing: + if (drawCursorPreamble(root) && m_ring.setup()) { + if (!m_ring.m_isButton) + extra = &m_ring; + drawCursorPostamble(); + } + break; + default: + ; + } + + unsigned int pic = m_glWebViewState.currentPictureCounter(); + if (extra) { + LayerAndroid* mainPicture = new LayerAndroid(m_navPictureUI); + m_glWebViewState.setExtra(extra, mainPicture); + } else { + m_glWebViewState.resetExtra(true); + } + + SkRect visibleRect; + calcOurContentVisibleRect(&visibleRect); + bool ret = m_baseLayer->drawGL(viewRect, visibleRect, scale); + if (ret || m_glWebViewState.currentPictureCounter() != pic) + return true; +#endif + return false; +} + +PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) +{ + PictureSet* ret = 0; + if (!m_baseLayer) { + canvas->drawColor(bgColor); + return ret; + } + + // draw the content of the base layer first + PictureSet* content = m_baseLayer->content(); + int sc = canvas->save(SkCanvas::kClip_SaveFlag); + canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(), + content->height()), SkRegion::kDifference_Op); + canvas->drawColor(bgColor); + canvas->restoreToCount(sc); + if (content->draw(canvas)) + ret = split ? new PictureSet(*content) : 0; + + CachedRoot* root = getFrameCache(AllowNewer); + if (!root) { + DBG_NAV_LOG("!root"); + if (extras == DrawExtrasCursorRing) + resetCursorRing(); + return ret; } LayerAndroid mainPicture(m_navPictureUI); DrawExtra* extra = 0; @@ -425,22 +482,24 @@ void drawExtras(SkCanvas* canvas, int extras) if (extra) extra->draw(canvas, &mainPicture); #if USE(ACCELERATED_COMPOSITING) - if (!m_rootLayer) - return; - m_rootLayer->setExtra(extra); + LayerAndroid* compositeLayer = compositeRoot(); + if (!compositeLayer) + return ret; + compositeLayer->setExtra(extra); SkRect visible; calcOurContentVisibleRect(&visible); // call this to be sure we've adjusted for any scrolling or animations // before we actually draw - m_rootLayer->updateFixedLayersPositions(visible); - m_rootLayer->updatePositions(); - // We have to set the canvas' matrix on the root layer + compositeLayer->updateFixedLayersPositions(visible); + compositeLayer->updatePositions(); + // We have to set the canvas' matrix on the base layer // (to have fixed layers work as intended) SkAutoCanvasRestore restore(canvas, true); - m_rootLayer->setMatrix(canvas->getTotalMatrix()); + m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); - m_rootLayer->draw(canvas); + m_baseLayer->draw(canvas); #endif + return ret; } @@ -487,7 +546,7 @@ void fixCursor() return; int x, y; const CachedFrame* frame; - const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, false); + const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, true); if (!node) return; // require that node have approximately the same bounds (+/- 4) and the same @@ -565,7 +624,7 @@ CachedRoot* getFrameCache(FrameCachePermission allowNewer) m_viewImpl->m_navPictureKit = 0; m_viewImpl->gFrameCacheMutex.unlock(); if (m_frameCacheUI) - m_frameCacheUI->setRootLayer(m_rootLayer); + m_frameCacheUI->setRootLayer(compositeRoot()); #if USE(ACCELERATED_COMPOSITING) if (layerId >= 0) { SkRect visible; @@ -658,10 +717,10 @@ static CachedFrame::Direction KeyToDirection(int32_t keyCode) } } -WebCore::String imageURI(int x, int y) +WTF::String imageURI(int x, int y) { const CachedRoot* root = getFrameCache(DontAllowNewer); - return root ? root->imageURI(x, y) : WebCore::String(); + return root ? root->imageURI(x, y) : WTF::String(); } bool cursorWantsKeyEvents() @@ -701,8 +760,6 @@ bool moveCursor(int keyCode, int count, bool ignoreScroll) int dx = 0; int dy = 0; int counter = count; - if (!cursor || !m_ring.m_followedLink) - root->setScrollOnly(m_ring.m_followedLink); while (--counter >= 0) { WebCore::IntPoint scroll = WebCore::IntPoint(0, 0); cachedNode = root->moveCursor(direction, &cachedFrame, &scroll); @@ -734,13 +791,13 @@ bool moveCursor(int keyCode, int count, bool ignoreScroll) } bool result = false; if (cachedNode) { + showCursorUntimed(); m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode); root->setCursor(const_cast<CachedFrame*>(cachedFrame), const_cast<CachedNode*>(cachedNode)); bool disableFocusController = cachedNode != root->currentFocus() && cachedNode->wantsKeyEvents(); sendMoveMouseIfLatest(disableFocusController); - viewInvalidate(); } else { int docHeight = root->documentHeight(); int docWidth = root->documentWidth(); @@ -807,34 +864,19 @@ void selectBestAt(const WebCore::IntRect& rect) m_viewImpl->m_hasCursorBounds = false; if (root) root->setCursor(0, 0); + viewInvalidate(); } else { DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index()); - root->rootHistory()->setMouseBounds(node->bounds(frame)); + WebCore::IntRect bounds = node->bounds(frame); + root->rootHistory()->setMouseBounds(frame->unadjustBounds(node, bounds)); m_viewImpl->updateCursorBounds(root, frame, node); + showCursorTimed(); root->setCursor(const_cast<CachedFrame*>(frame), const_cast<CachedNode*>(node)); } sendMoveMouseIfLatest(false); - viewInvalidate(); -} - -WebCore::IntRect getNavBounds() -{ - CachedRoot* root = getFrameCache(DontAllowNewer); - return root ? root->rootHistory()->navBounds() : - WebCore::IntRect(0, 0, 0, 0); } -void setNavBounds(const WebCore::IntRect& rect) -{ - CachedRoot* root = getFrameCache(DontAllowNewer); - if (!root) - return; - root->rootHistory()->setNavBounds(rect); -} - - - const CachedNode* m_cacheHitNode; const CachedFrame* m_cacheHitFrame; @@ -851,7 +893,6 @@ bool pointInNavCache(int x, int y, int slop) bool motionUp(int x, int y, int slop) { bool pageScrolled = false; - m_ring.m_followedLink = false; IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2); int rx, ry; CachedRoot* root = getFrameCache(AllowNewer); @@ -859,9 +900,10 @@ bool motionUp(int x, int y, int slop) return 0; const CachedFrame* frame = 0; const CachedNode* result = findAt(root, rect, &frame, &rx, &ry); + CachedHistory* history = root->rootHistory(); if (!result) { DBG_NAV_LOGD("no nodes found root=%p", root); - setNavBounds(rect); + history->setNavBounds(rect); m_viewImpl->m_hasCursorBounds = false; root->hideCursor(); int dx = root->checkForCenter(x, y); @@ -872,33 +914,46 @@ bool motionUp(int x, int y, int slop) sendMotionUp(frame ? (WebCore::Frame*) frame->framePointer() : 0, 0, x, y); viewInvalidate(); - clearTextEntry(); return pageScrolled; } DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result, result->index(), x, y, rx, ry); + // No need to call unadjustBounds below. rx and ry are already adjusted to + // the absolute position of the node. WebCore::IntRect navBounds = WebCore::IntRect(rx, ry, 1, 1); - setNavBounds(navBounds); - root->rootHistory()->setMouseBounds(navBounds); + history->setNavBounds(navBounds); + history->setMouseBounds(navBounds); m_viewImpl->updateCursorBounds(root, frame, result); root->setCursor(const_cast<CachedFrame*>(frame), const_cast<CachedNode*>(result)); - bool syntheticLink = result->isSyntheticLink(); - if (!syntheticLink) { + if (result->isSyntheticLink()) + overrideUrlLoading(result->getExport()); + else { sendMotionUp( (WebCore::Frame*) frame->framePointer(), (WebCore::Node*) result->nodePointer(), rx, ry); } - viewInvalidate(); - if (!result->isTextInput()) { - clearTextEntry(); - setFollowedLink(true); - if (syntheticLink) - overrideUrlLoading(result->getExport()); - } + if (result->isTextInput() || result->isSelect() + || result->isContentEditable()) { + showCursorUntimed(); + } else + showCursorTimed(); return pageScrolled; } +const LayerAndroid* scrollableLayer(int x, int y) +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) && USE(ACCELERATED_COMPOSITING) + const LayerAndroid* root = compositeRoot(); + if (!root) + return 0; + const LayerAndroid* result = root->find(x, y); + if (result != 0 && result->contentIsScrollable()) + return result; +#endif + return 0; +} + int getBlockLeftEdge(int x, int y, float scale) { CachedRoot* root = getFrameCache(AllowNewer); @@ -907,7 +962,7 @@ int getBlockLeftEdge(int x, int y, float scale) return -1; } -void overrideUrlLoading(const WebCore::String& url) +void overrideUrlLoading(const WTF::String& url) { JNIEnv* env = JSC::Bindings::getJNIEnv(); jstring jName = env->NewString((jchar*) url.characters(), url.length()); @@ -928,12 +983,19 @@ void setFindIsEmpty() m_findOnPage.clearCurrentLocation(); } -void setFollowedLink(bool followed) +void showCursorTimed() { - if ((m_ring.m_followedLink = followed) != false) { - m_ringAnimationEnd = SkTime::GetMSecs() + 500; - viewInvalidate(); - } + DBG_NAV_LOG(""); + m_ringAnimationEnd = SkTime::GetMSecs() + 500; + viewInvalidate(); +} + +void showCursorUntimed() +{ + DBG_NAV_LOG(""); + m_ring.m_isPressed = false; + m_ringAnimationEnd = UINT_MAX; + viewInvalidate(); } void setHeightCanMeasure(bool measure) @@ -1082,11 +1144,27 @@ void findNext(bool forward) // With this call, WebView takes ownership of matches, and is responsible for // deleting it. -void setMatches(WTF::Vector<MatchInfo>* matches) -{ +void setMatches(WTF::Vector<MatchInfo>* matches, jboolean sameAsLastSearch) +{ + // If this search is the same as the last one, check against the old + // location to determine whether to scroll. If the same word is found + // in the same place, then do not scroll. + IntRect oldLocation; + bool checkAgainstOldLocation; + if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) { + oldLocation = m_findOnPage.currentMatchBounds(); + checkAgainstOldLocation = true; + } else + checkAgainstOldLocation = false; + m_findOnPage.setMatches(matches); - if (!m_findOnPage.currentMatchIsInLayer()) - scrollRectOnScreen(m_findOnPage.currentMatchBounds()); + + if (!checkAgainstOldLocation + || oldLocation != m_findOnPage.currentMatchBounds()) { + // FIXME: Need to scroll if the match is in a layer. + if (!m_findOnPage.currentMatchIsInLayer()) + scrollRectOnScreen(m_findOnPage.currentMatchBounds()); + } viewInvalidate(); } @@ -1169,20 +1247,74 @@ int moveGeneration() return m_viewImpl->m_moveGeneration; } -LayerAndroid* rootLayer() const +LayerAndroid* compositeRoot() const { - return m_rootLayer; + LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1, + "base layer can't have more than one child %s", __FUNCTION__); + if (m_baseLayer && m_baseLayer->countChildren() == 1) + return static_cast<LayerAndroid*>(m_baseLayer->getChild(0)); + else + return 0; } -void setRootLayer(LayerAndroid* layer) +static void copyScrollPositionRecursive(const LayerAndroid* from, + LayerAndroid* root) { - delete m_rootLayer; - m_rootLayer = layer; + if (!from || !root) + return; + for (int i = 0; i < from->countChildren(); i++) { + const LayerAndroid* l = from->getChild(i); + if (l->contentIsScrollable()) { + LayerAndroid* match = + const_cast<LayerAndroid*>(root->findById(l->uniqueId())); + if (match != 0) + match->setScrollPosition(l->scrollPosition()); + } + copyScrollPositionRecursive(l, root); + } +} + +void setBaseLayer(BaseLayerAndroid* layer, WebCore::IntRect& rect) +{ +#if USE(ACCELERATED_COMPOSITING) + m_glWebViewState.setBaseLayer(layer, rect); +#endif + + if (layer) { + copyScrollPositionRecursive(compositeRoot(), + static_cast<LayerAndroid*>(layer->getChild(0))); + } + delete m_baseLayer; + m_baseLayer = layer; CachedRoot* root = getFrameCache(DontAllowNewer); if (!root) return; root->resetLayers(); - root->setRootLayer(m_rootLayer); + root->setRootLayer(compositeRoot()); +} + +void replaceBaseContent(PictureSet* set) +{ + if (!m_baseLayer) + return; + m_baseLayer->setContent(*set); + delete set; +} + +void copyBaseContentToPicture(SkPicture* picture) +{ + if (!m_baseLayer) + return; + PictureSet* content = m_baseLayer->content(); + content->draw(picture->beginRecording(content->width(), content->height(), + SkPicture::kUsePathBoundsForClip_RecordingFlag)); + picture->endRecording(); +} + +bool hasContent() { + if (!m_baseLayer) + return false; + return !m_baseLayer->content()->isEmpty(); } private: // local state for WebView @@ -1199,13 +1331,16 @@ private: // local state for WebView SelectText m_selectText; FindOnPage m_findOnPage; CursorRing m_ring; - LayerAndroid* m_rootLayer; + BaseLayerAndroid* m_baseLayer; +#if USE(ACCELERATED_COMPOSITING) + GLWebViewState m_glWebViewState; +#endif }; // end of WebView class /* * Native JNI methods */ -static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string) +static jstring WebCoreStringToJString(JNIEnv *env, WTF::String string) { int length = string.length(); if (!length) @@ -1332,6 +1467,19 @@ static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj) return cursor ? frame->textInput(cursor) : 0; } +static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj) +{ + const CachedNode* focus = getFocusNode(env, obj); + if (!focus) return false; + // Plugins handle shift and arrows whether or not they have focus. + if (focus->isPlugin()) return true; + const CachedNode* cursor = getCursorNode(env, obj); + // ContentEditable nodes should only receive shift and arrows if they have + // both the cursor and the focus. + return cursor && cursor->nodePointer() == focus->nodePointer() + && cursor->isContentEditable(); +} + static jboolean nativeCursorMatchesFocus(JNIEnv *env, jobject obj) { const CachedNode* cursor = getCursorNode(env, obj); @@ -1403,7 +1551,7 @@ static jobject nativeCursorText(JNIEnv *env, jobject obj) const CachedNode* node = getCursorNode(env, obj); if (!node) return 0; - WebCore::String value = node->getExport(); + WTF::String value = node->getExport(); return !value.isEmpty() ? env->NewString((jchar *)value.characters(), value.length()) : 0; } @@ -1417,35 +1565,58 @@ static void nativeDebugDump(JNIEnv *env, jobject obj) #endif } -static void nativeDrawExtras(JNIEnv *env, jobject obj, jobject canv, jint extras) -{ +static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, jint color, + jint extras, jboolean split) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); - GET_NATIVE_VIEW(env, obj)->drawExtras(canvas, extras); + return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split)); +} + +static bool nativeDrawGL(JNIEnv *env, jobject obj, jobject jrect, + jfloat scale, jint extras) +{ + WebCore::IntRect viewRect = jrect_to_webrect(env, jrect); + return GET_NATIVE_VIEW(env, obj)->drawGL(viewRect, scale, extras); } static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj) { #if USE(ACCELERATED_COMPOSITING) - const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer(); + const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot(); if (root) return root->evaluateAnimations(); #endif return false; } -static void nativeSetRootLayer(JNIEnv *env, jobject obj, jint layer) +static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer, jobject jrect) { -#if USE(ACCELERATED_COMPOSITING) - LayerAndroid* layerImpl = reinterpret_cast<LayerAndroid*>(layer); - GET_NATIVE_VIEW(env, obj)->setRootLayer(layerImpl); -#endif + BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer); + WebCore::IntRect rect = jrect_to_webrect(env, jrect); + GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, rect); +} + +static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content) +{ + PictureSet* set = reinterpret_cast<PictureSet*>(content); + GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set); +} + +static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict) +{ + SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); + GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture); +} + +static bool nativeHasContent(JNIEnv *env, jobject obj) +{ + return GET_NATIVE_VIEW(env, obj)->hasContent(); } static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y) { WebView* view = GET_NATIVE_VIEW(env, obj); LOG_ASSERT(view, "view not set in %s", __FUNCTION__); - WebCore::String uri = view->imageURI(x, y); + WTF::String uri = view->imageURI(x, y); jstring ret = 0; unsigned len = uri.length(); if (len) { @@ -1497,7 +1668,7 @@ static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj) const CachedInput* input = getInputCandidate(env, obj); if (!input) return 0; - const WebCore::String& name = input->name(); + const WTF::String& name = input->name(); return env->NewString((jchar*)name.characters(), name.length()); } @@ -1540,7 +1711,7 @@ static jobject nativeFocusCandidateText(JNIEnv *env, jobject obj) const CachedNode* node = getFocusCandidate(env, obj, 0); if (!node) return 0; - WebCore::String value = node->getExport(); + WTF::String value = node->getExport(); return !value.isEmpty() ? env->NewString((jchar *)value.characters(), value.length()) : 0; } @@ -1643,7 +1814,7 @@ static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect) { SkIRect irect = jrect_to_webrect(env, jrect); #if USE(ACCELERATED_COMPOSITING) - LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer(); + LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot(); if (root) { SkRect rect; rect.set(irect); @@ -1717,11 +1888,9 @@ static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj) GET_NATIVE_VIEW(env, obj)->setFindIsEmpty(); } -static void nativeSetFollowedLink(JNIEnv *env, jobject obj, bool followed) +static void nativeShowCursorTimed(JNIEnv *env, jobject obj) { - WebView* view = GET_NATIVE_VIEW(env, obj); - LOG_ASSERT(view, "view not set in %s", __FUNCTION__); - view->setFollowedLink(followed); + GET_NATIVE_VIEW(env, obj)->showCursorTimed(); } static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure) @@ -1747,7 +1916,7 @@ static jobject nativeGetCursorRingBounds(JNIEnv *env, jobject obj) } static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower, - jstring findUpper) + jstring findUpper, jboolean sameAsLastSearch) { // If one or the other is null, do not search. if (!(findLower && findUpper)) @@ -1795,7 +1964,7 @@ static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower, root->draw(canvas); WTF::Vector<MatchInfo>* matches = canvas.detachMatches(); // With setMatches, the WebView takes ownership of matches - view->setMatches(matches); + view->setMatches(matches, sameAsLastSearch); env->ReleaseStringChars(findLower, findLowerChars); env->ReleaseStringChars(findUpper, findUpperChars); @@ -1827,7 +1996,7 @@ static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring update const CachedNode* cachedFocusNode = root->currentFocus(); if (!cachedFocusNode || !cachedFocusNode->isTextInput()) return; - WebCore::String webcoreString = to_string(env, updatedText); + WTF::String webcoreString = jstringToWtfString(env, updatedText); (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString); root->setTextGeneration(generation); checkException(env); @@ -1867,8 +2036,9 @@ static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj) if (!next) return false; const WebCore::IntRect& bounds = next->bounds(frame); - root->rootHistory()->setMouseBounds(bounds); + root->rootHistory()->setMouseBounds(frame->unadjustBounds(next, bounds)); view->getWebViewCore()->updateCursorBounds(root, frame, next); + view->showCursorUntimed(); root->setCursor(const_cast<CachedFrame*>(frame), const_cast<CachedNode*>(next)); view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()), @@ -1892,6 +2062,24 @@ static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y) GET_NATIVE_VIEW(env, obj)->moveSelection(x, y); } +static jboolean nativeCleanupPrivateBrowsingFiles( + JNIEnv *env, jobject obj, jstring databaseDirectoryJString, jstring cacheDirectoryJString) { +#if USE(CHROME_NETWORK_STACK) + jboolean isCopy; + const char* cString = env->GetStringUTFChars(databaseDirectoryJString, &isCopy); + std::string databaseDirectory(cString); + if (isCopy == JNI_TRUE) + env->ReleaseStringUTFChars(databaseDirectoryJString, cString); + cString = env->GetStringUTFChars(cacheDirectoryJString, &isCopy); + std::string cacheDirectory(cString); + if (isCopy == JNI_TRUE) + env->ReleaseStringUTFChars(cacheDirectoryJString, cString); + return WebRequestContext::CleanupPrivateBrowsingFiles(databaseDirectory, cacheDirectory); +#else + return JNI_FALSE; +#endif +} + static void nativeResetSelection(JNIEnv *env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->resetSelection(); @@ -1979,26 +2167,13 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) SkDumpCanvas canvas(&dumper); // this will playback the picture into the canvas, which will // spew its contents to the dumper - view->getWebViewCore()->drawContent(&canvas, 0); -#if USE(ACCELERATED_COMPOSITING) - if (true) { - LayerAndroid* rootLayer = view->rootLayer(); - if (rootLayer) { - // We have to set the canvas' matrix on the root layer - // (to have fixed layers work as intended) - SkAutoCanvasRestore restore(&canvas, true); - rootLayer->setMatrix(canvas.getTotalMatrix()); - canvas.resetMatrix(); - rootLayer->draw(&canvas); - } - } -#endif + view->draw(&canvas, 0, 0, false); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); } #if USE(ACCELERATED_COMPOSITING) - const LayerAndroid* rootLayer = view->rootLayer(); + const LayerAndroid* rootLayer = view->compositeRoot(); if (rootLayer) { FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w"); if (file) { @@ -2011,6 +2186,40 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) #endif } +static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y) +{ + WebView* view = GET_NATIVE_VIEW(env, jwebview); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return (int) view->scrollableLayer(x, y); +} + +static bool validLayer(const LayerAndroid* root, const LayerAndroid* layer) { + if (root == layer) + return true; + for (int i = 0; i < root->countChildren(); i++) { + const LayerAndroid* l = root->getChild(i); + if (validLayer(l, layer)) + return true; + } + return false; +} + +static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint pLayer, jint dx, + jint dy) +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + WebView* view = GET_NATIVE_VIEW(env, obj); + const LayerAndroid* root = view->compositeRoot(); + LayerAndroid* layer = (LayerAndroid*) pLayer; + if (!validLayer(root, layer)) { + return false; + } + LOG_ASSERT(layer, "layer not set in %s", __FUNCTION__); + return layer->scrollBy(dx, dy); +#endif + return false; +} + /* * JNI registration */ @@ -2027,6 +2236,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCreate }, { "nativeCursorFramePointer", "()I", (void*) nativeCursorFramePointer }, + { "nativePageShouldHandleShiftAndArrows", "()Z", + (void*) nativePageShouldHandleShiftAndArrows }, { "nativeCursorMatchesFocus", "()Z", (void*) nativeCursorMatchesFocus }, { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;", @@ -2049,15 +2260,17 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeDebugDump }, { "nativeDestroy", "()V", (void*) nativeDestroy }, - { "nativeDrawExtras", "(Landroid/graphics/Canvas;I)V", - (void*) nativeDrawExtras }, + { "nativeDraw", "(Landroid/graphics/Canvas;IIZ)I", + (void*) nativeDraw }, + { "nativeDrawGL", "(Landroid/graphics/Rect;FI)Z", + (void*) nativeDrawGL }, { "nativeDumpDisplayTree", "(Ljava/lang/String;)V", (void*) nativeDumpDisplayTree }, { "nativeEvaluateLayersAnimations", "()Z", (void*) nativeEvaluateLayersAnimations }, { "nativeExtendSelection", "(II)V", (void*) nativeExtendSelection }, - { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I", + { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;Z)I", (void*) nativeFindAll }, { "nativeFindNext", "(Z)V", (void*) nativeFindNext }, @@ -2121,6 +2334,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeMoveGeneration }, { "nativeMoveSelection", "(II)V", (void*) nativeMoveSelection }, + { "nativeCleanupPrivateBrowsingFiles", "(Ljava/lang/String;Ljava/lang/String;)Z", + (void*) nativeCleanupPrivateBrowsingFiles }, { "nativePointInNavCache", "(III)Z", (void*) nativePointInNavCache }, { "nativeRecordButtons", "(ZZZ)V", @@ -2141,14 +2356,20 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSetFindIsEmpty }, { "nativeSetFindIsUp", "(Z)V", (void*) nativeSetFindIsUp }, - { "nativeSetFollowedLink", "(Z)V", - (void*) nativeSetFollowedLink }, { "nativeSetHeightCanMeasure", "(Z)V", (void*) nativeSetHeightCanMeasure }, - { "nativeSetRootLayer", "(I)V", - (void*) nativeSetRootLayer }, + { "nativeSetBaseLayer", "(ILandroid/graphics/Rect;)V", + (void*) nativeSetBaseLayer }, + { "nativeReplaceBaseContent", "(I)V", + (void*) nativeReplaceBaseContent }, + { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", + (void*) nativeCopyBaseContentToPicture }, + { "nativeHasContent", "()Z", + (void*) nativeHasContent }, { "nativeSetSelectionPointer", "(ZFII)V", (void*) nativeSetSelectionPointer }, + { "nativeShowCursorTimed", "()V", + (void*) nativeShowCursorTimed }, { "nativeStartSelection", "(II)Z", (void*) nativeStartSelection }, { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;", @@ -2161,6 +2382,10 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeWordSelection }, { "nativeGetBlockLeftEdge", "(IIF)I", (void*) nativeGetBlockLeftEdge }, + { "nativeScrollableLayer", "(II)I", + (void*) nativeScrollableLayer }, + { "nativeScrollLayer", "(III)Z", + (void*) nativeScrollLayer }, }; int register_webview(JNIEnv* env) |
