diff options
-rw-r--r-- | WebCore/platform/graphics/android/LayerAndroid.cpp | 35 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/LayerTexture.h | 12 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/TextureOwner.cpp | 7 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/TilesManager.cpp | 7 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/TilesManager.h | 2 | ||||
-rw-r--r-- | WebCore/plugins/android/PluginPackageAndroid.cpp | 30 | ||||
-rw-r--r-- | WebCore/rendering/RenderLayer.h | 6 | ||||
-rw-r--r-- | WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp | 1 | ||||
-rw-r--r-- | WebKit/android/jni/WebCoreFrameBridge.cpp | 1 | ||||
-rw-r--r-- | WebKit/android/jni/WebViewCore.cpp | 732 | ||||
-rw-r--r-- | WebKit/android/jni/WebViewCore.h | 14 | ||||
-rw-r--r-- | WebKit/android/nav/CacheBuilder.cpp | 14 | ||||
-rw-r--r-- | WebKit/android/nav/WebView.cpp | 32 | ||||
-rw-r--r-- | WebKit/android/plugins/PluginTimer.cpp | 1 |
14 files changed, 588 insertions, 306 deletions
diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index 03bb11d..77a948a 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -77,7 +77,7 @@ LayerAndroid::LayerAndroid(bool isRootLayer) : SkLayer(), m_uniqueId(++gUniqueId), m_drawingTexture(0), m_reservedTexture(0), - m_pictureUsed(-1), + m_pictureUsed(0), m_requestSent(false), m_scale(1) { @@ -594,12 +594,20 @@ void LayerAndroid::reserveGLTextures() IntRect cr = TilesManager::instance()->shader()->clippedRectWithViewport(tr); m_layerTextureRect = drawTransform().inverse().mapRect(cr); - reservedTexture = TilesManager::instance()->getExistingTextureForLayer(this, m_layerTextureRect); + reservedTexture = TilesManager::instance()->getExistingTextureForLayer( + this, m_layerTextureRect); // If we do not have a drawing texture (i.e. new LayerAndroid tree), // we get any one available. - if (!m_drawingTexture) - m_drawingTexture = TilesManager::instance()->getExistingTextureForLayer(this, m_layerTextureRect, true); + if (!m_drawingTexture) { + LayerTexture* texture = reservedTexture; + m_drawingTexture = + TilesManager::instance()->getExistingTextureForLayer( + this, m_layerTextureRect, true, texture); + + if (!m_drawingTexture) + m_drawingTexture = reservedTexture; + } } // SMP flush @@ -636,6 +644,7 @@ void LayerAndroid::createGLTextures() m_atomicSync.unlock(); if (reservedTexture && + reservedTexture->ready() && (reservedTexture != m_drawingTexture)) { if (m_drawingTexture) { TilesManager::instance()->removeOperationsForTexture(m_drawingTexture); @@ -668,13 +677,10 @@ bool LayerAndroid::needsScheduleRepaint(LayerTexture* texture) if (!texture) return false; - if (m_pictureUsed == -1 || - texture->pictureUsed() == -1 || + if (!texture->ready() || texture->pictureUsed() != m_pictureUsed) { XLOG("We mark layer %d (%x) as dirty because: m_pictureUsed(%d == 0?), texture picture used %x", uniqueId(), this, m_pictureUsed, texture->pictureUsed()); - if (m_pictureUsed == -1) - m_pictureUsed = 0; m_dirty = true; } @@ -705,8 +711,10 @@ bool LayerAndroid::drawGL(SkMatrix& matrix) // move the drawing depending on where the texture is on the layer TransformationMatrix m = drawTransform(); m.translate(textureRect.x(), textureRect.y()); - XLOG("LayerAndroid %d %x (%.2f, %.2f) drawGL (texture %x, %d, %d, %d, %d)", uniqueId(), this, getWidth(), getHeight(), - m_drawingTexture, textureRect.x(), textureRect.y(), textureRect.width(), textureRect.height()); + XLOG("LayerAndroid %d %x (%.2f, %.2f) drawGL (texture %x, %d, %d, %d, %d)", + uniqueId(), this, getWidth(), getHeight(), + m_drawingTexture, textureRect.x(), textureRect.y(), + textureRect.width(), textureRect.height()); TilesManager::instance()->shader()->drawLayerQuad(m, bounds, textureInfo->m_textureId, m_drawOpacity); @@ -768,7 +776,7 @@ void LayerAndroid::paintBitmapGL() return; } - XLOG("LayerAndroid paintBitmapGL (layer %d), texture used %x (%d, %d)", uniqueId(), texture, + XLOG("LayerAndroid %d paintBitmapGL, texture used %x (%d, %d)", uniqueId(), texture, texture->rect().width(), texture->rect().height()); // We need to mark the texture as busy before relinquishing the lock @@ -805,12 +813,13 @@ void LayerAndroid::paintBitmapGL() m_atomicSync.lock(); m_dirty = false; m_requestSent = false; - texture->setPictureUsed(m_pictureUsed); - m_atomicSync.unlock(); XLOG("LayerAndroid %d paintBitmapGL PAINTING DONE, updating the texture", uniqueId()); texture->producerUpdate(textureInfo); + texture->setPictureUsed(m_pictureUsed); + m_atomicSync.unlock(); + XLOG("LayerAndroid %d paintBitmapGL UPDATING DONE", uniqueId()); } diff --git a/WebCore/platform/graphics/android/LayerTexture.h b/WebCore/platform/graphics/android/LayerTexture.h index fb1b9fb..042422d 100644 --- a/WebCore/platform/graphics/android/LayerTexture.h +++ b/WebCore/platform/graphics/android/LayerTexture.h @@ -38,15 +38,22 @@ class LayerTexture : public BackedDoubleBufferedTexture { : BackedDoubleBufferedTexture(w, h, config) , m_id(0) , m_scale(1) - , m_pictureUsed(-1) + , m_pictureUsed(0) + , m_ready(false) {} virtual ~LayerTexture() {}; int id() { return m_id; } void setId(int id) { m_id = id; } + bool ready() { return m_ready; } unsigned int pictureUsed() { return m_pictureUsed; } - void setPictureUsed(unsigned pictureUsed) { m_pictureUsed = pictureUsed; } + void setPictureUsed(unsigned pictureUsed) + { + if (!m_ready) + m_ready = true; + m_pictureUsed = pictureUsed; + } void setRect(const IntRect& r) { m_rect = r; } IntRect& rect() { return m_rect; } float scale() { return m_scale; } @@ -58,6 +65,7 @@ class LayerTexture : public BackedDoubleBufferedTexture { IntRect m_rect; float m_scale; unsigned int m_pictureUsed; + bool m_ready; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/android/TextureOwner.cpp b/WebCore/platform/graphics/android/TextureOwner.cpp index 6a6845a..c4446d7 100644 --- a/WebCore/platform/graphics/android/TextureOwner.cpp +++ b/WebCore/platform/graphics/android/TextureOwner.cpp @@ -34,8 +34,11 @@ TextureOwner::~TextureOwner() { if (m_ownedTextures.size()) { // This TextureOwner owns textures still! - HashSet<BackedDoubleBufferedTexture*>::iterator it = m_ownedTextures.begin(); - for (; it != m_ownedTextures.end(); ++it) + HashSet<BackedDoubleBufferedTexture*> textures; + // Swap to a local copy because release will modify the iterator. + textures.swap(m_ownedTextures); + HashSet<BackedDoubleBufferedTexture*>::const_iterator it = textures.begin(); + for (; it != textures.end(); ++it) (*it)->release(this); } } diff --git a/WebCore/platform/graphics/android/TilesManager.cpp b/WebCore/platform/graphics/android/TilesManager.cpp index 1655016..16282c4 100644 --- a/WebCore/platform/graphics/android/TilesManager.cpp +++ b/WebCore/platform/graphics/android/TilesManager.cpp @@ -80,7 +80,7 @@ TilesManager::TilesManager() m_textures.append(reinterpret_cast<BackedDoubleBufferedTexture*>( android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture)))); } - XLOG("TilesManager ctor"); + XLOG("TilesManager ctor - init textures done"); m_pixmapsGenerationThread = new TexturesGenerator(); m_pixmapsGenerationThread->run("TexturesGenerator"); @@ -232,7 +232,8 @@ BackedDoubleBufferedTexture* TilesManager::getAvailableTexture(BaseTile* owner) LayerTexture* TilesManager::getExistingTextureForLayer(LayerAndroid* layer, const IntRect& rect, - bool any) + bool any, + LayerTexture* texture) { android::Mutex::Autolock lock(m_texturesLock); for (unsigned int i = 0; i< m_layersTextures.size(); i++) { @@ -242,6 +243,8 @@ LayerTexture* TilesManager::getExistingTextureForLayer(LayerAndroid* layer, continue; if (!any && layer->getScale() != m_layersTextures[i]->scale()) continue; + if (any && texture == m_layersTextures[i]) + continue; XLOG("return layer %d (%x) for tile %d (%x)", i, m_layersTextures[i], diff --git a/WebCore/platform/graphics/android/TilesManager.h b/WebCore/platform/graphics/android/TilesManager.h index c09a388..0338ece 100644 --- a/WebCore/platform/graphics/android/TilesManager.h +++ b/WebCore/platform/graphics/android/TilesManager.h @@ -72,7 +72,7 @@ public: void printLayersTextures(const char* s); void cleanupLayersTextures(LayerAndroid* layer, bool forceCleanup = false); LayerTexture* getExistingTextureForLayer(LayerAndroid* layer, const IntRect& rect, - bool any = false); + bool any = false, LayerTexture* texture = 0); LayerTexture* createTextureForLayer(LayerAndroid* layer, const IntRect& rect); void markGeneratorAsReady() diff --git a/WebCore/plugins/android/PluginPackageAndroid.cpp b/WebCore/plugins/android/PluginPackageAndroid.cpp index 97ec624..24de122 100644 --- a/WebCore/plugins/android/PluginPackageAndroid.cpp +++ b/WebCore/plugins/android/PluginPackageAndroid.cpp @@ -206,21 +206,23 @@ bool PluginPackage::load() m_loadCount++; PLUGIN_LOG("Already loaded, count now %d\n", m_loadCount); return true; - } - ASSERT(m_loadCount == 0); - ASSERT(m_module == NULL); + } else { + ASSERT(m_loadCount == 0); + ASSERT(m_module == NULL); - PLUGIN_LOG("Loading \"%s\"\n", m_path.utf8().data()); + PLUGIN_LOG("Loading \"%s\"\n", m_path.utf8().data()); - // Open the library - void *handle = dlopen(m_path.utf8().data(), RTLD_NOW); - if(!handle) { - PLUGIN_LOG("Couldn't load plugin library \"%s\": %s\n", - m_path.utf8().data(), dlerror()); - return false; + // Open the library + void *handle = dlopen(m_path.utf8().data(), RTLD_NOW); + if(!handle) { + PLUGIN_LOG("Couldn't load plugin library \"%s\": %s\n", + m_path.utf8().data(), dlerror()); + return false; + } + m_module = handle; + PLUGIN_LOG("Fetch Info Loaded %p\n", m_module); } - m_module = handle; - PLUGIN_LOG("Fetch Info Loaded %p\n", m_module); + // This object will call dlclose() and set m_module to NULL // when going out of scope. DynamicLibraryCloser dlCloser(&m_module); @@ -228,7 +230,7 @@ bool PluginPackage::load() NP_InitializeFuncPtr NP_Initialize; if(!getEntryPoint(m_module, "NP_Initialize", (void **) &NP_Initialize) || - !getEntryPoint(handle, "NP_Shutdown", (void **) &m_NPP_Shutdown)) { + !getEntryPoint(m_module, "NP_Shutdown", (void **) &m_NPP_Shutdown)) { PLUGIN_LOG("Couldn't find Initialize function\n"); return false; } @@ -254,8 +256,6 @@ bool PluginPackage::load() // Don't close the library - loaded OK. dlCloser.ok(); - // Retain the handle so we can close it in the future. - m_module = handle; m_isLoaded = true; ++m_loadCount; PLUGIN_LOG("Initial load ok, count now %d\n", m_loadCount); diff --git a/WebCore/rendering/RenderLayer.h b/WebCore/rendering/RenderLayer.h index c3c9097..711d398 100644 --- a/WebCore/rendering/RenderLayer.h +++ b/WebCore/rendering/RenderLayer.h @@ -142,6 +142,9 @@ public: return m_overflowClipRect == other.overflowClipRect() && m_fixedClipRect == other.fixedClipRect() && m_posClipRect == other.posClipRect() && +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + m_hitTestClip == other.hitTestClip() && +#endif m_fixed == other.fixed(); } @@ -150,6 +153,9 @@ public: m_overflowClipRect = other.overflowClipRect(); m_fixedClipRect = other.fixedClipRect(); m_posClipRect = other.posClipRect(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + m_hitTestClip = other.hitTestClip(); +#endif m_fixed = other.fixed(); return *this; } diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp index cb1efe1..f0958d9 100644 --- a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp @@ -37,7 +37,6 @@ #include "FrameLoader.h" #include "FrameView.h" #include "Geolocation.h" -#include "GraphicsLayerAndroid.h" #include "Icon.h" #include "Page.h" #include "PopupMenuAndroid.h" diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp index 5468a1e..476f017 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -539,7 +539,6 @@ WebFrame::loadStarted(WebCore::Frame* frame) WebCore::FrameLoadType loadType = frame->loader()->loadType(); if (loadType == WebCore::FrameLoadTypeReplace || - loadType == WebCore::FrameLoadTypeSame || (loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList && !isMainFrame)) return; diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp index 39f6f2d..0163376 100644 --- a/WebKit/android/jni/WebViewCore.cpp +++ b/WebKit/android/jni/WebViewCore.cpp @@ -36,6 +36,8 @@ #include "ChromeClientAndroid.h" #include "ChromiumIncludes.h" #include "Color.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" #include "DatabaseTracker.h" #include "Document.h" #include "DOMWindow.h" @@ -112,6 +114,7 @@ #include "WindowsKeyboardCodes.h" #include "android_graphics.h" #include "autofill/WebAutoFill.h" +#include "htmlediting.h" #include "markup.h" #include <JNIHelp.h> @@ -294,6 +297,7 @@ struct WebViewCore::JavaGlue { jmethodID m_setScrollbarModes; jmethodID m_setInstallableWebApp; jmethodID m_setWebTextViewAutoFillable; + jmethodID m_selectAt; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } @@ -389,6 +393,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V"); m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V"); m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V"); + m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V"); env->DeleteLocalRef(clazz); env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this); @@ -2078,39 +2083,6 @@ String WebViewCore::modifySelection(const int direction, const int axis) } } -String WebViewCore::moveSelection(WebCore::Frame* frame, WebCore::Node* node) -{ - if (!frame || !node) - return String(); - - if (!CacheBuilder::validNode(m_mainFrame, frame, node)) - return String(); - - PassRefPtr<Range> rangeRef = 0; - ExceptionCode ec = 0; - DOMSelection* selection = frame->domWindow()->getSelection(); - if (selection->rangeCount() > 0) { - rangeRef = selection->getRangeAt(0, ec); - if (ec) - return String(); - selection->removeAllRanges(); - } else { - rangeRef = frame->document()->createRange(); - } - - rangeRef->selectNode(node, ec); - if (ec) - return String(); - - selection->addRange(rangeRef.get()); - - scrollNodeIntoView(frame, node); - - String markup = formatMarkup(selection).stripWhiteSpace(); - LOGV("Selection markup: %s", markup.utf8().data()); - return markup; -} - void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) { if (!frame || !node) @@ -2135,178 +2107,410 @@ void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis) { - String directionString; - if (direction == DIRECTION_FORWARD) - directionString = "forward"; - else if (direction == DIRECTION_BACKWARD) - directionString = "backward"; - else { - LOGE("Invalid direction: %d", direction); - return String(); - } - String axisString; - if (axis == AXIS_CHARACTER) - axisString = "character"; - else if (axis == AXIS_WORD) - axisString = "word"; - else // axis == AXIS_SENTENCE - axisString = "sentence"; - // TODO: Add support of IFrames. HTMLElement* body = m_mainFrame->document()->body(); - Node* focusNode = 0; - if (m_currentNodeDomNavigationAxis - && CacheBuilder::validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis)) { - focusNode = m_currentNodeDomNavigationAxis; - m_currentNodeDomNavigationAxis = 0; - do { - focusNode = (direction == DIRECTION_FORWARD) ? - focusNode->traverseNextNode(body) : - focusNode->traversePreviousNode(body); - } while (focusNode && focusNode->isTextNode()); - } else - focusNode = (selection->focusNode()) ? selection->focusNode() : currentFocus(); - - Text* currentNode = 0; - if (!focusNode) { - // we have no selection so start from the body or its recursively last child - focusNode = (direction == DIRECTION_FORWARD) ? body : body->lastDescendant(); - if (focusNode->isTextNode()) - currentNode = static_cast<Text*>(focusNode); - else - currentNode = traverseNonEmptyNonWhitespaceTextNode(focusNode, body, direction); - if (!setSelection(selection, currentNode, direction)) + ExceptionCode ec = 0; + + // initialize the selection if necessary + if (selection->rangeCount() == 0) { + if (m_currentNodeDomNavigationAxis + && CacheBuilder::validNode(m_mainFrame, + m_mainFrame, m_currentNodeDomNavigationAxis)) { + PassRefPtr<Range> rangeRef = + selection->frame()->document()->createRange(); + rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec); + m_currentNodeDomNavigationAxis = 0; + if (ec) + return String(); + selection->addRange(rangeRef.get()); + } else if (currentFocus()) { + selection->setPosition(currentFocus(), 0, ec); + } else if (m_cursorNode + && CacheBuilder::validNode(m_mainFrame, + m_mainFrame, m_cursorNode)) { + PassRefPtr<Range> rangeRef = + selection->frame()->document()->createRange(); + rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec); + if (ec) + return String(); + selection->addRange(rangeRef.get()); + } else if (direction == DIRECTION_FORWARD) { + selection->setPosition(body->firstDescendant(), 0, ec); + } else { + selection->setPosition(body->lastDescendant(), 0, ec); + } + if (ec) return String(); - } else if (focusNode->isElementNode()) { - // find a non-empty text node in the current direction - currentNode = traverseNonEmptyNonWhitespaceTextNode(focusNode, body, direction); - if (!setSelection(selection, currentNode, direction)) + } + // collapse the selection + if (direction == DIRECTION_FORWARD) + selection->collapseToEnd(ec); + else + selection->collapseToStart(ec); + if (ec) + return String(); + + Node* oldAnchorNode = selection->anchorNode(); + if (!oldAnchorNode) + return String(); + + // Make sure the anchor node is a text node since we are generating + // the markup of the selection which includes the anchor, the focus, + // and any crossed nodes. Forcing the condition that the selection + // starts and ends on text nodes guarantees symmetric selection markup. + Node* anchorNode = selection->anchorNode(); + if (anchorNode->isElementNode()) { + int anchorOffset = rangeCompliantChildOffset(anchorNode, + selection->anchorOffset()); + anchorNode = selection->anchorNode()->childNodes()->item(anchorOffset); + Node* nextAnchorNode = traverseVisibleNonEmptyNonWhitespaceTextNode( + anchorNode, body, direction); + if (!nextAnchorNode) return String(); - } else { - currentNode = static_cast<Text*>(focusNode); if (direction == DIRECTION_FORWARD) { - // if end of non-whitespace text go to the next non-empty text node - int higherIndex = (selection->focusOffset() > selection->anchorOffset()) ? - selection->focusOffset() : selection->anchorOffset(); - String suffix = currentNode->data().substring(higherIndex, currentNode->length()); - if (suffix.isEmpty() || suffix.stripWhiteSpace().isEmpty()) { - currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction); - if (!setSelection(selection, currentNode, direction)) + Node* skippedControl = getFirstIntermediaryInputOrButton(anchorNode, + nextAnchorNode); + if (skippedControl) { + IntRect bounds = static_cast<Element*>( + skippedControl)->boundsInWindowSpace(); + selectAt(bounds.center().x(), bounds.center().y()); + selection->setBaseAndExtent(skippedControl, + caretMinOffset(skippedControl), skippedControl, + caretMaxOffset(skippedControl), ec); + if (ec) return String(); + return formatMarkup(selection).stripWhiteSpace(); } else { - ExceptionCode ec = 0; - selection->collapseToEnd(ec); + selection->setPosition(nextAnchorNode, 0, ec); if (ec) - LOGE("Error while collapsing selection. Error code: %d", ec); + return String(); } } else { - // if beginning of non-whitespace text go to the previous non-empty text node - int lowerIndex = (selection->focusOffset() > selection->anchorOffset()) ? - selection->anchorOffset() : selection->focusOffset(); - String prefix = currentNode->data().substring(0, lowerIndex); - if (prefix.isEmpty() || prefix.stripWhiteSpace().isEmpty()) { - currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction); - if (!setSelection(selection, currentNode, direction)) + Node* skippedControl = getFirstIntermediaryInputOrButton( + nextAnchorNode, anchorNode); + if (skippedControl) { + IntRect bounds = static_cast<Element*>( + skippedControl)->boundsInWindowSpace(); + selectAt(bounds.center().x(), bounds.center().y()); + selection->setBaseAndExtent(skippedControl, + caretMaxOffset(skippedControl), skippedControl, + caretMinOffset(skippedControl), ec); + if (ec) return String(); + return formatMarkup(selection).stripWhiteSpace(); } else { - ExceptionCode ec = 0; - selection->collapseToStart(ec); + selection->setPosition(nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); if (ec) - LOGE("Error while collapsing selection. Error code: %d", ec); + return String(); } } } - // extend the selection - loop as an insurance it does not get stuck - currentNode = static_cast<Text*>(selection->focusNode()); - int focusOffset = selection->focusOffset(); - while (true) { - selection->modify("extend", directionString, axisString); - if (selection->focusNode() != currentNode || selection->focusOffset() != focusOffset) - break; - currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction); - focusOffset = (direction == DIRECTION_FORWARD) ? 0 : currentNode->data().length(); - // setSelection returns false if currentNode is 0 => the loop always terminates - if (!setSelection(selection, currentNode, direction)) - return String(); + // If the selection is at the end of a non white space text move + // it to the next visible text node with non white space content. + // This is a workaround for the selection getting stuck. + anchorNode = selection->anchorNode(); + if (!anchorNode) + return String(); + if (anchorNode->isTextNode()) { + if (direction == DIRECTION_FORWARD) { + String suffix = anchorNode->textContent().substring( + selection->anchorOffset(), caretMaxOffset(anchorNode)); + if (suffix.stripWhiteSpace().isEmpty()) { + Node* nextAnchorNode = + traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, + body, direction); + if (!nextAnchorNode) + return String(); + Node* skippedControl = getFirstIntermediaryInputOrButton( + anchorNode, nextAnchorNode); + if (skippedControl) { + IntRect bounds = static_cast<Element*>( + skippedControl)->boundsInWindowSpace(); + selectAt(bounds.center().x(), bounds.center().y()); + selection->setBaseAndExtent(skippedControl, + caretMinOffset(skippedControl), skippedControl, + caretMaxOffset(skippedControl), ec); + if (ec) + return String(); + return formatMarkup(selection).stripWhiteSpace(); + } else { + selection->setPosition(nextAnchorNode, 0, ec); + if (ec) + return String(); + } + } + } else { + String prefix = anchorNode->textContent().substring(0, + selection->anchorOffset()); + if (prefix.stripWhiteSpace().isEmpty()) { + Node* nextAnchorNode = + traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, + body, direction); + if (!nextAnchorNode) + return String(); + Node* skippedControl = getFirstIntermediaryInputOrButton( + nextAnchorNode, anchorNode); + if (skippedControl) { + IntRect bounds = static_cast<Element*>( + skippedControl)->boundsInWindowSpace(); + selectAt(bounds.center().x(), bounds.center().y()); + selection->setBaseAndExtent(skippedControl, + caretMaxOffset(skippedControl), skippedControl, + caretMinOffset(skippedControl), ec); + if (ec) + return String(); + return formatMarkup(selection).stripWhiteSpace(); + } else { + selection->setPosition(nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); + if (ec) + return String(); + } + } + } } - if (direction == DIRECTION_FORWARD) { - // enforce the anchor node is a text node - if (selection->anchorNode()->isElementNode()) { - if (!setSelection(selection, selection->focusNode(), selection->focusNode(), 0, - selection->focusOffset())) - return String(); - } - // enforce the focus node is a text node - if (selection->focusNode()->isElementNode()) { - int endOffset = static_cast<Text*>(selection->anchorNode())->length(); // cast is safe - if (!setSelection(selection, selection->anchorNode(), selection->anchorNode(), - selection->anchorOffset(), endOffset)) + // extend the selection + String directionStr; + if (direction == DIRECTION_FORWARD) + directionStr = "forward"; + else + directionStr = "backward"; + + String axisStr; + if (axis == AXIS_CHARACTER) + axisStr = "character"; + else if (axis == AXIS_WORD) + axisStr = "word"; + else + axisStr = "sentence"; + + selection->modify("extend", directionStr, axisStr); + + // Make sure the focus node is a text node in order to have the + // selection generate symmetric markup because the latter + // includes all nodes crossed by the selection. + Node* focusNode = selection->focusNode(); + if (focusNode->isElementNode()) { + int focusOffset = rangeCompliantChildOffset(focusNode, + selection->focusOffset()); + if (direction == DIRECTION_FORWARD) { + focusNode = + focusNode->childNodes()->item(focusOffset)->lastDescendant(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, body, DIRECTION_BACKWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, caretMaxOffset(focusNode), ec); + if (ec) return String(); - } - } else { - // enforce the focus node is a text node - if (selection->focusNode()->isElementNode()) { - if (!setSelection(selection, selection->anchorNode(), selection->anchorNode(), 0, - selection->anchorOffset())) + } else { + focusNode = + focusNode->childNodes()->item(focusOffset)->firstDescendant(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, body, DIRECTION_FORWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, 0, ec); + if (ec) return String(); } - // enforce the anchor node is a text node - if (selection->anchorNode()->isElementNode()) { - int endOffset = static_cast<Text*>(selection->focusNode())->length(); // cast is safe - if (!setSelection(selection, selection->focusNode(), selection->focusNode(), - selection->focusOffset(), endOffset)) - return String(); + } + + // Enforce that the selection does not cross anchor boundaries. This is + // a workaround for the asymmetric behavior of WebKit while crossing + // anchors. + // NOTE: The code is asymmetric since the logic is based off the common + // ancestor in both directions - backward and forward. + // TODO: Factor out common code repeated below. + anchorNode = selection->anchorNode(); + focusNode = selection->focusNode(); + if (anchorNode != focusNode + && anchorNode->isTextNode() + && focusNode->isTextNode()) { + Node* commonAncestor = Range::commonAncestorContainer(anchorNode, + focusNode); + Node* currentNode = 0; + bool selectionAdjusted = false; + if (direction == DIRECTION_FORWARD) { + // catch if the anchor is in a link but the focus is not + if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { + currentNode = anchorNode; + while (currentNode != commonAncestor) { + if (isVisible(currentNode) && isInputControl(currentNode)) { + focusNode = currentNode->lastDescendant(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = + traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, commonAncestor, + DIRECTION_BACKWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, caretMaxOffset(focusNode), + ec); + if (ec) + return String(); + selectionAdjusted = true; + break; + } + currentNode = currentNode->parentNode(); + } + // catch if there is a link between the anchor and focus + if (!selectionAdjusted) { + currentNode = anchorNode; + while (currentNode != focusNode) { + if (isVisible(currentNode) + && isInputControl(currentNode)) { + focusNode = currentNode; + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = + traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, commonAncestor, + DIRECTION_BACKWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, + caretMaxOffset(focusNode), ec); + if (ec) + return String(); + break; + } + currentNode = currentNode->traverseNextNode(); + } + } + } + } else { + // catch if the anchor is in a link but the focus is not + // NOTE: There is not such case in forward direction because + // it is implicitly covered the second case. Also the + // base position used for computing the the common + // ancestor which is asymmteric. + if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { + currentNode = anchorNode; + while (currentNode != commonAncestor) { + if (isVisible(currentNode) && isInputControl(currentNode)) { + focusNode = currentNode->firstDescendant(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = + traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, commonAncestor, + DIRECTION_FORWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, 0, ec); + if (ec) + return String(); + selectionAdjusted = true; + break; + } + currentNode = currentNode->parentNode(); + } + // catch if there is a link between the anchor and focus + if (!selectionAdjusted) { + currentNode = anchorNode; + while (currentNode != focusNode) { + if (isVisible(currentNode) + && isInputControl(currentNode)) { + focusNode = currentNode->traverseNextSibling(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = + traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, commonAncestor, + DIRECTION_FORWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, 0, ec); + if (ec) + return String(); + selectionAdjusted = true; + break; + } + currentNode = currentNode->traversePreviousNode(); + } + } + // catch if the focus is in a link but the anchor is not + if (!selectionAdjusted) { + currentNode = focusNode; + while (currentNode != commonAncestor) { + if (isVisible(currentNode) + && isInputControl(currentNode)) { + focusNode = currentNode->traverseNextSibling(); + if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { + focusNode = + traverseVisibleNonEmptyNonWhitespaceTextNode( + focusNode, commonAncestor, + DIRECTION_FORWARD); + if (!focusNode) + return String(); + } + selection->extend(focusNode, 0, ec); + if (ec) + return String(); + break; + } + currentNode = currentNode->parentNode(); + } + } + } } } + // make sure the selection is visible if (direction == DIRECTION_FORWARD) - scrollNodeIntoView(m_mainFrame, selection->focusNode()); + scrollNodeIntoView(m_mainFrame, selection->focusNode()); else - scrollNodeIntoView(m_mainFrame, selection->anchorNode()); + scrollNodeIntoView(m_mainFrame, selection->anchorNode()); - tryFocusInlineSelectionElement(selection); + // format markup for the visible content + PassRefPtr<Range> range = selection->getRangeAt(0, ec); + if (ec) + return String(); + IntRect bounds = range->boundingBox(); + selectAt(bounds.center().x(), bounds.center().y()); String markup = formatMarkup(selection).stripWhiteSpace(); LOGV("Selection markup: %s", markup.utf8().data()); + return markup; } -bool WebViewCore::setSelection(DOMSelection* selection, Text* textNode, int direction) +bool WebViewCore::isInputControl(Node* node) { - if (!textNode) - return false; - int offset = (direction == DIRECTION_FORWARD) ? 0 : textNode->length(); - if (!setSelection(selection, textNode, textNode, offset, offset)) - return false; - return true; + return (node->hasTagName(WebCore::HTMLNames::aTag) + || node->hasTagName(WebCore::HTMLNames::inputTag) + || node->hasTagName(WebCore::HTMLNames::buttonTag)); } -bool WebViewCore::setSelection(DOMSelection* selection, Node* startNode, Node* endNode, int startOffset, int endOffset) +int WebViewCore::rangeCompliantChildOffset(Node* parent, int offset) { - if (!selection || (!startNode && !endNode)) - return false; - ExceptionCode ec = 0; - PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec); - if (ec) { - ec = 0; - rangeRef = m_mainFrame->document()->createRange(); - } - if (startNode) - rangeRef->setStart(PassRefPtr<Node>(startNode), startOffset, ec); - if (ec) - return false; - if (endNode) - rangeRef->setEnd(PassRefPtr<Node>(endNode), endOffset, ec); - if (ec) - return false; - selection->removeAllRanges(); - selection->addRange(rangeRef.get()); - return true; + if (offset < 0) + return 0; + int lastChildIndex = parent->childNodes()->length() - 1; + if (offset > lastChildIndex) + return lastChildIndex; + return offset; } -Text* WebViewCore::traverseNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode, int direction) +bool WebViewCore::isVisibleNonEmptyNonWhitespaceTextNode(Node* node) +{ + if (!node || !node->isTextNode()) + return false; + Text* textNode = static_cast<Text*>(node); + return (isVisible(textNode) && textNode->length() > 0 + && !textNode->containsOnlyWhitespace()); +} + +Text* WebViewCore::traverseVisibleNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode, int direction) { Node* currentNode = fromNode; do { @@ -2314,18 +2518,10 @@ Text* WebViewCore::traverseNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* t currentNode = currentNode->traverseNextNode(toNode); else currentNode = currentNode->traversePreviousNode(toNode); - } while (currentNode && (!currentNode->isTextNode() - || isEmptyOrOnlyWhitespaceTextNode(currentNode))); + } while (currentNode && !isVisibleNonEmptyNonWhitespaceTextNode(currentNode)); return static_cast<Text*>(currentNode); } -bool WebViewCore::isEmptyOrOnlyWhitespaceTextNode(Node* node) -{ - return (node->isTextNode() - && (static_cast<Text*>(node)->length() == 0 - || static_cast<Text*>(node)->containsOnlyWhitespace())); -} - String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis) { // TODO: Add support of IFrames. @@ -2334,12 +2530,14 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in m_currentNodeDomNavigationAxis = selection->focusNode(); selection->empty(); if (m_currentNodeDomNavigationAxis->isTextNode()) - m_currentNodeDomNavigationAxis = m_currentNodeDomNavigationAxis->parentNode(); + m_currentNodeDomNavigationAxis = + m_currentNodeDomNavigationAxis->parentNode(); } if (!m_currentNodeDomNavigationAxis) m_currentNodeDomNavigationAxis = currentFocus(); if (!m_currentNodeDomNavigationAxis - || !CacheBuilder::validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis)) + || !CacheBuilder::validNode(m_mainFrame, m_mainFrame, + m_currentNodeDomNavigationAxis)) m_currentNodeDomNavigationAxis = body; Node* currentNode = m_currentNodeDomNavigationAxis; if (axis == AXIS_HEADING) { @@ -2350,19 +2548,21 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in currentNode = currentNode->traverseNextNode(body); else currentNode = currentNode->traversePreviousNode(body); - } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode) - || !isHeading(currentNode))); + } while (currentNode && (currentNode->isTextNode() + || !isVisible(currentNode) || !isHeading(currentNode))); } else if (axis == AXIS_PARENT_FIRST_CHILD) { if (direction == DIRECTION_FORWARD) { currentNode = currentNode->firstChild(); - while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode))) + while (currentNode && (currentNode->isTextNode() + || !isVisible(currentNode))) currentNode = currentNode->nextSibling(); } else { do { if (currentNode == body) return String(); currentNode = currentNode->parentNode(); - } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode))); + } while (currentNode && (currentNode->isTextNode() + || !isVisible(currentNode))); } } else if (axis == AXIS_SIBLING) { do { @@ -2373,7 +2573,8 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in return String(); currentNode = currentNode->previousSibling(); } - } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode))); + } while (currentNode && (currentNode->isTextNode() + || !isVisible(currentNode))); } else if (axis == AXIS_DOCUMENT) { currentNode = body; if (direction == DIRECTION_FORWARD) @@ -2385,8 +2586,6 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in if (currentNode) { m_currentNodeDomNavigationAxis = currentNode; scrollNodeIntoView(m_mainFrame, currentNode); - focusIfFocusableAndNotTextInput(selection, currentNode); - // TODO (svetoslavganov): Draw the selected text in the WebView - a-la-Android String selectionString = createMarkup(currentNode); LOGV("Selection markup: %s", selectionString.utf8().data()); return selectionString; @@ -2407,7 +2606,8 @@ bool WebViewCore::isHeading(Node* node) if (node->isElementNode()) { Element* element = static_cast<Element*>(node); - String roleAttribute = element->getAttribute(WebCore::HTMLNames::roleAttr).string(); + String roleAttribute = + element->getAttribute(WebCore::HTMLNames::roleAttr).string(); if (equalIgnoringCase(roleAttribute, "heading")) return true; } @@ -2417,60 +2617,129 @@ bool WebViewCore::isHeading(Node* node) bool WebViewCore::isVisible(Node* node) { - if (!node->isStyledElement()) - return false; - RenderStyle* style = node->computedStyle(); - return (style->display() != NONE && style->visibility() != HIDDEN); + // TODO: Use DOMWindow#getComputedStyle instead. + Node* body = m_mainFrame->document()->body(); + Node* currentNode = node; + while (currentNode && currentNode != body) { + RenderStyle* style = currentNode->computedStyle(); + if (style->display() == NONE || style->visibility() == HIDDEN) { + return false; + } + currentNode = currentNode->parentNode(); + } + return true; } String WebViewCore::formatMarkup(DOMSelection* selection) { ExceptionCode ec = 0; - PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec); - if (ec) { - LOGE("Error accessing the first selection range. Error code: %d", ec); - return String(); - } - // TODO: This breaks in certain cases - WebKit bug. Figure out and work around it - String markup = createMarkup(rangeRef.get()); - int fromIdx = markup.find("<span class=\"Apple-style-span\""); - while (fromIdx > -1) { - unsigned toIdx = markup.find(">"); - markup = markup.replace(fromIdx, toIdx - fromIdx + 1, ""); - markup = markup.replace("</span>", ""); - fromIdx = markup.find("<span class=\"Apple-style-span\""); + String markup = String(); + + PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec); + if (ec) + return markup; + + if (!wholeRange->startContainer() || !wholeRange->startContainer()) + return markup; + + // Since formatted markup contains invisible nodes it + // is created from the concatenation of the visible fragments. + Node* firstNode = wholeRange->firstNode(); + Node* pastLastNode = wholeRange->pastLastNode(); + Node* currentNode = firstNode; + PassRefPtr<Range> currentRange; + while (currentNode != pastLastNode) { + Node* nextNode = currentNode->traverseNextNode(); + if (!isVisible(currentNode)) { + if (currentRange) { + markup = markup + stripAppleSpanFromMarkup( + currentRange->toHTML()).utf8().data(); + currentRange = 0; + } + } else { + if (!currentRange) { + currentRange = selection->frame()->document()->createRange(); + if (ec) + return markup; + if (currentNode == firstNode) { + currentRange->setStart(wholeRange->startContainer(), + wholeRange->startOffset(), ec); + if (ec) + return markup; + } else { + currentRange->setStart(currentNode->parentNode(), + currentNode->nodeIndex(), ec); + if (ec) + return markup; + } + } + if (nextNode == pastLastNode) { + currentRange->setEnd(wholeRange->endContainer(), + wholeRange->endOffset(), ec); + if (ec) + return markup; + markup = markup + stripAppleSpanFromMarkup( + currentRange->toHTML()).utf8().data(); + } else { + if (currentNode->offsetInCharacters()) + currentRange->setEnd(currentNode, + currentNode->maxCharacterOffset(), ec); + else + currentRange->setEnd(currentNode->parentNode(), + currentNode->nodeIndex() + 1, ec); + if (ec) + return markup; + } + } + currentNode = nextNode; } return markup; } -void WebViewCore::tryFocusInlineSelectionElement(DOMSelection* selection) +String WebViewCore::stripAppleSpanFromMarkup(String markup) { - Node* currentNode = selection->anchorNode(); - Node* endNode = selection->focusNode(); - while (currentNode) { - if (focusIfFocusableAndNotTextInput(selection, currentNode)) - return; - currentNode = currentNode->traverseNextNode(endNode); - } + int fromIdx = markup.find("<span class=\"Apple-style-span\""); + while (fromIdx > -1) { + int toIdx = markup.find(">"); + markup = markup.replace(fromIdx, toIdx - fromIdx + 1, ""); + markup = markup.replace("</span>", ""); + fromIdx = markup.find("<span class=\"Apple-style-span\""); + } + return markup; } -bool WebViewCore::focusIfFocusableAndNotTextInput(DOMSelection* selection, Node* node) +void WebViewCore::selectAt(int x, int y) { - // TODO (svetoslavganov): Synchronize Android and WebKit focus - if (node->isFocusable()) { - WebCore::RenderObject* renderer = node->renderer(); - if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) { - // restore the selection after focus workaround for - // the FIXME in Element.cpp#updateFocusAppearance - ExceptionCode ec = 0; - PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec); - moveFocus(m_mainFrame, node); - if (rangeRef) - selection->addRange(rangeRef.get()); - return true; + JNIEnv* env = JSC::Bindings::getJNIEnv(); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_selectAt, + x, y); + checkException(env); +} + +Node* WebViewCore::getFirstIntermediaryInputOrButton(Node* fromNode, Node* toNode) +{ + // do bidirectional traversal to catch the case in which + // the toNode is a descendant of a control but the fromNode + // is not and the other way around + Node* currentNode = fromNode->traverseNextNode(); + while (currentNode && currentNode != toNode) { + if (isVisible(currentNode) + && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) + || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { + return currentNode; } + currentNode = currentNode->traverseNextNode(); } - return false; + currentNode = fromNode->traverseNextNodePostOrder(); + while (currentNode && currentNode != toNode) { + if (isVisible(currentNode) + && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) + || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { + return currentNode; + } + currentNode = currentNode->traverseNextNodePostOrder(); + } + return 0; } void WebViewCore::deleteSelection(int start, int end, int textGeneration) @@ -2964,7 +3233,7 @@ static void scrollLayer(WebCore::RenderObject* renderer, WebCore::IntPoint* pos) return; // The cache uses absolute coordinates when clicking on nodes and it assumes // the layer is not scrolled. - layer->scrollToOffset(0, 0, false, false); + layer->scrollToOffset(0, 0, true, false); WebCore::IntRect absBounds = renderer->absoluteBoundingBoxRect(); // Do not include the outline when moving the node's bounds. @@ -2974,7 +3243,7 @@ static void scrollLayer(WebCore::RenderObject* renderer, WebCore::IntPoint* pos) absBounds.move(-layerBounds.x(), -layerBounds.y()); // Scroll the layer to the node's position. - layer->scrollToOffset(absBounds.x(), absBounds.y(), false, true); + layer->scrollToOffset(absBounds.x(), absBounds.y(), true, true); // Update the mouse position to the layer offset. pos->move(-layer->scrollXOffset(), -layer->scrollYOffset()); @@ -3659,17 +3928,6 @@ static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint gr return wtfStringToJstring(env, selectionString); } -static jstring MoveSelection(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); -#endif - WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - String selectionString = viewImpl->moveSelection((WebCore::Frame*) framePtr, - (WebCore::Node*) nodePtr); - return wtfStringToJstring(env, selectionString); -} - static void ReplaceTextfieldText(JNIEnv *env, jobject obj, jint oldStart, jint oldEnd, jstring replace, jint start, jint end, jint textGeneration) @@ -4258,8 +4516,6 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) SetSelection } , { "nativeModifySelection", "(II)Ljava/lang/String;", (void*) ModifySelection }, - { "nativeMoveSelection", "(II)Ljava/lang/String;", - (void*) MoveSelection }, { "nativeDeleteSelection", "(III)V", (void*) DeleteSelection } , { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V", diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h index 611ee59..028a0c7 100644 --- a/WebKit/android/jni/WebViewCore.h +++ b/WebKit/android/jni/WebViewCore.h @@ -665,17 +665,19 @@ namespace android { // below are members responsible for accessibility support String modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int granularity); String modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int granularity); - Text* traverseNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode ,int direction); + Text* traverseVisibleNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode ,int direction); bool isVisible(Node* node); bool isHeading(Node* node); - bool isEmptyOrOnlyWhitespaceTextNode(Node* node); String formatMarkup(DOMSelection* selection); - void tryFocusInlineSelectionElement(DOMSelection* selection); - bool focusIfFocusableAndNotTextInput(DOMSelection* selection, Node* node); - bool setSelection(DOMSelection* selection, Text* textNode, int direction); - bool setSelection(DOMSelection* selection, Node* startNode, Node* endNode, int startOffset, int endOffset); + void selectAt(int x, int y); Node* m_currentNodeDomNavigationAxis; void scrollNodeIntoView(Frame* frame, Node* node); + bool isVisibleNonEmptyNonWhitespaceTextNode(Node* node); + String stripAppleSpanFromMarkup(String markup); + int rangeCompliantChildOffset(Node* parent, int offset); + Node* getFirstIntermediaryInputOrButton(Node* fromNode, Node* toNode); + bool isInputControl(Node* node); + #if ENABLE(TOUCH_EVENTS) bool m_forwardingTouchEvents; #endif diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp index 40b2711..135bacc 100644 --- a/WebKit/android/nav/CacheBuilder.cpp +++ b/WebKit/android/nav/CacheBuilder.cpp @@ -36,7 +36,6 @@ #include "FrameTree.h" #include "FrameView.h" //#include "GraphicsContext.h" -#include "GraphicsLayerAndroid.h" #include "HTMLAreaElement.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" @@ -47,6 +46,7 @@ #include "HTMLTextAreaElement.h" #include "InlineTextBox.h" #include "KURL.h" +#include "LayerAndroid.h" #include "PluginView.h" #include "RegisteredEventListener.h" #include "RenderImage.h" @@ -508,9 +508,8 @@ void CacheBuilder::Debug::groups() { 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; + GraphicsLayer* grLayer = back ? back->graphicsLayer() : 0; + LayerAndroid* aLayer = grLayer ? grLayer->platformLayer() : 0; const SkPicture* pict = aLayer ? aLayer->picture() : 0; const IntRect& r = renderer->absoluteBoundingBoxRect(); snprintf(scratch, sizeof(scratch), "// layer:%p back:%p" @@ -2906,11 +2905,12 @@ void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker, RenderLayerBacking* back = layer->backing(); if (!back) return; - GraphicsLayerAndroid* grLayer = static_cast - <GraphicsLayerAndroid*>(back->graphicsLayer()); + GraphicsLayer* grLayer = back->graphicsLayer(); + if (back->hasContentsLayer()) + grLayer = back->foregroundLayer(); if (!grLayer) return; - LayerAndroid* aLayer = grLayer->contentLayer(); + LayerAndroid* aLayer = grLayer->platformLayer(); if (!aLayer) return; IntPoint scroll(layer->scrollXOffset(), layer->scrollYOffset()); diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index 683c2a3..046b354 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -114,7 +114,6 @@ struct JavaGlue { jmethodID m_sendMoveFocus; jmethodID m_sendMoveMouse; jmethodID m_sendMoveMouseIfLatest; - jmethodID m_sendMoveSelection; jmethodID m_sendMotionUp; jmethodID m_domChangedFocus; jmethodID m_getScaledMaxXScroll; @@ -149,7 +148,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) : m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V"); m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V"); m_javaGlue.m_sendMoveMouseIfLatest = GetJMethod(env, clazz, "sendMoveMouseIfLatest", "(Z)V"); - m_javaGlue.m_sendMoveSelection = GetJMethod(env, clazz, "sendMoveSelection", "(II)V"); m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIIII)V"); m_javaGlue.m_domChangedFocus = GetJMethod(env, clazz, "domChangedFocus", "()V"); m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I"); @@ -847,8 +845,6 @@ bool moveCursor(int keyCode, int count, bool ignoreScroll) bool clearTextEntry = cachedNode != focus && focus && cachedNode->nodePointer() != focus->nodePointer() && focus->isTextInput(); sendMoveMouseIfLatest(clearTextEntry); - sendMoveSelection((WebCore::Frame*) cachedFrame->framePointer(), - (WebCore::Node*) cachedNode->nodePointer()); } else { int docHeight = root->documentHeight(); int docWidth = root->documentWidth(); @@ -910,7 +906,6 @@ void selectBestAt(const WebCore::IntRect& rect) if (!root) return; const CachedNode* node = findAt(root, rect, &frame, &rx, &ry); - if (!node) { DBG_NAV_LOGD("no nodes found root=%p", root); m_viewImpl->m_hasCursorBounds = false; @@ -928,8 +923,6 @@ void selectBestAt(const WebCore::IntRect& rect) sendMoveMouseIfLatest(false); if (!node) return; - sendMoveSelection((WebCore::Frame*) frame->framePointer(), - (WebCore::Node*) node->nodePointer()); } const CachedNode* m_cacheHitNode; @@ -1022,8 +1015,8 @@ static const ScrollableLayerAndroid* findScrollableLayer( x -= bounds.fLeft; y -= bounds.fTop; int count = parent->countChildren(); - for (int i = 0; i < count; i++) { - const LayerAndroid* child = parent->getChild(i); + while (count--) { + const LayerAndroid* child = parent->getChild(count); const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y, foundBounds); if (result) { @@ -1217,15 +1210,6 @@ void sendMoveMouseIfLatest(bool clearTextEntry) checkException(env); } -void sendMoveSelection(WebCore::Frame* frame, WebCore::Node* node) -{ - DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", frame, node); - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue.object(env).get(), - m_javaGlue.m_sendMoveSelection, (jint) frame, (jint) node); - checkException(env); -} - void sendMotionUp( WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y, int scrollY) { @@ -1960,6 +1944,16 @@ static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect) view->selectBestAt(rect); } +static void nativeSelectAt(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::IntRect rect = IntRect(x, y , 1, 1); + view->selectBestAt(rect); + if (view->hasCursorNode()) + view->showCursorUntimed(); +} + static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer) { SkRect r; @@ -2516,6 +2510,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSelectAll }, { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V", (void*) nativeSelectBestAt }, + { "nativeSelectAt", "(II)V", + (void*) nativeSelectAt }, { "nativeSelectionX", "()I", (void*) nativeSelectionX }, { "nativeSelectionY", "()I", diff --git a/WebKit/android/plugins/PluginTimer.cpp b/WebKit/android/plugins/PluginTimer.cpp index 23cac77..9ed6a80 100644 --- a/WebKit/android/plugins/PluginTimer.cpp +++ b/WebKit/android/plugins/PluginTimer.cpp @@ -48,6 +48,7 @@ namespace WebCore { } m_prev = 0; *list = this; + relaxAdoptionRequirement(); } PluginTimer::~PluginTimer() |