summaryrefslogtreecommitdiffstats
path: root/WebKit/android/nav
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/android/nav')
-rw-r--r--WebKit/android/nav/CacheBuilder.cpp65
-rw-r--r--WebKit/android/nav/CacheBuilder.h2
-rw-r--r--WebKit/android/nav/CachedFrame.cpp1
-rw-r--r--WebKit/android/nav/CachedInput.cpp7
-rw-r--r--WebKit/android/nav/CachedInput.h5
-rw-r--r--WebKit/android/nav/CachedNode.cpp3
-rw-r--r--WebKit/android/nav/CachedNode.h4
-rw-r--r--WebKit/android/nav/CachedNodeType.h4
-rw-r--r--WebKit/android/nav/CachedPrefix.h7
-rw-r--r--WebKit/android/nav/FindCanvas.cpp3
-rw-r--r--WebKit/android/nav/FindCanvas.h1
-rw-r--r--WebKit/android/nav/SelectText.cpp1282
-rw-r--r--WebKit/android/nav/SelectText.h59
-rw-r--r--WebKit/android/nav/WebView.cpp361
14 files changed, 1507 insertions, 297 deletions
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp
index 7ee2a16..1c8af5e 100644
--- a/WebKit/android/nav/CacheBuilder.cpp
+++ b/WebKit/android/nav/CacheBuilder.cpp
@@ -412,7 +412,7 @@ void CacheBuilder::Debug::groups() {
comma(scratch);
Element* element = static_cast<Element*>(node);
if (node->isElementNode() && element->hasID())
- wideString(element->getIDAttribute());
+ wideString(element->getIdAttribute());
else if (node->isTextNode()) {
#if 01 // set to one to abbreviate text that can be omitted from the address detection code
if (rect.isEmpty() && node->textContent().length() > 100) {
@@ -469,22 +469,48 @@ void CacheBuilder::Debug::groups() {
}
}
}
- count++;
- newLine();
-#if USE(ACCELERATED_COMPOSITING)
- if (renderer && layer) {
+ if (renderer) {
+ RenderStyle* style = renderer->style();
+ snprintf(scratch, sizeof(scratch), "// renderStyle:"
+ " visibility=%s hasBackGround=%d"
+ " tapHighlightColor().alpha()=0x%02x",
+ style->visibility() == HIDDEN ? "HIDDEN" : "VISIBLE",
+ renderer->hasBackground(), style->tapHighlightColor().alpha());
+ newLine();
+ print(scratch);
+ RenderBlock* renderBlock = static_cast<RenderBlock*>(renderer);
+ if (renderer->isRenderBlock() && renderBlock->hasColumns()) {
+ const RenderBox* box = static_cast<RenderBox*>(renderer);
+ const IntRect& oRect = box->visibleOverflowRect();
+ snprintf(scratch, sizeof(scratch), "// renderBlock:"
+ " columnRects=%d columnGap=%d direction=%d"
+ " hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)",
+ renderBlock->columnRects(), renderBlock->columnGap(),
+ renderBlock->style()->direction(), renderer->hasOverflowClip(),
+ oRect.x(), oRect.y(), oRect.width(), oRect.height());
+ newLine();
+ print(scratch);
+ }
+ }
+ #if USE(ACCELERATED_COMPOSITING)
+ if (renderer && renderer->hasLayer()) {
+ RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
RenderLayerBacking* back = layer->backing();
GraphicsLayerAndroid* grLayer = static_cast
<GraphicsLayerAndroid*>(back ? back->graphicsLayer() : 0);
LayerAndroid* aLayer = grLayer ? grLayer->contentLayer() : 0;
const SkPicture* pict = aLayer ? aLayer->picture() : 0;
+ const IntRect& r = renderer->absoluteBoundingBoxRect();
snprintf(scratch, sizeof(scratch), "// layer:%p back:%p"
- " gLayer:%p aLayer:%p pict:%p", layer, back, grLayer,
- aLayer, pict);
- print(scratch);
+ " gLayer:%p aLayer:%p pict:%p r:(%d,%d,w=%d,h=%d)",
+ layer, back, grLayer, aLayer, pict, r.x(), r.y(),
+ r.width(), r.height());
newLine();
- }
-#endif
+ print(scratch);
+ }
+ #endif
+ count++;
+ newLine();
} while ((node = node->traverseNextNode()) != NULL);
DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1);
DUMP_NAV_LOGD("\n");
@@ -841,6 +867,7 @@ bool CacheBuilder::AnyIsClick(Node* node)
void CacheBuilder::buildCache(CachedRoot* root)
{
Frame* frame = FrameAnd(this);
+ mPictureSetDisabled = false;
BuildFrame(frame, frame, root, (CachedFrame*) root);
root->finishInit(); // set up frame parent pointers, child pointers
setData((CachedFrame*) root);
@@ -1028,7 +1055,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
RenderStyle* style = nodeRenderer->style();
if (style->visibility() == HIDDEN)
continue;
- isTransparent = style->hasBackground() == false;
+ isTransparent = nodeRenderer->hasBackground() == false;
#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
hasCursorRing = style->tapHighlightColor().alpha() > 0;
#endif
@@ -1105,6 +1132,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
if (node->hasTagName(HTMLNames::bodyTag))
bodyPos = originalAbsBounds.location();
+ else if (node->hasTagName(HTMLNames::canvasTag))
+ mPictureSetDisabled = true;
if (checkForPluginViewThatWantsFocus(nodeRenderer)) {
bounds = absBounds;
isUnclipped = true;
@@ -1112,13 +1141,18 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
type = PLUGIN_CACHEDNODETYPE;
goto keepNode;
}
+ // Only use the root contentEditable element
+ if (node->isContentEditable() && !node->parent()->isContentEditable()) {
+ bounds = absBounds;
+ takesFocus = true;
+ type = CONTENT_EDITABLE_CACHEDNODETYPE;
+ goto keepNode;
+ }
if (nodeRenderer->isRenderBlock()) {
RenderBlock* renderBlock = (RenderBlock*) nodeRenderer;
if (renderBlock->hasColumns()) {
columns = renderBlock->columnRects();
-#ifdef ANDROID_EXPOSE_COLUMN_GAP
columnGap = renderBlock->columnGap();
-#endif
direction = renderBlock->style()->direction();
}
}
@@ -1216,6 +1250,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string()))
// Set the exported string for all non-javascript anchors.
exported = href.string().threadsafeCopy();
+ } else if (node->hasTagName(HTMLNames::selectTag)) {
+ type = SELECT_CACHEDNODETYPE;
}
if (type == TEXT_INPUT_CACHEDNODETYPE) {
RenderTextControl* renderText =
@@ -2673,7 +2709,8 @@ bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node,
do {
do {
do {
- node = node->traverseNextNode();
+ if (node)
+ node = node->traverseNextNode();
if (node == NULL || node->hasTagName(HTMLNames::aTag)
|| node->hasTagName(HTMLNames::inputTag)
|| node->hasTagName(HTMLNames::textareaTag)) {
diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h
index 4ded58d..8183954 100644
--- a/WebKit/android/nav/CacheBuilder.h
+++ b/WebKit/android/nav/CacheBuilder.h
@@ -94,6 +94,7 @@ public:
static IntRect getAreaRect(const HTMLAreaElement* area);
static void GetGlobalOffset(Frame* , int* x, int * y);
static void GetGlobalOffset(Node* , int* x, int * y);
+ bool pictureSetDisabled() { return mPictureSetDisabled; }
static bool validNode(Frame* startFrame, void* framePtr, void* nodePtr);
private:
enum AddressProgress {
@@ -249,6 +250,7 @@ private:
Node* tryFocus(Direction direction);
Node* trySegment(Direction direction, int mainStart, int mainEnd);
CachedNodeBits mAllowableTypes;
+ bool mPictureSetDisabled;
#if DUMP_NAV_CACHE
public:
class Debug {
diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp
index ce5600b..ff13508 100644
--- a/WebKit/android/nav/CachedFrame.cpp
+++ b/WebKit/android/nav/CachedFrame.cpp
@@ -1390,6 +1390,7 @@ void CachedFrame::Debug::print() const
const CachedInput* input = b->textInput(node);
if (input)
input->mDebug.print();
+ DUMP_NAV_LOGD("\n");
}
DUMP_NAV_LOGD("// }; // end of nodes\n");
#if USE(ACCELERATED_COMPOSITING)
diff --git a/WebKit/android/nav/CachedInput.cpp b/WebKit/android/nav/CachedInput.cpp
index 924bbca..608c41b 100644
--- a/WebKit/android/nav/CachedInput.cpp
+++ b/WebKit/android/nav/CachedInput.cpp
@@ -28,6 +28,11 @@
namespace android {
+void CachedInput::init() {
+ bzero(this, sizeof(CachedInput));
+ mName = WebCore::String();
+}
+
#if DUMP_NAV_CACHE
#define DEBUG_PRINT_BOOL(field) \
@@ -55,7 +60,7 @@ void CachedInput::Debug::print() const
{
CachedInput* b = base();
printWebCoreString("// char* mName=\"", b->mName);
- DUMP_NAV_LOGD("// void* mForm=%p;", b->mForm);
+ DUMP_NAV_LOGD("// void* mForm=%p;\n", b->mForm);
DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength);
DUMP_NAV_LOGD("// int mTextSize=%d;\n", b->mTextSize);
DUMP_NAV_LOGD("// int mInputType=%d;\n", b->mInputType);
diff --git a/WebKit/android/nav/CachedInput.h b/WebKit/android/nav/CachedInput.h
index 42cadf1..a3d6b10 100644
--- a/WebKit/android/nav/CachedInput.h
+++ b/WebKit/android/nav/CachedInput.h
@@ -39,10 +39,7 @@ public:
// constructor
}
void* formPointer() const { return mForm; }
- void init() {
- bzero(this, sizeof(CachedInput));
- mName = WebCore::String();
- }
+ void init();
WebCore::HTMLInputElement::InputType inputType() const { return mInputType; }
bool isRtlText() const { return mIsRtlText; }
bool isTextField() const { return mIsTextField; }
diff --git a/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp
index 0c9d541..8fc5f5b 100644
--- a/WebKit/android/nav/CachedNode.cpp
+++ b/WebKit/android/nav/CachedNode.cpp
@@ -367,6 +367,8 @@ const char* CachedNode::Debug::type(android::CachedNodeType t) const
case FRAME_CACHEDNODETYPE: return "FRAME"; break;
case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break;
case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break;
+ case SELECT_CACHEDNODETYPE: return "SELECT"; break;
+ case CONTENT_EDITABLE_CACHEDNODETYPE: return "CONTENT_EDITABLE"; break;
default: return "???";
}
}
@@ -419,7 +421,6 @@ void CachedNode::Debug::print() const
DEBUG_PRINT_BOOL(mLast);
DEBUG_PRINT_BOOL(mUseBounds);
DEBUG_PRINT_BOOL(mUseHitBounds);
- DUMP_NAV_LOGD("\n");
}
#endif
diff --git a/WebKit/android/nav/CachedNode.h b/WebKit/android/nav/CachedNode.h
index f3cfd98..09f53c3 100644
--- a/WebKit/android/nav/CachedNode.h
+++ b/WebKit/android/nav/CachedNode.h
@@ -108,6 +108,7 @@ public:
int index() const { return mIndex; }
void init(WebCore::Node* node);
bool isAnchor() const { return mType == ANCHOR_CACHEDNODETYPE; }
+ bool isContentEditable() const { return mType == CONTENT_EDITABLE_CACHEDNODETYPE; }
bool isCursor() const { return mIsCursor; }
bool isArea() const { return mType == AREA_CACHEDNODETYPE; }
bool isFocus() const { return mIsFocus; }
@@ -118,6 +119,7 @@ public:
return clip.intersects(bounds(frame));
}
bool isPlugin() const { return mType == PLUGIN_CACHEDNODETYPE; }
+ bool isSelect() const { return mType == SELECT_CACHEDNODETYPE; }
bool isSyntheticLink() const {
return mType >= ADDRESS_CACHEDNODETYPE && mType <= PHONE_CACHEDNODETYPE;
}
@@ -173,7 +175,7 @@ public:
const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; }
bool useBounds() const { return mUseBounds; }
bool useHitBounds() const { return mUseHitBounds; }
- bool wantsKeyEvents() const { return isTextInput() || isPlugin(); }
+ bool wantsKeyEvents() const { return isTextInput() || isPlugin() || isContentEditable(); }
private:
friend class CacheBuilder;
WebCore::String mExport;
diff --git a/WebKit/android/nav/CachedNodeType.h b/WebKit/android/nav/CachedNodeType.h
index 21e2d40..8bc9328 100644
--- a/WebKit/android/nav/CachedNodeType.h
+++ b/WebKit/android/nav/CachedNodeType.h
@@ -37,7 +37,9 @@ enum CachedNodeType {
AREA_CACHEDNODETYPE,
FRAME_CACHEDNODETYPE,
PLUGIN_CACHEDNODETYPE,
- TEXT_INPUT_CACHEDNODETYPE
+ TEXT_INPUT_CACHEDNODETYPE,
+ SELECT_CACHEDNODETYPE,
+ CONTENT_EDITABLE_CACHEDNODETYPE
};
enum CachedNodeBits {
diff --git a/WebKit/android/nav/CachedPrefix.h b/WebKit/android/nav/CachedPrefix.h
index b682288..73a5c2c 100644
--- a/WebKit/android/nav/CachedPrefix.h
+++ b/WebKit/android/nav/CachedPrefix.h
@@ -43,4 +43,11 @@
#define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning
+#ifndef BZERO_DEFINED
+#define BZERO_DEFINED
+// http://www.opengroup.org/onlinepubs/000095399/functions/bzero.html
+// For maximum portability, it is recommended to replace the function call to bzero() as follows:
+#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
+#endif
+
#endif
diff --git a/WebKit/android/nav/FindCanvas.cpp b/WebKit/android/nav/FindCanvas.cpp
index d8e908b..8eaaaef 100644
--- a/WebKit/android/nav/FindCanvas.cpp
+++ b/WebKit/android/nav/FindCanvas.cpp
@@ -98,7 +98,7 @@ GlyphSet::~GlyphSet() {
// part of mLowerGlyphs
}
-GlyphSet::GlyphSet& GlyphSet::operator=(GlyphSet& src) {
+GlyphSet& GlyphSet::operator=(GlyphSet& src) {
mTypeface = src.mTypeface;
mCount = src.mCount;
if (mCount > MAX_STORAGE_COUNT) {
@@ -675,4 +675,3 @@ void FindOnPage::setMatches(WTF::Vector<MatchInfo>* matches)
}
}
-
diff --git a/WebKit/android/nav/FindCanvas.h b/WebKit/android/nav/FindCanvas.h
index b9dbeea..34929ec 100644
--- a/WebKit/android/nav/FindCanvas.h
+++ b/WebKit/android/nav/FindCanvas.h
@@ -220,6 +220,7 @@ public:
virtual ~FindOnPage() { delete m_matches; }
void clearCurrentLocation() { m_hasCurrentLocation = false; }
IntRect currentMatchBounds() const;
+ int currentMatchIndex() const { return m_findIndex; }
bool currentMatchIsInLayer() const;
virtual void draw(SkCanvas* , LayerAndroid* );
void findNext(bool forward);
diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp
index e471307..9df6ef5 100644
--- a/WebKit/android/nav/SelectText.cpp
+++ b/WebKit/android/nav/SelectText.cpp
@@ -23,9 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define LOG_TAG "webcoreglue"
+#define LOG_TAG "webviewglue"
#include "CachedPrefix.h"
+#include "BidiResolver.h"
#include "CachedRoot.h"
#include "LayerAndroid.h"
#include "SelectText.h"
@@ -39,26 +40,156 @@
#include "SkRect.h"
#include "SkRegion.h"
#include "SkUtils.h"
+#include "TextRun.h"
#ifdef DEBUG_NAV_UI
-#include "CString.h"
+#include <wtf/text/CString.h>
#endif
+#define VERBOSE_LOGGING 0
+// #define EXTRA_NOISY_LOGGING 1
+
+// TextRunIterator has been copied verbatim from GraphicsContext.cpp
+namespace WebCore {
+
+class TextRunIterator {
+public:
+ TextRunIterator()
+ : m_textRun(0)
+ , m_offset(0)
+ {
+ }
+
+ TextRunIterator(const TextRun* textRun, unsigned offset)
+ : m_textRun(textRun)
+ , m_offset(offset)
+ {
+ }
+
+ TextRunIterator(const TextRunIterator& other)
+ : m_textRun(other.m_textRun)
+ , m_offset(other.m_offset)
+ {
+ }
+
+ unsigned offset() const { return m_offset; }
+ void increment() { m_offset++; }
+ bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
+ UChar current() const { return (*m_textRun)[m_offset]; }
+ WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
+
+ bool operator==(const TextRunIterator& other)
+ {
+ return m_offset == other.m_offset && m_textRun == other.m_textRun;
+ }
+
+ bool operator!=(const TextRunIterator& other) { return !operator==(other); }
+
+private:
+ const TextRun* m_textRun;
+ int m_offset;
+};
+
+// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
+void ReverseBidi(UChar* chars, int len) {
+ using namespace WTF::Unicode;
+ WTF::Vector<UChar> result;
+ result.reserveCapacity(len);
+ TextRun run(chars, len);
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
+ bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
+ BidiContext::create(0, LeftToRight, false)));
+ bidiResolver.setPosition(TextRunIterator(&run, 0));
+ bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
+ if (!bidiResolver.runCount())
+ return;
+ BidiCharacterRun* bidiRun = bidiResolver.firstRun();
+ while (bidiRun) {
+ int bidiStart = bidiRun->start();
+ int bidiStop = bidiRun->stop();
+ int size = result.size();
+ int bidiCount = bidiStop - bidiStart;
+ result.append(chars + bidiStart, bidiCount);
+ if (bidiRun->level() % 2) {
+ UChar* start = &result[size];
+ UChar* end = start + bidiCount;
+ // reverse the order of any RTL substrings
+ while (start < end) {
+ UChar temp = *start;
+ *start++ = *--end;
+ *end = temp;
+ }
+ start = &result[size];
+ end = start + bidiCount - 1;
+ // if the RTL substring had a surrogate pair, restore its order
+ while (start < end) {
+ UChar trail = *start++;
+ if (!U16_IS_SURROGATE(trail))
+ continue;
+ start[-1] = *start; // lead
+ *start++ = trail;
+ }
+ }
+ bidiRun = bidiRun->next();
+ }
+ bidiResolver.deleteRuns();
+ memcpy(chars, &result[0], len * sizeof(UChar));
+}
+
+}
+
namespace android {
+/* SpaceBounds and SpaceCanvas are used to measure the left and right side
+ * bearings of two consecutive glyphs to help determine if the glyphs were
+ * originally laid out with a space character between the glyphs.
+ */
+class SpaceBounds : public SkBounder {
+public:
+ virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& rec)
+ {
+ mFirstGlyph = mLastGlyph;
+ mLastGlyph = rec;
+ return false;
+ }
+
+ SkBounder::GlyphRec mFirstGlyph;
+ SkBounder::GlyphRec mLastGlyph;
+};
+
+class SpaceCanvas : public SkCanvas {
+public:
+ SpaceCanvas(const SkIRect& area)
+ {
+ setBounder(&mBounder);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
+ area.height());
+ setBitmapDevice(bitmap);
+ translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
+ }
+
+ SpaceBounds mBounder;
+};
+
+#define HYPHEN_MINUS 0x2D // ASCII hyphen
+#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() : mMatrix(NULL), mPaint(NULL) {}
-
- virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
- const void* text) {
- mMatrix = &matrix;
- mPaint = &paint;
- mText = static_cast<const uint16_t*>(text);
- mY = y;
- mBase = mBottom = mTop = INT_MAX;
+ CommonCheck(int width, int height)
+ : mHeight(height)
+ , mLastUni(0)
+ , mMatrix(0)
+ , mPaint(0)
+ , mWidth(width)
+ {
+ mLastGlyph.fGlyphID = static_cast<uint16_t>(-1);
+ reset();
}
-
+
int base() {
if (mBase == INT_MAX) {
SkPoint result;
@@ -78,7 +209,148 @@ public:
}
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) && newBaseLine)
+ {
+ return false;
+ }
+ return isSpace(rec);
+ }
+
+ void finishGlyph()
+ {
+ mLastGlyph = mLastCandidate;
+ mLastUni = mLastUniCandidate;
+ }
+
+ SkUnichar getUniChar(const SkBounder::GlyphRec& rec)
+ {
+ SkUnichar unichar;
+ SkPaint utfPaint = *mPaint;
+ utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
+ return unichar;
+ }
+
+ bool isSpace(const SkBounder::GlyphRec& rec)
+ {
+ DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)"
+ " rec=((%g, %g),(%g, %g), %d)"
+ " mMinSpaceWidth=%g 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,
+ SkFixedToScalar(mMinSpaceWidth),
+ 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
+ uint16_t test[2];
+ test[0] = mLastGlyph.fGlyphID;
+ test[1] = rec.fGlyphID;
+ SkIRect area;
+ area.set(0, 0, mWidth, mHeight);
+ SpaceCanvas spaceChecker(area);
+ spaceChecker.drawText(test, sizeof(test),
+ SkFixedToScalar(mLastGlyph.fLSB.fX),
+ SkFixedToScalar(mLastGlyph.fLSB.fY), *mPaint);
+ const SkBounder::GlyphRec& g1 = spaceChecker.mBounder.mFirstGlyph;
+ const SkBounder::GlyphRec& g2 = spaceChecker.mBounder.mLastGlyph;
+ DBG_NAV_LOGD("g1=(%g, %g,%g, %g) g2=(%g, %g, %g, %g)",
+ SkFixedToScalar(g1.fLSB.fX), SkFixedToScalar(g1.fLSB.fY),
+ SkFixedToScalar(g1.fRSB.fX), SkFixedToScalar(g1.fRSB.fY),
+ SkFixedToScalar(g2.fLSB.fX), SkFixedToScalar(g2.fLSB.fY),
+ SkFixedToScalar(g2.fRSB.fX), SkFixedToScalar(g2.fRSB.fY));
+ gapOne = SkFixedAbs(gapOne);
+ gapTwo = SkFixedAbs(gapTwo);
+ SkFixed gap = gapOne < gapTwo ? gapOne : gapTwo;
+ SkFixed overlap = g2.fLSB.fX - g1.fRSB.fX;
+ if (overlap < 0)
+ gap -= overlap;
+ DBG_NAV_LOGD("gap=%g overlap=%g gapOne=%g gapTwo=%g minSpaceWidth()=%g",
+ SkFixedToScalar(gap), SkFixedToScalar(overlap),
+ SkFixedToScalar(gapOne), SkFixedToScalar(gapTwo),
+ SkFixedToScalar(minSpaceWidth()));
+ // FIXME: the -1/2 below takes care of slop beween the computed gap
+ // and the actual space width -- it's a rounding error from
+ // moving from fixed to float and back and could be much smaller.
+ return gap >= minSpaceWidth() - SK_Fixed1 / 2;
+ }
+
+ SkFixed minSpaceWidth()
+ {
+ if (mMinSpaceWidth == SK_FixedMax) {
+ SkPaint charPaint = *mPaint;
+ charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ SkScalar width = charPaint.measureText(" ", 1);
+ mMinSpaceWidth = SkScalarToFixed(width * mMatrix->getScaleX());
+ DBG_NAV_LOGD("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);
+ }
+
+ 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;
+ mPaint = check.mPaint;
+ reset();
+ }
+
+ void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
+ const void* text)
+ {
+ mMatrix = &matrix;
+ mPaint = &paint;
+ mText = static_cast<const uint16_t*>(text);
+ mY = y;
+ reset();
+ }
+
int top() {
if (mTop == INT_MAX) {
SkPoint result;
@@ -89,127 +361,417 @@ public:
}
return mTop;
}
-
-protected:
+
+#if DEBUG_NAV_UI
+ // make current (possibily uncomputed) value visible for debugging
+ int topDebug() const
+ {
+ return mTop;
+ }
+#endif
+
+protected:
+ int mHeight;
+ SkBounder::GlyphRec mLastCandidate;
+ SkBounder::GlyphRec mLastGlyph;
+ SkUnichar mLastUni;
+ SkUnichar mLastUniCandidate;
const SkMatrix* mMatrix;
const SkPaint* mPaint;
const uint16_t* mText;
+ int mWidth;
SkScalar mY;
+private:
int mBase;
int mBottom;
+ SkFixed mMinSpaceWidth;
int mTop;
+ friend class EdgeCheck;
};
class FirstCheck : public CommonCheck {
public:
- FirstCheck(int x, int y)
- : mDistance(INT_MAX), mFocusX(x), mFocusY(y) {
- mBestBounds.setEmpty();
+ FirstCheck(int x, int y, const SkIRect& area)
+ : INHERITED(area.width(), area.height())
+ , mFocusX(x - area.fLeft)
+ , mFocusY(y - area.fTop)
+ , mRecordGlyph(false)
+ {
+ reset();
}
- const SkIRect& bestBounds() {
- DBG_NAV_LOGD("mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
+ const SkIRect& adjustedBounds(const SkIRect& area, int* base)
+ {
+ *base = mBestBase + area.fTop;
+ mBestBounds.offset(area.fLeft, area.fTop);
+ DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
- mBestBounds.fBottom, mTop, mBottom);
- return mBestBounds;
- }
-
- void offsetBounds(int dx, int dy) {
- mBestBounds.offset(dx, dy);
+ mBestBounds.fBottom, topDebug(), bottomDebug());
+ return mBestBounds;
}
-
- virtual bool onIRect(const SkIRect& rect) {
- int dx = ((rect.fLeft + rect.fRight) >> 1) - mFocusX;
- int dy = ((top() + bottom()) >> 1) - 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
+ */
+ int dx = rect.fLeft + rect.fRight - (mFocusX << 1);
+ int dy = top() + bottom() - (mFocusY << 1);
int distance = dx * dx + dy * dy;
#ifdef EXTRA_NOISY_LOGGING
if (distance < 500 || abs(distance - mDistance) < 500)
- DBG_NAV_LOGD("distance=%d mDistance=%d", distance, mDistance);
+ DBG_NAV_LOGD("FirstCheck distance=%d mDistance=%d", distance, mDistance);
#endif
if (mDistance > distance) {
- mDistance = distance;
+ mBestBase = base();
mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
-#ifdef EXTRA_NOISY_LOGGING
- DBG_NAV_LOGD("mBestBounds={%d,%d,r=%d,b=%d}",
- mBestBounds.fLeft, mBestBounds.fTop,
- mBestBounds.fRight, mBestBounds.fBottom);
-#endif
+ if (distance < 100) {
+ DBG_NAV_LOGD("FirstCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom, distance >> 2);
+ }
+ mDistance = distance;
+ if (mRecordGlyph)
+ recordGlyph(rec);
}
return false;
}
+
+ void reset()
+ {
+ mBestBounds.setEmpty();
+ mDistance = INT_MAX;
+ }
+
+ void setRecordGlyph()
+ {
+ mRecordGlyph = true;
+ }
+
protected:
+ int mBestBase;
SkIRect mBestBounds;
int mDistance;
int mFocusX;
int mFocusY;
+ bool mRecordGlyph;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class EdgeCheck : public FirstCheck {
+public:
+ EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left)
+ : INHERITED(x, y, area)
+ , mLast(area.width(), area.height())
+ , mLeft(left)
+ {
+ mLast.set(last);
+ mLastGlyph = last.mLastGlyph;
+ mLastUni = last.mLastUni;
+ }
+
+ 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;
+ if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) {
+ if (abs(dx) <= 10 && abs(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;
+ }
+ int distance = dx * dx + dy * dy;
+ if (mDistance > distance) {
+ 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);
+ mDistance = distance;
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ if (distance <= 100) {
+ DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} distance=%d",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom, distance);
+ }
+ }
+ 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);
+ }
+
+protected:
+ CommonCheck mLast;
+ bool mLeft;
+private:
+ typedef FirstCheck INHERITED;
+};
+
+class FindFirst : public CommonCheck {
+public:
+ FindFirst(int width, int height)
+ : INHERITED(width, height)
+ {
+ mBestBounds.set(width, height, width, 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(int width, int height)
+ : INHERITED(width, height)
+ {
+ 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 MultilineBuilder : public CommonCheck {
public:
- MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy,
- SkRegion* region)
- : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) {
+ MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, SkRegion* region)
+ : INHERITED(area.width(),area.height())
+ , mCapture(false)
+ , mEnd(end)
+ , mEndBase(endBase)
+ , mFlipped(false)
+ , mSelectRegion(region)
+ , mStart(start)
+ , mStartBase(startBase)
+ {
+ mEnd.offset(-area.fLeft, -area.fTop);
+ mEndBase -= area.fTop;
mLast.setEmpty();
mLastBase = INT_MAX;
- mStart.offset(-dx, -dy);
- mEnd.offset(-dx, -dy);
+ mStart.offset(-area.fLeft, -area.fTop);
+ mStartBase -= area.fTop;
}
- virtual bool onIRect(const SkIRect& rect) {
- bool captureLast = false;
- if ((rect.fLeft == mStart.fLeft && rect.fRight == mStart.fRight &&
- top() == mStart.fTop && bottom() == mStart.fBottom) ||
- (rect.fLeft == mEnd.fLeft && rect.fRight == mEnd.fRight &&
- top() == mEnd.fTop && bottom() == mEnd.fBottom)) {
- captureLast = mCapture;
- mCapture ^= true;
+ void finish() {
+ if (!mFlipped || SkIRect::Intersects(mLast, mSelectRect)) {
+ 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);
}
- if (mCapture || captureLast) {
- SkIRect full;
- full.set(rect.fLeft, top(), rect.fRight, bottom());
- if ((mLast.fTop < base() && mLast.fBottom >= base())
- || (mLastBase <= full.fBottom && mLastBase > full.fTop)) {
- if (full.fLeft > mLast.fRight)
- full.fLeft = mLast.fRight;
- else if (full.fRight < mLast.fLeft)
- full.fRight = mLast.fLeft;
+ }
+
+ // 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;
+ mSelectRect = mStart;
+ mSelectRect.join(mEnd);
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ 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) {
+ if (fullBase < mStart.fTop || fullBase > mEnd.fBottom
+ || (baseLinesAgree(mStart, mStartBase, full, fullBase)
+ && full.fLeft < mStart.fLeft)
+ || (baseLinesAgree(mEnd, mEndBase, full, fullBase)
+ && full.fRight > mEnd.fRight)) {
+ return false;
+ }
+ if (baseLinesAgree(mLast, mLastBase, full, fullBase)
+ && (full.fLeft - mLast.fRight < minSpaceWidth() * 3
+ || (SkIRect::Intersects(full, mSelectRect)
+ && SkIRect::Intersects(mLast, mSelectRect)))) {
+ mLast.join(full);
+ return false;
}
- mSelectRegion->op(full, SkRegion::kUnion_Op);
- DBG_NAV_LOGD("MultilineBuilder full=(%d,%d,r=%d,b=%d)",
- full.fLeft, full.fTop, full.fRight, full.fBottom);
+ finish();
mLast = full;
- mLastBase = base();
- if (mStart == mEnd)
- mCapture = false;
+ mLastBase = fullBase;
+ return false;
+ }
+ if (full == mStart)
+ mCapture = true;
+ if (mCapture) {
+ if (baseLinesAgree(mLast, mLastBase, full, fullBase))
+ mLast.join(full);
+ else {
+ finish();
+ mLast = full;
+ mLastBase = fullBase;
+ }
}
+ if (full == mEnd)
+ mCapture = false;
return false;
}
-protected:
- SkIRect mStart;
+
+protected:
+ bool mCapture;
SkIRect mEnd;
+ int mEndBase;
+ bool mFlipped;
SkIRect mLast;
int mLastBase;
+ SkIRect mSelectRect;
SkRegion* mSelectRegion;
- bool mCapture;
+ SkIRect mStart;
+ int mStartBase;
+private:
+ typedef CommonCheck INHERITED;
};
-#define HYPHEN_MINUS 0x2D // ASCII hyphen
-#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
-#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
+static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
+{
+ return first->fTop < second->fTop;
+}
class TextExtractor : public CommonCheck {
public:
- TextExtractor(const SkRegion& region) : mSelectRegion(region),
- mSkipFirstSpace(true) { // don't start with a space
+ TextExtractor(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, bool flipped)
+ : INHERITED(area.width(), area.height())
+ , mEnd(end)
+ , mEndBase(endBase)
+ , mFlipped(flipped)
+ , mRecord(false)
+ , mSelectRect(start)
+ , mSkipFirstSpace(true) // don't start with a space
+ , mStart(start)
+ , mStartBase(startBase)
+ {
+ mEmpty.setEmpty();
+ mEnd.offset(-area.fLeft, -area.fTop);
+ mEndBase -= area.fTop;
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mSelectRect.join(end);
+ mSelectRect.offset(-area.fLeft, -area.fTop);
+ mStart.offset(-area.fLeft, -area.fTop);
+ mStartBase -= area.fTop;
+ }
+
+ void addCharacter(const SkBounder::GlyphRec& rec)
+ {
+ 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];
+ }
}
- virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
- const void* text) {
- INHERITED::setUp(paint, matrix, y, text);
- SkPaint charPaint = paint;
- charPaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
- mMinSpaceWidth = std::max(0, SkScalarToFixed(
- charPaint.measureText(" ", 1)) - SK_Fixed1);
+ void finish()
+ {
+ Vector<SkIRect*> sortedBounds;
+ SkTDArray<uint16_t> 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++) {
+ if (sortedBounds[index]->isEmpty())
+ continue;
+ int order = sortedBounds[index] - &mSelectBounds[0];
+ int start = order > 0 ? mSelectCount[order - 1] : 0;
+ int end = mSelectCount[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,
@@ -217,64 +779,85 @@ public:
{
SkIRect full;
full.set(rect.fLeft, top(), rect.fRight, bottom());
- if (mSelectRegion.contains(full)) {
- if (!mSkipFirstSpace && (mLastUni < HYPHEN || mLastUni > HORZ_BAR)
- && mLastUni != HYPHEN_MINUS
- && (mLastGlyph.fLSB.fY != rec.fLSB.fY // new baseline
- || mLastGlyph.fLSB.fX > rec.fLSB.fX // glyphs are LTR
- || mLastGlyph.fRSB.fX + mMinSpaceWidth < rec.fLSB.fX)) {
- DBG_NAV_LOGD("TextExtractor append space"
- " mLast=(%d,%d,r=%d,b=%d) mLastGlyph=((%g,%g),(%g,%g),%d)"
- " full=(%d,%d,r=%d,b=%d) rec=((%g,%g),(%g,%g),%d)"
- " mMinSpaceWidth=%g",
- mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom,
- SkFixedToScalar(mLastGlyph.fLSB.fX),
- SkFixedToScalar(mLastGlyph.fLSB.fY),
- SkFixedToScalar(mLastGlyph.fRSB.fX),
- SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
- full.fLeft, full.fTop, full.fRight, full.fBottom,
- SkFixedToScalar(rec.fLSB.fX),
- SkFixedToScalar(rec.fLSB.fY),
- SkFixedToScalar(rec.fRSB.fX),
- SkFixedToScalar(rec.fRSB.fY), rec.fGlyphID,
- SkFixedToScalar(mMinSpaceWidth));
- *mSelectText.append() = ' ';
- } else
- mSkipFirstSpace = false;
- DBG_NAV_LOGD("TextExtractor [%02x] append full=(%d,%d,r=%d,b=%d)",
- rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom);
- SkPaint utfPaint = *mPaint;
- utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
- utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &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];
+ int fullBase = base();
+ if (mFlipped) {
+ if (fullBase < mStart.fTop || fullBase > mEnd.fBottom
+ || (baseLinesAgree(mStart, mStartBase, full, fullBase)
+ && full.fLeft < mStart.fLeft)
+ || (baseLinesAgree(mEnd, mEndBase, full, fullBase)
+ && full.fRight > mEnd.fRight)) {
+ mSkipFirstSpace = true;
+ return false;
+ }
+ if (baseLinesAgree(mLast, mLastBase, full, fullBase)
+ && (full.fLeft - mLast.fRight < minSpaceWidth() * 3
+ || (SkIRect::Intersects(full, mSelectRect)
+ && SkIRect::Intersects(mLast, mSelectRect)))) {
+ mLast.join(full);
+ addCharacter(rec);
+ return false;
}
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("baseLinesAgree=%s"
+ " 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)",
+ baseLinesAgree(mLast, mLastBase, full, fullBase) ? "true" : "false",
+ 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);
+ *mSelectBounds.append() = SkIRect::Intersects(mLast, mSelectRect)
+ ? mLast : mEmpty;
+ *mSelectCount.append() = mSelectText.count();
+ addCharacter(rec);
mLast = full;
- mLastGlyph = rec;
- } else {
- mSkipFirstSpace = true;
- DBG_NAV_LOGD("TextExtractor [%02x] skip full=(%d,%d,r=%d,b=%d)",
- rec.fGlyphID, full.fLeft, full.fTop, full.fRight, full.fBottom);
+ mLastBase = fullBase;
+ return false;
}
+ if (full == mStart)
+ mRecord = true;
+ if (mRecord)
+ addCharacter(rec);
+ else
+ mSkipFirstSpace = true;
+ if (full == mEnd)
+ mRecord = false;
return false;
}
WebCore::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 WebCore::String(mSelectText.begin(), mSelectText.count());
}
protected:
- const SkRegion& mSelectRegion;
- SkTDArray<uint16_t> mSelectText;
+ SkIRect mEmpty;
+ SkIRect mEnd;
+ int mEndBase;
+ bool mFlipped;
SkIRect mLast;
- SkBounder::GlyphRec mLastGlyph;
- SkUnichar mLastUni;
- SkFixed mMinSpaceWidth;
+ int mLastBase;
+ bool mRecord;
+ SkTDArray<SkIRect> mSelectBounds;
+ SkTDArray<int> mSelectCount;
+ SkIRect mSelectRect;
+ SkTDArray<uint16_t> mSelectText;
bool mSkipFirstSpace;
+ SkIRect mStart;
+ int mStartBase;
private:
typedef CommonCheck INHERITED;
};
@@ -282,7 +865,7 @@ private:
class TextCanvas : public SkCanvas {
public:
- TextCanvas(CommonCheck* bounder, const SkPicture& picture, const SkIRect& area)
+ TextCanvas(CommonCheck* bounder, const SkIRect& area)
: mBounder(*bounder) {
setBounder(bounder);
SkBitmap bitmap;
@@ -340,49 +923,201 @@ public:
CommonCheck& mBounder;
};
-void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area,
- const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region) {
+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)",
+ " 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, selEnd, area.fLeft, area.fTop, region);
- TextCanvas checker(&builder, picture, area);
+ MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region);
+ TextCanvas checker(&builder, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
+ bool flipped = builder.flipped();
+ if (flipped) {
+ TextCanvas checker(&builder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ }
+ builder.finish();
region->translate(area.fLeft, area.fTop);
+ return flipped;
}
-SkIRect CopyPaste::findClosest(const SkPicture& picture, const SkIRect& area,
- int x, int y) {
- FirstCheck _check(x - area.fLeft, y - area.fTop);
- DBG_NAV_LOGD("area=(%d, %d, %d, %d) x=%d y=%d", area.fLeft, area.fTop,
- area.fRight, area.fBottom, x, y);
- TextCanvas checker(&_check, picture, area);
+static SkIRect findClosest(FirstCheck& _check, const SkPicture& picture,
+ const SkIRect& area, int* base)
+{
+ DBG_NAV_LOGD("area=(%d, %d, %d, %d)", area.fLeft, area.fTop,
+ area.fRight, area.fBottom);
+ TextCanvas checker(&_check, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
- _check.offsetBounds(area.fLeft, area.fTop);
- return _check.bestBounds();
+ _check.finishGlyph();
+ return _check.adjustedBounds(area, base);
}
-WebCore::String CopyPaste::text(const SkPicture& picture, const SkIRect& area,
- const SkRegion& region) {
- SkRegion copy = region;
- copy.translate(-area.fLeft, -area.fTop);
- const SkIRect& bounds = copy.getBounds();
- DBG_NAV_LOGD("area=(%d, %d, %d, %d) region=(%d, %d, %d, %d)",
- area.fLeft, area.fTop, area.fRight, area.fBottom,
- bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
- TextExtractor extractor(copy);
- TextCanvas checker(&extractor, picture, area);
+static SkIRect 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, area, &closestBase);
+ closest.inset(-TOUCH_SLOP, -TOUCH_SLOP);
+ if (!closest.contains(x, y)) {
+ DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d",
+ closest.fLeft, closest.fTop, closest.fRight, closest.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, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ edge.finishGlyph();
+ if (!edge.adjacent()) {
+ 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;
+}
+
+static SkIRect findFirst(const SkPicture& picture, int* base)
+{
+ FindFirst finder(picture.width(), picture.height());
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ TextCanvas checker(&finder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static SkIRect findLast(const SkPicture& picture, int* base)
+{
+ FindLast finder(picture.width(), picture.height());
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ TextCanvas checker(&finder, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static SkIRect findLeft(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, true, base);
+}
+
+static SkIRect findRight(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, false, base);
+}
+
+static WebCore::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, area);
checker.drawPicture(const_cast<SkPicture&>(picture));
return extractor.text();
}
+#define CONTROL_OFFSET 3
+#define CONTROL_NOTCH 9
+#define CONTROL_HEIGHT 18
+#define CONTROL_WIDTH 12
+#define STROKE_WIDTH 0.4f
+#define SLOP 20
+
+SelectText::SelectText()
+{
+ reset();
+ SkScalar innerW = CONTROL_WIDTH - STROKE_WIDTH;
+ SkScalar innerH = CONTROL_HEIGHT - STROKE_WIDTH;
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(STROKE_WIDTH);
+
+ SkPath startPath;
+ startPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
+ startPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
+ startPath.lineTo(0, CONTROL_HEIGHT);
+ startPath.lineTo(0, CONTROL_OFFSET);
+ startPath.close();
+
+ SkCanvas* canvas = m_startControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xD077A14B);
+ canvas->drawPath(startPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(0x40000000);
+ canvas->drawLine(-innerW, CONTROL_NOTCH, -innerW, innerH, paint);
+ canvas->drawLine(-innerW + STROKE_WIDTH, innerH, -STROKE_WIDTH, innerH, paint);
+ paint.setColor(0x40ffffff);
+ canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH,
+ -CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint);
+ canvas->drawLine(-STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH,
+ -STROKE_WIDTH, innerH, paint);
+ paint.setColor(0xffaaaaaa);
+ canvas->drawPath(startPath, paint);
+ m_startControl.endRecording();
+
+ SkPath endPath;
+ endPath.moveTo(0, CONTROL_OFFSET);
+ endPath.lineTo(0, CONTROL_HEIGHT);
+ endPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
+ endPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
+ endPath.close();
+
+ canvas = m_endControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xD077A14B);
+ canvas->drawPath(endPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(0x40000000);
+ canvas->drawLine(STROKE_WIDTH, CONTROL_OFFSET + STROKE_WIDTH,
+ STROKE_WIDTH, innerH, paint);
+ canvas->drawLine(STROKE_WIDTH + STROKE_WIDTH, innerH, innerW, innerH, paint);
+ paint.setColor(0x40ffffff);
+ canvas->drawLine(0, CONTROL_OFFSET + STROKE_WIDTH,
+ CONTROL_WIDTH, CONTROL_NOTCH + STROKE_WIDTH, paint);
+ canvas->drawLine(STROKE_WIDTH, CONTROL_NOTCH + STROKE_WIDTH,
+ STROKE_WIDTH, innerH, paint);
+ paint.setColor(0xffaaaaaa);
+ canvas->drawPath(endPath, paint);
+ m_endControl.endRecording();
+}
+
void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer)
{
- if (layer->picture() != m_picture)
- return;
- if (m_drawRegion)
+ // FIXME: layer may not own the original selected picture
+ m_picture = layer->picture();
+ DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d", m_extendSelection, m_drawPointer);
+ if (m_extendSelection)
drawSelectionRegion(canvas);
if (m_drawPointer)
drawSelectionPointer(canvas);
@@ -406,7 +1141,7 @@ void SelectText::drawSelectionPointer(SkCanvas* canvas)
paint.setStrokeWidth(SK_Scalar1 * 2);
int sc = canvas->save();
canvas->scale(m_inverseScale, m_inverseScale);
- canvas->translate(SkIntToScalar(m_selectX), SkIntToScalar(m_selectY));
+ canvas->translate(m_selectX, m_selectY);
canvas->drawPath(path, paint);
if (!m_extendSelection) {
paint.setStyle(SkPaint::kFill_Style);
@@ -424,19 +1159,92 @@ void SelectText::drawSelectionRegion(SkCanvas* canvas)
return;
SkIRect ivisBounds;
visBounds.round(&ivisBounds);
- CopyPaste::buildSelection(*m_picture, ivisBounds, m_selStart, m_selEnd,
- &m_selRegion);
+ ivisBounds.join(m_selStart);
+ ivisBounds.join(m_selEnd);
+ 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);
+ 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(0x40, 255, 51, 204));
+ paint.setColor(SkColorSetARGB(0x80, 151, 200, 73));
canvas->drawPath(path, paint);
+ // experiment to draw touchable controls that resize the selection
+ canvas->save();
+ canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
+ canvas->drawPicture(m_startControl);
+ canvas->restore();
+ canvas->save();
+ canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
+ canvas->drawPicture(m_endControl);
+ canvas->restore();
+}
+
+void SelectText::extendSelection(const SkPicture* picture, int x, int y)
+{
+ SkIRect clipRect = m_visibleRect;
+ int base;
+ 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());
+ }
+ DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft,
+ clipRect.fTop, clipRect.fRight, clipRect.fBottom);
+ m_picture = picture;
+ FirstCheck center(m_original.fX, m_original.fY, clipRect);
+ m_selStart = m_selEnd = findClosest(center, *picture, clipRect, &base);
+ m_startBase = m_endBase = base;
+ m_startSelection = false;
+ m_extendSelection = true;
+ m_original.fX = m_original.fY = 0;
+ } else if (picture != m_picture)
+ return;
+ 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)", clipRect.fLeft,
+ clipRect.fTop, clipRect.fRight, clipRect.fBottom);
+ FirstCheck extension(x, y, clipRect);
+ SkIRect found = findClosest(extension, *picture, clipRect, &base);
+ DBG_NAV_LOGD("pic=%p x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
+ " m_extendSelection=%s",
+ picture, 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();
}
const String SelectText::getSelection()
{
- String result = CopyPaste::text(*m_picture, m_visibleRect, m_selRegion);
+ 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;
}
@@ -447,35 +1255,173 @@ void SelectText::getSelectionArrow(SkPath* path)
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(SkIntToScalar(arrow[index]), SkIntToScalar(arrow[index + 1]));
+ path->lineTo(arrow[index], arrow[index + 1]);
path->close();
}
void SelectText::getSelectionCaret(SkPath* path)
{
- SkScalar height = SkIntToScalar(m_selStart.fBottom - m_selStart.fTop);
+ 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, -SK_Scalar1/2);
+ path->rMoveTo(0, -0.5f);
path->rLineTo(dist * 2, 0);
- path->rMoveTo(0, SK_Scalar1/2);
+ path->rMoveTo(0, 0.5f);
path->rLineTo(-dist, -dist);
}
-void SelectText::moveSelection(const SkPicture* picture, int x, int y,
- bool extendSelection)
+bool SelectText::hitCorner(int cx, int cy, int x, int y) const
+{
+ SkIRect test;
+ test.set(cx, cy, cx, cy);
+ test.inset(-SLOP, -SLOP);
+ return test.contains(x, y);
+}
+
+bool SelectText::hitSelection(int x, int y) const
+{
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(left, top, x, y))
+ return true;
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(right, bottom, x, y))
+ return true;
+ return m_selRegion.contains(x, y);
+}
+
+void SelectText::moveSelection(const SkPicture* picture, int x, int y)
{
- if (!extendSelection)
+ SkIRect clipRect = m_visibleRect;
+ clipRect.join(m_selStart);
+ clipRect.join(m_selEnd);
+ if (!m_extendSelection)
m_picture = picture;
- m_selEnd = CopyPaste::findClosest(*picture, m_visibleRect, x, y);
- if (!extendSelection)
- m_selStart = m_selEnd;
+ FirstCheck center(x, y, clipRect);
+ int base;
+ SkIRect found = findClosest(center, *picture, clipRect, &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, extendSelection ? "true" : "false",
+ " 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_selEnd.setEmpty();
+ m_extendSelection = false;
+ m_startSelection = false;
+}
+
+void SelectText::selectAll(const SkPicture* picture)
+{
+ m_selStart = findFirst(*picture, &m_startBase);
+ m_selEnd = findLast(*picture, &m_endBase);
+ m_extendSelection = true;
+}
+
+int SelectText::selectionX() const
+{
+ return m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight;
+}
+
+int SelectText::selectionY() const
+{
+ const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
+ return (rect.fTop + rect.fBottom) >> 1;
+}
+
+bool SelectText::startSelection(int x, int y)
+{
+ m_original.fX = x;
+ m_original.fY = y;
+ if (m_selStart.isEmpty()) {
+ DBG_NAV_LOGD("empty start x=%d y=%d", x, y);
+ m_startSelection = true;
+ return true;
+ }
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ m_hitTopLeft = hitCorner(left, top, x, y);
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ bool hitBottomRight = hitCorner(right, bottom, x, y);
+ DBG_NAV_LOGD("left=%d top=%d right=%d bottom=%d x=%d y=%d", left, top,
+ right, bottom, x, y);
+ if (m_hitTopLeft && (!hitBottomRight || y - top < bottom - y)) {
+ DBG_NAV_LOG("hit top left");
+ m_original.fX -= left;
+ m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ } else if (hitBottomRight) {
+ DBG_NAV_LOG("hit bottom right");
+ m_original.fX -= right;
+ m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
+ }
+ return m_hitTopLeft || hitBottomRight;
+}
+
+/* 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 SkPicture* picture)
+{
+ int x = m_selStart.fLeft;
+ int y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ SkIRect clipRect = m_visibleRect;
+ clipRect.fLeft -= m_visibleRect.width() >> 1;
+ int base;
+ SkIRect left = findLeft(*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(*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_extendSelection = true;
+ return true;
+ }
+ return false;
+}
+
+void SelectText::swapAsNeeded()
+{
+ if (m_selStart.fTop >= m_selEnd.fBottom
+ || (m_selStart.fBottom > m_selEnd.fTop
+ && 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");
+ }
}
}
diff --git a/WebKit/android/nav/SelectText.h b/WebKit/android/nav/SelectText.h
index 8174046..404e9e7 100644
--- a/WebKit/android/nav/SelectText.h
+++ b/WebKit/android/nav/SelectText.h
@@ -29,6 +29,7 @@
#include "DrawExtra.h"
#include "IntRect.h"
#include "PlatformString.h"
+#include "SkPath.h"
class SkPicture;
struct SkIRect;
@@ -38,47 +39,57 @@ namespace android {
class CachedRoot;
-class CopyPaste {
-public:
- static void buildSelection(const SkPicture& , const SkIRect& area,
- const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region);
- static SkIRect findClosest(const SkPicture& , const SkIRect& area,
- int x, int y);
- static String text(const SkPicture& , const SkIRect& area,
- const SkRegion& );
-};
-
class SelectText : public DrawExtra {
public:
- SelectText() {
- m_selStart.setEmpty();
- m_selEnd.setEmpty();
- }
+ SelectText();
virtual void draw(SkCanvas* , LayerAndroid* );
+ void extendSelection(const SkPicture* , int x, int y);
const String getSelection();
- void moveSelection(const SkPicture* , int x, int y, bool extendSelection);
+ bool hitSelection(int x, int y) const;
+ void moveSelection(const SkPicture* , int x, int y);
+ void reset();
+ void selectAll(const SkPicture* );
+ int selectionX() const;
+ int selectionY() const;
void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; }
- void setDrawRegion(bool drawRegion) { m_drawRegion = drawRegion; }
+ void setExtendSelection(bool extend) { m_extendSelection = extend; }
void setVisibleRect(const IntRect& rect) { m_visibleRect = rect; }
+ bool startSelection(int x, int y);
+ bool wordSelection(const SkPicture* picture);
+public:
+ float m_inverseScale; // inverse scale, x, y used for drawing select path
+ int m_selectX;
+ int m_selectY;
private:
- friend class WebView;
void drawSelectionPointer(SkCanvas* );
void drawSelectionRegion(SkCanvas* );
static void getSelectionArrow(SkPath* );
void getSelectionCaret(SkPath* );
+ bool hitCorner(int cx, int cy, int x, int y) const;
+ void swapAsNeeded();
+ SkIPoint m_original; // computed start of extend selection
SkIRect m_selStart;
SkIRect m_selEnd;
- SkIRect m_visibleRect;
- SkRegion m_selRegion;
+ int m_startBase;
+ int m_endBase;
+ SkIRect m_visibleRect; // constrains picture computations to visible area
+ SkRegion m_selRegion; // computed from sel start, end
+ SkPicture m_startControl;
+ SkPicture m_endControl;
const SkPicture* m_picture;
- float m_inverseScale;
- int m_selectX;
- int m_selectY;
- bool m_drawRegion;
bool m_drawPointer;
- bool m_extendSelection;
+ bool m_extendSelection; // false when trackball is moving pointer
+ bool m_flipped;
+ bool m_hitTopLeft;
+ bool m_startSelection;
};
}
+namespace WebCore {
+
+void ReverseBidi(UChar* chars, int len);
+
+}
+
#endif
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index 2ed6148..5154b42 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -30,10 +30,10 @@
#include "AndroidAnimation.h"
#include "AndroidLog.h"
#include "AtomicString.h"
+#include "BaseLayerAndroid.h"
#include "CachedFrame.h"
#include "CachedNode.h"
#include "CachedRoot.h"
-#include "CString.h"
#include "DrawExtra.h"
#include "FindCanvas.h"
#include "Frame.h"
@@ -68,6 +68,7 @@
#include <JNIHelp.h>
#include <jni.h>
#include <ui/KeycodeLabels.h>
+#include <wtf/text/CString.h>
namespace android {
@@ -177,7 +178,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) :
m_lastDx = 0;
m_lastDxTime = 0;
m_ringAnimationEnd = 0;
- m_rootLayer = 0;
+ m_baseLayer = 0;
}
~WebView()
@@ -190,7 +191,7 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl) :
}
delete m_frameCacheUI;
delete m_navPictureUI;
- delete m_rootLayer;
+ delete m_baseLayer;
}
WebViewCore* getWebViewCore() const {
@@ -303,10 +304,11 @@ void scrollRectOnScreen(const IntRect& rect)
SkRect visible;
calcOurContentVisibleRect(&visible);
#if USE(ACCELERATED_COMPOSITING)
- if (m_rootLayer) {
- m_rootLayer->updateFixedLayersPositions(visible);
- m_rootLayer->updatePositions();
- visible = m_rootLayer->subtractLayers(visible);
+ LayerAndroid* root = compositeRoot();
+ if (root) {
+ root->updateFixedLayersPositions(visible);
+ root->updatePositions();
+ visible = root->subtractLayers(visible);
}
#endif
int dx = 0;
@@ -394,14 +396,30 @@ void drawCursorPostamble()
}
}
-void drawExtras(SkCanvas* canvas, int extras)
+PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
{
+ PictureSet* ret = 0;
+ if (!m_baseLayer) {
+ canvas->drawColor(bgColor);
+ return ret;
+ }
+
+ // draw the content of the base layer first
+ PictureSet* content = m_baseLayer->content();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
+ content->height()), SkRegion::kDifference_Op);
+ canvas->drawColor(bgColor);
+ canvas->restoreToCount(sc);
+ if (content->draw(canvas))
+ ret = split ? new PictureSet(*content) : 0;
+
CachedRoot* root = getFrameCache(AllowNewer);
if (!root) {
DBG_NAV_LOG("!root");
if (extras == DrawExtrasCursorRing)
resetCursorRing();
- return;
+ return ret;
}
LayerAndroid mainPicture(m_navPictureUI);
DrawExtra* extra = 0;
@@ -425,22 +443,24 @@ void drawExtras(SkCanvas* canvas, int extras)
if (extra)
extra->draw(canvas, &mainPicture);
#if USE(ACCELERATED_COMPOSITING)
- if (!m_rootLayer)
- return;
- m_rootLayer->setExtra(extra);
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (!compositeLayer)
+ return ret;
+ compositeLayer->setExtra(extra);
SkRect visible;
calcOurContentVisibleRect(&visible);
// call this to be sure we've adjusted for any scrolling or animations
// before we actually draw
- m_rootLayer->updateFixedLayersPositions(visible);
- m_rootLayer->updatePositions();
- // We have to set the canvas' matrix on the root layer
+ compositeLayer->updateFixedLayersPositions(visible);
+ compositeLayer->updatePositions();
+ // We have to set the canvas' matrix on the base layer
// (to have fixed layers work as intended)
SkAutoCanvasRestore restore(canvas, true);
- m_rootLayer->setMatrix(canvas->getTotalMatrix());
+ m_baseLayer->setMatrix(canvas->getTotalMatrix());
canvas->resetMatrix();
- m_rootLayer->draw(canvas);
+ m_baseLayer->draw(canvas);
#endif
+ return ret;
}
@@ -565,7 +585,7 @@ CachedRoot* getFrameCache(FrameCachePermission allowNewer)
m_viewImpl->m_navPictureKit = 0;
m_viewImpl->gFrameCacheMutex.unlock();
if (m_frameCacheUI)
- m_frameCacheUI->setRootLayer(m_rootLayer);
+ m_frameCacheUI->setRootLayer(compositeRoot());
#if USE(ACCELERATED_COMPOSITING)
if (layerId >= 0) {
SkRect visible;
@@ -892,7 +912,8 @@ bool motionUp(int x, int y, int slop)
viewInvalidate();
if (!result->isTextInput()) {
clearTextEntry();
- setFollowedLink(true);
+ if (!result->isSelect() && !result->isContentEditable())
+ setFollowedLink(true);
if (syntheticLink)
overrideUrlLoading(result->getExport());
}
@@ -946,7 +967,7 @@ String getSelection()
return m_selectText.getSelection();
}
-void moveSelection(int x, int y, bool extendSelection)
+void moveSelection(int x, int y)
{
const CachedRoot* root = getFrameCache(DontAllowNewer);
if (!root)
@@ -957,26 +978,81 @@ void moveSelection(int x, int y, bool extendSelection)
IntRect visibleRect;
getVisibleRect(&visibleRect);
m_selectText.setVisibleRect(visibleRect);
- m_selectText.moveSelection(picture, x, y, extendSelection);
+ m_selectText.moveSelection(picture, x, y);
+}
+
+void selectAll()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ SkPicture* picture = root->pictureAt(0, 0);
+ m_selectText.selectAll(picture);
+}
+
+int selectionX()
+{
+ return m_selectText.selectionX();
}
-void setSelectionPointer(bool set, float scale, int x, int y,
- bool extendSelection)
+int selectionY()
+{
+ return m_selectText.selectionY();
+}
+
+void resetSelection()
+{
+ m_selectText.reset();
+}
+
+bool startSelection(int x, int y)
+{
+ return m_selectText.startSelection(x, y);
+}
+
+bool wordSelection(int x, int y)
+{
+ startSelection(x, y);
+ if (!extendSelection(x, y))
+ return false;
+ m_selectText.setDrawPointer(false);
+ SkPicture* picture = getFrameCache(DontAllowNewer)->pictureAt(x, y);
+ return m_selectText.wordSelection(picture);
+}
+
+bool extendSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ SkPicture* picture = root->pictureAt(x, y);
+ IntRect visibleRect;
+ getVisibleRect(&visibleRect);
+ m_selectText.setVisibleRect(visibleRect);
+ m_selectText.extendSelection(picture, x, y);
+ return true;
+}
+
+bool hitSelection(int x, int y)
+{
+ 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_extendSelection = extendSelection;
m_selectText.m_inverseScale = scale;
m_selectText.m_selectX = x;
m_selectText.m_selectY = y;
}
-void setSelectionRegion(bool set)
-{
- m_selectText.setDrawRegion(set);
-}
-
void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
{
DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
@@ -1035,6 +1111,11 @@ void setMatches(WTF::Vector<MatchInfo>* matches)
viewInvalidate();
}
+int currentMatchIndex()
+{
+ return m_findOnPage.currentMatchIndex();
+}
+
bool scrollBy(int dx, int dy)
{
LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
@@ -1109,20 +1190,49 @@ int moveGeneration()
return m_viewImpl->m_moveGeneration;
}
-LayerAndroid* rootLayer() const
+LayerAndroid* compositeRoot() const
{
- return m_rootLayer;
+ LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1,
+ "base layer can't have more than one child %s", __FUNCTION__);
+ if (m_baseLayer && m_baseLayer->countChildren() == 1)
+ return static_cast<LayerAndroid*>(m_baseLayer->getChild(0));
+ else
+ return 0;
}
-void setRootLayer(LayerAndroid* layer)
+void setBaseLayer(BaseLayerAndroid* layer)
{
- delete m_rootLayer;
- m_rootLayer = layer;
+ delete m_baseLayer;
+ m_baseLayer = layer;
CachedRoot* root = getFrameCache(DontAllowNewer);
if (!root)
return;
root->resetLayers();
- root->setRootLayer(m_rootLayer);
+ root->setRootLayer(compositeRoot());
+}
+
+void replaceBaseContent(PictureSet* set)
+{
+ if (!m_baseLayer)
+ return;
+ m_baseLayer->setContent(*set);
+ delete set;
+}
+
+void copyBaseContentToPicture(SkPicture* picture)
+{
+ if (!m_baseLayer)
+ return;
+ PictureSet* content = m_baseLayer->content();
+ content->draw(picture->beginRecording(content->width(), content->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag));
+ picture->endRecording();
+}
+
+bool hasContent() {
+ if (!m_baseLayer)
+ return false;
+ return !m_baseLayer->content()->isEmpty();
}
private: // local state for WebView
@@ -1139,7 +1249,7 @@ private: // local state for WebView
SelectText m_selectText;
FindOnPage m_findOnPage;
CursorRing m_ring;
- LayerAndroid* m_rootLayer;
+ BaseLayerAndroid* m_baseLayer;
}; // end of WebView class
/*
@@ -1272,6 +1382,19 @@ static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj)
return cursor ? frame->textInput(cursor) : 0;
}
+static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj)
+{
+ const CachedNode* focus = getFocusNode(env, obj);
+ if (!focus) return false;
+ // Plugins handle shift and arrows whether or not they have focus.
+ if (focus->isPlugin()) return true;
+ const CachedNode* cursor = getCursorNode(env, obj);
+ // ContentEditable nodes should only receive shift and arrows if they have
+ // both the cursor and the focus.
+ return cursor && cursor->nodePointer() == focus->nodePointer()
+ && cursor->isContentEditable();
+}
+
static jboolean nativeCursorMatchesFocus(JNIEnv *env, jobject obj)
{
const CachedNode* cursor = getCursorNode(env, obj);
@@ -1357,28 +1480,43 @@ static void nativeDebugDump(JNIEnv *env, jobject obj)
#endif
}
-static void nativeDrawExtras(JNIEnv *env, jobject obj, jobject canv, jint extras)
-{
+static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, jint color,
+ jint extras, jboolean split) {
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
- GET_NATIVE_VIEW(env, obj)->drawExtras(canvas, extras);
+ return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split));
}
static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj)
{
#if USE(ACCELERATED_COMPOSITING)
- const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer();
+ const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
if (root)
return root->evaluateAnimations();
#endif
return false;
}
-static void nativeSetRootLayer(JNIEnv *env, jobject obj, jint layer)
+static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer)
{
-#if USE(ACCELERATED_COMPOSITING)
- LayerAndroid* layerImpl = reinterpret_cast<LayerAndroid*>(layer);
- GET_NATIVE_VIEW(env, obj)->setRootLayer(layerImpl);
-#endif
+ BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
+ GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl);
+}
+
+static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
+{
+ PictureSet* set = reinterpret_cast<PictureSet*>(content);
+ GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
+}
+
+static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
+}
+
+static bool nativeHasContent(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasContent();
}
static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
@@ -1568,7 +1706,7 @@ static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect)
{
SkIRect irect = jrect_to_webrect(env, jrect);
#if USE(ACCELERATED_COMPOSITING)
- LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->rootLayer();
+ LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
if (root) {
SkRect rect;
rect.set(irect);
@@ -1644,9 +1782,12 @@ static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj)
static void nativeSetFollowedLink(JNIEnv *env, jobject obj, bool followed)
{
- WebView* view = GET_NATIVE_VIEW(env, obj);
- LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
- view->setFollowedLink(followed);
+ const CachedNode* cursor = getCursorNode(env, obj);
+ if (cursor && !cursor->isSelect() && ! cursor->isContentEditable()) {
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setFollowedLink(followed);
+ }
}
static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure)
@@ -1735,6 +1876,13 @@ static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
view->findNext(forward);
}
+static int nativeFindIndex(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindIndex");
+ return view->currentMatchIndex();
+}
+
static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation)
{
WebView* view = GET_NATIVE_VIEW(env, obj);
@@ -1805,11 +1953,39 @@ static int nativeMoveGeneration(JNIEnv *env, jobject obj)
return view->moveGeneration();
}
-static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y, bool ex)
+static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
{
- WebView* view = GET_NATIVE_VIEW(env, obj);
- LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
- view->moveSelection(x, y, ex);
+ GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
+}
+
+static void nativeResetSelection(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->resetSelection();
+}
+
+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)
@@ -1820,15 +1996,25 @@ static jobject nativeGetSelection(JNIEnv *env, jobject obj)
return env->NewString((jchar*)selection.characters(), selection.length());
}
-static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
- jfloat scale, jint x, jint y, bool ex)
+static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
{
- GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y, ex);
+ return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
}
-static void nativeSetSelectionRegion(JNIEnv *env, jobject obj, jboolean set)
+static jint nativeSelectionX(JNIEnv *env, jobject obj)
{
- GET_NATIVE_VIEW(env, obj)->setSelectionRegion(set);
+ 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, jboolean set,
+ jfloat scale, jint x, jint y)
+{
+ GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
}
#ifdef ANDROID_DUMP_DISPLAY_TREE
@@ -1859,26 +2045,13 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
SkDumpCanvas canvas(&dumper);
// this will playback the picture into the canvas, which will
// spew its contents to the dumper
- view->getWebViewCore()->drawContent(&canvas, 0);
-#if USE(ACCELERATED_COMPOSITING)
- if (true) {
- LayerAndroid* rootLayer = view->rootLayer();
- if (rootLayer) {
- // We have to set the canvas' matrix on the root layer
- // (to have fixed layers work as intended)
- SkAutoCanvasRestore restore(&canvas, true);
- rootLayer->setMatrix(canvas.getTotalMatrix());
- canvas.resetMatrix();
- rootLayer->draw(&canvas);
- }
- }
-#endif
+ view->draw(&canvas, 0, 0, false);
// we're done with the file now
fwrite("\n", 1, 1, file);
fclose(file);
}
#if USE(ACCELERATED_COMPOSITING)
- const LayerAndroid* rootLayer = view->rootLayer();
+ const LayerAndroid* rootLayer = view->compositeRoot();
if (rootLayer) {
FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
if (file) {
@@ -1907,6 +2080,8 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeCreate },
{ "nativeCursorFramePointer", "()I",
(void*) nativeCursorFramePointer },
+ { "nativePageShouldHandleShiftAndArrows", "()Z",
+ (void*) nativePageShouldHandleShiftAndArrows },
{ "nativeCursorMatchesFocus", "()Z",
(void*) nativeCursorMatchesFocus },
{ "nativeCursorNodeBounds", "()Landroid/graphics/Rect;",
@@ -1929,16 +2104,20 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeDebugDump },
{ "nativeDestroy", "()V",
(void*) nativeDestroy },
- { "nativeDrawExtras", "(Landroid/graphics/Canvas;I)V",
- (void*) nativeDrawExtras },
+ { "nativeDraw", "(Landroid/graphics/Canvas;IIZ)I",
+ (void*) nativeDraw },
{ "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
(void*) nativeDumpDisplayTree },
{ "nativeEvaluateLayersAnimations", "()Z",
(void*) nativeEvaluateLayersAnimations },
+ { "nativeExtendSelection", "(II)V",
+ (void*) nativeExtendSelection },
{ "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I",
(void*) nativeFindAll },
{ "nativeFindNext", "(Z)V",
(void*) nativeFindNext },
+ { "nativeFindIndex", "()I",
+ (void*) nativeFindIndex},
{ "nativeFocusCandidateFramePointer", "()I",
(void*) nativeFocusCandidateFramePointer },
{ "nativeFocusCandidateHasNextTextfield", "()Z",
@@ -1979,6 +2158,8 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeHasFocusNode },
{ "nativeHideCursor", "()V",
(void*) nativeHideCursor },
+ { "nativeHitSelection", "(II)Z",
+ (void*) nativeHitSelection },
{ "nativeImageURI", "(II)Ljava/lang/String;",
(void*) nativeImageURI },
{ "nativeInstrumentReport", "()V",
@@ -1991,14 +2172,24 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeMoveCursorToNextTextInput },
{ "nativeMoveGeneration", "()I",
(void*) nativeMoveGeneration },
- { "nativeMoveSelection", "(IIZ)V",
+ { "nativeMoveSelection", "(II)V",
(void*) nativeMoveSelection },
{ "nativePointInNavCache", "(III)Z",
(void*) nativePointInNavCache },
{ "nativeRecordButtons", "(ZZZ)V",
(void*) nativeRecordButtons },
+ { "nativeResetSelection", "()V",
+ (void*) nativeResetSelection },
+ { "nativeSelectAll", "()V",
+ (void*) nativeSelectAll },
{ "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
(void*) nativeSelectBestAt },
+ { "nativeSelectionX", "()I",
+ (void*) nativeSelectionX },
+ { "nativeSelectionY", "()I",
+ (void*) nativeSelectionY },
+ { "nativeSetExtendSelection", "()V",
+ (void*) nativeSetExtendSelection },
{ "nativeSetFindIsEmpty", "()V",
(void*) nativeSetFindIsEmpty },
{ "nativeSetFindIsUp", "(Z)V",
@@ -2007,18 +2198,26 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeSetFollowedLink },
{ "nativeSetHeightCanMeasure", "(Z)V",
(void*) nativeSetHeightCanMeasure },
- { "nativeSetRootLayer", "(I)V",
- (void*) nativeSetRootLayer },
- { "nativeSetSelectionPointer", "(ZFIIZ)V",
+ { "nativeSetBaseLayer", "(I)V",
+ (void*) nativeSetBaseLayer },
+ { "nativeReplaceBaseContent", "(I)V",
+ (void*) nativeReplaceBaseContent },
+ { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) nativeCopyBaseContentToPicture },
+ { "nativeHasContent", "()Z",
+ (void*) nativeHasContent },
+ { "nativeSetSelectionPointer", "(ZFII)V",
(void*) nativeSetSelectionPointer },
- { "nativeSetSelectionRegion", "(Z)V",
- (void*) nativeSetSelectionRegion },
+ { "nativeStartSelection", "(II)Z",
+ (void*) nativeStartSelection },
{ "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;",
(void*) nativeSubtractLayers },
{ "nativeTextGeneration", "()I",
(void*) nativeTextGeneration },
{ "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
(void*) nativeUpdateCachedTextfield },
+ { "nativeWordSelection", "(II)Z",
+ (void*) nativeWordSelection },
{ "nativeGetBlockLeftEdge", "(IIF)I",
(void*) nativeGetBlockLeftEdge },
};