From dea0c131566f424923425970fe5621305d136e5a Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 25 Jan 2012 09:20:19 -0800 Subject: Webkit text selection Change-Id: I8e2e400940c7c84055aeb2624958aaf47804900b --- Source/WebCore/editing/SelectionController.cpp | 3 + .../WebCore/platform/graphics/android/GLExtras.cpp | 27 +- .../WebCore/platform/graphics/android/GLExtras.h | 10 +- .../graphics/android/GraphicsLayerAndroid.h | 2 + Source/WebCore/platform/graphics/android/Layer.cpp | 8 +- Source/WebCore/platform/graphics/android/Layer.h | 8 +- .../platform/graphics/android/LayerAndroid.cpp | 6 +- .../platform/graphics/android/LayerAndroid.h | 3 +- .../platform/graphics/android/android_graphics.cpp | 2 +- .../platform/graphics/android/android_graphics.h | 2 +- Source/WebKit/android/jni/AndroidHitTestResult.cpp | 39 +- Source/WebKit/android/jni/WebCoreJni.cpp | 23 + Source/WebKit/android/jni/WebCoreJni.h | 3 + Source/WebKit/android/jni/WebViewCore.cpp | 300 ++- Source/WebKit/android/jni/WebViewCore.h | 10 + Source/WebKit/android/nav/DrawExtra.h | 5 +- Source/WebKit/android/nav/FindCanvas.cpp | 2 +- Source/WebKit/android/nav/FindCanvas.h | 2 +- Source/WebKit/android/nav/SelectText.cpp | 1934 +------------------- Source/WebKit/android/nav/SelectText.h | 110 +- Source/WebKit/android/nav/WebView.cpp | 406 ++-- 21 files changed, 543 insertions(+), 2362 deletions(-) diff --git a/Source/WebCore/editing/SelectionController.cpp b/Source/WebCore/editing/SelectionController.cpp index c5a33d3..3eec640 100644 --- a/Source/WebCore/editing/SelectionController.cpp +++ b/Source/WebCore/editing/SelectionController.cpp @@ -1562,6 +1562,9 @@ bool SelectionController::isFocusedAndActive() const void SelectionController::updateAppearance() { +#if PLATFORM(ANDROID) + return; +#endif ASSERT(!m_isDragCaretController); #if ENABLE(TEXT_CARET) diff --git a/Source/WebCore/platform/graphics/android/GLExtras.cpp b/Source/WebCore/platform/graphics/android/GLExtras.cpp index 9ad369a..37f598e 100644 --- a/Source/WebCore/platform/graphics/android/GLExtras.cpp +++ b/Source/WebCore/platform/graphics/android/GLExtras.cpp @@ -52,10 +52,10 @@ // Touch ring border width. This is doubled if the ring is not pressed #define RING_BORDER_WIDTH 1 -// Color of the ring is 0x6633b5e5 (copied from framework's holo_light) -#define COLOR_HOLO_LIGHT &m_lightRingTexture, 0x33, 0xb5, 0xe5, 0.4f -// Color of the ring is 0x660099cc (copied from framework's holo_dark) -#define COLOR_HOLO_DARK &m_darkRingTexture, 0x00, 0x99, 0xcc, 0.6f +// Color of the ring copied from framework's holo_light +#define COLOR_HOLO_LIGHT 0xFF33b5e5, .4f +// Color of the ring copied from framework's holo_dark +#define COLOR_HOLO_DARK 0xFF0099cc, .4f // Put a cap on the number of matches to draw. If the current page has more // matches than this, only draw the focused match. This both prevents clutter // on the page and keeps the performance happy @@ -65,8 +65,6 @@ GLExtras::GLExtras() : m_findOnPage(0) , m_ring(0) , m_drawExtra(0) - , m_lightRingTexture(-1) - , m_darkRingTexture(-1) , m_viewport() { } @@ -75,22 +73,20 @@ GLExtras::~GLExtras() { } -void GLExtras::drawRing(SkRect& srcRect, int* texture, int r, int g, int b, float a, +void GLExtras::drawRing(SkRect& srcRect, Color color, float alpha, const TransformationMatrix* drawMat) { - if (*texture == -1) - *texture = GLUtils::createSampleColorTexture(r, g, b); - if (srcRect.fRight <= srcRect.fLeft || srcRect.fBottom <= srcRect.fTop) { // Invalid rect, reject it return; } XLOG("drawQuad [%fx%f, %f, %f]", srcRect.fLeft, srcRect.fTop, srcRect.width(), srcRect.height()); - if (drawMat) - TilesManager::instance()->shader()->drawLayerQuad(*drawMat, srcRect, *texture, a); - else - TilesManager::instance()->shader()->drawQuad(srcRect, *texture, a); + if (drawMat) { + TilesManager::instance()->shader()->drawLayerQuad(*drawMat, srcRect, 0, + alpha, false, 0, color); + } else + TilesManager::instance()->shader()->drawQuad(srcRect, 0, alpha, color); } void GLExtras::drawRegion(const SkRegion& region, bool fill, @@ -234,7 +230,6 @@ void GLExtras::drawGL(const LayerAndroid* layer) else if (m_drawExtra == m_findOnPage) drawFindOnPage(layer); else - XLOGC("m_drawExtra %p is unknown! (cursor: %p, find: %p", - m_drawExtra, m_ring, m_findOnPage); + m_drawExtra->drawGL(this, layer); } } diff --git a/Source/WebCore/platform/graphics/android/GLExtras.h b/Source/WebCore/platform/graphics/android/GLExtras.h index 5ccf4cc..09e346c 100644 --- a/Source/WebCore/platform/graphics/android/GLExtras.h +++ b/Source/WebCore/platform/graphics/android/GLExtras.h @@ -26,6 +26,7 @@ #ifndef GLExtras_h #define GLExtras_h +#include "Color.h" #include "SkRect.h" #include "SkRegion.h" @@ -53,19 +54,18 @@ public: void setDrawExtra(android::DrawExtra* extra) { m_drawExtra = extra; } void setViewport(const SkRect & viewport) { m_viewport = viewport; } -private: - void drawRing(SkRect& srcRect, int* texture, int r, int g, int b, float a, - const TransformationMatrix* drawMat); void drawRegion(const SkRegion& region, bool fill, bool drawBorder, const TransformationMatrix* drawMat, bool useDark = false); + +private: + void drawRing(SkRect& srcRect, Color color, float alpha, + const TransformationMatrix* drawMat); void drawCursorRings(const LayerAndroid* layer); void drawFindOnPage(const LayerAndroid* layer); android::FindOnPage* m_findOnPage; android::CursorRing* m_ring; android::DrawExtra* m_drawExtra; - int m_lightRingTexture; - int m_darkRingTexture; SkRect m_viewport; }; diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h index c34aed8..4c049cd 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.h @@ -25,6 +25,7 @@ #include "GraphicsLayerClient.h" #include "LayerAndroid.h" #include "RefPtr.h" +#include "ScrollableLayerAndroid.h" #include "SkBitmapRef.h" #include "Vector.h" @@ -122,6 +123,7 @@ public: void notifyClientAnimationStarted(); LayerAndroid* contentLayer() { return m_contentLayer; } + ScrollableLayerAndroid* foregroundLayer() { return m_foregroundLayer; } static int instancesCount(); diff --git a/Source/WebCore/platform/graphics/android/Layer.cpp b/Source/WebCore/platform/graphics/android/Layer.cpp index 9280461..f58d648 100644 --- a/Source/WebCore/platform/graphics/android/Layer.cpp +++ b/Source/WebCore/platform/graphics/android/Layer.cpp @@ -158,13 +158,13 @@ void Layer::localToAncestor(const Layer* ancestor, SkMatrix* matrix) const { /////////////////////////////////////////////////////////////////////////////// -void Layer::onDraw(SkCanvas*, SkScalar opacity) { +void Layer::onDraw(SkCanvas*, SkScalar opacity, android::DrawExtra* extra) { // SkDebugf("----- no onDraw for %p\n", this); } #include "SkString.h" -void Layer::draw(SkCanvas* canvas, SkScalar opacity) { +void Layer::draw(SkCanvas* canvas, android::DrawExtra* extra, SkScalar opacity) { #if 0 SkString str1, str2; // getMatrix().toDumpString(&str1); @@ -193,7 +193,7 @@ void Layer::draw(SkCanvas* canvas, SkScalar opacity) { canvas->concat(tmp); } - onDraw(canvas, opacity); + onDraw(canvas, opacity, extra); #ifdef DEBUG_DRAW_LAYER_BOUNDS { @@ -213,7 +213,7 @@ void Layer::draw(SkCanvas* canvas, SkScalar opacity) { if (count > 0) { canvas->concat(getChildrenMatrix()); for (int i = 0; i < count; i++) { - getChild(i)->draw(canvas, opacity); + getChild(i)->draw(canvas, extra, opacity); } } } diff --git a/Source/WebCore/platform/graphics/android/Layer.h b/Source/WebCore/platform/graphics/android/Layer.h index 5200a3d..876ca24 100644 --- a/Source/WebCore/platform/graphics/android/Layer.h +++ b/Source/WebCore/platform/graphics/android/Layer.h @@ -17,6 +17,7 @@ #ifndef Layer_DEFINED #define Layer_DEFINED +#include "DrawExtra.h" #include "TestExport.h" #include "SkRefCnt.h" #include "SkTDArray.h" @@ -165,17 +166,14 @@ public: // paint method virtual bool drawCanvas(SkCanvas*) { return false; } - void draw(SkCanvas*, SkScalar opacity); - void draw(SkCanvas* canvas) { - this->draw(canvas, SK_Scalar1); - } + void draw(SkCanvas*, android::DrawExtra* extra, SkScalar opacity = SK_Scalar1); void setHasOverflowChildren(bool value) { m_hasOverflowChildren = value; } virtual bool contentIsScrollable() const { return false; } protected: - virtual void onDraw(SkCanvas*, SkScalar opacity); + virtual void onDraw(SkCanvas*, SkScalar opacity, android::DrawExtra* extra); bool m_hasOverflowChildren; diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp index 88e419d..79c02eb 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -1022,7 +1022,7 @@ bool LayerAndroid::drawCanvas(SkCanvas* canvas) layerRect.fTop = 0; layerRect.fRight = getWidth(); layerRect.fBottom = getHeight(); - onDraw(canvas, m_drawOpacity); + onDraw(canvas, m_drawOpacity, 0); } // When the layer is dirty, the UI thread should be notified to redraw. @@ -1133,7 +1133,7 @@ void LayerAndroid::contentDraw(SkCanvas* canvas) } } -void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity) +void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity, android::DrawExtra* extra) { if (m_haveClip) { SkRect r; @@ -1163,6 +1163,8 @@ void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity) ImagesManager::instance()->releaseImage(m_imageCRC); } contentDraw(canvas); + if (extra) + extra->draw(canvas, this); } SkPicture* LayerAndroid::recordContext() diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.h b/Source/WebCore/platform/graphics/android/LayerAndroid.h index 578c2ff..d33eea1 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.h @@ -199,6 +199,7 @@ public: setShouldInheritFromRootTransform(true); } + const IntPoint& scrollOffset() const { return m_offset; } void setScrollOffset(IntPoint offset) { m_offset = offset; } void setBackgroundColor(SkColor color); void setMaskLayer(LayerAndroid*); @@ -315,7 +316,7 @@ public: bool isReady(); protected: - virtual void onDraw(SkCanvas*, SkScalar opacity); + virtual void onDraw(SkCanvas*, SkScalar opacity, android::DrawExtra* extra); IntPoint m_offset; TransformationMatrix m_drawTransform; diff --git a/Source/WebCore/platform/graphics/android/android_graphics.cpp b/Source/WebCore/platform/graphics/android/android_graphics.cpp index 6b8725e..d76d581 100644 --- a/Source/WebCore/platform/graphics/android/android_graphics.cpp +++ b/Source/WebCore/platform/graphics/android/android_graphics.cpp @@ -57,7 +57,7 @@ CursorRing::CursorRing(WebViewCore* core) // The CSS values for the inner and outer widths may be specified as fractions #define WIDTH_SCALE 0.0625f // 1/16, to offset the scale in CSSStyleSelector -void CursorRing::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) +void CursorRing::drawLegacy(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) { if (!m_lastBounds.isEmpty()) { *inval = m_lastBounds; diff --git a/Source/WebCore/platform/graphics/android/android_graphics.h b/Source/WebCore/platform/graphics/android/android_graphics.h index bd08a6e..68207d7 100644 --- a/Source/WebCore/platform/graphics/android/android_graphics.h +++ b/Source/WebCore/platform/graphics/android/android_graphics.h @@ -54,7 +54,7 @@ class CursorRing : public DrawExtra { public: CursorRing(WebViewCore* core); virtual ~CursorRing() {} - virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ); + virtual void drawLegacy(SkCanvas* , LayerAndroid* , IntRect* ); void setIsButton(const CachedNode* ); bool setup(); WTF::Vector& rings() { return m_rings; } diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.cpp b/Source/WebKit/android/jni/AndroidHitTestResult.cpp index 64408da..fd2fc25 100644 --- a/Source/WebKit/android/jni/AndroidHitTestResult.cpp +++ b/Source/WebKit/android/jni/AndroidHitTestResult.cpp @@ -31,9 +31,14 @@ #include "Element.h" #include "HitTestResult.h" #include "KURL.h" +#include "LayerAndroid.h" #include "PlatformString.h" +#include "Range.h" +#include "RenderLayer.h" +#include "RenderLayerBacking.h" #include "RenderObject.h" #include "WebCoreJni.h" +#include "WebViewCore.h" #include #include @@ -45,8 +50,6 @@ using namespace WebCore; static bool gJniInitialized = false; static struct JavaGlue { - jmethodID m_rectInit; - jmethodID m_hitTestInit; jfieldID m_hitTestLinkUrl; jfieldID m_hitTestAnchorText; @@ -75,9 +78,6 @@ static void InitJni(JNIEnv* env) jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); - gJavaGlue.m_rectInit = env->GetMethodID(rectClass, "", "(IIII)V"); - ALOG_ASSERT(gJavaGlue.m_rectInit, "Could not find init method on Rect"); - gJavaGlue.m_hitTestInit = env->GetMethodID(hitTestClass, "", "()V"); ALOG_ASSERT(gJavaGlue.m_hitTestInit, "Could not find init method on android/webkit/WebViewCore$WebKitHitTest"); @@ -115,6 +115,13 @@ void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str) env->DeleteLocalRef(jstr); } +void setRectArray(JNIEnv* env, jobject obj, jfieldID field, Vector &rects) +{ + jobjectArray array = intRectVectorToRectArray(env, rects); + env->SetObjectField(obj, field, array); + env->DeleteLocalRef(array); +} + // Some helper macros specific to setting hitTest fields #define _SET(jtype, jfield, value) env->Set ## jtype ## Field(hitTest, gJavaGlue.m_hitTest ## jfield, value) #define SET_BOOL(jfield, value) _SET(Boolean, jfield, value) @@ -124,31 +131,17 @@ void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str) jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) { InitJni(env); - jclass rectClass = env->FindClass("android/graphics/Rect"); - ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); - jobjectArray array = env->NewObjectArray(m_highlightRects.size(), rectClass, 0); - ALOG_ASSERT(array, "Could not create a Rect array"); - - for (size_t i = 0; i < m_highlightRects.size(); i++) { - jobject rect = env->NewObject(rectClass, gJavaGlue.m_rectInit, - m_highlightRects[i].x(), m_highlightRects[i].y(), - m_highlightRects[i].maxX(), m_highlightRects[i].maxY()); - if (rect) { - env->SetObjectArrayElement(array, i, rect); - env->DeleteLocalRef(rect); - } - } - - TextDirection titleTextDirection; jobject hitTest = env->NewObject(hitTestClass, gJavaGlue.m_hitTestInit); - env->SetObjectField(hitTest, gJavaGlue.m_hitTestTouchRects, array); + setRectArray(env, hitTest, gJavaGlue.m_hitTestTouchRects, m_highlightRects); + SET_BOOL(Editable, m_hitTestResult.isContentEditable()); SET_STRING(LinkUrl, m_hitTestResult.absoluteLinkURL().string()); SET_STRING(ImageUrl, m_hitTestResult.absoluteImageURL().string()); SET_STRING(AltDisplayString, m_hitTestResult.altDisplayString()); + TextDirection titleTextDirection; SET_STRING(Title, m_hitTestResult.title(titleTextDirection)); if (m_hitTestResult.URLElement()) { Element* urlElement = m_hitTestResult.URLElement(); @@ -159,6 +152,8 @@ jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) } } + env->DeleteLocalRef(hitTestClass); + return hitTest; } diff --git a/Source/WebKit/android/jni/WebCoreJni.cpp b/Source/WebKit/android/jni/WebCoreJni.cpp index 10b3e95..65b6d20 100644 --- a/Source/WebKit/android/jni/WebCoreJni.cpp +++ b/Source/WebKit/android/jni/WebCoreJni.cpp @@ -26,7 +26,9 @@ #define LOG_TAG "webcoreglue" #include "config.h" +#include "IntRect.h" #include "WebCoreJni.h" +#include "wtf/Vector.h" #include "NotImplemented.h" #include @@ -114,4 +116,25 @@ jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZero #endif +jobjectArray intRectVectorToRectArray(JNIEnv* env, Vector& rects) +{ + jclass rectClass = env->FindClass("android/graphics/Rect"); + ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); + jmethodID rectInit = env->GetMethodID(rectClass, "", "(IIII)V"); + ALOG_ASSERT(rectInit, "Could not find init method on Rect"); + jobjectArray array = env->NewObjectArray(rects.size(), rectClass, 0); + ALOG_ASSERT(array, "Could not create a Rect array"); + for (size_t i = 0; i < rects.size(); i++) { + jobject rect = env->NewObject(rectClass, rectInit, + rects[i].x(), rects[i].y(), + rects[i].maxX(), rects[i].maxY()); + if (rect) { + env->SetObjectArrayElement(array, i, rect); + env->DeleteLocalRef(rect); + } + } + env->DeleteLocalRef(rectClass); + return array; +} + } diff --git a/Source/WebKit/android/jni/WebCoreJni.h b/Source/WebKit/android/jni/WebCoreJni.h index 0f77cc6..7a46f7b 100644 --- a/Source/WebKit/android/jni/WebCoreJni.h +++ b/Source/WebKit/android/jni/WebCoreJni.h @@ -27,6 +27,7 @@ #define WebCoreJni_h #include "ChromiumIncludes.h" +#include "IntRect.h" #include "PlatformString.h" #include @@ -91,6 +92,8 @@ std::string jstringToStdString(JNIEnv*, jstring); jstring stdStringToJstring(JNIEnv*, const std::string&, bool validOnZeroLength = false); #endif +jobjectArray intRectVectorToRectArray(JNIEnv*, Vector&); + } #endif diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index 027ab1e..cb08171 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -105,6 +105,7 @@ #include "RuntimeEnabledFeatures.h" #include "SchemeRegistry.h" #include "SelectionController.h" +#include "SelectText.h" #include "Settings.h" #include "SkANP.h" #include "SkTemplates.h" @@ -124,6 +125,7 @@ #include "autofill/WebAutofill.h" #include "htmlediting.h" #include "markup.h" +#include "visible_units.h" #include #include @@ -410,7 +412,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V"); m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V"); m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V"); - m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V"); + m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIIII)V"); m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V"); m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V"); m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V"); @@ -1658,6 +1660,226 @@ static IntRect getAbsoluteBoundingBox(Node* node) { return rect; } +VisiblePosition WebViewCore::visiblePositionForContentPoint(int x, int y) +{ + IntPoint point(x, y); + + // Hit test of this kind required for this to work inside input fields + HitTestRequest request(HitTestRequest::Active | HitTestRequest::MouseMove | HitTestRequest::ReadOnly); + + // Look for the inner-most frame containing the hit. Its document + // contains the document with the selected text. + Frame* frame = m_mainFrame; + Frame* hitFrame = m_mainFrame; + Node* node = 0; + IntPoint localPoint = point; + do { + HitTestResult result(localPoint); + frame = hitFrame; + frame->document()->renderView()->layer()->hitTest(request, result); + node = result.innerNode(); + if (!node) + return VisiblePosition(); + + if (node->isFrameOwnerElement()) + hitFrame = static_cast(node)->contentFrame(); + localPoint = result.localPoint(); + } while (hitFrame && hitFrame != frame); + + Element* element = node->parentElement(); + if (!node->inDocument() && element && element->inDocument()) + node = element; + + RenderObject* renderer = node->renderer(); + return renderer->positionForPoint(localPoint); +} + +void WebViewCore::selectWordAt(int x, int y) +{ + IntPoint point(x, y); + + // Hit test of this kind required for this to work inside input fields + HitTestRequest request(HitTestRequest::Active); + HitTestResult result(point); + m_mainFrame->document()->renderView()->layer()->hitTest(request, result); + + // Matching the logic in MouseEventWithHitTestResults::targetNode() + Node* node = result.innerNode(); + if (!node) + return; + Element* element = node->parentElement(); + if (!node->inDocument() && element && element->inDocument()) + node = element; + + SelectionController* sc = m_mainFrame->selection(); + if (!sc->contains(point) && (node->isContentEditable() || node->isTextNode()) && !result.isLiveLink() + && node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true))) { + VisiblePosition pos(node->renderer()->positionForPoint(result.localPoint())); + selectWordAroundPosition(node->document()->frame(), pos); + } +} + +void WebViewCore::selectWordAroundPosition(Frame* frame, VisiblePosition pos) +{ + VisibleSelection selection(pos); + selection.expandUsingGranularity(WordGranularity); + + if (frame->selection()->shouldChangeSelection(selection)) { + bool allWhitespaces = true; + RefPtr firstRange = selection.firstRange(); + String text = firstRange.get() ? firstRange->text() : ""; + for (size_t i = 0; i < text.length(); ++i) { + if (!isSpaceOrNewline(text[i])) { + allWhitespaces = false; + break; + } + } + + if (allWhitespaces) { + VisibleSelection emptySelection(selection.visibleStart(), selection.visibleStart()); + frame->selection()->setSelection(emptySelection); + } + frame->selection()->setSelection(selection); + } +} + +int WebViewCore::platformLayerIdFromNode(Node* node, LayerAndroid** outLayer) +{ + if (!node || !node->renderer()) + return -1; + RenderLayer* renderLayer = node->renderer()->enclosingLayer(); + if (!renderLayer || !renderLayer->isComposited()) + return -1; + GraphicsLayer* graphicsLayer = renderLayer->backing()->graphicsLayer(); + if (!graphicsLayer) + return -1; + GraphicsLayerAndroid* agl = static_cast(graphicsLayer); + LayerAndroid* layer = agl->foregroundLayer(); + if (!layer) + layer = agl->contentLayer(); + if (!layer) + return -1; + if (outLayer) + *outLayer = layer; + return layer->uniqueId(); +} + +void WebViewCore::layerToAbsoluteOffset(const LayerAndroid* layer, IntPoint& offset) +{ + while (layer) { + const SkPoint& pos = layer->getPosition(); + offset.move(pos.fX, pos.fY); + const IntPoint& scroll = layer->scrollOffset(); + offset.move(-scroll.x(), -scroll.y()); + layer = static_cast(layer->getParent()); + } +} + +void setCaretInfo(const VisiblePosition& pos, SelectText::HandleId handle, + SelectText* target) +{ + IntPoint offset; + LayerAndroid* layer = 0; + IntRect rect = pos.absoluteCaretBounds(); + int layerId = WebViewCore::platformLayerIdFromNode(pos.deepEquivalent().anchorNode(), &layer); + WebViewCore::layerToAbsoluteOffset(layer, offset); + rect.move(-offset.x(), -offset.y()); + target->setCaretRect(handle, rect); + target->setCaretLayerId(handle, layerId); +} + +SelectText* WebViewCore::createSelectText(const VisibleSelection& selection) +{ + if (!selection.isRange()) + return 0; + + RefPtr range = selection.firstRange(); + Node* startContainer = range->startContainer(); + Node* endContainer = range->endContainer(); + + if (!startContainer || !endContainer) + return 0; + + SelectText* selectTextContainer = new SelectText(); + + Node* stopNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != stopNode; node = node->traverseNextNode()) { + RenderObject* r = node->renderer(); + if (!r || !r->isText() || r->style()->visibility() != VISIBLE) + continue; + RenderText* renderText = toRenderText(r); + int startOffset = node == startContainer ? range->startOffset() : 0; + int endOffset = node == endContainer ? range->endOffset() : numeric_limits::max(); + LayerAndroid* layer = 0; + int layerId = platformLayerIdFromNode(node, &layer); + SkRegion* region = selectTextContainer->getHightlightRegionsForLayer(layerId); + bool needsSet = false; + if (!region) + selectTextContainer->setHighlightRegionsForLayer(layerId, region = new SkRegion()); + Vector rects; + renderText->absoluteRectsForRange(rects, startOffset, endOffset, true); + IntPoint offset; + layerToAbsoluteOffset(layer, offset); + for (size_t i = 0; i < rects.size(); i++) { + IntRect& r = rects.at(i); + r.move(-offset.x(), -offset.y()); + region->op(r.x(), r.y(), r.maxX(), r.maxY(), SkRegion::kUnion_Op); + } + } + + IntRect caretRect; + int layerId; + selectTextContainer->setBaseFirst(selection.isBaseFirst()); + ALOGD("isBaseFirst: %s", selectTextContainer->isBaseFirst() ? "true" : "false"); + setCaretInfo(selection.visibleStart(), SelectText::StartHandle, selectTextContainer); + setCaretInfo(selection.visibleEnd(), SelectText::EndHandle, selectTextContainer); + + selectTextContainer->setText(range->text()); + + return selectTextContainer; +} + +void WebViewCore::selectText(int startX, int startY, int endX, int endY) +{ + SelectionController* sc = m_mainFrame->selection(); + VisiblePosition startPosition(visiblePositionForContentPoint(startX, startY)); + VisiblePosition endPosition(visiblePositionForContentPoint(endX, endY)); + + if (startPosition.isNull() || endPosition.isNull() || startPosition == endPosition) { + return; + } + + // Ensure startPosition is before endPosition + if (comparePositions(startPosition, endPosition) > 0) + swap(startPosition, endPosition); + + if (sc->isContentEditable()) { + startPosition = sc->selection().visibleStart().honorEditableBoundaryAtOrAfter(startPosition); + endPosition = sc->selection().visibleEnd().honorEditableBoundaryAtOrBefore(endPosition); + if (startPosition.isNull() || endPosition.isNull()) { + return; + } + } + + // Ensure startPosition is not at end of block + if (startPosition != endPosition && isEndOfBlock(startPosition)) { + VisiblePosition nextStartPosition(startPosition.next()); + if (!nextStartPosition.isNull()) + startPosition = nextStartPosition; + } + // Ensure endPosition is not at start of block + if (startPosition != endPosition && isStartOfBlock(endPosition)) { + VisiblePosition prevEndPosition(endPosition.previous()); + if (!prevEndPosition.isNull()) + endPosition = prevEndPosition; + } + + VisibleSelection selection(startPosition, endPosition); + if (selection.isRange() && sc->shouldChangeSelection(selection)) { + sc->setSelection(selection); + } +} + // get the highlight rectangles for the touch point (x, y) with the slop AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool doMoveMouse) { @@ -3693,14 +3915,14 @@ void WebViewCore::updateTextSelection() if (!javaObject.get()) return; WebCore::Node* focusNode = currentFocus(); - if (!focusNode) - return; int start = 0; int end = 0; - getSelectionOffsets(focusNode, start, end); + if (focusNode) + getSelectionOffsets(focusNode, start, end); + SelectText* selectText = createSelectText(m_mainFrame->selection()->selection()); env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextSelection, reinterpret_cast(focusNode), - start, end, m_textGeneration); + start, end, m_textGeneration, selectText); checkException(env); } @@ -4044,10 +4266,8 @@ Vector WebViewCore::getTextRanges( // the selected text. startY--; endY--; - VisiblePosition startSelect = visiblePositionForWindowPoint( - startX - m_scrollOffsetX, startY - m_scrollOffsetY); - VisiblePosition endSelect = visiblePositionForWindowPoint( - endX - m_scrollOffsetX, endY - m_scrollOffsetY); + VisiblePosition startSelect = visiblePositionForContentPoint(startX, startY); + VisiblePosition endSelect = visiblePositionForContentPoint(endX, endY); Position start = startSelect.deepEquivalent(); Position end = endSelect.deepEquivalent(); Vector ranges; @@ -4129,43 +4349,6 @@ String WebViewCore::getText(int startX, int startY, int endX, int endY) return text; } -VisiblePosition WebViewCore::visiblePositionForWindowPoint(int x, int y) -{ - HitTestRequest::HitTestRequestType hitType = HitTestRequest::MouseMove; - hitType |= HitTestRequest::ReadOnly; - hitType |= HitTestRequest::Active; - HitTestRequest request(hitType); - FrameView* view = m_mainFrame->view(); - IntPoint point(view->windowToContents( - view->convertFromContainingWindow(IntPoint(x, y)))); - - // Look for the inner-most frame containing the hit. Its document - // contains the document with the selected text. - Frame* frame = m_mainFrame; - Frame* hitFrame = m_mainFrame; - Node* node = 0; - IntPoint localPoint = point; - do { - HitTestResult result(localPoint); - frame = hitFrame; - frame->document()->renderView()->layer()->hitTest(request, result); - node = result.innerNode(); - if (!node) - return VisiblePosition(); - - if (node->isFrameOwnerElement()) - hitFrame = static_cast(node)->contentFrame(); - localPoint = result.localPoint(); - } while (hitFrame && hitFrame != frame); - - Element* element = node->parentElement(); - if (!node->inDocument() && element && element->inDocument()) - node = element; - - RenderObject* renderer = node->renderer(); - return renderer->positionForPoint(localPoint); -} - //---------------------------------------------------------------------- // Native JNI methods //---------------------------------------------------------------------- @@ -4819,6 +5002,25 @@ static jobject GetText(JNIEnv* env, jobject obj, jint nativeClass, return text.isEmpty() ? 0 : wtfStringToJstring(env, text); } +static void SelectText(JNIEnv* env, jobject obj, jint nativeClass, + jint startX, jint startY, jint endX, jint endY) +{ + WebViewCore* viewImpl = reinterpret_cast(nativeClass); + viewImpl->selectText(startX, startY, endX, endY); +} + +static void ClearSelection(JNIEnv* env, jobject obj, jint nativeClass) +{ + WebViewCore* viewImpl = reinterpret_cast(nativeClass); + viewImpl->mainFrame()->selection()->clear(); +} + +static void SelectWordAt(JNIEnv* env, jobject obj, jint nativeClass, jint x, jint y) +{ + WebViewCore* viewImpl = reinterpret_cast(nativeClass); + viewImpl->selectWordAt(x, y); +} + // ---------------------------------------------------------------------------- /* @@ -4941,6 +5143,12 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) InsertText }, { "nativeGetText", "(IIIII)Ljava/lang/String;", (void*) GetText }, + { "nativeSelectText", "(IIIII)V", + (void*) SelectText }, + { "nativeClearTextSelection", "(I)V", + (void*) ClearSelection }, + { "nativeSelectWordAt", "(III)V", + (void*) SelectWordAt }, }; int registerWebViewCore(JNIEnv* env) diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h index 07034b2..cb345dd 100644 --- a/Source/WebKit/android/jni/WebViewCore.h +++ b/Source/WebKit/android/jni/WebViewCore.h @@ -104,6 +104,7 @@ namespace android { class CachedRoot; class ListBoxReply; class AndroidHitTestResult; + class SelectText; class WebCoreReply : public WebCoreRefObject { public: @@ -602,6 +603,11 @@ namespace android { */ Vector getTextRanges( int startX, int startY, int endX, int endY); + static int platformLayerIdFromNode(Node* node, LayerAndroid** outLayer = 0); + void selectText(int startX, int startY, int endX, int endY); + void selectWordAt(int x, int y); + + static void layerToAbsoluteOffset(const LayerAndroid* layer, IntPoint& offset); /** * Returns a text position at a given coordinate. @@ -701,6 +707,10 @@ namespace android { */ static WebCore::Position getPositionForOffset(Node* node, int offset); + VisiblePosition visiblePositionForContentPoint(int x, int y); + void selectWordAroundPosition(Frame* frame, VisiblePosition pos); + static SelectText* createSelectText(const VisibleSelection&); + // called from constructor, to add this to a global list static void addInstance(WebViewCore*); // called from destructor, to remove this from a global list diff --git a/Source/WebKit/android/nav/DrawExtra.h b/Source/WebKit/android/nav/DrawExtra.h index 6716a65..27a6598 100644 --- a/Source/WebKit/android/nav/DrawExtra.h +++ b/Source/WebKit/android/nav/DrawExtra.h @@ -31,6 +31,7 @@ class SkCanvas; namespace WebCore { class IntRect; class LayerAndroid; + class GLExtras; } using namespace WebCore; @@ -40,7 +41,9 @@ namespace android { class DrawExtra { public: virtual ~DrawExtra() {} - virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ) = 0; + virtual void drawLegacy(SkCanvas* , LayerAndroid* , IntRect* ) {} + virtual void draw(SkCanvas*, LayerAndroid*) {} + virtual void drawGL(GLExtras*, const LayerAndroid*) {} }; } diff --git a/Source/WebKit/android/nav/FindCanvas.cpp b/Source/WebKit/android/nav/FindCanvas.cpp index 35d31cd..dca9a75 100644 --- a/Source/WebKit/android/nav/FindCanvas.cpp +++ b/Source/WebKit/android/nav/FindCanvas.cpp @@ -564,7 +564,7 @@ void FindOnPage::storeCurrentMatchLocation() { // matches than this, only draw the focused match. #define MAX_NUMBER_OF_MATCHES_TO_DRAW 101 -void FindOnPage::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) { +void FindOnPage::drawLegacy(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) { if (!m_lastBounds.isEmpty()) { inval->unite(m_lastBounds); m_lastBounds.setEmpty(); diff --git a/Source/WebKit/android/nav/FindCanvas.h b/Source/WebKit/android/nav/FindCanvas.h index 939e568..1d81f9a 100644 --- a/Source/WebKit/android/nav/FindCanvas.h +++ b/Source/WebKit/android/nav/FindCanvas.h @@ -232,7 +232,7 @@ public: // This requires the current match to be in a layer. See // currentMatchIsInLayer(). int currentMatchLayerId() const; - virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ); + virtual void drawLegacy(SkCanvas* , LayerAndroid* , IntRect* ); void findNext(bool forward); bool isCurrentLocationValid() { return m_hasCurrentLocation; } void setMatches(WTF::Vector* matches); diff --git a/Source/WebKit/android/nav/SelectText.cpp b/Source/WebKit/android/nav/SelectText.cpp index 0f4ff23..b043406 100644 --- a/Source/WebKit/android/nav/SelectText.cpp +++ b/Source/WebKit/android/nav/SelectText.cpp @@ -25,23 +25,20 @@ #define LOG_TAG "webviewglue" -#include "CachedPrefix.h" +#include "config.h" + #include "BidiResolver.h" #include "BidiRunList.h" -#include "CachedRoot.h" +#include "GLExtras.h" #include "LayerAndroid.h" -#include "ParseCanvas.h" #include "SelectText.h" #include "SkBitmap.h" #include "SkBounder.h" -#include "SkGradientShader.h" -#include "SkMatrix.h" +#include "SkCanvas.h" #include "SkPicture.h" -#include "SkPixelXorXfermode.h" #include "SkPoint.h" #include "SkRect.h" #include "SkRegion.h" -#include "SkUtils.h" #include "TextRun.h" #ifdef DEBUG_NAV_UI @@ -149,1916 +146,49 @@ void ReverseBidi(UChar* chars, int len) { namespace android { -#define HYPHEN_MINUS 0x2D // ASCII hyphen -#define SOLIDUS 0x2F // ASCII slash -#define REVERSE_SOLIDUS 0x5C // ASCII backslash -#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes -#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes -#define TOUCH_SLOP 10 // additional distance from character rect when hit - -class CommonCheck : public SkBounder { -public: - CommonCheck(const SkIRect& area) - : mArea(area) - , mLastUni(0) - { - mLastGlyph.fGlyphID = static_cast(-1); - mLastCandidate.fGlyphID = static_cast(-1); - mMatrix.reset(); - reset(); - } - - /* called only while the picture is parsed */ - int base() { - if (mBase == INT_MAX) { - SkPoint result; - mMatrix.mapXY(0, mY, &result); - mBase = SkScalarFloor(result.fY); - } - return mBase; - } - - /* called only while the picture is parsed */ - int bottom() { - if (mBottom == INT_MAX) { - SkPoint result; - SkPaint::FontMetrics metrics; - mPaint.getFontMetrics(&metrics); - mMatrix.mapXY(0, metrics.fDescent + mY, &result); - mBottom = SkScalarCeil(result.fY); - } - return mBottom; - } - -#if DEBUG_NAV_UI - // make current (possibily uncomputed) value visible for debugging - int bottomDebug() const - { - return mBottom; - } -#endif - - bool addNewLine(const SkBounder::GlyphRec& rec) - { - SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY); - SkFixed lineHeight = SkIntToFixed(bottom() - top()); - return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5 - } - - bool addSpace(const SkBounder::GlyphRec& rec) - { - bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY; - if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR) - || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS - || mLastUni == REVERSE_SOLIDUS) && newBaseLine) - { - return false; - } - return isSpace(rec); - } - - void finishGlyph() - { - mLastGlyph = mLastCandidate; - mLastUni = mLastUniCandidate; - mLastPaint = mLastPaintCandidate; - } - - const SkIRect& getArea() const { - return mArea; - } - - /* called only while the picture is parsed */ - SkUnichar getUniChar(const SkBounder::GlyphRec& rec) - { - SkUnichar unichar; - SkPaint::TextEncoding save = mPaint.getTextEncoding(); - mPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - mPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar); - mPaint.setTextEncoding(save); - return unichar; - } - - bool isSpace(const SkBounder::GlyphRec& rec) - { - if (mLastGlyph.fGlyphID == static_cast(-1)) - return true; - DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)" - " rec=((%g, %g),(%g, %g), %d) mLastUni=0x%04x '%c'", - SkFixedToScalar(mLastGlyph.fLSB.fX), - SkFixedToScalar(mLastGlyph.fLSB.fY), - SkFixedToScalar(mLastGlyph.fRSB.fX), - SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID, - SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY), - SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY), - rec.fGlyphID, - mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?'); - bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY; - if (newBaseLine) - return true; - SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX; - SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX; - if (gapOne < 0 && gapTwo < 0) - return false; // overlaps - const SkBounder::GlyphRec& first = mLastGlyph.fLSB.fX < rec.fLSB.fX - ? mLastGlyph : rec; - const SkBounder::GlyphRec& second = mLastGlyph.fLSB.fX < rec.fLSB.fX - ? rec : mLastGlyph; - uint16_t firstGlyph = first.fGlyphID; - SkScalar firstWidth = mLastPaint.measureText(&firstGlyph, sizeof(firstGlyph)); - SkFixed ceilWidth = SkIntToFixed(SkScalarCeil(firstWidth)); - SkFixed posNoSpace = first.fLSB.fX + ceilWidth; - SkFixed ceilSpace = SkIntToFixed(SkFixedCeil(minSpaceWidth(mLastPaint))); - SkFixed posWithSpace = posNoSpace + ceilSpace; - SkFixed diffNoSpace = SkFixedAbs(second.fLSB.fX - posNoSpace); - SkFixed diffWithSpace = SkFixedAbs(second.fLSB.fX - posWithSpace); - DBG_NAV_LOGD("second=%g width=%g (%g) noSpace=%g (%g) withSpace=%g (%g)" - " fontSize=%g", - SkFixedToScalar(second.fLSB.fX), - firstWidth, SkFixedToScalar(ceilWidth), - SkFixedToScalar(posNoSpace), SkFixedToScalar(diffNoSpace), - SkFixedToScalar(posWithSpace), SkFixedToScalar(diffWithSpace), - mLastPaint.getTextSize()); - return diffWithSpace <= diffNoSpace; - } - - SkFixed minSpaceWidth(SkPaint& paint) - { - if (mMinSpaceWidth == SK_FixedMax) { - SkPaint::TextEncoding save = paint.getTextEncoding(); - paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); - SkScalar width = paint.measureText(" ", 1); - mMinSpaceWidth = SkScalarToFixed(width * mMatrix.getScaleX()); - paint.setTextEncoding(save); - DBG_NAV_LOGV("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)" - " mMinSpaceWidth=%g", width, - mMatrix.getScaleX(), mMatrix.getScaleY(), - mMatrix.getTranslateX(), mMatrix.getTranslateY(), - SkFixedToScalar(mMinSpaceWidth)); - } - return mMinSpaceWidth; - } - - void recordGlyph(const SkBounder::GlyphRec& rec) - { - mLastCandidate = rec; - mLastUniCandidate = getUniChar(rec); - mLastPaintCandidate = mPaint; - } - - void reset() - { - mMinSpaceWidth = SK_FixedMax; // mark as uninitialized - mBase = mBottom = mTop = INT_MAX; // mark as uninitialized - } - - void set(CommonCheck& check) - { - mLastGlyph = check.mLastGlyph; - mLastUni = check.mLastUni; - mMatrix = check.mMatrix; - mLastPaint = check.mLastPaint; - reset(); - } - - void setGlyph(CommonCheck& check) - { - mLastGlyph = check.mLastGlyph; - mLastUni = check.mLastUni; - mLastPaint = check.mLastPaint; - } - - void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y, - const void* text) - { - mMatrix = matrix; - mPaint = paint; - mText = static_cast(text); - mY = y; - reset(); - } - - /* called only while the picture is parsed */ - int top() { - if (mTop == INT_MAX) { - SkPoint result; - SkPaint::FontMetrics metrics; - mPaint.getFontMetrics(&metrics); - mMatrix.mapXY(0, metrics.fAscent + mY, &result); - mTop = SkScalarFloor(result.fY); - } - return mTop; - } - -#if DEBUG_NAV_UI - // make current (possibily uncomputed) value visible for debugging - int topDebug() const - { - return mTop; - } -#endif - -protected: - SkIRect mArea; - SkBounder::GlyphRec mLastCandidate; - SkBounder::GlyphRec mLastGlyph; - SkPaint mLastPaint; // available after picture has been parsed - SkPaint mLastPaintCandidate; // associated with candidate glyph - SkUnichar mLastUni; - SkUnichar mLastUniCandidate; - SkMatrix mMatrix; - SkPaint mPaint; // only set up while the picture is parsed - const uint16_t* mText; - SkScalar mY; -private: - int mBase; - int mBottom; - SkFixed mMinSpaceWidth; - int mTop; - friend class EdgeCheck; -}; - -// generate the limit area for the new selection -class LineCheck : public CommonCheck { -public: - LineCheck(int x, int y, const SkIRect& area) - : INHERITED(area) - , mX(x) - , mY(y) - , mInBetween(false) - { - mLast.setEmpty(); - } - - void finish(const SkRegion& selectedRgn) - { - if (!mParagraphs.count() && mLast.isEmpty()) - return; - processLine(); - bool above = false; - bool below = false; - bool selected = false; - SkRegion localRgn(selectedRgn); - localRgn.translate(-mArea.fLeft, -mArea.fTop, &localRgn); - DBG_NAV_LOGD("localRgn=(%d,%d,%d,%d)", - localRgn.getBounds().fLeft, localRgn.getBounds().fTop, - localRgn.getBounds().fRight, localRgn.getBounds().fBottom); - for (int index = 0; index < mParagraphs.count(); index++) { - const SkIRect& rect = mParagraphs[index]; - bool localSelected = localRgn.intersects(rect); - DBG_NAV_LOGD("[%d] rect=(%d,%d,%d,%d)", index, rect.fLeft, rect.fTop, - rect.fRight, rect.fBottom); - if (localSelected) { - DBG_NAV_LOGD("[%d] localSelected=true", index); - *mSelected.append() = rect; - } - if (rect.fRight <= mX || rect.fLeft >= mX) - continue; - if (mY > rect.fBottom) { - below = true; - selected |= localSelected; - DBG_NAV_LOGD("[%d] below=true localSelected=%s", index, - localSelected ? "true" : "false"); - } - if (mY < rect.fTop) { - above = true; - selected |= localSelected; - DBG_NAV_LOGD("[%d] above=true localSelected=%s", index, - localSelected ? "true" : "false"); - } - } - DBG_NAV_LOGD("mX=%d mY=%d above=%s below=%s selected=%s", - mX, mY, above ? "true" : "false", below ? "true" : "false", - selected ? "true" : "false"); - mInBetween = above && below && selected; - } - - bool inBetween() const - { - return mInBetween; - } - - bool inColumn(const SkIRect& test) const - { - for (int index = 0; index < mSelected.count(); index++) { - const SkIRect& rect = mSelected[index]; - if (rect.fRight > test.fLeft && rect.fLeft < test.fRight) - return true; - } - return false; - } - - bool inColumn(int x, int y) const - { - for (int index = 0; index < mSelected.count(); index++) { - const SkIRect& rect = mSelected[index]; - if (rect.contains(x, y)) - return true; - } - return false; - } - - virtual bool onIRect(const SkIRect& rect) - { - SkIRect bounds; - bounds.set(rect.fLeft, top(), rect.fRight, bottom()); - // assume that characters must be consecutive to describe spaces - // (i.e., don't join rects drawn at different times) - if (bounds.fTop != mLast.fTop || bounds.fBottom != mLast.fBottom - || bounds.fLeft > mLast.fRight + minSpaceWidth(mPaint) - || bounds.fLeft < mLast.fLeft) { - processLine(); - mLast = bounds; - } else - mLast.join(bounds); - return false; - } - - void processLine() - { - // assume line spacing of 1.5 - int lineHeight = bottom() - top(); - mLast.inset(0, -lineHeight >> 1); - // collect arrays of rectangles making up glyphs below or above this one - for (int index = 0; index < mParagraphs.count(); index++) { - SkIRect& rect = mParagraphs[index]; - if (SkIRect::Intersects(rect, mLast)) { - rect.join(mLast); - return; - } - } - *mParagraphs.append() = mLast; - } - -protected: - int mX; - int mY; - SkIRect mLast; - SkTDArray mParagraphs; - SkTDArray mSelected; - bool mInBetween; -private: - typedef CommonCheck INHERITED; -}; - -class SelectText::FirstCheck : public CommonCheck { -public: - FirstCheck(int x, int y, const SkIRect& area) - : INHERITED(area) - , mLineCheck(0) - , mFocusX(x - area.fLeft) - , mFocusY(y - area.fTop) - , mBestInColumn(false) - , mRecordGlyph(false) - { - reset(); - } - - const SkIRect& adjustedBounds(int* base) - { - *base = mBestBase + mArea.fTop; - mBestBounds.offset(mArea.fLeft, mArea.fTop); - DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d", - mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight, - mBestBounds.fBottom, topDebug(), bottomDebug()); - return mBestBounds; - } - - int focusX() const { return mFocusX; } - int focusY() const { return mFocusY; } - - virtual bool onIRectGlyph(const SkIRect& rect, - const SkBounder::GlyphRec& rec) - { - /* compute distance from rectangle center. - * centerX = (rect.L + rect.R) / 2 - * multiply centerX and comparison x by 2 to retain better precision - */ - SkIRect testBounds = {rect.fLeft, top(), rect.fRight, bottom()}; - // dx and dy are the distances from the tested edge - // The edge distance is paramount if the test point is far away - int dx = std::max(0, std::max(testBounds.fLeft - mFocusX, - mFocusX - testBounds.fRight)); - int dy = std::max(0, std::max(testBounds.fTop - mFocusY, - mFocusY - testBounds.fBottom)); - bool testInColumn = false; - bool inBetween = false; - bool inFocus = false; - if (mLineCheck) { - testInColumn = mLineCheck->inColumn(testBounds); - inBetween = mLineCheck->inBetween(); - inFocus = mLineCheck->inColumn(mFocusX, mFocusY); - } -#ifdef EXTRA_NOISY_LOGGING - if (dy < 10) { - SkUnichar ch = getUniChar(rec); - DBG_NAV_LOGD("FC dx/y=%d,%d mDx/y=%d,%d test=%d,%d,%d,%d" - " best=%d,%d,%d,%d bestIn=%s tween=%s testIn=%s focus=%s ch=%c", - dx, dy, mDx, mDy, - testBounds.fLeft, testBounds.fTop, testBounds.fRight, - testBounds.fBottom, mBestBounds.fLeft, mBestBounds.fTop, - mBestBounds.fRight, mBestBounds.fBottom, - mBestInColumn ? "true" : "false", inBetween ? "true" : "false", - testInColumn ? "true" : "false", inFocus ? "true" : "false", - ch < 0x7f ? ch : '?'); - } -#endif - if ((mBestInColumn || inBetween) && !testInColumn) { -#ifdef EXTRA_NOISY_LOGGING - if (dy < 10) DBG_NAV_LOG("FirstCheck reject column"); -#endif - return false; - } - bool ignoreColumn = mBestInColumn == testInColumn || !inFocus; - if (ignoreColumn && dy > 0 && (mDy < dy - || (mDy == dy && dx > 0 && mDx <= dx))) { -#ifdef EXTRA_NOISY_LOGGING - if (dy < 10) DBG_NAV_LOG("FirstCheck reject edge"); -#endif - return false; - } - // cx and cy are the distances from the tested center - // The center distance is used when the test point is over the text - int cx = std::abs(((testBounds.fLeft + testBounds.fRight) >> 1) - - mFocusX); - int cy = std::abs(((testBounds.fTop + testBounds.fBottom) >> 1) - - mFocusY); - if (ignoreColumn && dy == 0 && mDy == 0) { - if (mCy < cy) { -#ifdef EXTRA_NOISY_LOGGING - DBG_NAV_LOGD("FirstCheck reject cy=%d mCy=%d", cy, mCy); -#endif - return false; - } - if (mCy == cy) { - if (dx == 0 && mDx == 0) { - if (mCx < cx) { -#ifdef EXTRA_NOISY_LOGGING - DBG_NAV_LOGD("FirstCheck reject cx=%d mCx=%d", cx, mCx); -#endif - return false; - } - } else if (dx > 0 && mDx <= dx) { -#ifdef EXTRA_NOISY_LOGGING - DBG_NAV_LOGD("FirstCheck reject dx=%d mDx=%d", dx, mDx); -#endif - return false; - } - } - } -#ifdef EXTRA_NOISY_LOGGING - if (dy < 10) { - DBG_NAV_LOGD("FirstCheck cx/y=(%d,%d)", cx, cy); - } -#endif - mBestBase = base(); - mBestBounds = testBounds; - mBestInColumn = testInColumn; -#ifndef EXTRA_NOISY_LOGGING - if (dy < 10 && dx < 10) -#endif - { -#if DEBUG_NAV_UI - SkUnichar ch = getUniChar(rec); -#endif - DBG_NAV_LOGD("FirstCheck dx/y=(%d,%d) mFocus=(%d,%d)" - " mBestBounds={%d,%d,r=%d,b=%d} inColumn=%s ch=%c", - dx, dy, mFocusX, mFocusY, - mBestBounds.fLeft, mBestBounds.fTop, - mBestBounds.fRight, mBestBounds.fBottom, - mBestInColumn ? "true" : "false", ch < 0x7f ? ch : '?'); - } - mCx = cx; - mCy = cy; - mDx = dx; - mDy = dy; - if (mRecordGlyph) - recordGlyph(rec); - return false; - } - - void reset() - { - mBestBounds.setEmpty(); - mDx = mDy = mCx = mCy = INT_MAX; - } - - void setLines(const LineCheck* lineCheck) { mLineCheck = lineCheck; } - void setRecordGlyph() { mRecordGlyph = true; } - -protected: - const LineCheck* mLineCheck; - int mBestBase; - SkIRect mBestBounds; - int mCx; - int mCy; - int mDx; - int mDy; - int mFocusX; - int mFocusY; - bool mBestInColumn; - bool mRecordGlyph; -private: - typedef CommonCheck INHERITED; -}; - -class SelectText::EdgeCheck : public SelectText::FirstCheck { -public: - EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left) - : INHERITED(x, y, area) - , mLast(area) - , mLeft(left) - { - mLast.set(last); // CommonCheck::set() - setGlyph(last); - } - - bool adjacent() - { - return !mLast.isSpace(mLastGlyph); - } - - const SkIRect& bestBounds(int* base) - { - *base = mBestBase; - return mBestBounds; - } - - virtual bool onIRectGlyph(const SkIRect& rect, - const SkBounder::GlyphRec& rec) - { - int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX; - int dy = ((top() + bottom()) >> 1) - mFocusY; - dx = abs(dx); - dy = abs(dy); - if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) { - if (dx <= 10 && dy <= 10) { - DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d", - rect.fLeft, rect.fRight, mFocusX, dx, dy); - } - return false; - } - if (mDy > dy || (mDy == dy && mDx > dx)) { - if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) { - DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g", - SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX)); - return false; - } - recordGlyph(rec); - mDx = dx; - mDy = dy; - mBestBase = base(); - mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); - if (dx <= 10 && dy <= 10) { - DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} dx/y=(%d, %d)", - mBestBounds.fLeft, mBestBounds.fTop, - mBestBounds.fRight, mBestBounds.fBottom, dx, dy); - } - } - return false; - } - - void shiftStart(SkIRect bounds) - { - DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d", - mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight); - reset(); - mFocusX = mLeft ? bounds.fLeft : bounds.fRight; - mLast.set(*this); // CommonCheck::set() - } - -protected: - CommonCheck mLast; - bool mLeft; -private: - typedef SelectText::FirstCheck INHERITED; -}; - -class FindFirst : public CommonCheck { -public: - FindFirst(const SkIRect& area) - : INHERITED(area) - { - mBestBounds.set(area.width(), area.height(), area.width(), area.height()); - } - - const SkIRect& bestBounds(int* base) - { - *base = mBestBase; - return mBestBounds; - } - - virtual bool onIRect(const SkIRect& rect) - { - if (mBestBounds.isEmpty()) { - mBestBase = base(); - mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); - } - return false; - } - -protected: - int mBestBase; - SkIRect mBestBounds; -private: - typedef CommonCheck INHERITED; -}; - -class FindLast : public FindFirst { -public: - FindLast(const SkIRect& area) - : INHERITED(area) - { - mBestBounds.setEmpty(); - } - - virtual bool onIRect(const SkIRect& rect) - { - mBestBase = base(); - mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); - return false; - } - -private: - typedef FindFirst INHERITED; -}; - -static bool baseLinesAgree(const SkIRect& rectA, int baseA, - const SkIRect& rectB, int baseB) -{ - return (rectA.fTop < baseB && rectA.fBottom >= baseB) - || (rectB.fTop < baseA && rectB.fBottom >= baseA); -} - -class BuilderCheck : public CommonCheck { -protected: - enum IntersectionType { - NO_INTERSECTION, // debugging printf expects this to equal zero - LAST_INTERSECTION, // debugging printf expects this to equal one - WAIT_FOR_INTERSECTION - }; - - BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end, - int endBase, const SkIRect& area) - : INHERITED(area) - , mCapture(false) - , mEnd(end) - , mEndBase(endBase) - , mStart(start) - , mStartBase(startBase) - { - mEnd.offset(-area.fLeft, -area.fTop); - mEndBase -= area.fTop; - mEndExtra.setEmpty(); - mLast.setEmpty(); - mLastBase = INT_MAX; - mSelectRect.setEmpty(); - mStart.offset(-area.fLeft, -area.fTop); - mStartBase -= area.fTop; - mStartExtra.setEmpty(); - DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d" - " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d", - mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase, - mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase); - } - - int checkFlipRect(const SkIRect& full, int fullBase) { - mCollectFull = false; - // is the text to collect between the selection top and bottom? - if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) { - if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d", - mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase); - return mLastIntersects; - } - // is the text to the left of the selection start? - if (baseLinesAgree(mStart, mStartBase, full, fullBase) - && full.fLeft < mStart.fLeft) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" - " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d", - mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, - mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase); - mStartExtra.join(full); - return mLastIntersects; - } - // is the text to the right of the selection end? - if (baseLinesAgree(mEnd, mEndBase, full, fullBase) - && full.fRight > mEnd.fRight) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" - " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d", - mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, - mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase); - mEndExtra.join(full); - return mLastIntersects; - } - int spaceGap = SkFixedRound(minSpaceWidth(mPaint) * 3); - // should text to the left of the start be added to the selection bounds? - if (!mStartExtra.isEmpty()) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)" - " mStartExtra=(%d,%d,r=%d,b=%d)", - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, - mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom); - if (mStartExtra.fRight + spaceGap >= mStart.fLeft) - mSelectRect.join(mStartExtra); - mStartExtra.setEmpty(); - } - // should text to the right of the end be added to the selection bounds? - if (!mEndExtra.isEmpty()) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)" - " mEndExtra=(%d,%d,r=%d,b=%d)", - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, - mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom); - if (mEndExtra.fLeft - spaceGap <= mEnd.fRight) - mSelectRect.join(mEndExtra); - mEndExtra.setEmpty(); - } - bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase); - bool adjacent = (full.fLeft - mLast.fRight) < spaceGap; - // is this the first, or are there more characters on the same line? - if (mLast.isEmpty() || (sameBaseLine && adjacent)) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" - " mSelectRect=(%d,%d,r=%d,b=%d)", - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); - mLast.join(full); - mLastIntersects = SkIRect::Intersects(mLast, mSelectRect); - return WAIT_FOR_INTERSECTION; - } - if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4" - " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d" - " full=(%d,%d,r=%d,b=%d) fullBase=%d" - " mSelectRect=(%d,%d,r=%d,b=%d)" - " mStartExtra=(%d,%d,r=%d,b=%d)" - " mEndExtra=(%d,%d,r=%d,b=%d)", - mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase, - full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase, - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom, - mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom, - mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom); - // after the caller determines what to do with the last collection, - // start the collection over with full and fullBase. - mCollectFull = true; - return mLastIntersects; - } - - bool resetLast(const SkIRect& full, int fullBase) - { - if (mCollectFull) { - mLast = full; - mLastBase = fullBase; - mLastIntersects = SkIRect::Intersects(mLast, mSelectRect); - } else { - mLast.setEmpty(); - mLastBase = INT_MAX; - mLastIntersects = false; - } - return mCollectFull; - } - - void setFlippedState() - { - mSelectRect = mStart; - mSelectRect.join(mEnd); - DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)", - mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom); - mLast.setEmpty(); - mLastBase = INT_MAX; - mLastIntersects = NO_INTERSECTION; - } - - bool mCapture; - bool mCollectFull; - SkIRect mEnd; - int mEndBase; - SkIRect mEndExtra; - bool mFlipped; - SkIRect mLast; - int mLastBase; - int mLastIntersects; - SkIRect mSelectRect; - SkIRect mStart; - SkIRect mStartExtra; - int mStartBase; -private: - typedef CommonCheck INHERITED; - -}; - -class MultilineBuilder : public BuilderCheck { -public: - MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end, - int endBase, const SkIRect& area, SkRegion* region) - : INHERITED(start, startBase, end, endBase, area) - , mSelectRegion(region) - { - mFlipped = false; - } - - void addLastToRegion() { - if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); - mSelectRegion->op(mLast, SkRegion::kUnion_Op); - } - - void finish() { - if (!mFlipped || !mLastIntersects) - return; - addLastToRegion(); - } - - // return true if capture end was not found after capture begin - bool flipped() { - DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false"); - if (!mCapture) - return false; - mFlipped = true; - setFlippedState(); - mSelectRegion->setEmpty(); - return true; - } - - virtual bool onIRect(const SkIRect& rect) { - SkIRect full; - full.set(rect.fLeft, top(), rect.fRight, bottom()); - int fullBase = base(); - if (mFlipped) { - int intersectType = checkFlipRect(full, fullBase); - if (intersectType == LAST_INTERSECTION) - addLastToRegion(); - if (intersectType != WAIT_FOR_INTERSECTION) - resetLast(full, fullBase); - return false; - } - if (full == mStart) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)", - full.fLeft, full.fTop, full.fRight, full.fBottom); - mCapture = true; - } - if (mCapture) { - bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase); - if (sameLines) - mLast.join(full); - if (!sameLines || full == mEnd) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)", - mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom); - addLastToRegion(); - mLast = full; - mLastBase = fullBase; - } - } - if (full == mEnd) { - if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)", - full.fLeft, full.fTop, full.fRight, full.fBottom); - mCapture = false; - if (full == mStart) - addLastToRegion(); - } - return false; - } - -protected: - SkRegion* mSelectRegion; -private: - typedef BuilderCheck INHERITED; -}; - -static inline bool compareBounds(const SkIRect* first, const SkIRect* second) -{ - return first->fTop < second->fTop; -} - -class TextExtractor : public BuilderCheck { -public: - TextExtractor(const SkIRect& start, int startBase, const SkIRect& end, - int endBase, const SkIRect& area, bool flipped) - : INHERITED(start, startBase, end, endBase, area) - , mSelectStartIndex(-1) - , mSkipFirstSpace(true) // don't start with a space - { - mFlipped = flipped; - if (flipped) - setFlippedState(); - } - - void addCharacter(const SkBounder::GlyphRec& rec) - { - if (mSelectStartIndex < 0) - mSelectStartIndex = mSelectText.count(); - if (!mSkipFirstSpace) { - if (addNewLine(rec)) { - DBG_NAV_LOG("write new line"); - *mSelectText.append() = '\n'; - *mSelectText.append() = '\n'; - } else if (addSpace(rec)) { - DBG_NAV_LOG("write space"); - *mSelectText.append() = ' '; - } - } else - mSkipFirstSpace = false; - recordGlyph(rec); - finishGlyph(); - if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID, - mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?'); - if (mLastUni) { - uint16_t chars[2]; - size_t count = SkUTF16_FromUnichar(mLastUni, chars); - *mSelectText.append() = chars[0]; - if (count == 2) - *mSelectText.append() = chars[1]; - } - } - - void addLast() - { - *mSelectBounds.append() = mLast; - *mSelectStart.append() = mSelectStartIndex; - *mSelectEnd.append() = mSelectText.count(); - } - - /* Text characters are collected before it's been determined that the - characters are part of the selection. The bounds describe valid parts - of the selection, but the bounds are out of order. - - This sorts the characters by sorting the bounds, then copying the - characters that were captured. - */ - void finish() - { - if (mLastIntersects) - addLast(); - Vector sortedBounds; - SkTDArray temp; - int index; - DBG_NAV_LOGD("mSelectBounds.count=%d text=%d", mSelectBounds.count(), - mSelectText.count()); - for (index = 0; index < mSelectBounds.count(); index++) - sortedBounds.append(&mSelectBounds[index]); - std::sort(sortedBounds.begin(), sortedBounds.end(), compareBounds); - int lastEnd = -1; - for (index = 0; index < mSelectBounds.count(); index++) { - int order = sortedBounds[index] - &mSelectBounds[0]; - int start = mSelectStart[order]; - int end = mSelectEnd[order]; - DBG_NAV_LOGD("order=%d start=%d end=%d top=%d", order, start, end, - mSelectBounds[order].fTop); - int count = temp.count(); - if (count > 0 && temp[count - 1] != '\n' && start != lastEnd) { - // always separate paragraphs when original text is out of order - DBG_NAV_LOG("write new line"); - *temp.append() = '\n'; - *temp.append() = '\n'; - } - temp.append(end - start, &mSelectText[start]); - lastEnd = end; - } - mSelectText.swap(temp); - } - - virtual bool onIRectGlyph(const SkIRect& rect, - const SkBounder::GlyphRec& rec) - { - SkIRect full; - full.set(rect.fLeft, top(), rect.fRight, bottom()); - int fullBase = base(); - if (mFlipped) { - int intersectType = checkFlipRect(full, fullBase); - if (WAIT_FOR_INTERSECTION == intersectType) - addCharacter(rec); // may not be copied - else { - if (LAST_INTERSECTION == intersectType) - addLast(); - else - mSkipFirstSpace = true; - mSelectStartIndex = -1; - if (resetLast(full, fullBase)) - addCharacter(rec); // may not be copied - } - return false; - } - if (full == mStart) - mCapture = true; - if (mCapture) - addCharacter(rec); - else - mSkipFirstSpace = true; - if (full == mEnd) - mCapture = false; - return false; - } - - WTF::String text() { - if (mFlipped) - finish(); - // the text has been copied in visual order. Reverse as needed if - // result contains right-to-left characters. - const uint16_t* start = mSelectText.begin(); - const uint16_t* end = mSelectText.end(); - while (start < end) { - SkUnichar ch = SkUTF16_NextUnichar(&start); - WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch); - if (WTF::Unicode::RightToLeftArabic == charDirection - || WTF::Unicode::RightToLeft == charDirection) { - WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count()); - break; - } - } - return WTF::String(mSelectText.begin(), mSelectText.count()); - } - -protected: - SkIRect mEmpty; - SkTDArray mSelectBounds; - SkTDArray mSelectEnd; - SkTDArray mSelectStart; - int mSelectStartIndex; - SkTDArray mSelectText; - bool mSkipFirstSpace; -private: - typedef BuilderCheck INHERITED; -}; - -class TextCanvas : public ParseCanvas { -public: - - TextCanvas(CommonCheck* bounder) - : mBounder(*bounder) { - setBounder(bounder); - SkBitmap bitmap; - const SkIRect& area = bounder->getArea(); - bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(), - area.height()); - setBitmapDevice(bitmap); - translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop)); -#ifdef DEBUG_NAV_UI - const SkIRect& clip = getTotalClip().getBounds(); - const SkMatrix& matrix = getTotalMatrix(); - DBG_NAV_LOGD("bitmap=(%d,%d) clip=(%d,%d,%d,%d) matrix=(%g,%g)", - bitmap.width(), bitmap.height(), clip.fLeft, clip.fTop, - clip.fRight, clip.fBottom, matrix.getTranslateX(), matrix.getTranslateY()); -#endif - } - - virtual void drawPaint(const SkPaint& paint) { - } - - virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], - const SkPaint& paint) { - } - - virtual void drawRect(const SkRect& rect, const SkPaint& paint) { - } - - virtual void drawPath(const SkPath& path, const SkPaint& paint) { - } - - virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect, - const SkMatrix& matrix, const SkPaint& paint) { - } - - virtual void drawSprite(const SkBitmap& bitmap, int left, int top, - const SkPaint* paint = NULL) { - } - - virtual void drawText(const void* text, size_t byteLength, SkScalar x, - SkScalar y, const SkPaint& paint) { - mBounder.setUp(paint, getTotalMatrix(), y, text); - INHERITED::drawText(text, byteLength, x, y, paint); - } - - virtual void drawPosTextH(const void* text, size_t byteLength, - const SkScalar xpos[], SkScalar constY, - const SkPaint& paint) { - mBounder.setUp(paint, getTotalMatrix(), constY, text); - INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint); - } - - virtual void drawVertices(VertexMode vmode, int vertexCount, - const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - } - - CommonCheck& mBounder; -private: - typedef ParseCanvas INHERITED; -}; - -static bool buildSelection(const SkPicture& picture, const SkIRect& area, - const SkIRect& selStart, int startBase, - const SkIRect& selEnd, int endBase, SkRegion* region) -{ - DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)" - " selEnd=(%d, %d, %d, %d)", - area.fLeft, area.fTop, area.fRight, area.fBottom, - selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom, - selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom); - MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region); - TextCanvas checker(&builder); - checker.drawPicture(const_cast(picture)); - bool flipped = builder.flipped(); - if (flipped) { - TextCanvas checker(&builder); - checker.drawPicture(const_cast(picture)); - } - builder.finish(); - region->translate(area.fLeft, area.fTop); - return flipped; -} - -static SkIRect findFirst(const SkPicture& picture, int* base) -{ - SkIRect area; - area.set(0, 0, picture.width(), picture.height()); - FindFirst finder(area); - TextCanvas checker(&finder); - checker.drawPicture(const_cast(picture)); - return finder.bestBounds(base); -} - -static SkIRect findLast(const SkPicture& picture, int* base) -{ - SkIRect area; - area.set(0, 0, picture.width(), picture.height()); - FindLast finder(area); - TextCanvas checker(&finder); - checker.drawPicture(const_cast(picture)); - return finder.bestBounds(base); -} - -static WTF::String text(const SkPicture& picture, const SkIRect& area, - const SkIRect& start, int startBase, const SkIRect& end, - int endBase, bool flipped) -{ - TextExtractor extractor(start, startBase, end, endBase, area, flipped); - TextCanvas checker(&extractor); - checker.drawPicture(const_cast(picture)); - return extractor.text(); -} - -#define CONTROL_NOTCH 16 -// TODO: Now that java is the one actually drawing these, get the real values -// from the drawable itself -#define CONTROL_HEIGHT 47 -#define CONTROL_WIDTH 26 -#define CONTROL_SLOP 5 -#define STROKE_WIDTH 1.0f -#define STROKE_OUTSET 3.5f -#define STROKE_I_OUTSET 4 // (int) ceil(STROKE_OUTSET) -#define STROKE_COLOR 0x66000000 -#define OUTER_COLOR 0x33000000 -#define INNER_COLOR 0xe6aae300 - -SelectText::SelectText() - : m_controlWidth(CONTROL_WIDTH) - , m_controlHeight(CONTROL_HEIGHT) - , m_controlSlop(CONTROL_SLOP) -{ - m_picture = 0; - reset(); - SkPaint paint; - SkRect oval; - - SkPath startOuterPath; - oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET, - -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET); - startOuterPath.arcTo(oval, 180, 45, true); - oval.set(-STROKE_OUTSET, -STROKE_OUTSET, STROKE_OUTSET, STROKE_OUTSET); - startOuterPath.arcTo(oval, 180 + 45, 135, false); - oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET, - STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET); - startOuterPath.arcTo(oval, 0, 90, false); - oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET, - -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET); - startOuterPath.arcTo(oval, 90, 90, false); - startOuterPath.close(); - SkPath startInnerPath; - startInnerPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH); - startInnerPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT); - startInnerPath.lineTo(0, CONTROL_HEIGHT); - startInnerPath.lineTo(0, 0); - startInnerPath.close(); - startOuterPath.addPath(startInnerPath, 0, 0); - - SkCanvas* canvas = m_startControl.beginRecording( - CONTROL_WIDTH + STROKE_OUTSET * 2, - CONTROL_HEIGHT + STROKE_OUTSET * 2); - paint.setAntiAlias(true); - paint.setColor(INNER_COLOR); - paint.setStyle(SkPaint::kFill_Style); - canvas->drawPath(startInnerPath, paint); - paint.setColor(OUTER_COLOR); - canvas->drawPath(startOuterPath, paint); - paint.setStyle(SkPaint::kStroke_Style); - paint.setColor(STROKE_COLOR); - paint.setStrokeWidth(STROKE_WIDTH); - canvas->drawPath(startInnerPath, paint); - m_startControl.endRecording(); - - SkPath endOuterPath; - oval.set(-STROKE_OUTSET, -STROKE_OUTSET, STROKE_OUTSET, STROKE_OUTSET); - endOuterPath.arcTo(oval, 180, 135, true); - oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET, - CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET); - endOuterPath.arcTo(oval, 360 - 45, 45, false); - oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET, - CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET); - endOuterPath.arcTo(oval, 0, 90, false); - oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET, - STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET); - endOuterPath.arcTo(oval, 90, 90, false); - startOuterPath.close(); - SkPath endInnerPath; - endInnerPath.moveTo(0, 0); - endInnerPath.lineTo(0, CONTROL_HEIGHT); - endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT); - endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH); - endInnerPath.close(); - endOuterPath.addPath(endInnerPath, 0, 0); - - canvas = m_endControl.beginRecording(CONTROL_WIDTH + STROKE_OUTSET * 2, - CONTROL_HEIGHT + STROKE_OUTSET * 2); - paint.setColor(INNER_COLOR); - paint.setStyle(SkPaint::kFill_Style); - canvas->drawPath(endInnerPath, paint); - paint.setColor(OUTER_COLOR); - canvas->drawPath(endOuterPath, paint); - paint.setStyle(SkPaint::kStroke_Style); - paint.setColor(STROKE_COLOR); - paint.setStrokeWidth(STROKE_WIDTH); - canvas->drawPath(endInnerPath, paint); - m_endControl.endRecording(); -} - SelectText::~SelectText() { - SkSafeUnref(m_picture); -} - -void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) -{ - int drawingLayerId = layer ? layer->uniqueId() : -1; - if (m_layerId != drawingLayerId) - return; - DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d layer [%d]", - m_extendSelection, m_drawPointer, drawingLayerId); - if (m_extendSelection) - drawSelectionRegion(canvas, inval); - if (m_drawPointer) - drawSelectionPointer(canvas, inval); -} - -static void addInval(IntRect* inval, const SkCanvas* canvas, - const SkRect& bounds) { - const SkMatrix& matrix = canvas->getTotalMatrix(); - SkRect transformed; - matrix.mapRect(&transformed, bounds); - SkIRect iTrans; - transformed.round(&iTrans); - inval->unite(iTrans); -} - -void SelectText::drawSelectionPointer(SkCanvas* canvas, IntRect* inval) -{ - SkPath path; - if (m_extendSelection) - getSelectionCaret(&path); - else - getSelectionArrow(&path); - SkPixelXorXfermode xorMode(SK_ColorWHITE); - SkPaint paint; - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kStroke_Style); - paint.setColor(SK_ColorBLACK); - if (m_extendSelection) - paint.setXfermode(&xorMode); - else - paint.setStrokeWidth(SK_Scalar1 * 2); - int sc = canvas->save(); - canvas->scale(m_inverseScale, m_inverseScale); - canvas->translate(m_selectX, m_selectY); - canvas->drawPath(path, paint); - if (!m_extendSelection) { - paint.setStyle(SkPaint::kFill_Style); - paint.setColor(SK_ColorWHITE); - canvas->drawPath(path, paint); - } - SkRect bounds = path.getBounds(); - bounds.inset(-SK_Scalar1 * 2, -SK_Scalar1 * 2); // stroke width - addInval(inval, canvas, bounds); - canvas->restoreToCount(sc); -} - -static void addStart(SkRegion* diff, const SkIRect& rect) -{ - SkIRect bounds; - bounds.set(rect.fLeft - CONTROL_WIDTH - STROKE_I_OUTSET, - rect.fBottom - STROKE_I_OUTSET, rect.fLeft + STROKE_I_OUTSET, - rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET); - diff->op(bounds, SkRegion::kUnion_Op); -} - -static void addEnd(SkRegion* diff, const SkIRect& rect) -{ - SkIRect bounds; - bounds.set(rect.fRight - STROKE_I_OUTSET, rect.fBottom - STROKE_I_OUTSET, - rect.fRight + CONTROL_WIDTH + STROKE_I_OUTSET, - rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET); - diff->op(bounds, SkRegion::kUnion_Op); -} - -void SelectText::getSelectionRegion(const IntRect& vis, SkRegion *region, - LayerAndroid* root) -{ - SkIRect ivisBounds = vis; - ivisBounds.join(m_selStart); - ivisBounds.join(m_selEnd); - region->setEmpty(); - buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase, - m_selEnd, m_endBase, region); - if (root && m_layerId) { - Layer* layer = root->findById(m_layerId); - while (layer) { - const SkPoint& pos = layer->getPosition(); - region->translate(pos.fX, pos.fY); - layer = layer->getParent(); - } + HighlightRegionMap::iterator end = m_highlightRegions.end(); + for (HighlightRegionMap::iterator it = m_highlightRegions.begin(); it != end; ++it) { + delete it->second; + it->second = 0; } } -void SelectText::drawSelectionRegion(SkCanvas* canvas, IntRect* inval) +void SelectText::drawGL(GLExtras* extras, const LayerAndroid* layer) { - if (!m_picture) + SkRegion* region = getHightlightRegionsForLayer(layer ? layer->uniqueId() : -1); + if (!region || region->isEmpty()) return; - SkIRect ivisBounds = m_visibleRect; - ivisBounds.join(m_selStart); - ivisBounds.join(m_selEnd); - DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)" - " ivisBounds=(%d,%d,r=%d,b=%d)", - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, - m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom, - ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom); - if (m_lastSelRegion != m_selRegion) - m_lastSelRegion.set(m_selRegion); - SkRegion diff(m_lastSelRegion); - m_selRegion.setEmpty(); - m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase, - m_selEnd, m_endBase, &m_selRegion); - SkPath path; - m_selRegion.getBoundaryPath(&path); - path.setFillType(SkPath::kEvenOdd_FillType); - - SkPaint paint; - paint.setAntiAlias(true); - paint.setColor(SkColorSetARGB(0x80, 0x83, 0xCC, 0x39)); - canvas->drawPath(path, paint); - // experiment to draw touchable controls that resize the selection - float scale = m_controlHeight / (float)CONTROL_HEIGHT; - canvas->save(); - canvas->translate(m_selStart.fLeft, m_selStart.fBottom); - canvas->scale(scale, scale); - canvas->drawPicture(m_startControl); - canvas->restore(); - canvas->save(); - canvas->translate(m_selEnd.fRight, m_selEnd.fBottom); - canvas->scale(scale, scale); - canvas->drawPicture(m_endControl); - canvas->restore(); - -#if DEBUG_TOUCH_HANDLES - SkRect touchHandleRect; - paint.setColor(SkColorSetARGB(0x60, 0xFF, 0x00, 0x00)); - touchHandleRect.set(0, m_selStart.fBottom, m_selStart.fLeft, 0); - touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight; - touchHandleRect.fLeft = touchHandleRect.fRight - m_controlWidth; - canvas->drawRect(touchHandleRect, paint); - touchHandleRect.inset(-m_controlSlop, -m_controlSlop); - canvas->drawRect(touchHandleRect, paint); - touchHandleRect.set(m_selEnd.fRight, m_selEnd.fBottom, 0, 0); - touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight; - touchHandleRect.fRight = touchHandleRect.fLeft + m_controlWidth; - canvas->drawRect(touchHandleRect, paint); - touchHandleRect.inset(-m_controlSlop, -m_controlSlop); - canvas->drawRect(touchHandleRect, paint); -#endif - - SkIRect a = diff.getBounds(); - SkIRect b = m_selRegion.getBounds(); - diff.op(m_selRegion, SkRegion::kXOR_Op); - SkIRect c = diff.getBounds(); - DBG_NAV_LOGD("old=(%d,%d,r=%d,b=%d) new=(%d,%d,r=%d,b=%d) diff=(%d,%d,r=%d,b=%d)", - a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom, - c.fLeft, c.fTop, c.fRight, c.fBottom); - DBG_NAV_LOGD("lastStart=(%d,%d,r=%d,b=%d) m_lastEnd=(%d,%d,r=%d,b=%d)", - m_lastStart.fLeft, m_lastStart.fTop, m_lastStart.fRight, m_lastStart.fBottom, - m_lastEnd.fLeft, m_lastEnd.fTop, m_lastEnd.fRight, m_lastEnd.fBottom); - if (!m_lastDrawnStart.isEmpty()) - addStart(&diff, m_lastDrawnStart); - if (m_lastStart != m_selStart) { - m_lastDrawnStart = m_lastStart; - m_lastStart = m_selStart; - } - addStart(&diff, m_selStart); - if (!m_lastDrawnEnd.isEmpty()) - addEnd(&diff, m_lastDrawnEnd); - if (m_lastEnd != m_selEnd) { - m_lastDrawnEnd = m_lastEnd; - m_lastEnd = m_selEnd; - } - addEnd(&diff, m_selEnd); - SkIRect iBounds = diff.getBounds(); - DBG_NAV_LOGD("diff=(%d,%d,r=%d,b=%d)", - iBounds.fLeft, iBounds.fTop, iBounds.fRight, iBounds.fBottom); - SkRect bounds; - bounds.set(iBounds); - addInval(inval, canvas, bounds); + extras->drawRegion(*region, true, false, layer ? layer->drawTransform() : 0, false); } -void SelectText::extendSelection(const IntRect& vis, int x, int y) +void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer) { - if (!m_picture) + SkRegion* region = getHightlightRegionsForLayer(layer ? layer->uniqueId() : -1); + if (!region || region->isEmpty()) return; - setVisibleRect(vis); - SkIRect clipRect = m_visibleRect; - int base; - DBG_NAV_LOGD("extend x/y=%d,%d m_startOffset=%d,%d", x, y, - m_startOffset.fX, m_startOffset.fY); - x -= m_startOffset.fX; - y -= m_startOffset.fY; - if (m_startSelection) { - if (!clipRect.contains(x, y) - || !clipRect.contains(m_original.fX, m_original.fY)) { - clipRect.set(m_original.fX, m_original.fY, x, y); - clipRect.sort(); - clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height()); - } - FirstCheck center(m_original.fX, m_original.fY, clipRect); - m_selStart = m_selEnd = findClosest(center, *m_picture, &base); - if (m_selStart.isEmpty()) - return; - DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d) m_original=%d,%d" - " m_selStart=(%d,%d,%d,%d)", clipRect.fLeft, clipRect.fTop, - clipRect.fRight, clipRect.fBottom, m_original.fX, m_original.fY, - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom); - m_startBase = m_endBase = base; - m_startSelection = false; - m_extendSelection = true; - m_original.fX = m_original.fY = 0; - } - DBG_NAV_LOGD("extend x/y=%d,%d m_original=%d,%d", x, y, - m_original.fX, m_original.fY); - x -= m_original.fX; - y -= m_original.fY; - if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) { - clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y); - clipRect.sort(); - clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height()); - } - DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d) x/y=%d,%d wordSel=%s outsideWord=%s", - clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, x, y, - m_wordSelection ? "true" : "false", m_outsideWord ? "true" : "false"); - FirstCheck extension(x, y, clipRect); - SkIRect found = findClosest(extension, *m_picture, &base); - if (m_wordSelection) { - SkIRect wordBounds = m_wordBounds; - if (!m_outsideWord) - wordBounds.inset(-TOUCH_SLOP, -TOUCH_SLOP); - DBG_NAV_LOGD("x=%d y=%d wordBounds=(%d,%d,r=%d,b=%d)" - " found=(%d,%d,r=%d,b=%d)", x, y, wordBounds.fLeft, wordBounds.fTop, - wordBounds.fRight, wordBounds.fBottom, found.fLeft, found.fTop, - found.fRight, found.fBottom); - if (wordBounds.contains(x, y)) { - DBG_NAV_LOG("wordBounds.contains=true"); - m_outsideWord = false; - return; - } - m_outsideWord = true; - if (found.fBottom <= wordBounds.fTop) - m_hitTopLeft = true; - else if (found.fTop >= wordBounds.fBottom) - m_hitTopLeft = false; - else - m_hitTopLeft = (found.fLeft + found.fRight) - < (wordBounds.fLeft + wordBounds.fRight); - } - DBG_NAV_LOGD("x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)" - " m_extendSelection=%s", - x, y, m_startSelection ? "true" : "false", - m_hitTopLeft ? "m_selStart" : "m_selEnd", - found.fLeft, found.fTop, found.fRight, found.fBottom, - m_extendSelection ? "true" : "false"); - if (m_hitTopLeft) { - m_startBase = base; - m_selStart = found; - } else { - m_endBase = base; - m_selEnd = found; - } - swapAsNeeded(); -} - -SkIRect SelectText::findClosest(FirstCheck& check, const SkPicture& picture, - int* base) -{ - LineCheck lineCheck(check.focusX(), check.focusY(), check.getArea()); - TextCanvas lineChecker(&lineCheck); - lineChecker.drawPicture(const_cast(picture)); - lineCheck.finish(m_selRegion); - check.setLines(&lineCheck); - TextCanvas checker(&check); - checker.drawPicture(const_cast(picture)); - check.finishGlyph(); - return check.adjustedBounds(base); -} - -SkIRect SelectText::findEdge(const SkPicture& picture, const SkIRect& area, - int x, int y, bool left, int* base) -{ - SkIRect result; - result.setEmpty(); - FirstCheck center(x, y, area); - center.setRecordGlyph(); - int closestBase; - SkIRect closest = findClosest(center, picture, &closestBase); - SkIRect sloppy = closest; - sloppy.inset(-TOUCH_SLOP, -TOUCH_SLOP); - if (!sloppy.contains(x, y)) { - DBG_NAV_LOGD("sloppy=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d", - sloppy.fLeft, sloppy.fTop, sloppy.fRight, sloppy.fBottom, - area.fLeft, area.fTop, area.fRight, area.fBottom, x, y); - return result; - } - EdgeCheck edge(x, y, area, center, left); - do { // detect left or right until there's a gap - DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d", - &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom); - TextCanvas checker(&edge); - checker.drawPicture(const_cast(picture)); - edge.finishGlyph(); - if (!edge.adjacent()) { - if (result.isEmpty()) { - *base = closestBase; - DBG_NAV_LOGD("closest=%d,%d,%d,%d", closest.fLeft, - closest.fTop, closest.fRight, closest.fBottom); - return closest; - } - DBG_NAV_LOG("adjacent break"); - break; - } - int nextBase; - const SkIRect& next = edge.bestBounds(&nextBase); - if (next.isEmpty()) { - DBG_NAV_LOG("empty"); - break; - } - if (result == next) { - DBG_NAV_LOG("result == next"); - break; - } - *base = nextBase; - result = next; - edge.shiftStart(result); - } while (true); - if (!result.isEmpty()) { - *base += area.fTop; - result.offset(area.fLeft, area.fTop); - } - return result; -} - -SkIRect SelectText::findLeft(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base) -{ - return findEdge(picture, area, x, y, true, base); -} - -SkIRect SelectText::findRight(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base) -{ - return findEdge(picture, area, x, y, false, base); -} - -const String SelectText::getSelection() -{ - if (!m_picture) - return String(); - SkIRect clipRect; - clipRect.set(0, 0, m_picture->width(), m_picture->height()); - String result = text(*m_picture, clipRect, m_selStart, m_startBase, - m_selEnd, m_endBase, m_flipped); - DBG_NAV_LOGD("clip=(%d,%d,%d,%d)" - " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", - clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, - m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); - DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString - return result; -} - -void SelectText::getSelectionArrow(SkPath* path) -{ - const int arrow[] = { - 0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11 - }; - for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2) - path->lineTo(arrow[index], arrow[index + 1]); - path->close(); -} - -void SelectText::getSelectionCaret(SkPath* path) -{ - SkScalar height = m_selStart.fBottom - m_selStart.fTop; - SkScalar dist = height / 4; - path->moveTo(0, -height / 2); - path->rLineTo(0, height); - path->rLineTo(-dist, dist); - path->rMoveTo(0, -0.5f); - path->rLineTo(dist * 2, 0); - path->rMoveTo(0, 0.5f); - path->rLineTo(-dist, -dist); -} - -bool SelectText::hitCorner(int cx, int cy, int x, int y) const -{ - SkIRect test; - test.set(cx, cy, cx + m_controlWidth, cy + m_controlHeight); - test.inset(-m_controlSlop, -m_controlSlop); - DBG_HANDLE_LOG("checking if %dx%d,%d-%d contains %dx%d", - cx, cy, m_controlWidth, m_controlHeight, x, y); - return test.contains(x, y); -} - -bool SelectText::hitStartHandle(int x, int y) const -{ - int left = m_selStart.fLeft - m_controlWidth; - return hitCorner(left, m_selStart.fBottom, x, y); -} - -bool SelectText::hitEndHandle(int x, int y) const -{ - int left = m_selEnd.fRight; - return hitCorner(left, m_selEnd.fBottom, x, y); -} - -bool SelectText::hitSelection(int x, int y) const -{ - x -= m_startOffset.fX; - y -= m_startOffset.fY; - if (hitStartHandle(x, y)) - return true; - if (hitEndHandle(x, y)) - return true; - return m_selRegion.contains(x, y); -} - -void SelectText::getSelectionHandles(int* handles, LayerAndroid* root) -{ - handles[0] = m_selStart.fLeft; - handles[1] = m_selStart.fBottom; - handles[2] = m_selEnd.fRight; - handles[3] = m_selEnd.fBottom; - if (root && m_layerId) { - Layer* layer = root->findById(m_layerId); - while (layer) { - const SkPoint& pos = layer->getPosition(); - handles[0] += pos.fX; - handles[2] += pos.fX; - handles[1] += pos.fY; - handles[3] += pos.fY; - layer = layer->getParent(); - } - } -} - -void SelectText::moveSelection(const IntRect& vis, int x, int y) -{ - if (!m_picture) - return; - x -= m_startOffset.fX; - y -= m_startOffset.fY; - setVisibleRect(vis); - SkIRect clipRect = m_visibleRect; - clipRect.join(m_selStart); - clipRect.join(m_selEnd); - FirstCheck center(x, y, clipRect); - int base; - SkIRect found = findClosest(center, *m_picture, &base); - if (m_hitTopLeft || !m_extendSelection) { - m_startBase = base; - m_selStart = found; - } - if (!m_hitTopLeft || !m_extendSelection) { - m_endBase = base; - m_selEnd = found; - } - swapAsNeeded(); - DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)" - " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false", - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, - m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); -} - -void SelectText::reset() -{ - DBG_NAV_LOG("m_extendSelection=false"); - m_selStart.setEmpty(); - m_lastStart.setEmpty(); - m_lastDrawnStart.setEmpty(); - m_selEnd.setEmpty(); - m_lastEnd.setEmpty(); - m_lastDrawnEnd.setEmpty(); - m_extendSelection = false; - m_startSelection = false; - SkSafeUnref(m_picture); - m_picture = 0; - m_layerId = 0; -} - -IntPoint SelectText::selectableText(const CachedRoot* root) -{ - int x = 0; - int y = 0; - SkPicture* picture = root->pictureAt(&x, &y, &m_layerId); - if (!picture) { - DBG_NAV_LOG("picture==0"); - return IntPoint(0, 0); - } - int width = picture->width(); - int height = picture->height(); - IntRect vis(0, 0, width, height); - FirstCheck center(width >> 1, height >> 1, vis); - int base; - const SkIRect& closest = findClosest(center, *picture, &base); - return IntPoint((closest.fLeft + closest.fRight) >> 1, - (closest.fTop + closest.fBottom) >> 1); -} - -void SelectText::selectAll() -{ - if (!m_picture) - return; - m_selStart = findFirst(*m_picture, &m_startBase); - m_selEnd = findLast(*m_picture, &m_endBase); - m_extendSelection = true; -} - -int SelectText::selectionX() const -{ - return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX; -} - -int SelectText::selectionY() const -{ - const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd; - return ((rect.fTop + rect.fBottom) >> 1) + m_startOffset.fY; -} - -void SelectText::setVisibleRect(const IntRect& vis) -{ - DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%d,%d)", - vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX, - m_startOffset.fY); - m_visibleRect = vis; - m_visibleRect.offset(-m_startOffset.fX, -m_startOffset.fY); -} - -bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis, - int x, int y) -{ - m_wordSelection = false; - m_startOffset.set(x, y); - DBG_NAV_LOGD("x/y=(%d,%d)", x, y); - SkSafeUnref(m_picture); - m_picture = root->pictureAt(&x, &y, &m_layerId); - DBG_NAV_LOGD("m_picture=%p m_layerId=%d x/y=(%d,%d)", m_picture, m_layerId, - x, y); - if (!m_picture) { - DBG_NAV_LOG("picture==0"); - return false; - } - m_picture->ref(); - m_startOffset.fX -= x; - m_startOffset.fY -= y; - m_original.fX = x; - m_original.fY = y; - setVisibleRect(vis); - if (m_selStart.isEmpty()) { - DBG_NAV_LOGD("empty start picture=(%d,%d) x=%d y=%d", - m_picture->width(), m_picture->height(), x, y); - m_startSelection = true; - return true; - } - m_hitTopLeft = hitStartHandle(x, y); - bool hitBottomRight = hitEndHandle(x, y); - DBG_NAV_LOGD("picture=(%d,%d) left=%d top=%d right=%d bottom=%d x=%d y=%d", - m_picture->width(), m_picture->height(),left, top, right, bottom, x, y); - if (m_hitTopLeft) { - DBG_NAV_LOG("hit top left"); - m_original.fX -= m_selStart.fLeft; - m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1; - } else if (hitBottomRight) { - DBG_NAV_LOG("hit bottom right"); - m_original.fX -= m_selEnd.fRight; - m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1; - } - return m_hitTopLeft || hitBottomRight; -} - -void SelectText::updateHandleScale(float handleScale) -{ - m_controlHeight = CONTROL_HEIGHT * handleScale; - m_controlWidth = CONTROL_WIDTH * handleScale; - m_controlSlop = CONTROL_SLOP * handleScale; -} - -/* selects the word at (x, y) -* a word is normally delimited by spaces -* a string of digits (even with inside spaces) is a word (for phone numbers) -* FIXME: digit find isn't implemented yet -* returns true if a word was selected -*/ -bool SelectText::wordSelection(const CachedRoot* root, const IntRect& vis, - int x, int y) -{ - IntRect tapArea = IntRect(x - TOUCH_SLOP, y - TOUCH_SLOP, TOUCH_SLOP * 2, - TOUCH_SLOP * 2); - if (!startSelection(root, tapArea, x, y)) - return false; - extendSelection(tapArea, x, y); - if (m_selStart.isEmpty()) - return false; - setDrawPointer(false); - setVisibleRect(vis); - SkIRect ivisBounds = m_visibleRect; - ivisBounds.join(m_selStart); - ivisBounds.join(m_selEnd); - DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)" - " ivisBounds=(%d,%d,r=%d,b=%d)", - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, - m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom, - ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom); - m_selRegion.setEmpty(); - buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase, - m_selEnd, m_endBase, &m_selRegion); - x = m_selStart.fLeft; - y = (m_selStart.fTop + m_selStart.fBottom) >> 1; - SkIRect clipRect = m_visibleRect; - clipRect.fLeft -= m_visibleRect.width() >> 1; - clipRect.fLeft = std::max(clipRect.fLeft, 0); - int base; - SkIRect left = findLeft(*m_picture, clipRect, x, y, &base); - if (!left.isEmpty()) { - m_startBase = base; - m_selStart = left; - } - x = m_selEnd.fRight; - y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1; - clipRect = m_visibleRect; - clipRect.fRight += m_visibleRect.width() >> 1; - SkIRect right = findRight(*m_picture, clipRect, x, y, &base); - if (!right.isEmpty()) { - m_endBase = base; - m_selEnd = right; - } - DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)", - m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, - m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); - if (!left.isEmpty() || !right.isEmpty()) { - m_wordBounds = m_selStart; - m_wordBounds.join(m_selEnd); - m_extendSelection = m_wordSelection = true; - m_outsideWord = false; - return true; + SkRegion::Iterator rgnIter(*region); + SkPaint paint; + paint.setARGB(0x66, 0x33, 0xb5, 0xe5); + while (!rgnIter.done()) { + const SkIRect& ir = rgnIter.rect(); + SkRect r; + r.set(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); + canvas->drawRect(r, paint); + rgnIter.next(); } - return false; } -void SelectText::swapAsNeeded() +SelectText::HandleId SelectText::mapId(HandleId id) { - if (m_selStart.fTop >= (m_selEnd.fTop + m_selEnd.fBottom) >> 1 - || (m_selEnd.fTop < (m_selStart.fTop + m_selStart.fBottom) >> 1 - && m_selStart.fRight > m_selEnd.fLeft)) - { - SkTSwap(m_startBase, m_endBase); - SkTSwap(m_selStart, m_selEnd); - m_hitTopLeft ^= true; - DBG_NAV_LOGD("m_hitTopLeft=%s", m_hitTopLeft ? "true" : "false"); - } + if (id == StartHandle || id == EndHandle) + return id; + if (isBaseFirst()) + return (HandleId) (id - 2); + if (id == BaseHandle) + return EndHandle; + return StartHandle; } } diff --git a/Source/WebKit/android/nav/SelectText.h b/Source/WebKit/android/nav/SelectText.h index b454b8e..33d9f3f 100644 --- a/Source/WebKit/android/nav/SelectText.h +++ b/Source/WebKit/android/nav/SelectText.h @@ -34,83 +34,53 @@ #include "SkPicture.h" #include "SkRect.h" #include "SkRegion.h" +#include "wtf/Vector.h" namespace android { -class CachedRoot; - class SelectText : public DrawExtra { public: - SelectText(); + enum HandleId { + StartHandle = 0, + EndHandle = 1, + BaseHandle = 2, + ExtentHandle = 3, + }; + + SelectText() {} virtual ~SelectText(); - virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ); - void extendSelection(const IntRect& vis, int x, int y); - const String getSelection(); - bool hitSelection(int x, int y) const; - void moveSelection(const IntRect& vis, int x, int y); - void reset(); - IntPoint selectableText(const CachedRoot* ); - void selectAll(); - int selectionX() const; - int selectionY() const; - void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; } - void setExtendSelection(bool extend) { m_extendSelection = extend; } - bool startSelection(const CachedRoot* , const IntRect& vis, int x, int y); - bool wordSelection(const CachedRoot* , const IntRect& vis, int x, int y); - void getSelectionRegion(const IntRect& vis, SkRegion *region, LayerAndroid* root); - void updateHandleScale(float handleScale); - void getSelectionHandles(int* handles, LayerAndroid* root); -public: - float m_inverseScale; // inverse scale, x, y used for drawing select path - int m_selectX; - int m_selectY; + + SkRegion* getHightlightRegionsForLayer(int layerId) { + return m_highlightRegions.get(layerId); + } + + void setHighlightRegionsForLayer(int layerId, SkRegion* region) { + m_highlightRegions.set(layerId, region); + } + + virtual void draw(SkCanvas*, LayerAndroid*); + virtual void drawGL(GLExtras*, const LayerAndroid*); + + IntRect& caretRect(HandleId id) { return m_caretRects[mapId(id)]; } + void setCaretRect(HandleId id, const IntRect& rect) { m_caretRects[mapId(id)] = rect; } + int caretLayerId(HandleId id) { return m_caretLayerId[mapId(id)]; } + void setCaretLayerId(HandleId id, int layerId) { m_caretLayerId[mapId(id)] = layerId; } + + bool isBaseFirst() const { return m_baseIsFirst; } + void setBaseFirst(bool isFirst) { m_baseIsFirst = isFirst; } + + void setText(const String& text) { m_text = text.threadsafeCopy(); } + String& getText() { return m_text; } + private: - int m_controlWidth; - int m_controlHeight; - int m_controlSlop; - class FirstCheck; - class EdgeCheck; - void drawSelectionPointer(SkCanvas* , IntRect* ); - void drawSelectionRegion(SkCanvas* , IntRect* ); - SkIRect findClosest(FirstCheck& , const SkPicture& , int* base); - SkIRect findEdge(const SkPicture& , const SkIRect& area, - int x, int y, bool left, int* base); - SkIRect findLeft(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base); - SkIRect findRight(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base); - static void getSelectionArrow(SkPath* ); - void getSelectionCaret(SkPath* ); - bool hitCorner(int cx, int cy, int x, int y) const; - bool hitStartHandle(int x, int y) const; - bool hitEndHandle(int x, int y) const; - void setVisibleRect(const IntRect& ); - void swapAsNeeded(); - SkIPoint m_original; // computed start of extend selection - SkIPoint m_startOffset; // difference from global to layer - SkIRect m_selStart; - SkIRect m_selEnd; - SkIRect m_lastStart; - SkIRect m_lastEnd; - SkIRect m_lastDrawnStart; - SkIRect m_lastDrawnEnd; - SkIRect m_wordBounds; - int m_startBase; - int m_endBase; - int m_layerId; - SkIRect m_visibleRect; // constrains picture computations to visible area - SkRegion m_lastSelRegion; - SkRegion m_selRegion; // computed from sel start, end - SkPicture m_startControl; - SkPicture m_endControl; - const SkPicture* m_picture; - bool m_drawPointer; - bool m_extendSelection; // false when trackball is moving pointer - bool m_flipped; - bool m_hitTopLeft; - bool m_startSelection; - bool m_wordSelection; - bool m_outsideWord; + HandleId mapId(HandleId id); + + typedef HashMap HighlightRegionMap; + HighlightRegionMap m_highlightRegions; + IntRect m_caretRects[2]; + int m_caretLayerId[2]; + bool m_baseIsFirst; + String m_text; }; } diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index 6c49bb7..efdb67f 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -107,6 +107,7 @@ enum FrameCachePermission { AllowNewer }; +#define DRAW_EXTRAS_SIZE 3 enum DrawExtras { // keep this in sync with WebView.java DrawExtrasNone = 0, DrawExtrasFind = 1, @@ -139,7 +140,6 @@ struct JavaGlue { jfieldID m_rectFTop; jmethodID m_rectFWidth; jmethodID m_rectFHeight; - jmethodID m_getTextHandleScale; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } @@ -150,6 +150,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, m_ring((WebViewCore*) viewImpl) , m_isHighEndGfx(isHighEndGfx) { + memset(m_extras, 0, DRAW_EXTRAS_SIZE * sizeof(DrawExtra*)); jclass clazz = env->FindClass("android/webkit/WebView"); // m_javaGlue = new JavaGlue; m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView); @@ -169,7 +170,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz, "viewInvalidateDelayed", "(JIIII)V"); m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "(Z)V"); - m_javaGlue.m_getTextHandleScale = GetJMethod(env, clazz, "getTextHandleScale", "()F"); env->DeleteLocalRef(clazz); jclass rectClass = env->FindClass("android/graphics/Rect"); @@ -222,6 +222,45 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, delete m_frameCacheUI; SkSafeUnref(m_baseLayer); delete m_glDrawFunctor; + for (int i = 0; i < DRAW_EXTRAS_SIZE; i++) + delete m_extras[i]; +} + +DrawExtra* getDrawExtra(DrawExtras extras) +{ + if (extras == DrawExtrasNone) + return 0; + return m_extras[extras - 1]; +} + +DrawExtra* getDrawExtraLegacy(DrawExtras extras) +{ + CachedRoot* root = getFrameCache(AllowNewer); + if (!root) { + DBG_NAV_LOG("!root"); + if (extras == DrawExtrasCursorRing) + resetCursorRing(); + } + DrawExtra* extra = getDrawExtra(extras); + if (!extra) { + switch (extras) { + case DrawExtrasFind: + extra = &m_findOnPage; + break; + case DrawExtrasCursorRing: + if (drawCursorPreamble(root) && m_ring.setup()) { + if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX) + extra = &m_ring; + drawCursorPostamble(); + } + break; + // Just to prevent compiler warnings + case DrawExtrasSelection: + case DrawExtrasNone: + break; + } + } + return extra; } void stopGL() @@ -236,26 +275,6 @@ WebViewCore* getWebViewCore() const { return m_viewImpl; } -float getTextHandleScale() -{ - ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); - JNIEnv* env = JSC::Bindings::getJNIEnv(); - AutoJObject javaObject = m_javaGlue.object(env); - if (!javaObject.get()) - return 0; - float result = env->CallFloatMethod(javaObject.get(), m_javaGlue.m_getTextHandleScale); - checkException(env); - return result; -} - -void updateSelectionHandles() -{ - if (!m_baseLayer) - return; - // Adjust for device density & scale - m_selectText.updateHandleScale(getTextHandleScale()); -} - // removes the cursor altogether (e.g., when going to a new page) void clearCursor() { @@ -464,34 +483,7 @@ bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, } } - CachedRoot* root = getFrameCache(AllowNewer); - if (!root) { - DBG_NAV_LOG("!root"); - if (extras == DrawExtrasCursorRing) - resetCursorRing(); - } - DrawExtra* extra = 0; - switch (extras) { - case DrawExtrasFind: - extra = &m_findOnPage; - break; - case DrawExtrasSelection: - // This will involve a JNI call, but under normal circumstances we will - // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled - // in WebView.java will we hit this (so really debug only) - updateSelectionHandles(); - extra = &m_selectText; - break; - case DrawExtrasCursorRing: - if (drawCursorPreamble(root) && m_ring.setup()) { - if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX) - extra = &m_ring; - drawCursorPostamble(); - } - break; - default: - ; - } + DrawExtra* extra = getDrawExtraLegacy((DrawExtras) extras); unsigned int pic = m_glWebViewState->currentPictureCounter(); m_glWebViewState->glExtras()->setDrawExtra(extra); @@ -522,7 +514,7 @@ bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, return false; } -PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) +PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool split) { PictureSet* ret = 0; if (!m_baseLayer) { @@ -540,33 +532,10 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) if (content->draw(canvas)) ret = split ? new PictureSet(*content) : 0; - CachedRoot* root = getFrameCache(AllowNewer); - if (!root) { - DBG_NAV_LOG("!root"); - if (extras == DrawExtrasCursorRing) - resetCursorRing(); - } - DrawExtra* extra = 0; - switch (extras) { - case DrawExtrasFind: - extra = &m_findOnPage; - break; - case DrawExtrasSelection: - // This will involve a JNI call, but under normal circumstances we will - // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled - // in WebView.java will we hit this (so really debug only) - updateSelectionHandles(); - extra = &m_selectText; - break; - case DrawExtrasCursorRing: - if (drawCursorPreamble(root) && m_ring.setup()) { - extra = &m_ring; - drawCursorPostamble(); - } - break; - default: - ; - } + DrawExtra* extra = getDrawExtraLegacy(extras); + if (extra) + extra->draw(canvas, 0); + #if USE(ACCELERATED_COMPOSITING) LayerAndroid* compositeLayer = compositeRoot(); if (compositeLayer) { @@ -579,11 +548,11 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split) SkAutoCanvasRestore restore(canvas, true); m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); - m_baseLayer->draw(canvas); + m_baseLayer->draw(canvas, extra); } if (extra) { IntRect dummy; // inval area, unused for now - extra->draw(canvas, compositeLayer, &dummy); + extra->drawLegacy(canvas, compositeLayer, &dummy); } #endif return ret; @@ -1144,85 +1113,11 @@ void setHeightCanMeasure(bool measure) String getSelection() { - return m_selectText.getSelection(); -} - -void moveSelection(int x, int y) -{ - m_selectText.moveSelection(getVisibleRect(), x, y); -} - -IntPoint selectableText() -{ - const CachedRoot* root = getFrameCache(DontAllowNewer); - if (!root) - return IntPoint(0, 0); - return m_selectText.selectableText(root); -} - -void selectAll() -{ - m_selectText.selectAll(); -} - -int selectionX() -{ - return m_selectText.selectionX(); -} - -int selectionY() -{ - return m_selectText.selectionY(); -} - -void resetSelection() -{ - m_selectText.reset(); -} - -bool startSelection(int x, int y) -{ - const CachedRoot* root = getFrameCache(DontAllowNewer); - if (!root) - return false; - updateSelectionHandles(); - return m_selectText.startSelection(root, getVisibleRect(), x, y); -} - -bool wordSelection(int x, int y) -{ - const CachedRoot* root = getFrameCache(DontAllowNewer); - if (!root) - return false; - updateSelectionHandles(); - return m_selectText.wordSelection(root, getVisibleRect(), x, y); -} - -bool extendSelection(int x, int y) -{ - m_selectText.extendSelection(getVisibleRect(), x, y); - return true; -} - -bool hitSelection(int x, int y) -{ - updateSelectionHandles(); - return m_selectText.hitSelection(x, y); -} - -void setExtendSelection() -{ - m_selectText.setExtendSelection(true); -} - -void setSelectionPointer(bool set, float scale, int x, int y) -{ - m_selectText.setDrawPointer(set); - if (!set) - return; - m_selectText.m_inverseScale = scale; - m_selectText.m_selectX = x; - m_selectText.m_selectY = y; + SelectText* select = static_cast( + getDrawExtra(WebView::DrawExtrasSelection)); + if (select) + return select->getText(); + return String(); } void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr) @@ -1460,16 +1355,6 @@ void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndic root->setRootLayer(compositeRoot()); } -void getTextSelectionRegion(SkRegion *region) -{ - m_selectText.getSelectionRegion(getVisibleRect(), region, compositeRoot()); -} - -void getTextSelectionHandles(int* handles) -{ - m_selectText.getSelectionHandles(handles, compositeRoot()); -} - void replaceBaseContent(PictureSet* set) { if (!m_baseLayer) @@ -1515,6 +1400,38 @@ FindOnPage& findOnPage() { return m_findOnPage; } +void setDrawExtra(DrawExtra *extra, DrawExtras type) +{ + if (type == DrawExtrasNone) + return; + DrawExtra* old = m_extras[type - 1]; + m_extras[type - 1] = extra; + if (old != extra) { + delete old; + } +} + +void setTextSelection(SelectText *selection) { + setDrawExtra(selection, DrawExtrasSelection); +} + +int getHandleLayerId(SelectText::HandleId handleId, SkIRect& cursorRect) { + SelectText* selectText = static_cast(getDrawExtra(DrawExtrasSelection)); + if (!selectText) + return -1; + int layerId = selectText->caretLayerId(handleId); + const IntRect& r = selectText->caretRect(handleId); + cursorRect.set(r.x(), r.y(), r.maxX(), r.maxY()); + if (layerId != -1) { + LayerAndroid* root = compositeRoot(); + LayerAndroid* layer = root ? root->findById(layerId) : 0; + IntPoint offset; + WebViewCore::layerToAbsoluteOffset(layer, offset); + cursorRect.offset(offset.x(), offset.y()); + } + return layerId; +} + bool m_isDrawingPaused; private: // local state for WebView // private to getFrameCache(); other functions operate in a different thread @@ -1526,9 +1443,9 @@ private: // local state for WebView bool m_heightCanMeasure; int m_lastDx; SkMSec m_lastDxTime; - SelectText m_selectText; FindOnPage m_findOnPage; CursorRing m_ring; + DrawExtra* m_extras[DRAW_EXTRAS_SIZE]; BaseLayerAndroid* m_baseLayer; Functor* m_glDrawFunctor; #if USE(ACCELERATED_COMPOSITING) @@ -1852,7 +1769,8 @@ static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, WebView* webView = GET_NATIVE_VIEW(env, obj); SkRect visibleRect = jrectf_to_rect(env, visible); webView->setVisibleRect(visibleRect); - PictureSet* pictureSet = webView->draw(canvas, color, extras, split); + PictureSet* pictureSet = webView->draw(canvas, color, + static_cast(extras), split); return reinterpret_cast(pictureSet); } @@ -1920,24 +1838,6 @@ static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint nativeView, jint l registerPageSwapCallback); } -static void nativeGetTextSelectionRegion(JNIEnv *env, jobject obj, jint view, - jobject region) -{ - if (!region) - return; - SkRegion* nregion = GraphicsJNI::getNativeRegion(env, region); - ((WebView*)view)->getTextSelectionRegion(nregion); -} - -static void nativeGetSelectionHandles(JNIEnv *env, jobject obj, jint view, - jintArray arr) -{ - int handles[4]; - ((WebView*)view)->getTextSelectionHandles(handles); - env->SetIntArrayRegion(arr, 0, 4, handles); - checkException(env); -} - static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->getBaseLayer(); @@ -2449,51 +2349,6 @@ static int nativeMoveGeneration(JNIEnv *env, jobject obj) return view->moveGeneration(); } -static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y) -{ - GET_NATIVE_VIEW(env, obj)->moveSelection(x, y); -} - -static void nativeResetSelection(JNIEnv *env, jobject obj) -{ - return GET_NATIVE_VIEW(env, obj)->resetSelection(); -} - -static jobject nativeSelectableText(JNIEnv* env, jobject obj) -{ - IntPoint pos = GET_NATIVE_VIEW(env, obj)->selectableText(); - jclass pointClass = env->FindClass("android/graphics/Point"); - jmethodID init = env->GetMethodID(pointClass, "", "(II)V"); - jobject point = env->NewObject(pointClass, init, pos.x(), pos.y()); - env->DeleteLocalRef(pointClass); - return point; -} - -static void nativeSelectAll(JNIEnv* env, jobject obj) -{ - GET_NATIVE_VIEW(env, obj)->selectAll(); -} - -static void nativeSetExtendSelection(JNIEnv *env, jobject obj) -{ - GET_NATIVE_VIEW(env, obj)->setExtendSelection(); -} - -static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y) -{ - return GET_NATIVE_VIEW(env, obj)->startSelection(x, y); -} - -static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y) -{ - return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y); -} - -static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y) -{ - GET_NATIVE_VIEW(env, obj)->extendSelection(x, y); -} - static jobject nativeGetSelection(JNIEnv *env, jobject obj) { WebView* view = GET_NATIVE_VIEW(env, obj); @@ -2502,27 +2357,6 @@ static jobject nativeGetSelection(JNIEnv *env, jobject obj) return wtfStringToJstring(env, selection); } -static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y) -{ - return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y); -} - -static jint nativeSelectionX(JNIEnv *env, jobject obj) -{ - return GET_NATIVE_VIEW(env, obj)->selectionX(); -} - -static jint nativeSelectionY(JNIEnv *env, jobject obj) -{ - return GET_NATIVE_VIEW(env, obj)->selectionY(); -} - -static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jint nativeView, - jboolean set, jfloat scale, jint x, jint y) -{ - ((WebView*)nativeView)->setSelectionPointer(set, scale, x, y); -} - static void nativeRegisterPageSwapCallback(JNIEnv *env, jobject obj, jint nativeView) { ((WebView*)nativeView)->registerPageSwapCallback(); @@ -2675,7 +2509,7 @@ 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->draw(&canvas, 0, 0, false); + view->draw(&canvas, 0, WebView::DrawExtrasNone, false); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); @@ -2767,6 +2601,32 @@ static bool nativeDisableNavcache(JNIEnv *env, jobject obj) #endif } +static void nativeSetTextSelection(JNIEnv *env, jobject obj, jint nativeView, + jint selectionPtr) +{ + SelectText* selection = reinterpret_cast(selectionPtr); + reinterpret_cast(nativeView)->setTextSelection(selection); +} + +static jint nativeGetHandleLayerId(JNIEnv *env, jobject obj, jint nativeView, + jint handleIndex, jobject cursorRect) +{ + WebView* webview = reinterpret_cast(nativeView); + SkIRect nativeRect; + int layerId = webview->getHandleLayerId((SelectText::HandleId) handleIndex, nativeRect); + if (cursorRect) + GraphicsJNI::irect_to_jrect(nativeRect, env, cursorRect); + return layerId; +} + +static jboolean nativeIsBaseFirst(JNIEnv *env, jobject obj, jint nativeView) +{ + WebView* webview = reinterpret_cast(nativeView); + SelectText* select = static_cast( + webview->getDrawExtra(WebView::DrawExtrasSelection)); + return select ? select->isBaseFirst() : false; +} + /* * JNI registration */ @@ -2817,8 +2677,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeDumpDisplayTree }, { "nativeEvaluateLayersAnimations", "(I)Z", (void*) nativeEvaluateLayersAnimations }, - { "nativeExtendSelection", "(II)V", - (void*) nativeExtendSelection }, { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;Z)I", (void*) nativeFindAll }, { "nativeFindNext", "(Z)V", @@ -2875,8 +2733,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeHasFocusNode }, { "nativeHideCursor", "()V", (void*) nativeHideCursor }, - { "nativeHitSelection", "(II)Z", - (void*) nativeHitSelection }, { "nativeImageURI", "(II)Ljava/lang/String;", (void*) nativeImageURI }, { "nativeLayerBounds", "(I)Landroid/graphics/Rect;", @@ -2889,26 +2745,12 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeMoveCursorToNextTextInput }, { "nativeMoveGeneration", "()I", (void*) nativeMoveGeneration }, - { "nativeMoveSelection", "(II)V", - (void*) nativeMoveSelection }, { "nativePointInNavCache", "(III)Z", (void*) nativePointInNavCache }, - { "nativeResetSelection", "()V", - (void*) nativeResetSelection }, - { "nativeSelectableText", "()Landroid/graphics/Point;", - (void*) nativeSelectableText }, - { "nativeSelectAll", "()V", - (void*) nativeSelectAll }, { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V", (void*) nativeSelectBestAt }, { "nativeSelectAt", "(II)V", (void*) nativeSelectAt }, - { "nativeSelectionX", "()I", - (void*) nativeSelectionX }, - { "nativeSelectionY", "()I", - (void*) nativeSelectionY }, - { "nativeSetExtendSelection", "()V", - (void*) nativeSetExtendSelection }, { "nativeSetFindIsEmpty", "()V", (void*) nativeSetFindIsEmpty }, { "nativeSetFindIsUp", "(Z)V", @@ -2917,10 +2759,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSetHeightCanMeasure }, { "nativeSetBaseLayer", "(IILandroid/graphics/Region;ZZZ)V", (void*) nativeSetBaseLayer }, - { "nativeGetTextSelectionRegion", "(ILandroid/graphics/Region;)V", - (void*) nativeGetTextSelectionRegion }, - { "nativeGetSelectionHandles", "(I[I)V", - (void*) nativeGetSelectionHandles }, { "nativeGetBaseLayer", "()I", (void*) nativeGetBaseLayer }, { "nativeReplaceBaseContent", "(I)V", @@ -2929,8 +2767,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCopyBaseContentToPicture }, { "nativeHasContent", "()Z", (void*) nativeHasContent }, - { "nativeSetSelectionPointer", "(IZFII)V", - (void*) nativeSetSelectionPointer }, { "nativeShowCursorTimed", "()V", (void*) nativeShowCursorTimed }, { "nativeRegisterPageSwapCallback", "(I)V", @@ -2951,8 +2787,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeTileProfilingGetInt }, { "nativeTileProfilingGetFloat", "(IILjava/lang/String;)F", (void*) nativeTileProfilingGetFloat }, - { "nativeStartSelection", "(II)Z", - (void*) nativeStartSelection }, { "nativeStopGL", "()V", (void*) nativeStopGL }, { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;", @@ -2961,8 +2795,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeTextGeneration }, { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V", (void*) nativeUpdateCachedTextfield }, - { "nativeWordSelection", "(II)Z", - (void*) nativeWordSelection }, { "nativeGetBlockLeftEdge", "(IIF)I", (void*) nativeGetBlockLeftEdge }, { "nativeScrollableLayer", "(IILandroid/graphics/Rect;Landroid/graphics/Rect;)I", @@ -2987,6 +2819,12 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeDisableNavcache }, { "nativeFocusCandidateIsEditableText", "(I)Z", (void*) nativeFocusCandidateIsEditableText }, + { "nativeSetTextSelection", "(II)V", + (void*) nativeSetTextSelection }, + { "nativeGetHandleLayerId", "(IILandroid/graphics/Rect;)I", + (void*) nativeGetHandleLayerId }, + { "nativeIsBaseFirst", "(I)Z", + (void*) nativeIsBaseFirst }, }; int registerWebView(JNIEnv* env) -- cgit v1.1