summaryrefslogtreecommitdiffstats
path: root/WebKit
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit')
-rw-r--r--WebKit/Android.mk1
-rw-r--r--WebKit/android/RenderSkinAndroid.cpp4
-rw-r--r--WebKit/android/RenderSkinCombo.cpp94
-rw-r--r--WebKit/android/RenderSkinCombo.h20
-rw-r--r--WebKit/android/RenderSkinMediaButton.cpp171
-rw-r--r--WebKit/android/RenderSkinMediaButton.h61
-rw-r--r--WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp554
-rw-r--r--WebKit/android/jni/WebCoreJniOnLoad.cpp6
-rw-r--r--WebKit/android/jni/WebViewCore.cpp27
-rw-r--r--WebKit/android/jni/WebViewCore.h3
-rw-r--r--WebKit/android/nav/CacheBuilder.cpp15
-rw-r--r--WebKit/android/nav/CacheBuilder.h2
-rw-r--r--WebKit/android/nav/CachedInput.cpp4
-rw-r--r--WebKit/android/nav/CachedInput.h16
-rw-r--r--WebKit/android/nav/FindCanvas.h1
-rw-r--r--WebKit/android/nav/SelectText.cpp1468
-rw-r--r--WebKit/android/nav/SelectText.h59
-rw-r--r--WebKit/android/nav/WebView.cpp208
-rw-r--r--WebKit/android/plugins/ANPKeyCodes.h34
-rw-r--r--WebKit/android/plugins/ANPSurfaceInterface.cpp2
20 files changed, 2229 insertions, 521 deletions
diff --git a/WebKit/Android.mk b/WebKit/Android.mk
index 2c12ed3..47f37ca 100644
--- a/WebKit/Android.mk
+++ b/WebKit/Android.mk
@@ -31,6 +31,7 @@ LOCAL_SRC_FILES := \
android/RenderSkinAndroid.cpp \
android/RenderSkinButton.cpp \
android/RenderSkinCombo.cpp \
+ android/RenderSkinMediaButton.cpp \
android/RenderSkinRadio.cpp \
android/TimeCounter.cpp \
\
diff --git a/WebKit/android/RenderSkinAndroid.cpp b/WebKit/android/RenderSkinAndroid.cpp
index d148262..00f2b96 100644
--- a/WebKit/android/RenderSkinAndroid.cpp
+++ b/WebKit/android/RenderSkinAndroid.cpp
@@ -29,6 +29,7 @@
#include "RenderSkinAndroid.h"
#include "RenderSkinButton.h"
#include "RenderSkinCombo.h"
+#include "RenderSkinMediaButton.h"
#include "RenderSkinRadio.h"
#include "SkImageDecoder.h"
@@ -45,7 +46,8 @@ RenderSkinAndroid::RenderSkinAndroid()
void RenderSkinAndroid::Init(android::AssetManager* am, String drawableDirectory)
{
RenderSkinButton::Init(am, drawableDirectory);
- RenderSkinCombo::Init(am);
+ RenderSkinCombo::Init(am, drawableDirectory);
+ RenderSkinMediaButton::Init(am, drawableDirectory);
RenderSkinRadio::Init(am, drawableDirectory);
}
diff --git a/WebKit/android/RenderSkinCombo.cpp b/WebKit/android/RenderSkinCombo.cpp
index 6f88ee3..4378371 100644
--- a/WebKit/android/RenderSkinCombo.cpp
+++ b/WebKit/android/RenderSkinCombo.cpp
@@ -26,6 +26,7 @@
#include "config.h"
#include "RenderSkinCombo.h"
+#include "CString.h"
#include "Document.h"
#include "Element.h"
#include "Node.h"
@@ -36,43 +37,85 @@
namespace WebCore {
-static SkBitmap s_bitmap[2]; // Collection of assets for a combo box
-static bool s_decoded; // True if all assets were decoded
-static const int s_margin = 2;
-static const SkIRect s_mar = { s_margin, s_margin,
- RenderSkinCombo::extraWidth(), s_margin };
-static SkIRect s_subset;
+// Indicates if the entire asset is being drawn, or if the border is being
+// excluded and just the arrow drawn.
+enum BorderStyle {
+ FullAsset,
+ NoBorder
+};
+// There are 2.5 different concepts of a 'border' here, which results
+// in rather a lot of magic constants. In each case, there are 2
+// numbers, one for medium res and one for high-res. All sizes are in pixels.
-RenderSkinCombo::RenderSkinCombo()
-{
-}
+// Firstly, we have the extra padding that webkit needs to know about,
+// which defines how much bigger this element is made by the
+// asset. This is actually a bit broader than the actual border on the
+// asset, to make things look less cramped. The border is the same
+// width on all sides, except on the right when it's significantly
+// wider to allow for the arrow.
+const int RenderSkinCombo::arrowMargin[2] = {22, 34};
+const int RenderSkinCombo::padMargin[2] = {2, 5};
+
+// Then we have the borders used for the 9-patch stretch. The
+// rectangle at the centre of these borders is entirely below and to
+// the left of the arrow in the asset. Hence the border widths are the
+// same for the bottom and left, but are different for the top. The
+// right hand border width happens to be the same as arrowMargin
+// defined above.
+static const int stretchMargin[2] = {3, 5}; // border width for the bottom and left of the 9-patch
+static const int stretchTop[2] = {15, 23}; // border width for the top of the 9-patch
+
+// Finally, if the border is defined by the CSS, we only draw the
+// arrow and not the border. We do this by drawing the relevant subset
+// of the bitmap, which must now be precisely determined by what's in
+// the asset with no extra padding to make things look properly
+// spaced. The border to remove at the top, right and bottom of the
+// image is the same as stretchMargin above, but we need to know the width
+// of the arrow.
+static const int arrowWidth[2] = {22, 31};
+
+RenderSkinCombo::Resolution RenderSkinCombo::resolution = MedRes;
+
+const SkIRect RenderSkinCombo::margin[2][2] = {{{ stretchMargin[MedRes], stretchTop[MedRes],
+ RenderSkinCombo::arrowMargin[MedRes] + stretchMargin[MedRes], stretchMargin[MedRes] },
+ {0, stretchTop[MedRes], 0, stretchMargin[MedRes]}},
+ {{ stretchMargin[HighRes], stretchTop[HighRes],
+ RenderSkinCombo::arrowMargin[HighRes] + stretchMargin[HighRes], stretchMargin[HighRes] },
+ {0, stretchTop[HighRes], 0, stretchMargin[HighRes]}}};
+static SkBitmap bitmaps[2][2]; // Collection of assets for a combo box
+static bool isDecoded; // True if all assets were decoded
-void RenderSkinCombo::Init(android::AssetManager* am)
+void RenderSkinCombo::Init(android::AssetManager* am, String drawableDirectory)
{
- if (s_decoded)
+ if (isDecoded)
return;
- // Maybe short circuiting is fine, since I don't even draw if one state is not decoded properly
- // but is that necessary in the final version?
- s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-noHighlight.png", &s_bitmap[kNormal]);
- s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-disabled.png", &s_bitmap[kDisabled]) && s_decoded;
-
- int width = s_bitmap[kNormal].width();
- int height = s_bitmap[kNormal].height();
- s_subset.set(width - RenderSkinCombo::extraWidth() + s_margin, 0, width, height);
+
+ if (drawableDirectory[drawableDirectory.length() - 5] == 'h')
+ resolution = HighRes;
+
+ isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
+ isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
+
+ int width = bitmaps[kNormal][FullAsset].width();
+ int height = bitmaps[kNormal][FullAsset].height();
+ SkIRect subset;
+ subset.set(width - arrowWidth[resolution], 0, width, height);
+ bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
+ bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
}
bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
{
- if (!s_decoded)
+ if (!isDecoded)
return true;
State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
- if (height < (s_margin<<1) + 1) {
- height = (s_margin<<1) + 1;
- }
+ height = std::max(height, (stretchMargin[resolution]<<1) + 1);
+
SkRect bounds;
+ BorderStyle drawBorder = FullAsset;
bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
RenderStyle* style = element->renderStyle();
@@ -90,10 +133,9 @@ bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int wi
bounds.fRight -= SkIntToScalar(style->borderRightWidth());
bounds.fTop += SkIntToScalar(style->borderTopWidth());
bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
- canvas->drawBitmapRect(s_bitmap[state], &s_subset, bounds);
- } else {
- SkNinePatch::DrawNine(canvas, bounds, s_bitmap[state], s_mar);
+ drawBorder = NoBorder;
}
+ SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[resolution][drawBorder]);
return false;
}
diff --git a/WebKit/android/RenderSkinCombo.h b/WebKit/android/RenderSkinCombo.h
index 91c9367..38cd048 100644
--- a/WebKit/android/RenderSkinCombo.h
+++ b/WebKit/android/RenderSkinCombo.h
@@ -37,13 +37,10 @@ namespace WebCore {
class RenderSkinCombo : public RenderSkinAndroid
{
public:
- RenderSkinCombo();
- virtual ~RenderSkinCombo() {}
-
/**
* Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use.
*/
- static void Init(android::AssetManager*);
+ static void Init(android::AssetManager*, String drawableDirectory);
/**
* Draw the provided Node on the SkCanvas, using the dimensions provided by
@@ -53,11 +50,18 @@ public:
static bool Draw(SkCanvas* , Node* , int x, int y, int w, int h);
// The image is wider than the RenderObject, so this accounts for that.
- static int extraWidth() { return arrowMargin; }
-
+ static int extraWidth() { return arrowMargin[resolution]; }
+ static int padding() { return padMargin[resolution]; }
+
+ enum Resolution {
+ MedRes,
+ HighRes
+ };
private:
-
- static const int arrowMargin = 22;
+ static Resolution resolution;
+ const static int arrowMargin[2];
+ const static int padMargin[2];
+ const static SkIRect margin[2][2];
};
} // WebCore
diff --git a/WebKit/android/RenderSkinMediaButton.cpp b/WebKit/android/RenderSkinMediaButton.cpp
new file mode 100644
index 0000000..149be4e
--- /dev/null
+++ b/WebKit/android/RenderSkinMediaButton.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "CString.h"
+#include "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinMediaButton.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include "SkRect.h"
+#include <utils/Debug.h>
+#include <utils/Log.h>
+
+struct PatchData {
+ const char* name;
+ int8_t outset, margin;
+};
+
+static const PatchData gFiles[] =
+ {
+ { "btn_media_player.9.png", 0, 0 }, // DEFAULT BGD BUTTON
+ { "ic_media_pause.png", 0, 0}, // PAUSE
+ { "ic_media_play.png", 0, 0 }, // PLAY
+ { "ic_media_pause.png", 0, 0 }, // MUTE
+ { "ic_media_rew.png", 0, 0 }, // REWIND
+ { "ic_media_ff.png", 0, 0 }, // FORWARD
+ { "btn_media_player_disabled.9.png", 0, 0 }, // BACKGROUND_SLIDER
+ { "btn_media_player_pressed.9.png", 0, 0 }, // SLIDER_TRACK
+ { "btn_media_player.9.png", 0, 0 } // SLIDER_THUMB
+ };
+
+static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])];
+static bool gDecoded;
+static bool gHighRes;
+
+namespace WebCore {
+
+void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirectory)
+{
+ static bool gInited;
+ if (gInited)
+ return;
+
+ gInited = true;
+ gDecoded = true;
+ gHighRes = drawableDirectory[drawableDirectory.length() - 5] == 'h';
+ for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) {
+ String path = drawableDirectory + gFiles[i].name;
+ if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) {
+ gDecoded = false;
+ LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ break;
+ }
+ }
+}
+
+void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType)
+{
+ // If we failed to decode, do nothing. This way the browser still works,
+ // and webkit will still draw the label and layout space for us.
+ if (!gDecoded) {
+ return;
+ }
+
+ bool drawsNinePatch = true;
+ bool drawsImage = true;
+ bool drawsBackgroundColor = false;
+
+ int ninePatchIndex = 0;
+ int imageIndex = 0;
+
+ SkRect bounds(r);
+ SkScalar imageMargin = 8;
+ SkPaint paint;
+ SkColor backgroundColor = SkColorSetARGB(255, 200, 200, 200);
+ SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100);
+
+ switch (buttonType) {
+ case PAUSE:
+ case PLAY:
+ case MUTE:
+ case REWIND:
+ case FORWARD:
+ {
+ imageIndex = buttonType + 1;
+ drawsBackgroundColor = true;
+ paint.setColor(backgroundColor);
+ break;
+ }
+ case BACKGROUND_SLIDER:
+ {
+ drawsImage = false;
+ drawsNinePatch = false;
+ drawsBackgroundColor = true;
+ paint.setColor(backgroundColor);
+ break;
+ }
+ case SLIDER_TRACK:
+ {
+ drawsImage = false;
+ drawsNinePatch = false;
+ drawsBackgroundColor = true;
+ paint.setColor(trackBackgroundColor);
+ bounds.fTop += 8;
+ bounds.fBottom -= 8;
+ break;
+ }
+ case SLIDER_THUMB:
+ {
+ drawsImage = false;
+ ninePatchIndex = buttonType + 1;
+ break;
+ }
+ default:
+ drawsImage = false;
+ drawsNinePatch = false;
+ }
+
+ if (drawsBackgroundColor) {
+ canvas->drawRect(r, paint);
+ }
+
+ if (drawsNinePatch) {
+ const PatchData& pd = gFiles[ninePatchIndex];
+ int marginValue = pd.margin + pd.outset;
+
+ SkIRect margin;
+ margin.set(marginValue, marginValue, marginValue, marginValue);
+ SkNinePatch::DrawNine(canvas, bounds, gButton[0], margin);
+ }
+
+ if (drawsImage) {
+ SkScalar SIZE = gButton[imageIndex].width();
+ SkScalar width = r.width();
+ SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE);
+ int saveScaleCount = canvas->save();
+ canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin);
+ canvas->scale(scale, scale);
+ canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint);
+ canvas->restoreToCount(saveScaleCount);
+ }
+}
+
+} //WebCore
diff --git a/WebKit/android/RenderSkinMediaButton.h b/WebKit/android/RenderSkinMediaButton.h
new file mode 100644
index 0000000..b4e99f4
--- /dev/null
+++ b/WebKit/android/RenderSkinMediaButton.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RenderSkinMediaButton_h
+#define RenderSkinMediaButton_h
+
+#include "RenderSkinAndroid.h"
+
+class SkCanvas;
+
+namespace WebCore {
+class IntRect;
+class RenderSkinMediaButton
+{
+public:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any
+ * bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*, String drawableDirectory);
+ /**
+ * Draw the skin to the canvas, using the rectangle for its bounds and the
+ * State to determine which skin to use, i.e. focused or not focused.
+ */
+ static void Draw(SkCanvas* , const IntRect& , int buttonType);
+ /**
+ * Button types
+ */
+ enum { PAUSE, PLAY, MUTE, REWIND, FORWARD, BACKGROUND_SLIDER, SLIDER_TRACK, SLIDER_THUMB };
+ /**
+ * Slider dimensions
+ */
+ static int sliderThumbWidth() { return 10; }
+ static int sliderThumbHeight() { return 30; }
+
+};
+
+} // WebCore
+#endif // RenderSkinMediaButton_h
diff --git a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
index bfb5305..3a39730 100644
--- a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
@@ -30,7 +30,6 @@
#include "GraphicsContext.h"
#include "SkiaUtils.h"
-#include "TimeRanges.h"
#include "WebCoreJni.h"
#include "WebViewCore.h"
@@ -44,16 +43,22 @@ using namespace android;
namespace WebCore {
static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
+static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio";
struct MediaPlayerPrivate::JavaGlue
{
jobject m_javaProxy;
- jmethodID m_getInstance;
jmethodID m_play;
jmethodID m_teardown;
- jmethodID m_loadPoster;
jmethodID m_seek;
jmethodID m_pause;
+ // Audio
+ jmethodID m_newInstance;
+ jmethodID m_setDataSource;
+ jmethodID m_getMaxTimeSeekable;
+ // Video
+ jmethodID m_getInstance;
+ jmethodID m_loadPoster;
};
MediaPlayerPrivate::~MediaPlayerPrivate()
@@ -65,7 +70,6 @@ MediaPlayerPrivate::~MediaPlayerPrivate()
env->DeleteGlobalRef(m_glue->m_javaProxy);
}
}
-
delete m_glue;
}
@@ -74,29 +78,6 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
registrar(create, getSupportedTypes, supportsType);
}
-void MediaPlayerPrivate::load(const String& url)
-{
- // Just save the URl.
- m_url = url;
-}
-
-void MediaPlayerPrivate::cancelLoad()
-{
-}
-
-void MediaPlayerPrivate::play()
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env || !m_glue->m_javaProxy || !m_url.length())
- return;
-
- m_paused = false;
- jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
- env->DeleteLocalRef(jUrl);
- checkException(env);
-}
-
void MediaPlayerPrivate::pause()
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
@@ -108,22 +89,6 @@ void MediaPlayerPrivate::pause()
checkException(env);
}
-IntSize MediaPlayerPrivate::naturalSize() const
-{
- return m_naturalSize;
-}
-
-bool MediaPlayerPrivate::hasAudio() const
-{
- // TODO
- return false;
-}
-
-bool MediaPlayerPrivate::hasVideo() const
-{
- return m_hasVideo;
-}
-
void MediaPlayerPrivate::setVisible(bool visible)
{
m_isVisible = visible;
@@ -131,99 +96,19 @@ void MediaPlayerPrivate::setVisible(bool visible)
createJavaPlayerIfNeeded();
}
-float MediaPlayerPrivate::duration() const
-{
- return m_duration;
-}
-
-float MediaPlayerPrivate::currentTime() const
-{
- return m_currentTime;
-}
-
void MediaPlayerPrivate::seek(float time)
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env || !m_glue->m_javaProxy || !m_url.length())
+ if (!env || !m_url.length())
return;
- m_currentTime = time;
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
+ if (m_glue->m_javaProxy) {
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
+ m_currentTime = time;
+ }
checkException(env);
}
-bool MediaPlayerPrivate::seeking() const
-{
- return false;
-}
-
-void MediaPlayerPrivate::setEndTime(float)
-{
-}
-
-void MediaPlayerPrivate::setRate(float)
-{
-}
-
-bool MediaPlayerPrivate::paused() const
-{
- return m_paused;
-}
-
-void MediaPlayerPrivate::setVolume(float)
-{
-}
-
-MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
-{
- return m_networkState;
-}
-
-MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
-{
- return m_readyState;
-}
-
-float MediaPlayerPrivate::maxTimeSeekable() const
-{
- return 0;
-}
-
-PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
-{
- return TimeRanges::create();
-}
-
-int MediaPlayerPrivate::dataRate() const
-{
- return 0;
-}
-
-unsigned MediaPlayerPrivate::totalBytes() const
-{
- return 0;
-}
-
-unsigned MediaPlayerPrivate::bytesLoaded() const
-{
- return 0;
-}
-
-void MediaPlayerPrivate::setSize(const IntSize&)
-{
-}
-
-void MediaPlayerPrivate::setPoster(const String& url)
-{
- m_posterUrl = url;
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
- return;
- // Send the poster
- jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
- env->DeleteLocalRef(jUrl);
-}
void MediaPlayerPrivate::prepareToPlay() {
// We are about to start playing. Since our Java VideoView cannot
@@ -237,42 +122,10 @@ void MediaPlayerPrivate::prepareToPlay() {
m_player->readyStateChanged();
}
-void MediaPlayerPrivate::paint(GraphicsContext* ctxt, const IntRect& r)
-{
- if (ctxt->paintingDisabled())
- return;
-
- if (!m_isVisible)
- return;
-
- if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
- return;
-
- SkCanvas* canvas = ctxt->platformContext()->mCanvas;
- // We paint with the following rules in mind:
- // - only downscale the poster, never upscale
- // - maintain the natural aspect ratio of the poster
- // - the poster should be centered in the target rect
- float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
- int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
- int posterHeight = posterWidth / originalRatio;
- int posterX = ((r.width() - posterWidth) / 2) + r.x();
- int posterY = ((r.height() - posterHeight) / 2) + r.y();
- IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
- canvas->drawBitmapRect(*m_poster, 0, targetRect, 0);
-}
-
-MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
-{
- return new MediaPlayerPrivate(player);
-}
-
-void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&)
-{
-}
-
MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
{
+ if (WebViewCore::supportsMimeType(type))
+ return MediaPlayer::MayBeSupported;
return MediaPlayer::IsNotSupported;
}
@@ -290,72 +143,6 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
m_naturalSizeUnknown(true),
m_isVisible(false)
{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
- return;
-
- jclass clazz = env->FindClass(g_ProxyJavaClass);
- if (!clazz)
- return;
-
- m_glue = new JavaGlue;
- m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
- m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
- m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
- m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
- m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
- m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
- m_glue->m_javaProxy = NULL;
- env->DeleteLocalRef(clazz);
- // An exception is raised if any of the above fails.
- checkException(env);
-}
-
-void MediaPlayerPrivate::createJavaPlayerIfNeeded()
-{
- // Check if we have been already created.
- if (m_glue->m_javaProxy)
- return;
-
- FrameView* frameView = m_player->frameView();
- if (!frameView)
- return;
-
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
- return;
-
- jclass clazz = env->FindClass(g_ProxyJavaClass);
- if (!clazz)
- return;
-
- WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView);
- ASSERT(webViewCore);
-
- // Get the HTML5VideoViewProxy instance
- jobject obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
- m_glue->m_javaProxy = env->NewGlobalRef(obj);
- // Send the poster
- jstring jUrl = 0;
- if (m_posterUrl.length())
- jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
- // Sending a NULL jUrl allows the Java side to try to load the default poster.
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
- if (jUrl)
- env->DeleteLocalRef(jUrl);
- // Clean up.
- env->DeleteLocalRef(obj);
- env->DeleteLocalRef(clazz);
- checkException(env);
-}
-
-void MediaPlayerPrivate::onPrepared(int duration, int width, int height) {
- m_duration = duration / 1000.0f;
- m_naturalSize = IntSize(width, height);
- m_naturalSizeUnknown = false;
- m_hasVideo = true;
- m_player->durationChanged();
- m_player->sizeChanged();
}
void MediaPlayerPrivate::onEnded() {
@@ -368,18 +155,12 @@ void MediaPlayerPrivate::onEnded() {
m_readyState = MediaPlayer::HaveNothing;
}
-void MediaPlayerPrivate::onPosterFetched(SkBitmap* poster) {
- m_poster = poster;
- if (m_naturalSizeUnknown) {
- // We had to fake the size at startup, or else our paint
- // method would not be called. If we haven't yet received
- // the onPrepared event, update the intrinsic size to the size
- // of the poster. That will be overriden when onPrepare comes.
- // In case of an error, we should report the poster size, rather
- // than our initial fake value.
- m_naturalSize = IntSize(poster->width(), poster->height());
- m_player->sizeChanged();
- }
+void MediaPlayerPrivate::onPaused() {
+ m_paused = true;
+ m_currentTime = 0;
+ m_hasVideo = false;
+ m_networkState = MediaPlayer::Idle;
+ m_readyState = MediaPlayer::HaveNothing;
}
void MediaPlayerPrivate::onTimeupdate(int position) {
@@ -387,6 +168,264 @@ void MediaPlayerPrivate::onTimeupdate(int position) {
m_player->timeChanged();
}
+class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url) { m_url = url; }
+ void play() {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length() || !m_glue->m_javaProxy)
+ return;
+
+ m_paused = false;
+ jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
+ env->DeleteLocalRef(jUrl);
+
+ checkException(env);
+ }
+ bool canLoadPoster() const { return true; }
+ void setPoster(const String& url) {
+ m_posterUrl = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
+ return;
+ // Send the poster
+ jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ env->DeleteLocalRef(jUrl);
+ }
+ void paint(GraphicsContext* ctxt, const IntRect& r) {
+ if (ctxt->paintingDisabled())
+ return;
+
+ if (!m_isVisible)
+ return;
+
+ if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
+ return;
+
+ SkCanvas* canvas = ctxt->platformContext()->mCanvas;
+ // We paint with the following rules in mind:
+ // - only downscale the poster, never upscale
+ // - maintain the natural aspect ratio of the poster
+ // - the poster should be centered in the target rect
+ float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
+ int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
+ int posterHeight = posterWidth / originalRatio;
+ int posterX = ((r.width() - posterWidth) / 2) + r.x();
+ int posterY = ((r.height() - posterHeight) / 2) + r.y();
+ IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
+ canvas->drawBitmapRect(*m_poster, 0, targetRect, 0);
+ }
+
+ void onPosterFetched(SkBitmap* poster) {
+ m_poster = poster;
+ if (m_naturalSizeUnknown) {
+ // We had to fake the size at startup, or else our paint
+ // method would not be called. If we haven't yet received
+ // the onPrepared event, update the intrinsic size to the size
+ // of the poster. That will be overriden when onPrepare comes.
+ // In case of an error, we should report the poster size, rather
+ // than our initial fake value.
+ m_naturalSize = IntSize(poster->width(), poster->height());
+ m_player->sizeChanged();
+ }
+ }
+
+ void onPrepared(int duration, int width, int height) {
+ m_duration = duration / 1000.0f;
+ m_naturalSize = IntSize(width, height);
+ m_naturalSizeUnknown = false;
+ m_hasVideo = true;
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ }
+
+ bool hasAudio() { return false; } // do not display the audio UI
+ bool hasVideo() { return m_hasVideo; }
+
+ MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
+ m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
+
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
+ m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
+ m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
+ m_glue->m_javaProxy = NULL;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded() {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ jobject obj = NULL;
+
+ FrameView* frameView = m_player->frameView();
+ if (!frameView)
+ return;
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView);
+ ASSERT(webViewCore);
+
+ // Get the HTML5VideoViewProxy instance
+ obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+ // Send the poster
+ jstring jUrl = 0;
+ if (m_posterUrl.length())
+ jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
+ // Sending a NULL jUrl allows the Java side to try to load the default poster.
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ if (jUrl)
+ env->DeleteLocalRef(jUrl);
+
+ // Clean up.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+};
+
+class MediaPlayerAudioPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url) {
+ m_url = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
+ // start loading the data asynchronously
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl);
+ env->DeleteLocalRef(jUrl);
+ checkException(env);
+ }
+
+ void play() {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ m_paused = false;
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play);
+ checkException(env);
+ }
+
+ bool hasAudio() { return true; }
+
+ float maxTimeSeekable() const {
+ if (m_glue->m_javaProxy) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (env) {
+ float maxTime = env->CallFloatMethod(m_glue->m_javaProxy,
+ m_glue->m_getMaxTimeSeekable);
+ checkException(env);
+ return maxTime;
+ }
+ }
+ return 0;
+ }
+
+ MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V");
+ m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "()V");
+ m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F");
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
+ m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
+ m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
+ m_glue->m_javaProxy = NULL;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded() {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ jobject obj = NULL;
+
+ // Get the HTML5Audio instance
+ obj = env->NewObject(clazz, m_glue->m_newInstance, this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+
+ // Clean up.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+
+ void onPrepared(int duration, int width, int height) {
+ m_duration = duration / 1000.0f;
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ m_player->prepareToPlay();
+ }
+};
+
+MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
+{
+ if (player->mediaElementType() == MediaPlayer::Video)
+ return new MediaPlayerVideoPrivate(player);
+ return new MediaPlayerAudioPrivate(player);
+}
+
}
namespace android {
@@ -405,6 +444,13 @@ static void OnEnded(JNIEnv* env, jobject obj, int pointer) {
}
}
+static void OnPaused(JNIEnv* env, jobject obj, int pointer) {
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onPaused();
+ }
+}
+
static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer) {
if (!pointer || !poster)
return;
@@ -416,6 +462,13 @@ static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointe
player->onPosterFetched(posterNative);
}
+static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer) {
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ //TODO: player->onBuffering(percent);
+ }
+}
+
static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer) {
if (pointer) {
WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
@@ -431,17 +484,36 @@ static JNINativeMethod g_MediaPlayerMethods[] = {
(void*) OnPrepared },
{ "nativeOnEnded", "(I)V",
(void*) OnEnded },
+ { "nativeOnPaused", "(I)V",
+ (void*) OnPaused },
{ "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
(void*) OnPosterFetched },
{ "nativeOnTimeupdate", "(II)V",
(void*) OnTimeupdate },
};
-int register_mediaplayer(JNIEnv* env)
+static JNINativeMethod g_MediaAudioPlayerMethods[] = {
+ { "nativeOnBuffering", "(II)V",
+ (void*) OnBuffering },
+ { "nativeOnEnded", "(I)V",
+ (void*) OnEnded },
+ { "nativeOnPrepared", "(IIII)V",
+ (void*) OnPrepared },
+ { "nativeOnTimeupdate", "(II)V",
+ (void*) OnTimeupdate },
+};
+
+int register_mediaplayer_video(JNIEnv* env)
{
return jniRegisterNativeMethods(env, g_ProxyJavaClass,
g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
}
+int register_mediaplayer_audio(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio,
+ g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods));
+}
+
}
#endif // VIDEO
diff --git a/WebKit/android/jni/WebCoreJniOnLoad.cpp b/WebKit/android/jni/WebCoreJniOnLoad.cpp
index b5bf9dd..86248db 100644
--- a/WebKit/android/jni/WebCoreJniOnLoad.cpp
+++ b/WebKit/android/jni/WebCoreJniOnLoad.cpp
@@ -82,7 +82,8 @@ extern int register_webstorage(JNIEnv*);
extern int register_geolocation_permissions(JNIEnv*);
extern int register_mock_geolocation(JNIEnv*);
#if ENABLE(VIDEO)
-extern int register_mediaplayer(JNIEnv*);
+extern int register_mediaplayer_audio(JNIEnv*);
+extern int register_mediaplayer_video(JNIEnv*);
#endif
}
@@ -107,7 +108,8 @@ static RegistrationMethod gWebCoreRegMethods[] = {
{ "GeolocationPermissions", android::register_geolocation_permissions },
{ "MockGeolocation", android::register_mock_geolocation },
#if ENABLE(VIDEO)
- { "HTML5VideoViewProxy", android::register_mediaplayer },
+ { "HTML5Audio", android::register_mediaplayer_audio },
+ { "HTML5VideoViewProxy", android::register_mediaplayer_video },
#endif
};
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp
index 70e96cd..3f3eb42 100644
--- a/WebKit/android/jni/WebViewCore.cpp
+++ b/WebKit/android/jni/WebViewCore.cpp
@@ -189,6 +189,24 @@ jobject WebViewCore::getApplicationContext() {
return result;
}
+
+struct WebViewCoreStaticMethods {
+ jmethodID m_supportsMimeType;
+} gWebViewCoreStaticMethods;
+
+// Check whether a media mimeType is supported in Android media framework.
+bool WebViewCore::supportsMimeType(const WebCore::String& mimeType) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMimeType = env->NewString(mimeType.characters(), mimeType.length());
+ jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
+ bool val = env->CallStaticBooleanMethod(webViewCore,
+ gWebViewCoreStaticMethods.m_supportsMimeType, jMimeType);
+ checkException(env);
+ env->DeleteLocalRef(jMimeType);
+
+ return val;
+}
+
// ----------------------------------------------------------------------------
#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
@@ -1688,8 +1706,8 @@ void WebViewCore::deleteSelection(int start, int end, int textGeneration)
EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
m_mainFrame->editor()->client());
client->setUiGeneratedSelectionChange(true);
- PlatformKeyboardEvent down(kKeyCodeDel, 0, 0, true, false, false, false);
- PlatformKeyboardEvent up(kKeyCodeDel, 0, 0, false, false, false, false);
+ PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
+ PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
key(down);
key(up);
client->setUiGeneratedSelectionChange(false);
@@ -3295,6 +3313,11 @@ int register_webviewcore(JNIEnv* env)
LOG_ASSERT(gWebViewCoreFields.m_webView,
"Unable to find android/webkit/WebViewCore.mWebView");
+ gWebViewCoreStaticMethods.m_supportsMimeType =
+ env->GetStaticMethodID(widget, "supportsMimeType", "(Ljava/lang/String;)Z");
+ LOG_ASSERT(gWebViewCoreStaticMethods.m_supportsMimeType == NULL,
+ "Could not find static method supportsMimeType from WebViewCore");
+
return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
}
diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h
index 056dba1..2944599 100644
--- a/WebKit/android/jni/WebViewCore.h
+++ b/WebKit/android/jni/WebViewCore.h
@@ -578,6 +578,9 @@ namespace android {
// if there exists at least on WebViewCore instance then we return the
// application context, otherwise NULL is returned.
static jobject getApplicationContext();
+
+ // Check whether a media mimeType is supported in Android media framework.
+ static bool supportsMimeType(const WebCore::String& mimeType);
};
} // namespace android
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp
index 7ee2a16..f69b23d 100644
--- a/WebKit/android/nav/CacheBuilder.cpp
+++ b/WebKit/android/nav/CacheBuilder.cpp
@@ -960,7 +960,7 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
FocusTracker* last = &tracker.last();
int lastChildIndex = cachedFrame->size() - 1;
while (node == last->mLastChild) {
- if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
cacheIndex--;
tracker.removeLast();
lastChildIndex = last->mCachedNodeIndex;
@@ -1232,6 +1232,10 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
|| style->textAlign() == WebCore::RIGHT
|| style->textAlign() == WebCore::WEBKIT_RIGHT);
}
+ cachedInput.setPaddingLeft(renderText->paddingLeft() + renderText->borderLeft());
+ cachedInput.setPaddingTop(renderText->paddingTop() + renderText->borderTop());
+ cachedInput.setPaddingRight(renderText->paddingRight() + renderText->borderRight());
+ cachedInput.setPaddingBottom(renderText->paddingBottom() + renderText->borderBottom());
}
takesFocus = true;
bounds = absBounds;
@@ -1344,14 +1348,14 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
while (tracker.size() > 1) {
FocusTracker* last = &tracker.last();
int lastChildIndex = cachedFrame->size() - 1;
- if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
cacheIndex--;
tracker.removeLast();
}
}
-bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame,
- const FocusTracker* last, int lastChildIndex)
+bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot,
+ CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex)
{
// if outer is body, disable outer
// or if there's more than one inner, disable outer
@@ -1380,9 +1384,12 @@ bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame,
HasTriggerEvent(lastNode) == false;
if (onlyChildCached->parent() == lastCached)
onlyChildCached->setParentIndex(lastCached->parentIndex());
+ bool hasFocus = lastCached->isFocus() || onlyChildCached->isFocus();
if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL))
*lastCached = *onlyChildCached;
cachedFrame->removeLast();
+ if (hasFocus)
+ cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode());
return true;
}
diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h
index 4ded58d..407d590 100644
--- a/WebKit/android/nav/CacheBuilder.h
+++ b/WebKit/android/nav/CacheBuilder.h
@@ -215,7 +215,7 @@ private:
static bool NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length);
void BuildFrame(Frame* root, Frame* frame,
CachedRoot* cachedRoot, CachedFrame* cachedFrame);
- bool CleanUpContainedNodes(CachedFrame* cachedFrame,
+ bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame,
const FocusTracker* last, int lastChildIndex);
static bool ConstructTextRect(Text* textNode,
InlineTextBox* textBox, int start, int relEnd, int x, int y,
diff --git a/WebKit/android/nav/CachedInput.cpp b/WebKit/android/nav/CachedInput.cpp
index 924bbca..cba5820 100644
--- a/WebKit/android/nav/CachedInput.cpp
+++ b/WebKit/android/nav/CachedInput.cpp
@@ -59,6 +59,10 @@ void CachedInput::Debug::print() const
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);
+ DUMP_NAV_LOGD("// int mPaddingLeft=%d;\n", b->mPaddingLeft);
+ DUMP_NAV_LOGD("// int mPaddingTop=%d;\n", b->mPaddingTop);
+ DUMP_NAV_LOGD("// int mPaddingRight=%d;\n", b->mPaddingRight);
+ DUMP_NAV_LOGD("// int mPaddingBottom=%d;\n", b->mPaddingBottom);
DEBUG_PRINT_BOOL(mIsRtlText);
DEBUG_PRINT_BOOL(mIsTextField);
}
diff --git a/WebKit/android/nav/CachedInput.h b/WebKit/android/nav/CachedInput.h
index 42cadf1..01b40f9 100644
--- a/WebKit/android/nav/CachedInput.h
+++ b/WebKit/android/nav/CachedInput.h
@@ -48,20 +48,32 @@ public:
bool isTextField() const { return mIsTextField; }
int maxLength() const { return mMaxLength; };
const WebCore::String& name() const { return mName; }
+ int paddingBottom() const { return mPaddingBottom; }
+ int paddingLeft() const { return mPaddingLeft; }
+ int paddingRight() const { return mPaddingRight; }
+ int paddingTop() const { return mPaddingTop; }
void setFormPointer(void* form) { mForm = form; }
void setInputType(WebCore::HTMLInputElement::InputType type) { mInputType = type; }
void setIsRtlText(bool isRtlText) { mIsRtlText = isRtlText; }
void setIsTextField(bool isTextField) { mIsTextField = isTextField; }
void setMaxLength(int maxLength) { mMaxLength = maxLength; }
void setName(const WebCore::String& name) { mName = name; }
+ void setPaddingBottom(int bottom) { mPaddingBottom = bottom; }
+ void setPaddingLeft(int left) { mPaddingLeft = left; }
+ void setPaddingRight(int right) { mPaddingRight = right; }
+ void setPaddingTop(int top) { mPaddingTop = top; }
void setTextSize(int textSize) { mTextSize = textSize; }
int textSize() const { return mTextSize; }
private:
void* mForm;
- WebCore::String mName;
+ WebCore::HTMLInputElement::InputType mInputType;
int mMaxLength;
+ WebCore::String mName;
+ int mPaddingBottom;
+ int mPaddingLeft;
+ int mPaddingRight;
+ int mPaddingTop;
int mTextSize;
- WebCore::HTMLInputElement::InputType mInputType;
bool mIsRtlText : 1;
bool mIsTextField : 1;
#if DUMP_NAV_CACHE
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..25f9482 100644
--- a/WebKit/android/nav/SelectText.cpp
+++ b/WebKit/android/nav/SelectText.cpp
@@ -23,15 +23,17 @@
* 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"
#include "SkBitmap.h"
#include "SkBounder.h"
#include "SkCanvas.h"
+#include "SkGradientShader.h"
#include "SkMatrix.h"
#include "SkPicture.h"
#include "SkPixelXorXfermode.h"
@@ -39,26 +41,158 @@
#include "SkRect.h"
#include "SkRegion.h"
#include "SkUtils.h"
+#include "TextRun.h"
#ifdef DEBUG_NAV_UI
#include "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 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() : 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 +212,149 @@ 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 || mLastUni == SOLIDUS
+ || mLastUni == REVERSE_SOLIDUS) && 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/8 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 / 8;
+ }
+
+ 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 +365,568 @@ 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;
+ mBestBounds.fBottom, topDebug(), bottomDebug());
+ return mBestBounds;
}
-
- void offsetBounds(int dx, int dy) {
- mBestBounds.offset(dx, dy);
- }
-
- 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 MultilineBuilder : public CommonCheck {
+class EdgeCheck : public FirstCheck {
public:
- MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy,
- SkRegion* region)
- : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) {
+ 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 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.width(), area.height())
+ , 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;
- mStart.offset(-dx, -dy);
- mEnd.offset(-dx, -dy);
+ 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);
}
- 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;
+ 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;
}
- 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;
- }
- 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);
+ // 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() * 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 = base();
- if (mStart == mEnd)
- mCapture = false;
+ mLastBase = fullBase;
+ mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
+ } else {
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mLastIntersects = false;
}
- return false;
+ return mCollectFull;
}
-protected:
- SkIRect mStart;
+
+ 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;
+ }
+ return false;
+ }
+
+protected:
SkRegion* mSelectRegion;
- bool mCapture;
+private:
+ typedef BuilderCheck 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 {
+class TextExtractor : public BuilderCheck {
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(start, startBase, end, endBase, area)
+ , mSelectStartIndex(-1)
+ , mSkipFirstSpace(true) // don't start with a space
+ {
+ mFlipped = flipped;
+ if (flipped)
+ setFlippedState();
}
- 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 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<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++) {
+ 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,
@@ -217,72 +934,68 @@ 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) {
+ 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
}
- 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);
+ return false;
}
+ if (full == mStart)
+ mCapture = true;
+ if (mCapture)
+ addCharacter(rec);
+ else
+ mSkipFirstSpace = true;
+ if (full == mEnd)
+ mCapture = 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;
+ SkIRect mEmpty;
+ SkTDArray<SkIRect> mSelectBounds;
+ SkTDArray<int> mSelectEnd;
+ SkTDArray<int> mSelectStart;
+ int mSelectStartIndex;
SkTDArray<uint16_t> mSelectText;
- SkIRect mLast;
- SkBounder::GlyphRec mLastGlyph;
- SkUnichar mLastUni;
- SkFixed mMinSpaceWidth;
bool mSkipFirstSpace;
private:
- typedef CommonCheck INHERITED;
+ typedef BuilderCheck INHERITED;
};
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 +1053,240 @@ 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 0
+#define CONTROL_NOTCH 19
+#define CONTROL_HEIGHT 35
+#define CONTROL_WIDTH 21
+#define STROKE_WIDTH 2.0f
+#define STROKE_OUTSET 1.0f
+#define STROKE_NOTCH_R 2.2f
+#define STROKE_NOTCH_L 1.7f
+#define DROP_HEIGHT 4
+
+#define STROKE_COLOR 0x90000000
+#define FILL_GRADIENT_TOP 0xD0F8DFA0
+#define FILL_GRADIENT_BOTTOM 0xD0FFEFEF
+#define DROP_GRADIENT_TOP 0x50000000
+#define DROP_GRADIENT_BOTTOM 0x00000000
+
+#define SLOP 20
+
+SelectText::SelectText()
+{
+ reset();
+ SkPaint paint;
+
+ SkPath startFillPath;
+ startFillPath.moveTo(-CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH);
+ startFillPath.lineTo(-CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
+ startFillPath.lineTo(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
+ startFillPath.lineTo(-STROKE_OUTSET, CONTROL_OFFSET + STROKE_NOTCH_R);
+ startFillPath.close();
+ SkPath startStrokePath;
+ startStrokePath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
+ startStrokePath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
+ startStrokePath.lineTo(0, CONTROL_HEIGHT);
+ startStrokePath.lineTo(0, CONTROL_OFFSET);
+ startStrokePath.close();
+ SkPoint gradientLine[] = {{0, 0}, {0, CONTROL_HEIGHT}};
+ SkColor gradientColors[] = {FILL_GRADIENT_TOP, FILL_GRADIENT_BOTTOM};
+ SkShader* fillGradient = SkGradientShader::CreateLinear(gradientLine,
+ gradientColors, 0, 2, SkShader::kClamp_TileMode);
+ SkPoint dropLine[] = {{0, CONTROL_HEIGHT}, {0, CONTROL_HEIGHT + DROP_HEIGHT}};
+ SkColor dropColors[] = {DROP_GRADIENT_TOP, DROP_GRADIENT_BOTTOM};
+ SkShader* dropGradient = SkGradientShader::CreateLinear(dropLine,
+ dropColors, 0, 2, SkShader::kClamp_TileMode);
+ SkRect startDropRect = {-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT,
+ STROKE_OUTSET, CONTROL_HEIGHT + DROP_HEIGHT};
+
+ SkCanvas* canvas = m_startControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT + DROP_HEIGHT);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setShader(fillGradient);
+ canvas->drawPath(startFillPath, paint);
+ paint.setShader(0);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(STROKE_COLOR);
+ paint.setStrokeWidth(STROKE_WIDTH);
+ canvas->drawPath(startStrokePath, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xff000000);
+ paint.setShader(dropGradient);
+ canvas->drawRect(startDropRect, paint);
+ m_startControl.endRecording();
+
+ SkPath endFillPath;
+ endFillPath.moveTo(STROKE_OUTSET, CONTROL_OFFSET + STROKE_NOTCH_R);
+ endFillPath.lineTo(STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
+ endFillPath.lineTo(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET);
+ endFillPath.lineTo(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH);
+ endFillPath.close();
+ SkPath endStrokePath;
+ endStrokePath.moveTo(0, CONTROL_OFFSET);
+ endStrokePath.lineTo(0, CONTROL_HEIGHT);
+ endStrokePath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
+ endStrokePath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
+ endStrokePath.close();
+ SkRect endDropRect = {-STROKE_OUTSET, CONTROL_HEIGHT,
+ CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + DROP_HEIGHT};
+
+ canvas = m_endControl.beginRecording(CONTROL_WIDTH, CONTROL_HEIGHT);
+ paint.setColor(0xff000000);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setShader(fillGradient);
+ canvas->drawPath(endFillPath, paint);
+ paint.setShader(0);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(STROKE_COLOR);
+ paint.setStrokeWidth(STROKE_WIDTH);
+ canvas->drawPath(endStrokePath, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(0xff000000);
+ paint.setShader(dropGradient);
+ canvas->drawRect(endDropRect, paint);
+ m_endControl.endRecording();
+ fillGradient->safeUnref();
+ dropGradient->safeUnref();
+ m_picture = 0;
+}
+
void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer)
{
- if (layer->picture() != m_picture)
+ // Gmail makes layers appear dynamically the page scrolls. The picture
+ // recorded when the selection begins is confused by the pictures seen
+ // in subsequent layers. To work around this, only allow text selection
+ // in the main picture.
+ if (layer->uniqueId() != -1)
+ return;
+ // FIXME: layer may not own the original selected picture
+ m_picture = layer->picture();
+ if (!m_picture)
return;
- if (m_drawRegion)
+ 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 +1310,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 +1328,96 @@ 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, 0xFF, 0xA8, 0x00));
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)
+{
+ if (!picture)
+ return;
+ 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);
+ 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;
}
@@ -447,35 +1428,178 @@ 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
{
- if (!extendSelection)
+ 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;
+ SkIRect test;
+ test.set(x - CONTROL_WIDTH, y - CONTROL_HEIGHT, x + CONTROL_WIDTH,
+ y + CONTROL_HEIGHT);
+ return m_selRegion.intersects(test);
+}
+
+void SelectText::moveSelection(const SkPicture* picture, int x, int y)
+{
+ if (!picture)
+ return;
+ 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.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");
+ }
}
}
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 6c58852..913c4ba 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -637,19 +637,19 @@ void getVisibleRect(WebCore::IntRect* rect)
checkException(env);
}
-static CachedFrame::Direction KeyToDirection(KeyCode keyCode)
+static CachedFrame::Direction KeyToDirection(int32_t keyCode)
{
switch (keyCode) {
- case kKeyCodeDpadRight:
+ case AKEYCODE_DPAD_RIGHT:
DBG_NAV_LOGD("keyCode=%s", "right");
return CachedFrame::RIGHT;
- case kKeyCodeDpadLeft:
+ case AKEYCODE_DPAD_LEFT:
DBG_NAV_LOGD("keyCode=%s", "left");
return CachedFrame::LEFT;
- case kKeyCodeDpadDown:
+ case AKEYCODE_DPAD_DOWN:
DBG_NAV_LOGD("keyCode=%s", "down");
return CachedFrame::DOWN;
- case kKeyCodeDpadUp:
+ case AKEYCODE_DPAD_UP:
DBG_NAV_LOGD("keyCode=%s", "up");
return CachedFrame::UP;
default:
@@ -686,7 +686,7 @@ bool moveCursor(int keyCode, int count, bool ignoreScroll)
}
m_viewImpl->m_moveGeneration++;
- CachedFrame::Direction direction = KeyToDirection((KeyCode) keyCode);
+ CachedFrame::Direction direction = KeyToDirection(keyCode);
const CachedFrame* cachedFrame, * oldFrame = 0;
const CachedNode* cursor = root->currentCursor(&oldFrame);
WebCore::IntPoint cursorLocation = root->cursorLocation();
@@ -946,7 +946,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 +957,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 setSelectionPointer(bool set, float scale, int x, int y,
- bool extendSelection)
+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();
+}
+
+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 +1090,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!");
@@ -1441,17 +1501,32 @@ static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj)
return env->NewString((jchar*)name.characters(), name.length());
}
+static jobject createJavaRect(JNIEnv* env, int x, int y, int right, int bottom)
+{
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, x, y, right, bottom);
+ return rect;
+}
+
static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
{
const CachedFrame* frame;
const CachedNode* node = getFocusCandidate(env, obj, &frame);
WebCore::IntRect bounds = node ? node->bounds(frame)
: WebCore::IntRect(0, 0, 0, 0);
- jclass rectClass = env->FindClass("android/graphics/Rect");
- jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
- jobject rect = env->NewObject(rectClass, init, bounds.x(),
- bounds.y(), bounds.right(), bounds.bottom());
- return rect;
+ return createJavaRect(env, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
+}
+
+static jobject nativeFocusCandidatePaddingRect(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return 0;
+ // Note that the Java Rect is being used to pass four integers, rather than
+ // being used as an actual rectangle.
+ return createJavaRect(env, input->paddingLeft(), input->paddingTop(),
+ input->paddingRight(), input->paddingBottom());
}
static jint nativeFocusCandidatePointer(JNIEnv *env, jobject obj)
@@ -1735,6 +1810,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 +1887,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 +1930,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)
+{
+ 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)->setSelectionRegion(set);
+ GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
}
#ifdef ANDROID_DUMP_DISPLAY_TREE
@@ -1935,10 +2055,14 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(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",
@@ -1955,6 +2079,8 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeFocusCandidateName },
{ "nativeFocusCandidateNodeBounds", "()Landroid/graphics/Rect;",
(void*) nativeFocusCandidateNodeBounds },
+ { "nativeFocusCandidatePaddingRect", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusCandidatePaddingRect },
{ "nativeFocusCandidatePointer", "()I",
(void*) nativeFocusCandidatePointer },
{ "nativeFocusCandidateText", "()Ljava/lang/String;",
@@ -1979,6 +2105,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 +2119,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",
@@ -2009,16 +2147,18 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeSetHeightCanMeasure },
{ "nativeSetRootLayer", "(I)V",
(void*) nativeSetRootLayer },
- { "nativeSetSelectionPointer", "(ZFIIZ)V",
+ { "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 },
};
diff --git a/WebKit/android/plugins/ANPKeyCodes.h b/WebKit/android/plugins/ANPKeyCodes.h
index 328eb56..edfe2b9 100644
--- a/WebKit/android/plugins/ANPKeyCodes.h
+++ b/WebKit/android/plugins/ANPKeyCodes.h
@@ -29,8 +29,7 @@
/* List the key codes that are set to a plugin in the ANPKeyEvent.
These exactly match the values in android/view/KeyEvent.java and the
- corresponding .h file include/ui/KeycodeLabels.h, which contains more than
- I want to publish to plugin authors.
+ corresponding .h file android/keycodes.h.
*/
enum ANPKeyCodes {
kUnknown_ANPKeyCode = 0,
@@ -118,7 +117,36 @@ enum ANPKeyCodes {
kPlus_ANPKeyCode = 81,
kMenu_ANPKeyCode = 82,
kNotification_ANPKeyCode = 83,
- kSearch_ANPKeyCode = 84
+ kSearch_ANPKeyCode = 84,
+ kMediaPlayPause_ANPKeyCode = 85,
+ kMediaStop_ANPKeyCode = 86,
+ kMediaNext_ANPKeyCode = 87,
+ kMediaPrevious_ANPKeyCode = 88,
+ kMediaRewind_ANPKeyCode = 89,
+ kMediaFastForward_ANPKeyCode = 90,
+ kMute_ANPKeyCode = 91,
+ kPageUp_ANPKeyCode = 92,
+ kPageDown_ANPKeyCode = 93,
+ kPictsymbols_ANPKeyCode = 94,
+ kSwitchCharset_ANPKeyCode = 95,
+ kButtonA_ANPKeyCode = 96,
+ kButtonB_ANPKeyCode = 97,
+ kButtonC_ANPKeyCode = 98,
+ kButtonX_ANPKeyCode = 99,
+ kButtonY_ANPKeyCode = 100,
+ kButtonZ_ANPKeyCode = 101,
+ kButtonL1_ANPKeyCode = 102,
+ kButtonR1_ANPKeyCode = 103,
+ kButtonL2_ANPKeyCode = 104,
+ kButtonR2_ANPKeyCode = 105,
+ kButtonThumbL_ANPKeyCode = 106,
+ kButtonThumbR_ANPKeyCode = 107,
+ kButtonStart_ANPKeyCode = 108,
+ kButtonSelect_ANPKeyCode = 109,
+ kButtonMode_ANPKeyCode = 110,
+
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
};
#endif
diff --git a/WebKit/android/plugins/ANPSurfaceInterface.cpp b/WebKit/android/plugins/ANPSurfaceInterface.cpp
index 8830191..4b99b31 100644
--- a/WebKit/android/plugins/ANPSurfaceInterface.cpp
+++ b/WebKit/android/plugins/ANPSurfaceInterface.cpp
@@ -64,7 +64,7 @@ static inline sp<Surface> getSurface(JNIEnv* env, jobject view) {
jclass surfaceClass = env->FindClass("android/view/Surface");
gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
- "mSurface", "I");
+ ANDROID_VIEW_SURFACE_JNI_ID, "I");
env->DeleteLocalRef(surfaceClass);
env->DeleteLocalRef(surfaceViewClass);