summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/android
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/android')
-rw-r--r--Source/WebKit/android/AndroidLog.h36
-rw-r--r--Source/WebKit/android/RenderSkinAndroid.cpp13
-rw-r--r--Source/WebKit/android/RenderSkinAndroid.h4
-rw-r--r--Source/WebKit/android/RenderSkinButton.cpp105
-rw-r--r--Source/WebKit/android/RenderSkinButton.h55
-rw-r--r--Source/WebKit/android/RenderSkinCombo.cpp213
-rw-r--r--Source/WebKit/android/RenderSkinCombo.h62
-rw-r--r--Source/WebKit/android/RenderSkinMediaButton.cpp15
-rw-r--r--Source/WebKit/android/RenderSkinMediaButton.h2
-rw-r--r--Source/WebKit/android/RenderSkinNinePatch.cpp16
-rw-r--r--Source/WebKit/android/RenderSkinNinePatch.h2
-rw-r--r--Source/WebKit/android/RenderSkinRadio.cpp113
-rw-r--r--Source/WebKit/android/RenderSkinRadio.h (renamed from Source/WebKit/android/jni/AndroidHitTestResult.h)56
-rw-r--r--Source/WebKit/android/TimeCounter.cpp198
-rw-r--r--Source/WebKit/android/TimeCounter.h120
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp32
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h49
-rw-r--r--Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp18
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp59
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h12
-rw-r--r--Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp57
-rw-r--r--Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp4
-rw-r--r--Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp57
-rw-r--r--Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp9
-rw-r--r--Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp64
-rw-r--r--Source/WebKit/android/WebCoreSupport/V8Counters.cpp116
-rw-r--r--Source/WebKit/android/WebCoreSupport/V8Counters.h77
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCache.cpp17
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCache.h3
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp35
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCookieJar.h9
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequest.cpp37
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequest.h1
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp9
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp1
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp2
-rw-r--r--Source/WebKit/android/benchmark/Android.mk41
-rw-r--r--Source/WebKit/android/benchmark/Intercept.cpp190
-rw-r--r--Source/WebKit/android/benchmark/Intercept.h82
-rw-r--r--Source/WebKit/android/benchmark/MyJavaVM.cpp130
-rw-r--r--Source/WebKit/android/benchmark/MyJavaVM.h34
-rw-r--r--Source/WebKit/android/benchmark/main.cpp65
-rw-r--r--Source/WebKit/android/content/PhoneEmailDetector.cpp383
-rw-r--r--Source/WebKit/android/content/PhoneEmailDetector.h81
-rw-r--r--Source/WebKit/android/content/address_detector.cpp945
-rw-r--r--Source/WebKit/android/content/address_detector.h132
-rw-r--r--Source/WebKit/android/content/content_detector.cpp99
-rw-r--r--Source/WebKit/android/content/content_detector.h110
-rw-r--r--Source/WebKit/android/jni/AndroidHitTestResult.cpp269
-rw-r--r--Source/WebKit/android/jni/CacheManager.cpp6
-rw-r--r--Source/WebKit/android/jni/CookieManager.cpp47
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp10
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h2
-rwxr-xr-xSource/WebKit/android/jni/GeolocationPermissionsBridge.cpp4
-rw-r--r--Source/WebKit/android/jni/JavaBridge.cpp38
-rw-r--r--Source/WebKit/android/jni/JavaSharedClient.cpp47
-rw-r--r--Source/WebKit/android/jni/JniUtil.cpp58
-rw-r--r--Source/WebKit/android/jni/MIMETypeRegistry.cpp4
-rwxr-xr-xSource/WebKit/android/jni/MockGeolocation.cpp2
-rw-r--r--Source/WebKit/android/jni/PicturePile.cpp301
-rw-r--r--Source/WebKit/android/jni/PicturePile.h121
-rw-r--r--Source/WebKit/android/jni/PictureSet.cpp1236
-rw-r--r--Source/WebKit/android/jni/PictureSet.h145
-rw-r--r--Source/WebKit/android/jni/ViewStateSerializer.cpp197
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.cpp660
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.h14
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.cpp41
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.h6
-rw-r--r--Source/WebKit/android/jni/WebCoreJniOnLoad.cpp174
-rw-r--r--Source/WebKit/android/jni/WebCoreResourceLoader.cpp352
-rw-r--r--Source/WebKit/android/jni/WebCoreResourceLoader.h78
-rw-r--r--Source/WebKit/android/jni/WebCoreViewBridge.h2
-rw-r--r--Source/WebKit/android/jni/WebFrameView.cpp49
-rw-r--r--Source/WebKit/android/jni/WebFrameView.h2
-rw-r--r--Source/WebKit/android/jni/WebHistory.cpp754
-rw-r--r--Source/WebKit/android/jni/WebHistory.h32
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.cpp57
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.h2
-rw-r--r--Source/WebKit/android/jni/WebSettings.cpp153
-rw-r--r--Source/WebKit/android/jni/WebStorage.cpp6
-rw-r--r--Source/WebKit/android/jni/WebViewCore.cpp3303
-rw-r--r--Source/WebKit/android/jni/WebViewCore.h366
-rw-r--r--Source/WebKit/android/nav/CacheBuilder.cpp3194
-rw-r--r--Source/WebKit/android/nav/CacheBuilder.h297
-rw-r--r--Source/WebKit/android/nav/CachedColor.cpp58
-rw-r--r--Source/WebKit/android/nav/CachedColor.h87
-rw-r--r--Source/WebKit/android/nav/CachedDebug.h72
-rw-r--r--Source/WebKit/android/nav/CachedFrame.cpp1511
-rw-r--r--Source/WebKit/android/nav/CachedFrame.h287
-rw-r--r--Source/WebKit/android/nav/CachedHistory.cpp183
-rw-r--r--Source/WebKit/android/nav/CachedHistory.h89
-rw-r--r--Source/WebKit/android/nav/CachedInput.cpp100
-rw-r--r--Source/WebKit/android/nav/CachedInput.h115
-rw-r--r--Source/WebKit/android/nav/CachedLayer.cpp221
-rw-r--r--Source/WebKit/android/nav/CachedLayer.h86
-rw-r--r--Source/WebKit/android/nav/CachedNode.cpp431
-rw-r--r--Source/WebKit/android/nav/CachedNode.h245
-rw-r--r--Source/WebKit/android/nav/CachedNodeType.h56
-rw-r--r--Source/WebKit/android/nav/CachedPrefix.h53
-rw-r--r--Source/WebKit/android/nav/CachedRoot.cpp1815
-rw-r--r--Source/WebKit/android/nav/CachedRoot.h142
-rw-r--r--Source/WebKit/android/nav/DrawExtra.cpp96
-rw-r--r--Source/WebKit/android/nav/DrawExtra.h38
-rw-r--r--Source/WebKit/android/nav/FindCanvas.cpp700
-rw-r--r--Source/WebKit/android/nav/FindCanvas.h259
-rw-r--r--Source/WebKit/android/nav/SelectText.cpp1931
-rw-r--r--Source/WebKit/android/nav/SelectText.h97
-rw-r--r--Source/WebKit/android/nav/WebView.cpp2718
-rw-r--r--Source/WebKit/android/plugins/ANPSoundInterface.cpp8
-rw-r--r--Source/WebKit/android/plugins/ANPSurfaceInterface.cpp16
-rw-r--r--Source/WebKit/android/plugins/PluginWidgetAndroid.cpp7
-rw-r--r--Source/WebKit/android/smoke/MessageThread.cpp2
-rw-r--r--Source/WebKit/android/wds/Command.cpp2
-rw-r--r--Source/WebKit/android/wds/Connection.cpp2
-rw-r--r--Source/WebKit/android/wds/DebugServer.cpp16
-rw-r--r--Source/WebKit/android/wds/client/AdbConnection.cpp24
-rw-r--r--Source/WebKit/android/wds/client/ClientUtils.h2
-rw-r--r--Source/WebKit/android/wds/client/main.cpp16
118 files changed, 20857 insertions, 6627 deletions
diff --git a/Source/WebKit/android/AndroidLog.h b/Source/WebKit/android/AndroidLog.h
index f034d35..3d3eaaa 100644
--- a/Source/WebKit/android/AndroidLog.h
+++ b/Source/WebKit/android/AndroidLog.h
@@ -26,25 +26,17 @@
#ifndef AndroidLog_h
#define AndroidLog_h
-#ifndef LOG_TAG
-#define LOG_TAG __FILE__
-#endif
-
-#include <cutils/log.h>
-#include <utils/Trace.h>
-#include <wtf/CurrentTime.h>
-
#ifdef ANDROID_DOM_LOGGING
#include <stdio.h>
extern FILE* gDomTreeFile;
#define DOM_TREE_LOG_FILE "/sdcard/domTree.txt"
#define DUMP_DOM_LOGD(...) { if (gDomTreeFile) \
- fprintf(gDomTreeFile, __VA_ARGS__); else ALOGD(__VA_ARGS__); }
+ fprintf(gDomTreeFile, __VA_ARGS__); else LOGD(__VA_ARGS__); }
extern FILE* gRenderTreeFile;
#define RENDER_TREE_LOG_FILE "/sdcard/renderTree.txt"
#define DUMP_RENDER_LOGD(...) { if (gRenderTreeFile) \
- fprintf(gRenderTreeFile, __VA_ARGS__); else ALOGD(__VA_ARGS__); }
+ fprintf(gRenderTreeFile, __VA_ARGS__); else LOGD(__VA_ARGS__); }
#else
#define DUMP_DOM_LOGD(...) ((void)0)
#define DUMP_RENDER_LOGD(...) ((void)0)
@@ -53,28 +45,4 @@ extern FILE* gRenderTreeFile;
#define DISPLAY_TREE_LOG_FILE "/sdcard/displayTree.txt"
#define LAYERS_TREE_LOG_FILE "/sdcard/layersTree.plist"
-#define FLOAT_RECT_FORMAT "[x=%.2f,y=%.2f,w=%.2f,h=%.2f]"
-#define FLOAT_RECT_ARGS(fr) fr.x(), fr.y(), fr.width(), fr.height()
-#define INT_RECT_FORMAT "[x=%d,y=%d,w=%d,h=%d]"
-#define INT_RECT_ARGS(ir) ir.x(), ir.y(), ir.width(), ir.height()
-
-#define TRACE_METHOD() android::ScopedTrace __st(ATRACE_TAG_WEBVIEW, __func__);
-
-#define TIME_METHOD() MethodTimer __method_timer(__func__)
-class MethodTimer {
-public:
- MethodTimer(const char* name)
- : m_methodName(name)
- {
- m_startTime = currentTimeMS();
- }
- virtual ~MethodTimer() {
- double duration = currentTimeMS() - m_startTime;
- ALOGD("%s took %.2fms", m_methodName, duration);
- }
-private:
- const char* m_methodName;
- double m_startTime;
-};
-
#endif // AndroidLog_h
diff --git a/Source/WebKit/android/RenderSkinAndroid.cpp b/Source/WebKit/android/RenderSkinAndroid.cpp
index 9529624..4a9ce68 100644
--- a/Source/WebKit/android/RenderSkinAndroid.cpp
+++ b/Source/WebKit/android/RenderSkinAndroid.cpp
@@ -27,11 +27,14 @@
#include "config.h"
#include "RenderSkinAndroid.h"
+#include "RenderSkinButton.h"
+#include "RenderSkinCombo.h"
#include "RenderSkinMediaButton.h"
+#include "RenderSkinRadio.h"
#include "SkImageDecoder.h"
-#include <androidfw/AssetManager.h>
-#include <androidfw/Asset.h>
+#include <utils/AssetManager.h>
+#include <utils/Asset.h>
namespace WebCore {
@@ -40,6 +43,7 @@ RenderSkinAndroid::Resolution RenderSkinAndroid::s_drawableResolution = RenderSk
RenderSkinAndroid::~RenderSkinAndroid()
{
+ delete m_button;
}
RenderSkinAndroid::RenderSkinAndroid(String drawableDirectory)
{
@@ -52,6 +56,7 @@ RenderSkinAndroid::RenderSkinAndroid(String drawableDirectory)
s_drawableDirectory = drawableDirectory;
}
+ m_button = new RenderSkinButton(drawableDirectory);
}
bool RenderSkinAndroid::DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap)
@@ -60,14 +65,14 @@ bool RenderSkinAndroid::DecodeBitmap(android::AssetManager* am, const char* file
if (!asset) {
asset = am->openNonAsset(fileName, android::Asset::ACCESS_BUFFER);
if (!asset) {
- ALOGD("RenderSkinAndroid: File \"%s\" not found.\n", fileName);
+ LOGD("RenderSkinAndroid: File \"%s\" not found.\n", fileName);
return false;
}
}
bool success = SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), bitmap);
if (!success) {
- ALOGD("RenderSkinAndroid: Failed to decode %s\n", fileName);
+ LOGD("RenderSkinAndroid: Failed to decode %s\n", fileName);
}
delete asset;
diff --git a/Source/WebKit/android/RenderSkinAndroid.h b/Source/WebKit/android/RenderSkinAndroid.h
index 1d3820d..bbc327d 100644
--- a/Source/WebKit/android/RenderSkinAndroid.h
+++ b/Source/WebKit/android/RenderSkinAndroid.h
@@ -36,6 +36,7 @@ class SkBitmap;
namespace WebCore {
class Node;
+class RenderSkinButton;
class RenderSkinAndroid
{
@@ -68,9 +69,12 @@ public:
static String DrawableDirectory() { return s_drawableDirectory; }
static Resolution DrawableResolution() { return s_drawableResolution; }
+ RenderSkinButton* renderSkinButton() const { return m_button; }
+
private:
static String s_drawableDirectory;
static Resolution s_drawableResolution;
+ RenderSkinButton* m_button;
};
} // WebCore
diff --git a/Source/WebKit/android/RenderSkinButton.cpp b/Source/WebKit/android/RenderSkinButton.cpp
new file mode 100644
index 0000000..11e2fa8
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinButton.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2006, 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 "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinButton.h"
+#include "RenderSkinNinePatch.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include "SkRect.h"
+#include <utils/Asset.h>
+#include <utils/AssetManager.h>
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/ResourceTypes.h>
+#include <wtf/text/CString.h>
+
+extern android::AssetManager* globalAssetManager();
+
+static const char* gFiles[] = {
+ "btn_default_disabled_holo.9.png",
+ "btn_default_normal_holo.9.png",
+ "btn_default_focused_holo.9.png",
+ "btn_default_pressed_holo.9.png"
+ };
+
+namespace WebCore {
+
+RenderSkinButton::RenderSkinButton(String drawableDirectory)
+ : m_decoded(false)
+ , m_decodingAttempted(false)
+ , m_drawableDirectory(drawableDirectory)
+{
+ // Ensure our enums properly line up with our arrays.
+ android::CompileTimeAssert<(RenderSkinAndroid::kDisabled == 0)> a1;
+ android::CompileTimeAssert<(RenderSkinAndroid::kNormal == 1)> a2;
+ android::CompileTimeAssert<(RenderSkinAndroid::kFocused == 2)> a3;
+ android::CompileTimeAssert<(RenderSkinAndroid::kPressed == 3)> a4;
+}
+
+void RenderSkinButton::decode()
+{
+ m_decodingAttempted = true;
+
+ android::AssetManager* am = globalAssetManager();
+
+ for (size_t i = 0; i < 4; i++) {
+ String path = m_drawableDirectory;
+ path.append(String(gFiles[i]));
+ if (!RenderSkinNinePatch::decodeAsset(am, path.utf8().data(), &m_buttons[i])) {
+ m_decoded = false;
+ LOGE("RenderSkinButton::decode: button assets failed to decode\n\tWebView buttons will not draw");
+ return;
+ }
+ }
+ m_decoded = true;
+}
+
+void RenderSkinButton::draw(SkCanvas* canvas, const IntRect& r,
+ RenderSkinAndroid::State newState)
+{
+ if (!m_decodingAttempted)
+ decode();
+
+ // 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 (!m_decoded) {
+ return;
+ }
+
+ // Ensure that the state is within the valid range of our array.
+ SkASSERT(static_cast<unsigned>(newState) <
+ static_cast<unsigned>(RenderSkinAndroid::kNumStates));
+
+ RenderSkinNinePatch::DrawNinePatch(canvas, SkRect(r), m_buttons[newState]);
+}
+
+} //WebCore
diff --git a/Source/WebKit/android/RenderSkinButton.h b/Source/WebKit/android/RenderSkinButton.h
new file mode 100644
index 0000000..83c57dd
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinButton.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2006, 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 RenderSkinButton_h
+#define RenderSkinButton_h
+
+#include "RenderSkinAndroid.h"
+#include "RenderSkinNinePatch.h"
+
+class SkCanvas;
+
+namespace WebCore {
+class IntRect;
+
+class RenderSkinButton {
+public:
+ RenderSkinButton(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.
+ */
+ void draw(SkCanvas* , const IntRect& , RenderSkinAndroid::State);
+
+ void decode();
+private:
+ bool m_decoded;
+ bool m_decodingAttempted;
+ NinePatch m_buttons[4];
+ String m_drawableDirectory;
+};
+
+} // WebCore
+#endif
diff --git a/Source/WebKit/android/RenderSkinCombo.cpp b/Source/WebKit/android/RenderSkinCombo.cpp
new file mode 100644
index 0000000..1711cfa
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinCombo.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RenderSkinCombo.h"
+
+#include "Document.h"
+#include "Element.h"
+#include "Node.h"
+#include "NodeRenderStyle.h"
+#include "RenderStyle.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include <utils/AssetManager.h>
+#include <wtf/text/CString.h>
+
+extern android::AssetManager* globalAssetManager();
+
+namespace WebCore {
+
+// Indicates if the entire asset is being drawn, or if the border is being
+// excluded and just the arrow drawn.
+enum BorderStyle {
+ FullAsset,
+ NoBorder,
+ BorderStyleCount // Keep at the end.
+};
+
+// There are 2.5 different concepts of a 'border' here, which results
+// in rather a lot of magic constants.
+
+// 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[ResolutionCount] = {
+ 16, // Medium resolution
+ 25, // High resolution
+ 34 // Extra high resolution
+};
+const int RenderSkinCombo::padMargin[ResolutionCount] = {
+ 1, // Medium resolution
+ 1, // High resolution
+ 1 // Extra high resolution
+};
+
+namespace {
+// 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.
+const int stretchMargin[RenderSkinAndroid::ResolutionCount] = { // border width for the bottom and left of the 9-patch
+ 2, // Medium resolution
+ 2, // High resolution
+ 3 // Extra high resolution
+
+};
+const int stretchTop[RenderSkinAndroid::ResolutionCount] = { // border width for the top of the 9-patch
+ 16, // Medium resolution
+ 23, // High resolution
+ 32 // Extra high resolution
+};
+
+// 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.
+const int arrowWidth[RenderSkinAndroid::ResolutionCount] = {
+ 18, // Medium resolution
+ 27, // High resolution
+ 36 // Extra high resolution
+};
+
+// scale factors for various resolutions
+const float scaleFactor[RenderSkinAndroid::ResolutionCount] = {
+ 1.0f, // medium res
+ 1.5f, // high res
+ 2.0f // extra high res
+};
+
+// Store the calculated 9 patch margins for each border style.
+SkIRect margin[BorderStyleCount];
+
+SkBitmap bitmaps[2][BorderStyleCount]; // Collection of assets for a combo box - 2 states (enabled/disabled)
+bool isDecodingAttempted = false; // True if we've tried to decode the assets
+bool isDecoded = false; // True if all assets were decoded
+
+} // namespace
+
+int RenderSkinCombo::minHeight() {
+ return SkScalarRound(stretchTop[RenderSkinAndroid::DrawableResolution()]
+ / scaleFactor[RenderSkinAndroid::DrawableResolution()]);
+}
+
+void RenderSkinCombo::Decode()
+{
+ if (isDecodingAttempted)
+ return;
+
+ isDecodingAttempted = true;
+ isDecoded = false;
+
+ android::AssetManager* am = globalAssetManager();
+
+ String drawableDirectory = RenderSkinAndroid::DrawableDirectory();
+ Resolution res = RenderSkinAndroid::DrawableResolution();
+
+ 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[res], 0, width, height);
+ bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
+ bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
+
+ // Calculate 9 patch margins.
+ SkIRect fullAssetMargin;
+ fullAssetMargin.fLeft = stretchMargin[res];
+ fullAssetMargin.fTop = stretchMargin[res];
+ fullAssetMargin.fRight = arrowMargin[res] + stretchMargin[res];
+ fullAssetMargin.fBottom = stretchTop[res];
+
+ SkIRect noBorderMargin;
+ noBorderMargin.fLeft = 0;
+ noBorderMargin.fTop = stretchTop[res];
+ noBorderMargin.fRight = 0;
+ noBorderMargin.fBottom = stretchMargin[res];
+
+ margin[FullAsset] = fullAssetMargin;
+ margin[NoBorder] = noBorderMargin;
+}
+
+bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
+{
+ if (!isDecodingAttempted)
+ Decode();
+
+ if (!isDecoded)
+ return true;
+
+ int resolution = RenderSkinAndroid::DrawableResolution();
+ State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
+ height = std::max(height, (stretchMargin[resolution] * 2));
+
+ 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();
+ SkPaint paint;
+ paint.setColor(style->visitedDependentColor(CSSPropertyBackgroundColor).rgb());
+ canvas->drawRect(bounds, paint);
+
+ bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height));
+
+ // If this is an appearance where RenderTheme::paint returns true
+ // without doing anything, this means that
+ // RenderBox::PaintBoxDecorationWithSize will end up painting the
+ // border, so we shouldn't paint a border here.
+ if (style->appearance() == MenulistButtonPart ||
+ style->appearance() == ListboxPart ||
+ style->appearance() == TextFieldPart ||
+ style->appearance() == TextAreaPart) {
+ bounds.fLeft += SkIntToScalar(width - RenderSkinCombo::extraWidth());
+ bounds.fRight -= SkIntToScalar(style->borderRightWidth());
+ bounds.fTop += SkIntToScalar(style->borderTopWidth());
+ bounds.fBottom -= SkIntToScalar(style->borderBottomWidth());
+ drawBorder = NoBorder;
+ }
+ float scale = scaleFactor[resolution];
+ bounds.fLeft = bounds.fLeft * scale;
+ bounds.fRight = bounds.fRight * scale;
+ bounds.fTop = bounds.fTop * scale;
+ bounds.fBottom = bounds.fBottom * scale;
+ int count = canvas->save();
+ canvas->scale(1.0f / scale, 1.0f / scale);
+ SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[drawBorder]);
+ canvas->restoreToCount(count);
+ return false;
+}
+
+} // namspace WebCore
diff --git a/Source/WebKit/android/RenderSkinCombo.h b/Source/WebKit/android/RenderSkinCombo.h
new file mode 100644
index 0000000..a11faac
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinCombo.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006, 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 RenderSkinCombo_h
+#define RenderSkinCombo_h
+
+#include "RenderSkinAndroid.h"
+#include "SkRect.h"
+
+class SkCanvas;
+
+namespace WebCore {
+
+// This is very similar to RenderSkinButton - maybe they should be the same class?
+class RenderSkinCombo : public RenderSkinAndroid
+{
+public:
+
+ static void Decode();
+ /**
+ * Draw the provided Node on the SkCanvas, using the dimensions provided by
+ * x,y,w,h. Return true if we did not draw, and WebKit needs to draw it,
+ * false otherwise.
+ */
+ 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[RenderSkinAndroid::DrawableResolution()]; }
+ static int minHeight();
+ static int padding() { return padMargin[RenderSkinAndroid::DrawableResolution()]; }
+
+
+private:
+ const static int arrowMargin[ResolutionCount];
+ const static int padMargin[ResolutionCount];
+};
+
+} // WebCore
+
+#endif
diff --git a/Source/WebKit/android/RenderSkinMediaButton.cpp b/Source/WebKit/android/RenderSkinMediaButton.cpp
index b3aa57d..ef4b313 100644
--- a/Source/WebKit/android/RenderSkinMediaButton.cpp
+++ b/Source/WebKit/android/RenderSkinMediaButton.cpp
@@ -36,7 +36,7 @@
#include "SkCanvas.h"
#include "SkNinePatch.h"
#include "SkRect.h"
-#include <androidfw/AssetManager.h>
+#include <utils/AssetManager.h>
#include <utils/Debug.h>
#include <utils/Log.h>
#include <wtf/text/CString.h>
@@ -82,22 +82,19 @@ void RenderSkinMediaButton::Decode()
String path = drawableDirectory + gFiles[i].name;
if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) {
gDecodingFailed = true;
- ALOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ 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,
- bool translucent, RenderObject* o, bool drawBackground)
+ bool translucent, RenderObject* o)
{
if (!gDecoded) {
Decode();
}
- if (!canvas)
- return;
-
// 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 (gDecodingFailed)
@@ -105,6 +102,7 @@ void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonT
bool drawsNinePatch = false;
bool drawsImage = true;
+ bool drawsBackgroundColor = true;
int ninePatchIndex = 0;
int imageIndex = 0;
@@ -138,11 +136,13 @@ void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonT
case SPINNER_INNER:
case VIDEO:
{
+ drawsBackgroundColor = false;
imageIndex = buttonType + 1;
break;
}
case BACKGROUND_SLIDER:
{
+ drawsBackgroundColor = false;
drawsImage = false;
break;
}
@@ -155,6 +155,7 @@ void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonT
}
case SLIDER_THUMB:
{
+ drawsBackgroundColor = false;
imageMargin = 0;
imageIndex = buttonType + 1;
break;
@@ -163,7 +164,7 @@ void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonT
return;
}
- if (drawBackground) {
+ if (drawsBackgroundColor) {
canvas->drawRect(r, paint);
}
diff --git a/Source/WebKit/android/RenderSkinMediaButton.h b/Source/WebKit/android/RenderSkinMediaButton.h
index 484b90c..d8b7c8d 100644
--- a/Source/WebKit/android/RenderSkinMediaButton.h
+++ b/Source/WebKit/android/RenderSkinMediaButton.h
@@ -42,7 +42,7 @@ public:
* State to determine which skin to use, i.e. focused or not focused.
*/
static void Draw(SkCanvas* , const IntRect& , int buttonType, bool translucent = false,
- RenderObject* o = 0, bool drawBackground = true);
+ RenderObject* o = 0);
/**
* Button types
*/
diff --git a/Source/WebKit/android/RenderSkinNinePatch.cpp b/Source/WebKit/android/RenderSkinNinePatch.cpp
index 9ef0b4a..faa9dc4 100644
--- a/Source/WebKit/android/RenderSkinNinePatch.cpp
+++ b/Source/WebKit/android/RenderSkinNinePatch.cpp
@@ -16,18 +16,18 @@
#include "config.h"
-#include "NinePatchPeeker.h"
#include "RenderSkinNinePatch.h"
+#include "NinePatchPeeker.h"
#include "SkCanvas.h"
#include "SkImageDecoder.h"
#include "SkNinePatch.h"
#include "SkRect.h"
#include "SkStream.h"
#include "SkTemplates.h"
-#include <androidfw/Asset.h>
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
+#include <utils/Asset.h>
+#include <utils/AssetManager.h>
#include <utils/Log.h>
+#include <utils/ResourceTypes.h>
class SkPaint;
class SkRegion;
@@ -53,7 +53,7 @@ bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, Ni
SkImageDecoder* decoder = SkImageDecoder::Factory(&stream);
if (!decoder) {
asset->close();
- ALOGE("RenderSkinNinePatch::Failed to create an image decoder");
+ LOGE("RenderSkinNinePatch::Failed to create an image decoder");
return false;
}
@@ -68,13 +68,13 @@ bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, Ni
decoder->setPeeker(&peeker);
if (!decoder->decode(&stream, &ninepatch->m_bitmap, prefConfig, mode, true)) {
asset->close();
- ALOGE("RenderSkinNinePatch::Failed to decode nine patch asset");
+ LOGE("RenderSkinNinePatch::Failed to decode nine patch asset");
return false;
}
asset->close();
- if (!peeker.fPatch) {
- ALOGE("RenderSkinNinePatch::Patch data not valid");
+ if (!peeker.fPatchIsValid) {
+ LOGE("RenderSkinNinePatch::Patch data not valid");
return false;
}
void** data = &ninepatch->m_serializedPatchData;
diff --git a/Source/WebKit/android/RenderSkinNinePatch.h b/Source/WebKit/android/RenderSkinNinePatch.h
index 8cda795..e4db260 100644
--- a/Source/WebKit/android/RenderSkinNinePatch.h
+++ b/Source/WebKit/android/RenderSkinNinePatch.h
@@ -18,7 +18,7 @@
#define RenderSkinNinePatch_h
#include "SkBitmap.h"
-#include "androidfw/Asset.h"
+#include "utils/Asset.h"
namespace android {
class AssetManager;
diff --git a/Source/WebKit/android/RenderSkinRadio.cpp b/Source/WebKit/android/RenderSkinRadio.cpp
new file mode 100644
index 0000000..3c29818
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinRadio.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RenderSkinRadio.h"
+
+#include "android_graphics.h"
+#include "Document.h"
+#include "Element.h"
+#include "InputElement.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinAndroid.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include <utils/AssetManager.h>
+#include <wtf/text/CString.h>
+
+extern android::AssetManager* globalAssetManager();
+
+static const char* checks[] = { "btn_check_off_holo.png",
+ "btn_check_on_holo.png",
+ "btn_radio_off_holo.png",
+ "btn_radio_on_holo.png"};
+// Matches the width of the bitmap
+static SkScalar s_bitmapWidth;
+
+namespace WebCore {
+
+static SkBitmap s_bitmap[4];
+static bool s_decodingAttempted = false;
+static bool s_decoded = false;
+
+void RenderSkinRadio::Decode() {
+ if (s_decodingAttempted)
+ return;
+
+ s_decodingAttempted = true;
+ s_decoded = false;
+
+ android::AssetManager* am = globalAssetManager();
+ String drawableDir = RenderSkinAndroid::DrawableDirectory();
+ for (int i = 0; i < 4; i++) {
+ String path = drawableDir + checks[i];
+ if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[i]))
+ return;
+ }
+ s_decoded = true;
+ s_bitmapWidth = SkIntToScalar(s_bitmap[0].width());
+}
+
+void RenderSkinRadio::Draw(SkCanvas* canvas, Node* element, const IntRect& ir,
+ bool isCheckBox)
+{
+ if (!element)
+ return;
+
+ if (!s_decodingAttempted)
+ Decode();
+
+ if (!s_decoded)
+ return;
+
+ SkRect r(ir);
+ // Set up a paint to with filtering to look better.
+ SkPaint paint;
+ paint.setFlags(SkPaint::kFilterBitmap_Flag);
+ int saveScaleCount = 0;
+
+ if (!element->isElementNode() ||
+ !static_cast<Element*>(element)->isEnabledFormControl()) {
+ paint.setAlpha(0x80);
+ }
+ SkScalar width = r.width();
+ SkScalar scale = SkScalarDiv(width, s_bitmapWidth);
+ saveScaleCount = canvas->save();
+ canvas->translate(r.fLeft, r.fTop);
+ canvas->scale(scale, scale);
+
+ bool checked = false;
+ if (InputElement* inputElement = element->toInputElement()) {
+ checked = inputElement->isChecked();
+ }
+
+ canvas->drawBitmap(s_bitmap[checked + 2*(!isCheckBox)],
+ 0, 0, &paint);
+ canvas->restoreToCount(saveScaleCount);
+}
+
+} //WebCore
diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.h b/Source/WebKit/android/RenderSkinRadio.h
index 5bbfc6b..34101cf 100644
--- a/Source/WebKit/android/jni/AndroidHitTestResult.h
+++ b/Source/WebKit/android/RenderSkinRadio.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012, The Android Open Source Project
+ * Copyright 2006, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,45 +23,39 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AndroidHitTestResult_h
-#define AndroidHitTestResult_h
+#ifndef RenderSkinRadio_h
+#define RenderSkinRadio_h
-#include "content/content_detector.h"
-#include "Element.h"
-#include "HitTestResult.h"
-#include "IntRect.h"
-#include "wtf/Vector.h"
+#include "PlatformString.h"
-#include <jni.h>
+class SkCanvas;
namespace android {
+ class AssetManager;
+}
-class WebViewCore;
+namespace WebCore {
-class AndroidHitTestResult
+class Node;
+class IntRect;
+
+/* RenderSkin for a radio button or a checkbox
+ */
+class RenderSkinRadio
{
public:
- AndroidHitTestResult(WebViewCore*, WebCore::HitTestResult&);
- ~AndroidHitTestResult() {}
-
- WebCore::HitTestResult& hitTestResult() { return m_hitTestResult; }
- Vector<WebCore::IntRect>& highlightRects() { return m_highlightRects; }
+ static void SetDrawableDirectory(String drawableDirectory);
- void setURLElement(WebCore::Element* element);
- void buildHighlightRects();
- void searchContentDetectors();
+ // Perform lazy decoding the first time this a radio/checkbox is needed.
+ static void Decode();
- jobject createJavaObject(JNIEnv*);
-
-private:
- Vector<WebCore::IntRect> enclosingParentRects(WebCore::Node* node);
-
- WebViewCore* m_webViewCore;
- WebCore::HitTestResult m_hitTestResult;
- Vector<WebCore::IntRect> m_highlightRects;
- ContentDetector::Result m_searchResult;
+ /**
+ * Draw the element to the canvas at the specified size and location.
+ * param isCheckBox If true, draws a checkbox. Else, draw a radio button.
+ */
+ static void Draw(SkCanvas* canvas, Node* element, const IntRect&,
+ bool isCheckBox);
};
-} // namespace android
-
-#endif // AndroidHitTestResult_h
+} // WebCore
+#endif
diff --git a/Source/WebKit/android/TimeCounter.cpp b/Source/WebKit/android/TimeCounter.cpp
new file mode 100644
index 0000000..2393f8a
--- /dev/null
+++ b/Source/WebKit/android/TimeCounter.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2009, 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 "TimeCounter.h"
+
+#include "MemoryCache.h"
+#include "KURL.h"
+#include "Node.h"
+#include "SystemTime.h"
+#include "StyleBase.h"
+#include <sys/time.h>
+#include <time.h>
+#include <utils/Log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#if USE(JSC)
+#include "JSDOMWindow.h"
+#include <runtime/JSGlobalObject.h>
+#include <runtime/JSLock.h>
+#endif
+
+using namespace WebCore;
+using namespace WTF;
+using namespace JSC;
+
+namespace android {
+
+uint32_t getThreadMsec()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+ struct timespec tm;
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+ return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
+#else
+ struct timeval now;
+ struct timezone zone;
+
+ gettimeofday(&now, &zone);
+ return now.tv_sec * 1000LL + now.tv_usec / 1000;
+#endif
+}
+
+#ifdef ANDROID_INSTRUMENT
+
+static double sStartTotalTime;
+static uint32_t sStartThreadTime;
+static double sLastTotalTime;
+static uint32_t sLastThreadTime;
+
+uint32_t TimeCounter::sStartWebCoreThreadTime;
+uint32_t TimeCounter::sEndWebCoreThreadTime;
+bool TimeCounter::sRecordWebCoreTime;
+uint32_t TimeCounter::sTotalTimeUsed[TimeCounter::TotalTimeCounterCount];
+uint32_t TimeCounter::sLastTimeUsed[TimeCounter::TotalTimeCounterCount];
+uint32_t TimeCounter::sCounter[TimeCounter::TotalTimeCounterCount];
+uint32_t TimeCounter::sLastCounter[TimeCounter::TotalTimeCounterCount];
+uint32_t TimeCounter::sStartTime[TimeCounter::TotalTimeCounterCount];
+
+int QemuTracerAuto::reentry_count = 0;
+
+static const char* timeCounterNames[] = {
+ "css parsing",
+ "javascript",
+ "javascript init",
+ "javascript parsing",
+ "javascript execution",
+ "calculate style",
+ "Java callback (frame bridge)",
+ "parsing (may include calcStyle, Java callback or inline script execution)",
+ "layout",
+ "native 1 (frame bridge)",
+ "native 2 (resource load)",
+ "native 3 (shared timer)",
+ "build nav (webview core)",
+ "record content (webview core)",
+ "native 4 (webview core)",
+ "draw content (webview ui)",
+};
+
+void TimeCounter::record(enum Type type, const char* functionName)
+{
+ recordNoCounter(type, functionName);
+ sCounter[type]++;
+}
+
+void TimeCounter::recordNoCounter(enum Type type, const char* functionName)
+{
+ uint32_t time = sEndWebCoreThreadTime = getThreadMsec();
+ uint32_t elapsed = time - sStartTime[type];
+ sTotalTimeUsed[type] += elapsed;
+ if (elapsed > 1000)
+ LOGW("***** %s() used %d ms\n", functionName, elapsed);
+}
+
+void TimeCounter::report(const KURL& url, int live, int dead, size_t arenaSize)
+{
+ String urlString = url;
+ int totalTime = static_cast<int>((currentTime() - sStartTotalTime) * 1000);
+ int threadTime = getThreadMsec() - sStartThreadTime;
+ LOGD("*-* Total load time: %d ms, thread time: %d ms for %s\n",
+ totalTime, threadTime, urlString.utf8().data());
+ for (Type type = (Type) 0; type < TotalTimeCounterCount; type
+ = (Type) (type + 1)) {
+ char scratch[256];
+ int index = sprintf(scratch, "*-* Total %s time: %d ms",
+ timeCounterNames[type], sTotalTimeUsed[type]);
+ if (sCounter[type] > 0)
+ sprintf(&scratch[index], " called %d times", sCounter[type]);
+ LOGD("%s", scratch);
+ }
+ LOGD("Current cache has %d bytes live and %d bytes dead", live, dead);
+ LOGD("Current render arena takes %d bytes", arenaSize);
+#if USE(JSC)
+ JSLock lock(false);
+ Heap::Statistics jsHeapStatistics = JSDOMWindow::commonJSGlobalData()->heap.statistics();
+ LOGD("Current JavaScript heap size is %d and has %d bytes free",
+ jsHeapStatistics.size, jsHeapStatistics.free);
+#endif
+ LOGD("Current CSS styles use %d bytes", StyleBase::reportStyleSize());
+ LOGD("Current DOM nodes use %d bytes", WebCore::Node::reportDOMNodesSize());
+}
+
+void TimeCounter::reportNow()
+{
+ double current = currentTime();
+ uint32_t currentThread = getThreadMsec();
+ int elapsedTime = static_cast<int>((current - sLastTotalTime) * 1000);
+ int elapsedThreadTime = currentThread - sLastThreadTime;
+ LOGD("*-* Elapsed time: %d ms, ui thread time: %d ms, webcore thread time:"
+ " %d ms\n", elapsedTime, elapsedThreadTime, sEndWebCoreThreadTime -
+ sStartWebCoreThreadTime);
+ for (Type type = (Type) 0; type < TotalTimeCounterCount; type
+ = (Type) (type + 1)) {
+ if (sTotalTimeUsed[type] == sLastTimeUsed[type])
+ continue;
+ char scratch[256];
+ int index = sprintf(scratch, "*-* Diff %s time: %d ms",
+ timeCounterNames[type], sTotalTimeUsed[type] - sLastTimeUsed[type]);
+ if (sCounter[type] > sLastCounter[type])
+ sprintf(&scratch[index], " called %d times", sCounter[type]
+ - sLastCounter[type]);
+ LOGD("%s", scratch);
+ }
+ memcpy(sLastTimeUsed, sTotalTimeUsed, sizeof(sTotalTimeUsed));
+ memcpy(sLastCounter, sCounter, sizeof(sCounter));
+ sLastTotalTime = current;
+ sLastThreadTime = currentThread;
+ sRecordWebCoreTime = true;
+}
+
+void TimeCounter::reset() {
+ bzero(sTotalTimeUsed, sizeof(sTotalTimeUsed));
+ bzero(sCounter, sizeof(sCounter));
+ LOGD("*-* Start browser instrument\n");
+ sStartTotalTime = currentTime();
+ sStartThreadTime = getThreadMsec();
+}
+
+void TimeCounter::start(enum Type type)
+{
+ uint32_t time = getThreadMsec();
+ if (sRecordWebCoreTime) {
+ sStartWebCoreThreadTime = time;
+ sRecordWebCoreTime = false;
+ }
+ sStartTime[type] = time;
+}
+
+#endif // ANDROID_INSTRUMENT
+
+}
diff --git a/Source/WebKit/android/TimeCounter.h b/Source/WebKit/android/TimeCounter.h
new file mode 100644
index 0000000..ecede27
--- /dev/null
+++ b/Source/WebKit/android/TimeCounter.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2009, 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 TimeCounter_h
+#define TimeCounter_h
+
+#include "hardware_legacy/qemu_tracing.h"
+
+namespace WebCore {
+
+class KURL;
+
+}
+
+namespace android {
+
+uint32_t getThreadMsec();
+
+#ifdef ANDROID_INSTRUMENT
+
+class TimeCounter {
+public:
+ enum Type {
+ // function base counters
+ CSSParseTimeCounter,
+ JavaScriptTimeCounter,
+ JavaScriptInitTimeCounter,
+ JavaScriptParseTimeCounter,
+ JavaScriptExecuteTimeCounter,
+ CalculateStyleTimeCounter,
+ JavaCallbackTimeCounter,
+ ParsingTimeCounter,
+ LayoutTimeCounter,
+ // file base counters
+ NativeCallbackTimeCounter, // WebCoreFrameBridge.cpp
+ ResourceTimeCounter, // WebCoreResourceLoader.cpp
+ SharedTimerTimeCounter, // JavaBridge.cpp
+ WebViewCoreBuildNavTimeCounter,
+ WebViewCoreRecordTimeCounter,
+ WebViewCoreTimeCounter, // WebViewCore.cpp
+ WebViewUIDrawTimeCounter,
+ TotalTimeCounterCount
+ };
+
+ static void record(enum Type type, const char* functionName);
+ static void recordNoCounter(enum Type type, const char* functionName);
+ static void report(const WebCore::KURL& , int live, int dead, size_t arenaSize);
+ static void reportNow();
+ static void reset();
+ static void start(enum Type type);
+private:
+ static uint32_t sStartWebCoreThreadTime;
+ static uint32_t sEndWebCoreThreadTime;
+ static bool sRecordWebCoreTime;
+ static uint32_t sTotalTimeUsed[TotalTimeCounterCount];
+ static uint32_t sLastTimeUsed[TotalTimeCounterCount];
+ static uint32_t sCounter[TotalTimeCounterCount];
+ static uint32_t sLastCounter[TotalTimeCounterCount];
+ static uint32_t sStartTime[TotalTimeCounterCount];
+ friend class TimeCounterAuto;
+};
+
+class TimeCounterAuto {
+public:
+ TimeCounterAuto(TimeCounter::Type type) :
+ m_type(type), m_startTime(getThreadMsec()) {}
+ ~TimeCounterAuto() {
+ uint32_t time = getThreadMsec();
+ TimeCounter::sEndWebCoreThreadTime = time;
+ TimeCounter::sTotalTimeUsed[m_type] += time - m_startTime;
+ TimeCounter::sCounter[m_type]++;
+ }
+private:
+ TimeCounter::Type m_type;
+ uint32_t m_startTime;
+};
+
+class QemuTracerAuto {
+public:
+ QemuTracerAuto() {
+ if (!reentry_count)
+ qemu_start_tracing();
+ reentry_count++;
+ }
+
+ ~QemuTracerAuto() {
+ reentry_count--;
+ if (!reentry_count)
+ qemu_stop_tracing();
+ }
+private:
+ static int reentry_count;
+};
+#endif // ANDROID_INSTRUMENT
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
index 907dc3c..ab5fcb0 100644
--- a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
@@ -95,7 +95,7 @@ void ChromeClientAndroid::scheduleCompositingLayerSync()
m_needsLayerSync = true;
WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view());
if (webViewCore)
- webViewCore->contentDraw();
+ webViewCore->layersDraw();
}
void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization()
@@ -161,15 +161,8 @@ void ChromeClientAndroid::focus()
}
void ChromeClientAndroid::unfocus() { notImplemented(); }
-bool ChromeClientAndroid::canTakeFocus(FocusDirection direction)
-{
- return android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->chromeCanTakeFocus(direction);
-}
-
-void ChromeClientAndroid::takeFocus(FocusDirection direction)
-{
- android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->chromeTakeFocus(direction);
-}
+bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; }
+void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); }
void ChromeClientAndroid::focusedNodeChanged(Node* node)
{
@@ -188,10 +181,10 @@ Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&,
return frame->page();
#endif
- FloatRect window = windowRect();
+ const WebCoreViewBridge* bridge = frame->view()->platformWidget();
bool dialog = features.dialog || !features.resizable
- || (features.heightSet && features.height < window.height()
- && features.widthSet && features.width < window.width())
+ || (features.heightSet && features.height < bridge->height()
+ && features.widthSet && features.width < bridge->width())
|| (!features.menuBarVisible && !features.statusBarVisible
&& !features.toolBarVisible && !features.locationBarVisible
&& !features.scrollbarsVisible);
@@ -238,7 +231,11 @@ void ChromeClientAndroid::addMessageToConsole(MessageSource, MessageType, Messag
android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->addMessageToConsole(message, lineNumber, sourceID, msgLevel);
}
-void ChromeClientAndroid::formDidBlur(const WebCore::Node* node) { notImplemented(); }
+void ChromeClientAndroid::formDidBlur(const WebCore::Node* node)
+{
+ android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->formDidBlur(node);
+}
+
bool ChromeClientAndroid::canRunBeforeUnloadConfirmPanel() { return true; }
bool ChromeClientAndroid::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) {
String url = frame->document()->documentURI();
@@ -301,7 +298,7 @@ bool ChromeClientAndroid::shouldInterruptJavaScript() {
KeyboardUIMode ChromeClientAndroid::keyboardUIMode()
{
- return KeyboardAccessTabsToLinks;
+ return KeyboardAccessDefault;
}
IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); }
@@ -641,11 +638,6 @@ void ChromeClientAndroid::enterFullscreenForNode(Node* node)
void ChromeClientAndroid::exitFullscreenForNode(Node* node)
{
- FrameView* frameView = m_webFrame->page()->mainFrame()->view();
- android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
- if (core)
- core->exitFullscreenVideo();
- return;
}
#endif
diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h
index 1c898a0..022511a 100644
--- a/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h
+++ b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h
@@ -23,46 +23,31 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-// All source files wishing to include Chromium headers must include this file
-// and must not incude Chromium headers directly.
-
#ifndef ChromiumIncludes_h
#define ChromiumIncludes_h
#include "config.h"
-// Both WebKit and Chromium define LOG. In AOSP, the framework also defines
-// LOG. To avoid conflicts, we undefine LOG before including Chromium code,
-// then define it back to the WebKit macro.
+// Include all external/chromium files in this file so the problems with the LOG
+// and LOG_ASSERT defines can be handled in one place.
+
+// Undefine LOG and LOG_ASSERT before including chrome code, and if they were
+// defined attempt to set the macros to the Android logging macros (which are
+// the only ones that actually log).
+
#ifdef LOG
-#define LOG_WAS_DEFINED
+#define LOG_WAS_DEFINED LOG
#undef LOG
#endif
-// In AOSP, the framework still uses LOG_ASSERT (as well as ALOG_ASSERT), which
-// conflicts with Chromium's LOG_ASSERT. So we undefine LOG_ASSERT to allow the
-// Chromium implementation to be picked up. We also redefine ALOG_ASSERT to the
-// underlying framework implementation without using LOG_ASSERT.
-// TODO: Remove this once LOG_ASSERT is removed from the framework in AOSP.
+#ifdef LOG_ASSERT
+#define LOG_ASSERT_WAS_DEFINED LOG_ASSERT
#undef LOG_ASSERT
-#undef ALOG_ASSERT
-// Copied from log.h.
-#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
-
-// Chromium won't build without NDEBUG set, so we set it for all source files
-// that use Chromium code. This means that if NDEBUG was previously unset, we
-// have to redefine ASSERT() to a no-op, as this is enabled in debug builds.
-// Unfortunately, ASSERT() is defined from config.h, so we can't get in first.
-#ifndef NDEBUG
-#define NDEBUG 1
-#undef ASSERT
-#define ASSERT(assertion) (void(0))
#endif
#include <android/net/android_network_library_impl.h>
#include <android/jni/jni_utils.h>
#include <base/callback.h>
-#include <base/lazy_instance.h>
#include <base/memory/ref_counted.h>
#include <base/message_loop_proxy.h>
#include <base/openssl_util.h>
@@ -115,15 +100,13 @@
#endif
#undef LOG
-// If LOG was defined, restore it to the WebKit macro.
-#ifdef LOG_WAS_DEFINED
-// If LOG was defined, JOIN_LOG_CHANNEL_WITH_PREFIX must be too.
-// Copied from Assertions.h.
-#if LOG_DISABLED
-#define LOG(channel, ...) ((void)0)
-#else
-#define LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__)
+#if defined(LOG_WAS_DEFINED) && defined(LOG_PRI)
+#define LOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
+
+#undef LOG_ASSERT
+#if defined(LOG_ASSERT_WAS_DEFINED) && defined(LOG_FATAL_IF)
+#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
#endif
#endif
diff --git a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
index 042c227..785f0a8 100644
--- a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
@@ -228,22 +228,8 @@ void EditorClientAndroid::checkGrammarOfString(unsigned short const*, int, WTF::
void EditorClientAndroid::checkSpellingOfString(unsigned short const*, int, int*, int*) {}
String EditorClientAndroid::getAutoCorrectSuggestionForMisspelledWord(const String&) { return String(); }
void EditorClientAndroid::textFieldDidEndEditing(Element*) {}
-void EditorClientAndroid::textDidChangeInTextArea(Element* element)
-{
- Frame* frame = m_page->focusController()->focusedOrMainFrame();
- if (!frame || !frame->view())
- return;
- WebViewCore* webViewCore = WebViewCore::getWebViewCore(frame->view());
- webViewCore->updateTextSizeAndScroll(element);
-}
-void EditorClientAndroid::textDidChangeInTextField(Element* element)
-{
- Frame* frame = m_page->focusController()->focusedOrMainFrame();
- if (!frame || !frame->view())
- return;
- WebViewCore* webViewCore = WebViewCore::getWebViewCore(frame->view());
- webViewCore->updateTextSizeAndScroll(element);
-}
+void EditorClientAndroid::textDidChangeInTextArea(Element*) {}
+void EditorClientAndroid::textDidChangeInTextField(Element*) {}
void EditorClientAndroid::textFieldDidBeginEditing(Element*) {}
void EditorClientAndroid::ignoreWordInSpellDocument(String const&) {}
diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
index ac5cd9d..0be31eb 100644
--- a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
@@ -70,6 +70,7 @@
#include "SkRect.h"
#include "TextEncoding.h"
#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
#include "WebHistory.h"
#include "WebIconDatabase.h"
#include "WebFrameView.h"
@@ -78,7 +79,7 @@
#include "autofill/WebAutofill.h"
#include "android_graphics.h"
-#include <androidfw/AssetManager.h>
+#include <utils/AssetManager.h>
#include <wtf/text/CString.h>
#define verifiedOk() // Verified that we don't need to implement this.
@@ -266,11 +267,11 @@ void FrameLoaderClientAndroid::dispatchDidReceiveIcon() {
// There is a bug in webkit where cancelling an icon load is treated as a
// failure. When this is fixed, we can ASSERT again that we have an icon.
if (icon) {
- ALOGV("Received icon (%p) for %s", icon,
+ LOGV("Received icon (%p) for %s", icon,
url.utf8().data());
m_webFrame->didReceiveIcon(icon);
} else {
- ALOGV("Icon data for %s unavailable, registering for notification...",
+ LOGV("Icon data for %s unavailable, registering for notification...",
url.utf8().data());
registerForIconNotification();
}
@@ -422,9 +423,7 @@ void FrameLoaderClientAndroid::dispatchDidFirstLayout() {
// so that about:blank will update the screen.
if (!m_frame->tree()->parent()) {
// Only need to notify Java side for the top frame
- WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view());
- if (core)
- core->didFirstLayout();
+ WebViewCore::getWebViewCore(m_frame->view())->didFirstLayout();
}
}
@@ -947,11 +946,11 @@ void FrameLoaderClientAndroid::transitionToCommittedForNewPage() {
Retain(webViewCore);
// Save the old WebFrameView's bounds and apply them to the new WebFrameView
- RefPtr<WebCore::FrameView> oldFrameView = m_frame->view();
- WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (oldFrameView->platformWidget());
- IntRect bounds;
- if (oldWebFrameView)
- bounds = oldWebFrameView->getBounds();
+ WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (m_frame->view()->platformWidget());
+ IntRect bounds = oldWebFrameView->getBounds();
+ IntRect visBounds = oldWebFrameView->getVisibleBounds();
+ IntRect windowBounds = oldWebFrameView->getWindowBounds();
+ WebCore::FrameView* oldFrameView = oldWebFrameView->view();
const float oldZoomFactor = oldFrameView->frame()->textZoomFactor();
m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(),
oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout());
@@ -959,19 +958,21 @@ void FrameLoaderClientAndroid::transitionToCommittedForNewPage() {
m_frame->setTextZoomFactor(oldZoomFactor);
}
- if (oldWebFrameView) {
- IntRect visBounds = oldWebFrameView->getVisibleBounds();
- IntRect windowBounds = oldWebFrameView->getWindowBounds();
- // Create a new WebFrameView for the new FrameView
- WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore);
- newFrameView->setLocation(bounds.x(), bounds.y());
- newFrameView->setSize(bounds.width(), bounds.height());
- newFrameView->setVisibleSize(visBounds.width(), visBounds.height());
- newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height());
- // newFrameView attaches itself to FrameView which Retains the reference, so
- // call Release for newFrameView
- Release(newFrameView);
- }
+ // Create a new WebFrameView for the new FrameView
+ WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore);
+
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+#else
+ webViewCore->clearContent();
+#endif
+
+ newFrameView->setLocation(bounds.x(), bounds.y());
+ newFrameView->setSize(bounds.width(), bounds.height());
+ newFrameView->setVisibleSize(visBounds.width(), visBounds.height());
+ newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height());
+ // newFrameView attaches itself to FrameView which Retains the reference, so
+ // call Release for newFrameView
+ Release(newFrameView);
// WebFrameView Retains webViewCore, so call Release for webViewCore
Release(webViewCore);
@@ -1007,11 +1008,15 @@ WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL
newFrame->tree()->setName(name);
// Create a new FrameView and WebFrameView for the child frame to draw into.
RefPtr<FrameView> frameView = FrameView::create(newFrame);
+ WebFrameView* webFrameView = new WebFrameView(frameView.get(),
+ WebViewCore::getWebViewCore(parent->view()));
+ // frameView Retains webFrameView, so call Release for webFrameView
+ Release(webFrameView);
// Attach the frameView to the newFrame.
newFrame->setView(frameView);
newFrame->init();
newFrame->selection()->setFocused(true);
- ALOGV("::WebCore:: createSubFrame returning %p", newFrame);
+ LOGV("::WebCore:: createSubFrame returning %p", newFrame);
// The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
if (!pFrame->page())
@@ -1326,8 +1331,8 @@ void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWor
return;
ASSERT(m_frame);
- ALOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n",
- m_frame, m_frame->document()->url().string().ascii().data());
+ LOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n",
+ m_frame, m_frame->loader()->url().string().ascii().data());
m_webFrame->windowObjectCleared(m_frame);
}
diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
index 6eb4745..2464c58 100644
--- a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
+++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
@@ -26,6 +26,7 @@
#ifndef FrameLoaderClientAndroid_h
#define FrameLoaderClientAndroid_h
+#include "CacheBuilder.h"
#include "FrameLoaderClient.h"
#include "ResourceResponse.h"
#include "WebIconDatabase.h"
@@ -199,12 +200,14 @@ namespace android {
virtual void documentElementAvailable();
virtual void didPerformFirstNavigation() const;
- // TODO: Implement
+#if USE(V8)
+ // TODO(benm): Implement
virtual void didCreateScriptContextForFrame() { }
virtual void didDestroyScriptContextForFrame() { }
virtual void didCreateIsolatedScriptContext() { }
- virtual bool allowScriptExtension(const String& extensionName, int extensionGroup) { return false; }
+ virtual bool allowScriptExtension(const String& extensionName, int extensionGroup) { return false; }
+#endif
virtual void registerForIconNotification(bool listen = true);
@@ -215,6 +218,9 @@ namespace android {
// WebIconDatabaseClient api
virtual void didAddIconForPageUrl(const String& pageUrl);
+ // FIXME: this doesn't really go here, but it's better than Frame
+ CacheBuilder& getCacheBuilder() { return m_cacheBuilder; }
+
void enableOnDemandPlugins() { m_onDemandPluginsEnabled = true; }
void dispatchDidChangeIcons();
@@ -223,6 +229,7 @@ namespace android {
virtual void didSaveToPageCache() { }
virtual void didRestoreFromPageCache() { }
private:
+ CacheBuilder m_cacheBuilder;
Frame* m_frame;
WebFrame* m_webFrame;
PluginManualLoader* m_manualLoader;
@@ -258,6 +265,7 @@ namespace android {
ErrorFileNotFound = -14,
ErrorTooManyRequests = -15
};
+ friend class CacheBuilder;
};
}
diff --git a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
index 1607b0e..339e91b 100644
--- a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
@@ -29,6 +29,10 @@
#if ENABLE(VIDEO)
#include "BaseLayerAndroid.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
#include "GraphicsContext.h"
#include "SkiaUtils.h"
#include "TilesManager.h"
@@ -164,19 +168,6 @@ void MediaPlayerPrivate::onEnded()
m_networkState = MediaPlayer::Idle;
}
-void MediaPlayerPrivate::onRequestPlay()
-{
- play();
-}
-
-void MediaPlayerPrivate::onRestoreState()
-{
- if (!m_paused) {
- //Kick off a JNI call to start the video.
- play();
- }
-}
-
void MediaPlayerPrivate::onPaused()
{
m_paused = true;
@@ -220,6 +211,11 @@ public:
if (!env || !m_url.length() || !m_glue->m_javaProxy)
return;
+ // We only play video fullscreen on Android, so stop sites playing fullscreen video in the onload handler.
+ Frame* frame = m_player->frameView()->frame();
+ if (frame && !frame->loader()->documentLoader()->wasOnloadHandled())
+ return;
+
m_paused = false;
m_player->playbackStateChanged();
@@ -260,9 +256,7 @@ public:
if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
return;
- SkCanvas* canvas = ctxt->platformContext()->getCanvas();
- if (!canvas)
- 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
@@ -299,8 +293,7 @@ public:
m_player->durationChanged();
m_player->sizeChanged();
TilesManager::instance()->videoLayerManager()->updateVideoLayerSize(
- m_player->platformLayer()->uniqueId(), width * height,
- width / (float)height);
+ m_player->platformLayer()->uniqueId(), width*height);
}
virtual bool hasAudio() const { return false; } // do not display the audio UI
@@ -541,14 +534,6 @@ static void OnEnded(JNIEnv* env, jobject obj, int pointer)
}
}
-static void OnRequestPlay(JNIEnv* env, jobject obj, int pointer)
-{
- if (pointer) {
- WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
- player->onRequestPlay();
- }
-}
-
static void OnPaused(JNIEnv* env, jobject obj, int pointer)
{
if (pointer) {
@@ -585,15 +570,6 @@ static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer)
}
}
-static void OnRestoreState(JNIEnv* env, jobject obj, int pointer)
-{
- if (pointer) {
- WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
- player->onRestoreState();
- }
-}
-
-
// This is called on the UI thread only.
// The video layers are composited on the webkit thread and then copied over
// to the UI thread with the same ID. For rendering, we are only using the
@@ -617,9 +593,14 @@ static bool SendSurfaceTexture(JNIEnv* env, jobject obj, jobject surfTex,
BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(baseLayer);
if (!layerImpl)
return false;
+ if (!layerImpl->countChildren())
+ return false;
+ LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(layerImpl->getChild(0));
+ if (!compositedRoot)
+ return false;
VideoLayerAndroid* videoLayer =
- static_cast<VideoLayerAndroid*>(layerImpl->findById(videoLayerId));
+ static_cast<VideoLayerAndroid*>(compositedRoot->findById(videoLayerId));
if (!videoLayer)
return false;
@@ -651,8 +632,6 @@ static JNINativeMethod g_MediaPlayerMethods[] = {
(void*) OnPaused },
{ "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
(void*) OnPosterFetched },
- { "nativeOnRestoreState", "(I)V",
- (void*) OnRestoreState },
{ "nativeSendSurfaceTexture", "(Landroid/graphics/SurfaceTexture;IIII)Z",
(void*) SendSurfaceTexture },
{ "nativeOnTimeupdate", "(II)V",
@@ -666,8 +645,6 @@ static JNINativeMethod g_MediaAudioPlayerMethods[] = {
(void*) OnEnded },
{ "nativeOnPrepared", "(IIII)V",
(void*) OnPrepared },
- { "nativeOnRequestPlay", "(I)V",
- (void*) OnRequestPlay },
{ "nativeOnTimeupdate", "(II)V",
(void*) OnTimeupdate },
};
diff --git a/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp
index 592fad3..32cdebf 100644
--- a/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp
+++ b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp
@@ -29,7 +29,9 @@
#include <malloc.h>
#include <wtf/CurrentTime.h>
+#if USE(V8)
#include <v8.h>
+#endif // USE(V8)
using namespace WTF;
@@ -57,10 +59,12 @@ int MemoryUsageCache::getCachedMemoryUsage(bool forceFresh)
struct mallinfo minfo = mallinfo();
m_cachedMemoryUsage = (minfo.hblkhd + minfo.arena) >> 20;
+#if USE(V8)
v8::HeapStatistics stat;
v8::V8::GetHeapStatistics(&stat);
unsigned v8Usage = stat.total_heap_size() >> 20;
m_cachedMemoryUsage += v8Usage;
+#endif // USE(V8)
m_cacheTime = currentTimeMS();
return m_cachedMemoryUsage;
diff --git a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp
index 9b8aac8..27fe208 100644
--- a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp
+++ b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp
@@ -35,22 +35,18 @@
#include "KeyGeneratorClient.h"
#include "MemoryUsage.h"
#include "PluginView.h"
-#include "RenderLayer.h"
-#include "RenderView.h"
#include "Settings.h"
#include "WebCookieJar.h"
#include "WebRequestContext.h"
#include "WebViewCore.h"
#include "npruntime.h"
-#include <gui/SurfaceComposerClient.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>
#include <ui/PixelFormat.h>
#include <wtf/android/AndroidThreading.h>
#include <wtf/MainThread.h>
-#include <algorithm>
-
using namespace android;
namespace WebCore {
@@ -75,40 +71,62 @@ String PlatformBridge::getSignedPublicKeyAndChallengeString(unsigned index, cons
void PlatformBridge::setCookies(const Document* document, const KURL& url, const String& value)
{
+#if USE(CHROME_NETWORK_STACK)
std::string cookieValue(value.utf8().data());
GURL cookieGurl(url.string().utf8().data());
bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled();
- WebCookieJar* cookieJar = WebCookieJar::get(isPrivateBrowsing);
- if (cookieJar->allowCookies())
- cookieJar->cookieStore()->SetCookie(cookieGurl, cookieValue);
+ WebCookieJar::get(isPrivateBrowsing)->cookieStore()->SetCookie(cookieGurl, cookieValue);
+#else
+ CookieClient* client = JavaSharedClient::GetCookieClient();
+ if (!client)
+ return;
+
+ client->setCookies(url, value);
+#endif
}
String PlatformBridge::cookies(const Document* document, const KURL& url)
{
+#if USE(CHROME_NETWORK_STACK)
GURL cookieGurl(url.string().utf8().data());
bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled();
- WebCookieJar* cookieJar = WebCookieJar::get(isPrivateBrowsing);
- String cookieString;
- if (cookieJar->allowCookies()) {
- std::string cookies = cookieJar->cookieStore()->GetCookies(cookieGurl);
- cookieString = cookies.c_str();
- }
+ std::string cookies = WebCookieJar::get(isPrivateBrowsing)->cookieStore()->GetCookies(cookieGurl);
+ String cookieString(cookies.c_str());
return cookieString;
+#else
+ CookieClient* client = JavaSharedClient::GetCookieClient();
+ if (!client)
+ return String();
+
+ return client->cookies(url);
+#endif
}
bool PlatformBridge::cookiesEnabled(const Document* document)
{
+#if USE(CHROME_NETWORK_STACK)
bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled();
return WebCookieJar::get(isPrivateBrowsing)->allowCookies();
+#else
+ CookieClient* client = JavaSharedClient::GetCookieClient();
+ if (!client)
+ return false;
+
+ return client->cookiesEnabled();
+#endif
}
NPObject* PlatformBridge::pluginScriptableObject(Widget* widget)
{
+#if USE(V8)
if (!widget->isPluginView())
return 0;
PluginView* pluginView = static_cast<PluginView*>(widget);
return pluginView->getNPObject();
+#else
+ return 0;
+#endif
}
bool PlatformBridge::isWebViewPaused(const WebCore::FrameView* frameView)
@@ -157,11 +175,15 @@ int PlatformBridge::screenHeightInDocCoord(const WebCore::FrameView* frameView)
String PlatformBridge::computeDefaultLanguage()
{
+#if USE(CHROME_NETWORK_STACK)
String acceptLanguages = WebRequestContext::acceptLanguage();
size_t length = acceptLanguages.find(',');
if (length == std::string::npos)
length = acceptLanguages.length();
return acceptLanguages.substring(0, length);
+#else
+ return "en";
+#endif
}
void PlatformBridge::updateViewport(FrameView* frameView)
@@ -177,15 +199,10 @@ void PlatformBridge::updateTextfield(FrameView* frameView, Node* nodePtr, bool c
}
void PlatformBridge::setScrollPosition(ScrollView* scrollView, int x, int y) {
- FrameView* frameView = scrollView->frameView();
- if (!frameView) return;
// Check to make sure the view is the main FrameView.
android::WebViewCore *webViewCore = android::WebViewCore::getWebViewCore(scrollView);
- if (webViewCore->mainFrame()->view() == scrollView) {
- x = std::max(0, std::min(frameView->contentsWidth(), x));
- y = std::max(0, std::min(frameView->contentsHeight(), y));
+ if (webViewCore->mainFrame()->view() == scrollView)
webViewCore->scrollTo(x, y);
- }
}
int PlatformBridge::lowMemoryUsageMB()
diff --git a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp
index 92c39b8..7f54810 100644
--- a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp
@@ -29,6 +29,7 @@
#include "Frame.h"
#include "FrameLoaderClientAndroid.h"
#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
#include "WebUrlLoader.h"
#include "WebViewCore.h"
@@ -41,13 +42,18 @@ PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(
{
// Called on main thread
FrameLoaderClientAndroid* clientAndroid = static_cast<FrameLoaderClientAndroid*>(client);
+#if USE(CHROME_NETWORK_STACK)
WebViewCore* webViewCore = WebViewCore::getWebViewCore(clientAndroid->getFrame()->view());
bool isMainFrame = !(clientAndroid->getFrame()->tree() && clientAndroid->getFrame()->tree()->parent());
return WebUrlLoader::start(client, handle, request, isMainResource, isMainFrame, isSync, webViewCore->webRequestContext());
+#else
+ return clientAndroid->webFrame()->startLoadingResource(handle, request, isMainResource, isSync);
+#endif
}
bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t identifier)
{
+#if USE(CHROME_NETWORK_STACK)
// This method is used to determine if a POST request can be repeated from
// cache, but you cannot really know until you actually try to read from the
// cache. Even if we checked now, something else could come along and wipe
@@ -57,6 +63,9 @@ bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t
// reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we
// fix-up the cache policy of the request to force a load from the cache.
return true;
+#else
+ return WebCoreResourceLoader::willLoadFromCache(url, identifier);
+#endif
}
}
diff --git a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp
index d846daf..77e3c32 100644
--- a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp
+++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp
@@ -30,7 +30,6 @@
#include "UrlInterceptResponse.h"
#include "WebCoreJni.h"
-#include <ScopedLocalRef.h>
#include <utils/Log.h>
namespace android {
@@ -39,14 +38,15 @@ class JavaInputStreamWrapper {
public:
JavaInputStreamWrapper(JNIEnv* env, jobject inputStream)
: m_inputStream(env->NewGlobalRef(inputStream))
- , m_buffer(NULL) {
- LOG_ALWAYS_FATAL_IF(!m_inputStream);
- ScopedLocalRef<jclass> inputStreamClass(env, env->FindClass("java/io/InputStream"));
- LOG_ALWAYS_FATAL_IF(!inputStreamClass.get());
- m_read = env->GetMethodID(inputStreamClass.get(), "read", "([B)I");
+ , m_buffer(0) {
+ LOG_ALWAYS_FATAL_IF(!inputStream);
+ jclass inputStreamClass = env->FindClass("java/io/InputStream");
+ LOG_ALWAYS_FATAL_IF(!inputStreamClass);
+ m_read = env->GetMethodID(inputStreamClass, "read", "([B)I");
LOG_ALWAYS_FATAL_IF(!m_read);
- m_close = env->GetMethodID(inputStreamClass.get(), "close", "()V");
+ m_close = env->GetMethodID(inputStreamClass, "close", "()V");
LOG_ALWAYS_FATAL_IF(!m_close);
+ env->DeleteLocalRef(inputStreamClass);
}
~JavaInputStreamWrapper() {
@@ -63,10 +63,10 @@ public:
JNIEnv* env = JSC::Bindings::getJNIEnv();
// Initialize our read buffer to the capacity of out.
if (!m_buffer) {
- ScopedLocalRef<jbyteArray> buffer_local(env, env->NewByteArray(out->capacity()));
- m_buffer = static_cast<jbyteArray>(env->NewGlobalRef(buffer_local.get()));
+ m_buffer = env->NewByteArray(out->capacity());
+ m_buffer = (jbyteArray) env->NewGlobalRef(m_buffer);
}
- int size = env->CallIntMethod(m_inputStream, m_read, m_buffer);
+ int size = (int) env->CallIntMethod(m_inputStream, m_read, m_buffer);
if (checkException(env) || size < 0)
return;
// Copy from m_buffer to out.
@@ -82,32 +82,40 @@ private:
};
UrlInterceptResponse::UrlInterceptResponse(JNIEnv* env, jobject response) {
- ScopedLocalRef<jclass> javaResponse(env, env->FindClass("android/webkit/WebResourceResponse"));
- LOG_ALWAYS_FATAL_IF(!javaResponse.get());
- jfieldID mimeType = env->GetFieldID(javaResponse.get(), "mMimeType", "Ljava/lang/String;");
+ jclass javaResponse = env->FindClass("android/webkit/WebResourceResponse");
+ LOG_ALWAYS_FATAL_IF(!javaResponse);
+ jfieldID mimeType = env->GetFieldID(javaResponse, "mMimeType",
+ "Ljava/lang/String;");
LOG_ALWAYS_FATAL_IF(!mimeType);
- jfieldID encoding = env->GetFieldID(javaResponse.get(), "mEncoding", "Ljava/lang/String;");
+ jfieldID encoding = env->GetFieldID(javaResponse, "mEncoding",
+ "Ljava/lang/String;");
LOG_ALWAYS_FATAL_IF(!encoding);
- jfieldID inputStream = env->GetFieldID(javaResponse.get(), "mInputStream", "Ljava/io/InputStream;");
+ jfieldID inputStream = env->GetFieldID(javaResponse, "mInputStream",
+ "Ljava/io/InputStream;");
LOG_ALWAYS_FATAL_IF(!inputStream);
- ScopedLocalRef<jobject> stream(env, env->GetObjectField(response, inputStream));
- if (stream.get())
- m_inputStream.set(new JavaInputStreamWrapper(env, stream.get()));
+ jobject stream = env->GetObjectField(response, inputStream);
+ if (stream)
+ m_inputStream.set(new JavaInputStreamWrapper(env, stream));
- ScopedLocalRef<jstring> mimeStr(env, static_cast<jstring>(env->GetObjectField(response, mimeType)));
- ScopedLocalRef<jstring> encodingStr(env, static_cast<jstring>(env->GetObjectField(response, encoding)));
+ jstring mimeStr = (jstring) env->GetObjectField(response, mimeType);
+ jstring encodingStr = (jstring) env->GetObjectField(response, encoding);
- if (mimeStr.get()) {
- const char* s = env->GetStringUTFChars(mimeStr.get(), NULL);
- m_mimeType.assign(s, env->GetStringUTFLength(mimeStr.get()));
- env->ReleaseStringUTFChars(mimeStr.get(), s);
+ if (mimeStr) {
+ const char* s = env->GetStringUTFChars(mimeStr, NULL);
+ m_mimeType.assign(s, env->GetStringUTFLength(mimeStr));
+ env->ReleaseStringUTFChars(mimeStr, s);
}
- if (encodingStr.get()) {
- const char* s = env->GetStringUTFChars(encodingStr.get(), NULL);
- m_encoding.assign(s, env->GetStringUTFLength(encodingStr.get()));
- env->ReleaseStringUTFChars(encodingStr.get(), s);
+ if (encodingStr) {
+ const char* s = env->GetStringUTFChars(encodingStr, NULL);
+ m_encoding.assign(s, env->GetStringUTFLength(encodingStr));
+ env->ReleaseStringUTFChars(encodingStr, s);
}
+
+ env->DeleteLocalRef(javaResponse);
+ env->DeleteLocalRef(stream);
+ env->DeleteLocalRef(mimeStr);
+ env->DeleteLocalRef(encodingStr);
}
UrlInterceptResponse::~UrlInterceptResponse() {
diff --git a/Source/WebKit/android/WebCoreSupport/V8Counters.cpp b/Source/WebKit/android/WebCoreSupport/V8Counters.cpp
new file mode 100644
index 0000000..d164f9a
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/V8Counters.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+
+#ifdef ANDROID_INSTRUMENT
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "V8Counters.h"
+
+#include "NotImplemented.h"
+#include <utils/Log.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringHash.h>
+
+#if USE(V8)
+
+namespace WebCore {
+
+V8Counters::Counter::Counter(bool isHistogram)
+ : m_count(0), m_sampleTotal(0), m_isHistogram(isHistogram) { }
+
+void V8Counters::Counter::addSample(int sample)
+{
+ m_count++;
+ m_sampleTotal += sample;
+}
+
+HashMap<String, V8Counters::Counter*> V8Counters::m_counters;
+
+// static
+int* V8Counters::counterForName(const char* name)
+{
+ Counter* counter = m_counters.get(name);
+ if (!counter) {
+ counter = new Counter(false);
+ m_counters.add(name, counter);
+ }
+ return *counter;
+}
+
+// static
+void* V8Counters::createHistogram(const char* name, int min, int max,
+ size_t buckets)
+{
+ Counter* counter = new Counter(true);
+ m_counters.add(name, counter);
+ return counter;
+}
+
+// static
+void V8Counters::addHistogramSample(void* histogram, int sample)
+{
+ Counter* counter = reinterpret_cast<Counter*>(histogram);
+ counter->addSample(sample);
+}
+
+// static
+void V8Counters::initCounters()
+{
+ static bool isInitialized = false;
+ if (!isInitialized) {
+ v8::V8::SetCounterFunction(counterForName);
+ v8::V8::SetCreateHistogramFunction(createHistogram);
+ v8::V8::SetAddHistogramSampleFunction(addHistogramSample);
+ isInitialized = true;
+ }
+}
+
+// static
+void V8Counters::dumpCounters()
+{
+ LOGD("+----------------------------------------+-------------+\n");
+ LOGD("| Name | Value |\n");
+ LOGD("+----------------------------------------+-------------+\n");
+ typedef HashMap<String, V8Counters::Counter*>::iterator CounterIterator;
+ for (CounterIterator iter = m_counters.begin(); iter != m_counters.end(); ++iter) {
+ Counter* counter = iter->second;
+ if (counter->isHistogram()) {
+ LOGD("| c:%-36s | %11i |\n", iter->first.latin1().data(), counter->count());
+ LOGD("| t:%-36s | %11i |\n", iter->first.latin1().data(), counter->sampleTotal());
+ } else {
+ LOGD("| %-38s | %11i |\n", iter->first.latin1().data(), counter->count());
+ }
+ }
+ LOGD("+----------------------------------------+-------------+\n");
+}
+
+}
+
+#endif // ANDROID_INSTRUMENT
+
+#endif // USE(V8)
diff --git a/Source/WebKit/android/WebCoreSupport/V8Counters.h b/Source/WebKit/android/WebCoreSupport/V8Counters.h
new file mode 100644
index 0000000..499b856
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/V8Counters.h
@@ -0,0 +1,77 @@
+/*
+ * 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 V8Counters_h
+#define V8Counters_h
+
+#if USE(V8)
+
+#ifdef ANDROID_INSTRUMENT
+
+#include <PlatformString.h>
+#include <v8.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class V8Counters {
+public:
+ // Counter callbacks, see v8.h
+ static int* counterForName(const char* name);
+
+ static void* createHistogram(const char* name,
+ int min,
+ int max,
+ size_t buckets);
+
+ static void addHistogramSample(void* histogram, int sample);
+
+ static void initCounters();
+ static void dumpCounters();
+private:
+ class Counter {
+ public:
+ Counter(bool isHistogram);
+
+ int count() { return m_count; }
+ int sampleTotal() { return m_sampleTotal; }
+ bool isHistogram() { return m_isHistogram; }
+ void addSample(int32_t sample);
+
+ operator int*() { return &m_count; }
+ private:
+ int m_count;
+ int m_sampleTotal;
+ bool m_isHistogram;
+ };
+
+ static HashMap<String, Counter*> m_counters;
+};
+
+}
+
+#endif // ANDROID_INSTRUMENT
+#endif // USE(V8)
+#endif // V8Counters_h
diff --git a/Source/WebKit/android/WebCoreSupport/WebCache.cpp b/Source/WebKit/android/WebCoreSupport/WebCache.cpp
index 82e3b27..9b505ee 100644
--- a/Source/WebKit/android/WebCoreSupport/WebCache.cpp
+++ b/Source/WebKit/android/WebCoreSupport/WebCache.cpp
@@ -30,7 +30,7 @@
#include "WebCoreJni.h"
#include "WebRequestContext.h"
#include "WebUrlLoaderClient.h"
-#include "net/http/http_network_session.h"
+
#include <wtf/text/CString.h>
using namespace WTF;
@@ -131,21 +131,6 @@ void WebCache::clear()
thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::clearImpl));
}
-void WebCache::certTrustChanged()
-{
- base::Thread* thread = WebUrlLoaderClient::ioThread();
- if (thread)
- thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::certTrustChangedImpl));
-}
-
-void WebCache::certTrustChangedImpl()
-{
- net::HttpNetworkSession* session = m_cache->GetSession();
- if (session)
- session->cert_verifier()->ClearCache();
- m_cache->CloseAllConnections();
-}
-
void WebCache::closeIdleConnections()
{
base::Thread* thread = WebUrlLoaderClient::ioThread();
diff --git a/Source/WebKit/android/WebCoreSupport/WebCache.h b/Source/WebKit/android/WebCoreSupport/WebCache.h
index ad57d88..c3b623d 100644
--- a/Source/WebKit/android/WebCoreSupport/WebCache.h
+++ b/Source/WebKit/android/WebCoreSupport/WebCache.h
@@ -48,7 +48,7 @@ public:
net::HttpCache* cache() { return m_cache.get(); }
net::ProxyConfigServiceAndroid* proxy() { return m_proxyConfigService; }
void closeIdleConnections();
- void certTrustChanged();
+
private:
WebCache(bool isPrivateBrowsing);
@@ -60,7 +60,6 @@ private:
// For closeIdleConnections
void closeIdleImpl();
- void certTrustChangedImpl();
// For getEntry()
void getEntryImpl();
diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
index 0af3cc2..1dc4637 100644
--- a/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
+++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
@@ -98,7 +98,7 @@ static std::string databaseDirectory(bool isPrivateBrowsing)
return databaseFilePath;
}
-static scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing)
+scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing)
{
static scoped_refptr<WebCookieJar> regularInstance;
static scoped_refptr<WebCookieJar> privateInstance;
@@ -127,16 +127,10 @@ void WebCookieJar::cleanup(bool isPrivateBrowsing)
}
WebCookieJar::WebCookieJar(const std::string& databaseFilePath)
- : m_cookieStoreInitialized(false)
- , m_databaseFilePath(databaseFilePath)
- , m_allowCookies(true) {}
-
-void WebCookieJar::initCookieStore() {
- MutexLocker lock(m_cookieStoreInitializeMutex);
- if (m_cookieStoreInitialized)
- return;
+ : m_allowCookies(true)
+{
// Setup the permissions for the file
- const char* cDatabasePath = m_databaseFilePath.c_str();
+ const char* cDatabasePath = databaseFilePath.c_str();
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if (access(cDatabasePath, F_OK) == 0)
chmod(cDatabasePath, mode);
@@ -146,10 +140,9 @@ void WebCookieJar::initCookieStore() {
close(fd);
}
- FilePath cookiePath(cDatabasePath);
+ FilePath cookiePath(databaseFilePath.c_str());
m_cookieDb = new SQLitePersistentCookieStore(cookiePath);
m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0);
- m_cookieStoreInitialized = true;
}
bool WebCookieJar::allowCookies()
@@ -164,6 +157,13 @@ void WebCookieJar::setAllowCookies(bool allow)
m_allowCookies = allow;
}
+int WebCookieJar::getNumCookiesInDatabase()
+{
+ if (!m_cookieStore)
+ return 0;
+ return m_cookieStore->GetCookieMonster()->GetAllCookies().size();
+}
+
// From CookiePolicy in chromium
int WebCookieJar::CanGetCookies(const GURL&, const GURL&) const
{
@@ -178,17 +178,6 @@ int WebCookieJar::CanSetCookie(const GURL&, const GURL&, const std::string&) con
return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED;
}
-net::CookieStore* WebCookieJar::cookieStore()
-{
- initCookieStore();
- return m_cookieStore.get();
-}
-
-int WebCookieJar::getNumCookiesInDatabase()
-{
- return cookieStore()->GetCookieMonster()->GetAllCookies().size();
-}
-
class FlushSemaphore : public base::RefCounted<FlushSemaphore>
{
public:
diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.h b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h
index 7ade9d0..b6490af 100644
--- a/Source/WebKit/android/WebCoreSupport/WebCookieJar.h
+++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h
@@ -55,10 +55,9 @@ public:
static bool acceptFileSchemeCookies();
static void setAcceptFileSchemeCookies(bool);
- // TODO
// Instead of this it would probably be better to add the cookie methods
// here so the rest of WebKit doesn't have to know about Chromium classes
- net::CookieStore* cookieStore();
+ net::CookieStore* cookieStore() { return m_cookieStore.get(); }
net::CookiePolicy* cookiePolicy() { return this; }
// Get the number of cookies that have actually been saved to flash.
@@ -67,13 +66,7 @@ public:
private:
WebCookieJar(const std::string& databaseFilePath);
- void initCookieStore();
-private:
- bool m_cookieStoreInitialized;
- WTF::Mutex m_cookieStoreInitializeMutex;
-
- const std::string m_databaseFilePath;
scoped_refptr<SQLitePersistentCookieStore> m_cookieDb;
scoped_refptr<net::CookieStore> m_cookieStore;
bool m_allowCookies;
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.cpp b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp
index 9d16378..90b0939 100644
--- a/Source/WebKit/android/WebCoreSupport/WebRequest.cpp
+++ b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp
@@ -30,7 +30,6 @@
#include "MainThread.h"
#include "UrlInterceptResponse.h"
#include "WebCoreFrameBridge.h"
-#include "WebCoreJni.h"
#include "WebRequestContext.h"
#include "WebResourceRequest.h"
#include "WebUrlLoaderClient.h"
@@ -39,7 +38,7 @@
#include <cutils/log.h>
#include <openssl/x509.h>
#include <string>
-#include <androidfw/AssetManager.h>
+#include <utils/AssetManager.h>
extern android::AssetManager* globalAssetManager();
@@ -59,28 +58,12 @@ while (0)
namespace android {
namespace {
-const int kInitialReadBufSize = 32768;
-const char* kXRequestedWithHeader = "X-Requested-With";
-
-struct RequestPackageName {
- std::string value;
- RequestPackageName();
-};
-
-RequestPackageName::RequestPackageName() {
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
- jmethodID method = env->GetStaticMethodID(bridgeClass, "getPackageName", "()Ljava/lang/String;");
- value = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
- env->DeleteLocalRef(bridgeClass);
-}
-
-base::LazyInstance<RequestPackageName> s_packageName(base::LINKER_INITIALIZED);
-
+ const int kInitialReadBufSize = 32768;
}
WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest)
: m_urlLoader(loader)
+ , m_androidUrl(false)
, m_url(webResourceRequest.url())
, m_userAgent(webResourceRequest.userAgent())
, m_loadState(Created)
@@ -96,7 +79,6 @@ WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& web
m_request = new net::URLRequest(gurl, this);
m_request->SetExtraRequestHeaders(webResourceRequest.requestHeaders());
- m_request->SetExtraRequestHeaderByName(kXRequestedWithHeader, s_packageName.Get().value, false);
m_request->set_referrer(webResourceRequest.referrer());
m_request->set_method(webResourceRequest.method());
m_request->set_load_flags(webResourceRequest.loadFlags());
@@ -107,6 +89,7 @@ WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& web
WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest, UrlInterceptResponse* intercept)
: m_urlLoader(loader)
, m_interceptResponse(intercept)
+ , m_androidUrl(true)
, m_url(webResourceRequest.url())
, m_userAgent(webResourceRequest.userAgent())
, m_loadState(Created)
@@ -291,9 +274,15 @@ void WebRequest::handleInterceptedURL()
// Get the MIME type from the URL. "text/html" is a last resort, hopefully overridden.
std::string mimeType("text/html");
if (mime == "") {
- // Get the MIME type from the file extension, if any.
- FilePath path(m_url);
- net::GetMimeTypeFromFile(path, &mimeType);
+ // Gmail appends the MIME to the end of the URL, with a ? separator.
+ size_t mimeTypeIndex = m_url.find_last_of('?');
+ if (mimeTypeIndex != std::string::npos) {
+ mimeType.assign(m_url.begin() + mimeTypeIndex + 1, m_url.end());
+ } else {
+ // Get the MIME type from the file extension, if any.
+ FilePath path(m_url);
+ net::GetMimeTypeFromFile(path, &mimeType);
+ }
} else {
// Set from the intercept response.
mimeType = mime;
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.h b/Source/WebKit/android/WebCoreSupport/WebRequest.h
index d9054e9..285e577 100644
--- a/Source/WebKit/android/WebCoreSupport/WebRequest.h
+++ b/Source/WebKit/android/WebCoreSupport/WebRequest.h
@@ -107,6 +107,7 @@ private:
OwnPtr<net::URLRequest> m_request;
scoped_refptr<net::IOBuffer> m_networkBuffer;
scoped_ptr<UrlInterceptResponse> m_interceptResponse;
+ bool m_androidUrl;
std::string m_url;
std::string m_userAgent;
LoadState m_loadState;
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
index 5df0ed2..43037ab 100644
--- a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
+++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
@@ -37,7 +37,6 @@
#include "WebRequest.h"
#include "WebResourceRequest.h"
-#include <utils/Log.h>
#include <wtf/text/CString.h>
using base::Lock;
@@ -157,11 +156,11 @@ WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHand
break;
#if ENABLE(BLOB)
case FormDataElement::encodedBlob:
- ALOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
+ LOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
break;
#endif // ENABLE(BLOB)
default:
- ALOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
+ LOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
break;
}
}
@@ -200,7 +199,7 @@ bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync,
syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime));
if (m_queue.empty()) {
- ALOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s",
+ LOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s",
kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str());
num_timeout++;
if (num_timeout >= kMaxNumTimeout) {
@@ -254,7 +253,7 @@ void WebUrlLoaderClient::downloadFile()
if (!m_isCertMimeType)
cancel();
} else {
- ALOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str());
+ LOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str());
// TODO: Turn off asserts crashing before release
// http://b/issue?id=2951985
CRASH();
diff --git a/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp
index 1857e9c..260c76e 100644
--- a/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp
+++ b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp
@@ -79,7 +79,6 @@ WebViewClientError ToWebViewClientError(net::Error error) {
case ERR_CONNECTION_ABORTED:
case ERR_CONNECTION_FAILED:
case ERR_SOCKET_NOT_CONNECTED:
- case ERR_CACHE_MISS:
return ERROR_CONNECT;
case ERR_ADDRESS_INVALID:
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
index 2969252..e837244 100644
--- a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
+++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
@@ -562,7 +562,7 @@ void FormManager::ExtractForms(Frame* frame) {
ResetFrame(frame);
- WTF::RefPtr<HTMLCollection> web_forms = frame->document()->forms();
+ WTF::PassRefPtr<HTMLCollection> web_forms = frame->document()->forms();
for (size_t i = 0; i < web_forms->length(); ++i) {
// Owned by |form_elements|.
diff --git a/Source/WebKit/android/benchmark/Android.mk b/Source/WebKit/android/benchmark/Android.mk
new file mode 100644
index 0000000..5b189e1
--- /dev/null
+++ b/Source/WebKit/android/benchmark/Android.mk
@@ -0,0 +1,41 @@
+##
+##
+## Copyright 2009, 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 APPLE COMPUTER, INC. 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.
+##
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.cpp
+
+# Pull the webkit definitions from the base webkit makefile.
+LOCAL_SHARED_LIBRARIES := libwebcore $(WEBKIT_SHARED_LIBRARIES)
+LOCAL_LDLIBS := $(WEBKIT_LDLIBS)
+
+LOCAL_MODULE := webcore_test
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/Source/WebKit/android/benchmark/Intercept.cpp b/Source/WebKit/android/benchmark/Intercept.cpp
new file mode 100644
index 0000000..deffac2
--- /dev/null
+++ b/Source/WebKit/android/benchmark/Intercept.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2009, 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 APPLE COMPUTER, INC. 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_test"
+#include "config.h"
+
+#include "Base64.h"
+#include "HTTPParsers.h"
+#include "Intercept.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "TextEncoding.h"
+
+#include <utils/Log.h>
+#include <wtf/HashMap.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringHash.h>
+
+PassRefPtr<WebCore::ResourceLoaderAndroid> MyResourceLoader::create(
+ ResourceHandle* handle, String url)
+{
+ return adoptRef<WebCore::ResourceLoaderAndroid>(
+ new MyResourceLoader(handle, url));
+}
+
+void MyResourceLoader::handleRequest()
+{
+ if (protocolIs(m_url, "data"))
+ loadData(m_url.substring(5)); // 5 for data:
+ else if (protocolIs(m_url, "file"))
+ loadFile(m_url.substring(7)); // 7 for file://
+}
+
+void MyResourceLoader::loadData(const String& data)
+{
+ LOGD("Loading data (%s) ...", data.latin1().data());
+ ResourceHandleClient* client = m_handle->client();
+ int index = data.find(',');
+ if (index == -1) {
+ client->cannotShowURL(m_handle);
+ return;
+ }
+
+ String mediaType = data.substring(0, index);
+ String base64 = data.substring(index + 1);
+
+ bool decode = mediaType.endsWith(";base64", false);
+ if (decode)
+ mediaType = mediaType.left(mediaType.length() - 7); // 7 for base64;
+
+ if (mediaType.isEmpty())
+ mediaType = "text/plain;charset=US-ASCII";
+
+ String mimeType = extractMIMETypeFromMediaType(mediaType);
+ String charset = extractCharsetFromMediaType(mediaType);
+
+ ResourceResponse response;
+ response.setMimeType(mimeType);
+
+ if (decode) {
+ base64 = decodeURLEscapeSequences(base64);
+ response.setTextEncodingName(charset);
+ client->didReceiveResponse(m_handle, response);
+
+ // FIXME: This is annoying. WebCore's Base64 decoder chokes on spaces.
+ // That is correct with strict decoding but html authors (particularly
+ // the acid3 authors) put spaces in the data which should be ignored.
+ // Remove them here before sending to the decoder.
+ Vector<char> in;
+ CString str = base64.latin1();
+ const char* chars = str.data();
+ unsigned i = 0;
+ while (i < str.length()) {
+ char c = chars[i];
+ // Don't send spaces or control characters.
+ if (c != ' ' && c != '\n' && c != '\t' && c != '\b'
+ && c != '\f' && c != '\r')
+ in.append(chars[i]);
+ i++;
+ }
+ Vector<char> out;
+ if (base64Decode(in, out) && out.size() > 0)
+ client->didReceiveData(m_handle, out.data(), out.size(), 0);
+ } else {
+ base64 = decodeURLEscapeSequences(base64, TextEncoding(charset));
+ response.setTextEncodingName("UTF-16");
+ client->didReceiveResponse(m_handle, response);
+ if (base64.length() > 0)
+ client->didReceiveData(m_handle, (const char*)base64.characters(),
+ base64.length() * sizeof(UChar), 0);
+ }
+ client->didFinishLoading(m_handle, 0);
+}
+static String mimeTypeForExtension(const String& file)
+{
+ static HashMap<String, String, CaseFoldingHash> extensionToMime;
+ if (extensionToMime.isEmpty()) {
+ extensionToMime.set("txt", "text/plain");
+ extensionToMime.set("html", "text/html");
+ extensionToMime.set("htm", "text/html");
+ extensionToMime.set("png", "image/png");
+ extensionToMime.set("jpeg", "image/jpeg");
+ extensionToMime.set("jpg", "image/jpeg");
+ extensionToMime.set("gif", "image/gif");
+ extensionToMime.set("ico", "image/x-icon");
+ extensionToMime.set("js", "text/javascript");
+ }
+ int dot = file.reverseFind('.');
+ String mime("text/plain");
+ if (dot != -1) {
+ String ext = file.substring(dot + 1);
+ if (extensionToMime.contains(ext))
+ mime = extensionToMime.get(ext);
+ }
+ return mime;
+}
+
+void MyResourceLoader::loadFile(const String& file)
+{
+ LOGD("Loading file (%s) ...", file.latin1().data());
+ FILE* f = fopen(file.latin1().data(), "r");
+ ResourceHandleClient* client = m_handle->client();
+ if (!f) {
+ client->didFail(m_handle,
+ ResourceError("", -14, file, "Could not open file"));
+ } else {
+ ResourceResponse response;
+ response.setTextEncodingName("utf-8");
+ response.setMimeType(mimeTypeForExtension(file));
+ client->didReceiveResponse(m_handle, response);
+ char buf[512];
+ while (true) {
+ int res = fread(buf, 1, sizeof(buf), f);
+ if (res <= 0)
+ break;
+ client->didReceiveData(m_handle, buf, res, 0);
+ }
+ fclose(f);
+ client->didFinishLoading(m_handle, 0);
+ }
+}
+
+PassRefPtr<WebCore::ResourceLoaderAndroid> MyWebFrame::startLoadingResource(
+ ResourceHandle* handle, const ResourceRequest& req, bool ignore,
+ bool ignore2)
+{
+ RefPtr<WebCore::ResourceLoaderAndroid> loader =
+ MyResourceLoader::create(handle, req.url().string());
+ m_requests.append(loader);
+ if (!m_timer.isActive())
+ m_timer.startOneShot(0);
+ return loader.release();
+}
+
+void MyWebFrame::timerFired(Timer<MyWebFrame>*)
+{
+ LOGD("Handling requests...");
+ Vector<RefPtr<WebCore::ResourceLoaderAndroid> > reqs;
+ reqs.swap(m_requests);
+ Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator i = reqs.begin();
+ Vector<RefPtr<WebCore::ResourceLoaderAndroid> >::iterator end = reqs.end();
+ for (; i != end; i++)
+ static_cast<MyResourceLoader*>((*i).get())->handleRequest();
+
+ LOGD("...done");
+}
diff --git a/Source/WebKit/android/benchmark/Intercept.h b/Source/WebKit/android/benchmark/Intercept.h
new file mode 100644
index 0000000..6694dba
--- /dev/null
+++ b/Source/WebKit/android/benchmark/Intercept.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009, 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 APPLE COMPUTER, INC. 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 Intercept_h
+#define Intercept_h
+
+#include "MyJavaVM.h"
+#include "PlatformString.h"
+#include "Timer.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
+#include <JNIUtility.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class Page;
+ class ResourceHandle;
+ class ResourceRequest;
+}
+
+using namespace android;
+using namespace WebCore;
+using namespace WTF;
+
+class MyResourceLoader : public WebCoreResourceLoader {
+public:
+ static PassRefPtr<WebCore::ResourceLoaderAndroid> create(
+ ResourceHandle* handle, String url);
+ void handleRequest();
+
+private:
+ MyResourceLoader(ResourceHandle* handle, String url)
+ : WebCoreResourceLoader(JSC::Bindings::getJNIEnv(), MY_JOBJECT)
+ , m_handle(handle)
+ , m_url(url) {}
+
+ void loadData(const String&);
+ void loadFile(const String&);
+ ResourceHandle* m_handle;
+ String m_url;
+};
+
+class MyWebFrame : public WebFrame {
+public:
+ MyWebFrame(Page* page)
+ : WebFrame(JSC::Bindings::getJNIEnv(), MY_JOBJECT, MY_JOBJECT, page)
+ , m_timer(this, &MyWebFrame::timerFired) {}
+
+ virtual PassRefPtr<WebCore::ResourceLoaderAndroid> startLoadingResource(
+ ResourceHandle* handle, const ResourceRequest& req, bool, bool);
+
+ virtual bool canHandleRequest(const ResourceRequest&) { return true; }
+
+private:
+ void timerFired(Timer<MyWebFrame>*);
+ Vector<RefPtr<WebCore::ResourceLoaderAndroid> > m_requests;
+ Timer<MyWebFrame> m_timer;
+};
+
+#endif
diff --git a/Source/WebKit/android/benchmark/MyJavaVM.cpp b/Source/WebKit/android/benchmark/MyJavaVM.cpp
new file mode 100644
index 0000000..574c745
--- /dev/null
+++ b/Source/WebKit/android/benchmark/MyJavaVM.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2009, 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 APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MyJavaVM.h"
+
+#include <JNIUtility.h>
+#include <jni.h>
+
+static JNIEnv* s_env;
+static JavaVM* s_jvm;
+
+// JavaVM functions
+jint vm_attachCurrentThread(JavaVM*, JNIEnv** env, void*) {
+ *env = s_env;
+ return JNI_OK;
+}
+
+// JNIEnv functions
+jobject env_callObjectMethodV(JNIEnv*, jobject, jmethodID, va_list) {
+ return MY_JOBJECT;
+}
+void env_callVoidMethodV(JNIEnv*, jobject, jmethodID, va_list) {}
+void env_deleteRef(JNIEnv*, jobject) {}
+jboolean env_exceptionCheck(JNIEnv*) {
+ return false;
+}
+jclass env_findClass(JNIEnv*, const char*) {
+ return (jclass) 1;
+}
+jbyte* env_getByteArrayElements(JNIEnv*, jbyteArray, jboolean*) {
+ return NULL;
+}
+jmethodID env_getMethodID(JNIEnv*, jclass, const char*, const char*) {
+ return (jmethodID) 1;
+}
+jclass env_getObjectClass(JNIEnv*, jobject) {
+ return (jclass) 1;
+}
+static const char* s_fakeString = "Fake Java String";
+const jchar* env_getStringChars(JNIEnv*, jstring, jboolean* isCopy) {
+ if (isCopy)
+ *isCopy = false;
+ return (const jchar*)s_fakeString;
+}
+jsize env_getStringLength(JNIEnv*, jstring) {
+ return sizeof(s_fakeString) - 1;
+}
+jbyteArray env_newByteArray(JNIEnv*, jsize) {
+ return (jbyteArray) 1;
+}
+jobject env_newRef(JNIEnv*, jobject obj) {
+ return obj;
+}
+jobject env_newObjectV(JNIEnv*, jclass, jmethodID, va_list) {
+ return MY_JOBJECT;
+}
+jstring env_newString(JNIEnv*, const jchar*, jsize) {
+ return (jstring) 1;
+}
+void env_releaseByteArrayElements(JNIEnv*, jbyteArray, jbyte*, jint) {}
+void env_releaseStringChars(JNIEnv*, jstring, const jchar*) {}
+void env_setByteArrayRegion(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*) {}
+void env_setIntField(JNIEnv*, jobject, jfieldID, jint) {}
+
+void InitializeJavaVM() {
+ // First, create the fake vm
+ s_jvm = new JavaVM;
+ JNIInvokeInterface* i = new JNIInvokeInterface;
+ memset(i, 0, sizeof(JNIInvokeInterface));
+ s_jvm->functions = i;
+
+ // Now, assign the functions of the vm to our fake ones.
+ i->AttachCurrentThread = vm_attachCurrentThread;
+
+ // Create the fake env next
+ s_env = new JNIEnv;
+ JNINativeInterface* n = new JNINativeInterface;
+ memset(n, 0, sizeof(JNINativeInterface));
+ s_env->functions = n;
+
+ // Point the functions we care about to out fake ones.
+ n->CallObjectMethodV = env_callObjectMethodV;
+ n->CallVoidMethodV = env_callVoidMethodV;
+ n->DeleteLocalRef = env_deleteRef;
+ n->DeleteGlobalRef = env_deleteRef;
+ n->DeleteWeakGlobalRef = env_deleteRef;
+ n->ExceptionCheck = env_exceptionCheck;
+ n->FindClass = env_findClass;
+ n->GetByteArrayElements = env_getByteArrayElements;
+ n->GetMethodID = env_getMethodID;
+ n->GetObjectClass = env_getObjectClass;
+ n->GetStringChars = env_getStringChars;
+ n->GetStringLength = env_getStringLength;
+ n->NewByteArray = env_newByteArray;
+ n->NewLocalRef = env_newRef;
+ n->NewGlobalRef = env_newRef;
+ n->NewWeakGlobalRef = env_newRef;
+ n->NewObjectV = env_newObjectV;
+ n->NewString = env_newString;
+ n->ReleaseByteArrayElements = env_releaseByteArrayElements;
+ n->ReleaseStringChars = env_releaseStringChars;
+ n->SetByteArrayRegion = env_setByteArrayRegion;
+ n->SetIntField = env_setIntField;
+
+ // Tell WebCore about the vm
+ JSC::Bindings::setJavaVM(s_jvm);
+}
diff --git a/Source/WebKit/android/benchmark/MyJavaVM.h b/Source/WebKit/android/benchmark/MyJavaVM.h
new file mode 100644
index 0000000..3092161
--- /dev/null
+++ b/Source/WebKit/android/benchmark/MyJavaVM.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2009, 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 APPLE COMPUTER, INC. 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 MyJavaVM_h
+#define MyJavaVM_h
+
+// Make it 1 just to appease any assertions or checks for valid objects
+#define MY_JOBJECT ((jobject) 1)
+
+void InitializeJavaVM();
+
+#endif
diff --git a/Source/WebKit/android/benchmark/main.cpp b/Source/WebKit/android/benchmark/main.cpp
new file mode 100644
index 0000000..fcb797d
--- /dev/null
+++ b/Source/WebKit/android/benchmark/main.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2009, 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 APPLE COMPUTER, INC. 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_test"
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <utils/Log.h>
+
+namespace android {
+extern void benchmark(const char*, int, int ,int);
+}
+
+int main(int argc, char** argv) {
+ int width = 800;
+ int height = 600;
+ int reloadCount = 0;
+ while (true) {
+ int c = getopt(argc, argv, "d:r:");
+ if (c == -1)
+ break;
+ else if (c == 'd') {
+ char* x = strchr(optarg, 'x');
+ if (x) {
+ width = atoi(optarg);
+ height = atoi(x + 1);
+ LOGD("Rendering page at %dx%d", width, height);
+ }
+ } else if (c == 'r') {
+ reloadCount = atoi(optarg);
+ if (reloadCount < 0)
+ reloadCount = 0;
+ LOGD("Reloading %d times", reloadCount);
+ }
+ }
+ if (optind >= argc) {
+ LOGE("Please supply a file to read\n");
+ return 1;
+ }
+
+ android::benchmark(argv[optind], reloadCount, width, height);
+}
diff --git a/Source/WebKit/android/content/PhoneEmailDetector.cpp b/Source/WebKit/android/content/PhoneEmailDetector.cpp
deleted file mode 100644
index ca97a71..0000000
--- a/Source/WebKit/android/content/PhoneEmailDetector.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#undef WEBKIT_IMPLEMENTATION
-#undef LOG
-
-#include "base/utf_string_conversions.h"
-#include "net/base/escape.h"
-#include "PhoneEmailDetector.h"
-#include "Settings.h"
-#include "WebString.h"
-
-#define LOG_TAG "PhoneNumberDetector"
-#include <cutils/log.h>
-
-#define PHONE_PATTERN "(200) /-.\\ 100 -. 0000"
-
-static const char kTelSchemaPrefix[] = "tel:";
-static const char kEmailSchemaPrefix[] = "mailto:";
-
-void FindReset(FindState* state);
-void FindResetNumber(FindState* state);
-FoundState FindPartialNumber(const UChar* chars, unsigned length,
- FindState* s);
-struct FindState;
-
-static FoundState FindPartialEMail(const UChar* , unsigned length, FindState* );
-static bool IsDomainChar(UChar ch);
-static bool IsMailboxChar(UChar ch);
-
-PhoneEmailDetector::PhoneEmailDetector()
- : m_foundResult(FOUND_NONE)
-{
-}
-
-bool PhoneEmailDetector::IsEnabled(const WebKit::WebHitTestInfo& hit_test)
-{
- WebCore::Settings* settings = GetSettings(hit_test);
- if (!settings)
- return false;
- m_isPhoneDetectionEnabled = settings->formatDetectionTelephone();
- m_isEmailDetectionEnabled = settings->formatDetectionEmail();
- return m_isEmailDetectionEnabled || m_isPhoneDetectionEnabled;
-}
-
-bool PhoneEmailDetector::FindContent(const string16::const_iterator& begin,
- const string16::const_iterator& end,
- size_t* start_pos,
- size_t* end_pos)
-{
- FindReset(&m_findState);
- m_foundResult = FOUND_NONE;
- if (m_isPhoneDetectionEnabled)
- m_foundResult = FindPartialNumber(begin, end - begin, &m_findState);
- if (m_foundResult == FOUND_COMPLETE)
- m_prefix = kTelSchemaPrefix;
- else {
- FindReset(&m_findState);
- if (m_isEmailDetectionEnabled)
- m_foundResult = FindPartialEMail(begin, end - begin, &m_findState);
- m_prefix = kEmailSchemaPrefix;
- }
- *start_pos = m_findState.mStartResult;
- *end_pos = m_findState.mEndResult;
- return m_foundResult == FOUND_COMPLETE;
-}
-
-std::string PhoneEmailDetector::GetContentText(const WebKit::WebRange& range)
-{
- if (m_foundResult == FOUND_COMPLETE) {
- if (m_prefix == kTelSchemaPrefix)
- return UTF16ToUTF8(m_findState.mStore);
- else
- return UTF16ToUTF8(range.toPlainText());
- }
- return std::string();
-}
-
-GURL PhoneEmailDetector::GetIntentURL(const std::string& content_text)
-{
- return GURL(m_prefix +
- EscapeQueryParamValue(content_text, true));
-}
-
-void FindReset(FindState* state)
-{
- memset(state, 0, sizeof(FindState));
- state->mCurrent = ' ';
- FindResetNumber(state);
-}
-
-void FindResetNumber(FindState* state)
-{
- state->mOpenParen = false;
- state->mPattern = (char*) PHONE_PATTERN;
- state->mStorePtr = state->mStore;
-}
-
-FoundState FindPartialNumber(const UChar* chars, unsigned length,
- FindState* s)
-{
- char* pattern = s->mPattern;
- UChar* store = s->mStorePtr;
- const UChar* start = chars;
- const UChar* end = chars + length;
- const UChar* lastDigit = 0;
- string16 search16(chars, length);
- std::string searchSpace = UTF16ToUTF8(search16);
- do {
- bool initialized = s->mInitialized;
- while (chars < end) {
- if (initialized == false) {
- s->mBackTwo = s->mBackOne;
- s->mBackOne = s->mCurrent;
- }
- UChar ch = s->mCurrent = *chars;
- do {
- char patternChar = *pattern;
- switch (patternChar) {
- case '2':
- if (initialized == false) {
- s->mStartResult = chars - start;
- initialized = true;
- }
- case '0':
- case '1':
- if (ch < patternChar || ch > '9')
- goto resetPattern;
- *store++ = ch;
- pattern++;
- lastDigit = chars;
- goto nextChar;
- case '\0':
- if (WTF::isASCIIDigit(ch) == false) {
- *store = '\0';
- goto checkMatch;
- }
- goto resetPattern;
- case ' ':
- if (ch == patternChar)
- goto nextChar;
- break;
- case '(':
- if (ch == patternChar) {
- s->mStartResult = chars - start;
- initialized = true;
- s->mOpenParen = true;
- }
- goto commonPunctuation;
- case ')':
- if ((ch == patternChar) ^ s->mOpenParen)
- goto resetPattern;
- default:
- commonPunctuation:
- if (ch == patternChar) {
- pattern++;
- goto nextChar;
- }
- }
- } while (++pattern); // never false
- nextChar:
- chars++;
- }
- break;
-resetPattern:
- if (s->mContinuationNode)
- return FOUND_NONE;
- FindResetNumber(s);
- pattern = s->mPattern;
- store = s->mStorePtr;
- } while (++chars < end);
-checkMatch:
- if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo)) {
- return FOUND_NONE;
- }
- *store = '\0';
- s->mStorePtr = store;
- s->mPattern = pattern;
- s->mEndResult = lastDigit - start + 1;
- char pState = pattern[0];
- return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ?
- FOUND_NONE : FOUND_PARTIAL;
-}
-
-FoundState FindPartialEMail(const UChar* chars, unsigned length,
- FindState* s)
-{
- // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp
- // hand-edit at your own risk
- static const int domainTwoLetter[] = {
- 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz]
- 0x036e73fb, // b followed by: [abdefghijmnorstvwyz]
- 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz]
- 0x02005610, // d followed by: [ejkmoz]
- 0x001e00d4, // e followed by: [ceghrstu]
- 0x00025700, // f followed by: [ijkmor]
- 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy]
- 0x001a3400, // h followed by: [kmnrtu]
- 0x000f7818, // i followed by: [delmnoqrst]
- 0x0000d010, // j followed by: [emop]
- 0x0342b1d0, // k followed by: [eghimnprwyz]
- 0x013e0507, // l followed by: [abcikrstuvy]
- 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz]
- 0x0212c975, // n followed by: [acefgilopruz]
- 0x00001000, // o followed by: [m]
- 0x014e3cf1, // p followed by: [aefghklmnrstwy]
- 0x00000001, // q followed by: [a]
- 0x00504010, // r followed by: [eouw]
- 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz]
- 0x026afeec, // t followed by: [cdfghjklmnoprtvwz]
- 0x03041441, // u followed by: [agkmsyz]
- 0x00102155, // v followed by: [aceginu]
- 0x00040020, // w followed by: [fs]
- 0x00000000, // x
- 0x00180010, // y followed by: [etu]
- 0x00401001, // z followed by: [amw]
- };
-
- static char const* const longDomainNames[] = {
- "\x03" "ero" "\x03" "rpa", // aero, arpa
- "\x02" "iz", // biz
- "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop
- NULL, // d
- "\x02" "du", // edu
- NULL, // f
- "\x02" "ov", // gov
- NULL, // h
- "\x03" "nfo" "\x02" "nt", // info, int
- "\x03" "obs", // jobs
- NULL, // k
- NULL, // l
- "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum
- "\x03" "ame" "\x02" "et", // name, net
- "\x02" "rg", // , org
- "\x02" "ro", // pro
- NULL, // q
- NULL, // r
- NULL, // s
- "\x05" "ravel", // travel
- NULL, // u
- NULL, // v
- NULL, // w
- NULL, // x
- NULL, // y
- NULL, // z
- };
-
- const UChar* start = chars;
- const UChar* end = chars + length;
- while (chars < end) {
- UChar ch = *chars++;
- if (ch != '@')
- continue;
- const UChar* atLocation = chars - 1;
- // search for domain
- ch = *chars++ | 0x20; // convert uppercase to lower
- if (ch < 'a' || ch > 'z')
- continue;
- while (chars < end) {
- ch = *chars++;
- if (IsDomainChar(ch) == false)
- goto nextAt;
- if (ch != '.')
- continue;
- UChar firstLetter = *chars++ | 0x20; // first letter of the domain
- if (chars >= end)
- return FOUND_NONE; // only one letter; must be at least two
- firstLetter -= 'a';
- if (firstLetter > 'z' - 'a')
- continue; // non-letter followed '.'
- int secondLetterMask = domainTwoLetter[firstLetter];
- ch = *chars | 0x20; // second letter of the domain
- ch -= 'a';
- if (ch >= 'z' - 'a')
- continue;
- bool secondMatch = (secondLetterMask & 1 << ch) != 0;
- const char* wordMatch = longDomainNames[firstLetter];
- int wordIndex = 0;
- while (wordMatch != NULL) {
- int len = *wordMatch++;
- char match;
- do {
- match = wordMatch[wordIndex];
- if (match < 0x20)
- goto foundDomainStart;
- if (chars[wordIndex] != match)
- break;
- wordIndex++;
- } while (true);
- wordMatch += len;
- if (*wordMatch == '\0')
- break;
- wordIndex = 0;
- }
- if (secondMatch) {
- wordIndex = 1;
- foundDomainStart:
- chars += wordIndex;
- if (chars < end) {
- ch = *chars;
- if (ch != '.') {
- if (IsDomainChar(ch))
- goto nextDot;
- } else if (chars + 1 < end && IsDomainChar(chars[1]))
- goto nextDot;
- }
- // found domain. Search backwards from '@' for beginning of email address
- s->mEndResult = chars - start;
- chars = atLocation;
- if (chars <= start)
- goto nextAt;
- ch = *--chars;
- if (ch == '.')
- goto nextAt; // mailbox can't end in period
- do {
- if (IsMailboxChar(ch) == false) {
- chars++;
- break;
- }
- if (chars == start)
- break;
- ch = *--chars;
- } while (true);
- UChar firstChar = *chars;
- if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty
- goto nextAt;
- s->mStartResult = chars - start;
- return FOUND_COMPLETE;
- }
- nextDot:
- ;
- }
-nextAt:
- chars = atLocation + 1;
- }
- return FOUND_NONE;
-}
-
-bool IsDomainChar(UChar ch)
-{
- static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z
- ch -= 0x20;
- if (ch > 'z' - 0x20)
- return false;
- return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
-}
-
-bool IsMailboxChar(UChar ch)
-{
- // According to http://en.wikipedia.org/wiki/Email_address
- // ! # $ % & ' * + - . / 0-9 = ?
- // A-Z ^ _
- // ` a-z { | } ~
- static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff};
- ch -= 0x20;
- if (ch > '~' - 0x20)
- return false;
- return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
-}
diff --git a/Source/WebKit/android/content/PhoneEmailDetector.h b/Source/WebKit/android/content/PhoneEmailDetector.h
deleted file mode 100644
index 74ff5b1..0000000
--- a/Source/WebKit/android/content/PhoneEmailDetector.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "content/content_detector.h"
-#include "PlatformString.h"
-
-#define NAVIGATION_MAX_PHONE_LENGTH 14
-
-struct FindState {
- int mStartResult;
- int mEndResult;
- char* mPattern;
- UChar mStore[NAVIGATION_MAX_PHONE_LENGTH + 1];
- UChar* mStorePtr;
- UChar mBackOne;
- UChar mBackTwo;
- UChar mCurrent;
- bool mOpenParen;
- bool mInitialized;
- bool mContinuationNode;
-};
-
-enum FoundState {
- FOUND_NONE,
- FOUND_PARTIAL,
- FOUND_COMPLETE
-};
-
-// Searches for phone numbers (US only) or email addresses based off of the navcache code
-class PhoneEmailDetector : public ContentDetector {
-public:
- PhoneEmailDetector();
- virtual ~PhoneEmailDetector() {}
-
-private:
- // Implementation of ContentDetector.
- virtual bool FindContent(const string16::const_iterator& begin,
- const string16::const_iterator& end,
- size_t* start_pos,
- size_t* end_pos);
-
- virtual std::string GetContentText(const WebKit::WebRange& range);
- virtual GURL GetIntentURL(const std::string& content_text);
- virtual size_t GetMaximumContentLength() {
- return NAVIGATION_MAX_PHONE_LENGTH * 4;
- }
- virtual bool IsEnabled(const WebKit::WebHitTestInfo& hit_test) OVERRIDE;
-
- DISALLOW_COPY_AND_ASSIGN(PhoneEmailDetector);
-
- FindState m_findState;
- FoundState m_foundResult;
- const char* m_prefix;
- // TODO: This shouldn't be done like this. PhoneEmailDetector should be
- // refactored into two pieces and follow the IsEnabled style. This will
- // only work because we always call IsEnabled before FindContent
- bool m_isPhoneDetectionEnabled;
- bool m_isEmailDetectionEnabled;
-};
diff --git a/Source/WebKit/android/content/address_detector.cpp b/Source/WebKit/android/content/address_detector.cpp
deleted file mode 100644
index f6657d9..0000000
--- a/Source/WebKit/android/content/address_detector.cpp
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * 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.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-// Magic pretend-to-be-a-chromium-build flags
-#undef WEBKIT_IMPLEMENTATION
-#undef LOG
-
-#include "content/address_detector.h"
-
-#include <bitset>
-
-#include "base/utf_string_conversions.h"
-#include "net/base/escape.h"
-#include "Settings.h"
-#include "WebString.h"
-
-#include <wtf/HashSet.h>
-#include <wtf/Noncopyable.h>
-#include <wtf/text/StringHash.h>
-#include <wtf/text/WTFString.h>
-
-namespace {
-
-// Prefix used for geographical address intent URIs.
-static const char kAddressSchemaPrefix[] = "geo:0,0?q=";
-
-// Maximum text length to be searched for address detection.
-static const size_t kMaxAddressLength = 500;
-
-// Minimum number of words in an address after the house number
-// before a state is expected to be found.
-// A value too high can miss short addresses.
-const size_t kMinAddressWords = 3;
-
-// Maximum number of words allowed in an address between the house number
-// and the state, both not included.
-const size_t kMaxAddressWords = 12;
-
-// Maximum number of lines allowed in an address between the house number
-// and the state, both not included.
-const size_t kMaxAddressLines = 5;
-
-// Maximum length allowed for any address word between the house number
-// and the state, both not included.
-const size_t kMaxAddressNameWordLength = 25;
-
-// Maximum number of words after the house number in which the location name
-// should be found.
-const size_t kMaxLocationNameDistance = 4;
-
-// Number of digits for a valid zip code.
-const size_t kZipDigits = 5;
-
-// Number of digits for a valid zip code in the Zip Plus 4 format.
-const size_t kZipPlus4Digits = 9;
-
-// Maximum number of digits of a house number, including possible hyphens.
-const size_t kMaxHouseDigits = 5;
-
-// Additional characters used as new line delimiters.
-const char16 kNewlineDelimiters[] = {
- ',',
- '*',
- 0x2022, // Unicode bullet
- 0,
-};
-
-char16 SafePreviousChar(const string16::const_iterator& it,
- const string16::const_iterator& begin) {
- if (it == begin)
- return ' ';
- return *(it - 1);
-}
-
-char16 SafeNextChar(const string16::const_iterator& it,
- const string16::const_iterator& end) {
- if (it == end)
- return ' ';
- return *(it + 1);
-}
-
-bool WordLowerCaseEqualsASCII(string16::const_iterator word_begin,
- string16::const_iterator word_end, const char* ascii_to_match) {
- for (string16::const_iterator it = word_begin; it != word_end;
- ++it, ++ascii_to_match) {
- if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match)
- return false;
- }
- return *ascii_to_match == 0 || *ascii_to_match == ' ';
-}
-
-bool LowerCaseEqualsASCIIWithPlural(string16::const_iterator word_begin,
- string16::const_iterator word_end, const char* ascii_to_match,
- bool allow_plural) {
- for (string16::const_iterator it = word_begin; it != word_end;
- ++it, ++ascii_to_match) {
- if (!*ascii_to_match && allow_plural && *it == 's' && it + 1 == word_end)
- return true;
-
- if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match)
- return false;
- }
- return *ascii_to_match == 0;
-}
-
-} // anonymous namespace
-
-
-AddressDetector::AddressDetector() {
-}
-
-AddressDetector::~AddressDetector() {
-}
-
-std::string AddressDetector::GetContentText(const WebKit::WebRange& range) {
- // Get the address and replace unicode bullets with commas.
- string16 address_16 = CollapseWhitespace(range.toPlainText(), false);
- std::replace(address_16.begin(), address_16.end(),
- static_cast<char16>(0x2022), static_cast<char16>(','));
- return UTF16ToUTF8(address_16);
-}
-
-GURL AddressDetector::GetIntentURL(const std::string& content_text) {
- return GURL(kAddressSchemaPrefix +
- EscapeQueryParamValue(content_text, true));
-}
-
-size_t AddressDetector::GetMaximumContentLength() {
- return kMaxAddressLength;
-}
-
-bool AddressDetector::IsEnabled(const WebKit::WebHitTestInfo& hit_test) {
- WebCore::Settings* settings = GetSettings(hit_test);
- return settings && settings->formatDetectionAddress();
-}
-
-bool AddressDetector::FindContent(const string16::const_iterator& begin,
- const string16::const_iterator& end, size_t* start_pos, size_t* end_pos) {
- HouseNumberParser house_number_parser;
-
- // Keep going through the input string until a potential house number is
- // detected. Start tokenizing the following words to find a valid
- // street name within a word range. Then, find a state name followed
- // by a valid zip code for that state. Also keep a look for any other
- // possible house numbers to continue from in case of no match and for
- // state names not followed by a zip code (e.g. New York, NY 10000).
- const string16 newline_delimiters = kNewlineDelimiters;
- const string16 delimiters = kWhitespaceUTF16 + newline_delimiters;
- for (string16::const_iterator it = begin; it != end; ) {
- Word house_number;
- if (!house_number_parser.Parse(it, end, &house_number))
- return false;
-
- String16Tokenizer tokenizer(house_number.end, end, delimiters);
- tokenizer.set_options(String16Tokenizer::RETURN_DELIMS);
-
- std::vector<Word> words;
- words.push_back(house_number);
-
- bool found_location_name = false;
- bool continue_on_house_number = true;
- size_t next_house_number_word = 0;
- size_t num_lines = 1;
-
- // Don't include the house number in the word count.
- size_t next_word = 1;
- for (; next_word <= kMaxAddressWords + 1; ++next_word) {
-
- // Extract a new word from the tokenizer.
- if (next_word == words.size()) {
- do {
- if (!tokenizer.GetNext())
- return false;
-
- // Check the number of address lines.
- if (tokenizer.token_is_delim() && newline_delimiters.find(
- *tokenizer.token_begin()) != string16::npos) {
- ++num_lines;
- }
- } while (tokenizer.token_is_delim());
-
- if (num_lines > kMaxAddressLines)
- break;
-
- words.push_back(Word(tokenizer.token_begin(), tokenizer.token_end()));
- }
-
- // Check the word length. If too long, don't try to continue from
- // the next house number as no address can hold this word.
- const Word& current_word = words[next_word];
- DCHECK_GT(std::distance(current_word.begin, current_word.end), 0);
- size_t current_word_length = std::distance(
- current_word.begin, current_word.end);
- if (current_word_length > kMaxAddressNameWordLength) {
- continue_on_house_number = false;
- break;
- }
-
- // Check if the new word is a valid house number.
- // This is used to properly resume parsing in case the maximum number
- // of words is exceeded.
- if (next_house_number_word == 0 &&
- house_number_parser.Parse(current_word.begin, current_word.end, NULL)) {
- next_house_number_word = next_word;
- continue;
- }
-
- // Look for location names in the words after the house number.
- // A range limitation is introduced to avoid matching
- // anything that starts with a number before a legitimate address.
- if (next_word <= kMaxLocationNameDistance &&
- IsValidLocationName(current_word)) {
- found_location_name = true;
- continue;
- }
-
- // Don't count the house number.
- if (next_word > kMinAddressWords) {
- // Looking for the state is likely to add new words to the list while
- // checking for multi-word state names.
- size_t state_first_word = next_word;
- size_t state_last_word, state_index;
- if (FindStateStartingInWord(&words, state_first_word, &state_last_word,
- &tokenizer, &state_index)) {
-
- // A location name should have been found at this point.
- if (!found_location_name)
- break;
-
- // Explicitly exclude "et al", as "al" is a valid state code.
- if (current_word_length == 2 && words.size() > 2) {
- const Word& previous_word = words[state_first_word - 1];
- if (previous_word.end - previous_word.begin == 2 &&
- LowerCaseEqualsASCII(previous_word.begin, previous_word.end,
- "et") &&
- LowerCaseEqualsASCII(current_word.begin, current_word.end,
- "al"))
- break;
- }
-
- // Extract one more word from the tokenizer if not already available.
- size_t zip_word = state_last_word + 1;
- if (zip_word == words.size()) {
- do {
- if (!tokenizer.GetNext()) {
- // Zip is optional
- *start_pos = words[0].begin - begin;
- *end_pos = words[state_last_word].end - begin;
- return true;
- }
- } while (tokenizer.token_is_delim());
- words.push_back(Word(tokenizer.token_begin(),
- tokenizer.token_end()));
- }
-
- // Check the parsing validity and state range of the zip code.
- next_word = state_last_word;
- if (!IsZipValid(words[zip_word], state_index))
- continue;
-
- *start_pos = words[0].begin - begin;
- *end_pos = words[zip_word].end - begin;
- return true;
- }
- }
- }
-
- // Avoid skipping too many words because of a non-address number
- // at the beginning of the contents to parse.
- if (continue_on_house_number && next_house_number_word > 0) {
- it = words[next_house_number_word].begin;
- } else {
- DCHECK(!words.empty());
- next_word = std::min(next_word, words.size() - 1);
- it = words[next_word].end;
- }
- }
-
- return false;
-}
-
-bool AddressDetector::HouseNumberParser::IsPreDelimiter(
- char16 character) {
- return character == ':' || IsPostDelimiter(character);
-}
-
-bool AddressDetector::HouseNumberParser::IsPostDelimiter(
- char16 character) {
- return IsWhitespace(character) || strchr(",\"'", character);
-}
-
-void AddressDetector::HouseNumberParser::RestartOnNextDelimiter() {
- ResetState();
- for (; it_ != end_ && !IsPreDelimiter(*it_); ++it_) {}
-}
-
-void AddressDetector::HouseNumberParser::AcceptChars(size_t num_chars) {
- size_t offset = std::min(static_cast<size_t>(std::distance(it_, end_)),
- num_chars);
- it_ += offset;
- result_chars_ += offset;
-}
-
-void AddressDetector::HouseNumberParser::SkipChars(size_t num_chars) {
- it_ += std::min(static_cast<size_t>(std::distance(it_, end_)), num_chars);
-}
-
-void AddressDetector::HouseNumberParser::ResetState() {
- num_digits_ = 0;
- result_chars_ = 0;
-}
-
-bool AddressDetector::HouseNumberParser::CheckFinished(Word* word) const {
- // There should always be a number after a hyphen.
- if (result_chars_ == 0 || SafePreviousChar(it_, begin_) == '-')
- return false;
-
- if (word) {
- word->begin = it_ - result_chars_;
- word->end = it_;
- }
- return true;
-}
-
-bool AddressDetector::HouseNumberParser::Parse(
- const string16::const_iterator& begin,
- const string16::const_iterator& end, Word* word) {
- it_ = begin_ = begin;
- end_ = end;
- ResetState();
-
- // Iterations only used as a fail-safe against any buggy infinite loops.
- size_t iterations = 0;
- size_t max_iterations = end - begin + 1;
- for (; it_ != end_ && iterations < max_iterations; ++iterations) {
-
- // Word finished case.
- if (IsPostDelimiter(*it_)) {
- if (CheckFinished(word))
- return true;
- else if (result_chars_)
- ResetState();
-
- SkipChars(1);
- continue;
- }
-
- // More digits. There should be no more after a letter was found.
- if (IsAsciiDigit(*it_)) {
- if (num_digits_ >= kMaxHouseDigits) {
- RestartOnNextDelimiter();
- } else {
- AcceptChars(1);
- ++num_digits_;
- }
- continue;
- }
-
- if (IsAsciiAlpha(*it_)) {
- // Handle special case 'one'.
- if (result_chars_ == 0) {
- if (it_ + 3 <= end_ && LowerCaseEqualsASCII(it_, it_ + 3, "one"))
- AcceptChars(3);
- else
- RestartOnNextDelimiter();
- continue;
- }
-
- // There should be more than 1 character because of result_chars.
- DCHECK_GT(result_chars_, 0U);
- DCHECK_NE(it_, begin_);
- char16 previous = SafePreviousChar(it_, begin_);
- if (IsAsciiDigit(previous)) {
- // Check cases like '12A'.
- char16 next = SafeNextChar(it_, end_);
- if (IsPostDelimiter(next)) {
- AcceptChars(1);
- continue;
- }
-
- // Handle cases like 12a, 1st, 2nd, 3rd, 7th.
- if (IsAsciiAlpha(next)) {
- char16 last_digit = previous;
- char16 first_letter = base::ToLowerASCII(*it_);
- char16 second_letter = base::ToLowerASCII(next);
- bool is_teen = SafePreviousChar(it_ - 1, begin_) == '1' &&
- num_digits_ == 2;
-
- switch (last_digit - '0') {
- case 1:
- if ((first_letter == 's' && second_letter == 't') ||
- (first_letter == 't' && second_letter == 'h' && is_teen)) {
- AcceptChars(2);
- continue;
- }
- break;
-
- case 2:
- if ((first_letter == 'n' && second_letter == 'd') ||
- (first_letter == 't' && second_letter == 'h' && is_teen)) {
- AcceptChars(2);
- continue;
- }
- break;
-
- case 3:
- if ((first_letter == 'r' && second_letter == 'd') ||
- (first_letter == 't' && second_letter == 'h' && is_teen)) {
- AcceptChars(2);
- continue;
- }
- break;
-
- case 0:
- // Explicitly exclude '0th'.
- if (num_digits_ == 1)
- break;
-
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- if (first_letter == 't' && second_letter == 'h') {
- AcceptChars(2);
- continue;
- }
- break;
-
- default:
- NOTREACHED();
- }
- }
- }
-
- RestartOnNextDelimiter();
- continue;
- }
-
- if (*it_ == '-' && num_digits_ > 0) {
- AcceptChars(1);
- ++num_digits_;
- continue;
- }
-
- RestartOnNextDelimiter();
- SkipChars(1);
- }
-
- if (iterations >= max_iterations)
- return false;
-
- return CheckFinished(word);
-}
-
-bool AddressDetector::FindStateStartingInWord(WordList* words,
- size_t state_first_word, size_t* state_last_word,
- String16Tokenizer* tokenizer, size_t* state_index) {
-
- // Bitmasks containing the allowed suffixes for 2-letter state codes.
- static const int state_two_letter_suffix[23] = {
- 0x02060c00, // A followed by: [KLRSZ].
- 0x00000000, // B.
- 0x00084001, // C followed by: [AOT].
- 0x00000014, // D followed by: [CE].
- 0x00000000, // E.
- 0x00001800, // F followed by: [LM].
- 0x00100001, // G followed by: [AU].
- 0x00000100, // H followed by: [I].
- 0x00002809, // I followed by: [ADLN].
- 0x00000000, // J.
- 0x01040000, // K followed by: [SY].
- 0x00000001, // L followed by: [A].
- 0x000ce199, // M followed by: [ADEHINOPST].
- 0x0120129c, // N followed by: [CDEHJMVY].
- 0x00020480, // O followed by: [HKR].
- 0x00420001, // P followed by: [ARW].
- 0x00000000, // Q.
- 0x00000100, // R followed by: [I].
- 0x0000000c, // S followed by: [CD].
- 0x00802000, // T followed by: [NX].
- 0x00080000, // U followed by: [T].
- 0x00080101, // V followed by: [AIT].
- 0x01200101 // W followed by: [AIVY].
- };
-
- // Accumulative number of states for the 2-letter code indexed by the first.
- static const int state_two_letter_accumulative[24] = {
- 0, 5, 5, 8, 10, 10, 12, 14,
- 15, 19, 19, 21, 22, 32, 40, 43,
- 46, 46, 47, 49, 51, 52, 55, 59
- };
-
- // State names sorted alphabetically with their lengths.
- // There can be more than one possible name for a same state if desired.
- static const struct StateNameInfo {
- const char* string;
- char first_word_length;
- char length;
- char state_index; // Relative to two-character code alphabetical order.
- } state_names[59] = {
- { "alabama", 7, 7, 1 }, { "alaska", 6, 6, 0 },
- { "american samoa", 8, 14, 3 }, { "arizona", 7, 7, 4 },
- { "arkansas", 8, 8, 2 },
- { "california", 10, 10, 5 }, { "colorado", 8, 8, 6 },
- { "connecticut", 11, 11, 7 }, { "delaware", 8, 8, 9 },
- { "district of columbia", 8, 20, 8 },
- { "federated states of micronesia", 9, 30, 11 }, { "florida", 7, 7, 10 },
- { "guam", 4, 4, 13 }, { "georgia", 7, 7, 12 },
- { "hawaii", 6, 6, 14 },
- { "idaho", 5, 5, 16 }, { "illinois", 8, 8, 17 }, { "indiana", 7, 7, 18 },
- { "iowa", 4, 4, 15 },
- { "kansas", 6, 6, 19 }, { "kentucky", 8, 8, 20 },
- { "louisiana", 9, 9, 21 },
- { "maine", 5, 5, 24 }, { "marshall islands", 8, 16, 25 },
- { "maryland", 8, 8, 23 }, { "massachusetts", 13, 13, 22 },
- { "michigan", 8, 8, 26 }, { "minnesota", 9, 9, 27 },
- { "mississippi", 11, 11, 30 }, { "missouri", 8, 8, 28 },
- { "montana", 7, 7, 31 },
- { "nebraska", 8, 8, 34 }, { "nevada", 6, 6, 38 },
- { "new hampshire", 3, 13, 35 }, { "new jersey", 3, 10, 36 },
- { "new mexico", 3, 10, 37 }, { "new york", 3, 8, 39 },
- { "north carolina", 5, 14, 32 }, { "north dakota", 5, 12, 33 },
- { "northern mariana islands", 8, 24, 29 },
- { "ohio", 4, 4, 40 }, { "oklahoma", 8, 8, 41 }, { "oregon", 6, 6, 42 },
- { "palau", 5, 5, 45 }, { "pennsylvania", 12, 12, 43 },
- { "puerto rico", 6, 11, 44 },
- { "rhode island", 5, 5, 46 },
- { "south carolina", 5, 14, 47 }, { "south dakota", 5, 12, 48 },
- { "tennessee", 9, 9, 49 }, { "texas", 5, 5, 50 },
- { "utah", 4, 4, 51 },
- { "vermont", 7, 7, 54 }, { "virgin islands", 6, 14, 53 },
- { "virginia", 8, 8, 52 },
- { "washington", 10, 10, 55 }, { "west virginia", 4, 13, 57 },
- { "wisconsin", 9, 9, 56 }, { "wyoming", 7, 7, 58 }
- };
-
- // Accumulative number of states for sorted names indexed by the first letter.
- // Required a different one since there are codes that don't share their
- // first letter with the name of their state (MP = Northern Mariana Islands).
- static const int state_names_accumulative[24] = {
- 0, 5, 5, 8, 10, 10, 12, 14,
- 15, 19, 19, 21, 22, 31, 40, 43,
- 46, 46, 47, 49, 51, 52, 55, 59
- };
-
- DCHECK_EQ(state_names_accumulative[arraysize(state_names_accumulative) - 1],
- static_cast<int>(ARRAYSIZE_UNSAFE(state_names)));
-
- const Word& first_word = words->at(state_first_word);
- int length = first_word.end - first_word.begin;
- if (length < 2 || !IsAsciiAlpha(*first_word.begin))
- return false;
-
- // No state names start with x, y, z.
- char16 first_letter = base::ToLowerASCII(*first_word.begin);
- if (first_letter > 'w')
- return false;
-
- DCHECK(first_letter >= 'a');
- int first_index = first_letter - 'a';
-
- // Look for two-letter state names.
- if (length == 2 && IsAsciiAlpha(*(first_word.begin + 1))) {
- char16 second_letter = base::ToLowerASCII(*(first_word.begin + 1));
- DCHECK(second_letter >= 'a');
-
- int second_index = second_letter - 'a';
- if (!(state_two_letter_suffix[first_index] & (1 << second_index)))
- return false;
-
- std::bitset<32> previous_suffixes = state_two_letter_suffix[first_index] &
- ((1 << second_index) - 1);
- *state_last_word = state_first_word;
- *state_index = state_two_letter_accumulative[first_index] +
- previous_suffixes.count();
- return true;
- }
-
- // Look for full state names by their first letter. Discard by length.
- for (int state = state_names_accumulative[first_index];
- state < state_names_accumulative[first_index + 1]; ++state) {
- if (state_names[state].first_word_length != length)
- continue;
-
- bool state_match = false;
- size_t state_word = state_first_word;
- for (int pos = 0; true; ) {
- if (!WordLowerCaseEqualsASCII(words->at(state_word).begin,
- words->at(state_word).end, &state_names[state].string[pos]))
- break;
-
- pos += words->at(state_word).end - words->at(state_word).begin + 1;
- if (pos >= state_names[state].length) {
- state_match = true;
- break;
- }
-
- // Ran out of words, extract more from the tokenizer.
- if (++state_word == words->size()) {
- do {
- if (!tokenizer->GetNext())
- break;
- } while (tokenizer->token_is_delim());
- words->push_back(Word(tokenizer->token_begin(), tokenizer->token_end()));
- }
- }
-
- if (state_match) {
- *state_last_word = state_word;
- *state_index = state_names[state].state_index;
- return true;
- }
- }
-
- return false;
-}
-
-bool AddressDetector::IsZipValid(const Word& word, size_t state_index) {
- size_t length = word.end - word.begin;
- if (length != kZipDigits && length != kZipPlus4Digits + 1)
- return false;
-
- for (string16::const_iterator it = word.begin; it != word.end; ++it) {
- size_t pos = it - word.begin;
- if (IsAsciiDigit(*it) || (*it == '-' && pos == kZipDigits))
- continue;
- return false;
- }
- return IsZipValidForState(word, state_index);
-}
-
-bool AddressDetector::IsZipValidForState(const Word& word, size_t state_index)
-{
- enum USState {
- AP = -4, // AP (military base in the Pacific)
- AA = -3, // AA (military base inside the US)
- AE = -2, // AE (military base outside the US)
- XX = -1, // (not in use)
- AK = 0, // AK Alaska
- AL = 1, // AL Alabama
- AR = 2, // AR Arkansas
- AS = 3, // AS American Samoa
- AZ = 4, // AZ Arizona
- CA = 5, // CA California
- CO = 6, // CO Colorado
- CT = 7, // CT Connecticut
- DC = 8, // DC District of Columbia
- DE = 9, // DE Delaware
- FL = 10, // FL Florida
- FM = 11, // FM Federated States of Micronesia
- GA = 12, // GA Georgia
- GU = 13, // GU Guam
- HI = 14, // HI Hawaii
- IA = 15, // IA Iowa
- ID = 16, // ID Idaho
- IL = 17, // IL Illinois
- IN = 18, // IN Indiana
- KS = 19, // KS Kansas
- KY = 20, // KY Kentucky
- LA = 21, // LA Louisiana
- MA = 22, // MA Massachusetts
- MD = 23, // MD Maryland
- ME = 24, // ME Maine
- MH = 25, // MH Marshall Islands
- MI = 26, // MI Michigan
- MN = 27, // MN Minnesota
- MO = 28, // MO Missouri
- MP = 29, // MP Northern Mariana Islands
- MS = 30, // MS Mississippi
- MT = 31, // MT Montana
- NC = 32, // NC North Carolina
- ND = 33, // ND North Dakota
- NE = 34, // NE Nebraska
- NH = 35, // NH New Hampshire
- NJ = 36, // NJ New Jersey
- NM = 37, // NM New Mexico
- NV = 38, // NV Nevada
- NY = 39, // NY New York
- OH = 40, // OH Ohio
- OK = 41, // OK Oklahoma
- OR = 42, // OR Oregon
- PA = 43, // PA Pennsylvania
- PR = 44, // PR Puerto Rico
- PW = 45, // PW Palau
- RI = 46, // RI Rhode Island
- SC = 47, // SC South Carolina
- SD = 48, // SD South Dakota
- TN = 49, // TN Tennessee
- TX = 50, // TX Texas
- UT = 51, // UT Utah
- VA = 52, // VA Virginia
- VI = 53, // VI Virgin Islands
- VT = 54, // VT Vermont
- WA = 55, // WA Washington
- WI = 56, // WI Wisconsin
- WV = 57, // WV West Virginia
- WY = 58, // WY Wyoming
- };
-
- static const USState stateForZipPrefix[] = {
- // 0 1 2 3 4 5 6 7 8 9
- XX, XX, XX, XX, XX, NY, PR, PR, VI, PR, // 000-009
- MA, MA, MA, MA, MA, MA, MA, MA, MA, MA, // 010-019
- MA, MA, MA, MA, MA, MA, MA, MA, RI, RI, // 020-029
- NH, NH, NH, NH, NH, NH, NH, NH, NH, ME, // 030-039
- ME, ME, ME, ME, ME, ME, ME, ME, ME, ME, // 040-049
- VT, VT, VT, VT, VT, MA, VT, VT, VT, VT, // 050-059
- CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 060-069
- NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 070-079
- NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 080-089
- AE, AE, AE, AE, AE, AE, AE, AE, AE, XX, // 090-099
- NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 100-109
- NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 110-119
- NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 120-129
- NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 130-139
- NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 140-149
- PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 150-159
- PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 160-169
- PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 170-179
- PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 180-189
- PA, PA, PA, PA, PA, PA, PA, DE, DE, DE, // 190-199
- DC, VA, DC, DC, DC, DC, MD, MD, MD, MD, // 200-209
- MD, MD, MD, XX, MD, MD, MD, MD, MD, MD, // 210-219
- VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 220-229
- VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 230-239
- VA, VA, VA, VA, VA, VA, VA, WV, WV, WV, // 240-249
- WV, WV, WV, WV, WV, WV, WV, WV, WV, WV, // 250-259
- WV, WV, WV, WV, WV, WV, WV, WV, WV, XX, // 260-269
- NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 270-279
- NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 280-289
- SC, SC, SC, SC, SC, SC, SC, SC, SC, SC, // 290-299
- GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 300-309
- GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 310-319
- FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 320-329
- FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 330-339
- AA, FL, FL, XX, FL, XX, FL, FL, XX, FL, // 340-349
- AL, AL, AL, XX, AL, AL, AL, AL, AL, AL, // 350-359
- AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // 360-369
- TN, TN, TN, TN, TN, TN, TN, TN, TN, TN, // 370-379
- TN, TN, TN, TN, TN, TN, MS, MS, MS, MS, // 380-389
- MS, MS, MS, MS, MS, MS, MS, MS, GA, GA, // 390-399
- KY, KY, KY, KY, KY, KY, KY, KY, KY, KY, // 400-409
- KY, KY, KY, KY, KY, KY, KY, KY, KY, XX, // 410-419
- KY, KY, KY, KY, KY, KY, KY, KY, XX, XX, // 420-429
- OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 430-439
- OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 440-449
- OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 450-459
- IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 460-469
- IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 470-479
- MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 480-489
- MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 490-499
- IA, IA, IA, IA, IA, IA, IA, IA, IA, IA, // 500-509
- IA, IA, IA, IA, IA, IA, IA, XX, XX, XX, // 510-519
- IA, IA, IA, IA, IA, IA, IA, IA, IA, XX, // 520-529
- WI, WI, WI, XX, WI, WI, XX, WI, WI, WI, // 530-539
- WI, WI, WI, WI, WI, WI, WI, WI, WI, WI, // 540-549
- MN, MN, XX, MN, MN, MN, MN, MN, MN, MN, // 550-559
- MN, MN, MN, MN, MN, MN, MN, MN, XX, DC, // 560-569
- SD, SD, SD, SD, SD, SD, SD, SD, XX, XX, // 570-579
- ND, ND, ND, ND, ND, ND, ND, ND, ND, XX, // 580-589
- MT, MT, MT, MT, MT, MT, MT, MT, MT, MT, // 590-599
- IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 600-609
- IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 610-619
- IL, XX, IL, IL, IL, IL, IL, IL, IL, IL, // 620-629
- MO, MO, XX, MO, MO, MO, MO, MO, MO, MO, // 630-639
- MO, MO, XX, XX, MO, MO, MO, MO, MO, MO, // 640-649
- MO, MO, MO, MO, MO, MO, MO, MO, MO, XX, // 650-659
- KS, KS, KS, XX, KS, KS, KS, KS, KS, KS, // 660-669
- KS, KS, KS, KS, KS, KS, KS, KS, KS, KS, // 670-679
- NE, NE, XX, NE, NE, NE, NE, NE, NE, NE, // 680-689
- NE, NE, NE, NE, XX, XX, XX, XX, XX, XX, // 690-699
- LA, LA, XX, LA, LA, LA, LA, LA, LA, XX, // 700-709
- LA, LA, LA, LA, LA, XX, AR, AR, AR, AR, // 710-719
- AR, AR, AR, AR, AR, AR, AR, AR, AR, AR, // 720-729
- OK, OK, XX, TX, OK, OK, OK, OK, OK, OK, // 730-739
- OK, OK, XX, OK, OK, OK, OK, OK, OK, OK, // 740-749
- TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 750-759
- TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 760-769
- TX, XX, TX, TX, TX, TX, TX, TX, TX, TX, // 770-779
- TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 780-789
- TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 790-799
- CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, // 800-809
- CO, CO, CO, CO, CO, CO, CO, XX, XX, XX, // 810-819
- WY, WY, WY, WY, WY, WY, WY, WY, WY, WY, // 820-829
- WY, WY, ID, ID, ID, ID, ID, ID, ID, XX, // 830-839
- UT, UT, UT, UT, UT, UT, UT, UT, XX, XX, // 840-849
- AZ, AZ, AZ, AZ, XX, AZ, AZ, AZ, XX, AZ, // 850-859
- AZ, XX, XX, AZ, AZ, AZ, XX, XX, XX, XX, // 860-869
- NM, NM, NM, NM, NM, NM, XX, NM, NM, NM, // 870-879
- NM, NM, NM, NM, NM, TX, XX, XX, XX, NV, // 880-889
- NV, NV, XX, NV, NV, NV, XX, NV, NV, XX, // 890-899
- CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 900-909
- CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 910-919
- CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 920-929
- CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 930-939
- CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 940-949
- CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 950-959
- CA, CA, AP, AP, AP, AP, AP, HI, HI, GU, // 960-969
- OR, OR, OR, OR, OR, OR, OR, OR, OR, OR, // 970-979
- WA, WA, WA, WA, WA, WA, WA, XX, WA, WA, // 980-989
- WA, WA, WA, WA, WA, AK, AK, AK, AK, AK, // 990-999
- };
-
- if (!word.begin || !word.end || (word.end - word.begin) < 3)
- return false;
- const char16* zipPtr = word.begin;
- if (zipPtr[0] < '0' || zipPtr[0] > '9' ||
- zipPtr[1] < '0' || zipPtr[1] > '9' ||
- zipPtr[2] < '0' || zipPtr[2] > '9')
- return false;
-
- int zip = zipPtr[0] - '0';
- zip *= 10;
- zip += zipPtr[1] - '0';
- zip *= 10;
- zip += zipPtr[2] - '0';
- return stateForZipPrefix[zip] == (int) state_index;
-}
-
-static const char* s_rawStreetSuffixes[] = {
- "allee", "alley", "ally", "aly",
- "anex", "annex", "anx", "arc", "arcade", "av", "ave", "aven", "avenu",
- "avenue", "avn", "avnue", "bayoo", "bayou", "bch", "beach", "bend",
- "bg", "bgs", "blf", "blfs", "bluf", "bluff", "bluffs", "blvd", "bnd",
- "bot", "bottm", "bottom", "boul", "boulevard", "boulv", "br", "branch",
- "brdge", "brg", "bridge", "brk", "brks", "brnch", "brook", "brooks",
- "btm", "burg", "burgs", "byp", "bypa", "bypas", "bypass", "byps", "byu",
- "camp", "canyn", "canyon", "cape", "causeway", "causway", "cen", "cent",
- "center", "centers", "centr", "centre", "cir", "circ", "circl",
- "circle", "circles", "cirs", "ck", "clb", "clf", "clfs", "cliff",
- "cliffs", "club", "cmn", "cmp", "cnter", "cntr", "cnyn", "common",
- "cor", "corner", "corners", "cors", "course", "court", "courts", "cove",
- "coves", "cp", "cpe", "cr", "crcl", "crcle", "crecent", "creek", "cres",
- "crescent", "cresent", "crest", "crk", "crossing", "crossroad",
- "crscnt", "crse", "crsent", "crsnt", "crssing", "crssng", "crst", "crt",
- "cswy", "ct", "ctr", "ctrs", "cts", "curv", "curve", "cv", "cvs", "cyn",
- "dale", "dam", "div", "divide", "dl", "dm", "dr", "driv", "drive",
- "drives", "drs", "drv", "dv", "dvd", "est", "estate", "estates", "ests",
- "exp", "expr", "express", "expressway", "expw", "expy", "ext",
- "extension", "extensions", "extn", "extnsn", "exts", "fall", "falls",
- "ferry", "field", "fields", "flat", "flats", "fld", "flds", "fls",
- "flt", "flts", "ford", "fords", "forest", "forests", "forg", "forge",
- "forges", "fork", "forks", "fort", "frd", "frds", "freeway", "freewy",
- "frg", "frgs", "frk", "frks", "frry", "frst", "frt", "frway", "frwy",
- "fry", "ft", "fwy", "garden", "gardens", "gardn", "gateway", "gatewy",
- "gatway", "gdn", "gdns", "glen", "glens", "gln", "glns", "grden",
- "grdn", "grdns", "green", "greens", "grn", "grns", "grov", "grove",
- "groves", "grv", "grvs", "gtway", "gtwy", "harb", "harbor", "harbors",
- "harbr", "haven", "havn", "hbr", "hbrs", "height", "heights", "hgts",
- "highway", "highwy", "hill", "hills", "hiway", "hiwy", "hl", "hllw",
- "hls", "hollow", "hollows", "holw", "holws", "hrbor", "ht", "hts",
- "hvn", "hway", "hwy", "inlet", "inlt", "is", "island", "islands",
- "isle", "isles", "islnd", "islnds", "iss", "jct", "jction", "jctn",
- "jctns", "jcts", "junction", "junctions", "junctn", "juncton", "key",
- "keys", "knl", "knls", "knol", "knoll", "knolls", "ky", "kys", "la",
- "lake", "lakes", "land", "landing", "lane", "lanes", "lck", "lcks",
- "ldg", "ldge", "lf", "lgt", "lgts", "light", "lights", "lk", "lks",
- "ln", "lndg", "lndng", "loaf", "lock", "locks", "lodg", "lodge", "loop",
- "loops", "mall", "manor", "manors", "mdw", "mdws", "meadow", "meadows",
- "medows", "mews", "mill", "mills", "mission", "missn", "ml", "mls",
- "mnr", "mnrs", "mnt", "mntain", "mntn", "mntns", "motorway", "mount",
- "mountain", "mountains", "mountin", "msn", "mssn", "mt", "mtin", "mtn",
- "mtns", "mtwy", "nck", "neck", "opas", "orch", "orchard", "orchrd",
- "oval", "overpass", "ovl", "park", "parks", "parkway", "parkways",
- "parkwy", "pass", "passage", "path", "paths", "pike", "pikes", "pine",
- "pines", "pk", "pkway", "pkwy", "pkwys", "pky", "pl", "place", "plain",
- "plaines", "plains", "plaza", "pln", "plns", "plz", "plza", "pne",
- "pnes", "point", "points", "port", "ports", "pr", "prairie", "prarie",
- "prk", "prr", "prt", "prts", "psge", "pt", "pts", "rad", "radial",
- "radiel", "radl", "ramp", "ranch", "ranches", "rapid", "rapids", "rd",
- "rdg", "rdge", "rdgs", "rds", "real", "rest", "ridge", "ridges", "riv", "river",
- "rivr", "rnch", "rnchs", "road", "roads", "route", "row", "rpd", "rpds",
- "rst", "rte", "rue", "run", "rvr", "shl", "shls", "shoal", "shoals",
- "shoar", "shoars", "shore", "shores", "shr", "shrs", "skwy", "skyway",
- "smt", "spg", "spgs", "spng", "spngs", "spring", "springs", "sprng",
- "sprngs", "spur", "spurs", "sq", "sqr", "sqre", "sqrs", "sqs", "squ",
- "square", "squares", "st", "sta", "station", "statn", "stn", "str",
- "stra", "strav", "strave", "straven", "stravenue", "stravn", "stream",
- "street", "streets", "streme", "strm", "strt", "strvn", "strvnue",
- "sts", "sumit", "sumitt", "summit", "ter", "terr", "terrace",
- "throughway", "tpk", "tpke", "tr", "trace", "traces", "track", "tracks",
- "trafficway", "trail", "trails", "trak", "trce", "trfy", "trk", "trks",
- "trl", "trls", "trnpk", "trpk", "trwy", "tunel", "tunl", "tunls",
- "tunnel", "tunnels", "tunnl", "turnpike", "turnpk", "un", "underpass",
- "union", "unions", "uns", "upas", "valley", "valleys", "vally", "vdct",
- "via", "viadct", "viaduct", "view", "views", "vill", "villag",
- "village", "villages", "ville", "villg", "villiage", "vis", "vist",
- "vista", "vl", "vlg", "vlgs", "vlly", "vly", "vlys", "vst", "vsta",
- "vw", "vws", "walk", "walks", "wall", "way", "ways", "well", "wells",
- "wl", "wls", "wy", "xing", "xrd",
- 0,
-};
-
-bool AddressDetector::IsValidLocationName(const Word& word) {
- using namespace WTF;
- static HashSet<String> streetNames;
- if (!streetNames.size()) {
- const char** suffixes = s_rawStreetSuffixes;
- while (const char* suffix = *suffixes) {
- int index = suffix[0] - 'a';
- streetNames.add(suffix);
- suffixes++;
- }
- }
- char16 first_letter = base::ToLowerASCII(*word.begin);
- if (first_letter > 'z' || first_letter < 'a')
- return false;
- int index = first_letter - 'a';
- int length = std::distance(word.begin, word.end);
- if (*word.end == '.')
- length--;
- String value(word.begin, length);
- return streetNames.contains(value.lower());
-}
diff --git a/Source/WebKit/android/content/address_detector.h b/Source/WebKit/android/content/address_detector.h
deleted file mode 100644
index 6dc4ce8..0000000
--- a/Source/WebKit/android/content/address_detector.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * 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.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "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 CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_
-#define CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_
-#pragma once
-
-#include "build/build_config.h" // Needed for OS_ANDROID
-
-#if defined(OS_ANDROID)
-
-#include <vector>
-
-#include "base/string_tokenizer.h"
-#include "base/string_util.h"
-#include "content/content_detector.h"
-
-// Finds a geographical address (currently US only) in the given text string.
-class AddressDetector : public ContentDetector {
- public:
- AddressDetector();
- virtual ~AddressDetector();
-
- // Implementation of ContentDetector.
- virtual bool FindContent(const string16::const_iterator& begin,
- const string16::const_iterator& end,
- size_t* start_pos,
- size_t* end_pos) OVERRIDE;
-
- private:
- friend class AddressDetectorTest;
-
- virtual std::string GetContentText(const WebKit::WebRange& range) OVERRIDE;
- virtual GURL GetIntentURL(const std::string& content_text) OVERRIDE;
- virtual size_t GetMaximumContentLength() OVERRIDE;
- virtual bool IsEnabled(const WebKit::WebHitTestInfo& hit_test) OVERRIDE;
-
- // Internal structs and classes. Required to be visible by the unit tests.
- struct Word {
- string16::const_iterator begin;
- string16::const_iterator end;
-
- Word() {}
- Word(const string16::const_iterator& begin_it,
- const string16::const_iterator& end_it)
- : begin(begin_it),
- end(end_it) {
- DCHECK(begin_it <= end_it);
- }
- };
-
- class HouseNumberParser {
- public:
- HouseNumberParser() {}
-
- bool Parse(const string16::const_iterator& begin,
- const string16::const_iterator& end,
- Word* word);
-
- private:
- static inline bool IsPreDelimiter(char16 character);
- static inline bool IsPostDelimiter(char16 character);
- inline void RestartOnNextDelimiter();
-
- inline bool CheckFinished(Word* word) const;
- inline void AcceptChars(size_t num_chars);
- inline void SkipChars(size_t num_chars);
- inline void ResetState();
-
- // Iterators to the beginning, current position and ending of the string
- // being currently parsed.
- string16::const_iterator begin_;
- string16::const_iterator it_;
- string16::const_iterator end_;
-
- // Number of digits found in the current result candidate.
- size_t num_digits_;
-
- // Number of characters previous to the current iterator that belong
- // to the current result candidate.
- size_t result_chars_;
-
- DISALLOW_COPY_AND_ASSIGN(HouseNumberParser);
- };
-
- typedef std::vector<Word> WordList;
- typedef StringTokenizerT<string16, string16::const_iterator>
- String16Tokenizer;
-
- static bool FindStateStartingInWord(WordList* words,
- size_t state_first_word,
- size_t* state_last_word,
- String16Tokenizer* tokenizer,
- size_t* state_index);
-
- static bool IsValidLocationName(const Word& word);
- static bool IsZipValid(const Word& word, size_t state_index);
- static bool IsZipValidForState(const Word& word, size_t state_index);
-
- DISALLOW_COPY_AND_ASSIGN(AddressDetector);
-};
-
-#endif // defined(OS_ANDROID)
-
-#endif // CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_
diff --git a/Source/WebKit/android/content/content_detector.cpp b/Source/WebKit/android/content/content_detector.cpp
deleted file mode 100644
index e423207..0000000
--- a/Source/WebKit/android/content/content_detector.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * 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.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-// Magic pretend-to-be-a-chromium-build flags
-#undef WEBKIT_IMPLEMENTATION
-#undef LOG
-
-#include "content/content_detector.h"
-
-#include "public/android/WebDOMTextContentWalker.h"
-#include "public/android/WebHitTestInfo.h"
-
-#include "Document.h"
-#include "Node.h"
-#include "Page.h"
-#include "Settings.h"
-
-using WebKit::WebDOMTextContentWalker;
-using WebKit::WebRange;
-
-ContentDetector::Result ContentDetector::FindTappedContent(
- const WebKit::WebHitTestInfo& hit_test) {
- if (!IsEnabled(hit_test))
- return Result();
- WebKit::WebRange range = FindContentRange(hit_test);
- if (range.isNull())
- return Result();
-
- std::string text = GetContentText(range);
- GURL intent_url = GetIntentURL(text);
- return Result(range, text, intent_url);
-}
-
-WebRange ContentDetector::FindContentRange(
- const WebKit::WebHitTestInfo& hit_test) {
- WebDOMTextContentWalker content_walker(hit_test, GetMaximumContentLength());
- string16 content = content_walker.content();
- if (content.empty())
- return WebRange();
-
- size_t selected_offset = content_walker.hitOffsetInContent();
- for (size_t start_offset = 0; start_offset < content.length();) {
- size_t relative_start, relative_end;
- if (!FindContent(content.begin() + start_offset,
- content.end(), &relative_start, &relative_end)) {
- break;
- } else {
- size_t content_start = start_offset + relative_start;
- size_t content_end = start_offset + relative_end;
- DCHECK(content_end <= content.length());
-
- if (selected_offset >= content_start && selected_offset < content_end) {
- WebRange range = content_walker.contentOffsetsToRange(
- content_start, content_end);
- DCHECK(!range.isNull());
- return range;
- } else {
- start_offset += relative_end;
- }
- }
- }
-
- return WebRange();
-}
-
-WebCore::Settings* ContentDetector::GetSettings(const WebKit::WebHitTestInfo& hit_test) {
- if (!hit_test.node() || !hit_test.node()->document())
- return 0;
- return hit_test.node()->document()->page()->settings();
-}
diff --git a/Source/WebKit/android/content/content_detector.h b/Source/WebKit/android/content/content_detector.h
deleted file mode 100644
index 270928d..0000000
--- a/Source/WebKit/android/content/content_detector.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- *
- * 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.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "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 CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_
-#define CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_
-#pragma once
-
-#include "build/build_config.h" // Needed for OS_ANDROID
-
-#if defined(OS_ANDROID)
-
-#include "base/string_util.h"
-#include "googleurl/src/gurl.h"
-#include "public/WebRange.h"
-
-namespace WebKit {
-class WebHitTestInfo;
-}
-
-namespace WebCore {
-class Settings;
-}
-
-// Base class for text-based content detectors.
-class ContentDetector {
- public:
-
- // Holds the content detection results.
- struct Result {
- bool valid; // Flag indicating if the result is valid.
- WebKit::WebRange range; // Range describing the content boundaries.
- std::string text; // Processed text of the content.
- GURL intent_url; // URL of the intent that should process this content.
-
- Result() : valid(false) {}
-
- Result(const WebKit::WebRange& range,
- const std::string& text,
- const GURL& intent_url)
- : valid(true),
- range(range),
- text(text),
- intent_url(intent_url) {}
- };
-
- virtual ~ContentDetector() {}
-
- // Returns a WebKit range delimiting the contents found around the tapped
- // position. If no content is found a null range will be returned.
- Result FindTappedContent(const WebKit::WebHitTestInfo& hit_test);
-
- protected:
- // Parses the input string defined by the begin/end iterators returning true
- // if the desired content is found. The start and end positions relative to
- // the input iterators are returned in start_pos and end_pos.
- // The end position is assumed to be non-inclusive.
- virtual bool FindContent(const string16::const_iterator& begin,
- const string16::const_iterator& end,
- size_t* start_pos,
- size_t* end_pos) = 0;
-
- virtual bool IsEnabled(const WebKit::WebHitTestInfo& hit_test) = 0;
- WebCore::Settings* GetSettings(const WebKit::WebHitTestInfo& hit_test);
-
- // Extracts and processes the text of the detected content.
- virtual std::string GetContentText(const WebKit::WebRange& range) = 0;
-
- // Returns the intent URL that should process the content, if any.
- virtual GURL GetIntentURL(const std::string& content_text) = 0;
-
- // Returns the maximum length of text to be extracted around the tapped
- // position in order to search for content.
- virtual size_t GetMaximumContentLength() = 0;
-
- ContentDetector() {}
- WebKit::WebRange FindContentRange(const WebKit::WebHitTestInfo& hit_test);
-
- DISALLOW_COPY_AND_ASSIGN(ContentDetector);
-};
-
-#endif // defined(OS_ANDROID)
-
-#endif // CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_
diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.cpp b/Source/WebKit/android/jni/AndroidHitTestResult.cpp
deleted file mode 100644
index a135c42..0000000
--- a/Source/WebKit/android/jni/AndroidHitTestResult.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright 2012, 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 "AndroidHitTestResult"
-
-#include "config.h"
-#include "AndroidHitTestResult.h"
-
-#include "content/address_detector.h"
-#include "content/PhoneEmailDetector.h"
-#include "android/WebHitTestInfo.h"
-#include "Document.h"
-#include "Element.h"
-#include "Frame.h"
-#include "HitTestResult.h"
-#include "KURL.h"
-#include "LayerAndroid.h"
-#include "PlatformString.h"
-#include "Range.h"
-#include "RenderLayer.h"
-#include "RenderLayerBacking.h"
-#include "RenderObject.h"
-#include "WebCoreJni.h"
-#include "WebViewCore.h"
-
-#include <cutils/log.h>
-#include <JNIHelp.h>
-#include <JNIUtility.h>
-
-namespace android {
-
-using namespace WebCore;
-
-static bool gJniInitialized = false;
-static struct {
- jmethodID m_Init;
- jfieldID m_LinkUrl;
- jfieldID m_AnchorText;
- jfieldID m_ImageUrl;
- jfieldID m_AltDisplayString;
- jfieldID m_Title;
- jfieldID m_Editable;
- jfieldID m_TouchRects;
- jfieldID m_TapHighlightColor;
- jfieldID m_EnclosingParentRects;
- jfieldID m_HasFocus;
- jfieldID m_IntentUrl;
-} gHitTestGlue;
-
-struct field {
- jclass m_class;
- const char *m_fieldName;
- const char *m_fieldType;
- jfieldID *m_jfield;
-};
-
-static void InitJni(JNIEnv* env)
-{
- if (gJniInitialized)
- return;
-
- jclass rectClass = env->FindClass("android/graphics/Rect");
- ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
- jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest");
- ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest");
-
- gHitTestGlue.m_Init = env->GetMethodID(hitTestClass, "<init>", "()V");
- ALOG_ASSERT(gHitTestGlue.m_Init, "Could not find init method on android/webkit/WebViewCore$WebKitHitTest");
-
- field fields[] = {
- { hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;", &gHitTestGlue.m_TouchRects },
- { hitTestClass, "mEditable", "Z", &gHitTestGlue.m_Editable },
- { hitTestClass, "mLinkUrl", "Ljava/lang/String;", &gHitTestGlue.m_LinkUrl },
- { hitTestClass, "mIntentUrl", "Ljava/lang/String;", &gHitTestGlue.m_IntentUrl },
- { hitTestClass, "mAnchorText", "Ljava/lang/String;", &gHitTestGlue.m_AnchorText },
- { hitTestClass, "mImageUrl", "Ljava/lang/String;", &gHitTestGlue.m_ImageUrl },
- { hitTestClass, "mAltDisplayString", "Ljava/lang/String;", &gHitTestGlue.m_AltDisplayString },
- { hitTestClass, "mTitle", "Ljava/lang/String;", &gHitTestGlue.m_Title },
- { hitTestClass, "mTapHighlightColor", "I", &gHitTestGlue.m_TapHighlightColor },
- { hitTestClass, "mEnclosingParentRects", "[Landroid/graphics/Rect;", &gHitTestGlue.m_EnclosingParentRects },
- { hitTestClass, "mHasFocus", "Z", &gHitTestGlue.m_HasFocus },
- {0, 0, 0, 0},
- };
-
- for (int i = 0; fields[i].m_jfield; i++) {
- field *f = &fields[i];
- jfieldID field = env->GetFieldID(f->m_class, f->m_fieldName, f->m_fieldType);
- ALOG_ASSERT(field, "Can't find %s", f->m_fieldName);
- *(f->m_jfield) = field;
- }
-
- gJniInitialized = true;
-}
-
-AndroidHitTestResult::AndroidHitTestResult(WebViewCore* webViewCore, WebCore::HitTestResult& hitTestResult)
- : m_webViewCore(webViewCore)
- , m_hitTestResult(hitTestResult)
-{
- buildHighlightRects();
-}
-
-void AndroidHitTestResult::setURLElement(Element* element)
-{
- m_hitTestResult.setURLElement(element);
- buildHighlightRects();
-}
-
-void AndroidHitTestResult::buildHighlightRects()
-{
- m_highlightRects.clear();
- Node* node = m_hitTestResult.URLElement();
- if (!node || !node->renderer())
- node = m_hitTestResult.innerNode();
- if (!node || !node->renderer())
- return;
- if (!WebViewCore::nodeIsClickableOrFocusable(node))
- return;
- Frame* frame = node->document()->frame();
- IntPoint frameOffset = m_webViewCore->convertGlobalContentToFrameContent(IntPoint(), frame);
- RenderObject* renderer = node->renderer();
- Vector<FloatQuad> quads;
- if (renderer->isInline())
- renderer->absoluteFocusRingQuads(quads);
- if (!quads.size())
- renderer->absoluteQuads(quads); // No fancy rings, grab a bounding box
- for (size_t i = 0; i < quads.size(); i++) {
- IntRect boundingBox = quads[i].enclosingBoundingBox();
- boundingBox.move(-frameOffset.x(), -frameOffset.y());
- m_highlightRects.append(boundingBox);
- }
-}
-
-void AndroidHitTestResult::searchContentDetectors()
-{
- AddressDetector address;
- PhoneEmailDetector phoneEmail;
- Node* node = m_hitTestResult.innerNode();
- if (!node || !node->isTextNode())
- return;
- if (!m_hitTestResult.absoluteLinkURL().isEmpty())
- return;
- WebKit::WebHitTestInfo webHitTest(m_hitTestResult);
- m_searchResult = address.FindTappedContent(webHitTest);
- if (!m_searchResult.valid) {
- m_searchResult = phoneEmail.FindTappedContent(webHitTest);
- }
- if (m_searchResult.valid) {
- m_highlightRects.clear();
- RefPtr<Range> range = (PassRefPtr<Range>) m_searchResult.range;
- range->textRects(m_highlightRects, true);
- }
-}
-
-void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str)
-{
- jstring jstr = wtfStringToJstring(env, str, false);
- env->SetObjectField(obj, field, jstr);
- env->DeleteLocalRef(jstr);
-}
-
-void setStringField(JNIEnv* env, jobject obj, jfieldID field, const GURL& url)
-{
- jstring jstr = stdStringToJstring(env, url.spec(), false);
- env->SetObjectField(obj, field, jstr);
- env->DeleteLocalRef(jstr);
-}
-
-void setRectArray(JNIEnv* env, jobject obj, jfieldID field, Vector<IntRect> &rects)
-{
- jobjectArray array = intRectVectorToRectArray(env, rects);
- env->SetObjectField(obj, field, array);
- env->DeleteLocalRef(array);
-}
-
-// Some helper macros specific to setting hitTest fields
-#define _SET(jtype, jfield, value) env->Set ## jtype ## Field(hitTest, gHitTestGlue.m_ ## jfield, value)
-#define SET_BOOL(jfield, value) _SET(Boolean, jfield, value)
-#define SET_STRING(jfield, value) setStringField(env, hitTest, gHitTestGlue.m_ ## jfield, value)
-#define SET_INT(jfield, value) _SET(Int, jfield, value)
-
-jobject AndroidHitTestResult::createJavaObject(JNIEnv* env)
-{
- InitJni(env);
- jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest");
- ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest");
-
- jobject hitTest = env->NewObject(hitTestClass, gHitTestGlue.m_Init);
- setRectArray(env, hitTest, gHitTestGlue.m_TouchRects, m_highlightRects);
-
- Vector<IntRect> rects = enclosingParentRects(m_hitTestResult.innerNode());
- setRectArray(env, hitTest, gHitTestGlue.m_EnclosingParentRects, rects);
-
- SET_BOOL(Editable, m_hitTestResult.isContentEditable());
- SET_STRING(LinkUrl, m_hitTestResult.absoluteLinkURL().string());
- if (m_searchResult.valid)
- SET_STRING(IntentUrl, m_searchResult.intent_url);
- SET_STRING(ImageUrl, m_hitTestResult.absoluteImageURL().string());
- SET_STRING(AltDisplayString, m_hitTestResult.altDisplayString());
- TextDirection titleTextDirection;
- SET_STRING(Title, m_hitTestResult.title(titleTextDirection));
- if (m_hitTestResult.URLElement()) {
- Element* urlElement = m_hitTestResult.URLElement();
- SET_STRING(AnchorText, urlElement->innerText());
- if (urlElement->renderer()) {
- SET_INT(TapHighlightColor,
- urlElement->renderer()->style()->tapHighlightColor().rgb());
- }
- }
- Node* focusedNode = m_webViewCore->focusedFrame()->document()->focusedNode();
- SET_BOOL(HasFocus,
- focusedNode == m_hitTestResult.URLElement()
- || focusedNode == m_hitTestResult.innerNode()
- || focusedNode == m_hitTestResult.innerNonSharedNode());
-
- env->DeleteLocalRef(hitTestClass);
-
- return hitTest;
-}
-
-Vector<IntRect> AndroidHitTestResult::enclosingParentRects(Node* node)
-{
- int count = 0;
- int lastX = 0;
- Vector<IntRect> rects;
-
- while (node && count < 5) {
- RenderObject* render = node->renderer();
- if (!render || render->isBody())
- break;
-
- IntPoint frameOffset = m_webViewCore->convertGlobalContentToFrameContent(IntPoint(),
- node->document()->frame());
- IntRect rect = render->absoluteBoundingBoxRect();
- rect.move(-frameOffset.x(), -frameOffset.y());
- if (count == 0 || rect.x() != lastX) {
- rects.append(rect);
- lastX = rect.x();
- count++;
- }
-
- node = node->parentNode();
- }
-
- return rects;
-}
-
-} /* namespace android */
diff --git a/Source/WebKit/android/jni/CacheManager.cpp b/Source/WebKit/android/jni/CacheManager.cpp
index b34776d..f600d00 100644
--- a/Source/WebKit/android/jni/CacheManager.cpp
+++ b/Source/WebKit/android/jni/CacheManager.cpp
@@ -25,6 +25,8 @@
#include "config.h"
+#if USE(CHROME_NETWORK_STACK)
+
#include "ChromiumIncludes.h"
#include "WebCache.h"
#include "WebCoreJni.h"
@@ -131,10 +133,12 @@ int registerCacheManager(JNIEnv* env)
{
#ifndef NDEBUG
jclass cacheManager = env->FindClass(javaCacheManagerClass);
- ALOG_ASSERT(cacheManager, "Unable to find class");
+ LOG_ASSERT(cacheManager, "Unable to find class");
env->DeleteLocalRef(cacheManager);
#endif
return jniRegisterNativeMethods(env, javaCacheManagerClass, gCacheManagerMethods, NELEM(gCacheManagerMethods));
}
} // namespace android
+
+#endif // USE(CHROME_NETWORK_STACK)
diff --git a/Source/WebKit/android/jni/CookieManager.cpp b/Source/WebKit/android/jni/CookieManager.cpp
index 5db1e16..f8c2dee 100644
--- a/Source/WebKit/android/jni/CookieManager.cpp
+++ b/Source/WebKit/android/jni/CookieManager.cpp
@@ -35,35 +35,54 @@ using namespace net;
namespace android {
-// JNI for android.webkit.CookieManagerClassic
-static const char* javaCookieManagerClass = "android/webkit/CookieManagerClassic";
+// JNI for android.webkit.CookieManager
+static const char* javaCookieManagerClass = "android/webkit/CookieManager";
static bool acceptCookie(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
// This is a static method which gets the cookie policy for all WebViews. We
// always apply the same configuration to the contexts for both regular and
// private browsing, so expect the same result here.
bool regularAcceptCookies = WebCookieJar::get(false)->allowCookies();
ASSERT(regularAcceptCookies == WebCookieJar::get(true)->allowCookies());
return regularAcceptCookies;
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return false;
+#endif
}
static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing)
{
+#if USE(CHROME_NETWORK_STACK)
GURL gurl(jstringToStdString(env, url));
CookieOptions options;
options.set_include_httponly();
std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options);
return stdStringToJstring(env, cookies);
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return jstring();
+#endif
}
static bool hasCookies(JNIEnv*, jobject, jboolean privateBrowsing)
{
+#if USE(CHROME_NETWORK_STACK)
return WebCookieJar::get(privateBrowsing)->getNumCookiesInDatabase() > 0;
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return false;
+#endif
}
static void removeAllCookie(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->DeleteAll(true);
// This will lazily create a new private browsing context. However, if the
// context doesn't already exist, there's no need to create it, as cookies
@@ -75,62 +94,84 @@ static void removeAllCookie(JNIEnv*, jobject)
// The Java code removes cookies directly from the backing database, so we do the same,
// but with a NULL callback so it's asynchronous.
WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->FlushStore(NULL);
+#endif
}
static void removeExpiredCookie(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
// This simply forces a GC. The getters delete expired cookies so won't return expired cookies anyway.
WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetAllCookies();
WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->GetAllCookies();
+#endif
}
static void removeSessionCookies(WebCookieJar* cookieJar)
{
+#if USE(CHROME_NETWORK_STACK)
CookieMonster* cookieMonster = cookieJar->cookieStore()->GetCookieMonster();
CookieList cookies = cookieMonster->GetAllCookies();
for (CookieList::const_iterator iter = cookies.begin(); iter != cookies.end(); ++iter) {
if (iter->IsSessionCookie())
cookieMonster->DeleteCanonicalCookie(*iter);
}
+#endif
}
static void removeSessionCookie(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
removeSessionCookies(WebCookieJar::get(false));
removeSessionCookies(WebCookieJar::get(true));
+#endif
}
static void setAcceptCookie(JNIEnv*, jobject, jboolean accept)
{
+#if USE(CHROME_NETWORK_STACK)
// This is a static method which configures the cookie policy for all
// WebViews, so we configure the contexts for both regular and private
// browsing.
WebCookieJar::get(false)->setAllowCookies(accept);
WebCookieJar::get(true)->setAllowCookies(accept);
+#endif
}
static void setCookie(JNIEnv* env, jobject, jstring url, jstring value, jboolean privateBrowsing)
{
+#if USE(CHROME_NETWORK_STACK)
GURL gurl(jstringToStdString(env, url));
std::string line(jstringToStdString(env, value));
CookieOptions options;
options.set_include_httponly();
WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->SetCookieWithOptions(gurl, line, options);
+#endif
}
static void flushCookieStore(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
WebCookieJar::flush();
+#endif
}
static bool acceptFileSchemeCookies(JNIEnv*, jobject)
{
+#if USE(CHROME_NETWORK_STACK)
return WebCookieJar::acceptFileSchemeCookies();
+#else
+ // File scheme cookies are always accepted with the Android HTTP stack.
+ return true;
+#endif
}
static void setAcceptFileSchemeCookies(JNIEnv*, jobject, jboolean accept)
{
+#if USE(CHROME_NETWORK_STACK)
WebCookieJar::setAcceptFileSchemeCookies(accept);
+#else
+ // File scheme cookies are always accepted with the Android HTTP stack.
+#endif
}
static JNINativeMethod gCookieManagerMethods[] = {
@@ -151,7 +192,7 @@ int registerCookieManager(JNIEnv* env)
{
#ifndef NDEBUG
jclass cookieManager = env->FindClass(javaCookieManagerClass);
- ALOG_ASSERT(cookieManager, "Unable to find class");
+ LOG_ASSERT(cookieManager, "Unable to find class");
env->DeleteLocalRef(cookieManager);
#endif
return jniRegisterNativeMethods(env, javaCookieManagerClass, gCookieManagerMethods, NELEM(gCookieManagerMethods));
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
index b69c2af..8beb372 100644
--- a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
@@ -46,7 +46,7 @@ DeviceMotionAndOrientationManager::DeviceMotionAndOrientationManager(WebViewCore
{
}
-void DeviceMotionAndOrientationManager::setUseMock()
+void DeviceMotionAndOrientationManager::useMock()
{
m_useMock = true;
}
@@ -124,9 +124,9 @@ static WebViewCore* getWebViewCore(JNIEnv* env, jobject webViewCoreObject)
return reinterpret_cast<WebViewCore*>(env->GetIntField(webViewCoreObject, nativeClassField));
}
-static void setUseMock(JNIEnv* env, jobject, jobject webViewCoreObject)
+static void useMock(JNIEnv* env, jobject, jobject webViewCoreObject)
{
- getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->setUseMock();
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->useMock();
}
static void onMotionChange(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z, double interval)
@@ -151,7 +151,7 @@ static void onOrientationChange(JNIEnv* env, jobject, jobject webViewCoreObject,
}
static JNINativeMethod gDeviceMotionAndOrientationManagerMethods[] = {
- { "nativeSetUseMock", "(Landroid/webkit/WebViewCore;)V", (void*) setUseMock },
+ { "nativeUseMock", "(Landroid/webkit/WebViewCore;)V", (void*) useMock },
{ "nativeOnMotionChange", "(Landroid/webkit/WebViewCore;ZDZDZDD)V", (void*) onMotionChange },
{ "nativeSetMockOrientation", "(Landroid/webkit/WebViewCore;ZDZDZD)V", (void*) setMockOrientation },
{ "nativeOnOrientationChange", "(Landroid/webkit/WebViewCore;ZDZDZD)V", (void*) onOrientationChange }
@@ -161,7 +161,7 @@ int registerDeviceMotionAndOrientationManager(JNIEnv* env)
{
#ifndef NDEBUG
jclass deviceMotionAndOrientationManager = env->FindClass(javaDeviceMotionAndOrientationManagerClass);
- ALOG_ASSERT(deviceMotionAndOrientationManager, "Unable to find class");
+ LOG_ASSERT(deviceMotionAndOrientationManager, "Unable to find class");
env->DeleteLocalRef(deviceMotionAndOrientationManager);
#endif
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
index cc6821d..44463c1 100644
--- a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
@@ -46,7 +46,7 @@ class DeviceMotionAndOrientationManager {
public:
DeviceMotionAndOrientationManager(WebViewCore*);
- void setUseMock();
+ void useMock();
void setMockMotion(PassRefPtr<WebCore::DeviceMotionData>);
void onMotionChange(PassRefPtr<WebCore::DeviceMotionData>);
void setMockOrientation(PassRefPtr<WebCore::DeviceOrientation>);
diff --git a/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
index 295b347..a366601 100755
--- a/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
+++ b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
@@ -99,10 +99,10 @@ static JNINativeMethod gGeolocationPermissionsMethods[] = {
int registerGeolocationPermissions(JNIEnv* env)
{
- const char* kGeolocationPermissionsClass = "android/webkit/GeolocationPermissionsClassic";
+ const char* kGeolocationPermissionsClass = "android/webkit/GeolocationPermissions";
#ifndef NDEBUG
jclass geolocationPermissions = env->FindClass(kGeolocationPermissionsClass);
- ALOG_ASSERT(geolocationPermissions, "Unable to find class");
+ LOG_ASSERT(geolocationPermissions, "Unable to find class");
env->DeleteLocalRef(geolocationPermissions);
#endif
diff --git a/Source/WebKit/android/jni/JavaBridge.cpp b/Source/WebKit/android/jni/JavaBridge.cpp
index 9f89ccd..68eb367 100644
--- a/Source/WebKit/android/jni/JavaBridge.cpp
+++ b/Source/WebKit/android/jni/JavaBridge.cpp
@@ -41,6 +41,9 @@
#include "PluginDatabase.h"
#include "Timer.h"
#include "TimerClient.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
#include "WebCache.h"
#include "WebCoreJni.h"
@@ -145,15 +148,15 @@ JavaBridge::JavaBridge(JNIEnv* env, jobject obj)
mResolveFilePathForContentUri = env->GetMethodID(clazz, "resolveFilePathForContentUri", "(Ljava/lang/String;)Ljava/lang/String;");
env->DeleteLocalRef(clazz);
- ALOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer");
- ALOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer");
- ALOG_ASSERT(mSetCookies, "Could not find method setCookies");
- ALOG_ASSERT(mCookies, "Could not find method cookies");
- ALOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled");
- ALOG_ASSERT(mGetPluginDirectories, "Could not find method getPluginDirectories");
- ALOG_ASSERT(mGetPluginSharedDataDirectory, "Could not find method getPluginSharedDataDirectory");
- ALOG_ASSERT(mGetKeyStrengthList, "Could not find method getKeyStrengthList");
- ALOG_ASSERT(mGetSignedPublicKey, "Could not find method getSignedPublicKey");
+ LOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer");
+ LOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer");
+ LOG_ASSERT(mSetCookies, "Could not find method setCookies");
+ LOG_ASSERT(mCookies, "Could not find method cookies");
+ LOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled");
+ LOG_ASSERT(mGetPluginDirectories, "Could not find method getPluginDirectories");
+ LOG_ASSERT(mGetPluginSharedDataDirectory, "Could not find method getPluginSharedDataDirectory");
+ LOG_ASSERT(mGetKeyStrengthList, "Could not find method getKeyStrengthList");
+ LOG_ASSERT(mGetSignedPublicKey, "Could not find method getSignedPublicKey");
JavaSharedClient::SetTimerClient(this);
JavaSharedClient::SetCookieClient(this);
@@ -280,7 +283,7 @@ JavaBridge::getPluginSharedDataDirectory()
void
JavaBridge::setSharedTimerCallback(void (*f)())
{
- ALOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f,
+ LOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f,
"Shared timer callback may already be set or null!");
sSharedTimerFiredCallback = f;
@@ -360,8 +363,8 @@ void JavaBridge::Finalize(JNIEnv* env, jobject obj)
{
JavaBridge* javaBridge = (JavaBridge*)
(env->GetIntField(obj, gJavaBridge_ObjectID));
- ALOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!");
- ALOGV("webcore_javabridge::nativeFinalize(%p)\n", javaBridge);
+ LOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!");
+ LOGV("webcore_javabridge::nativeFinalize(%p)\n", javaBridge);
delete javaBridge;
env->SetIntField(obj, gJavaBridge_ObjectID, 0);
}
@@ -370,7 +373,16 @@ void JavaBridge::Finalize(JNIEnv* env, jobject obj)
void JavaBridge::SharedTimerFired(JNIEnv* env, jobject)
{
if (sSharedTimerFiredCallback)
+ {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::start(TimeCounter::SharedTimerTimeCounter);
+#endif
+ SkAutoMemoryUsageProbe mup("JavaBridge::sharedTimerFired");
sSharedTimerFiredCallback();
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::record(TimeCounter::SharedTimerTimeCounter, __FUNCTION__);
+#endif
+ }
}
void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes)
@@ -469,10 +481,12 @@ void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName
void JavaBridge::UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList)
{
+#if USE(CHROME_NETWORK_STACK)
std::string proxy = jstringToStdString(env, newProxy);
std::string exList = jstringToStdString(env, newExList);
WebCache::get(false)->proxy()->UpdateProxySettings(proxy, exList);
WebCache::get(true)->proxy()->UpdateProxySettings(proxy, exList);
+#endif
}
diff --git a/Source/WebKit/android/jni/JavaSharedClient.cpp b/Source/WebKit/android/jni/JavaSharedClient.cpp
index 4d073c2..4f40355 100644
--- a/Source/WebKit/android/jni/JavaSharedClient.cpp
+++ b/Source/WebKit/android/jni/JavaSharedClient.cpp
@@ -88,12 +88,12 @@ namespace android {
FileSystemClient* JavaSharedClient::gFileSystemClient = NULL;
///////////////////////////////////////////////////////////////////////////
-
+
struct FuncPtrRec {
void (*fProc)(void* payload);
void* fPayload;
};
-
+
static SkMutex gFuncPtrQMutex;
static SkDeque gFuncPtrQ(sizeof(FuncPtrRec));
@@ -105,34 +105,33 @@ namespace android {
FuncPtrRec* rec = (FuncPtrRec*)gFuncPtrQ.push_back();
rec->fProc = proc;
rec->fPayload = payload;
-
+
gFuncPtrQMutex.release();
-
+
gTimerClient->signalServiceFuncPtrQueue();
}
void JavaSharedClient::ServiceFunctionPtrQueue()
{
- // Don't let execution block the WebViewCore thread for too long.
- void (*proc)(void*) = 0;
- void* payload = 0;
- const FuncPtrRec* rec;
-
- // we have to copy the proc/payload (if present). we do this so we
- // don't call the proc inside the mutex (possible deadlock!)
- gFuncPtrQMutex.acquire();
- rec = (const FuncPtrRec*)gFuncPtrQ.front();
- if (rec) {
- proc = rec->fProc;
- payload = rec->fPayload;
- gFuncPtrQ.pop_front();
- }
- bool scheduleAdditionalCall = (gFuncPtrQ.count() > 0);
- gFuncPtrQMutex.release();
-
- if (rec)
+ for (;;) {
+ void (*proc)(void*) = 0;
+ void* payload = 0;
+ const FuncPtrRec* rec;
+
+ // we have to copy the proc/payload (if present). we do this so we
+ // don't call the proc inside the mutex (possible deadlock!)
+ gFuncPtrQMutex.acquire();
+ rec = (const FuncPtrRec*)gFuncPtrQ.front();
+ if (rec) {
+ proc = rec->fProc;
+ payload = rec->fPayload;
+ gFuncPtrQ.pop_front();
+ }
+ gFuncPtrQMutex.release();
+
+ if (!rec)
+ break;
proc(payload);
- if (scheduleAdditionalCall)
- gTimerClient->signalServiceFuncPtrQueue();
+ }
}
}
diff --git a/Source/WebKit/android/jni/JniUtil.cpp b/Source/WebKit/android/jni/JniUtil.cpp
new file mode 100644
index 0000000..ee1e3f9
--- /dev/null
+++ b/Source/WebKit/android/jni/JniUtil.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "ChromiumIncludes.h"
+#include <JNIHelp.h>
+
+namespace android {
+
+static const char* javaJniUtilClass = "android/webkit/JniUtil";
+
+static bool useChromiumHttpStack(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static JNINativeMethod gJniUtilMethods[] = {
+ { "nativeUseChromiumHttpStack", "()Z", (void*) useChromiumHttpStack },
+};
+
+int registerJniUtil(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass jniUtil = env->FindClass(javaJniUtilClass);
+ LOG_ASSERT(jniUtil, "Unable to find class");
+ env->DeleteLocalRef(jniUtil);
+#endif
+ return jniRegisterNativeMethods(env, javaJniUtilClass, gJniUtilMethods, NELEM(gJniUtilMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/MIMETypeRegistry.cpp b/Source/WebKit/android/jni/MIMETypeRegistry.cpp
index 2734aeb..cbfef6c 100644
--- a/Source/WebKit/android/jni/MIMETypeRegistry.cpp
+++ b/Source/WebKit/android/jni/MIMETypeRegistry.cpp
@@ -44,11 +44,11 @@ String MIMETypeRegistry::getMIMETypeForExtension(const String& ext)
ASSERT(isMainThread());
JNIEnv* env = JSC::Bindings::getJNIEnv();
jclass mimeClass = env->FindClass("android/webkit/MimeTypeMap");
- ALOG_ASSERT(mimeClass, "Could not find class MimeTypeMap");
+ LOG_ASSERT(mimeClass, "Could not find class MimeTypeMap");
jmethodID mimeTypeFromExtension = env->GetStaticMethodID(mimeClass,
"mimeTypeFromExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
- ALOG_ASSERT(mimeTypeFromExtension,
+ LOG_ASSERT(mimeTypeFromExtension,
"Could not find method mimeTypeFromExtension");
jstring extString = wtfStringToJstring(env, ext);
jobject mimeType = env->CallStaticObjectMethod(mimeClass,
diff --git a/Source/WebKit/android/jni/MockGeolocation.cpp b/Source/WebKit/android/jni/MockGeolocation.cpp
index 250953f..1370715 100755
--- a/Source/WebKit/android/jni/MockGeolocation.cpp
+++ b/Source/WebKit/android/jni/MockGeolocation.cpp
@@ -74,7 +74,7 @@ int registerMockGeolocation(JNIEnv* env)
{
#ifndef NDEBUG
jclass mockGeolocation = env->FindClass(javaMockGeolocationClass);
- ALOG_ASSERT(mockGeolocation, "Unable to find class");
+ LOG_ASSERT(mockGeolocation, "Unable to find class");
env->DeleteLocalRef(mockGeolocation);
#endif
diff --git a/Source/WebKit/android/jni/PicturePile.cpp b/Source/WebKit/android/jni/PicturePile.cpp
deleted file mode 100644
index ccdfa59..0000000
--- a/Source/WebKit/android/jni/PicturePile.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2012, 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 "PicturePile"
-#define LOG_NDEBUG 1
-
-#include "config.h"
-#include "PicturePile.h"
-
-#include "AndroidLog.h"
-#include "FloatRect.h"
-#include "GraphicsContext.h"
-#include "PlatformGraphicsContextSkia.h"
-#include "SkCanvas.h"
-#include "SkNWayCanvas.h"
-#include "SkPicture.h"
-#include "SkPixelRef.h"
-#include "SkRect.h"
-#include "SkRegion.h"
-
-#define ENABLE_PRERENDERED_INVALS true
-#define MAX_OVERLAP_COUNT 2
-#define MAX_OVERLAP_AREA .7
-
-namespace WebCore {
-
-static SkIRect toSkIRect(const IntRect& rect) {
- return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
-}
-
-static IntRect extractClipBounds(SkCanvas* canvas, const IntSize& size) {
- SkRect clip;
- canvas->getClipBounds(&clip);
- clip.intersect(0, 0, size.width(), size.height());
- return enclosingIntRect(clip);
-}
-
-PicturePile::PicturePile(const PicturePile& other)
- : m_size(other.m_size)
- , m_pile(other.m_pile)
- , m_webkitInvals(other.m_webkitInvals)
-{
-}
-
-PicturePile::PicturePile(SkPicture* picture)
-{
- m_size = IntSize(picture->width(), picture->height());
- PictureContainer pc(IntRect(0, 0, m_size.width(), m_size.height()));
- pc.picture = picture;
- pc.dirty = false;
- m_pile.append(pc);
-}
-
-void PicturePile::draw(SkCanvas* canvas)
-{
- /* Loop down recursively, subtracting the previous clip from the SkRegion,
- * stopping when the SkRegion is empty. This will still draw back-to-front,
- * but it will clip out anything obscured. For performance reasons we use
- * the rect bounds of the SkRegion for the clip, so this still can't be
- * used for translucent surfaces
- */
- TRACE_METHOD();
- IntRect clipBounds = extractClipBounds(canvas, m_size);
- SkRegion clipRegion(toSkIRect(clipBounds));
- drawWithClipRecursive(canvas, clipRegion, m_pile.size() - 1);
-}
-
-void PicturePile::clearPrerenders()
-{
- for (size_t i = 0; i < m_pile.size(); i++)
- m_pile[i].prerendered.clear();
-}
-
-void PicturePile::drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion,
- int index)
-{
- // TODO: Add some debug visualizations of this
- if (index < 0 || clipRegion.isEmpty())
- return;
- PictureContainer& pc = m_pile[index];
- IntRect intersection = clipRegion.getBounds();
- intersection.intersect(pc.area);
- if (pc.picture && !intersection.isEmpty()) {
- clipRegion.op(intersection, SkRegion::kDifference_Op);
- drawWithClipRecursive(canvas, clipRegion, index - 1);
- int saved = canvas->save();
- canvas->clipRect(intersection);
- canvas->translate(pc.area.x(), pc.area.y());
- canvas->drawPicture(*pc.picture);
- canvas->restoreToCount(saved);
- } else
- drawWithClipRecursive(canvas, clipRegion, index - 1);
-}
-
-// Used by WebViewCore
-void PicturePile::invalidate(const IntRect& dirtyRect)
-{
- // This will typically happen if the document has been resized but we haven't
- // drawn yet. As the first draw after a size change will do a full inval anyway,
- // don't bother tracking individual rects
- // TODO: Instead of clipping here, we should take the invals as given
- // and when the size changes just inval the deltas. This prevents a full
- // redraw for a page that grows
- IntRect inval = dirtyRect;
- inval.intersect(IntRect(0, 0, m_size.width(), m_size.height()));
- if (inval.isEmpty()) {
- ALOGV("Rejecting inval " INT_RECT_FORMAT, INT_RECT_ARGS(dirtyRect));
- return;
- }
- // TODO: Support multiple non-intersecting webkit invals
- if (m_webkitInvals.size())
- m_webkitInvals[0].unite(inval);
- else
- m_webkitInvals.append(inval);
-}
-
-void PicturePile::setSize(const IntSize& size)
-{
- if (m_size == size)
- return;
- m_size = size;
- // TODO: See above about just adding invals for new content
- m_pile.clear();
- m_webkitInvals.clear();
- if (!size.isEmpty()) {
- IntRect area(0, 0, size.width(), size.height());
- m_webkitInvals.append(area);
- m_pile.append(area);
- }
-}
-
-void PicturePile::updatePicturesIfNeeded(PicturePainter* painter)
-{
- applyWebkitInvals();
- for (size_t i = 0; i < m_pile.size(); i++) {
- PictureContainer& pc = m_pile[i];
- if (pc.dirty)
- updatePicture(painter, pc);
- }
-}
-
-void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc)
-{
- /* The ref counting here is a bit unusual. What happens is begin/end recording
- * will ref/unref the recording canvas. However, 'canvas' might be pointing
- * at an SkNWayCanvas instead of the recording canvas, which needs to be
- * unref'd. Thus what we do is ref the recording canvas so that we can
- * always unref whatever canvas we have at the end.
- */
- TRACE_METHOD();
- SkPicture* picture = new SkPicture();
- SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(),
- SkPicture::kUsePathBoundsForClip_RecordingFlag);
- SkSafeRef(canvas);
- canvas->translate(-pc.area.x(), -pc.area.y());
- IntRect drawArea = pc.area;
- if (pc.prerendered.get()) {
- SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get());
- if (!prerender) {
- ALOGV("Failed to create prerendered for " INT_RECT_FORMAT,
- INT_RECT_ARGS(pc.prerendered->area));
- pc.prerendered.clear();
- } else {
- drawArea.unite(pc.prerendered->area);
- SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height());
- nwayCanvas->translate(-drawArea.x(), -drawArea.y());
- nwayCanvas->addCanvas(canvas);
- nwayCanvas->addCanvas(prerender);
- SkSafeUnref(canvas);
- SkSafeUnref(prerender);
- canvas = nwayCanvas;
- }
- }
- WebCore::PlatformGraphicsContextSkia pgc(canvas);
- WebCore::GraphicsContext gc(&pgc);
- ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea));
- painter->paintContents(&gc, drawArea);
- SkSafeUnref(canvas);
- picture->endRecording();
-
- SkSafeUnref(pc.picture);
- pc.picture = picture;
- pc.dirty = false;
-}
-
-void PicturePile::reset()
-{
- m_size = IntSize(0,0);
- m_pile.clear();
- m_webkitInvals.clear();
-}
-
-void PicturePile::applyWebkitInvals()
-{
- m_dirtyRegion.setEmpty();
- if (!m_webkitInvals.size())
- return;
- // Build the invals (TODO: Support multiple inval regions)
- IntRect inval = m_webkitInvals[0];
- m_dirtyRegion.setRect(toSkIRect(inval));
- for (size_t i = 1; i < m_webkitInvals.size(); i++) {
- inval.unite(m_webkitInvals[i]);
- m_dirtyRegion.op(toSkIRect(m_webkitInvals[i]), SkRegion::kUnion_Op);
- }
- m_webkitInvals.clear();
- ALOGV("Webkit inval: " INT_RECT_FORMAT, INT_RECT_ARGS(inval));
- if (inval.isEmpty())
- return;
-
- // Find the overlaps
- Vector<int> overlaps;
- for (size_t i = 0; i < m_pile.size(); i++) {
- PictureContainer& pc = m_pile[i];
- if (pc.area.contains(inval)) {
- if (pc.dirty) {
- ALOGV("Found already dirty intersection");
- return;
- }
- if (pc.area == inval) {
- appendToPile(inval);
- return;
- }
- // Don't count the base surface as an overlap
- if (pc.area.size() != m_size)
- overlaps.append(i);
- } else if (pc.area.intersects(inval))
- overlaps.append(i);
- }
-
- if (overlaps.size() >= MAX_OVERLAP_COUNT) {
- ALOGV("Exceeds overlap count");
- IntRect overlap = inval;
- for (int i = (int) overlaps.size() - 1; i >= 0; i--) {
- overlap.unite(m_pile[overlaps[i]].area);
- m_pile.remove(overlaps[i]);
- }
- float overlapArea = overlap.width() * overlap.height();
- float totalArea = m_size.width() * m_size.height();
- if (overlapArea / totalArea > MAX_OVERLAP_AREA)
- overlap = IntRect(0, 0, m_size.width(), m_size.height());
- appendToPile(overlap, inval);
- return;
- }
-
- // Append!
- appendToPile(inval);
-}
-
-void PicturePile::appendToPile(const IntRect& inval, const IntRect& originalInval)
-{
- ALOGV("Adding inval " INT_RECT_FORMAT " for original inval " INT_RECT_FORMAT,
- INT_RECT_ARGS(inval), INT_RECT_ARGS(originalInval));
- // Remove any entries this obscures
- for (int i = (int) m_pile.size() - 1; i >= 0; i--) {
- if (inval.contains(m_pile[i].area))
- m_pile.remove(i);
- }
- PictureContainer container(inval);
- if (ENABLE_PRERENDERED_INVALS) {
- container.prerendered = PrerenderedInval::create(originalInval.isEmpty()
- ? inval : originalInval);
- }
- m_pile.append(container);
-}
-
-PrerenderedInval* PicturePile::prerenderedInvalForArea(const IntRect& area)
-{
- for (int i = (int) m_pile.size() - 1; i >= 0; i--) {
- if (m_pile[i].area.intersects(area)) {
- RefPtr<PrerenderedInval> inval = m_pile[i].prerendered;
- if (inval.get() && inval->area.contains(area))
- return inval.get();
- return 0;
- }
- }
- return 0;
-}
-
-} // namespace WebCore
diff --git a/Source/WebKit/android/jni/PicturePile.h b/Source/WebKit/android/jni/PicturePile.h
deleted file mode 100644
index b28a792..0000000
--- a/Source/WebKit/android/jni/PicturePile.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2012, 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 PicturePile_h
-#define PicturePile_h
-
-#include "IntRect.h"
-#include "IntSize.h"
-#include "PrerenderedInval.h"
-#include "SkBitmap.h"
-#include "SkRegion.h"
-#include "SkRefCnt.h"
-
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/ThreadSafeRefCounted.h>
-#include <wtf/Vector.h>
-
-class SkPicture;
-class SkCanvas;
-
-namespace WebCore {
-
-class GraphicsContext;
-
-class PicturePainter {
-public:
- virtual void paintContents(GraphicsContext* gc, IntRect& dirty) = 0;
- virtual SkCanvas* createPrerenderCanvas(PrerenderedInval* prerendered)
- {
- return 0;
- }
- virtual ~PicturePainter() {}
-};
-
-class PictureContainer {
-public:
- SkPicture* picture;
- IntRect area;
- bool dirty;
- RefPtr<PrerenderedInval> prerendered;
-
- PictureContainer(const IntRect& area)
- : picture(0)
- , area(area)
- , dirty(true)
- {}
-
- PictureContainer(const PictureContainer& other)
- : picture(other.picture)
- , area(other.area)
- , dirty(other.dirty)
- , prerendered(other.prerendered)
- {
- SkSafeRef(picture);
- }
-
- ~PictureContainer()
- {
- SkSafeUnref(picture);
- }
-};
-
-class PicturePile {
-public:
- PicturePile() {}
- PicturePile(const PicturePile& other);
- PicturePile(SkPicture* picture);
-
- const IntSize& size() { return m_size; }
-
- void clearPrerenders();
-
- // used by PicturePileLayerContents
- void draw(SkCanvas* canvas);
-
- // Used by WebViewCore
- void invalidate(const IntRect& dirtyRect);
- void setSize(const IntSize& size);
- void updatePicturesIfNeeded(PicturePainter* painter);
- void reset();
- SkRegion& dirtyRegion() { return m_dirtyRegion; }
- PrerenderedInval* prerenderedInvalForArea(const IntRect& area);
-
-private:
- void applyWebkitInvals();
- void updatePicture(PicturePainter* painter, PictureContainer& container);
- void appendToPile(const IntRect& inval, const IntRect& originalInval = IntRect());
- void drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, int index);
-
- IntSize m_size;
- Vector<PictureContainer> m_pile;
- Vector<IntRect> m_webkitInvals;
- SkRegion m_dirtyRegion;
-};
-
-} // namespace android
-
-#endif // PicturePile_h
diff --git a/Source/WebKit/android/jni/PictureSet.cpp b/Source/WebKit/android/jni/PictureSet.cpp
new file mode 100644
index 0000000..4d9d16c
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.cpp
@@ -0,0 +1,1236 @@
+/*
+ * Copyright 2008, 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_NDEBUG 0
+#define LOG_TAG "pictureset"
+
+//#include <config.h>
+#include "CachedPrefix.h"
+#include "android_graphics.h"
+#include "PictureSet.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+#include "TimeCounter.h"
+
+#define MAX_DRAW_TIME 100
+#define MIN_SPLITTABLE 400
+#define MAX_ADDITIONAL_AREA 0.65
+#define MAX_ADDITIONAL_PICTURES 32
+
+#define BUCKET_SIZE 1024
+#define MAX_BUCKET_COUNT_X 16
+#define MAX_BUCKET_COUNT_Y 64
+
+#include <wtf/CurrentTime.h>
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+
+#undef XLOGC
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
+
+#ifdef DEBUG
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "PictureSet", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+#if PICTURE_SET_DEBUG
+class MeasureStream : public SkWStream {
+public:
+ MeasureStream() : mTotal(0) {}
+ virtual bool write(const void* , size_t size) {
+ mTotal += size;
+ return true;
+ }
+ size_t mTotal;
+};
+#endif
+
+namespace android {
+
+PictureSet::PictureSet() :
+#ifdef FAST_PICTURESET
+ mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE),
+ mBucketCountX(0), mBucketCountY(0),
+#endif
+ mHeight(0), mWidth(0)
+{
+ setDimensions(0, 0);
+ mBaseArea = mAdditionalArea = 0;
+}
+
+PictureSet::PictureSet(SkPicture* picture) :
+#ifdef FAST_PICTURESET
+ mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE),
+ mBucketCountX(0), mBucketCountY(0),
+#endif
+ mHeight(0), mWidth(0)
+{
+ mBaseArea = mAdditionalArea = 0;
+ if (!picture) {
+ setDimensions(0, 0);
+ return;
+ }
+ setDimensions(picture->width(), picture->height());
+ mBaseArea = mWidth * mHeight;
+#ifdef FAST_PICTURESET
+ SkIRect area;
+ area.set(0, 0, mWidth, mHeight);
+ splitAdd(area);
+ WTF::Vector<Bucket*>* buckets = bucketsToUpdate();
+ for (unsigned int i = 0; i < buckets->size(); i++) {
+ Bucket* bucket = (*buckets)[i];
+ for (unsigned int j = 0; j < bucket->size(); j++) {
+ BucketPicture& bucketPicture = (*bucket)[j];
+ const SkIRect& inval = bucketPicture.mRealArea;
+ SkPicture *splitPicture = new SkPicture();
+ SkCanvas *canvas = splitPicture->beginRecording(
+ inval.width(), inval.height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag);
+ canvas->translate(-inval.fLeft, -inval.fTop);
+ picture->draw(canvas);
+ splitPicture->endRecording();
+ SkSafeUnref(bucketPicture.mPicture);
+ bucketPicture.mPicture = splitPicture;
+ }
+ }
+ buckets->clear();
+#else
+ Pictures pictureAndBounds;
+ pictureAndBounds.mPicture = picture;
+ SkSafeRef(pictureAndBounds.mPicture);
+ pictureAndBounds.mEmpty = false;
+ pictureAndBounds.mArea.setRect(0, 0, mWidth, mHeight);
+ pictureAndBounds.mSplit = false;
+ pictureAndBounds.mBase = true;
+ pictureAndBounds.mElapsed = 0;
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+#endif // FAST_PICTURESET
+}
+
+PictureSet::~PictureSet()
+{
+ clear();
+}
+
+#ifdef FAST_PICTURESET
+#else
+void PictureSet::add(const Pictures* temp)
+{
+ Pictures pictureAndBounds = *temp;
+ SkSafeRef(pictureAndBounds.mPicture);
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+}
+#endif // FAST_PICTURESET
+
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split)
+{
+ if (area.isRect()) {
+#ifdef FAST_PICTURESET
+ splitAdd(area.getBounds());
+#else
+ add(area, picture, elapsed, split, false);
+#endif // FAST_PICTURESET
+ } else {
+ SkRegion::Iterator cliperator(area);
+ while (!cliperator.done()) {
+ SkIRect ir = cliperator.rect();
+#ifdef FAST_PICTURESET
+ splitAdd(ir);
+#else
+ SkRegion newArea;
+ newArea.setRect(ir);
+ add(newArea, picture, elapsed, split, false);
+#endif // FAST_PICTURESET
+ cliperator.next();
+ }
+ }
+}
+
+#ifdef FAST_PICTURESET
+
+Bucket* PictureSet::getBucket(int x, int y)
+{
+ // only create buckets for valid, positive coordinates, ignore and return
+ // NULL otherwise
+ if (x < 0 || y < 0)
+ return 0;
+
+ BucketPosition position(x+1, y+1);
+ if (!mBuckets.contains(position)) {
+ XLOG("PictureSet::getBucket(%d, %d) adding new bucket", x, y);
+ Bucket* bucket = new Bucket();
+ mBuckets.add(position, bucket);
+ }
+ return mBuckets.get(position);
+}
+
+void PictureSet::displayBucket(Bucket* bucket)
+{
+ BucketPicture* first = bucket->begin();
+ BucketPicture* last = bucket->end();
+ for (BucketPicture* current = first; current != last; current++) {
+ XLOGC("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x",
+ bucket,
+ current->mArea.fLeft,
+ current->mArea.fTop,
+ current->mArea.fRight,
+ current->mArea.fBottom,
+ current->mArea.width(),
+ current->mArea.height(),
+ current->mPicture,
+ current->mBase);
+ }
+}
+
+void PictureSet::displayBuckets()
+{
+ XLOGC("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this);
+ for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
+ XLOGC("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second);
+ displayBucket(iter->second);
+ }
+ XLOGC("\n****** END OF DISPLAY BUCKETS ******\n\n");
+}
+
+// When we receive an inval in a Bucket, we try to see if we intersect with
+// existing invals/pictures in the Bucket.
+void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect)
+{
+ bool resetBase = false;
+
+ SkIRect totalArea = rect;
+ BucketPicture* first = bucket->begin();
+ BucketPicture* last = bucket->end();
+
+ // If the inval covers a large area of the base inval, let's repaint the
+ // entire bucket.
+ if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY)
+ resetBase = true;
+
+ // let's gather all the BucketPicture intersecting with the new invalidated
+ // area, collect their area and remove their picture
+ for (BucketPicture* current = first; current != last; current++) {
+ bool remove = resetBase;
+ bool intersect = false;
+
+ if (!remove)
+ intersect = SkIRect::Intersects(current->mArea, rect);
+ // If the current picture is not a base, and we intersect, remove it
+ if (!remove && !current->mBase && intersect)
+ remove = true;
+ // If the current picture is a base, check if the new inval completely
+ // contains the base, and if so remove it.
+ if (!remove && current->mBase && rect.contains(current->mArea))
+ remove = true;
+ // If the current picture is a base and it intersects,
+ // also check that it fully covers the bucket -- otherwise,
+ // let's aggregate it with the new inval.
+ if (!remove && current->mBase && intersect
+ && (current->mArea.width() < mBucketSizeX || current->mArea.height() < mBucketSizeY)) {
+ remove = true;
+ }
+
+ if (remove) {
+ totalArea.join(current->mArea);
+ current->mBase = false;
+ current->mArea.setEmpty();
+ SkSafeUnref(current->mPicture);
+ current->mPicture = 0;
+ }
+ }
+
+ // Now, let's add the new BucketPicture to the list, with the correct
+ // area that needs to be repainted
+ SkRegion region;
+ SkIRect area = totalArea;
+ area.offset(dx, dy);
+ BucketPicture picture = { 0, totalArea, area, false };
+
+ bucket->append(picture);
+
+ first = bucket->begin();
+ last = bucket->end();
+
+ bool clearUp = false;
+ if (last - first > MAX_ADDITIONAL_PICTURES) {
+ // too many pictures in the bucket, let's collapse
+ clearUp = true;
+ }
+
+ float bucketBaseArea = 0;
+ float bucketAdditionalArea = 0;
+ for (BucketPicture* current = first; current != last; current++) {
+ float area = current->mArea.width() * current->mArea.height();
+ if (current->mBase)
+ bucketBaseArea += area;
+ else
+ bucketAdditionalArea += area;
+ }
+
+ if (bucketBaseArea > 0 && bucketBaseArea * MAX_ADDITIONAL_AREA <= bucketAdditionalArea) {
+ // additional area too large, not worth maintaining
+ clearUp = true;
+ }
+
+ // To clear things up, we just need to mark the pictures' area as empty
+ // We only keep the base surface.
+ if (clearUp) {
+ for (BucketPicture* current = first; current != last; current++) {
+ if (!current->mBase)
+ current->mArea.setEmpty();
+ SkSafeUnref(current->mPicture);
+ current->mPicture = 0;
+ }
+ }
+
+ // let's do a pass to collapse out empty areas
+ BucketPicture* writer = first;
+ for (BucketPicture* current = first; current != last; current++) {
+ if (current && current->mArea.isEmpty())
+ continue;
+ *writer++ = *current;
+ }
+
+ bucket->shrink(writer - first);
+
+ // let's recompute the bases
+ first = bucket->begin();
+ last = bucket->end();
+ SkRegion drawn;
+ drawn.setEmpty();
+ for (BucketPicture* current = first; current != last; current++) {
+ if (drawn.contains(current->mArea) == false) {
+ current->mBase = true;
+ }
+ drawn.op(current->mArea, SkRegion::kUnion_Op);
+ }
+}
+
+void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect)
+{
+ XLOG("\n--- gatherBucketsForArea for rect %d, %d, %d, %d (%d x %d)",
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ rect.width(), rect.height());
+
+ if (!mBucketSizeX || !mBucketSizeY) {
+ XLOGC("PictureSet::gatherBucketsForArea() called with bad bucket size: x=%d y=%d",
+ mBucketSizeX, mBucketSizeY);
+ return;
+ }
+
+ int x = rect.fLeft;
+ int y = rect.fTop;
+ int firstTileX = rect.fLeft / mBucketSizeX;
+ int firstTileY = rect.fTop / mBucketSizeY;
+ int lastTileX = rect.fRight / mBucketSizeX;
+ int lastTileY = rect.fBottom / mBucketSizeY;
+
+ for (int i = firstTileX; i <= lastTileX; i++) {
+ for (int j = firstTileY; j <= lastTileY; j++) {
+ Bucket* bucket = getBucket(i, j);
+ XLOG("gather bucket %x for %d, %d", bucket, i+1, j+1);
+ if (bucket)
+ list.append(bucket);
+ }
+ }
+}
+
+// When we receive a new inval rect, we first find the Buckets that intersect
+// with it; then we split the original inval into a serie of invals (one for
+// each Bucket we intersect with). We then send that inval to the Bucket.
+void PictureSet::splitAdd(const SkIRect& rect)
+{
+ XLOG("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)",
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ rect.width(), rect.height());
+
+ if (!mBucketSizeX || !mBucketSizeY) {
+ XLOGC("PictureSet::splitAdd() called with bad bucket size: x=%d y=%d",
+ mBucketSizeX, mBucketSizeY);
+ return;
+ }
+
+ // TODO: reuse gatherBucketsForArea() (change Bucket to be a class)
+ int x = rect.fLeft;
+ int y = rect.fTop;
+ int firstTileX = rect.fLeft / mBucketSizeX;
+ int firstTileY = rect.fTop / mBucketSizeY;
+ int lastTileX = rect.fRight / mBucketSizeX;
+ int lastTileY = rect.fBottom / mBucketSizeY;
+
+ XLOG("--- firstTile(%d, %d) lastTile(%d, %d)",
+ firstTileX, firstTileY,
+ lastTileX, lastTileY);
+
+ for (int i = firstTileX; i <= lastTileX; i++) {
+ for (int j = firstTileY; j <= lastTileY; j++) {
+ Bucket* bucket = getBucket(i, j);
+ if (!bucket)
+ continue;
+
+ SkIRect newRect;
+ int deltaX = i * mBucketSizeX;
+ int deltaY = j * mBucketSizeY;
+ int left = (i == firstTileX) ? rect.fLeft - deltaX : 0;
+ int top = (j == firstTileY) ? rect.fTop - deltaY : 0;
+ int right = (i == lastTileX) ? rect.fRight % mBucketSizeX : mBucketSizeX;
+ int bottom = (j == lastTileY) ? rect.fBottom % mBucketSizeY : mBucketSizeY;
+
+ newRect.set(left, top, right, bottom);
+ addToBucket(bucket, deltaX, deltaY, newRect);
+ mUpdatedBuckets.append(bucket);
+ }
+ }
+
+ XLOG("--- splitAdd DONE\n");
+}
+
+#endif // FAST_PICTURESET
+
+// This function is used to maintain the list of Pictures.
+// Pictures contain an SkPicture covering a specific area; some
+// Pictures are "base" Pictures -- i.e. there is no Pictures
+// underneath them.
+// The idea here is to keep a balance between the number of Pictures
+// we have (more Pictures slow us down) and the area of Pictures that
+// need to be repainted (obviously, smaller areas are better).
+// To do so, we try to not update/repaint the base pictures -- by
+// construction, they usually cover a large area (the entire page).
+// We only reset a base picture if the new invalidated area entirely
+// contains it.
+// Most of the time we thus work on smaller pictures on top of the
+// base ones; We compute the total area of all pictures intersecting
+// with the passed invalidated area (as they would need to be invalidated),
+// and use that as the basis for the correct area we want to invalidate
+// (we then can simply delete the pictures we intersect with).
+// In addition, we do a couple of things to limit the total number of pictures
+// we keep in the list:
+// - if the total area of additional textures reach 65% of the base pictures,
+// we delete the additional pictures and mark the base pictures as
+// needing a full repaint
+// - we limit the number of pictures to 32 -- above that, we do the same
+// things (deleting additional pictures + full repaint of base pictures)
+#ifdef FAST_PICTURESET
+#else
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split, bool empty)
+{
+ bool checkForNewBases = false;
+
+ Pictures* first = mPictures.begin();
+ Pictures* last = mPictures.end();
+#ifdef DEBUG
+ XLOG("--- before adding the new inval ---");
+ for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
+ SkIRect currentArea = working->mArea.getBounds();
+ XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
+ working - first,
+ currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
+ currentArea.width(), currentArea.height(),
+ working->mArea.isRect() ? 'Y' : 'N',
+ working->mBase ? 'Y' : 'N');
+ }
+ XLOG("----------------------------------");
+#endif
+
+ // let's gather all the Pictures intersecting with the new invalidated
+ // area, collect their area and remove their picture
+ SkIRect totalArea = area.getBounds();
+ for (Pictures* working = first; working != last; working++) {
+ SkIRect inval = area.getBounds();
+ bool remove = false;
+ if (!working->mBase && working->mArea.intersects(inval))
+ remove = true;
+ if (working->mBase) {
+ SkIRect baseArea = working->mArea.getBounds();
+ if (area.contains(baseArea)) {
+ remove = true;
+ checkForNewBases = true;
+ }
+ }
+
+ if (remove) {
+ SkIRect currentArea = working->mArea.getBounds();
+ if (working->mBase)
+ mBaseArea -= currentArea.width() * currentArea.height();
+ else
+ mAdditionalArea -= currentArea.width() * currentArea.height();
+
+ totalArea.join(currentArea);
+ XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) intersects with the new inval area (%d, %d, %d, %d - %d x %d) (isRect? %c, we remove it",
+ working - first,
+ currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
+ currentArea.width(), currentArea.height(),
+ working->mArea.isRect() ? 'Y' : 'N',
+ inval.fLeft, inval.fTop, inval.fRight, inval.fBottom,
+ inval.width(), inval.height(),
+ area.isRect() ? 'Y' : 'N');
+ working->mArea.setEmpty();
+ SkSafeUnref(working->mPicture);
+ working->mPicture = 0;
+ }
+ }
+
+ // Now we can add the new Picture to the list, with the correct area
+ // that need to be repainted
+ SkRegion collect;
+ collect.setRect(totalArea);
+ Pictures pictureAndBounds = {collect, 0, collect.getBounds(),
+ elapsed, split, false, false, empty};
+
+#ifdef FAST_PICTURESET
+ if (mPictures.size() == 0)
+ checkForNewBases = true;
+#endif
+
+ mPictures.append(pictureAndBounds);
+ mAdditionalArea += totalArea.width() * totalArea.height();
+ last = mPictures.end();
+ first = mPictures.begin();
+
+ // Then, let's see if we have to clear up the pictures in order to keep
+ // the total number of pictures under our limit
+ bool clearUp = false;
+ if (last - first > MAX_ADDITIONAL_PICTURES) {
+ XLOG("--- too many pictures, only keeping the bases : %d", last - first);
+ clearUp = true;
+ }
+
+ if (!clearUp) {
+ if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) {
+ XLOG("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f",
+ MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea);
+ clearUp = true;
+ }
+ }
+
+ if (clearUp) {
+ for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
+ if (!working->mBase)
+ working->mArea.setEmpty();
+ SkSafeUnref(working->mPicture);
+ working->mPicture = 0;
+ }
+ }
+
+#ifdef DEBUG
+ XLOG("--- after adding the new inval, but before collapsing ---");
+ for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
+ SkIRect currentArea = working->mArea.getBounds();
+ XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c",
+ working - first,
+ currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
+ currentArea.width(), currentArea.height(),
+ working->mArea.isRect() ? 'Y' : 'N',
+ working->mBase ? 'Y' : 'N');
+ }
+ XLOG("----------------------------------");
+ XLOG("let's collapse...");
+#endif
+
+ // Finally, let's do a pass to collapse out empty regions
+ Pictures* writer = first;
+ for (Pictures* working = first; working != last; working++) {
+ if (working && working->mArea.isEmpty())
+ continue;
+ *writer++ = *working;
+ }
+ XLOG("shiking of %d elements", writer - first);
+ mPictures.shrink(writer - first);
+
+#ifdef DEBUG
+ XLOG("--- after adding the new inval ---");
+ for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) {
+ SkIRect currentArea = working->mArea.getBounds();
+ XLOG("picture %d (%d, %d, %d, %d - %d x %d) (isRect? %c) base: %c picture %x",
+ working - first,
+ currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom,
+ currentArea.width(), currentArea.height(),
+ working->mArea.isRect() ? 'Y' : 'N',
+ working->mBase ? 'Y' : 'N', working->mPicture);
+ }
+ XLOG("----------------------------------");
+#endif
+
+ // Base pictures might have been removed/added -- let's recompute them
+ SkRegion drawn;
+ if (checkForNewBases) {
+ drawn.setEmpty();
+ Pictures* last = mPictures.end();
+ XLOG("checkForNewBases...");
+ for (Pictures* working = mPictures.begin(); working != last; working++) {
+ SkRegion& area = working->mArea;
+ const SkIRect& a = area.getBounds();
+ if (drawn.contains(working->mArea) == false) {
+ working->mBase = true;
+ float area = a.width() * a.height();
+ mBaseArea += area;
+ mAdditionalArea -= area;
+ }
+ drawn.op(working->mArea, SkRegion::kUnion_Op);
+ }
+ }
+}
+#endif // FAST_PICTURESET
+
+void PictureSet::setDimensions(int width, int height, SkRegion* inval)
+{
+ // Note that setDimensions() may be called by our ctor and should behave accordingly
+ if (mWidth == width && mHeight == height)
+ return;
+ DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
+ mWidth, mHeight, width, height);
+ bool clearCache = false;
+ if (inval) {
+ if (mWidth == width && height > mHeight) { // only grew vertically
+ SkIRect rect;
+ rect.set(0, mHeight, width, height);
+ inval->op(rect, SkRegion::kUnion_Op);
+ } else {
+ clearCache = true;
+ inval->setRect(0, 0, width, height);
+ }
+ }
+#ifdef FAST_PICTURESET
+ // First figure out how large each bucket would be if we used all of the buckets
+ int tmpSizeX = (width + MAX_BUCKET_COUNT_X - 1) / MAX_BUCKET_COUNT_X;
+ int tmpSizeY = (height + MAX_BUCKET_COUNT_Y - 1) / MAX_BUCKET_COUNT_Y;
+
+ // Then round the bucket size up to the nearest chunk
+ int bucketSizeX = ((tmpSizeX - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
+ int bucketSizeY = ((tmpSizeY - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE;
+
+ int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX;
+ int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY;
+
+ // Clear the cache if the horizontal bucket count changed or the vertical
+ // count shrank
+ if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY)
+ clearCache = true;
+
+ // Or if the bucket size changed
+ if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY)
+ clearCache = true;
+
+ XLOG("old width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
+ mWidth, mHeight, mBucketSizeX, mBucketSizeY, mBucketCountX, mBucketCountY, clearCache);
+ XLOG("new width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d",
+ width, height, bucketSizeX, bucketSizeY, bucketCountX, bucketCountY, clearCache);
+#endif
+ if (clearCache)
+ clear();
+ mWidth = width;
+ mHeight = height;
+#ifdef FAST_PICTURESET
+ mBucketSizeX = bucketSizeX;
+ mBucketSizeY = bucketSizeY;
+ mBucketCountX = bucketCountX;
+ mBucketCountY = bucketCountY;
+#endif
+}
+
+void PictureSet::clear()
+{
+ DBG_SET_LOG("");
+#ifdef FAST_PICTURESET
+ for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
+ Bucket* bucket = iter->second;
+ BucketPicture* first = bucket->begin();
+ BucketPicture* last = bucket->end();
+ for (BucketPicture* current = first; current != last; current++) {
+ SkSafeUnref(current->mPicture);
+ current->mPicture = 0;
+ }
+ bucket->clear();
+ }
+ mBuckets.clear();
+ mBucketSizeX = mBucketSizeY = BUCKET_SIZE;
+#else
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++) {
+ working->mArea.setEmpty();
+ SkSafeUnref(working->mPicture);
+ }
+ mPictures.clear();
+#endif // FAST_PICTURESET
+ mWidth = mHeight = 0;
+}
+
+bool PictureSet::draw(SkCanvas* canvas)
+{
+#ifdef FAST_PICTURESET
+ XLOG("PictureSet %x draw on canvas %x", this, canvas);
+ SkRect bounds;
+ if (canvas->getClipBounds(&bounds) == false)
+ return false;
+ SkIRect irect;
+ bounds.roundOut(&irect);
+
+ WTF::Vector<Bucket*> list;
+ gatherBucketsForArea(list, irect);
+
+ XLOG("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size());
+ for (unsigned int i = 0; i < list.size(); i++) {
+ Bucket* bucket = list[i];
+ XLOG("We paint using bucket %x with %d pictures", bucket, bucket->size());
+ for (unsigned int j = 0; j < bucket->size(); j++) {
+ BucketPicture& picture = bucket->at(j);
+ if (!picture.mPicture)
+ continue;
+ int saved = canvas->save();
+ SkRect pathBounds;
+ pathBounds.set(picture.mRealArea);
+ XLOG("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d",
+ j, bucket->size(),
+ picture.mRealArea.fLeft,
+ picture.mRealArea.fTop,
+ picture.mRealArea.fRight,
+ picture.mRealArea.fBottom,
+ picture.mRealArea.width(),
+ picture.mRealArea.height());
+ canvas->clipRect(pathBounds);
+ canvas->translate(pathBounds.fLeft, pathBounds.fTop);
+ canvas->save();
+ canvas->drawPicture(*picture.mPicture);
+ canvas->restoreToCount(saved);
+ }
+ }
+ return false;
+
+#else
+
+ validate(__FUNCTION__);
+ Pictures* first = mPictures.begin();
+ Pictures* last = mPictures.end();
+ Pictures* working;
+ SkRect bounds;
+ if (canvas->getClipBounds(&bounds) == false)
+ return false;
+ SkIRect irect;
+ bounds.roundOut(&irect);
+ for (working = last; working != first; ) {
+ --working;
+ if (working->mArea.contains(irect)) {
+#if PICTURE_SET_DEBUG
+ const SkIRect& b = working->mArea.getBounds();
+ DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
+ " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
+ irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
+#endif
+ first = working;
+ break;
+ }
+ }
+ DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
+ last - mPictures.begin());
+ uint32_t maxElapsed = 0;
+ for (working = first; working != last; working++) {
+ const SkRegion& area = working->mArea;
+ if (area.quickReject(irect)) {
+#if PICTURE_SET_DEBUG
+ const SkIRect& b = area.getBounds();
+ DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
+ " irect={%d,%d,%d,%d}", working - first, working,
+ b.fLeft, b.fTop, b.fRight, b.fBottom,
+ irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
+#endif
+ working->mElapsed = 0;
+ continue;
+ }
+ int saved = canvas->save();
+ SkRect pathBounds;
+ if (area.isComplex()) {
+ SkPath pathClip;
+ area.getBoundaryPath(&pathClip);
+ canvas->clipPath(pathClip);
+ pathBounds = pathClip.getBounds();
+ } else {
+ pathBounds.set(area.getBounds());
+ canvas->clipRect(pathBounds);
+ }
+ canvas->translate(pathBounds.fLeft, pathBounds.fTop);
+ canvas->save();
+ uint32_t startTime = getThreadMsec();
+ canvas->drawPicture(*working->mPicture);
+ size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
+ working->mWroteElapsed = true;
+ if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
+ pathBounds.height() >= MIN_SPLITTABLE))
+ maxElapsed = elapsed;
+ canvas->restoreToCount(saved);
+#define DRAW_TEST_IMAGE 01
+#if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
+ SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
+ canvas->drawColor(color);
+ SkPaint paint;
+ color ^= 0x00ffffff;
+ paint.setColor(color);
+ char location[256];
+ for (int x = area.getBounds().fLeft & ~0x3f;
+ x < area.getBounds().fRight; x += 0x40) {
+ for (int y = area.getBounds().fTop & ~0x3f;
+ y < area.getBounds().fBottom; y += 0x40) {
+ int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
+ canvas->drawText(location, len, x, y, paint);
+ }
+ }
+#endif
+ DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
+ working - first, working,
+ area.getBounds().fLeft, area.getBounds().fTop,
+ area.getBounds().fRight, area.getBounds().fBottom,
+ working->mElapsed, working->mBase ? "true" : "false");
+ }
+ // dump(__FUNCTION__);
+ return maxElapsed >= MAX_DRAW_TIME;
+#endif // FAST_PICTURESET
+}
+
+void PictureSet::dump(const char* label) const
+{
+#if PICTURE_SET_DUMP
+ DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
+ mWidth, mHeight);
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ const SkIRect& bounds = working->mArea.getBounds();
+ const SkIRect& unsplit = working->mUnsplit;
+ MeasureStream measure;
+ if (working->mPicture != NULL)
+ working->mPicture->serialize(&measure);
+ LOGD(" [%d]"
+ " mArea.bounds={%d,%d,r=%d,b=%d}"
+ " mPicture=%p"
+ " mUnsplit={%d,%d,r=%d,b=%d}"
+ " mElapsed=%d"
+ " mSplit=%s"
+ " mWroteElapsed=%s"
+ " mBase=%s"
+ " pict-size=%d",
+ working - mPictures.begin(),
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+ working->mPicture,
+ unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
+ working->mElapsed, working->mSplit ? "true" : "false",
+ working->mWroteElapsed ? "true" : "false",
+ working->mBase ? "true" : "false",
+ measure.mTotal);
+ }
+#endif
+}
+
+class IsEmptyBounder : public SkBounder {
+ virtual bool onIRect(const SkIRect& rect) {
+ return false;
+ }
+};
+
+class IsEmptyCanvas : public SkCanvas {
+public:
+ IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
+ mPicture(picture), mEmpty(true) {
+ setBounder(bounder);
+ }
+
+ void notEmpty() {
+ mEmpty = false;
+ mPicture->abortPlayback();
+ }
+
+ virtual bool clipPath(const SkPath&, SkRegion::Op) {
+ // this can be expensive to actually do, and doesn't affect the
+ // question of emptiness, so we make it a no-op
+ return true;
+ }
+
+ virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
+ const SkMatrix& , const SkPaint& ) {
+ if (bitmap.width() <= 1 || bitmap.height() <= 1)
+ return;
+ DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
+ notEmpty();
+ }
+
+ virtual void drawPaint(const SkPaint& paint) {
+ }
+
+ virtual void drawPath(const SkPath& , const SkPaint& paint) {
+ DBG_SET_LOG("abort");
+ notEmpty();
+ }
+
+ virtual void drawPoints(PointMode , size_t , const SkPoint [],
+ const SkPaint& paint) {
+ }
+
+ virtual void drawRect(const SkRect& , const SkPaint& paint) {
+ // wait for visual content
+ if (paint.getColor() != SK_ColorWHITE)
+ notEmpty();
+ }
+
+ virtual void drawSprite(const SkBitmap& , int , int ,
+ const SkPaint* paint = NULL) {
+ DBG_SET_LOG("abort");
+ notEmpty();
+ }
+
+ virtual void drawText(const void* , size_t byteLength, SkScalar ,
+ SkScalar , const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPosText(const void* , size_t byteLength,
+ const SkPoint [], const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPosTextH(const void* , size_t byteLength,
+ const SkScalar [], SkScalar ,
+ const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawTextOnPath(const void* , size_t byteLength,
+ const SkPath& , const SkMatrix* ,
+ const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPicture(SkPicture& picture) {
+ SkCanvas::drawPicture(picture);
+ }
+
+ SkPicture* mPicture;
+ bool mEmpty;
+};
+
+bool PictureSet::emptyPicture(SkPicture* picture) const
+{
+ IsEmptyBounder isEmptyBounder;
+ IsEmptyCanvas checker(&isEmptyBounder, picture);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
+ checker.setBitmapDevice(bitmap);
+ checker.drawPicture(*picture);
+ return checker.mEmpty;
+}
+
+bool PictureSet::isEmpty() const
+{
+#ifdef FAST_PICTURESET
+ // For now, just assume the pictureset is *not* empty
+ // if the hashmap contains something
+ for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) {
+ if (iter->second->size() > 0)
+ return false;
+ }
+ return true;
+#else
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ if (!working->mEmpty)
+ return false;
+ }
+ return true;
+#endif // FAST_PICTURESET
+}
+
+void PictureSet::set(const PictureSet& src)
+{
+ DBG_SET_LOGD("start %p src=%p", this, &src);
+ clear();
+ setDimensions(src.mWidth, src.mHeight);
+#ifdef FAST_PICTURESET
+ XLOG("\n--- set picture ---");
+ for (BucketMap::const_iterator iter = src.mBuckets.begin();
+ iter != src.mBuckets.end(); ++iter) {
+ Bucket* sourceBucket = iter->second;
+ Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1);
+ BucketPicture* first = sourceBucket->begin();
+ BucketPicture* last = sourceBucket->end();
+ XLOG("set from bucket %x (%d, %d), %d pictures", sourceBucket,
+ iter->first.first, iter->first.second, sourceBucket->size());
+ for (BucketPicture* current = first; current != last; current++) {
+ XLOG("set picture %x from bucket %x in bucket %x (%d, %d)",
+ current->mPicture, sourceBucket, targetBucket,
+ iter->first.first, iter->first.second);
+ SkSafeRef(current->mPicture);
+ BucketPicture picture = { current->mPicture, current->mArea,
+ current->mRealArea, current->mBase };
+ targetBucket->append(picture);
+ }
+ }
+ XLOG("--- DONE set picture ---\n");
+#else
+ const Pictures* last = src.mPictures.end();
+ for (const Pictures* working = src.mPictures.begin(); working != last; working++)
+ add(working);
+ // dump(__FUNCTION__);
+ validate(__FUNCTION__);
+ DBG_SET_LOG("end");
+#endif // FAST_PICTURESET
+}
+
+#ifdef FAST_PICTURESET
+#else
+
+bool PictureSet::reuseSubdivided(const SkRegion& inval)
+{
+ validate(__FUNCTION__);
+
+ if (inval.isComplex())
+ return false;
+ Pictures* working, * last = mPictures.end();
+ const SkIRect& invalBounds = inval.getBounds();
+ bool steal = false;
+ for (working = mPictures.begin(); working != last; working++) {
+ if (working->mSplit && invalBounds == working->mUnsplit) {
+ steal = true;
+ continue;
+ }
+ if (steal == false)
+ continue;
+ SkRegion temp = SkRegion(inval);
+ temp.op(working->mArea, SkRegion::kIntersect_Op);
+ if (temp.isEmpty() || temp == working->mArea)
+ continue;
+ return false;
+ }
+ if (steal == false)
+ return false;
+ for (working = mPictures.begin(); working != last; working++) {
+ if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
+ inval.contains(working->mArea) == false)
+ continue;
+ SkSafeUnref(working->mPicture);
+ working->mPicture = NULL;
+ }
+ return true;
+}
+
+void PictureSet::setDrawTimes(const PictureSet& src)
+{
+ validate(__FUNCTION__);
+ if (mWidth != src.mWidth || mHeight != src.mHeight)
+ return;
+ Pictures* last = mPictures.end();
+ Pictures* working = mPictures.begin();
+ if (working == last)
+ return;
+ const Pictures* srcLast = src.mPictures.end();
+ const Pictures* srcWorking = src.mPictures.begin();
+ for (; srcWorking != srcLast; srcWorking++) {
+ if (srcWorking->mWroteElapsed == false)
+ continue;
+ while ((srcWorking->mArea != working->mArea ||
+ srcWorking->mPicture != working->mPicture)) {
+ if (++working == last)
+ return;
+ }
+ DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
+ this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
+ working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
+ working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
+ working->mElapsed, srcWorking->mElapsed);
+ working->mElapsed = srcWorking->mElapsed;
+ }
+}
+
+void PictureSet::setPicture(size_t i, SkPicture* p)
+{
+ SkSafeUnref(mPictures[i].mPicture);
+ mPictures[i].mPicture = p;
+ mPictures[i].mEmpty = emptyPicture(p);
+}
+
+void PictureSet::split(PictureSet* out) const
+{
+ dump(__FUNCTION__);
+ DBG_SET_LOGD("%p", this);
+ SkIRect totalBounds;
+ out->mWidth = mWidth;
+ out->mHeight = mHeight;
+ totalBounds.set(0, 0, mWidth, mHeight);
+ SkRegion* total = new SkRegion(totalBounds);
+ const Pictures* last = mPictures.end();
+ const Pictures* working;
+ uint32_t balance = 0;
+ int multiUnsplitFastPictures = 0; // > 1 has more than 1
+ for (working = mPictures.begin(); working != last; working++) {
+ if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
+ continue;
+ if (++multiUnsplitFastPictures > 1)
+ break;
+ }
+ for (working = mPictures.begin(); working != last; working++) {
+ uint32_t elapsed = working->mElapsed;
+ if (elapsed < MAX_DRAW_TIME) {
+ bool split = working->mSplit;
+ DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
+ "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
+ total->getBounds().fLeft, total->getBounds().fTop,
+ total->getBounds().fRight, total->getBounds().fBottom,
+ split ? "true" : "false");
+ if (multiUnsplitFastPictures <= 1 || split) {
+ total->op(working->mArea, SkRegion::kDifference_Op);
+ out->add(working->mArea, working->mPicture, elapsed, split,
+ working->mEmpty);
+ } else if (balance < elapsed)
+ balance = elapsed;
+ continue;
+ }
+ total->op(working->mArea, SkRegion::kDifference_Op);
+ const SkIRect& bounds = working->mArea.getBounds();
+ int width = bounds.width();
+ int height = bounds.height();
+ int across = 1;
+ int down = 1;
+ while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
+ if (height >= width) {
+ height >>= 1;
+ down <<= 1;
+ } else {
+ width >>= 1;
+ across <<= 1 ;
+ }
+ if ((elapsed >>= 1) < MAX_DRAW_TIME)
+ break;
+ }
+ width = bounds.width();
+ height = bounds.height();
+ int top = bounds.fTop;
+ for (int indexY = 0; indexY < down; ) {
+ int bottom = bounds.fTop + height * ++indexY / down;
+ int left = bounds.fLeft;
+ for (int indexX = 0; indexX < across; ) {
+ int right = bounds.fLeft + width * ++indexX / across;
+ SkIRect cBounds;
+ cBounds.set(left, top, right, bottom);
+ out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
+ working->mPicture, elapsed, true,
+ (across | down) != 1 ? false : working->mEmpty);
+ left = right;
+ }
+ top = bottom;
+ }
+ }
+ DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
+ this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
+ multiUnsplitFastPictures);
+ if (!total->isEmpty() && multiUnsplitFastPictures > 1)
+ out->add(*total, NULL, balance, false, false);
+ delete total;
+ validate(__FUNCTION__);
+ out->dump("split-out");
+}
+
+#endif // FAST_PICTURESET
+
+bool PictureSet::validate(const char* funct) const
+{
+#ifdef FAST_PICTURESET
+ return true;
+#else
+ bool valid = true;
+#if PICTURE_SET_VALIDATE
+ SkRegion all;
+ const Pictures* first = mPictures.begin();
+ for (const Pictures* working = mPictures.end(); working != first; ) {
+ --working;
+ const SkPicture* pict = working->mPicture;
+ const SkRegion& area = working->mArea;
+ const SkIRect& bounds = area.getBounds();
+ bool localValid = false;
+ if (working->mUnsplit.isEmpty())
+ LOGD("%s working->mUnsplit.isEmpty()", funct);
+ else if (working->mUnsplit.contains(bounds) == false)
+ LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
+ else if (working->mElapsed >= 1000)
+ LOGD("%s working->mElapsed >= 1000", funct);
+ else if ((working->mSplit & 0xfe) != 0)
+ LOGD("%s (working->mSplit & 0xfe) != 0", funct);
+ else if ((working->mWroteElapsed & 0xfe) != 0)
+ LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
+ else if (pict != NULL) {
+ int pictWidth = pict->width();
+ int pictHeight = pict->height();
+ if (pictWidth < bounds.width())
+ LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
+ else if (pictHeight < bounds.height())
+ LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
+ else if (working->mArea.isEmpty())
+ LOGD("%s working->mArea.isEmpty()", funct);
+ else
+ localValid = true;
+ } else
+ localValid = true;
+ working->mArea.validate();
+ if (localValid == false) {
+ if (all.contains(area) == true)
+ LOGD("%s all.contains(area) == true", funct);
+ else
+ localValid = true;
+ }
+ valid &= localValid;
+ all.op(area, SkRegion::kUnion_Op);
+ }
+ const SkIRect& allBounds = all.getBounds();
+ if (valid) {
+ valid = false;
+ if (allBounds.width() != mWidth)
+ LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
+ else if (allBounds.height() != mHeight)
+ LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
+ else
+ valid = true;
+ }
+ while (valid == false)
+ ;
+#endif
+ return valid;
+#endif // FAST_PICTURESET
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/PictureSet.h b/Source/WebKit/android/jni/PictureSet.h
new file mode 100644
index 0000000..fe47361
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2008, 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 PictureSet_h
+#define PictureSet_h
+
+#define PICTURE_SET_DUMP 0
+#define PICTURE_SET_DEBUG 0
+#define PICTURE_SET_VALIDATE 0
+
+#if PICTURE_SET_DEBUG
+#define DBG_SET_LOG(message) LOGD("%s %s", __FUNCTION__, message)
+#define DBG_SET_LOGD(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#define DEBUG_SET_UI_LOGD(...) LOGD(__VA_ARGS__)
+#else
+#define DBG_SET_LOG(message) ((void)0)
+#define DBG_SET_LOGD(format, ...) ((void)0)
+#define DEBUG_SET_UI_LOGD(...) ((void)0)
+#endif
+
+#include "jni.h"
+#include "SkRegion.h"
+#include <wtf/Vector.h>
+#include <wtf/HashMap.h>
+
+// #define FAST_PICTURESET // use a hierarchy of pictures
+
+class SkCanvas;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+#ifdef FAST_PICTURESET
+ struct BucketPicture {
+ SkPicture* mPicture;
+ SkIRect mArea;
+ SkIRect mRealArea;
+ bool mBase;
+ };
+
+ typedef std::pair<int, int> BucketPosition;
+ typedef WTF::Vector<BucketPicture> Bucket;
+ typedef WTF::HashMap<BucketPosition , Bucket* > BucketMap;
+#endif
+
+ class PictureSet {
+ public:
+ PictureSet();
+ PictureSet(const PictureSet& src) { set(src); }
+ PictureSet(SkPicture* picture);
+ virtual ~PictureSet();
+
+#ifdef FAST_PICTURESET
+ void displayBucket(Bucket* bucket);
+ void displayBuckets();
+ WTF::Vector<Bucket*>* bucketsToUpdate() { return &mUpdatedBuckets; }
+ Bucket* getBucket(int x, int y);
+ void addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect);
+ void gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect);
+ void splitAdd(const SkIRect& rect);
+#endif
+
+ void add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split);
+
+ // Update mWidth/mHeight, and adds any additional inval region
+ void setDimensions(int width, int height, SkRegion* inval = 0);
+ void clear();
+ bool draw(SkCanvas* );
+ static PictureSet* GetNativePictureSet(JNIEnv* env, jobject jpic);
+ int height() const { return mHeight; }
+ bool isEmpty() const; // returns true if empty or only trivial content
+ void set(const PictureSet& );
+
+#ifdef FAST_PICTURESET
+#else
+ void add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split, bool empty);
+ const SkIRect& bounds(size_t i) const {
+ return mPictures[i].mArea.getBounds(); }
+ bool reuseSubdivided(const SkRegion& );
+ void setPicture(size_t i, SkPicture* p);
+ void setDrawTimes(const PictureSet& );
+ size_t size() const { return mPictures.size(); }
+ void split(PictureSet* result) const;
+ bool upToDate(size_t i) const { return mPictures[i].mPicture != NULL; }
+#endif
+ int width() const { return mWidth; }
+ void dump(const char* label) const;
+ bool validate(const char* label) const;
+ private:
+ bool emptyPicture(SkPicture* ) const; // true if no text, images, paths
+
+#ifdef FAST_PICTURESET
+ BucketMap mBuckets;
+ WTF::Vector<Bucket*> mUpdatedBuckets;
+ int mBucketSizeX;
+ int mBucketSizeY;
+ int mBucketCountX;
+ int mBucketCountY;
+#else
+ struct Pictures {
+ SkRegion mArea;
+ SkPicture* mPicture;
+ SkIRect mUnsplit;
+ uint32_t mElapsed;
+ bool mSplit : 8;
+ bool mWroteElapsed : 8;
+ bool mBase : 8; // true if nothing is drawn underneath this
+ bool mEmpty : 8; // true if the picture only draws white
+ };
+ void add(const Pictures* temp);
+ WTF::Vector<Pictures> mPictures;
+#endif
+ float mBaseArea;
+ float mAdditionalArea;
+ int mHeight;
+ int mWidth;
+ };
+}
+
+#endif
diff --git a/Source/WebKit/android/jni/ViewStateSerializer.cpp b/Source/WebKit/android/jni/ViewStateSerializer.cpp
index 02ddca6..6b473f5 100644
--- a/Source/WebKit/android/jni/ViewStateSerializer.cpp
+++ b/Source/WebKit/android/jni/ViewStateSerializer.cpp
@@ -23,23 +23,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define LOG_TAG "ViewStateSerializer"
-#define LOG_NDEBUG 1
-
#include "config.h"
#include "BaseLayerAndroid.h"
#include "CreateJavaOutputStreamAdaptor.h"
-#include "FixedPositioning.h"
#include "ImagesManager.h"
-#include "IFrameContentLayerAndroid.h"
-#include "IFrameLayerAndroid.h"
#include "Layer.h"
#include "LayerAndroid.h"
-#include "LayerContent.h"
-#include "PictureLayerContent.h"
+#include "PictureSet.h"
#include "ScrollableLayerAndroid.h"
-#include "SkFlattenable.h"
#include "SkPicture.h"
#include "TilesManager.h"
@@ -47,13 +39,24 @@
#include <JNIHelp.h>
#include <jni.h>
+#ifdef DEBUG
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "ViewStateSerializer", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
namespace android {
enum LayerTypes {
LTNone = 0,
LTLayerAndroid = 1,
LTScrollableLayerAndroid = 2,
- LTFixedLayerAndroid = 3
};
static bool nativeSerializeViewState(JNIEnv* env, jobject, jint jbaseLayer,
@@ -69,14 +72,16 @@ static bool nativeSerializeViewState(JNIEnv* env, jobject, jint jbaseLayer,
#else
stream->write32(0);
#endif
+ SkPicture picture;
+ PictureSet* content = baseLayer->content();
+ baseLayer->drawCanvas(picture.beginRecording(content->width(), content->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag));
+ picture.endRecording();
if (!stream)
return false;
- if (baseLayer->content())
- baseLayer->content()->serialize(stream);
- else
- return false;
+ picture.serialize(stream);
int childCount = baseLayer->countChildren();
- ALOGV("BaseLayer has %d child(ren)", childCount);
+ XLOG("BaseLayer has %d child(ren)", childCount);
stream->write32(childCount);
for (int i = 0; i < childCount; i++) {
LayerAndroid* layer = static_cast<LayerAndroid*>(baseLayer->getChild(i));
@@ -86,28 +91,23 @@ static bool nativeSerializeViewState(JNIEnv* env, jobject, jint jbaseLayer,
return true;
}
-static BaseLayerAndroid* nativeDeserializeViewState(JNIEnv* env, jobject, jint version,
- jobject jstream, jbyteArray jstorage)
+static BaseLayerAndroid* nativeDeserializeViewState(JNIEnv* env, jobject, jobject jstream,
+ jbyteArray jstorage)
{
SkStream* stream = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
if (!stream)
return 0;
+ BaseLayerAndroid* layer = new BaseLayerAndroid();
Color color = stream->readU32();
- SkPicture* picture = new SkPicture(stream);
- PictureLayerContent* content = new PictureLayerContent(picture);
-
- BaseLayerAndroid* layer = new BaseLayerAndroid(content);
+#if USE(ACCELERATED_COMPOSITING)
layer->setBackgroundColor(color);
-
- SkRegion dirtyRegion;
- dirtyRegion.setRect(0, 0, content->width(), content->height());
- layer->markAsDirty(dirtyRegion);
-
- SkSafeUnref(content);
+#endif
+ SkPicture* picture = new SkPicture(stream);
+ layer->setContent(picture);
SkSafeUnref(picture);
int childCount = stream->readS32();
for (int i = 0; i < childCount; i++) {
- LayerAndroid* childLayer = deserializeLayer(version, stream);
+ LayerAndroid* childLayer = deserializeLayer(stream);
if (childLayer)
layer->addChild(childLayer);
}
@@ -242,20 +242,20 @@ void readTransformationMatrix(SkStream *stream, TransformationMatrix& matrix)
void serializeLayer(LayerAndroid* layer, SkWStream* stream)
{
if (!layer) {
- ALOGV("NULL layer!");
+ XLOG("NULL layer!");
stream->write8(LTNone);
return;
}
if (layer->isMedia() || layer->isVideo()) {
- ALOGV("Layer isn't supported for serialization: isMedia: %s, isVideo: %s",
+ XLOG("Layer isn't supported for serialization: isMedia: %s, isVideo: %s",
layer->isMedia() ? "true" : "false",
layer->isVideo() ? "true" : "false");
stream->write8(LTNone);
return;
}
- LayerTypes type = LTLayerAndroid;
- if (layer->contentIsScrollable())
- type = LTScrollableLayerAndroid;
+ LayerTypes type = layer->contentIsScrollable()
+ ? LTScrollableLayerAndroid
+ : LTLayerAndroid;
stream->write8(type);
// Start with Layer fields
@@ -272,43 +272,20 @@ void serializeLayer(LayerAndroid* layer, SkWStream* stream)
// Next up, LayerAndroid fields
stream->writeBool(layer->m_haveClip);
- stream->writeBool(layer->isPositionFixed());
+ stream->writeBool(layer->m_isFixed);
stream->writeBool(layer->m_backgroundColorSet);
- stream->writeBool(layer->isIFrame());
-
- // With the current LayerAndroid hierarchy, LayerAndroid doesn't have
- // those fields anymore. Let's keep the current serialization format for
- // now and output blank fields... not great, but probably better than
- // dealing with multiple versions.
- if (layer->fixedPosition()) {
- FixedPositioning* fixedPosition = layer->fixedPosition();
- writeSkLength(stream, fixedPosition->m_fixedLeft);
- writeSkLength(stream, fixedPosition->m_fixedTop);
- writeSkLength(stream, fixedPosition->m_fixedRight);
- writeSkLength(stream, fixedPosition->m_fixedBottom);
- writeSkLength(stream, fixedPosition->m_fixedMarginLeft);
- writeSkLength(stream, fixedPosition->m_fixedMarginTop);
- writeSkLength(stream, fixedPosition->m_fixedMarginRight);
- writeSkLength(stream, fixedPosition->m_fixedMarginBottom);
- writeSkRect(stream, fixedPosition->m_fixedRect);
- stream->write32(fixedPosition->m_renderLayerPos.x());
- stream->write32(fixedPosition->m_renderLayerPos.y());
- } else {
- SkLength length;
- SkRect rect;
- writeSkLength(stream, length); // fixedLeft
- writeSkLength(stream, length); // fixedTop
- writeSkLength(stream, length); // fixedRight
- writeSkLength(stream, length); // fixedBottom
- writeSkLength(stream, length); // fixedMarginLeft
- writeSkLength(stream, length); // fixedMarginTop
- writeSkLength(stream, length); // fixedMarginRight
- writeSkLength(stream, length); // fixedMarginBottom
- writeSkRect(stream, rect); // fixedRect
- stream->write32(0); // renderLayerPos.x()
- stream->write32(0); // renderLayerPos.y()
- }
-
+ stream->writeBool(layer->m_isIframe);
+ writeSkLength(stream, layer->m_fixedLeft);
+ writeSkLength(stream, layer->m_fixedTop);
+ writeSkLength(stream, layer->m_fixedRight);
+ writeSkLength(stream, layer->m_fixedBottom);
+ writeSkLength(stream, layer->m_fixedMarginLeft);
+ writeSkLength(stream, layer->m_fixedMarginTop);
+ writeSkLength(stream, layer->m_fixedMarginRight);
+ writeSkLength(stream, layer->m_fixedMarginBottom);
+ writeSkRect(stream, layer->m_fixedRect);
+ stream->write32(layer->m_renderLayerPos.x());
+ stream->write32(layer->m_renderLayerPos.y());
stream->writeBool(layer->m_backfaceVisibility);
stream->writeBool(layer->m_visible);
stream->write32(layer->m_backgroundColor);
@@ -328,10 +305,10 @@ void serializeLayer(LayerAndroid* layer, SkWStream* stream)
stream->write32(buffer.size());
buffer.writeToStream(stream);
}
- bool hasRecordingPicture = layer->m_content != 0 && !layer->m_content->isEmpty();
+ bool hasRecordingPicture = layer->m_recordingPicture != 0;
stream->writeBool(hasRecordingPicture);
if (hasRecordingPicture)
- layer->m_content->serialize(stream);
+ layer->m_recordingPicture->serialize(stream);
// TODO: support m_animations (maybe?)
stream->write32(0); // placeholder for m_animations.size();
writeTransformationMatrix(stream, layer->m_transform);
@@ -350,7 +327,7 @@ void serializeLayer(LayerAndroid* layer, SkWStream* stream)
serializeLayer(layer->getChild(i), stream);
}
-LayerAndroid* deserializeLayer(int version, SkStream* stream)
+LayerAndroid* deserializeLayer(SkStream* stream)
{
int type = stream->readU8();
if (type == LTNone)
@@ -362,7 +339,7 @@ LayerAndroid* deserializeLayer(int version, SkStream* stream)
else if (type == LTScrollableLayerAndroid)
layer = new ScrollableLayerAndroid((RenderLayer*) 0);
else {
- ALOGV("Unexpected layer type: %d, aborting!", type);
+ XLOG("Unexpected layer type: %d, aborting!", type);
return 0;
}
@@ -377,55 +354,20 @@ LayerAndroid* deserializeLayer(int version, SkStream* stream)
// LayerAndroid fields
layer->m_haveClip = stream->readBool();
-
- // Keep the legacy serialization/deserialization format...
- bool isFixed = stream->readBool();
-
+ layer->m_isFixed = stream->readBool();
layer->m_backgroundColorSet = stream->readBool();
-
- bool isIframe = stream->readBool();
- // If we are a scrollable layer android, we are an iframe content
- if (isIframe && type == LTScrollableLayerAndroid) {
- IFrameContentLayerAndroid* iframeContent = new IFrameContentLayerAndroid(*layer);
- layer->unref();
- layer = iframeContent;
- } else if (isIframe) { // otherwise we are just the iframe (we use it to compute offset)
- IFrameLayerAndroid* iframe = new IFrameLayerAndroid(*layer);
- layer->unref();
- layer = iframe;
- }
-
- if (isFixed) {
- FixedPositioning* fixedPosition = new FixedPositioning(layer);
-
- fixedPosition->m_fixedLeft = readSkLength(stream);
- fixedPosition->m_fixedTop = readSkLength(stream);
- fixedPosition->m_fixedRight = readSkLength(stream);
- fixedPosition->m_fixedBottom = readSkLength(stream);
- fixedPosition->m_fixedMarginLeft = readSkLength(stream);
- fixedPosition->m_fixedMarginTop = readSkLength(stream);
- fixedPosition->m_fixedMarginRight = readSkLength(stream);
- fixedPosition->m_fixedMarginBottom = readSkLength(stream);
- fixedPosition->m_fixedRect = readSkRect(stream);
- fixedPosition->m_renderLayerPos.setX(stream->readS32());
- fixedPosition->m_renderLayerPos.setY(stream->readS32());
-
- layer->setFixedPosition(fixedPosition);
- } else {
- // Not a fixed element, bypass the values in the stream
- readSkLength(stream); // fixedLeft
- readSkLength(stream); // fixedTop
- readSkLength(stream); // fixedRight
- readSkLength(stream); // fixedBottom
- readSkLength(stream); // fixedMarginLeft
- readSkLength(stream); // fixedMarginTop
- readSkLength(stream); // fixedMarginRight
- readSkLength(stream); // fixedMarginBottom
- readSkRect(stream); // fixedRect
- stream->readS32(); // renderLayerPos.x()
- stream->readS32(); // renderLayerPos.y()
- }
-
+ layer->m_isIframe = stream->readBool();
+ layer->m_fixedLeft = readSkLength(stream);
+ layer->m_fixedTop = readSkLength(stream);
+ layer->m_fixedRight = readSkLength(stream);
+ layer->m_fixedBottom = readSkLength(stream);
+ layer->m_fixedMarginLeft = readSkLength(stream);
+ layer->m_fixedMarginTop = readSkLength(stream);
+ layer->m_fixedMarginRight = readSkLength(stream);
+ layer->m_fixedMarginBottom = readSkLength(stream);
+ layer->m_fixedRect = readSkRect(stream);
+ layer->m_renderLayerPos.setX(stream->readS32());
+ layer->m_renderLayerPos.setY(stream->readS32());
layer->m_backfaceVisibility = stream->readBool();
layer->m_visible = stream->readBool();
layer->m_backgroundColor = stream->readU32();
@@ -446,11 +388,7 @@ LayerAndroid* deserializeLayer(int version, SkStream* stream)
}
bool hasRecordingPicture = stream->readBool();
if (hasRecordingPicture) {
- SkPicture* picture = new SkPicture(stream);
- PictureLayerContent* content = new PictureLayerContent(picture);
- layer->setContent(content);
- SkSafeUnref(content);
- SkSafeUnref(picture);
+ layer->m_recordingPicture = new SkPicture(stream);
}
int animationCount = stream->readU32(); // TODO: Support (maybe?)
readTransformationMatrix(stream, layer->m_transform);
@@ -466,11 +404,12 @@ LayerAndroid* deserializeLayer(int version, SkStream* stream)
}
int childCount = stream->readU32();
for (int i = 0; i < childCount; i++) {
- LayerAndroid *childLayer = deserializeLayer(version, stream);
+ LayerAndroid *childLayer = deserializeLayer(stream);
if (childLayer)
layer->addChild(childLayer);
}
- ALOGV("Created layer with id %d", layer->uniqueId());
+ layer->needsRepaint();
+ XLOG("Created layer with id %d", layer->uniqueId());
return layer;
}
@@ -480,7 +419,7 @@ LayerAndroid* deserializeLayer(int version, SkStream* stream)
static JNINativeMethod gSerializerMethods[] = {
{ "nativeSerializeViewState", "(ILjava/io/OutputStream;[B)Z",
(void*) nativeSerializeViewState },
- { "nativeDeserializeViewState", "(ILjava/io/InputStream;[B)I",
+ { "nativeDeserializeViewState", "(Ljava/io/InputStream;[B)I",
(void*) nativeDeserializeViewState },
};
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
index 4ce3d8e..d53ddb6 100644
--- a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -60,8 +60,6 @@
#include "IconDatabase.h"
#include "Image.h"
#include "InspectorClientAndroid.h"
-#include "JavaNPObjectV8.h"
-#include "JavaInstanceJobjectV8.h"
#include "KURL.h"
#include "Page.h"
#include "PageCache.h"
@@ -83,6 +81,7 @@
#include "WebArchiveAndroid.h"
#include "WebCache.h"
#include "WebCoreJni.h"
+#include "WebCoreResourceLoader.h"
#include "WebHistory.h"
#include "WebIconDatabase.h"
#include "WebFrameView.h"
@@ -100,13 +99,30 @@
#include <android_runtime/android_util_AssetManager.h>
#include <openssl/x509.h>
#include <utils/misc.h>
-#include <androidfw/AssetManager.h>
+#include <utils/AssetManager.h>
#include <wtf/CurrentTime.h>
#include <wtf/Platform.h>
#include <wtf/text/AtomicString.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
+#if USE(JSC)
+#include "GCController.h"
+#include "JSDOMWindow.h"
+#include "JavaInstanceJSC.h"
+#include <runtime_object.h>
+#include <runtime_root.h>
+#include <runtime/JSLock.h>
+#elif USE(V8)
+#include "JavaNPObjectV8.h"
+#include "JavaInstanceJobjectV8.h"
+#include "V8Counters.h"
+#endif // USE(JSC)
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
#if ENABLE(WEB_AUTOFILL)
#include "autofill/WebAutofill.h"
#endif
@@ -228,6 +244,8 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page*
mJavaFrame = new JavaBrowserFrame;
mJavaFrame->mObj = env->NewWeakGlobalRef(obj);
mJavaFrame->mHistoryList = env->NewWeakGlobalRef(historyList);
+ mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BJIZZZLjava/lang/String;Ljava/lang/String;)Landroid/webkit/LoadListener;");
mJavaFrame->mMaybeSavePassword = env->GetMethodID(clazz, "maybeSavePassword",
"([BLjava/lang/String;Ljava/lang/String;)V");
mJavaFrame->mShouldInterceptRequest =
@@ -283,39 +301,41 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page*
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
env->DeleteLocalRef(clazz);
- ALOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword");
- ALOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest");
- ALOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted");
- ALOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted");
- ALOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished");
- ALOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError");
- ALOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle");
- ALOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared");
- ALOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress");
- ALOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon");
- ALOG_ASSERT(mJavaFrame->mDidReceiveTouchIconUrl, "Could not find method didReceiveTouchIconUrl");
- ALOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory");
- ALOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl");
- ALOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow");
- ALOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow");
- ALOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission");
- ALOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus");
- ALOG_ASSERT(mJavaFrame->mGetRawResFilename, "Could not find method getRawResFilename");
- ALOG_ASSERT(mJavaFrame->mDensity, "Could not find method density");
- ALOG_ASSERT(mJavaFrame->mGetFileSize, "Could not find method getFileSize");
- ALOG_ASSERT(mJavaFrame->mGetFile, "Could not find method getFile");
- ALOG_ASSERT(mJavaFrame->mDidReceiveAuthenticationChallenge, "Could not find method didReceiveAuthenticationChallenge");
- ALOG_ASSERT(mJavaFrame->mReportSslCertError, "Could not find method reportSslCertError");
- ALOG_ASSERT(mJavaFrame->mRequestClientCert, "Could not find method requestClientCert");
- ALOG_ASSERT(mJavaFrame->mDownloadStart, "Could not find method downloadStart");
- ALOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData");
- ALOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading");
- ALOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate");
- ALOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData");
- ALOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData");
- ALOG_ASSERT(mJavaFrame->mAutoLogin, "Could not find method autoLogin");
+ LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource");
+ LOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword");
+ LOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest");
+ LOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted");
+ LOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted");
+ LOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished");
+ LOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError");
+ LOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle");
+ LOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared");
+ LOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress");
+ LOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon");
+ LOG_ASSERT(mJavaFrame->mDidReceiveTouchIconUrl, "Could not find method didReceiveTouchIconUrl");
+ LOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory");
+ LOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl");
+ LOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow");
+ LOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow");
+ LOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission");
+ LOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus");
+ LOG_ASSERT(mJavaFrame->mGetRawResFilename, "Could not find method getRawResFilename");
+ LOG_ASSERT(mJavaFrame->mDensity, "Could not find method density");
+ LOG_ASSERT(mJavaFrame->mGetFileSize, "Could not find method getFileSize");
+ LOG_ASSERT(mJavaFrame->mGetFile, "Could not find method getFile");
+ LOG_ASSERT(mJavaFrame->mDidReceiveAuthenticationChallenge, "Could not find method didReceiveAuthenticationChallenge");
+ LOG_ASSERT(mJavaFrame->mReportSslCertError, "Could not find method reportSslCertError");
+ LOG_ASSERT(mJavaFrame->mRequestClientCert, "Could not find method requestClientCert");
+ LOG_ASSERT(mJavaFrame->mDownloadStart, "Could not find method downloadStart");
+ LOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData");
+ LOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading");
+ LOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate");
+ LOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData");
+ LOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData");
+ LOG_ASSERT(mJavaFrame->mAutoLogin, "Could not find method autoLogin");
mUserAgent = WTF::String();
+ mUserInitiatedAction = false;
mBlockNetworkLoads = false;
m_renderSkins = 0;
}
@@ -342,14 +362,14 @@ WebFrame* WebFrame::getWebFrame(const WebCore::Frame* frame)
static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHeaderMap& map)
{
jclass mapClass = env->FindClass("java/util/HashMap");
- ALOG_ASSERT(mapClass, "Could not find HashMap class!");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
- ALOG_ASSERT(init, "Could not find constructor for HashMap");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
jobject hashMap = env->NewObject(mapClass, init, map.size());
- ALOG_ASSERT(hashMap, "Could not create a new HashMap");
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
jmethodID put = env->GetMethodID(mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
- ALOG_ASSERT(put, "Could not find put method on HashMap");
+ LOG_ASSERT(put, "Could not find put method on HashMap");
WebCore::HTTPHeaderMap::const_iterator end = map.end();
for (WebCore::HTTPHeaderMap::const_iterator i = map.begin(); i != end; ++i) {
@@ -396,10 +416,104 @@ private:
int m_size;
};
+PassRefPtr<WebCore::ResourceLoaderAndroid>
+WebFrame::startLoadingResource(WebCore::ResourceHandle* loader,
+ const WebCore::ResourceRequest& request,
+ bool mainResource,
+ bool synchronous)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: startLoadingResource(%p, %s)",
+ loader, request.url().string().latin1().data());
+
+ JNIEnv* env = getJNIEnv();
+ AutoJObject javaFrame = mJavaFrame->frame(env);
+ if (!javaFrame.get())
+ return 0;
+
+ WTF::String method = request.httpMethod();
+ WebCore::HTTPHeaderMap headers = request.httpHeaderFields();
+
+ WTF::String urlStr = request.url().string();
+ int colon = urlStr.find(':');
+ bool allLower = true;
+ for (int index = 0; index < colon; index++) {
+ UChar ch = urlStr[index];
+ if (!WTF::isASCIIAlpha(ch))
+ break;
+ allLower &= WTF::isASCIILower(ch);
+ if (index == colon - 1 && !allLower) {
+ urlStr = urlStr.substring(0, colon).lower()
+ + urlStr.substring(colon);
+ }
+ }
+ LOGV("%s lower=%s", __FUNCTION__, urlStr.latin1().data());
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jstring jMethodStr = NULL;
+ if (!method.isEmpty())
+ jMethodStr = wtfStringToJstring(env, method);
+ WebCore::FormData* formdata = request.httpBody();
+ jbyteArray jPostDataStr = getPostData(request);
+ jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers);
+
+ // Convert the WebCore Cache Policy to a WebView Cache Policy.
+ int cacheMode = 0; // WebSettings.LOAD_NORMAL
+ switch (request.cachePolicy()) {
+ case WebCore::ReloadIgnoringCacheData:
+ cacheMode = 2; // WebSettings.LOAD_NO_CACHE
+ break;
+ case WebCore::ReturnCacheDataDontLoad:
+ cacheMode = 3; // WebSettings.LOAD_CACHE_ONLY
+ break;
+ case WebCore::ReturnCacheDataElseLoad:
+ cacheMode = 1; // WebSettings.LOAD_CACHE_ELSE_NETWORK
+ break;
+ case WebCore::UseProtocolCachePolicy:
+ default:
+ break;
+ }
+
+ LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode);
+
+ ResourceHandleInternal* loaderInternal = loader->getInternal();
+ jstring jUsernameString = loaderInternal->m_user.isEmpty() ?
+ NULL : wtfStringToJstring(env, loaderInternal->m_user);
+ jstring jPasswordString = loaderInternal->m_pass.isEmpty() ?
+ NULL : wtfStringToJstring(env, loaderInternal->m_pass);
+
+ bool isUserGesture = UserGestureIndicator::processingUserGesture();
+ jobject jLoadListener =
+ env->CallObjectMethod(javaFrame.get(), mJavaFrame->mStartLoadingResource,
+ (int)loader, jUrlStr, jMethodStr, jHeaderMap,
+ jPostDataStr, formdata ? formdata->identifier(): 0,
+ cacheMode, mainResource, isUserGesture,
+ synchronous, jUsernameString, jPasswordString);
+
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jMethodStr);
+ env->DeleteLocalRef(jPostDataStr);
+ env->DeleteLocalRef(jHeaderMap);
+ env->DeleteLocalRef(jUsernameString);
+ env->DeleteLocalRef(jPasswordString);
+ if (checkException(env))
+ return 0;
+
+ PassRefPtr<WebCore::ResourceLoaderAndroid> h;
+ if (jLoadListener)
+ h = WebCoreResourceLoader::create(env, jLoadListener);
+ env->DeleteLocalRef(jLoadListener);
+ return h;
+}
+
UrlInterceptResponse*
WebFrame::shouldInterceptRequest(const WTF::String& url)
{
- ALOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data());
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data());
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
@@ -420,7 +534,10 @@ void
WebFrame::reportError(int errorCode, const WTF::String& description,
const WTF::String& failingUrl)
{
- ALOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -436,6 +553,7 @@ WebFrame::reportError(int errorCode, const WTF::String& description,
WTF::String
WebFrame::convertIDNToUnicode(const WebCore::KURL& url) {
WTF::String converted = url.string();
+#if USE(CHROME_NETWORK_STACK)
const WTF::String host = url.host();
if (host.find("xn--") == notFound) // no punycode IDN found.
return converted;
@@ -448,12 +566,16 @@ WebFrame::convertIDNToUnicode(const WebCore::KURL& url) {
newUrl.setHost(convertedHost);
converted = newUrl.string();
}
+#endif
return converted;
}
void
WebFrame::loadStarted(WebCore::Frame* frame)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -467,7 +589,7 @@ WebFrame::loadStarted(WebCore::Frame* frame)
const WebCore::KURL& url = documentLoader->url();
if (url.isEmpty())
return;
- ALOGV("::WebCore:: loadStarted %s", url.string().ascii().data());
+ LOGV("::WebCore:: loadStarted %s", url.string().ascii().data());
bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
WebCore::FrameLoadType loadType = frame->loader()->loadType();
@@ -487,7 +609,7 @@ WebFrame::loadStarted(WebCore::Frame* frame)
WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(urlString, WebCore::IntSize(16, 16));
if (icon)
favicon = webcoreImageToJavaBitmap(env, icon);
- ALOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data());
+ LOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data());
}
jstring urlStr = wtfStringToJstring(env, urlString);
@@ -511,6 +633,9 @@ WebFrame::loadStarted(WebCore::Frame* frame)
void
WebFrame::transitionToCommitted(WebCore::Frame* frame)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -525,6 +650,9 @@ WebFrame::transitionToCommitted(WebCore::Frame* frame)
void
WebFrame::didFinishLoad(WebCore::Frame* frame)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -539,7 +667,7 @@ WebFrame::didFinishLoad(WebCore::Frame* frame)
const WebCore::KURL& url = documentLoader->url();
if (url.isEmpty())
return;
- ALOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data());
+ LOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data());
bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
WebCore::FrameLoadType loadType = loader->loadType();
@@ -553,7 +681,10 @@ WebFrame::didFinishLoad(WebCore::Frame* frame)
void
WebFrame::addHistoryItem(WebCore::HistoryItem* item)
{
- ALOGV("::WebCore:: addHistoryItem");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: addHistoryItem");
JNIEnv* env = getJNIEnv();
WebHistory::AddItem(mJavaFrame->history(env), item);
}
@@ -561,7 +692,10 @@ WebFrame::addHistoryItem(WebCore::HistoryItem* item)
void
WebFrame::removeHistoryItem(int index)
{
- ALOGV("::WebCore:: removeHistoryItem at %d", index);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: removeHistoryItem at %d", index);
JNIEnv* env = getJNIEnv();
WebHistory::RemoveItem(mJavaFrame->history(env), index);
}
@@ -569,7 +703,10 @@ WebFrame::removeHistoryItem(int index)
void
WebFrame::updateHistoryIndex(int newIndex)
{
- ALOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
JNIEnv* env = getJNIEnv();
WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
}
@@ -577,8 +714,11 @@ WebFrame::updateHistoryIndex(int newIndex)
void
WebFrame::setTitle(const WTF::String& title)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
#ifndef NDEBUG
- ALOGV("setTitle(%s)", title.ascii().data());
+ LOGV("setTitle(%s)", title.ascii().data());
#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
@@ -595,7 +735,10 @@ WebFrame::setTitle(const WTF::String& title)
void
WebFrame::windowObjectCleared(WebCore::Frame* frame)
{
- ALOGV("::WebCore:: windowObjectCleared");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: windowObjectCleared");
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -608,6 +751,9 @@ WebFrame::windowObjectCleared(WebCore::Frame* frame)
void
WebFrame::setProgress(float newProgress)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -627,7 +773,10 @@ WebFrame::userAgentForURL(const WebCore::KURL* url)
void
WebFrame::didReceiveIcon(WebCore::Image* icon)
{
- ALOG_ASSERT(icon, "DidReceiveIcon called without an image!");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOG_ASSERT(icon, "DidReceiveIcon called without an image!");
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -645,6 +794,9 @@ WebFrame::didReceiveIcon(WebCore::Image* icon)
void
WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -660,6 +812,9 @@ WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed)
void
WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -676,6 +831,9 @@ WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
bool
WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -716,6 +874,9 @@ WebFrame::shouldSaveFormData()
WebCore::Frame*
WebFrame::createWindow(bool dialog, bool userGesture)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -729,6 +890,9 @@ WebFrame::createWindow(bool dialog, bool userGesture)
void
WebFrame::requestFocus() const
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -740,6 +904,9 @@ WebFrame::requestFocus() const
void
WebFrame::closeWindow(WebViewCore* webViewCore)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
assert(webViewCore);
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
@@ -758,6 +925,9 @@ struct PolicyFunctionWrapper {
void
WebFrame::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -791,9 +961,13 @@ WebFrame::density() const
return dpi;
}
+#if USE(CHROME_NETWORK_STACK)
void
WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials, bool suppressDialog)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -811,6 +985,9 @@ WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const st
void
WebFrame::reportSslCertError(WebUrlLoaderClient* client, int error, const std::string& cert, const std::string& url)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -831,6 +1008,9 @@ WebFrame::reportSslCertError(WebUrlLoaderClient* client, int error, const std::s
void
WebFrame::requestClientCert(WebUrlLoaderClient* client, const std::string& hostAndPort)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
int jHandle = reinterpret_cast<int>(client);
@@ -844,6 +1024,9 @@ WebFrame::requestClientCert(WebUrlLoaderClient* client, const std::string& hostA
void
WebFrame::downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, long long contentLength)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -864,6 +1047,9 @@ WebFrame::downloadStart(const std::string& url, const std::string& userAgent, co
void
WebFrame::didReceiveData(const char* data, int size) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -878,6 +1064,9 @@ WebFrame::didReceiveData(const char* data, int size) {
void
WebFrame::didFinishLoading() {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -889,6 +1078,9 @@ WebFrame::didFinishLoading() {
void WebFrame::setCertificate(const std::string& cert)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -902,9 +1094,13 @@ void WebFrame::setCertificate(const std::string& cert)
checkException(env);
}
+#endif // USE(CHROME_NETWORK_STACK)
void WebFrame::autoLogin(const std::string& loginHeader)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimerCoutner::JavaCallbackTimeCounter);
+#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
@@ -978,7 +1174,7 @@ void WebFrame::maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceR
bool WebFrame::getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password)
{
bool found = false;
- WTF::RefPtr<WebCore::HTMLCollection> form = frame->document()->forms();
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = frame->document()->forms();
WebCore::Node* node = form->firstItem();
while (node && !found && !node->namespaceURI().isNull() &&
!node->namespaceURI().isEmpty()) {
@@ -1075,10 +1271,13 @@ jbyteArray WebFrame::getPostData(const WebCore::ResourceRequest& request)
static void CallPolicyFunction(JNIEnv* env, jobject obj, jint func, jint decision)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!");
PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func;
- ALOG_ASSERT(pFunc, "nativeCallPolicyFunction must take a valid function pointer!");
+ LOG_ASSERT(pFunc, "nativeCallPolicyFunction must take a valid function pointer!");
// If we are resending the form then we should reset the multiple submission protection.
if (decision == WebCore::PolicyUse)
@@ -1091,9 +1290,17 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss
{
ScriptController::initializeThreading();
+#if USE(CHROME_NETWORK_STACK)
// needs to be called before any other chromium code
initChromium();
+#endif
+#ifdef ANDROID_INSTRUMENT
+#if USE(V8)
+ V8Counters::initCounters();
+#endif
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
// Create a new page
ChromeClientAndroid* chromeC = new ChromeClientAndroid;
EditorClientAndroid* editorC = new EditorClientAndroid;
@@ -1155,7 +1362,7 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss
WebCore::SecurityOrigin::setLocalLoadPolicy(
WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData);
- ALOGV("::WebCore:: createFrame %p", frame);
+ LOGV("::WebCore:: createFrame %p", frame);
// Set the mNativeFrame field in Frame
SET_NATIVE_FRAME(env, obj, (int)frame);
@@ -1163,7 +1370,7 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss
String directory = webFrame->getRawResourceFilename(
WebCore::PlatformBridge::DrawableDir);
if (directory.isEmpty())
- ALOGE("Can't find the drawable directory");
+ LOGE("Can't find the drawable directory");
else {
// Initialize our skinning classes
webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(directory));
@@ -1177,10 +1384,13 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss
static void DestroyFrame(JNIEnv* env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!");
- ALOGV("::WebCore:: deleting frame %p", pFrame);
+ LOGV("::WebCore:: deleting frame %p", pFrame);
WebCore::FrameView* view = pFrame->view();
view->ref();
@@ -1205,8 +1415,11 @@ static void DestroyFrame(JNIEnv* env, jobject obj)
static void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");
WTF::String webcoreUrl = jstringToWtfString(env, url);
WebCore::KURL kurl(WebCore::KURL(), webcoreUrl);
@@ -1250,14 +1463,17 @@ static void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers)
env->DeleteLocalRef(set);
env->DeleteLocalRef(mapClass);
}
- ALOGV("LoadUrl %s", kurl.string().latin1().data());
+ LOGV("LoadUrl %s", kurl.string().latin1().data());
pFrame->loader()->load(request, false);
}
static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativePostUrl must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativePostUrl must take a valid frame pointer!");
WebCore::KURL kurl(WebCore::KURL(), jstringToWtfString(env, url));
WebCore::ResourceRequest request(kurl);
@@ -1275,7 +1491,7 @@ static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData)
env->ReleaseByteArrayElements(postData, bytes, 0);
}
- ALOGV("PostUrl %s", kurl.string().latin1().data());
+ LOGV("PostUrl %s", kurl.string().latin1().data());
WebCore::FrameLoadRequest frameRequest(pFrame->document()->securityOrigin(), request);
pFrame->loader()->loadFrameRequest(frameRequest, false, false, 0, 0, WebCore::SendReferrer);
}
@@ -1283,19 +1499,22 @@ static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData)
static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data,
jstring mimeType, jstring encoding, jstring failUrl)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!");
// Setup the resource request
WebCore::ResourceRequest request(jstringToWtfString(env, baseUrl));
// Setup the substituteData
- WTF::CString cData = jstringToWtfString(env, data).utf8();
- const char* dataStr = cData.data();
- WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer =
+ const char* dataStr = env->GetStringUTFChars(data, NULL);
+ WTF::PassRefPtr<WebCore::SharedBuffer> sharedBuffer =
WebCore::SharedBuffer::create();
- ALOG_ASSERT(dataStr, "nativeLoadData has a null data string.");
- sharedBuffer->append(dataStr, strlen(dataStr)); // copy dataStr
+ LOG_ASSERT(dataStr, "nativeLoadData has a null data string.");
+ sharedBuffer->append(dataStr, strlen(dataStr));
+ env->ReleaseStringUTFChars(data, dataStr);
WebCore::SubstituteData substituteData(sharedBuffer,
jstringToWtfString(env, mimeType), jstringToWtfString(env, encoding),
@@ -1307,9 +1526,12 @@ static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data,
static void StopLoading(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!");
- ALOGV("::WebCore:: stopLoading %p", pFrame);
+ LOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!");
+ LOGV("::WebCore:: stopLoading %p", pFrame);
// Stop loading the page and do not send an unload event
pFrame->loader()->stopForUserCancel();
@@ -1357,7 +1579,7 @@ static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboole
{
#if ENABLE(WEB_ARCHIVE)
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeSaveWebArchive must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeSaveWebArchive must take a valid frame pointer!");
String mimeType = pFrame->loader()->documentLoader()->mainResource()->mimeType();
if ((mimeType != "text/html") && (mimeType != "application/xhtml+xml"))
return NULL;
@@ -1375,7 +1597,7 @@ static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboole
}
if (filename.isNull() || filename.isEmpty()) {
- ALOGD("saveWebArchive: Failed to select a filename to save.");
+ LOGD("saveWebArchive: Failed to select a filename to save.");
releaseCharactersForJStringInEnv(env, basename, basenameNative);
return NULL;
}
@@ -1383,7 +1605,7 @@ static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboole
const int noCompression = 0;
xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.utf8().data(), noCompression);
if (writer == NULL) {
- ALOGD("saveWebArchive: Failed to initialize xml writer.");
+ LOGD("saveWebArchive: Failed to initialize xml writer.");
releaseCharactersForJStringInEnv(env, basename, basenameNative);
return NULL;
}
@@ -1404,8 +1626,11 @@ static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboole
static jstring ExternalRepresentation(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "android_webcore_nativeExternalRepresentation must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "android_webcore_nativeExternalRepresentation must take a valid frame pointer!");
// Request external representation of the render tree
WTF::String renderDump = WebCore::externalRepresentation(pFrame);
@@ -1436,8 +1661,11 @@ static StringBuilder FrameAsText(WebCore::Frame *pFrame, jboolean dumpChildFrame
static jstring DocumentAsText(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
WTF::String renderDump = FrameAsText(pFrame, false /* dumpChildFrames */).toString();
return wtfStringToJstring(env, renderDump);
@@ -1445,8 +1673,11 @@ static jstring DocumentAsText(JNIEnv *env, jobject obj)
static jstring ChildFramesAsText(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
StringBuilder renderDumpBuilder;
for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) {
@@ -1458,8 +1689,11 @@ static jstring ChildFramesAsText(JNIEnv *env, jobject obj)
static void Reload(JNIEnv *env, jobject obj, jboolean allowStale)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!");
WebCore::FrameLoader* loader = pFrame->loader();
if (allowStale) {
@@ -1475,8 +1709,11 @@ static void Reload(JNIEnv *env, jobject obj, jboolean allowStale)
static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!");
if (pos == 1)
pFrame->page()->goForward();
@@ -1488,8 +1725,11 @@ static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos)
static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj, jstring script)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!");
WebCore::ScriptValue value =
pFrame->script()->executeScript(jstringToWtfString(env, script), true);
@@ -1503,16 +1743,32 @@ static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj,
// Wrap the JavaInstance used when binding custom javascript interfaces. Use a
// weak reference so that the gc can collect the WebView. Override virtualBegin
// and virtualEnd and swap the weak reference for the real object.
+#if USE(JSC)
+class WeakJavaInstance : public JavaInstance {
+#elif USE(V8)
class WeakJavaInstance : public JavaInstanceJobject {
+#endif
public:
+#if USE(JSC)
+ static PassRefPtr<WeakJavaInstance> create(jobject obj, PassRefPtr<RootObject> root)
+ {
+ return adoptRef(new WeakJavaInstance(obj, root));
+ }
+#elif USE(V8)
static PassRefPtr<WeakJavaInstance> create(jobject obj)
{
return adoptRef(new WeakJavaInstance(obj));
}
+#endif
private:
+#if USE(JSC)
+ WeakJavaInstance(jobject instance, PassRefPtr<RootObject> rootObject)
+ : JavaInstance(instance, rootObject)
+#elif USE(V8)
WeakJavaInstance(jobject instance)
: JavaInstanceJobject(instance)
+#endif
, m_beginEndDepth(0)
{
JNIEnv* env = getJNIEnv();
@@ -1524,7 +1780,7 @@ private:
}
~WeakJavaInstance()
{
- ALOG_ASSERT(!m_beginEndDepth, "Unbalanced calls to WeakJavaInstance::begin() / end()");
+ LOG_ASSERT(!m_beginEndDepth, "Unbalanced calls to WeakJavaInstance::begin() / end()");
JNIEnv* env = getJNIEnv();
// The JavaInstance destructor attempts to delete the global ref stored
// in m_instance. Since we replaced it in our constructor with a weak
@@ -1562,7 +1818,11 @@ private:
}
private:
+#if USE(JSC)
+ typedef JavaInstance INHERITED;
+#elif USE(V8)
typedef JavaInstanceJobject INHERITED;
+#endif
jweak m_weakRef;
// The current depth of nested calls to virtualBegin and virtualEnd.
int m_beginEndDepth;
@@ -1571,17 +1831,42 @@ private:
static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
jobject javascriptObj, jstring interfaceName)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = 0;
if (nativeFramePointer == 0)
pFrame = GET_NATIVE_FRAME(env, obj);
else
pFrame = (WebCore::Frame*)nativeFramePointer;
- ALOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
JavaVM* vm;
env->GetJavaVM(&vm);
- ALOGV("::WebCore:: addJSInterface: %p", pFrame);
-
+ LOGV("::WebCore:: addJSInterface: %p", pFrame);
+
+#if USE(JSC)
+ // Copied from qwebframe.cpp
+ JSC::JSLock lock(JSC::SilenceAssertionsOnly);
+ WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame, mainThreadNormalWorld());
+ if (window) {
+ RootObject *root = pFrame->script()->bindingRootObject();
+ setJavaVM(vm);
+ // Add the binding to JS environment
+ JSC::ExecState* exec = window->globalExec();
+ JSC::JSObject* addedObject = WeakJavaInstance::create(javascriptObj,
+ root)->createRuntimeObject(exec);
+ const jchar* s = env->GetStringChars(interfaceName, NULL);
+ if (s) {
+ // Add the binding name to the window's table of child objects.
+ JSC::PutPropertySlot slot;
+ window->put(exec, JSC::Identifier(exec, (const UChar *)s,
+ env->GetStringLength(interfaceName)), addedObject, slot);
+ env->ReleaseStringChars(interfaceName, s);
+ checkException(env);
+ }
+ }
+#elif USE(V8)
if (pFrame) {
RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj);
const char* name = getCharactersFromJStringInEnv(env, interfaceName);
@@ -1602,6 +1887,24 @@ static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePoi
NPN_ReleaseObject(npObject);
releaseCharactersForJString(interfaceName, name);
}
+#endif
+
+}
+
+static void SetCacheDisabled(JNIEnv *env, jobject obj, jboolean disabled)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::memoryCache()->setDisabled(disabled);
+}
+
+static jboolean CacheDisabled(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ return WebCore::memoryCache()->disabled();
}
static void ClearWebCoreCache()
@@ -1623,32 +1926,59 @@ static void ClearWebCoreCache()
static void ClearWebViewCache()
{
+#if USE(CHROME_NETWORK_STACK)
WebCache::get(false /*privateBrowsing*/)->clear();
+#else
+ // The Android network stack provides a WebView cache in CacheManager.java.
+ // Clearing this is handled entirely Java-side.
+#endif
}
static void ClearCache(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#if USE(JSC)
+ JSC::JSLock lock(false);
+ JSC::Heap::Statistics jsHeapStatistics = WebCore::JSDOMWindow::commonJSGlobalData()->heap.statistics();
+ LOGD("About to gc and JavaScript heap size is %d and has %d bytes free",
+ jsHeapStatistics.size, jsHeapStatistics.free);
+#endif // USE(JSC)
+ LOGD("About to clear cache and current cache has %d bytes live and %d bytes dead",
+ memoryCache()->getLiveSize(), memoryCache()->getDeadSize());
+#endif // ANDROID_INSTRUMENT
ClearWebCoreCache();
ClearWebViewCache();
+#if USE(JSC)
+ // force JavaScript to GC when clear cache
+ WebCore::gcController().garbageCollectSoon();
+#elif USE(V8)
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
pFrame->script()->lowMemoryNotification();
+#endif // USE(JSC)
}
static jboolean DocumentHasImages(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!");
return pFrame->document()->images()->length() > 0;
}
static jboolean HasPasswordField(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!");
bool found = false;
- WTF::RefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
WebCore::Node* node = form->firstItem();
// Null/Empty namespace means that node is not created in HTMLFormElement
// class, but just normal Element class.
@@ -1671,8 +2001,11 @@ static jboolean HasPasswordField(JNIEnv *env, jobject obj)
static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!");
jobjectArray strArray = NULL;
WTF::String username;
WTF::String password;
@@ -1689,13 +2022,16 @@ static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj)
static void SetUsernamePassword(JNIEnv *env, jobject obj,
jstring username, jstring password)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!");
+ LOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!");
WebCore::HTMLInputElement* usernameEle = NULL;
WebCore::HTMLInputElement* passwordEle = NULL;
bool found = false;
- WTF::RefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
WebCore::Node* node = form->firstItem();
while (node && !found && !node->namespaceURI().isNull() &&
!node->namespaceURI().isEmpty()) {
@@ -1735,14 +2071,14 @@ WebFrame::saveFormData(HTMLFormElement* form)
if (form->autoComplete()) {
JNIEnv* env = getJNIEnv();
jclass mapClass = env->FindClass("java/util/HashMap");
- ALOG_ASSERT(mapClass, "Could not find HashMap class!");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
- ALOG_ASSERT(init, "Could not find constructor for HashMap");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
jobject hashMap = env->NewObject(mapClass, init, 1);
- ALOG_ASSERT(hashMap, "Could not create a new HashMap");
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
jmethodID put = env->GetMethodID(mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
- ALOG_ASSERT(put, "Could not find put method on HashMap");
+ LOG_ASSERT(put, "Could not find put method on HashMap");
WTF::Vector<WebCore::FormAssociatedElement*> elements = form->associatedElements();
size_t size = elements.size();
for (size_t i = 0; i < size; i++) {
@@ -1757,7 +2093,7 @@ WebFrame::saveFormData(HTMLFormElement* form)
const WTF::AtomicString& name = input->name();
jstring key = wtfStringToJstring(env, name);
jstring val = wtfStringToJstring(env, value);
- ALOG_ASSERT(key && val, "name or value not set");
+ LOG_ASSERT(key && val, "name or value not set");
env->CallObjectMethod(hashMap, put, key, val);
env->DeleteLocalRef(key);
env->DeleteLocalRef(val);
@@ -1773,8 +2109,11 @@ WebFrame::saveFormData(HTMLFormElement* form)
static void OrientationChanged(JNIEnv *env, jobject obj, int orientation)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
- ALOGV("Sending orientation: %d", orientation);
+ LOGV("Sending orientation: %d", orientation);
pFrame->sendOrientationChangeEvent(orientation);
}
@@ -1787,9 +2126,8 @@ static jboolean GetShouldStartScrolledRight(JNIEnv *env, jobject obj,
if (document) {
RenderStyle* style = document->renderer()->style();
WritingMode writingMode = style->writingMode();
- ALOG_ASSERT(writingMode != WebCore::BottomToTopWritingMode,
- "BottomToTopWritingMode isn't possible in any "
- "language and cannot be specified in w3c writing-mode.");
+ LOG_ASSERT(writingMode != WebCore::BottomToTopWritingMode,
+ "BottomToTopWritingMode isn't supported");
if (writingMode == WebCore::RightToLeftWritingMode)
startScrolledRight = true; // vertical-rl pages start scrolled right
else if (writingMode == WebCore::TopToBottomWritingMode)
@@ -1798,6 +2136,8 @@ static jboolean GetShouldStartScrolledRight(JNIEnv *env, jobject obj,
return startScrolledRight;
}
+#if USE(CHROME_NETWORK_STACK)
+
static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
{
WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
@@ -1824,12 +2164,39 @@ static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_er
client->cancelSslCertError(cert_error);
}
-static scoped_refptr<net::X509Certificate> getX509Cert(JNIEnv *env, jobjectArray chain)
+static void SslClientCert(JNIEnv *env, jobject obj, int handle, jbyteArray pkey, jobjectArray chain)
{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ if (pkey == NULL || chain == NULL) {
+ client->sslClientCert(NULL, NULL);
+ return;
+ }
+
+ // Based on Android's NativeCrypto_SSL_use_PrivateKey
+ ScopedByteArrayRO pkeyBytes(env, pkey);
+ if (pkeyBytes.get() == NULL) {
+ client->sslClientCert(NULL, NULL);
+ return;
+ }
+
+ base::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8;
+ const unsigned char* pkeyChars = reinterpret_cast<const unsigned char*>(pkeyBytes.get());
+ pkcs8.reset(d2i_PKCS8_PRIV_KEY_INFO(NULL, &pkeyChars, pkeyBytes.size()));
+ if (!pkcs8.get()) {
+ client->sslClientCert(NULL, NULL);
+ return;
+ }
+ base::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> privateKey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (!privateKey.get()) {
+ client->sslClientCert(NULL, NULL);
+ return;
+ }
+
// Based on Android's NativeCrypto_SSL_use_certificate
int length = env->GetArrayLength(chain);
if (length == 0) {
- return NULL;
+ client->sslClientCert(NULL, NULL);
+ return;
}
base::ScopedOpenSSL<X509, X509_free> first;
@@ -1838,17 +2205,20 @@ static scoped_refptr<net::X509Certificate> getX509Cert(JNIEnv *env, jobjectArray
ScopedLocalRef<jbyteArray> cert(env,
reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(chain, i)));
if (cert.get() == NULL) {
- return NULL;
+ client->sslClientCert(NULL, NULL);
+ return;
}
ScopedByteArrayRO certBytes(env, cert.get());
if (certBytes.get() == NULL) {
- return NULL;
+ client->sslClientCert(NULL, NULL);
+ return;
}
const char* data = reinterpret_cast<const char*>(certBytes.get());
int length = certBytes.size();
X509* x509 = net::X509Certificate::CreateOSCertHandleFromBytes(data, length);
if (x509 == NULL) {
- return NULL;
+ client->sslClientCert(NULL, NULL);
+ return;
}
if (i == 0) {
first.reset(x509);
@@ -1861,39 +2231,10 @@ static scoped_refptr<net::X509Certificate> getX509Cert(JNIEnv *env, jobjectArray
for (size_t i = 0; i < rest.size(); i++) {
certChain[i] = rest[i]->get();
}
- return net::X509Certificate::CreateFromHandle(first.get(),
- net::X509Certificate::SOURCE_FROM_NETWORK,
- certChain);
-}
-
-static void SslClientCertPKCS8(JNIEnv *env, jobject obj, int handle, jbyteArray pkey, jobjectArray chain)
-{
- WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
- if (pkey == NULL || chain == NULL) {
- client->sslClientCert(NULL, NULL);
- return;
- }
-
- // Based on Android's NativeCrypto_SSL_use_PrivateKey
- ScopedByteArrayRO pkeyBytes(env, pkey);
- if (pkeyBytes.get() == NULL) {
- client->sslClientCert(NULL, NULL);
- return;
- }
-
- base::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8;
- const unsigned char* pkeyChars = reinterpret_cast<const unsigned char*>(pkeyBytes.get());
- pkcs8.reset(d2i_PKCS8_PRIV_KEY_INFO(NULL, &pkeyChars, pkeyBytes.size()));
- if (!pkcs8.get()) {
- client->sslClientCert(NULL, NULL);
- return;
- }
- base::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> privateKey(EVP_PKCS82PKEY(pkcs8.get()));
- if (!privateKey.get()) {
- client->sslClientCert(NULL, NULL);
- return;
- }
- scoped_refptr<net::X509Certificate> certificate = getX509Cert(env, chain);
+ net::X509Certificate* certificate
+ = net::X509Certificate::CreateFromHandle(first.get(),
+ net::X509Certificate::SOURCE_FROM_NETWORK,
+ certChain);
if (certificate == NULL) {
client->sslClientCert(NULL, NULL);
return;
@@ -1901,22 +2242,33 @@ static void SslClientCertPKCS8(JNIEnv *env, jobject obj, int handle, jbyteArray
client->sslClientCert(privateKey.release(), certificate);
}
-static void SslClientCertCtx(JNIEnv *env, jobject obj, int handle, jint ctx, jobjectArray chain)
+#else
+
+static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
{
- WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
- EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(static_cast<uintptr_t>(ctx));
- if (pkey == NULL || chain == NULL) {
- client->sslClientCert(NULL, NULL);
- return;
- }
- scoped_refptr<net::X509Certificate> certificate = getX509Cert(env, chain);
- if (certificate == NULL) {
- client->sslClientCert(NULL, NULL);
- return;
- }
- CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
- client->sslClientCert(pkey, certificate);
+ LOGW("Chromium authentication API called, but libchromium is not available");
+}
+
+static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle)
+{
+ LOGW("Chromium authentication API called, but libchromium is not available");
+}
+
+static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle)
+{
+ LOGW("Chromium SSL API called, but libchromium is not available");
+}
+
+static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error)
+{
+ LOGW("Chromium SSL API called, but libchromium is not available");
+}
+
+static void SslClientCert(JNIEnv *env, jobject obj, int handle, jbyteArray privateKey, jobjectArray chain)
+{
+ LOGW("Chromium SSL API called, but libchromium is not available");
}
+#endif // USE(CHROME_NETWORK_STACK)
// ----------------------------------------------------------------------------
@@ -1956,6 +2308,10 @@ static JNINativeMethod gBrowserFrameNativeMethods[] = {
{ "stringByEvaluatingJavaScriptFromString",
"(Ljava/lang/String;)Ljava/lang/String;",
(void*) StringByEvaluatingJavaScriptFromString },
+ { "setCacheDisabled", "(Z)V",
+ (void*) SetCacheDisabled },
+ { "cacheDisabled", "()Z",
+ (void*) CacheDisabled },
{ "clearCache", "()V",
(void*) ClearCache },
{ "documentHasImages", "()Z",
@@ -1976,10 +2332,8 @@ static JNINativeMethod gBrowserFrameNativeMethods[] = {
(void*) SslCertErrorProceed },
{ "nativeSslCertErrorCancel", "(II)V",
(void*) SslCertErrorCancel },
- { "nativeSslClientCert", "(II[[B)V",
- (void*) SslClientCertCtx },
{ "nativeSslClientCert", "(I[B[[B)V",
- (void*) SslClientCertPKCS8 },
+ (void*) SslClientCert },
{ "nativeGetShouldStartScrolledRight", "(I)Z",
(void*) GetShouldStartScrolledRight },
};
@@ -1987,9 +2341,9 @@ static JNINativeMethod gBrowserFrameNativeMethods[] = {
int registerWebFrame(JNIEnv* env)
{
jclass clazz = env->FindClass("android/webkit/BrowserFrame");
- ALOG_ASSERT(clazz, "Cannot find BrowserFrame");
+ LOG_ASSERT(clazz, "Cannot find BrowserFrame");
gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
- ALOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame");
+ LOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame");
env->DeleteLocalRef(clazz);
return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.h b/Source/WebKit/android/jni/WebCoreFrameBridge.h
index 30c1d83..eaee63c 100644
--- a/Source/WebKit/android/jni/WebCoreFrameBridge.h
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.h
@@ -64,6 +64,10 @@ class WebFrame : public WebCoreRefObject {
// helper function
static WebFrame* getWebFrame(const WebCore::Frame* frame);
+ virtual PassRefPtr<WebCore::ResourceLoaderAndroid> startLoadingResource(WebCore::ResourceHandle*,
+ const WebCore::ResourceRequest& request, bool mainResource,
+ bool synchronous);
+
UrlInterceptResponse* shouldInterceptRequest(const WTF::String& url);
void reportError(int errorCode, const WTF::String& description,
@@ -113,6 +117,7 @@ class WebFrame : public WebCoreRefObject {
float density() const;
+#if USE(CHROME_NETWORK_STACK)
void didReceiveAuthenticationChallenge(WebUrlLoaderClient*, const std::string& host, const std::string& realm, bool useCachedCredentials, bool suppressDialog);
void reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert, const std::string& url);
void requestClientCert(WebUrlLoaderClient* client, const std::string& hostAndPort);
@@ -120,6 +125,7 @@ class WebFrame : public WebCoreRefObject {
void didReceiveData(const char* data, int size);
void didFinishLoading();
void setCertificate(const std::string& cert);
+#endif
void maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request);
@@ -128,6 +134,13 @@ class WebFrame : public WebCoreRefObject {
// application.
void autoLogin(const std::string& loginHeader);
+ /**
+ * When the user initiates a click, we set mUserInitiatedAction to true.
+ * If a load happens due to this click, then we ask the application if it wants
+ * to override the load. Otherwise, we attempt to load the resource internally.
+ */
+ void setUserInitiatedAction(bool userInitiatedAction) { mUserInitiatedAction = userInitiatedAction; }
+
WebCore::Page* page() const { return mPage; }
// Currently used only by the chrome net stack. A similar field is used by
@@ -156,6 +169,7 @@ class WebFrame : public WebCoreRefObject {
WebCore::Page* mPage;
WTF::String mUserAgent;
bool mBlockNetworkLoads;
+ bool mUserInitiatedAction;
WebCore::RenderSkinAndroid* m_renderSkins;
};
diff --git a/Source/WebKit/android/jni/WebCoreJni.cpp b/Source/WebKit/android/jni/WebCoreJni.cpp
index 72ded59..2a07999 100644
--- a/Source/WebKit/android/jni/WebCoreJni.cpp
+++ b/Source/WebKit/android/jni/WebCoreJni.cpp
@@ -26,9 +26,7 @@
#define LOG_TAG "webcoreglue"
#include "config.h"
-#include "IntRect.h"
#include "WebCoreJni.h"
-#include "wtf/Vector.h"
#include "NotImplemented.h"
#include <JNIUtility.h>
@@ -40,7 +38,7 @@ namespace android {
AutoJObject getRealObject(JNIEnv* env, jobject obj)
{
jobject real = env->NewLocalRef(obj);
- ALOG_ASSERT(real, "The real object has been deleted!");
+ LOG_ASSERT(real, "The real object has been deleted!");
return AutoJObject(env, real);
}
@@ -52,7 +50,7 @@ bool checkException(JNIEnv* env)
{
if (env->ExceptionCheck() != 0)
{
- ALOGE("*** Uncaught exception returned from Java call!\n");
+ LOGE("*** Uncaught exception returned from Java call!\n");
env->ExceptionDescribe();
return true;
}
@@ -79,6 +77,8 @@ jstring wtfStringToJstring(JNIEnv* env, const WTF::String& str, bool validOnZero
return length || validOnZeroLength ? env->NewString(str.characters(), length) : 0;
}
+
+#if USE(CHROME_NETWORK_STACK)
string16 jstringToString16(JNIEnv* env, jstring jstr)
{
if (!jstr || !env)
@@ -112,37 +112,6 @@ jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZero
return !str.empty() || validOnZeroLength ? env->NewStringUTF(str.c_str()) : 0;
}
-jobject intRectToRect(JNIEnv* env, const WebCore::IntRect& rect)
-{
- jclass rectClass = env->FindClass("android/graphics/Rect");
- ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
- jmethodID rectInit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
- ALOG_ASSERT(rectInit, "Could not find init method on Rect");
- jobject jrect = env->NewObject(rectClass, rectInit, rect.x(), rect.y(),
- rect.maxX(), rect.maxY());
- env->DeleteLocalRef(rectClass);
- return jrect;
-}
-
-jobjectArray intRectVectorToRectArray(JNIEnv* env, Vector<WebCore::IntRect>& rects)
-{
- jclass rectClass = env->FindClass("android/graphics/Rect");
- ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
- jmethodID rectInit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
- ALOG_ASSERT(rectInit, "Could not find init method on Rect");
- jobjectArray array = env->NewObjectArray(rects.size(), rectClass, 0);
- ALOG_ASSERT(array, "Could not create a Rect array");
- for (size_t i = 0; i < rects.size(); i++) {
- jobject rect = env->NewObject(rectClass, rectInit,
- rects[i].x(), rects[i].y(),
- rects[i].maxX(), rects[i].maxY());
- if (rect) {
- env->SetObjectArrayElement(array, i, rect);
- env->DeleteLocalRef(rect);
- }
- }
- env->DeleteLocalRef(rectClass);
- return array;
-}
+#endif
}
diff --git a/Source/WebKit/android/jni/WebCoreJni.h b/Source/WebKit/android/jni/WebCoreJni.h
index e8cc6ea..0f77cc6 100644
--- a/Source/WebKit/android/jni/WebCoreJni.h
+++ b/Source/WebKit/android/jni/WebCoreJni.h
@@ -27,7 +27,6 @@
#define WebCoreJni_h
#include "ChromiumIncludes.h"
-#include "IntRect.h"
#include "PlatformString.h"
#include <jni.h>
@@ -82,6 +81,7 @@ WTF::String jstringToWtfString(JNIEnv*, jstring);
// an empty WTF String returns 0.
jstring wtfStringToJstring(JNIEnv*, const WTF::String&, bool validOnZeroLength = false);
+#if USE(CHROME_NETWORK_STACK)
string16 jstringToString16(JNIEnv*, jstring);
std::string jstringToStdString(JNIEnv*, jstring);
@@ -89,10 +89,8 @@ std::string jstringToStdString(JNIEnv*, jstring);
// passing in an empty std::string will result in an empty jstring. Otherwise
// an empty std::string returns 0.
jstring stdStringToJstring(JNIEnv*, const std::string&, bool validOnZeroLength = false);
+#endif
-jobjectArray intRectVectorToRectArray(JNIEnv*, Vector<WebCore::IntRect>&);
-
-jobject intRectToRect(JNIEnv* env, const WebCore::IntRect& rect);
}
#endif
diff --git a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
index ec052f1..bb71bf5 100644
--- a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
+++ b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
@@ -62,6 +62,8 @@
#include "WebCoreViewBridge.h"
#include "WebFrameView.h"
#include "WebViewCore.h"
+#include "benchmark/Intercept.h"
+#include "benchmark/MyJavaVM.h"
#include <JNIUtility.h>
#include <jni.h>
@@ -73,6 +75,7 @@ namespace android {
extern int registerWebFrame(JNIEnv*);
extern int registerJavaBridge(JNIEnv*);
+extern int registerJniUtil(JNIEnv*);
extern int registerResourceLoader(JNIEnv*);
extern int registerWebViewCore(JNIEnv*);
extern int registerWebHistory(JNIEnv*);
@@ -91,7 +94,9 @@ extern int registerMediaPlayerVideo(JNIEnv*);
#endif
extern int registerDeviceMotionAndOrientationManager(JNIEnv*);
extern int registerCookieManager(JNIEnv*);
+#if USE(CHROME_NETWORK_STACK)
extern int registerCacheManager(JNIEnv*);
+#endif
}
@@ -102,11 +107,13 @@ struct RegistrationMethod {
static RegistrationMethod gWebCoreRegMethods[] = {
{ "JavaBridge", android::registerJavaBridge },
+ { "JniUtil", android::registerJniUtil },
{ "WebFrame", android::registerWebFrame },
+ { "WebCoreResourceLoader", android::registerResourceLoader },
{ "WebViewCore", android::registerWebViewCore },
{ "WebHistory", android::registerWebHistory },
{ "WebIconDatabase", android::registerWebIconDatabase },
- { "WebSettingsClassic", android::registerWebSettings },
+ { "WebSettings", android::registerWebSettings },
#if ENABLE(DATABASE)
{ "WebStorage", android::registerWebStorage },
#endif
@@ -120,7 +127,9 @@ static RegistrationMethod gWebCoreRegMethods[] = {
#endif
{ "DeviceMotionAndOrientationManager", android::registerDeviceMotionAndOrientationManager },
{ "CookieManager", android::registerCookieManager },
+#if USE(CHROME_NETWORK_STACK)
{ "CacheManager", android::registerCacheManager },
+#endif
};
EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
@@ -132,16 +141,16 @@ EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("GetEnv failed!");
+ LOGE("GetEnv failed!");
return result;
}
- ALOG_ASSERT(env, "Could not retrieve the env!");
+ LOG_ASSERT(env, "Could not retrieve the env!");
const RegistrationMethod* method = gWebCoreRegMethods;
const RegistrationMethod* end = method + sizeof(gWebCoreRegMethods)/sizeof(RegistrationMethod);
while (method != end) {
if (method->func(env) < 0) {
- ALOGE("%s registration failed!", method->name);
+ LOGE("%s registration failed!", method->name);
return result;
}
method++;
@@ -153,3 +162,160 @@ EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
return JNI_VERSION_1_4;
}
+
+class MyJavaSharedClient : public TimerClient, public CookieClient {
+public:
+ MyJavaSharedClient() : m_hasTimer(false) {}
+ virtual void setSharedTimer(long long timemillis) { m_hasTimer = true; }
+ virtual void stopSharedTimer() { m_hasTimer = false; }
+ virtual void setSharedTimerCallback(void (*f)()) { m_func = f; }
+ virtual void signalServiceFuncPtrQueue() {}
+
+ // Cookie methods that do nothing.
+ virtual void setCookies(const KURL&, const String&) {}
+ virtual String cookies(const KURL&) { return ""; }
+ virtual bool cookiesEnabled() { return false; }
+
+ bool m_hasTimer;
+ void (*m_func)();
+};
+
+static void historyItemChanged(HistoryItem* i) {
+ if (i->bridge())
+ i->bridge()->updateHistoryItem(i);
+}
+
+namespace android {
+
+EXPORT void benchmark(const char* url, int reloadCount, int width, int height) {
+ ScriptController::initializeThreading();
+
+ // Setting this allows data: urls to load from a local file.
+ SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForAll);
+
+ // Create the fake JNIEnv and JavaVM
+ InitializeJavaVM();
+
+ // The real function is private to libwebcore but we know what it does.
+ notifyHistoryItemChanged = historyItemChanged;
+
+ // Implement the shared timer callback
+ MyJavaSharedClient client;
+ JavaSharedClient::SetTimerClient(&client);
+ JavaSharedClient::SetCookieClient(&client);
+
+ // Create the page with all the various clients
+ ChromeClientAndroid* chrome = new ChromeClientAndroid;
+ EditorClientAndroid* editor = new EditorClientAndroid;
+ DeviceMotionClientAndroid* deviceMotion = new DeviceMotionClientAndroid;
+ DeviceOrientationClientAndroid* deviceOrientation = new DeviceOrientationClientAndroid;
+ WebCore::Page::PageClients pageClients;
+ pageClients.chromeClient = chrome;
+ pageClients.contextMenuClient = new ContextMenuClientAndroid;
+ pageClients.editorClient = editor;
+ pageClients.dragClient = new DragClientAndroid;
+ pageClients.inspectorClient = new InspectorClientAndroid;
+ pageClients.deviceMotionClient = deviceMotion;
+ pageClients.deviceOrientationClient = deviceOrientation;
+ WebCore::Page* page = new WebCore::Page(pageClients);
+ editor->setPage(page);
+
+ // Create MyWebFrame that intercepts network requests
+ MyWebFrame* webFrame = new MyWebFrame(page);
+ webFrame->setUserAgent("Performance testing"); // needs to be non-empty
+ chrome->setWebFrame(webFrame);
+ // ChromeClientAndroid maintains the reference.
+ Release(webFrame);
+
+ // Create the Frame and the FrameLoaderClient
+ FrameLoaderClientAndroid* loader = new FrameLoaderClientAndroid(webFrame);
+ RefPtr<Frame> frame = Frame::create(page, NULL, loader);
+ loader->setFrame(frame.get());
+
+ // Build our View system, resize it to the given dimensions and release our
+ // references. Note: We keep a referenec to frameView so we can layout and
+ // draw later without risk of it being deleted.
+ WebViewCore* webViewCore = new WebViewCore(JSC::Bindings::getJNIEnv(),
+ MY_JOBJECT, frame.get());
+ RefPtr<FrameView> frameView = FrameView::create(frame.get());
+ WebFrameView* webFrameView = new WebFrameView(frameView.get(), webViewCore);
+ frame->setView(frameView);
+ frameView->resize(width, height);
+ Release(webViewCore);
+ Release(webFrameView);
+
+ // Initialize the frame and turn of low-bandwidth display (it fails an
+ // assertion in the Cache code)
+ frame->init();
+ frame->selection()->setFocused(true);
+ frame->page()->focusController()->setFocused(true);
+
+ deviceMotion->setWebViewCore(webViewCore);
+ deviceOrientation->setWebViewCore(webViewCore);
+
+ // Set all the default settings the Browser normally uses.
+ Settings* s = frame->settings();
+#ifdef ANDROID_LAYOUT
+ s->setLayoutAlgorithm(Settings::kLayoutNormal); // Normal layout for now
+#endif
+ s->setStandardFontFamily("sans-serif");
+ s->setFixedFontFamily("monospace");
+ s->setSansSerifFontFamily("sans-serif");
+ s->setSerifFontFamily("serif");
+ s->setCursiveFontFamily("cursive");
+ s->setFantasyFontFamily("fantasy");
+ s->setMinimumFontSize(8);
+ s->setMinimumLogicalFontSize(8);
+ s->setDefaultFontSize(16);
+ s->setDefaultFixedFontSize(13);
+ s->setLoadsImagesAutomatically(true);
+ s->setJavaScriptEnabled(true);
+ s->setDefaultTextEncodingName("latin1");
+ s->setPluginsEnabled(false);
+ s->setShrinksStandaloneImagesToFit(false);
+#ifdef ANDROID_LAYOUT
+ s->setUseWideViewport(false);
+#endif
+
+ // Finally, load the actual data
+ ResourceRequest req(url);
+ frame->loader()->load(req, false);
+
+ do {
+ // Layout the page and service the timer
+ frame->view()->layout();
+ while (client.m_hasTimer) {
+ client.m_func();
+ JavaSharedClient::ServiceFunctionPtrQueue();
+ }
+ JavaSharedClient::ServiceFunctionPtrQueue();
+
+ // Layout more if needed.
+ while (frame->view()->needsLayout())
+ frame->view()->layout();
+ JavaSharedClient::ServiceFunctionPtrQueue();
+
+ if (reloadCount)
+ frame->loader()->reload(true);
+ } while (reloadCount--);
+
+ // Draw into an offscreen bitmap
+ SkBitmap bmp;
+ bmp.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bmp.allocPixels();
+ SkCanvas canvas(bmp);
+ PlatformGraphicsContext ctx(&canvas);
+ GraphicsContext gc(&ctx);
+ frame->view()->paintContents(&gc, IntRect(0, 0, width, height));
+
+ // Write the bitmap to the sdcard
+ SkImageEncoder* enc = SkImageEncoder::Create(SkImageEncoder::kPNG_Type);
+ enc->encodeFile("/sdcard/webcore_test.png", bmp, 100);
+ delete enc;
+
+ // Tear down the world.
+ frame->loader()->detachFromParent();
+ delete page;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.cpp b/Source/WebKit/android/jni/WebCoreResourceLoader.cpp
new file mode 100644
index 0000000..f9acc97
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreResourceLoader.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2006, 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 "webcoreglue"
+
+#include "config.h"
+#include "WebCoreResourceLoader.h"
+
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceHandleInternal.h"
+#include "ResourceResponse.h"
+#include "SkUtils.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SkTypes.h>
+#include <stdlib.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct resourceloader_t {
+ jfieldID mObject;
+ jmethodID mCancelMethodID;
+ jmethodID mDownloadFileMethodID;
+ jmethodID mWillLoadFromCacheMethodID;
+ jmethodID mPauseLoadMethodID;
+} gResourceLoader;
+
+// ----------------------------------------------------------------------------
+
+#define GET_NATIVE_HANDLE(env, obj) ((WebCore::ResourceHandle*)env->GetIntField(obj, gResourceLoader.mObject))
+#define SET_NATIVE_HANDLE(env, obj, handle) (env->SetIntField(obj, gResourceLoader.mObject, handle))
+
+//-----------------------------------------------------------------------------
+// ResourceLoadHandler
+
+PassRefPtr<WebCore::ResourceLoaderAndroid> WebCoreResourceLoader::create(JNIEnv *env, jobject jLoadListener)
+{
+ return adoptRef<WebCore::ResourceLoaderAndroid>(new WebCoreResourceLoader(env, jLoadListener));
+}
+
+WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener)
+ : mPausedLoad(false)
+{
+ mJLoader = env->NewGlobalRef(jLoadListener);
+}
+
+WebCoreResourceLoader::~WebCoreResourceLoader()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ SET_NATIVE_HANDLE(env, mJLoader, 0);
+ env->DeleteGlobalRef(mJLoader);
+ mJLoader = 0;
+}
+
+void WebCoreResourceLoader::cancel()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID);
+ checkException(env);
+}
+
+void WebCoreResourceLoader::downloadFile()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID);
+ checkException(env);
+}
+
+void WebCoreResourceLoader::pauseLoad(bool pause)
+{
+ if (mPausedLoad == pause)
+ return;
+
+ mPausedLoad = pause;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mPauseLoadMethodID, pause);
+ checkException(env);
+}
+
+/*
+* This static method is called to check to see if a POST response is in
+* the cache. This may be slow, but is only used during a navigation to
+* a POST response.
+*/
+bool WebCoreResourceLoader::willLoadFromCache(const WebCore::KURL& url, int64_t identifier)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WTF::String urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ bool val = env->CallStaticBooleanMethod(resourceLoader, gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr, identifier);
+ checkException(env);
+ env->DeleteLocalRef(resourceLoader);
+ env->DeleteLocalRef(jUrlStr);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ LOG_ASSERT(response, "nativeSetResponseHeader must take a valid response pointer!");
+
+ LOG_ASSERT(key, "How did a null value become a key?");
+ if (val)
+ response->setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, val));
+}
+
+jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
+ jstring statusText, jstring mimeType, jlong expectedLength,
+ jstring encoding)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOG_ASSERT(url, "Must have a url in the response!");
+ WebCore::KURL kurl(WebCore::ParsedURLString, jstringToWtfString(env, url));
+ WTF::String encodingStr;
+ WTF::String mimeTypeStr;
+ if (mimeType) {
+ mimeTypeStr = jstringToWtfString(env, mimeType);
+ LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
+ }
+ if (encoding) {
+ encodingStr = jstringToWtfString(env, encoding);
+ LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
+ }
+ WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
+ kurl, mimeTypeStr, (long long)expectedLength,
+ encodingStr, WTF::String());
+ response->setHTTPStatusCode(statusCode);
+ if (statusText) {
+ WTF::String status = jstringToWtfString(env, statusText);
+ response->setHTTPStatusText(status);
+ LOGV("Response setStatusText: %s", status.latin1().data());
+ }
+ return (int)response;
+}
+
+void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeReceivedResponse must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ LOG_ASSERT(response, "nativeReceivedResponse must take a valid resource pointer!");
+ handle->client()->didReceiveResponse(handle, *response);
+ // As the client makes a copy of the response, delete it here.
+ delete response;
+}
+
+void WebCoreResourceLoader::AddData(JNIEnv* env, jobject obj, jbyteArray dataArray, jint length)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader data(%d)", length);
+
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeAddData must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ SkAutoMemoryUsageProbe mup("android_webcore_resourceloader_nativeAddData");
+
+ bool result = false;
+ jbyte * data = env->GetByteArrayElements(dataArray, NULL);
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ handle->client()->didReceiveData(handle, (const char *)data, length, length);
+ env->ReleaseByteArrayElements(dataArray, data, JNI_ABORT);
+}
+
+void WebCoreResourceLoader::Finished(JNIEnv* env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader finished");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeFinished must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ handle->client()->didFinishLoading(handle, 0);
+}
+
+jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj,
+ jstring baseUrl, jstring redirectTo, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader redirectedToUrl");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return NULL;
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ WebCore::ResourceRequest r = handle->firstRequest();
+ WebCore::KURL url(WebCore::KURL(WebCore::ParsedURLString, jstringToWtfString(env, baseUrl)),
+ jstringToWtfString(env, redirectTo));
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ // If the url fails to resolve the relative path, return null.
+ if (url.protocol().isEmpty()) {
+ delete response;
+ return NULL;
+ } else {
+ // Ensure the protocol is lowercase.
+ url.setProtocol(url.protocol().lower());
+ }
+ // Set the url after updating the protocol.
+ r.setURL(url);
+ if (r.httpMethod() == "POST") {
+ r.setHTTPMethod("GET");
+ r.clearHTTPReferrer();
+ r.setHTTPBody(0);
+ r.setHTTPContentType("");
+ }
+ handle->client()->willSendRequest(handle, r, *response);
+ delete response;
+ return wtfStringToJstring(env, url.string());
+}
+
+void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description,
+ jstring failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader error");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeError must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ handle->client()->didFail(handle, WebCore::ResourceError("", id,
+ jstringToWtfString(env, failingUrl), jstringToWtfString(env, description)));
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gResourceloaderMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeSetResponseHeader", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) WebCoreResourceLoader::SetResponseHeader },
+ { "nativeCreateResponse", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLjava/lang/String;)I",
+ (void*) WebCoreResourceLoader::CreateResponse },
+ { "nativeReceivedResponse", "(I)V",
+ (void*) WebCoreResourceLoader::ReceivedResponse },
+ { "nativeAddData", "([BI)V",
+ (void*) WebCoreResourceLoader::AddData },
+ { "nativeFinished", "()V",
+ (void*) WebCoreResourceLoader::Finished },
+ { "nativeRedirectedToUrl", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;",
+ (void*) WebCoreResourceLoader::RedirectedToUrl },
+ { "nativeError", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) WebCoreResourceLoader::Error }
+};
+
+int registerResourceLoader(JNIEnv* env)
+{
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ LOG_FATAL_IF(resourceLoader == NULL,
+ "Unable to find class android/webkit/LoadListener");
+
+ gResourceLoader.mObject =
+ env->GetFieldID(resourceLoader, "mNativeLoader", "I");
+ LOG_FATAL_IF(gResourceLoader.mObject == NULL,
+ "Unable to find android/webkit/LoadListener.mNativeLoader");
+
+ gResourceLoader.mCancelMethodID =
+ env->GetMethodID(resourceLoader, "cancel", "()V");
+ LOG_FATAL_IF(gResourceLoader.mCancelMethodID == NULL,
+ "Could not find method cancel on LoadListener");
+
+ gResourceLoader.mDownloadFileMethodID =
+ env->GetMethodID(resourceLoader, "downloadFile", "()V");
+ LOG_FATAL_IF(gResourceLoader.mDownloadFileMethodID == NULL,
+ "Could not find method downloadFile on LoadListener");
+
+ gResourceLoader.mPauseLoadMethodID =
+ env->GetMethodID(resourceLoader, "pauseLoad", "(Z)V");
+ LOG_FATAL_IF(gResourceLoader.mPauseLoadMethodID == NULL,
+ "Could not find method pauseLoad on LoadListener");
+
+ gResourceLoader.mWillLoadFromCacheMethodID =
+ env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;J)Z");
+ LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
+ "Could not find static method willLoadFromCache on LoadListener");
+
+ env->DeleteLocalRef(resourceLoader);
+
+ return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
+ gResourceloaderMethods, NELEM(gResourceloaderMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.h b/Source/WebKit/android/jni/WebCoreResourceLoader.h
new file mode 100644
index 0000000..0e34a5b
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreResourceLoader.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2006, 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 WebCoreResourceLoader_h
+#define WebCoreResourceLoader_h
+
+#include <KURL.h>
+#include <ResourceLoaderAndroid.h>
+#include <jni.h>
+
+namespace android {
+
+class WebCoreResourceLoader : public WebCore::ResourceLoaderAndroid
+{
+public:
+ static PassRefPtr<WebCore::ResourceLoaderAndroid> create(JNIEnv *env, jobject jLoadListener);
+ virtual ~WebCoreResourceLoader();
+
+ /**
+ * Call to java to cancel the current load.
+ */
+ virtual void cancel();
+
+ /**
+ * Call to java to download the current load rather than feed it
+ * back to WebCore
+ */
+ virtual void downloadFile();
+
+ virtual void pauseLoad(bool);
+
+ /**
+ * Call to java to find out if this URL is in the cache
+ */
+ static bool willLoadFromCache(const WebCore::KURL& url, int64_t identifier);
+
+ // Native jni functions
+ static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring);
+ static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring,
+ jstring, jlong, jstring);
+ static void ReceivedResponse(JNIEnv*, jobject, jint);
+ static void AddData(JNIEnv*, jobject, jbyteArray, jint);
+ static void Finished(JNIEnv*, jobject);
+ static jstring RedirectedToUrl(JNIEnv*, jobject, jstring, jstring, jint);
+ static void Error(JNIEnv*, jobject, jint, jstring, jstring);
+
+protected:
+ WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener);
+private:
+ jobject mJLoader;
+ bool mPausedLoad;
+};
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreViewBridge.h b/Source/WebKit/android/jni/WebCoreViewBridge.h
index cca4ca5..b04a0f9 100644
--- a/Source/WebKit/android/jni/WebCoreViewBridge.h
+++ b/Source/WebKit/android/jni/WebCoreViewBridge.h
@@ -48,7 +48,7 @@ public:
virtual ~WebCoreViewBridge() { }
- virtual void draw(WebCore::GraphicsContext* ctx,
+ virtual void draw(WebCore::GraphicsContext* ctx,
const WebCore::IntRect& rect) = 0;
const WebCore::IntRect& getBounds() const
diff --git a/Source/WebKit/android/jni/WebFrameView.cpp b/Source/WebKit/android/jni/WebFrameView.cpp
index 10e31dc..8e5eac4 100644
--- a/Source/WebKit/android/jni/WebFrameView.cpp
+++ b/Source/WebKit/android/jni/WebFrameView.cpp
@@ -54,16 +54,51 @@ WebFrameView::~WebFrameView() {
Release(mWebViewCore);
}
-void WebFrameView::draw(WebCore::GraphicsContext* gc, const WebCore::IntRect& rect) {
+void WebFrameView::draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) {
WebCore::Frame* frame = mFrameView->frame();
- if (frame->contentRenderer())
- mFrameView->paintContents(gc, rect);
- else {
- // FIXME: I'm not entirely sure this ever happens or is needed
- gc->setFillColor(WebCore::Color::white, WebCore::ColorSpaceDeviceRGB);
- gc->fillRect(rect);
+ if (NULL == frame->contentRenderer()) {
+ // We only do this if there is nothing else to draw.
+ // If there is a renderer, it will fill the bg itself, so we don't want to
+ // double-draw (slow)
+ SkCanvas* canvas = ctx->platformContext()->mCanvas;
+ canvas->drawColor(SK_ColorWHITE);
+ } else if (frame->tree()->parent()) {
+ // Note: this code was moved from FrameLoaderClientAndroid
+ //
+ // For subframe, create a new translated rect from the given rectangle.
+ WebCore::IntRect transRect(rect);
+ // In Frame::markAllMatchesForText(), it does a fake paint. So we need
+ // to handle the case where platformContext() is null. However, we still
+ // want to call paint, since WebKit must have called the paint for a reason.
+ SkCanvas* canvas = ctx->platformContext() ? ctx->platformContext()->mCanvas : NULL;
+ if (canvas) {
+ const WebCore::IntRect& bounds = getBounds();
+
+ // Grab the intersection of transRect and the frame's bounds.
+ transRect.intersect(bounds);
+ if (transRect.isEmpty())
+ return;
+
+ // Move the transRect into the frame's local coordinates.
+ transRect.move(-bounds.x(), -bounds.y());
+
+ // Translate the canvas, add a clip.
+ canvas->save();
+ canvas->translate(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+ canvas->clipRect(transRect);
+ }
+ mFrameView->paintContents(ctx, transRect);
+ if (canvas)
+ canvas->restore();
+ } else {
+ mFrameView->paintContents(ctx, rect);
}
}
+void WebFrameView::setView(WebCore::FrameView* frameView) {
+ mFrameView = frameView;
+ mFrameView->setPlatformWidget(this);
+}
+
} // namespace android
diff --git a/Source/WebKit/android/jni/WebFrameView.h b/Source/WebKit/android/jni/WebFrameView.h
index ac81afe..117b603 100644
--- a/Source/WebKit/android/jni/WebFrameView.h
+++ b/Source/WebKit/android/jni/WebFrameView.h
@@ -47,6 +47,8 @@ namespace android {
return mWebViewCore;
}
+ void setView(WebCore::FrameView* frameView);
+
WebCore::FrameView* view() const {
return mFrameView;
}
diff --git a/Source/WebKit/android/jni/WebHistory.cpp b/Source/WebKit/android/jni/WebHistory.cpp
index b6972e4..aa74b81 100644
--- a/Source/WebKit/android/jni/WebHistory.cpp
+++ b/Source/WebKit/android/jni/WebHistory.cpp
@@ -28,7 +28,6 @@
#include "config.h"
#include "WebHistory.h"
-#include "AndroidLog.h"
#include "BackForwardList.h"
#include "BackForwardListImpl.h"
#include "DocumentLoader.h"
@@ -36,7 +35,6 @@
#include "FrameLoader.h"
#include "FrameLoaderClientAndroid.h"
#include "FrameTree.h"
-#include "GraphicsJNI.h"
#include "HistoryItem.h"
#include "IconDatabase.h"
#include "Page.h"
@@ -56,13 +54,16 @@
namespace android {
// Forward declarations
-static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item);
-static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent);
-static bool readItemRecursive(WebCore::HistoryItem* child, const char** pData, int length);
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item);
+static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent);
+static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length);
// Field ids for WebHistoryItems
struct WebHistoryItemFields {
jmethodID mInit;
+ jmethodID mUpdate;
+ jfieldID mTitle;
+ jfieldID mUrl;
} gWebHistoryItem;
struct WebBackForwardListFields {
@@ -77,7 +78,7 @@ struct WebBackForwardListFields {
static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
{
- ALOG_ASSERT(frame, "Close needs a valid Frame pointer!");
+ LOG_ASSERT(frame, "Close needs a valid Frame pointer!");
WebCore::Frame* pFrame = (WebCore::Frame*)frame;
WebCore::BackForwardListImpl* list = static_cast<WebCore::BackForwardListImpl*>(pFrame->page()->backForwardList());
@@ -144,7 +145,7 @@ static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
{
- ALOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
+ LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
WebCore::Frame* pFrame = (WebCore::Frame*)frame;
WebCore::Page* page = pFrame->page();
WebCore::HistoryItem* currentItem =
@@ -155,10 +156,10 @@ static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint in
page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
}
-static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
+static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
{
- ALOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
- ALOG_ASSERT(data, "Inflate needs a valid data pointer!");
+ LOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
+ LOG_ASSERT(data, "Inflate needs a valid data pointer!");
// Get the actual bytes and the length from the java array.
const jbyte* bytes = env->GetByteArrayElements(data, NULL);
@@ -167,7 +168,7 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// Inflate the history tree into one HistoryItem or null if the inflation
// failed.
RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
- WebHistoryItem* bridge = new WebHistoryItem(newItem.get());
+ WebHistoryItem* bridge = new WebHistoryItem(env, obj, newItem.get());
newItem->setBridge(bridge);
// Inflate the item recursively. If it fails, that is ok. We'll have an
@@ -177,7 +178,7 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// ptr's value. We can't pass &bytes since we have to send bytes to
// ReleaseByteArrayElements unchanged.
const char* ptr = reinterpret_cast<const char*>(bytes);
- readItemRecursive(newItem.get(), &ptr, (int)size);
+ read_item_recursive(newItem.get(), &ptr, (int)size);
env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
bridge->setActive();
@@ -187,110 +188,49 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// Update the item.
bridge->updateHistoryItem(newItem.get());
- // Ref here because Java expects to adopt the reference, and as such will not
- // call ref on it. However, setBridge has also adopted the reference
- // TODO: This is confusing as hell, clean up ownership and have setBridge
- // take a RefPtr instead of a raw ptr and calling adoptRef on it
- bridge->ref();
- return reinterpret_cast<jint>(bridge);
-}
-
-static void WebHistoryRef(JNIEnv* env, jobject obj, jint ptr)
-{
- if (ptr)
- reinterpret_cast<WebHistoryItem*>(ptr)->ref();
-}
-
-static void WebHistoryUnref(JNIEnv* env, jobject obj, jint ptr)
-{
- if (ptr)
- reinterpret_cast<WebHistoryItem*>(ptr)->deref();
-}
-
-static jobject WebHistoryGetTitle(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_title, false);
-}
-
-static jobject WebHistoryGetUrl(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_url, false);
-}
-
-static jobject WebHistoryGetOriginalUrl(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_originalUrl, false);
-}
-
-static jobject WebHistoryGetFlattenedData(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
-
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
-
- if (!item->m_dataCached) {
- // Try to create a new java byte array.
- jbyteArray b = env->NewByteArray(item->m_data.size());
- if (!b)
- return NULL;
-
- // Write our flattened data to the java array.
- env->SetByteArrayRegion(b, 0, item->m_data.size(),
- (const jbyte*)item->m_data.data());
- item->m_dataCached = env->NewGlobalRef(b);
- env->DeleteLocalRef(b);
- }
- return item->m_dataCached;
-}
-
-static jobject WebHistoryGetFavicon(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- if (!item->m_faviconCached && item->m_favicon) {
- jobject favicon = GraphicsJNI::createBitmap(env,
- item->m_favicon,
- false, NULL);
- item->m_favicon = 0; // Framework now owns the pointer
- item->m_faviconCached = env->NewGlobalRef(favicon);
- env->DeleteLocalRef(favicon);
- }
- return item->m_faviconCached;
}
// 6 empty strings + no document state + children count + 2 scales = 10 unsigned values
// 1 char for isTargetItem.
#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
-void WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& vector, WebCore::HistoryItem* item)
+jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
if (!item)
- return;
+ return NULL;
// Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
- vector.reserveCapacity(HISTORY_MIN_SIZE);
+ v.reserveCapacity(HISTORY_MIN_SIZE);
// Write the top-level history item and then write all the children
// recursively.
- ALOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
- writeItem(vector, item);
- writeChildrenRecursive(vector, item);
+ LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
+ write_item(v, item);
+ write_children_recursive(v, item);
+
+ // Try to create a new java byte array.
+ jbyteArray b = env->NewByteArray(v.size());
+ if (!b)
+ return NULL;
+
+ // Write our flattened data to the java array.
+ env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
+ return b;
+}
+
+WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj,
+ WebCore::HistoryItem* item) : WebCore::AndroidWebHistoryBridge(item) {
+ m_object = env->NewWeakGlobalRef(obj);
+ m_parent = 0;
+}
+
+WebHistoryItem::~WebHistoryItem() {
+ if (m_object) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+ env->DeleteWeakGlobalRef(m_object);
+ }
}
void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
@@ -306,7 +246,7 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// if the parent only has one ref, it is from this WebHistoryItem.
// This means that the matching WebCore::HistoryItem has been freed.
// This can happen during clear().
- ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
return;
}
while (webItem->parent())
@@ -316,7 +256,7 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// If a HistoryItem only exists for page cache, it is possible that
// the parent HistoryItem destroyed before the child HistoryItem. If
// it happens, skip updating.
- ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
return;
}
}
@@ -324,15 +264,23 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
if (!env)
return;
- MutexLocker locker(webItem->m_lock);
+ // Don't do anything if the item has been gc'd already
+ AutoJObject realItem = getRealObject(env, webItem->m_object);
+ if (!realItem.get())
+ return;
- // TODO: Figure out if we can't just use item->urlString() instead...
const WTF::String urlString = WebFrame::convertIDNToUnicode(item->url());
- webItem->m_url = urlString.threadsafeCopy();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = wtfStringToJstring(env, urlString);
const WTF::String originalUrlString = WebFrame::convertIDNToUnicode(item->originalURL());
- webItem->m_originalUrl = originalUrlString.threadsafeCopy();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull())
+ originalUrlStr = wtfStringToJstring(env, originalUrlString);
const WTF::String& titleString = item->title();
- webItem->m_title = titleString.threadsafeCopy();
+ jstring titleStr = NULL;
+ if (!titleString.isNull())
+ titleStr = wtfStringToJstring(env, titleString);
// Try to get the favicon from the history item. For some pages like Grand
// Prix, there are history items with anchors. If the icon fails for the
@@ -346,41 +294,24 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// FIXME: This method should not be used from outside WebCore and will be removed.
// http://trac.webkit.org/changeset/81484
WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16));
- delete webItem->m_favicon;
- webItem->m_favicon = webcoreImageToSkBitmap(icon);
- if (webItem->m_faviconCached) {
- env->DeleteGlobalRef(webItem->m_faviconCached);
- webItem->m_faviconCached = 0;
- }
- webItem->m_data.clear();
- WebHistory::Flatten(env, webItem->m_data, item);
- if (webItem->m_dataCached) {
- env->DeleteGlobalRef(webItem->m_dataCached);
- webItem->m_dataCached = 0;
- }
-}
-
-WebHistoryItem::~WebHistoryItem()
-{
- delete m_favicon;
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env) {
- ALOGW("Failed to get JNIEnv*! Potential memory leak!");
- return;
- }
- if (m_faviconCached) {
- env->DeleteGlobalRef(m_faviconCached);
- m_faviconCached = 0;
- }
- if (m_dataCached) {
- env->DeleteGlobalRef(m_dataCached);
- m_dataCached = 0;
- }
+ if (icon)
+ favicon = webcoreImageToJavaBitmap(env, icon);
+
+ WTF::Vector<char> data;
+ jbyteArray array = WebHistory::Flatten(env, data, item);
+ env->CallVoidMethod(realItem.get(), gWebHistoryItem.mUpdate, urlStr,
+ originalUrlStr, titleStr, favicon, array);
+ env->DeleteLocalRef(urlStr);
+ env->DeleteLocalRef(originalUrlStr);
+ env->DeleteLocalRef(titleStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+ env->DeleteLocalRef(array);
}
static void historyItemChanged(WebCore::HistoryItem* item) {
- ALOG_ASSERT(item, "historyItemChanged called with a null item");
+ LOG_ASSERT(item, "historyItemChanged called with a null item");
if (item->bridge())
item->bridge()->updateHistoryItem(item);
@@ -388,22 +319,22 @@ static void historyItemChanged(WebCore::HistoryItem* item) {
void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
{
- ALOG_ASSERT(item, "newItem must take a valid HistoryItem!");
+ LOG_ASSERT(item, "newItem must take a valid HistoryItem!");
// Item already added. Should only happen when we are inflating the list.
if (item->bridge() || !list.get())
return;
JNIEnv* env = list.env();
- // Create the bridge, make it active, and attach it to the item.
- WebHistoryItem* bridge = new WebHistoryItem(item);
- bridge->setActive();
- item->setBridge(bridge);
// Allocate a blank WebHistoryItem
jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
- jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit,
- reinterpret_cast<int>(bridge));
+ jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit);
env->DeleteLocalRef(clazz);
+ // Create the bridge, make it active, and attach it to the item.
+ WebHistoryItem* bridge = new WebHistoryItem(env, newItem, item);
+ bridge->setActive();
+ item->setBridge(bridge);
+
// Update the history item which will flatten the data and call update on
// the java item.
bridge->updateHistoryItem(item);
@@ -427,7 +358,7 @@ void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mSetCurrentIndex, newIndex);
}
-static void writeString(WTF::Vector<char>& vector, const WTF::String& str)
+static void write_string(WTF::Vector<char>& v, const WTF::String& str)
{
unsigned strLen = str.length();
// Only do work if the string has data.
@@ -435,96 +366,96 @@ static void writeString(WTF::Vector<char>& vector, const WTF::String& str)
// Determine how much to grow the vector. Use the worst case for utf8 to
// avoid reading the string twice. Add sizeof(unsigned) to hold the
// string length in utf8.
- unsigned vectorLen = vector.size() + sizeof(unsigned);
+ unsigned vectorLen = v.size() + sizeof(unsigned);
unsigned length = (strLen << 2) + vectorLen;
// Grow the vector. This will change the value of v.size() but we
// remember the original size above.
- vector.grow(length);
+ v.grow(length);
// Grab the position to write to.
- char* data = vector.begin() + vectorLen;
+ char* data = v.begin() + vectorLen;
// Write the actual string
int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
- ALOGV("Writing string %d %.*s", l, l, data);
+ LOGV("Writing string %d %.*s", l, l, data);
// Go back and write the utf8 length. Subtract sizeof(unsigned) from
// data to get the position to write the length.
memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned));
// Shrink the internal state of the vector so we match what was
// actually written.
- vector.shrink(vectorLen + l);
+ v.shrink(vectorLen + l);
} else
- vector.append((char*)&strLen, sizeof(unsigned));
+ v.append((char*)&strLen, sizeof(unsigned));
}
-static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item)
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
// Original url
- writeString(vector, item->originalURLString());
+ write_string(v, item->originalURLString());
// Url
- writeString(vector, item->urlString());
+ write_string(v, item->urlString());
// Title
- writeString(vector, item->title());
+ write_string(v, item->title());
// Form content type
- writeString(vector, item->formContentType());
+ write_string(v, item->formContentType());
// Form data
const WebCore::FormData* formData = item->formData();
if (formData) {
- writeString(vector, formData->flattenToString());
+ write_string(v, formData->flattenToString());
// save the identifier as it is not included in the flatten data
int64_t id = formData->identifier();
- vector.append((char*)&id, sizeof(int64_t));
+ v.append((char*)&id, sizeof(int64_t));
} else
- writeString(vector, WTF::String()); // Empty constructor does not allocate a buffer.
+ write_string(v, WTF::String()); // Empty constructor does not allocate a buffer.
// Target
- writeString(vector, item->target());
+ write_string(v, item->target());
AndroidWebHistoryBridge* bridge = item->bridge();
- ALOG_ASSERT(bridge, "We should have a bridge here!");
+ LOG_ASSERT(bridge, "We should have a bridge here!");
// Screen scale
const float scale = bridge->scale();
- ALOGV("Writing scale %f", scale);
- vector.append((char*)&scale, sizeof(float));
+ LOGV("Writing scale %f", scale);
+ v.append((char*)&scale, sizeof(float));
const float textWrapScale = bridge->textWrapScale();
- ALOGV("Writing text wrap scale %f", textWrapScale);
- vector.append((char*)&textWrapScale, sizeof(float));
+ LOGV("Writing text wrap scale %f", textWrapScale);
+ v.append((char*)&textWrapScale, sizeof(float));
// Scroll position.
const int scrollX = item->scrollPoint().x();
- vector.append((char*)&scrollX, sizeof(int));
+ v.append((char*)&scrollX, sizeof(int));
const int scrollY = item->scrollPoint().y();
- vector.append((char*)&scrollY, sizeof(int));
+ v.append((char*)&scrollY, sizeof(int));
// Document state
const WTF::Vector<WTF::String>& docState = item->documentState();
WTF::Vector<WTF::String>::const_iterator end = docState.end();
unsigned stateSize = docState.size();
- ALOGV("Writing docState %d", stateSize);
- vector.append((char*)&stateSize, sizeof(unsigned));
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
- writeString(vector, *i);
+ write_string(v, *i);
}
// Is target item
- ALOGV("Writing isTargetItem %d", item->isTargetItem());
- vector.append((char)item->isTargetItem());
+ LOGV("Writing isTargetItem %d", item->isTargetItem());
+ v.append((char)item->isTargetItem());
// Children count
unsigned childCount = item->children().size();
- ALOGV("Writing childCount %d", childCount);
- vector.append((char*)&childCount, sizeof(unsigned));
+ LOGV("Writing childCount %d", childCount);
+ v.append((char*)&childCount, sizeof(unsigned));
}
-static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent)
+static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent)
{
const WebCore::HistoryItemVector& children = parent->children();
WebCore::HistoryItemVector::const_iterator end = children.end();
for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) {
WebCore::HistoryItem* item = (*i).get();
- ALOG_ASSERT(parent->bridge(),
+ LOG_ASSERT(parent->bridge(),
"The parent item should have a bridge object!");
if (!item->bridge()) {
WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
@@ -536,223 +467,116 @@ static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryIt
// parent must not have a parent bridge.
WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
- ALOG_ASSERT(parentBridge->parent() == 0 ||
+ LOG_ASSERT(parentBridge->parent() == 0 ||
bridge->parent() == parentBridge,
"Somehow this item has an incorrect parent");
bridge->setParent(parentBridge);
}
- writeItem(vector, item);
- writeChildrenRecursive(vector, item);
- }
-}
-
-bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0);
-bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0);
-bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0);
-bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0);
-bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0);
-bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0);
-
-bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(unsigned))) {
- ALOGW("\tNot enough data to read unsigned; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(unsigned));
- data += sizeof(unsigned);
- if (dbgLabel)
- ALOGV("Reading %-16s %u", dbgLabel, result);
- return true;
-}
-
-bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int))) {
- ALOGW("Not enough data to read int; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
+ write_item(v, item);
+ write_children_recursive(v, item);
}
-
- memcpy(&result, data, sizeof(int));
- data += sizeof(int);
- if (dbgLabel)
- ALOGV("Reading %-16s %d", dbgLabel, result);
- return true;
}
-bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int64_t))) {
- ALOGW("Not enough data to read int64_t; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(int64_t));
- data += sizeof(int64_t);
- if (dbgLabel)
- ALOGV("Reading %-16s %ll", dbgLabel, result);
- return true;
-}
-
-bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(float))) {
- ALOGW("Not enough data to read float; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(float));
- data += sizeof(float);
- if (dbgLabel)
- ALOGV("Reading %-16s %f", dbgLabel, result);
- return true;
-}
-
-// Note that the return value indicates success or failure, while the result
-// parameter indicates the read value of the bool
-bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(char))) {
- ALOGW("Not enough data to read bool; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- char c;
- memcpy(&c, data, sizeof(char));
- data += sizeof(char);
- if (dbgLabel)
- ALOGV("Reading %-16s %d", dbgLabel, c);
- result = c;
-
- // Valid bool results are 0 or 1
- if ((c != 0) && (c != 1)) {
- ALOGW("Invalid value for bool; tag=\"%s\" end=%p data=%p c=%u",
- dbgLabel ? dbgLabel : "<no tag>", end, data, c);
- return false;
- }
-
- return true;
-}
-
-bool readString(const char*& data, const char* end, String& result, const char* dbgLabel)
-{
- unsigned stringLength;
- if (!readUnsigned(data, end, stringLength)) {
- ALOGW("Not enough data to read string length; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- if (dbgLabel)
- ALOGV("Reading %-16s %d %.*s", dbgLabel, stringLength, stringLength, data);
-
- // If length was 0, there will be no string content, but still return true
- if (!stringLength) {
- result = String();
- return true;
- }
-
- if ((end < data) || ((unsigned)(end - data) < stringLength)) {
- ALOGW("Not enough data to read content; tag=\"%s\" end=%p data=%p stringLength=%u",
- dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength);
- return false;
- }
-
- const unsigned MAX_REASONABLE_STRING_LENGTH = 10000;
- if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
- ALOGW("String length is suspiciously large (>%d); tag=\"%s\" end=%p data=%p stringLength=%u",
- MAX_REASONABLE_STRING_LENGTH, dbgLabel ? dbgLabel : "<no tag>",
- end, data, stringLength);
- }
-
- bool decodeFailed = false;
- static const WebCore::TextEncoding& encoding = WebCore::UTF8Encoding();
- result = encoding.decode(data, stringLength, true, decodeFailed);
- if (decodeFailed) {
- ALOGW("Decode failed, tag=\"%s\" end=%p data=%p stringLength=%u content=\"%s\"",
- dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength,
- result.utf8().data());
- return false;
- }
-
- if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
- ALOGW("\tdecodeFailed=%d (flag is ignored) content=\"%s\"",
- decodeFailed, result.utf8().data());
- }
-
- data += stringLength;
- return true;
-}
-
-static bool readItemRecursive(WebCore::HistoryItem* newItem,
+static bool read_item_recursive(WebCore::HistoryItem* newItem,
const char** pData, int length)
{
- if (!pData || length < HISTORY_MIN_SIZE) {
- ALOGW("readItemRecursive() bad params; pData=%p length=%d", pData, length);
+ if (!pData || length < HISTORY_MIN_SIZE)
return false;
- }
+ const WebCore::TextEncoding& e = WebCore::UTF8Encoding();
const char* data = *pData;
const char* end = data + length;
- String content;
+ int sizeofUnsigned = (int)sizeof(unsigned);
// Read the original url
- if (readString(data, end, content, "Original url"))
- newItem->setOriginalURLString(content);
- else
+ // Read the expected length of the string.
+ unsigned l;
+ memcpy(&l, data, sizeofUnsigned);
+ // Increment data pointer by the size of an unsigned int.
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Original url %d %.*s", l, l, data);
+ // If we have a length, check if that length exceeds the data length
+ // and return null if there is not enough data.
+ if (data + l < end)
+ newItem->setOriginalURLString(e.decode(data, l));
+ else
+ return false;
+ // Increment the data pointer by the length of the string.
+ data += l;
+ }
+ // Check if we have enough data left to continue.
+ if (end - data < sizeofUnsigned)
return false;
// Read the url
- if (readString(data, end, content, "Url"))
- newItem->setURLString(content);
- else
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Url %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setURLString(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
// Read the title
- if (readString(data, end, content, "Title"))
- newItem->setTitle(content);
- else
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Title %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setTitle(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
// Generate a new ResourceRequest object for populating form information.
- // Read the form content type
WTF::String formContentType;
- if (!readString(data, end, formContentType, "Content type"))
- return false;
+ WTF::PassRefPtr<WebCore::FormData> formData = NULL;
- // Read the form data size
- unsigned formDataSize;
- if (!readUnsigned(data, end, formDataSize, "Form data size"))
+ // Read the form content type
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Content type %d %.*s", l, l, data);
+ if (data + l < end)
+ formContentType = e.decode(data, l);
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
// Read the form data
- WTF::RefPtr<WebCore::FormData> formData;
- if (formDataSize) {
- ALOGV("Reading Form data %d %.*s", formDataSize, formDataSize, data);
- if ((end < data) || ((size_t)(end - data) < formDataSize)) {
- ALOGW("\tNot enough data to read form data; returning");
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Form data %d %.*s", l, l, data);
+ if (data + l < end)
+ formData = WebCore::FormData::create(data, l);
+ else
return false;
- }
- formData = WebCore::FormData::create(data, formDataSize);
- data += formDataSize;
+ data += l;
// Read the identifier
- int64_t id;
- if (!readInt64(data, end, id, "Form id"))
- return false;
- if (id)
- formData->setIdentifier(id);
+ {
+ int64_t id;
+ int size = (int)sizeof(int64_t);
+ memcpy(&id, data, size);
+ data += size;
+ if (id)
+ formData->setIdentifier(id);
+ }
}
+ if (end - data < sizeofUnsigned)
+ return false;
// Set up the form info
if (formData != NULL) {
@@ -764,76 +588,112 @@ static bool readItemRecursive(WebCore::HistoryItem* newItem,
}
// Read the target
- if (readString(data, end, content, "Target"))
- newItem->setTarget(content);
- else
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Target %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setTarget(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
AndroidWebHistoryBridge* bridge = newItem->bridge();
- ALOG_ASSERT(bridge, "There should be a bridge object during inflate");
-
- // Read the screen scale
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
float fValue;
- if (readFloat(data, end, fValue, "Screen scale"))
- bridge->setScale(fValue);
- else
- return false;
+ // Read the screen scale
+ memcpy(&fValue, data, sizeof(float));
+ LOGV("Screen scale %f", fValue);
+ bridge->setScale(fValue);
+ data += sizeof(float);
+ memcpy(&fValue, data, sizeofUnsigned);
+ LOGV("Text wrap scale %f", fValue);
+ bridge->setTextWrapScale(fValue);
+ data += sizeof(float);
- // Read the text wrap scale
- if (readFloat(data, end, fValue, "Text wrap scale"))
- bridge->setTextWrapScale(fValue);
- else
+ if (end - data < sizeofUnsigned)
return false;
// Read scroll position.
- int scrollX;
- if (!readInt(data, end, scrollX, "Scroll pos x"))
- return false;
- int scrollY;
- if (!readInt(data, end, scrollY, "Scroll pos y"))
- return false;
+ int scrollX = 0;
+ memcpy(&scrollX, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ int scrollY = 0;
+ memcpy(&scrollY, data, sizeofUnsigned);
+ data += sizeofUnsigned;
newItem->setScrollPoint(IntPoint(scrollX, scrollY));
- // Read the document state
- unsigned docStateCount;
- if (!readUnsigned(data, end, docStateCount, "Doc state count"))
+ if (end - data < sizeofUnsigned)
return false;
- if (docStateCount) {
+
+ // Read the document state
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Document state %d", l);
+ data += sizeofUnsigned;
+ if (l) {
+ // Check if we have enough data to at least parse the sizes of each
+ // document state string.
+ if (data + l * sizeofUnsigned >= end)
+ return false;
// Create a new vector and reserve enough space for the document state.
WTF::Vector<WTF::String> docState;
- docState.reserveCapacity(docStateCount);
- while (docStateCount--) {
- // Read a document state string
- if (readString(data, end, content, "Document state"))
- docState.append(content);
+ docState.reserveCapacity(l);
+ while (l--) {
+ // Check each time if we have enough to parse the length of the next
+ // string.
+ if (end - data < sizeofUnsigned)
+ return false;
+ int strLen;
+ memcpy(&strLen, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (data + strLen < end)
+ docState.append(e.decode(data, strLen));
else
return false;
+ LOGV("\t\t%d %.*s", strLen, strLen, data);
+ data += strLen;
}
newItem->setDocumentState(docState);
}
+ // Check if we have enough to read the next byte
+ if (data >= end)
+ return false;
// Read is target item
- bool c;
- if (readBool(data, end, c, "Target item"))
- newItem->setIsTargetItem(c);
- else
+ // Cast the value to unsigned char in order to make a negative value larger
+ // than 1. A value that is not 0 or 1 is a failure.
+ unsigned char c = (unsigned char)data[0];
+ if (c > 1)
+ return false;
+ LOGV("Target item %d", c);
+ newItem->setIsTargetItem((bool)c);
+ data++;
+ if (end - data < sizeofUnsigned)
return false;
// Read the child count
- unsigned count;
- if (!readUnsigned(data, end, count, "Child count"))
- return false;
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Child count %d", l);
+ data += sizeofUnsigned;
*pData = data;
- if (count) {
- while (count--) {
+ if (l) {
+ // Check if we have the minimum amount need to parse l children.
+ if (data + l * HISTORY_MIN_SIZE >= end)
+ return false;
+ while (l--) {
// No need to check the length each time because read_item_recursive
// will return null if there isn't enough data left to parse.
- WTF::RefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
+ WTF::PassRefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
// Set a bridge that will not call into java.
child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge)));
// Read the child item.
- if (!readItemRecursive(child.get(), pData, end - data))
+ if (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
return false;
+ }
child->bridge()->setActive();
newItem->addChildItem(child);
}
@@ -849,86 +709,83 @@ static bool readItemRecursive(WebCore::HistoryItem* newItem,
// main thread will be incorrect and an assert will fire later.
// In conclusion, define UNIT_TEST only if you know what you are doing.
#ifdef UNIT_TEST
-static void unitTest()
+static void unit_test()
{
- ALOGD("Entering history unit test!");
+ LOGD("Entering history unit test!");
const char* test1 = new char[0];
WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
WebCore::HistoryItem* testItem = item.get();
testItem->setBridge(new WebHistoryItem(0));
- ALOG_ASSERT(!readItemRecursive(testItem, &test1, 0), "0 length array should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!");
delete[] test1;
const char* test2 = new char[2];
- ALOG_ASSERT(!readItemRecursive(testItem, &test2, 2), "Small array should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!");
delete[] test2;
- ALOG_ASSERT(!readItemRecursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
// Original Url
char* test3 = new char[HISTORY_MIN_SIZE];
const char* ptr = (const char*)test3;
memset(test3, 0, HISTORY_MIN_SIZE);
*(int*)test3 = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
// Url
int offset = 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
// Title
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
// Form content type
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
// Form data
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
// Target
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
- offset += 4; // Screen scale
- offset += 4; // Text wrap scale
- offset += 4; // Scroll pos x
- offset += 4; // Scroll pos y
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
+ offset += 4; // Scale
// Document state
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
// Is target item
offset += 1;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(char*)(test3 + offset) = '!';
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
// Child count
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ offset = 36;
// Test document state
- offset = 40;
delete[] test3;
test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)];
memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned));
ptr = (const char*)test3;
*(int*)(test3 + offset) = 1;
*(int*)(test3 + offset + 4) = 20;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
delete[] test3;
test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)];
memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned));
@@ -936,9 +793,8 @@ static void unitTest()
*(int*)(test3 + offset) = 2;
*(int*)(test3 + offset + 4) = 0;
*(int*)(test3 + offset + 8) = 20;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
delete[] test3;
- ALOGD("Leaving history unit test!");
}
#endif
@@ -953,22 +809,8 @@ static JNINativeMethod gWebBackForwardListMethods[] = {
};
static JNINativeMethod gWebHistoryItemMethods[] = {
- { "inflate", "(I[B)I",
- (void*) WebHistoryInflate },
- { "nativeRef", "(I)V",
- (void*) WebHistoryRef },
- { "nativeUnref", "(I)V",
- (void*) WebHistoryUnref },
- { "nativeGetTitle", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetTitle },
- { "nativeGetUrl", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetUrl },
- { "nativeGetOriginalUrl", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetOriginalUrl },
- { "nativeGetFlattenedData", "(I)[B",
- (void*) WebHistoryGetFlattenedData },
- { "nativeGetFavicon", "(I)Landroid/graphics/Bitmap;",
- (void*) WebHistoryGetFavicon },
+ { "inflate", "(I[B)V",
+ (void*) WebHistoryInflate }
};
int registerWebHistory(JNIEnv* env)
@@ -976,27 +818,35 @@ int registerWebHistory(JNIEnv* env)
// Get notified of all changes to history items.
WebCore::notifyHistoryItemChanged = historyItemChanged;
#ifdef UNIT_TEST
- unitTest();
+ unit_test();
#endif
// Find WebHistoryItem, its constructor, and the update method.
jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
- ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
- gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "(I)V");
- ALOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
-
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
+ gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V");
+ LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
+ gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V");
+ LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem");
+
+ // Find the field ids for mTitle and mUrl.
+ gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem");
+ gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem");
env->DeleteLocalRef(clazz);
// Find the WebBackForwardList object and method.
clazz = env->FindClass("android/webkit/WebBackForwardList");
- ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
"(Landroid/webkit/WebHistoryItem;)V");
- ALOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
+ LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
"(I)V");
- ALOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
+ LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
- ALOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
+ LOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
env->DeleteLocalRef(clazz);
int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",
diff --git a/Source/WebKit/android/jni/WebHistory.h b/Source/WebKit/android/jni/WebHistory.h
index e89959c..fc0b340 100644
--- a/Source/WebKit/android/jni/WebHistory.h
+++ b/Source/WebKit/android/jni/WebHistory.h
@@ -28,12 +28,8 @@
#include "AndroidWebHistoryBridge.h"
-#include "PlatformString.h"
-#include "SkBitmap.h"
-
#include <jni.h>
#include <wtf/RefCounted.h>
-#include <wtf/Threading.h>
#include <wtf/Vector.h>
namespace android {
@@ -42,7 +38,7 @@ class AutoJObject;
class WebHistory {
public:
- static void Flatten(JNIEnv*, WTF::Vector<char>&, WebCore::HistoryItem*);
+ static jbyteArray Flatten(JNIEnv*, WTF::Vector<char>&, WebCore::HistoryItem*);
static void AddItem(const AutoJObject&, WebCore::HistoryItem*);
static void RemoveItem(const AutoJObject&, int);
static void UpdateHistoryIndex(const AutoJObject&, int);
@@ -55,36 +51,16 @@ class WebHistoryItem : public WebCore::AndroidWebHistoryBridge {
public:
WebHistoryItem(WebHistoryItem* parent)
: WebCore::AndroidWebHistoryBridge(0)
- , m_favicon(0)
- , m_faviconCached(0)
- , m_dataCached(0)
, m_parent(parent)
- {}
- WebHistoryItem(WebCore::HistoryItem* item)
- : WebCore::AndroidWebHistoryBridge(item)
- , m_favicon(0)
- , m_faviconCached(0)
- , m_dataCached(0)
- , m_parent(0)
- {}
+ , m_object(NULL) { }
+ WebHistoryItem(JNIEnv*, jobject, WebCore::HistoryItem*);
~WebHistoryItem();
void updateHistoryItem(WebCore::HistoryItem* item);
void setParent(WebHistoryItem* parent) { m_parent = parent; }
WebHistoryItem* parent() const { return m_parent.get(); }
-
- // TODO: This is ugly. Really the whole bindings of WebHistoryItem needs to be
- // cleaned up, but this will do for now
- WTF::Mutex m_lock;
- String m_url;
- String m_originalUrl;
- String m_title;
- SkBitmap* m_favicon;
- WTF::Vector<char> m_data;
- jobject m_faviconCached;
- jobject m_dataCached;
-
private:
RefPtr<WebHistoryItem> m_parent;
+ jweak m_object;
};
};
diff --git a/Source/WebKit/android/jni/WebIconDatabase.cpp b/Source/WebKit/android/jni/WebIconDatabase.cpp
index e9219df..d5f8947 100644
--- a/Source/WebKit/android/jni/WebIconDatabase.cpp
+++ b/Source/WebKit/android/jni/WebIconDatabase.cpp
@@ -50,31 +50,18 @@
namespace android {
-SkBitmap* webcoreImageToSkBitmap(WebCore::Image* icon)
+jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon)
{
if (!icon)
- return 0;
+ return NULL;
+ SkBitmap bm;
WebCore::SharedBuffer* buffer = icon->data();
- if (!buffer)
- return 0;
- SkBitmap* bm = new SkBitmap;
- if (!SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(), bm,
- SkBitmap::kNo_Config,
- SkImageDecoder::kDecodePixels_Mode)
- || bm->isNull() || !bm->width() || !bm->height()
- || bm->config() == SkBitmap::kNo_Config) {
- delete bm;
- return 0;
- }
- return bm;
-}
+ if (!buffer || !SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(),
+ &bm, SkBitmap::kNo_Config,
+ SkImageDecoder::kDecodePixels_Mode))
+ return NULL;
-jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon)
-{
- SkBitmap* bm = webcoreImageToSkBitmap(icon);
- if (!bm)
- return 0;
- return GraphicsJNI::createBitmap(env, bm, false, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bm), false, NULL);
}
static WebIconDatabase* gIconDatabaseClient = new WebIconDatabase();
@@ -181,7 +168,7 @@ static void Open(JNIEnv* env, jobject obj, jstring path)
return;
iconDb.setEnabled(true);
iconDb.setClient(gIconDatabaseClient);
- ALOG_ASSERT(path, "No path given to nativeOpen");
+ LOG_ASSERT(path, "No path given to nativeOpen");
WTF::String pathStr = jstringToWtfString(env, path);
WTF::CString fullPath = WebCore::pathByAppendingComponent(pathStr,
WebCore::IconDatabase::defaultDatabaseFilename()).utf8();
@@ -198,12 +185,12 @@ static void Open(JNIEnv* env, jobject obj, jstring path)
}
}
if (didSetPermissions) {
- ALOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
+ LOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
bool res = iconDb.open(pathStr, WebCore::IconDatabase::defaultDatabaseFilename());
if (!res)
- ALOGE("Open failed!");
+ LOGE("Open failed!");
} else
- ALOGE("Failed to set permissions on '%s'", fullPath.data());
+ LOGE("Failed to set permissions on '%s'", fullPath.data());
}
static void Close(JNIEnv* env, jobject obj)
@@ -213,37 +200,37 @@ static void Close(JNIEnv* env, jobject obj)
static void RemoveAllIcons(JNIEnv* env, jobject obj)
{
- ALOGV("Removing all icons");
+ LOGV("Removing all icons");
WebCore::iconDatabase().removeAllIcons();
}
static jobject IconForPageUrl(JNIEnv* env, jobject obj, jstring url)
{
- ALOG_ASSERT(url, "No url given to iconForPageUrl");
+ LOG_ASSERT(url, "No url given to iconForPageUrl");
WTF::String urlStr = jstringToWtfString(env, url);
// FIXME: This method should not be used from outside WebCore and will be removed.
// http://trac.webkit.org/changeset/81484
WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(urlStr, WebCore::IntSize(16, 16));
- ALOGV("Retrieving icon for '%s' %p", urlStr.latin1().data(), icon);
+ LOGV("Retrieving icon for '%s' %p", urlStr.latin1().data(), icon);
return webcoreImageToJavaBitmap(env, icon);
}
static void RetainIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
{
- ALOG_ASSERT(url, "No url given to retainIconForPageUrl");
+ LOG_ASSERT(url, "No url given to retainIconForPageUrl");
WTF::String urlStr = jstringToWtfString(env, url);
- ALOGV("Retaining icon for '%s'", urlStr.latin1().data());
+ LOGV("Retaining icon for '%s'", urlStr.latin1().data());
WebCore::iconDatabase().retainIconForPageURL(urlStr);
}
static void ReleaseIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
{
- ALOG_ASSERT(url, "No url given to releaseIconForPageUrl");
+ LOG_ASSERT(url, "No url given to releaseIconForPageUrl");
WTF::String urlStr = jstringToWtfString(env, url);
- ALOGV("Releasing icon for '%s'", urlStr.latin1().data());
+ LOGV("Releasing icon for '%s'", urlStr.latin1().data());
WebCore::iconDatabase().releaseIconForPageURL(urlStr);
}
@@ -268,12 +255,12 @@ static JNINativeMethod gWebIconDatabaseMethods[] = {
int registerWebIconDatabase(JNIEnv* env)
{
#ifndef NDEBUG
- jclass webIconDatabase = env->FindClass("android/webkit/WebIconDatabaseClassic");
- ALOG_ASSERT(webIconDatabase, "Unable to find class android.webkit.WebIconDatabaseClassic");
+ jclass webIconDatabase = env->FindClass("android/webkit/WebIconDatabase");
+ LOG_ASSERT(webIconDatabase, "Unable to find class android.webkit.WebIconDatabase");
env->DeleteLocalRef(webIconDatabase);
#endif
- return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabaseClassic",
+ return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabase",
gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods));
}
diff --git a/Source/WebKit/android/jni/WebIconDatabase.h b/Source/WebKit/android/jni/WebIconDatabase.h
index 7b7c937..3011b9f 100644
--- a/Source/WebKit/android/jni/WebIconDatabase.h
+++ b/Source/WebKit/android/jni/WebIconDatabase.h
@@ -28,7 +28,6 @@
#include "IconDatabaseClient.h"
#include "PlatformString.h"
-#include "SkBitmap.h"
#include "utils/threads.h"
#include <jni.h>
#include <wtf/Vector.h>
@@ -74,7 +73,6 @@ namespace android {
bool mDeliveryRequested;
};
- SkBitmap* webcoreImageToSkBitmap(WebCore::Image* icon);
jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon);
};
diff --git a/Source/WebKit/android/jni/WebSettings.cpp b/Source/WebKit/android/jni/WebSettings.cpp
index 467da3d..dd847e8 100644
--- a/Source/WebKit/android/jni/WebSettings.cpp
+++ b/Source/WebKit/android/jni/WebSettings.cpp
@@ -49,7 +49,9 @@
#include "Settings.h"
#include "WebCoreFrameBridge.h"
#include "WebCoreJni.h"
+#if USE(V8)
#include "WorkerContextExecutionProxy.h"
+#endif
#include "WebRequestContext.h"
#include "WebViewCore.h"
@@ -120,9 +122,6 @@ struct FieldIds {
mGeolocationEnabled = env->GetFieldID(clazz, "mGeolocationEnabled", "Z");
mGeolocationDatabasePath = env->GetFieldID(clazz, "mGeolocationDatabasePath", "Ljava/lang/String;");
mXSSAuditorEnabled = env->GetFieldID(clazz, "mXSSAuditorEnabled", "Z");
-#if ENABLE(LINK_PREFETCH)
- mLinkPrefetchEnabled = env->GetFieldID(clazz, "mLinkPrefetchEnabled", "Z");
-#endif
mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
"mJavaScriptCanOpenWindowsAutomatically", "Z");
mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
@@ -135,8 +134,8 @@ struct FieldIds {
mPageCacheCapacity = env->GetFieldID(clazz, "mPageCacheCapacity", "I");
#if ENABLE(WEB_AUTOFILL)
mAutoFillEnabled = env->GetFieldID(clazz, "mAutoFillEnabled", "Z");
- mAutoFillProfile = env->GetFieldID(clazz, "mAutoFillProfile", "Landroid/webkit/WebSettingsClassic$AutoFillProfile;");
- jclass autoFillProfileClass = env->FindClass("android/webkit/WebSettingsClassic$AutoFillProfile");
+ mAutoFillProfile = env->GetFieldID(clazz, "mAutoFillProfile", "Landroid/webkit/WebSettings$AutoFillProfile;");
+ jclass autoFillProfileClass = env->FindClass("android/webkit/WebSettings$AutoFillProfile");
mAutoFillProfileFullName = env->GetFieldID(autoFillProfileClass, "mFullName", "Ljava/lang/String;");
mAutoFillProfileEmailAddress = env->GetFieldID(autoFillProfileClass, "mEmailAddress", "Ljava/lang/String;");
mAutoFillProfileCompanyName = env->GetFieldID(autoFillProfileClass, "mCompanyName", "Ljava/lang/String;");
@@ -149,57 +148,57 @@ struct FieldIds {
mAutoFillProfilePhoneNumber = env->GetFieldID(autoFillProfileClass, "mPhoneNumber", "Ljava/lang/String;");
env->DeleteLocalRef(autoFillProfileClass);
#endif
+#if USE(CHROME_NETWORK_STACK)
mOverrideCacheMode = env->GetFieldID(clazz, "mOverrideCacheMode", "I");
- mPasswordEchoEnabled = env->GetFieldID(clazz, "mPasswordEchoEnabled", "Z");
-
- ALOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm");
- ALOG_ASSERT(mTextSize, "Could not find field mTextSize");
- ALOG_ASSERT(mStandardFontFamily, "Could not find field mStandardFontFamily");
- ALOG_ASSERT(mFixedFontFamily, "Could not find field mFixedFontFamily");
- ALOG_ASSERT(mSansSerifFontFamily, "Could not find field mSansSerifFontFamily");
- ALOG_ASSERT(mSerifFontFamily, "Could not find field mSerifFontFamily");
- ALOG_ASSERT(mCursiveFontFamily, "Could not find field mCursiveFontFamily");
- ALOG_ASSERT(mFantasyFontFamily, "Could not find field mFantasyFontFamily");
- ALOG_ASSERT(mDefaultTextEncoding, "Could not find field mDefaultTextEncoding");
- ALOG_ASSERT(mGetUserAgentString, "Could not find method getUserAgentString");
- ALOG_ASSERT(mGetAcceptLanguage, "Could not find method getAcceptLanguage");
- ALOG_ASSERT(mMinimumFontSize, "Could not find field mMinimumFontSize");
- ALOG_ASSERT(mMinimumLogicalFontSize, "Could not find field mMinimumLogicalFontSize");
- ALOG_ASSERT(mDefaultFontSize, "Could not find field mDefaultFontSize");
- ALOG_ASSERT(mDefaultFixedFontSize, "Could not find field mDefaultFixedFontSize");
- ALOG_ASSERT(mLoadsImagesAutomatically, "Could not find field mLoadsImagesAutomatically");
+#endif
+
+ LOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm");
+ LOG_ASSERT(mTextSize, "Could not find field mTextSize");
+ LOG_ASSERT(mStandardFontFamily, "Could not find field mStandardFontFamily");
+ LOG_ASSERT(mFixedFontFamily, "Could not find field mFixedFontFamily");
+ LOG_ASSERT(mSansSerifFontFamily, "Could not find field mSansSerifFontFamily");
+ LOG_ASSERT(mSerifFontFamily, "Could not find field mSerifFontFamily");
+ LOG_ASSERT(mCursiveFontFamily, "Could not find field mCursiveFontFamily");
+ LOG_ASSERT(mFantasyFontFamily, "Could not find field mFantasyFontFamily");
+ LOG_ASSERT(mDefaultTextEncoding, "Could not find field mDefaultTextEncoding");
+ LOG_ASSERT(mGetUserAgentString, "Could not find method getUserAgentString");
+ LOG_ASSERT(mGetAcceptLanguage, "Could not find method getAcceptLanguage");
+ LOG_ASSERT(mMinimumFontSize, "Could not find field mMinimumFontSize");
+ LOG_ASSERT(mMinimumLogicalFontSize, "Could not find field mMinimumLogicalFontSize");
+ LOG_ASSERT(mDefaultFontSize, "Could not find field mDefaultFontSize");
+ LOG_ASSERT(mDefaultFixedFontSize, "Could not find field mDefaultFixedFontSize");
+ LOG_ASSERT(mLoadsImagesAutomatically, "Could not find field mLoadsImagesAutomatically");
#ifdef ANDROID_BLOCK_NETWORK_IMAGE
- ALOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage");
+ LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage");
#endif
- ALOG_ASSERT(mBlockNetworkLoads, "Could not find field mBlockNetworkLoads");
- ALOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
- ALOG_ASSERT(mAllowUniversalAccessFromFileURLs,
+ LOG_ASSERT(mBlockNetworkLoads, "Could not find field mBlockNetworkLoads");
+ LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
+ LOG_ASSERT(mAllowUniversalAccessFromFileURLs,
"Could not find field mAllowUniversalAccessFromFileURLs");
- ALOG_ASSERT(mAllowFileAccessFromFileURLs,
+ LOG_ASSERT(mAllowFileAccessFromFileURLs,
"Could not find field mAllowFileAccessFromFileURLs");
- ALOG_ASSERT(mPluginState, "Could not find field mPluginState");
+ LOG_ASSERT(mPluginState, "Could not find field mPluginState");
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
- ALOG_ASSERT(mAppCacheEnabled, "Could not find field mAppCacheEnabled");
- ALOG_ASSERT(mAppCachePath, "Could not find field mAppCachePath");
- ALOG_ASSERT(mAppCacheMaxSize, "Could not find field mAppCacheMaxSize");
+ LOG_ASSERT(mAppCacheEnabled, "Could not find field mAppCacheEnabled");
+ LOG_ASSERT(mAppCachePath, "Could not find field mAppCachePath");
+ LOG_ASSERT(mAppCacheMaxSize, "Could not find field mAppCacheMaxSize");
#endif
#if ENABLE(WORKERS)
- ALOG_ASSERT(mWorkersEnabled, "Could not find field mWorkersEnabled");
+ LOG_ASSERT(mWorkersEnabled, "Could not find field mWorkersEnabled");
#endif
- ALOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically,
+ LOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically,
"Could not find field mJavaScriptCanOpenWindowsAutomatically");
- ALOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport");
- ALOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows");
- ALOG_ASSERT(mShrinksStandaloneImagesToFit, "Could not find field mShrinksStandaloneImagesToFit");
- ALOG_ASSERT(mMaximumDecodedImageSize, "Could not find field mMaximumDecodedImageSize");
- ALOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree");
- ALOG_ASSERT(mPageCacheCapacity, "Could not find field mPageCacheCapacity");
- ALOG_ASSERT(mPasswordEchoEnabled, "Could not find field mPasswordEchoEnabled");
+ LOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport");
+ LOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows");
+ LOG_ASSERT(mShrinksStandaloneImagesToFit, "Could not find field mShrinksStandaloneImagesToFit");
+ LOG_ASSERT(mMaximumDecodedImageSize, "Could not find field mMaximumDecodedImageSize");
+ LOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree");
+ LOG_ASSERT(mPageCacheCapacity, "Could not find field mPageCacheCapacity");
jclass enumClass = env->FindClass("java/lang/Enum");
- ALOG_ASSERT(enumClass, "Could not find Enum class!");
+ LOG_ASSERT(enumClass, "Could not find Enum class!");
mOrdinal = env->GetMethodID(enumClass, "ordinal", "()I");
- ALOG_ASSERT(mOrdinal, "Could not find method ordinal");
+ LOG_ASSERT(mOrdinal, "Could not find method ordinal");
env->DeleteLocalRef(enumClass);
}
@@ -258,9 +257,6 @@ struct FieldIds {
jfieldID mGeolocationEnabled;
jfieldID mGeolocationDatabasePath;
jfieldID mXSSAuditorEnabled;
-#if ENABLE(LINK_PREFETCH)
- jfieldID mLinkPrefetchEnabled;
-#endif
#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
jfieldID mDatabasePath;
jfieldID mDatabasePathHasBeenSet;
@@ -279,8 +275,9 @@ struct FieldIds {
jfieldID mAutoFillProfileCountry;
jfieldID mAutoFillProfilePhoneNumber;
#endif
+#if USE(CHROME_NETWORK_STACK)
jfieldID mOverrideCacheMode;
- jfieldID mPasswordEchoEnabled;
+#endif
};
static struct FieldIds* gFieldIds;
@@ -326,7 +323,7 @@ public:
static void Sync(JNIEnv* env, jobject obj, jint frame)
{
WebCore::Frame* pFrame = (WebCore::Frame*)frame;
- ALOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__);
+ LOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__);
WebCore::Settings* s = pFrame->settings();
if (!s)
return;
@@ -342,7 +339,7 @@ public:
pFrame->document()->styleSelectorChanged(WebCore::RecalcStyleImmediately);
if (pFrame->document()->renderer()) {
recursiveCleanupForFullLayout(pFrame->document()->renderer());
- ALOG_ASSERT(pFrame->view(), "No view for this frame when trying to relayout");
+ LOG_ASSERT(pFrame->view(), "No view for this frame when trying to relayout");
pFrame->view()->layout();
// FIXME: This call used to scroll the page to put the focus into view.
// It worked on the WebViewCore, but now scrolling is done outside of the
@@ -380,6 +377,7 @@ public:
str = (jstring)env->CallObjectMethod(obj, gFieldIds->mGetUserAgentString);
WebFrame::getWebFrame(pFrame)->setUserAgent(jstringToWtfString(env, str));
+#if USE(CHROME_NETWORK_STACK)
WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextUserAgent();
jint cacheMode = env->GetIntField(obj, gFieldIds->mOverrideCacheMode);
@@ -387,6 +385,7 @@ public:
str = (jstring)env->CallObjectMethod(obj, gFieldIds->mGetAcceptLanguage);
WebRequestContext::setAcceptLanguage(jstringToWtfString(env, str));
+#endif
jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize);
s->setMinimumFontSize(size);
@@ -424,11 +423,6 @@ public:
flag = env->GetBooleanField(obj, gFieldIds->mAllowFileAccessFromFileURLs);
s->setAllowFileAccessFromFileURLs(flag);
- // Hyperlink auditing (the ping attribute) has similar privacy
- // considerations as does the running of JavaScript, so to keep the UI
- // simpler, we leverage the same setting.
- s->setHyperlinkAuditingEnabled(flag);
-
// ON = 0
// ON_DEMAND = 1
// OFF = 2
@@ -440,38 +434,22 @@ public:
#endif
#if ENABLE(OFFLINE_WEB_APPLICATIONS)
- // We only enable AppCache if it's been enabled with a call to
- // setAppCacheEnabled() and if a valid path has been supplied to
- // setAppCachePath(). Note that the path is applied to all WebViews
- // whereas enabling is applied per WebView.
-
- // WebCore asserts that the path is only set once. Since the path is
- // shared between WebViews, we can't do the required checks to guard
- // against this in the Java WebSettings.
- bool isPathValid = false;
- if (cacheStorage().cacheDirectory().isNull()) {
- str = static_cast<jstring>(env->GetObjectField(obj, gFieldIds->mAppCachePath));
- // Check for non-null string as an optimization, as this is the common case.
- if (str) {
- String path = jstringToWtfString(env, str);
- ALOG_ASSERT(!path.empty(), "Java side should never send empty string for AppCache path");
+ flag = env->GetBooleanField(obj, gFieldIds->mAppCacheEnabled);
+ s->setOfflineWebApplicationCacheEnabled(flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mAppCachePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ if (path.length() && cacheStorage().cacheDirectory().isNull()) {
+ cacheStorage().setCacheDirectory(path);
// This database is created on the first load. If the file
// doesn't exist, we create it and set its permissions. The
// filename must match that in ApplicationCacheStorage.cpp.
String filename = pathByAppendingComponent(path, "ApplicationCache.db");
- int fd = open(filename.utf8().data(), O_CREAT, permissionFlags660);
- if (fd >= 0) {
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
close(fd);
- cacheStorage().setCacheDirectory(path);
- isPathValid = true;
- }
}
- } else
- isPathValid = true;
-
- flag = env->GetBooleanField(obj, gFieldIds->mAppCacheEnabled);
- s->setOfflineWebApplicationCacheEnabled(flag && isPathValid);
-
+ }
jlong maxsize = env->GetLongField(obj, gFieldIds->mAppCacheMaxSize);
cacheStorage().setMaximumSize(maxsize);
#endif
@@ -569,11 +547,6 @@ public:
flag = env->GetBooleanField(obj, gFieldIds->mXSSAuditorEnabled);
s->setXSSAuditorEnabled(flag);
-#if ENABLE(LINK_PREFETCH)
- flag = env->GetBooleanField(obj, gFieldIds->mLinkPrefetchEnabled);
- s->setLinkPrefetchEnabled(flag);
-#endif
-
size = env->GetIntField(obj, gFieldIds->mPageCacheCapacity);
if (size > 0) {
s->setUsesPageCache(true);
@@ -612,10 +585,6 @@ public:
// This is required to enable the XMLTreeViewer when loading an XML document that
// has no style attached to it. http://trac.webkit.org/changeset/79799
s->setDeveloperExtrasEnabled(true);
- s->setSpatialNavigationEnabled(true);
- bool echoPassword = env->GetBooleanField(obj,
- gFieldIds->mPasswordEchoEnabled);
- s->setPasswordEchoEnabled(echoPassword);
}
};
@@ -631,11 +600,11 @@ static JNINativeMethod gWebSettingsMethods[] = {
int registerWebSettings(JNIEnv* env)
{
- jclass clazz = env->FindClass("android/webkit/WebSettingsClassic");
- ALOG_ASSERT(clazz, "Unable to find class WebSettingsClassic!");
+ jclass clazz = env->FindClass("android/webkit/WebSettings");
+ LOG_ASSERT(clazz, "Unable to find class WebSettings!");
gFieldIds = new FieldIds(env, clazz);
env->DeleteLocalRef(clazz);
- return jniRegisterNativeMethods(env, "android/webkit/WebSettingsClassic",
+ return jniRegisterNativeMethods(env, "android/webkit/WebSettings",
gWebSettingsMethods, NELEM(gWebSettingsMethods));
}
diff --git a/Source/WebKit/android/jni/WebStorage.cpp b/Source/WebKit/android/jni/WebStorage.cpp
index 66a3517..9ce207d 100644
--- a/Source/WebKit/android/jni/WebStorage.cpp
+++ b/Source/WebKit/android/jni/WebStorage.cpp
@@ -174,12 +174,12 @@ static JNINativeMethod gWebStorageMethods[] = {
int registerWebStorage(JNIEnv* env)
{
#ifndef NDEBUG
- jclass webStorage = env->FindClass("android/webkit/WebStorageClassic");
- ALOG_ASSERT(webStorage, "Unable to find class android.webkit.WebStorageClassic");
+ jclass webStorage = env->FindClass("android/webkit/WebStorage");
+ LOG_ASSERT(webStorage, "Unable to find class android.webkit.WebStorage");
env->DeleteLocalRef(webStorage);
#endif
- return jniRegisterNativeMethods(env, "android/webkit/WebStorageClassic",
+ return jniRegisterNativeMethods(env, "android/webkit/WebStorage",
gWebStorageMethods, NELEM(gWebStorageMethods));
}
diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp
index f17e573..1b53a6e 100644
--- a/Source/WebKit/android/jni/WebViewCore.cpp
+++ b/Source/WebKit/android/jni/WebViewCore.cpp
@@ -29,9 +29,10 @@
#include "WebViewCore.h"
#include "AccessibilityObject.h"
-#include "AndroidHitTestResult.h"
#include "Attribute.h"
-#include "content/address_detector.h"
+#include "BaseLayerAndroid.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
#include "Chrome.h"
#include "ChromeClientAndroid.h"
#include "ChromiumIncludes.h"
@@ -42,7 +43,6 @@
#include "CSSValueKeywords.h"
#include "DatabaseTracker.h"
#include "Document.h"
-#include "DocumentMarkerController.h"
#include "DOMWindow.h"
#include "DOMSelection.h"
#include "Element.h"
@@ -53,7 +53,6 @@
#include "ExceptionCode.h"
#include "FocusController.h"
#include "Font.h"
-#include "FontCache.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClientAndroid.h"
@@ -62,7 +61,6 @@
#include "Geolocation.h"
#include "GraphicsContext.h"
#include "GraphicsJNI.h"
-#include "GraphicsOperationCollection.h"
#include "HTMLAnchorElement.h"
#include "HTMLAreaElement.h"
#include "HTMLElement.h"
@@ -80,7 +78,6 @@
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "InlineTextBox.h"
-#include "KeyboardEvent.h"
#include "MemoryUsage.h"
#include "NamedNodeMap.h"
#include "Navigator.h"
@@ -88,8 +85,6 @@
#include "NodeList.h"
#include "Page.h"
#include "PageGroup.h"
-#include "PictureLayerContent.h"
-#include "PicturePileLayerContent.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformString.h"
#include "PluginWidgetAndroid.h"
@@ -98,7 +93,6 @@
#include "ProgressTracker.h"
#include "Range.h"
#include "RenderBox.h"
-#include "RenderImage.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderPart.h"
@@ -109,44 +103,39 @@
#include "ResourceRequest.h"
#include "RuntimeEnabledFeatures.h"
#include "SchemeRegistry.h"
-#include "ScopedLocalRef.h"
-#include "ScriptController.h"
#include "SelectionController.h"
-#include "SelectText.h"
#include "Settings.h"
#include "SkANP.h"
#include "SkTemplates.h"
#include "SkTDArray.h"
#include "SkTypes.h"
#include "SkCanvas.h"
-#include "SkGraphics.h"
#include "SkPicture.h"
#include "SkUtils.h"
#include "Text.h"
-#include "TextIterator.h"
-#include "TilesManager.h"
#include "TypingCommand.h"
#include "WebCache.h"
#include "WebCoreFrameBridge.h"
-#include "WebCoreJni.h"
#include "WebFrameView.h"
#include "WindowsKeyboardCodes.h"
#include "android_graphics.h"
#include "autofill/WebAutofill.h"
#include "htmlediting.h"
#include "markup.h"
-#include "visible_units.h"
#include <JNIHelp.h>
#include <JNIUtility.h>
-#include <androidfw/KeycodeLabels.h>
-#include <cutils/properties.h>
-#include <v8.h>
+#include <ui/KeycodeLabels.h>
#include <wtf/CurrentTime.h>
#include <wtf/text/AtomicString.h>
-#include <wtf/text/CString.h>
#include <wtf/text/StringImpl.h>
+#if USE(V8)
+#include "ScriptController.h"
+#include "V8Counters.h"
+#include <wtf/text/CString.h>
+#endif
+
#if DEBUG_NAV_UI
#include "SkTime.h"
#endif
@@ -164,85 +153,34 @@ FILE* gDomTreeFile = 0;
FILE* gRenderTreeFile = 0;
#endif
-#include "BaseLayerAndroid.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
#if USE(ACCELERATED_COMPOSITING)
#include "GraphicsLayerAndroid.h"
#include "RenderLayerCompositor.h"
#endif
-#define FOREGROUND_TIMER_INTERVAL 0.004 // 4ms
-#define BACKGROUND_TIMER_INTERVAL 1.0 // 1s
+#if USE(V8)
+#include <v8.h>
+#endif
-// How many ms to wait for the scroll to "settle" before we will consider doing
-// prerenders
-#define PRERENDER_AFTER_SCROLL_DELAY 750
+// In some cases, too many invalidations passed to the UI will slow us down.
+// Limit ourselves to 32 rectangles, past this just send the area bounds to the UI.
+// see WebViewCore::recordPictureSet().
+#define MAX_INVALIDATIONS 32
-#define TOUCH_FLAG_HIT_HANDLER 0x1
-#define TOUCH_FLAG_PREVENT_DEFAULT 0x2
+/* We pass this flag when recording the actual content, so that we don't spend
+ time actually regionizing complex path clips, when all we really want to do
+ is record them.
+ */
+#define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag
////////////////////////////////////////////////////////////////////////////////////////////////
namespace android {
-// Copied from CacheBuilder, not sure if this is needed/correct
-IntRect getAreaRect(const HTMLAreaElement* area)
-{
- Node* node = area->document();
- while ((node = node->traverseNextNode()) != NULL) {
- RenderObject* renderer = node->renderer();
- if (renderer && renderer->isRenderImage()) {
- RenderImage* image = static_cast<RenderImage*>(renderer);
- HTMLMapElement* map = image->imageMap();
- if (map) {
- Node* n;
- for (n = map->firstChild(); n;
- n = n->traverseNextNode(map)) {
- if (n == area) {
- if (area->isDefault())
- return image->absoluteBoundingBoxRect();
- return area->computeRect(image);
- }
- }
- }
- }
- }
- return IntRect();
-}
-
-// Copied from CacheBuilder, not sure if this is needed/correct
-// TODO: See if this is even needed (I suspect not), and if not remove it
-bool validNode(Frame* startFrame, void* matchFrame,
- void* matchNode)
-{
- if (matchFrame == startFrame) {
- if (matchNode == NULL)
- return true;
- Node* node = startFrame->document();
- while (node != NULL) {
- if (node == matchNode) {
- const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ?
- getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect();
- // Consider nodes with empty rects that are not at the origin
- // to be valid, since news.google.com has valid nodes like this
- if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty())
- return false;
- return true;
- }
- node = node->traverseNextNode();
- }
- return false;
- }
- Frame* child = startFrame->tree()->firstChild();
- while (child) {
- bool result = validNode(child, matchFrame, matchNode);
- if (result)
- return result;
- child = child->tree()->nextSibling();
- }
- return false;
-}
-
static SkTDArray<WebViewCore*> gInstanceList;
void WebViewCore::addInstance(WebViewCore* inst) {
@@ -251,7 +189,7 @@ void WebViewCore::addInstance(WebViewCore* inst) {
void WebViewCore::removeInstance(WebViewCore* inst) {
int index = gInstanceList.find(inst);
- ALOG_ASSERT(index >= 0, "RemoveInstance inst not found");
+ LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
if (index >= 0) {
gInstanceList.removeShuffle(index);
}
@@ -304,6 +242,8 @@ bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
// ----------------------------------------------------------------------------
+#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
+
// Field ids for WebViewCore
struct WebViewCoreFields {
jfieldID m_nativeClass;
@@ -314,6 +254,7 @@ struct WebViewCoreFields {
jfieldID m_viewportMaximumScale;
jfieldID m_viewportUserScalable;
jfieldID m_viewportDensityDpi;
+ jfieldID m_webView;
jfieldID m_drawIsPaused;
jfieldID m_lowMemoryUsageMb;
jfieldID m_highMemoryUsageMb;
@@ -326,6 +267,7 @@ struct WebViewCore::JavaGlue {
jweak m_obj;
jmethodID m_scrollTo;
jmethodID m_contentDraw;
+ jmethodID m_layersDraw;
jmethodID m_requestListBox;
jmethodID m_openFileChooser;
jmethodID m_requestSingleListBox;
@@ -334,18 +276,17 @@ struct WebViewCore::JavaGlue {
jmethodID m_jsPrompt;
jmethodID m_jsUnload;
jmethodID m_jsInterrupt;
- jmethodID m_getWebView;
jmethodID m_didFirstLayout;
jmethodID m_updateViewport;
jmethodID m_sendNotifyProgressFinished;
jmethodID m_sendViewInvalidate;
jmethodID m_updateTextfield;
jmethodID m_updateTextSelection;
- jmethodID m_updateTextSizeAndScroll;
jmethodID m_clearTextEntry;
jmethodID m_restoreScale;
jmethodID m_needTouchEvents;
jmethodID m_requestKeyboard;
+ jmethodID m_requestKeyboardWithSelection;
jmethodID m_exceededDatabaseQuota;
jmethodID m_reachedMaxAppCacheSize;
jmethodID m_populateVisitedLinks;
@@ -354,7 +295,7 @@ struct WebViewCore::JavaGlue {
jmethodID m_getDeviceMotionService;
jmethodID m_getDeviceOrientationService;
jmethodID m_addMessageToConsole;
- jmethodID m_focusNodeChanged;
+ jmethodID m_formDidBlur;
jmethodID m_getPluginClass;
jmethodID m_showFullScreenPlugin;
jmethodID m_hideFullScreenPlugin;
@@ -364,17 +305,14 @@ struct WebViewCore::JavaGlue {
jmethodID m_destroySurface;
jmethodID m_getContext;
jmethodID m_keepScreenOn;
+ jmethodID m_sendFindAgain;
jmethodID m_showRect;
jmethodID m_centerFitRect;
jmethodID m_setScrollbarModes;
jmethodID m_setInstallableWebApp;
jmethodID m_enterFullscreenForVideoLayer;
- jmethodID m_exitFullscreenVideo;
jmethodID m_setWebTextViewAutoFillable;
jmethodID m_selectAt;
- jmethodID m_initEditField;
- jmethodID m_chromeCanTakeFocus;
- jmethodID m_chromeTakeFocus;
AutoJObject object(JNIEnv* env) {
// We hold a weak reference to the Java WebViewCore to avoid memeory
// leaks due to circular references when WebView.destroy() is not
@@ -387,23 +325,6 @@ struct WebViewCore::JavaGlue {
}
};
-struct WebViewCore::TextFieldInitDataGlue {
- jmethodID m_constructor;
- jfieldID m_fieldPointer;
- jfieldID m_text;
- jfieldID m_type;
- jfieldID m_isSpellCheckEnabled;
- jfieldID m_isTextFieldNext;
- jfieldID m_isTextFieldPrev;
- jfieldID m_isAutoCompleteEnabled;
- jfieldID m_name;
- jfieldID m_label;
- jfieldID m_maxLength;
- jfieldID m_contentBounds;
- jfieldID m_nodeLayerId;
- jfieldID m_contentRect;
-};
-
/*
* WebViewCore Implementation
*/
@@ -411,37 +332,58 @@ struct WebViewCore::TextFieldInitDataGlue {
static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
{
jmethodID m = env->GetMethodID(clazz, name, signature);
- ALOG_ASSERT(m, "Could not find method %s", name);
+ LOG_ASSERT(m, "Could not find method %s", name);
return m;
}
+Mutex WebViewCore::gFrameCacheMutex;
+Mutex WebViewCore::gCursorBoundsMutex;
+
WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
- : m_touchGeneration(0)
+ : m_frameCacheKit(0)
+ , m_navPictureKit(0)
+ , m_moveGeneration(0)
+ , m_touchGeneration(0)
, m_lastGeneration(0)
+ , m_updatedFrameCache(true)
+ , m_findIsUp(false)
+ , m_hasCursorBounds(false)
+ , m_cursorBounds(WebCore::IntRect(0, 0, 0, 0))
+ , m_cursorHitBounds(WebCore::IntRect(0, 0, 0, 0))
+ , m_cursorFrame(0)
+ , m_cursorLocation(WebCore::IntPoint(0, 0))
+ , m_cursorNode(0)
, m_javaGlue(new JavaGlue)
- , m_textFieldInitDataGlue(new TextFieldInitDataGlue)
, m_mainFrame(mainframe)
, m_popupReply(0)
+ , m_lastFocused(0)
+ , m_lastFocusedBounds(WebCore::IntRect(0,0,0,0))
+ , m_blurringNodePointer(0)
+ , m_lastFocusedSelStart(0)
+ , m_lastFocusedSelEnd(0)
, m_blockTextfieldUpdates(false)
, m_focusBoundsChanged(false)
, m_skipContentDraw(false)
, m_textGeneration(0)
+ , m_temp(0)
+ , m_tempPict(0)
, m_maxXScroll(320/4)
, m_maxYScroll(240/4)
, m_scrollOffsetX(0)
, m_scrollOffsetY(0)
, m_mousePos(WebCore::IntPoint(0,0))
+ , m_frameCacheOutOfDate(true)
+ , m_progressDone(false)
, m_screenWidth(320)
, m_screenHeight(240)
, m_textWrapWidth(320)
, m_scale(1.0f)
+ , m_domtree_version(0)
+ , m_check_domtree_version(true)
, m_groupForVisitedLinks(0)
, m_isPaused(false)
, m_cacheMode(0)
- , m_fullscreenVideoMode(false)
- , m_matchCount(0)
- , m_activeMatchIndex(0)
- , m_activeMatch(0)
+ , m_shouldPaintCaret(true)
, m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
, m_screenOnCounter(0)
, m_currentNodeDomNavigationAxis(0)
@@ -449,35 +391,36 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m
#if ENABLE(TOUCH_EVENTS)
, m_forwardingTouchEvents(false)
#endif
+#if USE(CHROME_NETWORK_STACK)
, m_webRequestContext(0)
- , m_prerenderEnabled(false)
+#endif
{
- ALOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
+ LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
jclass clazz = env->GetObjectClass(javaWebViewCore);
m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V");
m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V");
m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
- m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;");
m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
- m_javaGlue->m_getWebView = GetJMethod(env, clazz, "getWebView", "()Landroid/webkit/WebView;");
m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
- m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIIII)V");
- m_javaGlue->m_updateTextSizeAndScroll = GetJMethod(env, clazz, "updateTextSizeAndScroll", "(IIIII)V");
+ m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V");
m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
+ m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
@@ -486,7 +429,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m
m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;");
m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;");
m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
- m_javaGlue->m_focusNodeChanged = GetJMethod(env, clazz, "focusNodeChanged", "(ILandroid/webkit/WebViewCore$WebKitHitTest;)V");
+ m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V");
m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;II)V");
m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
@@ -496,40 +439,20 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m
m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V");
+ m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V");
#if ENABLE(VIDEO)
m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V");
- m_javaGlue->m_exitFullscreenVideo = GetJMethod(env, clazz, "exitFullscreenVideo", "()V");
#endif
m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V");
- m_javaGlue->m_initEditField = GetJMethod(env, clazz, "initEditField", "(IIILandroid/webkit/WebViewCore$TextFieldInitData;)V");
- m_javaGlue->m_chromeCanTakeFocus = GetJMethod(env, clazz, "chromeCanTakeFocus", "(I)Z");
- m_javaGlue->m_chromeTakeFocus = GetJMethod(env, clazz, "chromeTakeFocus", "(I)V");
env->DeleteLocalRef(clazz);
env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
- jclass tfidClazz = env->FindClass("android/webkit/WebViewCore$TextFieldInitData");
- m_textFieldInitDataGlue->m_fieldPointer = env->GetFieldID(tfidClazz, "mFieldPointer", "I");
- m_textFieldInitDataGlue->m_text = env->GetFieldID(tfidClazz, "mText", "Ljava/lang/String;");
- m_textFieldInitDataGlue->m_type = env->GetFieldID(tfidClazz, "mType", "I");
- m_textFieldInitDataGlue->m_isSpellCheckEnabled = env->GetFieldID(tfidClazz, "mIsSpellCheckEnabled", "Z");
- m_textFieldInitDataGlue->m_isTextFieldNext = env->GetFieldID(tfidClazz, "mIsTextFieldNext", "Z");
- m_textFieldInitDataGlue->m_isTextFieldPrev = env->GetFieldID(tfidClazz, "mIsTextFieldPrev", "Z");
- m_textFieldInitDataGlue->m_isAutoCompleteEnabled = env->GetFieldID(tfidClazz, "mIsAutoCompleteEnabled", "Z");
- m_textFieldInitDataGlue->m_name = env->GetFieldID(tfidClazz, "mName", "Ljava/lang/String;");
- m_textFieldInitDataGlue->m_label = env->GetFieldID(tfidClazz, "mLabel", "Ljava/lang/String;");
- m_textFieldInitDataGlue->m_maxLength = env->GetFieldID(tfidClazz, "mMaxLength", "I");
- m_textFieldInitDataGlue->m_contentBounds = env->GetFieldID(tfidClazz, "mContentBounds", "Landroid/graphics/Rect;");
- m_textFieldInitDataGlue->m_nodeLayerId = env->GetFieldID(tfidClazz, "mNodeLayerId", "I");
- m_textFieldInitDataGlue->m_contentRect = env->GetFieldID(tfidClazz, "mContentRect", "Landroid/graphics/Rect;");
- m_textFieldInitDataGlue->m_constructor = GetJMethod(env, tfidClazz, "<init>", "()V");
- env->DeleteLocalRef(tfidClazz);
-
PageGroup::setShouldTrackVisitedLinks(true);
clearContent();
@@ -540,23 +463,22 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m
WebViewCore::addInstance(this);
+#if USE(CHROME_NETWORK_STACK)
AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
+#endif
- // increase the font cache size beyond the standard system setting
- SkGraphics::SetFontCacheLimit(1572864); // 1572864 bytes == 1.5 MB
-
+#if USE(V8)
// Static initialisation of certain important V8 static data gets performed at system startup when
// libwebcore gets loaded. We now need to associate the WebCore thread with V8 to complete
// initialisation.
v8::V8::Initialize();
+#endif
// Configure any RuntimeEnabled features that we need to change from their default now.
// See WebCore/bindings/generic/RuntimeEnabledFeatures.h
// HTML5 History API
RuntimeEnabledFeatures::setPushStateEnabled(true);
- if (m_mainFrame)
- m_mainFrame->settings()->setMinDOMTimerInterval(FOREGROUND_TIMER_INTERVAL);
}
WebViewCore::~WebViewCore()
@@ -572,40 +494,24 @@ WebViewCore::~WebViewCore()
m_javaGlue->m_obj = 0;
}
delete m_javaGlue;
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
}
WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
{
- if (!view)
- return 0;
- if (view->platformWidget())
- return static_cast<WebFrameView*>(view->platformWidget())->webViewCore();
- Frame* frame = view->frame();
- while (Frame* parent = frame->tree()->parent())
- frame = parent;
- WebFrameView* webFrameView = 0;
- if (frame && frame->view())
- webFrameView = static_cast<WebFrameView*>(frame->view()->platformWidget());
- if (!webFrameView)
- return 0;
- return webFrameView->webViewCore();
+ return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
}
WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
{
if (!view)
return 0;
- if (view->platformWidget())
- return static_cast<WebFrameView*>(view->platformWidget())->webViewCore();
- const FrameView* frameView = 0;
- if (view->isFrameView())
- frameView = static_cast<const FrameView*>(view);
- else {
- frameView = static_cast<const FrameView*>(view->root());
- if (!frameView)
- return 0;
- }
- return getWebViewCore(frameView);
+
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ if (!webFrameView)
+ return 0;
+ return webFrameView->webViewCore();
}
static bool layoutIfNeededRecursive(WebCore::Frame* f)
@@ -616,35 +522,84 @@ static bool layoutIfNeededRecursive(WebCore::Frame* f)
WebCore::FrameView* v = f->view();
if (!v)
return true;
- v->updateLayoutAndStyleIfNeededRecursive();
- return !v->needsLayout();
+
+ if (v->needsLayout())
+ v->layout(f->tree()->parent());
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
}
-WebCore::Node* WebViewCore::currentFocus()
+CacheBuilder& WebViewCore::cacheBuilder()
{
- return focusedFrame()->document()->focusedNode();
+ return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
}
-void WebViewCore::layout()
+WebCore::Node* WebViewCore::currentFocus()
{
- TRACE_METHOD();
+ return cacheBuilder().currentFocus();
+}
+void WebViewCore::recordPicture(SkPicture* picture)
+{
// if there is no document yet, just return
if (!m_mainFrame->document()) {
- ALOGV("!m_mainFrame->document()");
+ DBG_NAV_LOG("no document");
+ return;
+ }
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame)) {
+ DBG_NAV_LOG("layout failed");
return;
}
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
+ view->contentsHeight());
+ SkAutoPictureRecord arp(picture, view->contentsWidth(),
+ view->contentsHeight(), PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+
+ WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas());
+ WebCore::GraphicsContext gc(&pgc);
+ view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+}
+void WebViewCore::recordPictureSet(PictureSet* content)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document()) {
+ DBG_SET_LOG("!m_mainFrame->document()");
+ return;
+ }
+ if (m_addInval.isEmpty()) {
+ DBG_SET_LOG("m_addInval.isEmpty()");
+ return;
+ }
// Call layout to ensure that the contentWidth and contentHeight are correct
// it's fine for layout to gather invalidates, but defeat sending a message
// back to java to call webkitDraw, since we're already in the middle of
// doing that
+ m_skipContentDraw = true;
bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
// We may be mid-layout and thus cannot draw.
if (!success)
return;
+ { // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
+#endif
+
// if the webkit page dimensions changed, discard the pictureset and redraw.
WebCore::FrameView* view = m_mainFrame->view();
int width = view->contentsWidth();
@@ -695,8 +650,6 @@ void WebViewCore::layout()
// If the new total is larger than the content, resize the view to include
// all the content.
if (!contentRect.contains(total)) {
- // TODO: Does this ever happen? Is this needed now that we don't flatten
- // frames?
// Resize the view to change the overflow clip.
view->resize(total.fRight, total.fBottom);
@@ -705,27 +658,159 @@ void WebViewCore::layout()
view->forceLayout();
// Relayout similar to above
- layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = true;
+ bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+ if (!success)
+ return;
+
+ // Set the computed content width
+ width = view->contentsWidth();
+ height = view->contentsHeight();
}
-}
-void WebViewCore::recordPicturePile()
-{
- // if the webkit page dimensions changed, discard the pictureset and redraw.
- WebCore::FrameView* view = m_mainFrame->view();
- int width = view ? view->contentsWidth() : 0;
- int height = view ? view->contentsHeight() : 0;
+ if (cacheBuilder().pictureSetDisabled())
+ content->clear();
+
+#if USE(ACCELERATED_COMPOSITING)
+ // The invals are not always correct when the content size has changed. For
+ // now, let's just reset the inval so that it invalidates the entire content
+ // -- the pictureset will be fully repainted, tiles will be marked dirty and
+ // will have to be repainted.
+
+ // FIXME: the webkit invals ought to have been enough...
+ if (content->width() != width || content->height() != height) {
+ SkIRect r;
+ r.fLeft = 0;
+ r.fTop = 0;
+ r.fRight = width;
+ r.fBottom = height;
+ m_addInval.setRect(r);
+ }
+#endif
- m_content.setSize(IntSize(width, height));
+ content->setDimensions(width, height, &m_addInval);
+
+ // Add the current inval rects to the PictureSet, and rebuild it.
+ content->add(m_addInval, 0, 0, false);
+
+ // If we have too many invalidations, just get the area bounds
+ SkRegion::Iterator iterator(m_addInval);
+ int nbInvals = 0;
+ while (!iterator.done()) {
+ iterator.next();
+ nbInvals++;
+ if (nbInvals > MAX_INVALIDATIONS)
+ break;
+ }
+ if (nbInvals > MAX_INVALIDATIONS) {
+ SkIRect r = m_addInval.getBounds();
+ m_addInval.setRect(r);
+ }
// Rebuild the pictureset (webkit repaint)
- m_content.updatePicturesIfNeeded(this);
+ rebuildPictureSet(content);
+ } // WebViewCoreRecordTimeCounter
+
+ WebCore::Node* oldFocusNode = currentFocus();
+ m_frameCacheOutOfDate = true;
+ WebCore::IntRect oldBounds;
+ int oldSelStart = 0;
+ int oldSelEnd = 0;
+ if (oldFocusNode) {
+ oldBounds = oldFocusNode->getRect();
+ RenderObject* renderer = oldFocusNode->renderer();
+ if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
+ WebCore::RenderTextControl* rtc =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ oldSelStart = rtc->selectionStart();
+ oldSelEnd = rtc->selectionEnd();
+ }
+ } else
+ oldBounds = WebCore::IntRect(0,0,0,0);
+ unsigned latestVersion = 0;
+ if (m_check_domtree_version) {
+ // as domTreeVersion only increment, we can just check the sum to see
+ // whether we need to update the frame cache
+ for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ const Document* doc = frame->document();
+ latestVersion += doc->domTreeVersion() + doc->styleVersion();
+ }
+ }
+ DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
+ " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
+ " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
+ m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
+ m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
+ m_check_domtree_version ? "true" : "false",
+ latestVersion, m_domtree_version);
+ if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
+ && m_lastFocusedSelStart == oldSelStart
+ && m_lastFocusedSelEnd == oldSelEnd
+ && !m_findIsUp
+ && (!m_check_domtree_version || latestVersion == m_domtree_version))
+ {
+ return;
+ }
+ m_focusBoundsChanged |= m_lastFocused == oldFocusNode
+ && m_lastFocusedBounds != oldBounds;
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ m_lastFocusedSelStart = oldSelStart;
+ m_lastFocusedSelEnd = oldSelEnd;
+ m_domtree_version = latestVersion;
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ if (m_findIsUp) {
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue->object(env);
+ if (javaObject.get()) {
+ env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendFindAgain);
+ checkException(env);
+ }
+ }
+}
+
+// note: updateCursorBounds is called directly by the WebView thread
+// This needs to be called each time we call CachedRoot::setCursor() with
+// non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
+// about the cursor is incorrect. When we call setCursor(0,0), we need
+// to set hasCursorBounds to false.
+void WebViewCore::updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode)
+{
+ LOG_ASSERT(root, "updateCursorBounds: root cannot be null");
+ LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
+ LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
+ gCursorBoundsMutex.lock();
+ m_hasCursorBounds = !cachedNode->isHidden();
+ // If m_hasCursorBounds is false, we never look at the other
+ // values, so do not bother setting them.
+ if (m_hasCursorBounds) {
+ WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
+ if (m_cursorBounds != bounds)
+ DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ m_cursorBounds = bounds;
+ m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
+ m_cursorFrame = cachedFrame->framePointer();
+ root->getSimulatedMousePosition(&m_cursorLocation);
+ m_cursorNode = cachedNode->nodePointer();
+ }
+ gCursorBoundsMutex.unlock();
}
void WebViewCore::clearContent()
{
- m_content.reset();
- updateLocale();
+ DBG_SET_LOG("");
+ m_content.clear();
+ m_addInval.setEmpty();
+ m_rebuildInval.setEmpty();
}
bool WebViewCore::focusBoundsChanged()
@@ -735,82 +820,75 @@ bool WebViewCore::focusBoundsChanged()
return result;
}
-void WebViewCore::paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty)
+SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
{
WebCore::FrameView* view = m_mainFrame->view();
- if (!view) {
- gc->setFillColor(WebCore::Color::white, WebCore::ColorSpaceDeviceRGB);
- gc->fillColor();
- return;
- }
+ int width = view->contentsWidth();
+ int height = view->contentsHeight();
+ SkPicture* picture = new SkPicture();
+ SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+ SkCanvas* recordingCanvas = arp.getRecordingCanvas();
+ WebCore::PlatformGraphicsContext pgc(recordingCanvas);
+ WebCore::GraphicsContext gc(&pgc);
IntPoint origin = view->minimumScrollPosition();
- IntRect drawArea = dirty;
- gc->translate(-origin.x(), -origin.y());
- drawArea.move(origin.x(), origin.y());
- view->platformWidget()->draw(gc, drawArea);
-}
+ WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(),
+ inval.width(), inval.height());
+ recordingCanvas->translate(-drawArea.x(), -drawArea.y());
+ recordingCanvas->save();
+ view->platformWidget()->draw(&gc, drawArea);
+ m_rebuildInval.op(inval, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
+ m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
+ m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
-void WebViewCore::setPrerenderingEnabled(bool enable)
-{
- MutexLocker locker(m_prerenderLock);
- m_prerenderEnabled = enable;
+ return picture;
}
-bool WebViewCore::prerenderingEnabled()
+void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
{
- MutexLocker locker(m_prerenderLock);
- return m_prerenderEnabled;
+ WebCore::FrameView* view = m_mainFrame->view();
+
+#ifdef FAST_PICTURESET
+ WTF::Vector<Bucket*>* buckets = pictureSet->bucketsToUpdate();
+
+ for (unsigned int i = 0; i < buckets->size(); i++) {
+ Bucket* bucket = (*buckets)[i];
+ for (unsigned int j = 0; j < bucket->size(); j++) {
+ BucketPicture& bucketPicture = (*bucket)[j];
+ const SkIRect& inval = bucketPicture.mRealArea;
+ SkPicture* picture = rebuildPicture(inval);
+ SkSafeUnref(bucketPicture.mPicture);
+ bucketPicture.mPicture = picture;
+ }
+ }
+ buckets->clear();
+#else
+ size_t size = pictureSet->size();
+ for (size_t index = 0; index < size; index++) {
+ if (pictureSet->upToDate(index))
+ continue;
+ const SkIRect& inval = pictureSet->bounds(index);
+ DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
+ inval.fLeft, inval.fTop, inval.width(), inval.height());
+ pictureSet->setPicture(index, rebuildPicture(inval));
+ }
+
+ pictureSet->validate(__FUNCTION__);
+#endif
}
-SkCanvas* WebViewCore::createPrerenderCanvas(PrerenderedInval* prerendered)
+bool WebViewCore::updateLayers(LayerAndroid* layers)
{
- // Has WebView disabled prerenders (not attached, etc...)?
- if (!prerenderingEnabled())
- return 0;
- // Does this WebView have focus?
- if (!m_mainFrame->page()->focusController()->isActive())
- return 0;
- // Are we scrolling?
- if (currentTimeMS() - m_scrollSetTime < PRERENDER_AFTER_SCROLL_DELAY)
- return 0;
- // Do we have anything to render?
- if (prerendered->area.isEmpty())
- return 0;
- FloatRect scaleTemp(m_scrollOffsetX, m_scrollOffsetY, m_screenWidth, m_screenHeight);
- scaleTemp.scale(m_scale);
- IntRect visibleTileClip = enclosingIntRect(scaleTemp);
- FloatRect scaledArea = prerendered->area;
- scaledArea.scale(m_scale);
- IntRect enclosingScaledArea = enclosingIntRect(scaledArea);
- if (enclosingScaledArea.isEmpty())
- return 0;
- // "round out" the screen to tile boundaries so that we can clip yet still
- // cover any visible tiles with the prerender
- int tw = TilesManager::tileWidth();
- int th = TilesManager::tileHeight();
- float left = tw * (int) (visibleTileClip.x() / tw);
- float top = th * (int) (visibleTileClip.y() / th);
- float right = tw * (int) ceilf(visibleTileClip.maxX() / (float) tw);
- float bottom = th * (int) ceilf(visibleTileClip.maxY() / (float) th);
- visibleTileClip = IntRect(left, top, right - left, bottom - top);
- enclosingScaledArea.intersect(visibleTileClip);
- if (enclosingScaledArea.isEmpty())
- return 0;
- prerendered->screenArea = enclosingScaledArea;
- FloatRect enclosingDocArea(enclosingScaledArea);
- enclosingDocArea.scale(1 / m_scale);
- prerendered->area = enclosingIntRect(enclosingDocArea);
- if (prerendered->area.isEmpty())
- return 0;
- prerendered->bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- enclosingScaledArea.width(),
- enclosingScaledArea.height());
- prerendered->bitmap.allocPixels();
- SkCanvas* bitmapCanvas = new SkCanvas(prerendered->bitmap);
- bitmapCanvas->scale(m_scale, m_scale);
- bitmapCanvas->translate(-enclosingDocArea.x(), -enclosingDocArea.y());
- return bitmapCanvas;
+ // We update the layers
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
+ if (root) {
+ LayerAndroid* updatedLayer = root->contentLayer();
+ return layers->updateWithTree(updatedLayer);
+ }
+ return true;
}
void WebViewCore::notifyAnimationStarted()
@@ -824,113 +902,93 @@ void WebViewCore::notifyAnimationStarted()
}
-BaseLayerAndroid* WebViewCore::createBaseLayer(GraphicsLayerAndroid* root)
+BaseLayerAndroid* WebViewCore::createBaseLayer(SkRegion* region)
{
- // We set the background color
- Color background = Color::white;
+ BaseLayerAndroid* base = new BaseLayerAndroid();
+ base->setContent(m_content);
- bool bodyHasFixedBackgroundImage = false;
- bool bodyHasCSSBackground = false;
+ m_skipContentDraw = true;
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+ // Layout only fails if called during a layout.
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+#if USE(ACCELERATED_COMPOSITING)
+ // We set the background color
if (m_mainFrame && m_mainFrame->document()
&& m_mainFrame->document()->body()) {
-
Document* document = m_mainFrame->document();
RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
if (style->hasBackground()) {
- background = style->visitedDependentColor(CSSPropertyBackgroundColor);
- bodyHasCSSBackground = true;
- }
- WebCore::FrameView* view = m_mainFrame->view();
- if (view) {
- Color viewBackground = view->baseBackgroundColor();
- background = bodyHasCSSBackground ? viewBackground.blend(background) : viewBackground;
+ Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (color.isValid() && color.alpha() > 0)
+ base->setBackgroundColor(color);
}
- if (style->hasFixedBackgroundImage()) {
- Image* backgroundImage = FixedBackgroundImageLayerAndroid::GetCachedImage(style);
- if (backgroundImage && backgroundImage->width() > 1 && backgroundImage->height() > 1)
- bodyHasFixedBackgroundImage = true;
- }
- }
-
- PicturePileLayerContent* content = new PicturePileLayerContent(m_content);
- m_content.clearPrerenders();
-
- BaseLayerAndroid* realBase = 0;
- LayerAndroid* base = 0;
-
- //If we have a fixed background image on the body element, the fixed image
- // will be contained in the PictureSet (the content object), and the foreground
- //of the body element will be moved to a layer.
- //In that case, let's change the hierarchy to obtain:
- //
- //BaseLayerAndroid
- // \- FixedBackgroundBaseLayerAndroid (fixed positioning)
- // \- ForegroundBaseLayerAndroid
- // \- root layer (webkit composited tree)
-
- if (bodyHasFixedBackgroundImage) {
- base = new ForegroundBaseLayerAndroid(0);
- base->setSize(content->width(), content->height());
-
- Document* document = m_mainFrame->document();
- RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
-
- FixedBackgroundImageLayerAndroid* baseBackground =
- new FixedBackgroundImageLayerAndroid(style, content->width(), content->height());
-
- realBase = new BaseLayerAndroid(0);
- realBase->setSize(content->width(), content->height());
- realBase->addChild(baseBackground);
- realBase->addChild(base);
- baseBackground->unref();
- base->unref();
- } else {
- realBase = new BaseLayerAndroid(content);
- base = realBase;
}
- realBase->setBackgroundColor(background);
-
- SkSafeUnref(content);
-
// We update the layers
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
if (root) {
LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
base->addChild(copyLayer);
copyLayer->unref();
root->contentLayer()->clearDirtyRegion();
}
+#endif
- return realBase;
+ return base;
}
-BaseLayerAndroid* WebViewCore::recordContent(SkIPoint* point)
+BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
{
- m_skipContentDraw = true;
- layout();
- ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
- GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
- m_skipContentDraw = false;
- recordPicturePile();
-
- BaseLayerAndroid* baseLayer = createBaseLayer(root);
-
- baseLayer->markAsDirty(m_content.dirtyRegion());
- m_content.dirtyRegion().setEmpty();
+ DBG_SET_LOG("start");
+ // If there is a pending style recalculation, just return.
+ if (m_mainFrame->document()->isPendingStyleRecalc()) {
+ DBG_SET_LOGD("recordContent: pending style recalc, ignoring.");
+ return 0;
+ }
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ m_progressDone = progress <= 0.0f || progress >= 1.0f;
+ recordPictureSet(&m_content);
+ if (!m_progressDone && m_content.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return 0;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
#if USE(ACCELERATED_COMPOSITING)
#else
- baseLayer->markAsDirty(m_rebuildInval);
+ region->op(m_rebuildInval, SkRegion::kUnion_Op);
#endif
- point->fX = m_content.size().width();
- point->fY = m_content.size().height();
+ m_rebuildInval.setEmpty();
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
+ region->getBounds().fTop, region->getBounds().fRight,
+ region->getBounds().fBottom);
+ DBG_SET_LOG("end");
- return baseLayer;
+ return createBaseLayer(region);
+}
+
+void WebViewCore::splitContent(PictureSet* content)
+{
+#ifdef FAST_PICTURESET
+#else
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+ content->split(&m_content);
+ rebuildPictureSet(&m_content);
+ content->set(m_content);
+#endif // FAST_PICTURESET
}
void WebViewCore::scrollTo(int x, int y, bool animate)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+// LOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -943,7 +1001,7 @@ void WebViewCore::scrollTo(int x, int y, bool animate)
void WebViewCore::sendNotifyProgressFinished()
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
if (!javaObject.get())
@@ -954,7 +1012,7 @@ void WebViewCore::sendNotifyProgressFinished()
void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
if (!javaObject.get())
@@ -975,12 +1033,26 @@ void WebViewCore::contentDraw()
checkException(env);
}
+void WebViewCore::layersDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue->object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue->m_layersDraw);
+ checkException(env);
+}
+
void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
{
- IntPoint origin = m_mainFrame->view()->minimumScrollPosition();
- IntRect dirty = r;
- dirty.move(-origin.x(), -origin.y());
- m_content.invalidate(dirty);
+ DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
+ SkIRect rect(r);
+ if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
+ return;
+ m_addInval.op(rect, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
+ m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
+ m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
if (!m_skipContentDraw)
contentDraw();
}
@@ -1000,9 +1072,19 @@ void WebViewCore::offInvalidate(const WebCore::IntRect &r)
contentInvalidate(r);
}
+static int pin_pos(int x, int width, int targetWidth)
+{
+ if (x + width > targetWidth)
+ x = targetWidth - width;
+ if (x < 0)
+ x = 0;
+ return x;
+}
+
void WebViewCore::didFirstLayout()
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -1012,7 +1094,7 @@ void WebViewCore::didFirstLayout()
const WebCore::KURL& url = m_mainFrame->document()->url();
if (url.isEmpty())
return;
- ALOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
WebCore::FrameLoadType loadType = m_mainFrame->loader()->loadType();
@@ -1026,11 +1108,17 @@ void WebViewCore::didFirstLayout()
// a newly-loaded page.
|| loadType == WebCore::FrameLoadTypeSame);
checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ m_check_domtree_version = false;
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
}
void WebViewCore::updateViewport()
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -1042,7 +1130,8 @@ void WebViewCore::updateViewport()
void WebViewCore::restoreScale(float scale, float textWrapScale)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -1054,7 +1143,8 @@ void WebViewCore::restoreScale(float scale, float textWrapScale)
void WebViewCore::needTouchEvents(bool need)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
#if ENABLE(TOUCH_EVENTS)
JNIEnv* env = JSC::Bindings::getJNIEnv();
@@ -1072,9 +1162,26 @@ void WebViewCore::needTouchEvents(bool need)
#endif
}
+void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
+ int selStart, int selEnd)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue->object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(),
+ m_javaGlue->m_requestKeyboardWithSelection,
+ reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
+ checkException(env);
+}
+
void WebViewCore::requestKeyboard(bool showKeyboard)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -1086,15 +1193,42 @@ void WebViewCore::requestKeyboard(bool showKeyboard)
void WebViewCore::notifyProgressFinished()
{
+ m_check_domtree_version = true;
sendNotifyProgressFinished();
}
-void WebViewCore::setScrollOffset(bool sendScrollEvent, int dx, int dy)
+void WebViewCore::doMaxScroll(CacheBuilder::Direction dir)
+{
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case CacheBuilder::LEFT:
+ dx = -m_maxXScroll;
+ break;
+ case CacheBuilder::UP:
+ dy = -m_maxYScroll;
+ break;
+ case CacheBuilder::RIGHT:
+ dx = m_maxXScroll;
+ break;
+ case CacheBuilder::DOWN:
+ dy = m_maxYScroll;
+ break;
+ case CacheBuilder::UNINITIALIZED:
+ default:
+ LOG_ASSERT(0, "unexpected focus selector");
+ }
+ WebCore::FrameView* view = m_mainFrame->view();
+ this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true);
+}
+
+void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy)
{
+ DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy,
+ m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent);
if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
m_scrollOffsetX = dx;
m_scrollOffsetY = dy;
- m_scrollSetTime = currentTimeMS();
// The visible rect is located within our coordinate space so it
// contains the actual scroll position. Setting the location makes hit
// testing work correctly.
@@ -1125,10 +1259,19 @@ void WebViewCore::setScrollOffset(bool sendScrollEvent, int dx, int dy)
// update the currently visible screen
sendPluginVisibleScreen();
}
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ IntPoint location = m_cursorLocation;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds)
+ return;
+ moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
}
void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
{
+ DBG_NAV_LOGD("{%d,%d}", x, y);
m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
}
@@ -1147,6 +1290,9 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
int osw = m_screenWidth;
int osh = m_screenHeight;
int otw = m_textWrapWidth;
+ float oldScale = m_scale;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
+ ow, oh, osw, m_scale, width, height, screenWidth, scale);
m_screenWidth = screenWidth;
m_screenHeight = screenHeight;
m_textWrapWidth = textWrapWidth;
@@ -1165,8 +1311,11 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
if (ow != width || (!ignoreHeight && oh != height) || reflow) {
WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
+ screenWidth, screenHeight);
if (r) {
WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
+ DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
RefPtr<WebCore::Node> node;
WebCore::IntRect bounds;
WebCore::IntPoint offset;
@@ -1178,19 +1327,11 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
m_mainFrame->eventHandler()->hitTestResultAtPoint(
anchorPoint, false);
node = hitTestResult.innerNode();
- if (node && !node->isTextNode()) {
- // If the hitTestResultAtPoint didn't find a suitable node
- // for anchoring, try again with some slop.
- static const int HIT_SLOP = 30;
- anchorPoint.move(HIT_SLOP, HIT_SLOP);
- hitTestResult =
- m_mainFrame->eventHandler()->hitTestResultAtPoint(
- anchorPoint, false);
- node = hitTestResult.innerNode();
- }
}
if (node) {
bounds = node->getRect();
+ DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
// sites like nytimes.com insert a non-standard tag <nyt_text>
// in the html. If it is the HitTestResult, it may have zero
// width and height. In this case, use its parent node.
@@ -1198,6 +1339,8 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
node = node->parentOrHostNode();
if (node) {
bounds = node->getRect();
+ DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
}
}
}
@@ -1218,6 +1361,9 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
// scroll to restore current screen center
if (node && node->inDocument()) {
const WebCore::IntRect& newBounds = node->getRect();
+ DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
+ "h=%d)", newBounds.x(), newBounds.y(),
+ newBounds.width(), newBounds.height());
if ((osw && osh && bounds.width() && bounds.height())
&& (bounds != newBounds)) {
WebCore::FrameView* view = m_mainFrame->view();
@@ -1294,6 +1440,13 @@ void WebViewCore::dumpRenderTree(bool useFile)
#endif
}
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ cacheBuilder().mDebug.print();
+#endif
+}
+
HTMLElement* WebViewCore::retrieveElement(int x, int y,
const QualifiedName& tagName)
{
@@ -1302,12 +1455,12 @@ HTMLElement* WebViewCore::retrieveElement(int x, int y,
DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
IntSize(1, 1));
if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
- ALOGE("Should not happen: no in document Node found");
+ LOGE("Should not happen: no in document Node found");
return 0;
}
const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
if (list.isEmpty()) {
- ALOGE("Should not happen: no rect-based-test nodes found");
+ LOGE("Should not happen: no rect-based-test nodes found");
return 0;
}
Node* node = hitTestResult.innerNode();
@@ -1316,6 +1469,9 @@ HTMLElement* WebViewCore::retrieveElement(int x, int y,
|| !element->hasTagName(tagName))) {
element = element->parentNode();
}
+ DBG_NAV_LOGD("node=%p element=%p x=%d y=%d nodeName=%s tagName=%s", node,
+ element, x, y, node->nodeName().utf8().data(),
+ element ? ((Element*) element)->tagName().utf8().data() : "<none>");
return static_cast<WebCore::HTMLElement*>(element);
}
@@ -1333,10 +1489,8 @@ HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y)
WTF::String WebViewCore::retrieveHref(int x, int y)
{
- // TODO: This is expensive, cache
- HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
- false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1));
- return result.absoluteLinkURL();
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->href() : WTF::String();
}
WTF::String WebViewCore::retrieveAnchorText(int x, int y)
@@ -1347,16 +1501,14 @@ WTF::String WebViewCore::retrieveAnchorText(int x, int y)
WTF::String WebViewCore::retrieveImageSource(int x, int y)
{
- // TODO: This is expensive, cache
- HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
- false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1));
- return result.absoluteImageURL();
+ HTMLImageElement* image = retrieveImageElement(x, y);
+ return image ? image->src().string() : WTF::String();
}
WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
WebCore::Node* node)
{
- if (node && validNode(m_mainFrame, frame, node)) {
+ if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
unsigned length = list->length();
for (unsigned i = 0; i < length; i++) {
@@ -1380,18 +1532,17 @@ WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
static bool isContentEditable(const WebCore::Node* node)
{
- if (!node)
- return false;
- return node->isContentEditable();
+ if (!node) return false;
+ return node->document()->frame()->selection()->isContentEditable();
}
// Returns true if the node is a textfield, textarea, or contentEditable
static bool isTextInput(const WebCore::Node* node)
{
- if (!node)
- return false;
if (isContentEditable(node))
return true;
+ if (!node)
+ return false;
WebCore::RenderObject* renderer = node->renderer();
return renderer && (renderer->isTextField() || renderer->isTextArea());
}
@@ -1409,9 +1560,107 @@ void WebViewCore::revealSelection()
focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
}
+void WebViewCore::updateCacheOnNodeChange()
+{
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ IntRect bounds = m_cursorHitBounds;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds || !node)
+ return;
+ if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->style()->visibility() != HIDDEN) {
+ IntRect absBox = renderer->absoluteBoundingBoxRect();
+ int globalX, globalY;
+ CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
+ absBox.move(globalX, globalY);
+ if (absBox == bounds)
+ return;
+ DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
+ absBox.x(), absBox.y(), absBox.width(), absBox.height(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ }
+ }
+ DBG_NAV_LOGD("updateFrameCache node=%p", node);
+ updateFrameCache();
+}
+
+void WebViewCore::updateFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return;
+ }
+
+ // If there is a pending style recalculation, do not update the frame cache.
+ // Until the recalculation is complete, there may be internal objects that
+ // are in an inconsistent state (such as font pointers).
+ // In any event, there's not much point to updating the cache while a style
+ // recalculation is pending, since it will simply have to be updated again
+ // once the recalculation is complete.
+ // TODO: Do we need to reschedule an update for after the style is recalculated?
+ if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("updateFrameCache: pending style recalc, ignoring.");
+ return;
+ }
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
+#endif
+ m_frameCacheOutOfDate = false;
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
+ if (graphicsLayer)
+ m_temp->setRootLayer(graphicsLayer->contentLayer());
+#endif
+ CacheBuilder& builder = cacheBuilder();
+ WebCore::Settings* settings = m_mainFrame->page()->settings();
+ builder.allowAllTextDetection();
+#ifdef ANDROID_META_SUPPORT
+ if (settings) {
+ if (!settings->formatDetectionAddress())
+ builder.disallowAddressDetection();
+ if (!settings->formatDetectionEmail())
+ builder.disallowEmailDetection();
+ if (!settings->formatDetectionTelephone())
+ builder.disallowPhoneDetection();
+ }
+#endif
+ builder.buildCache(m_temp);
+ m_tempPict = new SkPicture();
+ recordPicture(m_tempPict);
+ m_temp->setPicture(m_tempPict);
+ m_temp->setTextGeneration(m_textGeneration);
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
+ m_scrollOffsetY, window->width(), window->height()));
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = m_temp;
+ m_navPictureKit = m_tempPict;
+ m_updatedFrameCache = true;
+#if DEBUG_NAV_UI
+ const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
+ DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
+ cachedFocusNode ? cachedFocusNode->index() : 0,
+ cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
+#endif
+ gFrameCacheMutex.unlock();
+}
+
+void WebViewCore::updateFrameCacheIfLoading()
+{
+ if (!m_check_domtree_version)
+ updateFrameCache();
+}
+
struct TouchNodeData {
- Node* mUrlNode;
- Node* mInnerNode;
+ Node* mNode;
IntRect mBounds;
};
@@ -1419,8 +1668,6 @@ struct TouchNodeData {
static IntRect getAbsoluteBoundingBox(Node* node) {
IntRect rect;
RenderObject* render = node->renderer();
- if (!render)
- return rect;
if (render->isRenderInline())
rect = toRenderInline(render)->linesVisualOverflowBoundingBox();
else if (render->isBox())
@@ -1428,395 +1675,30 @@ static IntRect getAbsoluteBoundingBox(Node* node) {
else if (render->isText())
rect = toRenderText(render)->linesBoundingBox();
else
- ALOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
- FloatPoint absPos = render->localToAbsolute(FloatPoint(), false, true);
+ LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
+ FloatPoint absPos = render->localToAbsolute();
rect.move(absPos.x(), absPos.y());
return rect;
}
-WebCore::Frame* WebViewCore::focusedFrame() const
-{
- return m_mainFrame->page()->focusController()->focusedOrMainFrame();
-}
-
-VisiblePosition WebViewCore::visiblePositionForContentPoint(int x, int y)
-{
- return visiblePositionForContentPoint(IntPoint(x, y));
-}
-
-VisiblePosition WebViewCore::visiblePositionForContentPoint(const IntPoint& point)
-{
- // Hit test of this kind required for this to work inside input fields
- HitTestRequest request(HitTestRequest::Active
- | HitTestRequest::MouseMove
- | HitTestRequest::ReadOnly
- | HitTestRequest::IgnoreClipping);
- HitTestResult result(point);
- focusedFrame()->document()->renderView()->layer()->hitTest(request, result);
-
- // Matching the logic in MouseEventWithHitTestResults::targetNode()
- Node* node = result.innerNode();
- if (!node)
- return VisiblePosition();
- Element* element = node->parentElement();
- if (!node->inDocument() && element && element->inDocument())
- node = element;
-
- return node->renderer()->positionForPoint(result.localPoint());
-}
-
-bool WebViewCore::selectWordAt(int x, int y)
-{
- HitTestResult hoverResult;
- moveMouse(x, y, &hoverResult);
- if (hoverResult.innerNode()) {
- Node* node = hoverResult.innerNode();
- Frame* frame = node->document()->frame();
- Page* page = m_mainFrame->document()->page();
- page->focusController()->setFocusedFrame(frame);
- }
-
- IntPoint point = convertGlobalContentToFrameContent(IntPoint(x, y));
-
- // Hit test of this kind required for this to work inside input fields
- HitTestRequest request(HitTestRequest::Active);
- HitTestResult result(point);
-
- focusedFrame()->document()->renderView()->layer()->hitTest(request, result);
-
- // Matching the logic in MouseEventWithHitTestResults::targetNode()
- Node* node = result.innerNode();
- if (!node)
- return false;
- Element* element = node->parentElement();
- if (!node->inDocument() && element && element->inDocument())
- node = element;
-
- SelectionController* sc = focusedFrame()->selection();
- bool wordSelected = false;
- if (!sc->contains(point) && (node->isContentEditable() || node->isTextNode()) && !result.isLiveLink()
- && node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true))) {
- VisiblePosition pos(node->renderer()->positionForPoint(result.localPoint()));
- wordSelected = selectWordAroundPosition(node->document()->frame(), pos);
- }
- return wordSelected;
-}
-
-bool WebViewCore::selectWordAroundPosition(Frame* frame, VisiblePosition pos)
-{
- VisibleSelection selection(pos);
- selection.expandUsingGranularity(WordGranularity);
- SelectionController* selectionController = frame->selection();
-
- bool wordSelected = false;
- if (selectionController->shouldChangeSelection(selection)) {
- bool allWhitespaces = true;
- RefPtr<Range> firstRange = selection.firstRange();
- String text = firstRange.get() ? firstRange->text() : "";
- for (size_t i = 0; i < text.length(); ++i) {
- if (!isSpaceOrNewline(text[i])) {
- allWhitespaces = false;
- break;
- }
- }
- if (allWhitespaces) {
- VisibleSelection emptySelection(pos);
- selectionController->setSelection(emptySelection);
- } else {
- selectionController->setSelection(selection);
- wordSelected = true;
- }
- }
- return wordSelected;
-}
-
-int WebViewCore::platformLayerIdFromNode(Node* node, LayerAndroid** outLayer)
-{
- if (!node || !node->renderer())
- return -1;
- RenderLayer* renderLayer = node->renderer()->enclosingLayer();
- while (renderLayer && !renderLayer->isComposited())
- renderLayer = renderLayer->parent();
- if (!renderLayer || !renderLayer->isComposited())
- return -1;
- GraphicsLayer* graphicsLayer = renderLayer->backing()->graphicsLayer();
- if (!graphicsLayer)
- return -1;
- GraphicsLayerAndroid* agl = static_cast<GraphicsLayerAndroid*>(graphicsLayer);
- LayerAndroid* layer = agl->foregroundLayer();
- if (!layer)
- layer = agl->contentLayer();
- if (!layer)
- return -1;
- if (outLayer)
- *outLayer = layer;
- return layer->uniqueId();
-}
-
-void WebViewCore::layerToAbsoluteOffset(const LayerAndroid* layer, IntPoint& offset)
-{
- while (layer) {
- const SkPoint& pos = layer->getPosition();
- offset.move(pos.fX, pos.fY);
- const IntPoint& scroll = layer->getScrollOffset();
- offset.move(-scroll.x(), -scroll.y());
- layer = static_cast<LayerAndroid*>(layer->getParent());
- }
-}
-
-void WebViewCore::setSelectionCaretInfo(SelectText* selectTextContainer,
- const WebCore::Position& pos, const IntPoint& frameOffset,
- SelectText::HandleId handleId, int caretRectOffset, EAffinity affinity)
-{
- Node* node = pos.anchorNode();
- LayerAndroid* layer = 0;
- int layerId = platformLayerIdFromNode(node, &layer);
- selectTextContainer->setCaretLayerId(handleId, layerId);
- IntPoint offset = frameOffset;
- layerToAbsoluteOffset(layer, offset);
- RenderObject* r = node->renderer();
- RenderText* renderText = toRenderText(r);
- int caretOffset;
- InlineBox* inlineBox;
- pos.getInlineBoxAndOffset(affinity, inlineBox, caretOffset);
- IntRect caretRect = renderText->localCaretRect(inlineBox, caretOffset);
- FloatPoint absoluteOffset = renderText->localToAbsolute(caretRect.location());
- caretRect.setX(absoluteOffset.x() - offset.x() + caretRectOffset);
- caretRect.setY(absoluteOffset.y() - offset.y());
- selectTextContainer->setCaretRect(handleId, caretRect);
- selectTextContainer->setTextRect(handleId,
- positionToTextRect(pos, affinity, offset));
-}
-
-bool WebViewCore::isLtr(const Position& position)
-{
- InlineBox* inlineBox = 0;
- int caretOffset = 0;
- position.getInlineBoxAndOffset(DOWNSTREAM, inlineBox, caretOffset);
- bool isLtr;
- if (inlineBox)
- isLtr = inlineBox->isLeftToRightDirection();
- else
- isLtr = position.primaryDirection() == LTR;
- return isLtr;
-}
-
-SelectText* WebViewCore::createSelectText(const VisibleSelection& selection)
-{
- bool isCaret = selection.isCaret();
- if (selection.isNone() || (!selection.isContentEditable() && isCaret)
- || !selection.start().anchorNode()
- || !selection.start().anchorNode()->renderer()
- || !selection.end().anchorNode()
- || !selection.end().anchorNode()->renderer())
- return 0;
-
- RefPtr<Range> range = selection.firstRange();
- Node* startContainer = range->startContainer();
- Node* endContainer = range->endContainer();
-
- if (!startContainer || !endContainer)
- return 0;
- if (!isCaret && startContainer == endContainer
- && range->startOffset() == range->endOffset())
- return 0;
-
- IntPoint frameOffset = convertGlobalContentToFrameContent(IntPoint());
- SelectText* selectTextContainer = new SelectText();
- if (isCaret) {
- setSelectionCaretInfo(selectTextContainer, selection.start(), frameOffset,
- SelectText::LeftHandle, 0, selection.affinity());
- setSelectionCaretInfo(selectTextContainer, selection.start(), frameOffset,
- SelectText::RightHandle, 0, selection.affinity());
- } else {
- bool ltr = isLtr(selection.start());
- Position left = ltr ? selection.start() : selection.end();
- Position right = ltr ? selection.end() : selection.start();
- int leftOffset = isLtr(left) ? 0 : -1;
- int rightOffset = isLtr(right) ? 0 : -1;
- setSelectionCaretInfo(selectTextContainer, left, frameOffset,
- SelectText::LeftHandle, leftOffset, selection.affinity());
- setSelectionCaretInfo(selectTextContainer, right, frameOffset,
- SelectText::RightHandle, rightOffset, selection.affinity());
-
- Node* stopNode = range->pastLastNode();
- for (Node* node = range->firstNode(); node != stopNode; node = node->traverseNextNode()) {
- RenderObject* r = node->renderer();
- if (!r || !r->isText() || r->style()->visibility() != VISIBLE)
- continue;
- RenderText* renderText = toRenderText(r);
- int startOffset = node == startContainer ? range->startOffset() : 0;
- int endOffset = node == endContainer ? range->endOffset() : numeric_limits<int>::max();
- LayerAndroid* layer = 0;
- int layerId = platformLayerIdFromNode(node, &layer);
- Vector<IntRect> rects;
- renderText->absoluteRectsForRange(rects, startOffset, endOffset, true);
- selectTextContainer->addHighlightRegion(layer, rects, frameOffset);
- }
- }
- selectTextContainer->setText(range->text());
- return selectTextContainer;
-}
-
-IntRect WebViewCore::positionToTextRect(const Position& position,
- EAffinity affinity, const WebCore::IntPoint& offset)
-{
- IntRect textRect;
- InlineBox* inlineBox;
- int offsetIndex;
- position.getInlineBoxAndOffset(affinity, inlineBox, offsetIndex);
- if (inlineBox && inlineBox->isInlineTextBox()) {
- InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox);
- RootInlineBox* root = box->root();
- RenderText* renderText = box->textRenderer();
- int left = root->logicalLeft();
- int width = root->logicalWidth();
- int top = root->selectionTop();
- int height = root->selectionHeight();
-
- if (!renderText->style()->isHorizontalWritingMode()) {
- swap(left, top);
- swap(width, height);
- }
- FloatPoint origin(left, top);
- FloatPoint absoluteOrigin = renderText->localToAbsolute(origin);
-
- textRect.setX(absoluteOrigin.x() - offset.x());
- textRect.setWidth(width);
- textRect.setY(absoluteOrigin.y() - offset.y());
- textRect.setHeight(height);
- }
- return textRect;
-}
-
-IntPoint WebViewCore::convertGlobalContentToFrameContent(const IntPoint& point, WebCore::Frame* frame)
-{
- if (!frame) frame = focusedFrame();
- IntPoint frameOffset(-m_scrollOffsetX, -m_scrollOffsetY);
- frameOffset = frame->view()->windowToContents(frameOffset);
- return IntPoint(point.x() + frameOffset.x(), point.y() + frameOffset.y());
-}
-
-Position WebViewCore::trimSelectionPosition(const Position &start, const Position& stop)
-{
- int direction = comparePositions(start, stop);
- if (direction == 0)
- return start;
- bool forward = direction < 0;
- EAffinity affinity = forward ? DOWNSTREAM : UPSTREAM;
- bool move;
- Position pos = start;
- bool movedTooFar = false;
- do {
- move = true;
- Node* node = pos.anchorNode();
- if (node && node->isTextNode() && node->renderer()) {
- RenderText *textRenderer = toRenderText(node->renderer());
- move = !textRenderer->textLength();
- }
- if (move) {
- Position nextPos = forward ? pos.next() : pos.previous();
- movedTooFar = nextPos.isNull() || pos == nextPos
- || ((comparePositions(nextPos, stop) < 0) != forward);
- pos = nextPos;
- }
- } while (move && !movedTooFar);
- if (movedTooFar)
- pos = stop;
- return pos;
-}
-
-void WebViewCore::selectText(int startX, int startY, int endX, int endY)
-{
- SelectionController* sc = focusedFrame()->selection();
- IntPoint startPoint = convertGlobalContentToFrameContent(IntPoint(startX, startY));
- VisiblePosition startPosition(visiblePositionForContentPoint(startPoint));
- IntPoint endPoint = convertGlobalContentToFrameContent(IntPoint(endX, endY));
- VisiblePosition endPosition(visiblePositionForContentPoint(endPoint));
-
- if (startPosition.isNull() || endPosition.isNull())
- return;
-
- // Ensure startPosition is before endPosition
- if (comparePositions(startPosition, endPosition) > 0)
- swap(startPosition, endPosition);
-
- if (sc->isContentEditable()) {
- startPosition = sc->selection().visibleStart().honorEditableBoundaryAtOrAfter(startPosition);
- endPosition = sc->selection().visibleEnd().honorEditableBoundaryAtOrBefore(endPosition);
- if (startPosition.isNull() || endPosition.isNull()) {
- return;
- }
- }
-
- // Ensure startPosition is not at end of block
- if (startPosition != endPosition && isEndOfBlock(startPosition)) {
- VisiblePosition nextStartPosition(startPosition.next());
- if (!nextStartPosition.isNull())
- startPosition = nextStartPosition;
- }
- // Ensure endPosition is not at start of block
- if (startPosition != endPosition && isStartOfBlock(endPosition)) {
- VisiblePosition prevEndPosition(endPosition.previous());
- if (!prevEndPosition.isNull())
- endPosition = prevEndPosition;
- }
-
- Position start = startPosition.deepEquivalent();
- Position end = endPosition.deepEquivalent();
- start = trimSelectionPosition(start, end);
- end = trimSelectionPosition(end, start);
- VisibleSelection selection(start, end);
- // Only allow changes between caret positions or to text selection.
- bool selectChangeAllowed = (!selection.isCaret() || sc->isCaret());
- if (selectChangeAllowed && sc->shouldChangeSelection(selection))
- sc->setSelection(selection);
-}
-
-bool WebViewCore::nodeIsClickableOrFocusable(Node* node)
-{
- if (!node)
- return false;
- if (node->disabled())
- return false;
- if (!node->inDocument())
- return false;
- if (!node->renderer() || node->renderer()->style()->visibility() != VISIBLE)
- return false;
- return node->supportsFocus()
- || node->hasEventListeners(eventNames().clickEvent)
- || node->hasEventListeners(eventNames().mousedownEvent)
- || node->hasEventListeners(eventNames().mouseupEvent)
- || node->hasEventListeners(eventNames().mouseoverEvent);
-}
-
// get the highlight rectangles for the touch point (x, y) with the slop
-AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool doMoveMouse)
+Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop)
{
- if (doMoveMouse)
- moveMouse(x, y, 0, true);
+ Vector<IntRect> rects;
+ m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop));
- AndroidHitTestResult androidHitResult(this, hitTestResult);
if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
- ALOGE("Should not happen: no in document Node found");
- return androidHitResult;
+ LOGE("Should not happen: no in document Node found");
+ return rects;
}
const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
if (list.isEmpty()) {
- ALOGE("Should not happen: no rect-based-test nodes found");
- return androidHitResult;
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return rects;
}
Frame* frame = hitTestResult.innerNode()->document()->frame();
Vector<TouchNodeData> nodeDataList;
- if (hitTestResult.innerNode() != hitTestResult.innerNonSharedNode()
- && hitTestResult.innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) {
- HTMLAreaElement* area = static_cast<HTMLAreaElement*>(hitTestResult.innerNode());
- androidHitResult.hitTestResult().setURLElement(area);
- androidHitResult.highlightRects().append(area->computeRect(
- hitTestResult.innerNonSharedNode()->renderer()));
- return androidHitResult;
- }
ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) {
// TODO: it seems reasonable to not search across the frame. Isn't it?
@@ -1826,12 +1708,14 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do
// traverse up the tree to find the first node that needs highlight
bool found = false;
Node* eventNode = it->get();
- Node* innerNode = eventNode;
while (eventNode) {
RenderObject* render = eventNode->renderer();
if (render && (render->isBody() || render->isRenderView()))
break;
- if (nodeIsClickableOrFocusable(eventNode)) {
+ if (eventNode->supportsFocus()
+ || eventNode->hasEventListeners(eventNames().clickEvent)
+ || eventNode->hasEventListeners(eventNames().mousedownEvent)
+ || eventNode->hasEventListeners(eventNames().mouseupEvent)) {
found = true;
break;
}
@@ -1863,7 +1747,7 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do
Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
// found the same node, skip it
- if (eventNode == n->mUrlNode) {
+ if (eventNode == n->mNode) {
found = false;
break;
}
@@ -1904,19 +1788,16 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do
}
if (!found) {
TouchNodeData newNode;
- newNode.mUrlNode = eventNode;
+ newNode.mNode = eventNode;
newNode.mBounds = rect;
- newNode.mInnerNode = innerNode;
nodeDataList.append(newNode);
}
}
- if (!nodeDataList.size()) {
- androidHitResult.searchContentDetectors();
- return androidHitResult;
- }
+ if (!nodeDataList.size())
+ return rects;
// finally select the node with the largest overlap with the fat point
TouchNodeData final;
- final.mUrlNode = 0;
+ final.mNode = 0;
IntPoint docPos = frame->view()->windowToContents(m_mousePos);
IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1);
int area = 0;
@@ -1925,47 +1806,101 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do
IntRect rect = n->mBounds;
rect.intersect(testRect);
int a = rect.width() * rect.height();
- if (a > area || !final.mUrlNode) {
+ if (a > area) {
final = *n;
area = a;
}
}
// now get the node's highlight rectangles in the page coordinate system
- if (final.mUrlNode) {
- // Update innerNode and innerNonSharedNode
- androidHitResult.hitTestResult().setInnerNode(final.mInnerNode);
- androidHitResult.hitTestResult().setInnerNonSharedNode(final.mInnerNode);
- if (final.mUrlNode->isElementNode()) {
- // We found a URL element. Update the hitTestResult
- androidHitResult.setURLElement(static_cast<Element*>(final.mUrlNode));
- } else {
- androidHitResult.setURLElement(0);
+ if (final.mNode) {
+ IntPoint frameAdjust;
+ if (frame != m_mainFrame) {
+ frameAdjust = frame->view()->contentsToWindow(IntPoint());
+ frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
}
- Vector<IntRect>& highlightRects = androidHitResult.highlightRects();
- if (doMoveMouse && highlightRects.size() > 0) {
- // adjust m_mousePos if it is not inside the returned highlight
- // rectangles
- IntRect foundIntersection;
- IntRect inputRect = IntRect(x - slop, y - slop,
- slop * 2 + 1, slop * 2 + 1);
- for (size_t i = 0; i < highlightRects.size(); i++) {
- IntRect& hr = highlightRects[i];
- IntRect test = inputRect;
- test.intersect(hr);
- if (!test.isEmpty()) {
- foundIntersection = test;
- break;
+ if (final.mNode->isLink()) {
+ // most of the links are inline instead of box style. So the bounding box is not
+ // a good representation for the highlights. Get the list of rectangles instead.
+ RenderObject* render = final.mNode->renderer();
+ IntPoint offset = roundedIntPoint(render->localToAbsolute());
+ render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y());
+ bool inside = false;
+ int distance = INT_MAX;
+ int newx = x, newy = y;
+ int i = rects.size();
+ while (i--) {
+ if (rects[i].isEmpty()) {
+ rects.remove(i);
+ continue;
+ }
+ // check whether the point (x, y) is inside one of the rectangles.
+ if (inside)
+ continue;
+ if (rects[i].contains(x, y)) {
+ inside = true;
+ continue;
+ }
+ if (x >= rects[i].x() && x < rects[i].maxX()) {
+ if (y < rects[i].y()) {
+ if (rects[i].y() - y < distance) {
+ newx = x;
+ newy = rects[i].y();
+ distance = rects[i].y() - y;
+ }
+ } else if (y >= rects[i].maxY()) {
+ if (y - rects[i].maxY() + 1 < distance) {
+ newx = x;
+ newy = rects[i].maxY() - 1;
+ distance = y - rects[i].maxY() + 1;
+ }
+ }
+ } else if (y >= rects[i].y() && y < rects[i].maxY()) {
+ if (x < rects[i].x()) {
+ if (rects[i].x() - x < distance) {
+ newx = rects[i].x();
+ newy = y;
+ distance = rects[i].x() - x;
+ }
+ } else if (x >= rects[i].maxX()) {
+ if (x - rects[i].maxX() + 1 < distance) {
+ newx = rects[i].maxX() - 1;
+ newy = y;
+ distance = x - rects[i].maxX() + 1;
+ }
+ }
}
}
- if (!foundIntersection.isEmpty() && !foundIntersection.contains(x, y)) {
- IntPoint pt = foundIntersection.center();
- moveMouse(pt.x(), pt.y(), 0, true);
+ if (!rects.isEmpty()) {
+ if (!inside) {
+ // if neither x nor y has overlap, just pick the top/left of the first rectangle
+ if (newx == x && newy == y) {
+ newx = rects[0].x();
+ newy = rects[0].y();
+ }
+ m_mousePos.setX(newx - m_scrollOffsetX);
+ m_mousePos.setY(newy - m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
+ return rects;
}
}
- } else {
- androidHitResult.searchContentDetectors();
+ IntRect rect = final.mBounds;
+ rect.move(frameAdjust.x(), frameAdjust.y());
+ rects.append(rect);
+ // adjust m_mousePos if it is not inside the returned highlight rectangle
+ testRect.move(frameAdjust.x(), frameAdjust.y());
+ testRect.intersect(rect);
+ if (!testRect.contains(x, y)) {
+ m_mousePos = testRect.center();
+ m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
}
- return androidHitResult;
+ return rects;
}
///////////////////////////////////////////////////////////////////////////////
@@ -2136,46 +2071,70 @@ static PluginView* nodeIsPlugin(Node* node) {
return 0;
}
+Node* WebViewCore::cursorNodeIsPlugin() {
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ gCursorBoundsMutex.unlock();
+ if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
+ && nodeIsPlugin(node)) {
+ return node;
+ }
+ return 0;
+}
+
///////////////////////////////////////////////////////////////////////////////
+void WebViewCore::moveMouseIfLatest(int moveGeneration,
+ WebCore::Frame* frame, int x, int y)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " frame=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, frame, x, y);
+ if (m_moveGeneration > moveGeneration) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
+ m_moveGeneration, moveGeneration);
+ return; // short-circuit if a newer move has already been generated
+ }
+ m_lastGeneration = moveGeneration;
+ moveMouse(frame, x, y);
+}
+
+void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
+{
+ DBG_NAV_LOGD("frame=%p node=%p", frame, node);
+ if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
+ || !node->isElementNode())
+ return;
+ // Code borrowed from FocusController::advanceFocus
+ WebCore::FocusController* focusController
+ = m_mainFrame->page()->focusController();
+ WebCore::Document* oldDoc
+ = focusController->focusedOrMainFrame()->document();
+ if (oldDoc->focusedNode() == node)
+ return;
+ if (node->document() != oldDoc)
+ oldDoc->setFocusedNode(0);
+ focusController->setFocusedFrame(frame);
+ static_cast<WebCore::Element*>(node)->focus(false);
+}
// Update mouse position
-void WebViewCore::moveMouse(int x, int y, HitTestResult* hoveredNode, bool isClickCandidate)
+void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
{
+ DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
+ x, y, m_scrollOffsetX, m_scrollOffsetY);
+ if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
// mouse event expects the position in the window coordinate
m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
- if (isClickCandidate)
- m_mouseClickPos = m_mousePos;
// validNode will still return true if the node is null, as long as we have
// a valid frame. Do not want to make a call on frame unless it is valid.
WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
false, WTF::currentTime());
- m_mainFrame->eventHandler()->handleMouseMoveEvent(mouseEvent, hoveredNode);
-}
-
-Position WebViewCore::getPositionForOffset(Node* node, int offset)
-{
- Position start = firstPositionInNode(node);
- Position end = lastPositionInNode(node);
- Document* document = node->document();
- PassRefPtr<Range> range = Range::create(document, start, end);
- WebCore::CharacterIterator iterator(range.get());
- iterator.advance(offset);
- return iterator.range()->startPosition();
-}
-
-void WebViewCore::setSelection(Node* node, int start, int end)
-{
- RenderTextControl* control = toRenderTextControl(node);
- if (control)
- setSelectionRange(node, start, end);
- else {
- Position startPosition = getPositionForOffset(node, start);
- Position endPosition = getPositionForOffset(node, end);
- VisibleSelection selection(startPosition, endPosition);
- SelectionController* selector = node->document()->frame()->selection();
- selector->setSelection(selection);
- }
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ updateCacheOnNodeChange();
}
void WebViewCore::setSelection(int start, int end)
@@ -2183,22 +2142,28 @@ void WebViewCore::setSelection(int start, int end)
WebCore::Node* focus = currentFocus();
if (!focus)
return;
- if (start > end)
- swap(start, end);
-
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
+ return;
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
// Tell our EditorClient that this change was generated from the UI, so it
// does not need to echo it to the UI.
EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
m_mainFrame->editor()->client());
client->setUiGeneratedSelectionChange(true);
- setSelection(focus, start, end);
- RenderTextControl* control = toRenderTextControl(focus);
- if (start != end && control) {
+ setSelectionRange(focus, start, end);
+ if (start != end) {
// Fire a select event. No event is sent when the selection reduces to
// an insertion point
+ RenderTextControl* control = toRenderTextControl(renderer);
control->selectionChanged(true);
}
client->setUiGeneratedSelectionChange(false);
+ WebCore::Frame* focusedFrame = focus->document()->frame();
bool isPasswordField = false;
if (focus->isElementNode()) {
WebCore::Element* element = static_cast<WebCore::Element*>(focus);
@@ -2207,7 +2172,7 @@ void WebViewCore::setSelection(int start, int end)
}
// For password fields, this is done in the UI side via
// bringPointIntoView, since the UI does the drawing.
- if ((control && control->isTextArea()) || !isPasswordField)
+ if (renderer->isTextArea() || !isPasswordField)
revealSelection();
}
@@ -2232,7 +2197,7 @@ String WebViewCore::modifySelection(const int direction, const int axis)
case AXIS_DOCUMENT:
return modifySelectionDomNavigationAxis(selection, direction, axis);
default:
- ALOGE("Invalid navigation axis: %d", axis);
+ LOGE("Invalid navigation axis: %d", axis);
return String();
}
}
@@ -2273,9 +2238,9 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i
// initialize the selection if necessary
if (selection->rangeCount() == 0) {
if (m_currentNodeDomNavigationAxis
- && validNode(m_mainFrame,
+ && CacheBuilder::validNode(m_mainFrame,
m_mainFrame, m_currentNodeDomNavigationAxis)) {
- RefPtr<Range> rangeRef =
+ PassRefPtr<Range> rangeRef =
selection->frame()->document()->createRange();
rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec);
m_currentNodeDomNavigationAxis = 0;
@@ -2284,6 +2249,15 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i
selection->addRange(rangeRef.get());
} else if (currentFocus()) {
selection->setPosition(currentFocus(), 0, ec);
+ } else if (m_cursorNode
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_cursorNode)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec);
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
} else {
selection->setPosition(body, 0, ec);
}
@@ -2482,13 +2456,13 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i
scrollNodeIntoView(m_mainFrame, selection->anchorNode());
// format markup for the visible content
- RefPtr<Range> range = selection->getRangeAt(0, ec);
+ PassRefPtr<Range> range = selection->getRangeAt(0, ec);
if (ec)
return String();
IntRect bounds = range->boundingBox();
selectAt(bounds.center().x(), bounds.center().y());
markup = formatMarkup(selection);
- ALOGV("Selection markup: %s", markup.utf8().data());
+ LOGV("Selection markup: %s", markup.utf8().data());
return markup;
}
@@ -2696,7 +2670,7 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in
if (!m_currentNodeDomNavigationAxis)
m_currentNodeDomNavigationAxis = currentFocus();
if (!m_currentNodeDomNavigationAxis
- || !validNode(m_mainFrame, m_mainFrame,
+ || !CacheBuilder::validNode(m_mainFrame, m_mainFrame,
m_currentNodeDomNavigationAxis))
m_currentNodeDomNavigationAxis = body;
Node* currentNode = m_currentNodeDomNavigationAxis;
@@ -2740,14 +2714,14 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in
if (direction == DIRECTION_FORWARD)
currentNode = currentNode->lastDescendant();
} else {
- ALOGE("Invalid axis: %d", axis);
+ LOGE("Invalid axis: %d", axis);
return String();
}
if (currentNode) {
m_currentNodeDomNavigationAxis = currentNode;
scrollNodeIntoView(m_mainFrame, currentNode);
String selectionString = createMarkup(currentNode);
- ALOGV("Selection markup: %s", selectionString.utf8().data());
+ LOGV("Selection markup: %s", selectionString.utf8().data());
return selectionString;
}
return String();
@@ -2797,7 +2771,7 @@ bool WebViewCore::isVisible(Node* node)
while (currentNode && currentNode != body) {
RenderStyle* style = currentNode->computedStyle();
if (style &&
- (style->display() == WebCore::NONE || style->visibility() == WebCore::HIDDEN)) {
+ (style->display() == NONE || style->visibility() == HIDDEN)) {
return false;
}
currentNode = currentNode->parentNode();
@@ -2809,7 +2783,7 @@ String WebViewCore::formatMarkup(DOMSelection* selection)
{
ExceptionCode ec = 0;
String markup = String();
- RefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
+ PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
if (ec)
return String();
if (!wholeRange->startContainer() || !wholeRange->startContainer())
@@ -2819,7 +2793,7 @@ String WebViewCore::formatMarkup(DOMSelection* selection)
Node* firstNode = wholeRange->firstNode();
Node* pastLastNode = wholeRange->pastLastNode();
Node* currentNode = firstNode;
- RefPtr<Range> currentRange;
+ PassRefPtr<Range> currentRange;
while (currentNode != pastLastNode) {
Node* nextNode = currentNode->traverseNextNode();
@@ -2896,6 +2870,7 @@ void WebViewCore::deleteSelection(int start, int end, int textGeneration)
key(up);
client->setUiGeneratedSelectionChange(false);
m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
}
void WebViewCore::replaceTextfieldText(int oldStart,
@@ -2911,15 +2886,13 @@ void WebViewCore::replaceTextfieldText(int oldStart,
EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
m_mainFrame->editor()->client());
client->setUiGeneratedSelectionChange(true);
- if (replace.length())
- WebCore::TypingCommand::insertText(focus->document(), replace,
- false);
- else
- WebCore::TypingCommand::deleteSelection(focus->document());
+ WebCore::TypingCommand::insertText(focus->document(), replace,
+ false);
client->setUiGeneratedSelectionChange(false);
// setSelection calls revealSelection, so there is no need to do it here.
setSelection(start, end);
m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
}
void WebViewCore::passToJs(int generation, const WTF::String& current,
@@ -2927,6 +2900,13 @@ void WebViewCore::passToJs(int generation, const WTF::String& current,
{
WebCore::Node* focus = currentFocus();
if (!focus) {
+ DBG_NAV_LOG("!focus");
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
clearTextEntry();
return;
}
@@ -2941,36 +2921,42 @@ void WebViewCore::passToJs(int generation, const WTF::String& current,
client->setUiGeneratedSelectionChange(false);
m_blockTextfieldUpdates = false;
m_textGeneration = generation;
- WTF::String test = getInputText(focus);
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ WTF::String test = renderText->text();
if (test != current) {
// If the text changed during the key event, update the UI text field.
updateTextfield(focus, false, test);
+ } else {
+ DBG_NAV_LOG("test == current");
}
// Now that the selection has settled down, send it.
updateTextSelection();
+ m_shouldPaintCaret = true;
}
-WebCore::IntRect WebViewCore::scrollFocusedTextInput(float xPercent, int y)
+void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
{
WebCore::Node* focus = currentFocus();
if (!focus) {
+ DBG_NAV_LOG("!focus");
clearTextEntry();
- return WebCore::IntRect();
+ return;
}
- WebCore::RenderTextControl* renderText = toRenderTextControl(focus);
- if (!renderText) {
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
clearTextEntry();
- return WebCore::IntRect();
+ return;
}
-
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
int x = (int) (xPercent * (renderText->scrollWidth() -
renderText->clientWidth()));
+ DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
+ xPercent, renderText->scrollWidth(), renderText->clientWidth());
renderText->setScrollLeft(x);
renderText->setScrollTop(y);
- focus->document()->frame()->selection()->recomputeCaretRect();
- LayerAndroid* layer = 0;
- platformLayerIdFromNode(focus, &layer);
- return absoluteContentRect(focus, layer);
}
void WebViewCore::setFocusControllerActive(bool active)
@@ -2980,7 +2966,7 @@ void WebViewCore::setFocusControllerActive(bool active)
void WebViewCore::saveDocumentState(WebCore::Frame* frame)
{
- if (!validNode(m_mainFrame, frame, 0))
+ if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
frame = m_mainFrame;
WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
@@ -2996,9 +2982,9 @@ void WebViewCore::saveDocumentState(WebCore::Frame* frame)
static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
{
jclass stringClass = env->FindClass("java/lang/String");
- ALOG_ASSERT(stringClass, "Could not find java/lang/String");
+ LOG_ASSERT(stringClass, "Could not find java/lang/String");
jobjectArray array = env->NewObjectArray(count, stringClass, 0);
- ALOG_ASSERT(array, "Could not create new string array");
+ LOG_ASSERT(array, "Could not create new string array");
for (size_t i = 0; i < count; i++) {
jobject newString = env->NewString(&labels[i][1], labels[i][0]);
@@ -3021,19 +3007,11 @@ void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser)
return;
WTF::String acceptType = chooser->acceptTypes();
- WTF::String capture;
-
-#if ENABLE(MEDIA_CAPTURE)
- capture = chooser->capture();
-#endif
-
jstring jAcceptType = wtfStringToJstring(env, acceptType, true);
- jstring jCapture = wtfStringToJstring(env, capture, true);
jstring jName = (jstring) env->CallObjectMethod(
- javaObject.get(), m_javaGlue->m_openFileChooser, jAcceptType, jCapture);
+ javaObject.get(), m_javaGlue->m_openFileChooser, jAcceptType);
checkException(env);
env->DeleteLocalRef(jAcceptType);
- env->DeleteLocalRef(jCapture);
WTF::String wtfString = jstringToWtfString(env, jName);
env->DeleteLocalRef(jName);
@@ -3045,7 +3023,7 @@ void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser)
void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
bool multiple, const int selected[], size_t selectedCountOrSelection)
{
- ALOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
+ LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue->object(env);
@@ -3104,15 +3082,14 @@ bool WebViewCore::key(const PlatformKeyboardEvent& event)
{
WebCore::EventHandler* eventHandler;
WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
+ event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
if (focusNode) {
WebCore::Frame* frame = focusNode->document()->frame();
+ WebFrame* webFrame = WebFrame::getWebFrame(frame);
eventHandler = frame->eventHandler();
VisibleSelection old = frame->selection()->selection();
- EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
- m_mainFrame->editor()->client());
- client->setUiGeneratedSelectionChange(true);
bool handled = eventHandler->keyEvent(event);
- client->setUiGeneratedSelectionChange(false);
if (isContentEditable(focusNode)) {
// keyEvent will return true even if the contentEditable did not
// change its selection. In the case that it does not, we want to
@@ -3122,53 +3099,33 @@ bool WebViewCore::key(const PlatformKeyboardEvent& event)
}
return handled;
} else {
- eventHandler = focusedFrame()->eventHandler();
+ eventHandler = m_mainFrame->eventHandler();
}
return eventHandler->keyEvent(event);
}
-bool WebViewCore::chromeCanTakeFocus(FocusDirection direction)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
- return false;
- return env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_chromeCanTakeFocus, direction);
-}
-
-void WebViewCore::chromeTakeFocus(FocusDirection direction)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
- return;
- env->CallVoidMethod(javaObject.get(), m_javaGlue->m_chromeTakeFocus, direction);
-}
-
-void WebViewCore::setInitialFocus(const WebCore::PlatformKeyboardEvent& platformEvent)
-{
- Frame* frame = focusedFrame();
- Document* document = frame->document();
- if (document)
- document->setFocusedNode(0);
- FocusDirection direction;
- switch (platformEvent.nativeVirtualKeyCode()) {
- case AKEYCODE_DPAD_LEFT:
- direction = FocusDirectionLeft;
- break;
- case AKEYCODE_DPAD_RIGHT:
- direction = FocusDirectionRight;
- break;
- case AKEYCODE_DPAD_UP:
- direction = FocusDirectionUp;
- break;
- default:
- direction = FocusDirectionDown;
- break;
+// For when the user clicks the trackball, presses dpad center, or types into an
+// unfocused textfield. In the latter case, 'fake' will be true
+void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node, bool fake) {
+ if (!node) {
+ WebCore::IntPoint pt = m_mousePos;
+ pt.move(m_scrollOffsetX, m_scrollOffsetY);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(pt, false);
+ node = hitTestResult.innerNode();
+ frame = node->document()->frame();
+ DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
+ " node=%p", m_mousePos.x(), m_mousePos.y(),
+ m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
+ }
+ if (node) {
+ EditorClientAndroid* client
+ = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setShouldChangeSelectedRange(false);
+ handleMouseClick(frame, node, fake);
+ client->setShouldChangeSelectedRange(true);
}
- RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0);
- m_mainFrame->page()->focusController()->setInitialFocus(direction,
- webkitEvent.get());
}
#if USE(ACCELERATED_COMPOSITING)
@@ -3182,9 +3139,9 @@ GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
}
#endif
-int WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
+bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
{
- int flags = 0;
+ bool preventDefault = false;
#if USE(ACCELERATED_COMPOSITING)
GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
@@ -3225,10 +3182,18 @@ int WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>
type = WebCore::TouchEnd;
defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
break;
+ case 0x100: // WebViewCore.ACTION_LONGPRESS
+ type = WebCore::TouchLongPress;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 0x200: // WebViewCore.ACTION_DOUBLETAP
+ type = WebCore::TouchDoubleTap;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
default:
// We do not support other kinds of touch event inside WebCore
// at the moment.
- ALOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
+ LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
return 0;
}
@@ -3248,41 +3213,52 @@ int WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>
}
WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState);
- if (m_mainFrame->eventHandler()->handleTouchEvent(te))
- flags |= TOUCH_FLAG_PREVENT_DEFAULT;
- if (te.hitTouchHandler())
- flags |= TOUCH_FLAG_HIT_HANDLER;
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
#endif
#if USE(ACCELERATED_COMPOSITING)
if (rootLayer)
rootLayer->pauseDisplay(false);
#endif
- return flags;
+ return preventDefault;
}
-bool WebViewCore::performMouseClick()
+void WebViewCore::touchUp(int touchGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y)
{
- WebCore::PlatformMouseEvent mouseDown(m_mouseClickPos, m_mouseClickPos, WebCore::LeftButton,
- WebCore::MouseEventPressed, 1, false, false, false, false,
- WTF::currentTime());
- // ignore the return from as it will return true if the hit point can trigger selection change
- m_mainFrame->eventHandler()->handleMousePressEvent(mouseDown);
- WebCore::PlatformMouseEvent mouseUp(m_mouseClickPos, m_mouseClickPos, WebCore::LeftButton,
- WebCore::MouseEventReleased, 1, false, false, false, false,
- WTF::currentTime());
- bool handled = m_mainFrame->eventHandler()->handleMouseReleaseEvent(mouseUp);
-
- WebCore::Node* focusNode = currentFocus();
- initializeTextInput(focusNode, false);
- return handled;
+ if (touchGeneration == 0) {
+ // m_mousePos should be set in getTouchHighlightRects()
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false);
+ node = hitTestResult.innerNode();
+ if (node)
+ frame = node->document()->frame();
+ else
+ frame = 0;
+ DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame);
+ } else {
+ if (m_touchGeneration > touchGeneration) {
+ DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
+ " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
+ return; // short circuit if a newer touch has been generated
+ }
+ // This moves m_mousePos to the correct place, and handleMouseClick uses
+ // m_mousePos to determine where the click happens.
+ moveMouse(frame, x, y);
+ m_lastGeneration = touchGeneration;
+ }
+ if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node, false);
}
// Check for the "x-webkit-soft-keyboard" attribute. If it is there and
// set to hidden, do not show the soft keyboard. Node passed as a parameter
// must not be null.
static bool shouldSuppressKeyboard(const WebCore::Node* node) {
- ALOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
+ LOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
const NamedNodeMap* attributes = node->attributes();
if (!attributes) return false;
size_t length = attributes->length();
@@ -3294,160 +3270,84 @@ static bool shouldSuppressKeyboard(const WebCore::Node* node) {
return false;
}
-WebViewCore::InputType WebViewCore::getInputType(Node* node)
-{
- WebCore::RenderObject* renderer = node->renderer();
- if (!renderer)
- return WebViewCore::NONE;
- if (renderer->isTextArea())
- return WebViewCore::TEXT_AREA;
-
- if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
- HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node);
- if (htmlInput->isPasswordField())
- return WebViewCore::PASSWORD;
- if (htmlInput->isSearchField())
- return WebViewCore::SEARCH;
- if (htmlInput->isEmailField())
- return WebViewCore::EMAIL;
- if (htmlInput->isNumberField())
- return WebViewCore::NUMBER;
- if (htmlInput->isTelephoneField())
- return WebViewCore::TELEPHONE;
- if (htmlInput->isTextField())
- return WebViewCore::NORMAL_TEXT_FIELD;
- }
-
- if (node->isContentEditable())
- return WebViewCore::TEXT_AREA;
-
- return WebViewCore::NONE;
-}
-
-int WebViewCore::getMaxLength(Node* node)
-{
- int maxLength = -1;
- if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
- HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node);
- maxLength = htmlInput->maxLength();
- }
- return maxLength;
-}
-
-String WebViewCore::getFieldName(Node* node)
-{
- String name;
- if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
- HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node);
- name = htmlInput->name();
- }
- return name;
-}
-
-bool WebViewCore::isSpellCheckEnabled(Node* node)
-{
- bool isEnabled = true;
- if (node->isElementNode()) {
- WebCore::Element* element = static_cast<WebCore::Element*>(node);
- isEnabled = element->isSpellCheckingEnabled();
- }
- return isEnabled;
-}
-
-bool WebViewCore::isAutoCompleteEnabled(Node* node)
-{
- bool isEnabled = false;
- if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
- HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node);
- isEnabled = htmlInput->autoComplete();
- }
- return isEnabled;
-}
-
-WebCore::IntRect WebViewCore::absoluteContentRect(WebCore::Node* node,
- LayerAndroid* layer)
-{
- IntRect contentRect;
- if (node) {
- RenderObject* render = node->renderer();
- if (render && render->isBox() && !render->isBody()) {
- IntPoint offset = convertGlobalContentToFrameContent(IntPoint(),
- node->document()->frame());
- WebViewCore::layerToAbsoluteOffset(layer, offset);
-
- RenderBox* renderBox = toRenderBox(render);
- contentRect = renderBox->absoluteContentBox();
- contentRect.move(-offset.x(), -offset.y());
+// Common code for both clicking with the trackball and touchUp
+// Also used when typing into a non-focused textfield to give the textfield focus,
+// in which case, 'fake' is set to true
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake)
+{
+ bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ if (valid && nodePtr) {
+ // Need to special case area tags because an image map could have an area element in the middle
+ // so when attempting to get the default, the point chosen would be follow the wrong link.
+ if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
+ webFrame->setUserInitiatedAction(true);
+ nodePtr->dispatchSimulatedClick(0, true, true);
+ webFrame->setUserInitiatedAction(false);
+ DBG_NAV_LOG("area");
+ return true;
}
}
- return contentRect;
-}
+ if (!valid || !framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedAction(true);
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WTF::currentTime());
+ // ignore the return from as it will return true if the hit point can trigger selection change
+ framePtr->eventHandler()->handleMousePressEvent(mouseDown);
+ WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventReleased, 1, false, false, false, false,
+ WTF::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedAction(false);
-jobject WebViewCore::createTextFieldInitData(Node* node)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- TextFieldInitDataGlue* classDef = m_textFieldInitDataGlue;
- ScopedLocalRef<jclass> clazz(env,
- env->FindClass("android/webkit/WebViewCore$TextFieldInitData"));
- jobject initData = env->NewObject(clazz.get(), classDef->m_constructor);
- env->SetIntField(initData, classDef->m_fieldPointer,
- reinterpret_cast<int>(node));
- ScopedLocalRef<jstring> inputText(env,
- wtfStringToJstring(env, getInputText(node), true));
- env->SetObjectField(initData, classDef->m_text, inputText.get());
- env->SetIntField(initData, classDef->m_type, getInputType(node));
- env->SetBooleanField(initData, classDef->m_isSpellCheckEnabled,
- isSpellCheckEnabled(node));
- Document* document = node->document();
- PlatformKeyboardEvent tab(AKEYCODE_TAB, 0, 0, false, false, false, false);
- PassRefPtr<KeyboardEvent> tabEvent =
- KeyboardEvent::create(tab, document->defaultView());
- env->SetBooleanField(initData, classDef->m_isTextFieldNext,
- isTextInput(document->nextFocusableNode(node, tabEvent.get())));
- env->SetBooleanField(initData, classDef->m_isTextFieldPrev,
- isTextInput(document->previousFocusableNode(node, tabEvent.get())));
- env->SetBooleanField(initData, classDef->m_isAutoCompleteEnabled,
- isAutoCompleteEnabled(node));
- ScopedLocalRef<jstring> fieldName(env,
- wtfStringToJstring(env, getFieldName(node), false));
- env->SetObjectField(initData, classDef->m_name, fieldName.get());
- ScopedLocalRef<jstring> label(env,
- wtfStringToJstring(env, requestLabel(document->frame(), node), false));
- env->SetObjectField(initData, classDef->m_label, label.get());
- env->SetIntField(initData, classDef->m_maxLength, getMaxLength(node));
- LayerAndroid* layer = 0;
- int layerId = platformLayerIdFromNode(node, &layer);
- IntRect bounds = absoluteContentRect(node, layer);
- ScopedLocalRef<jobject> jbounds(env, intRectToRect(env, bounds));
- env->SetObjectField(initData, classDef->m_contentBounds, jbounds.get());
- env->SetIntField(initData, classDef->m_nodeLayerId, layerId);
- IntRect contentRect;
- RenderTextControl* rtc = toRenderTextControl(node);
- if (rtc) {
- contentRect.setWidth(rtc->scrollWidth());
- contentRect.setHeight(rtc->scrollHeight());
- contentRect.move(-rtc->scrollLeft(), -rtc->scrollTop());
+ // If the user clicked on a textfield, make the focusController active
+ // so we show the blinking cursor.
+ WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
+ m_mousePos.y(), focusNode, handled ? "true" : "false");
+ if (focusNode) {
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ bool ime = !shouldSuppressKeyboard(focusNode)
+ && !(static_cast<WebCore::HTMLInputElement*>(focusNode))->readOnly();
+ if (ime) {
+#if ENABLE(WEB_AUTOFILL)
+ if (renderer->isTextField()) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(framePtr->page()->editorClient());
+ WebAutofill* autoFill = editorC->getAutofill();
+ autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode));
+ }
+#endif
+ if (!fake) {
+ RenderTextControl* rtc
+ = static_cast<RenderTextControl*> (renderer);
+ // Force an update of the navcache as this will fire off a
+ // message to WebView that *must* have an updated focus.
+ m_frameCacheOutOfDate = true;
+ updateFrameCache();
+ requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
+ rtc->selectionEnd());
+ }
+ } else if (!fake) {
+ requestKeyboard(false);
+ }
+ } else if (!fake){
+ // If the selection is contentEditable, show the keyboard so the
+ // user can type. Otherwise hide the keyboard because no text
+ // input is needed.
+ if (isContentEditable(focusNode)) {
+ requestKeyboard(true);
+ } else if (!nodeIsPlugin(focusNode)) {
+ clearTextEntry();
+ }
+ }
+ } else if (!fake) {
+ // There is no focusNode, so the keyboard is not needed.
+ clearTextEntry();
}
- ScopedLocalRef<jobject> jcontentRect(env, intRectToRect(env, contentRect));
- env->SetObjectField(initData, classDef->m_contentRect, jcontentRect.get());
- return initData;
-}
-
-void WebViewCore::initEditField(Node* node)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
- return;
- m_textGeneration = 0;
- int start = 0;
- int end = 0;
- getSelectionOffsets(node, start, end);
- SelectText* selectText = createSelectText(focusedFrame()->selection()->selection());
- ScopedLocalRef<jobject> initData(env, createTextFieldInitData(node));
- env->CallVoidMethod(javaObject.get(), m_javaGlue->m_initEditField,
- start, end, reinterpret_cast<int>(selectText), initData.get());
- checkException(env);
+ return handled;
}
void WebViewCore::popupReply(int index)
@@ -3468,99 +3368,28 @@ void WebViewCore::popupReply(const int* array, int count)
}
}
-// This is a slightly modified Node::nextNodeConsideringAtomicNodes() with the
-// extra constraint of limiting the search to inside a containing parent
-WebCore::Node* nextNodeWithinParent(WebCore::Node* parent, WebCore::Node* start)
+void WebViewCore::formDidBlur(const WebCore::Node* node)
{
- if (!isAtomicNode(start) && start->firstChild())
- return start->firstChild();
- if (start->nextSibling())
- return start->nextSibling();
- const Node *n = start;
- while (n && !n->nextSibling()) {
- n = n->parentNode();
- if (n == parent)
- return 0;
- }
- if (n)
- return n->nextSibling();
- return 0;
+ // If the blur is on a text input, keep track of the node so we can
+ // hide the soft keyboard when the new focus is set, if it is not a
+ // text input.
+ if (isTextInput(node))
+ m_blurringNodePointer = reinterpret_cast<int>(node);
}
-void WebViewCore::initializeTextInput(WebCore::Node* node, bool fake)
+void WebViewCore::focusNodeChanged(const WebCore::Node* newFocus)
{
- if (node) {
- if (isTextInput(node)) {
- bool showKeyboard = true;
- initEditField(node);
- WebCore::RenderTextControl* rtc = toRenderTextControl(node);
- if (rtc && node->hasTagName(HTMLNames::inputTag)) {
- HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node);
- bool ime = !shouldSuppressKeyboard(node) && !inputElement->readOnly();
- if (ime) {
-#if ENABLE(WEB_AUTOFILL)
- if (rtc->isTextField()) {
- Page* page = node->document()->page();
- EditorClient* editorClient = page->editorClient();
- EditorClientAndroid* androidEditor =
- static_cast<EditorClientAndroid*>(editorClient);
- WebAutofill* autoFill = androidEditor->getAutofill();
- autoFill->formFieldFocused(inputElement);
- }
-#endif
- } else
- showKeyboard = false;
- }
- if (!fake)
- requestKeyboard(showKeyboard);
- } else if (!fake && !nodeIsPlugin(node)) {
- // not a text entry field, put away the keyboard.
- clearTextEntry();
- }
- } else if (!fake) {
- // There is no focusNode, so the keyboard is not needed.
- clearTextEntry();
- }
-}
-
-void WebViewCore::focusNodeChanged(WebCore::Node* newFocus)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
- return;
if (isTextInput(newFocus))
- initializeTextInput(newFocus, true);
- HitTestResult focusHitResult;
- focusHitResult.setInnerNode(newFocus);
- focusHitResult.setInnerNonSharedNode(newFocus);
- if (newFocus && newFocus->isLink() && newFocus->isElementNode()) {
- focusHitResult.setURLElement(static_cast<Element*>(newFocus));
- if (newFocus->hasChildNodes() && !newFocus->hasTagName(HTMLNames::imgTag)) {
- // Check to see if any of the children are images, and if so
- // set them as the innerNode and innerNonSharedNode
- // This will stop when it hits the first image. I'm not sure what
- // should be done in the case of multiple images inside one anchor...
- Node* nextNode = newFocus->firstChild();
- bool found = false;
- while (nextNode) {
- if (nextNode->hasTagName(HTMLNames::imgTag)) {
- found = true;
- break;
- }
- nextNode = nextNodeWithinParent(newFocus, nextNode);
- }
- if (found) {
- focusHitResult.setInnerNode(nextNode);
- focusHitResult.setInnerNonSharedNode(nextNode);
- }
- }
+ m_shouldPaintCaret = true;
+ else if (m_blurringNodePointer) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue->object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue->m_formDidBlur, m_blurringNodePointer);
+ checkException(env);
+ m_blurringNodePointer = 0;
}
- AndroidHitTestResult androidHitTest(this, focusHitResult);
- jobject jHitTestObj = androidHitTest.createJavaObject(env);
- env->CallVoidMethod(javaObject.get(), m_javaGlue->m_focusNodeChanged,
- reinterpret_cast<int>(newFocus), jHitTestObj);
- env->DeleteLocalRef(jHitTestObj);
}
void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
@@ -3759,57 +3588,7 @@ WebViewCore::getWebViewJavaObject()
AutoJObject javaObject = m_javaGlue->object(env);
if (!javaObject.get())
return 0;
- return env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getWebView);
-}
-
-RenderTextControl* WebViewCore::toRenderTextControl(Node* node)
-{
- RenderTextControl* rtc = 0;
- RenderObject* renderer = node->renderer();
- if (renderer && renderer->isTextControl()) {
- rtc = WebCore::toRenderTextControl(renderer);
- }
- return rtc;
-}
-
-void WebViewCore::getSelectionOffsets(Node* node, int& start, int& end)
-{
- RenderTextControl* rtc = toRenderTextControl(node);
- if (rtc) {
- start = rtc->selectionStart();
- end = rtc->selectionEnd();
- } else {
- // It must be content editable field.
- Document* document = node->document();
- Frame* frame = document->frame();
- SelectionController* selector = frame->selection();
- Position selectionStart = selector->start();
- Position selectionEnd = selector->end();
- Position startOfNode = firstPositionInNode(node);
- RefPtr<Range> startRange = Range::create(document, startOfNode,
- selectionStart);
- start = TextIterator::rangeLength(startRange.get(), true);
- RefPtr<Range> endRange = Range::create(document, startOfNode,
- selectionEnd);
- end = TextIterator::rangeLength(endRange.get(), true);
- }
-}
-
-String WebViewCore::getInputText(Node* node)
-{
- String text;
- WebCore::RenderTextControl* renderText = toRenderTextControl(node);
- if (renderText)
- text = renderText->text();
- else {
- // It must be content editable field.
- Position start = firstPositionInNode(node);
- Position end = lastPositionInNode(node);
- VisibleSelection allEditableText(start, end);
- if (allEditableText.isRange())
- text = allEditableText.firstRange()->text();
- }
- return text;
+ return env->GetObjectField(javaObject.get(), gWebViewCoreFields.m_webView);
}
void WebViewCore::updateTextSelection()
@@ -3818,33 +3597,16 @@ void WebViewCore::updateTextSelection()
AutoJObject javaObject = m_javaGlue->object(env);
if (!javaObject.get())
return;
- VisibleSelection selection = focusedFrame()->selection()->selection();
- int start = 0;
- int end = 0;
- if (selection.isCaretOrRange())
- getSelectionOffsets(selection.start().anchorNode(), start, end);
- SelectText* selectText = createSelectText(selection);
- env->CallVoidMethod(javaObject.get(),
- m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(currentFocus()),
- start, end, m_textGeneration, reinterpret_cast<int>(selectText));
- checkException(env);
-}
-
-void WebViewCore::updateTextSizeAndScroll(WebCore::Node* node)
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
+ WebCore::Node* focusNode = currentFocus();
+ if (!focusNode)
return;
- RenderTextControl* rtc = toRenderTextControl(node);
- if (!rtc)
+ RenderObject* renderer = focusNode->renderer();
+ if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
return;
- int width = rtc->scrollWidth();
- int height = rtc->contentHeight();
- int scrollX = rtc->scrollLeft();
- int scrollY = rtc->scrollTop();
- env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextSizeAndScroll,
- reinterpret_cast<int>(node), width, height, scrollX, scrollY);
+ RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
+ env->CallVoidMethod(javaObject.get(),
+ m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
+ rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
checkException(env);
}
@@ -3888,18 +3650,11 @@ void WebViewCore::setBackgroundColor(SkColor c)
// need (int) cast to find the right constructor
WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
(int)SkColorGetB(c), (int)SkColorGetA(c));
-
- if (view->baseBackgroundColor() == bcolor)
- return;
-
view->setBaseBackgroundColor(bcolor);
// Background color of 0 indicates we want a transparent background
if (c == 0)
view->setTransparent(true);
-
- //invalidate so the new color is shown
- contentInvalidateAll();
}
jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
@@ -4025,6 +3780,21 @@ void WebViewCore::keepScreenOn(bool screenOn) {
m_screenOnCounter--;
}
+bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
+ const IntRect& originalAbsoluteBounds)
+{
+ bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
+ if (!valid)
+ return false;
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return false;
+ IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
+ ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
+ : renderer->absoluteBoundingBoxRect();
+ return absBounds == originalAbsoluteBounds;
+}
+
void WebViewCore::showRect(int left, int top, int width, int height,
int contentWidth, int contentHeight, float xPercentInDoc,
float xPercentInView, float yPercentInDoc, float yPercentInView)
@@ -4078,20 +3848,6 @@ void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& u
return;
jstring jUrlStr = wtfStringToJstring(env, url);
env->CallVoidMethod(javaObject.get(), m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr);
- m_fullscreenVideoMode = true;
- checkException(env);
-}
-
-void WebViewCore::exitFullscreenVideo()
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- AutoJObject javaObject = m_javaGlue->object(env);
- if (!javaObject.get())
- return;
- if (m_fullscreenVideoMode) {
- env->CallVoidMethod(javaObject.get(), m_javaGlue->m_exitFullscreenVideo);
- m_fullscreenVideoMode = false;
- }
checkException(env);
}
#endif
@@ -4117,6 +3873,7 @@ bool WebViewCore::drawIsPaused() const
return false;
}
+#if USE(CHROME_NETWORK_STACK)
void WebViewCore::setWebRequestContextUserAgent()
{
// We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
@@ -4144,6 +3901,7 @@ WebRequestContext* WebViewCore::webRequestContext()
}
return m_webRequestContext.get();
}
+#endif
void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
{
@@ -4164,370 +3922,189 @@ void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
if (!owner)
return;
- if (owner->isRootLayer()) {
- FrameView* view = owner->renderer()->frame()->view();
- IntPoint pt(rect.fLeft, rect.fTop);
- view->setScrollPosition(pt);
- } else
+ if (owner->stackingContext())
owner->scrollToOffset(rect.fLeft, rect.fTop);
#endif
}
-Vector<VisibleSelection> WebViewCore::getTextRanges(
- int startX, int startY, int endX, int endY)
-{
- // These are the positions of the selection handles,
- // which reside below the line that they are selecting.
- // Use the vertical position higher, which will include
- // the selected text.
- startY--;
- endY--;
- VisiblePosition startSelect = visiblePositionForContentPoint(startX, startY);
- VisiblePosition endSelect = visiblePositionForContentPoint(endX, endY);
- Position start = startSelect.deepEquivalent();
- Position end = endSelect.deepEquivalent();
- Vector<VisibleSelection> ranges;
- if (!start.isNull() && !end.isNull()) {
- if (comparePositions(start, end) > 0) {
- swap(start, end); // RTL start/end positions may be swapped
- }
- Position nextRangeStart = start;
- Position previousRangeEnd;
- do {
- VisibleSelection selection(nextRangeStart, end);
- ranges.append(selection);
- previousRangeEnd = selection.end();
- nextRangeStart = nextCandidate(previousRangeEnd);
- } while (comparePositions(previousRangeEnd, end) < 0);
- }
- return ranges;
-}
-
-void WebViewCore::deleteText(int startX, int startY, int endX, int endY)
-{
- Vector<VisibleSelection> ranges =
- getTextRanges(startX, startY, endX, endY);
-
- EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
- m_mainFrame->editor()->client());
- client->setUiGeneratedSelectionChange(true);
-
- SelectionController* selector = m_mainFrame->selection();
- for (size_t i = 0; i < ranges.size(); i++) {
- const VisibleSelection& selection = ranges[i];
- if (selection.isContentEditable()) {
- selector->setSelection(selection, CharacterGranularity);
- Document* document = selection.start().anchorNode()->document();
- WebCore::TypingCommand::deleteSelection(document, 0);
- }
- }
- client->setUiGeneratedSelectionChange(false);
-}
-
-void WebViewCore::insertText(const WTF::String &text)
-{
- WebCore::Node* focus = currentFocus();
- if (!focus || !isTextInput(focus))
- return;
-
- Document* document = focus->document();
-
- EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
- m_mainFrame->editor()->client());
- if (!client)
- return;
- client->setUiGeneratedSelectionChange(true);
- WebCore::TypingCommand::insertText(document, text,
- TypingCommand::PreventSpellChecking);
- client->setUiGeneratedSelectionChange(false);
-}
-
-void WebViewCore::resetFindOnPage()
-{
- m_searchText.truncate(0);
- m_matchCount = 0;
- m_activeMatchIndex = 0;
- m_activeMatch = 0;
-}
-
-int WebViewCore::findTextOnPage(const WTF::String &text)
-{
- resetFindOnPage(); // reset even if parameters are bad
-
- WebCore::Frame* frame = m_mainFrame;
- if (!frame)
- return 0;
-
- m_searchText = text;
- FindOptions findOptions = WebCore::CaseInsensitive;
-
- do {
- frame->document()->markers()->removeMarkers(DocumentMarker::TextMatch);
- m_matchCount += frame->editor()->countMatchesForText(text, findOptions,
- 0, true);
- frame->editor()->setMarkedTextMatchesAreHighlighted(true);
- frame = frame->tree()->traverseNextWithWrap(false);
- } while (frame);
- m_activeMatchIndex = m_matchCount - 1; // prime first findNext
- return m_matchCount;
-}
-
-int WebViewCore::findNextOnPage(bool forward)
-{
- if (!m_mainFrame)
- return -1;
- if (!m_matchCount)
- return -1;
-
- EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
- m_mainFrame->editor()->client());
- client->setUiGeneratedSelectionChange(true);
-
- // Clear previous active match.
- if (m_activeMatch) {
- m_mainFrame->document()->markers()->setMarkersActive(
- m_activeMatch.get(), false);
- }
-
- FindOptions findOptions = WebCore::CaseInsensitive
- | WebCore::StartInSelection | WebCore::WrapAround;
- if (!forward)
- findOptions |= WebCore::Backwards;
-
- // Start from the previous active match.
- if (m_activeMatch) {
- m_mainFrame->selection()->setSelection(m_activeMatch.get());
- }
-
- bool found = m_mainFrame->editor()->findString(m_searchText, findOptions);
- if (found) {
- VisibleSelection selection(m_mainFrame->selection()->selection());
- if (selection.isNone() || selection.start() == selection.end()) {
- // Temporary workaround for findString() refusing to select text
- // marked "-webkit-user-select: none".
- m_activeMatchIndex = 0;
- m_activeMatch = 0;
- } else {
- // Mark current match "active".
- if (forward) {
- ++m_activeMatchIndex;
- if (m_activeMatchIndex == m_matchCount)
- m_activeMatchIndex = 0;
- } else {
- if (m_activeMatchIndex == 0)
- m_activeMatchIndex = m_matchCount;
- --m_activeMatchIndex;
- }
- m_activeMatch = selection.firstRange();
- m_mainFrame->document()->markers()->setMarkersActive(
- m_activeMatch.get(), true);
- m_mainFrame->selection()->revealSelection(
- ScrollAlignment::alignCenterIfNeeded, true);
- }
- }
-
- // Clear selection so it doesn't display.
- m_mainFrame->selection()->clear();
- client->setUiGeneratedSelectionChange(false);
- return m_activeMatchIndex;
-}
-
-String WebViewCore::getText(int startX, int startY, int endX, int endY)
-{
- String text;
-
- Vector<VisibleSelection> ranges =
- getTextRanges(startX, startY, endX, endY);
-
- for (size_t i = 0; i < ranges.size(); i++) {
- const VisibleSelection& selection = ranges[i];
- if (selection.isRange()) {
- PassRefPtr<Range> range = selection.firstRange();
- String textInRange = range->text();
- if (textInRange.length() > 0) {
- if (text.length() > 0)
- text.append('\n');
- text.append(textInRange);
- }
- }
- }
-
- return text;
-}
-
-/**
- * Read the persistent locale.
- */
-void WebViewCore::getLocale(String& language, String& region)
-{
- char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
-
- property_get("persist.sys.language", propLang, "");
- property_get("persist.sys.country", propRegn, "");
- if (*propLang == 0 && *propRegn == 0) {
- /* Set to ro properties, default is en_US */
- property_get("ro.product.locale.language", propLang, "en");
- property_get("ro.product.locale.region", propRegn, "US");
- }
- language = String(propLang, 2);
- region = String(propRegn, 2);
-}
-
-void WebViewCore::updateLocale()
-{
- static String prevLang;
- static String prevRegn;
- String language;
- String region;
-
- getLocale(language, region);
-
- if ((language != prevLang) || (region != prevRegn)) {
- prevLang = language;
- prevRegn = region;
- GlyphPageTreeNode::resetRoots();
- fontCache()->invalidate();
- }
-}
-
//----------------------------------------------------------------------
// Native JNI methods
//----------------------------------------------------------------------
-static void RevealSelection(JNIEnv* env, jobject obj, jint nativeClass)
+static void RevealSelection(JNIEnv *env, jobject obj)
{
- reinterpret_cast<WebViewCore*>(nativeClass)->revealSelection();
+ GET_NATIVE_VIEW(env, obj)->revealSelection();
}
-static jstring RequestLabel(JNIEnv* env, jobject obj, jint nativeClass,
- int framePointer, int nodePointer)
+static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
+ int nodePointer)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- return wtfStringToJstring(env, viewImpl->requestLabel(
+ return wtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
(WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
}
-static void ClearContent(JNIEnv* env, jobject obj, jint nativeClass)
+static void ClearContent(JNIEnv *env, jobject obj)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
viewImpl->clearContent();
}
-static void SetSize(JNIEnv* env, jobject obj, jint nativeClass, jint width,
- jint height, jint textWrapWidth, jfloat scale, jint screenWidth,
- jint screenHeight, jint anchorX, jint anchorY, jboolean ignoreHeight)
+static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading();
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight,
+ jint anchorX, jint anchorY, jboolean ignoreHeight)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
- ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale,
screenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
}
-static void SetScrollOffset(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean sendScrollEvent, jint x, jint y)
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "need viewImpl");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
- viewImpl->setScrollOffset(sendScrollEvent, x, y);
+ viewImpl->setScrollOffset(gen, sendScrollEvent, x, y);
}
-static void SetGlobalBounds(JNIEnv* env, jobject obj, jint nativeClass,
- jint x, jint y, jint h, jint v)
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "need viewImpl");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
viewImpl->setGlobalBounds(x, y, h, v);
}
-static jboolean Key(JNIEnv* env, jobject obj, jint nativeClass, jint keyCode,
- jint unichar, jint repeatCount, jboolean isShift, jboolean isAlt,
- jboolean isSym, jboolean isDown)
+static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
+ jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
+ jboolean isDown)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- return viewImpl->key(PlatformKeyboardEvent(keyCode,
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
unichar, repeatCount, isDown, isShift, isAlt, isSym));
}
-static void SetInitialFocus(JNIEnv* env, jobject obj, jint nativeClass,
- jint keyDirection)
+static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr, jboolean fake)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- viewImpl->setInitialFocus(PlatformKeyboardEvent(keyDirection,
- 0, 0, false, false, false, false));
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Click");
+
+ viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
+ reinterpret_cast<WebCore::Node*>(nodePtr), fake);
}
-static void ContentInvalidateAll(JNIEnv* env, jobject obj, jint nativeClass)
+static void ContentInvalidateAll(JNIEnv *env, jobject obj)
{
- reinterpret_cast<WebViewCore*>(nativeClass)->contentInvalidateAll();
+ GET_NATIVE_VIEW(env, obj)->contentInvalidateAll();
}
-static void DeleteSelection(JNIEnv* env, jobject obj, jint nativeClass,
- jint start, jint end, jint textGeneration)
+static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
+ jint textGeneration)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
viewImpl->deleteSelection(start, end, textGeneration);
}
-static void SetSelection(JNIEnv* env, jobject obj, jint nativeClass,
- jint start, jint end)
+static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
viewImpl->setSelection(start, end);
}
-static jstring ModifySelection(JNIEnv* env, jobject obj, jint nativeClass,
- jint direction, jint granularity)
+static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint granularity)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
String selectionString = viewImpl->modifySelection(direction, granularity);
return wtfStringToJstring(env, selectionString);
}
-static void ReplaceTextfieldText(JNIEnv* env, jobject obj, jint nativeClass,
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
jint textGeneration)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
WTF::String webcoreString = jstringToWtfString(env, replace);
viewImpl->replaceTextfieldText(oldStart,
oldEnd, webcoreString, start, end, textGeneration);
}
-static void PassToJs(JNIEnv* env, jobject obj, jint nativeClass,
+static void PassToJs(JNIEnv *env, jobject obj,
jint generation, jstring currentText, jint keyCode,
jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
WTF::String current = jstringToWtfString(env, currentText);
- reinterpret_cast<WebViewCore*>(nativeClass)->passToJs(generation, current,
+ GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
}
-static void ScrollFocusedTextInput(JNIEnv* env, jobject obj, jint nativeClass,
- jfloat xPercent, jint y, jobject contentBounds)
+static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
+ jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- IntRect bounds = viewImpl->scrollFocusedTextInput(xPercent, y);
- if (contentBounds)
- GraphicsJNI::irect_to_jrect(bounds, env, contentBounds);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->scrollFocusedTextInput(xPercent, y);
}
-static void SetFocusControllerActive(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean active)
+static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active)
{
- ALOGV("webviewcore::nativeSetFocusControllerActive()\n");
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSetFocusControllerActive()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
viewImpl->setFocusControllerActive(active);
}
-static void SaveDocumentState(JNIEnv* env, jobject obj, jint nativeClass)
+static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame)
{
- ALOGV("webviewcore::nativeSaveDocumentState()\n");
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
- viewImpl->saveDocumentState(viewImpl->focusedFrame());
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSaveDocumentState()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
+ viewImpl->saveDocumentState((WebCore::Frame*) frame);
}
void WebViewCore::addVisitedLink(const UChar* string, int length)
@@ -4536,26 +4113,53 @@ void WebViewCore::addVisitedLink(const UChar* string, int length)
m_groupForVisitedLinks->addVisitedLink(string, length);
}
-static void NotifyAnimationStarted(JNIEnv* env, jobject obj, jint nativeClass)
+static bool UpdateLayers(JNIEnv *env, jobject obj, jint nativeClass, jint jbaseLayer)
+{
+ WebViewCore* viewImpl = (WebViewCore*) nativeClass;
+ BaseLayerAndroid* baseLayer = (BaseLayerAndroid*) jbaseLayer;
+ if (baseLayer) {
+ LayerAndroid* root = static_cast<LayerAndroid*>(baseLayer->getChild(0));
+ if (root)
+ return viewImpl->updateLayers(root);
+ }
+ return true;
+}
+
+static void NotifyAnimationStarted(JNIEnv *env, jobject obj, jint nativeClass)
{
WebViewCore* viewImpl = (WebViewCore*) nativeClass;
viewImpl->notifyAnimationStarted();
}
-static jint RecordContent(JNIEnv* env, jobject obj, jint nativeClass, jobject pt)
+static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
SkIPoint nativePt;
- BaseLayerAndroid* result = viewImpl->recordContent(&nativePt);
+ BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt);
GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
return reinterpret_cast<jint>(result);
}
-static void SendListBoxChoice(JNIEnv* env, jobject obj, jint nativeClass,
- jint choice)
+static void SplitContent(JNIEnv *env, jobject obj, jint content)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent(reinterpret_cast<PictureSet*>(content));
+}
+
+static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
viewImpl->popupReply(choice);
}
@@ -4565,11 +4169,14 @@ static void SendListBoxChoice(JNIEnv* env, jobject obj, jint nativeClass,
// number of items in the average multiple-select listbox.
#define PREPARED_LISTBOX_STORAGE 10
-static void SendListBoxChoices(JNIEnv* env, jobject obj, jint nativeClass,
- jbooleanArray jArray, jint size)
+static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
+ jint size)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
int* array = storage.get();
@@ -4583,19 +4190,21 @@ static void SendListBoxChoices(JNIEnv* env, jobject obj, jint nativeClass,
viewImpl->popupReply(array, count);
}
-// TODO: Move this to WebView.cpp since it is only needed there
-static jstring FindAddress(JNIEnv* env, jobject obj, jstring addr,
- jboolean caseInsensitive)
+static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr,
+ jboolean caseInsensitive)
{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
if (!addr)
return 0;
int length = env->GetStringLength(addr);
if (!length)
return 0;
const jchar* addrChars = env->GetStringChars(addr, 0);
- size_t start, end;
- AddressDetector detector;
- bool success = detector.FindContent(addrChars, addrChars + length, &start, &end);
+ int start, end;
+ bool success = CacheBuilder::FindAddress(addrChars, length,
+ &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
jstring ret = 0;
if (success)
ret = env->NewString(addrChars + start, end - start);
@@ -4603,12 +4212,15 @@ static jstring FindAddress(JNIEnv* env, jobject obj, jstring addr,
return ret;
}
-static jint HandleTouchEvent(JNIEnv* env, jobject obj, jint nativeClass,
- jint action, jintArray idArray, jintArray xArray, jintArray yArray,
- jint count, jint actionIndex, jint metaState)
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jintArray idArray,
+ jintArray xArray, jintArray yArray,
+ jint count, jint actionIndex, jint metaState)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
jint* ptrIdArray = env->GetIntArrayElements(idArray, 0);
jint* ptrXArray = env->GetIntArrayElements(xArray, 0);
jint* ptrYArray = env->GetIntArrayElements(yArray, 0);
@@ -4626,53 +4238,105 @@ static jint HandleTouchEvent(JNIEnv* env, jobject obj, jint nativeClass,
return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState);
}
-static bool MouseClick(JNIEnv* env, jobject obj, jint nativeClass)
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint frame, jint node, jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- return viewImpl->performMouseClick();
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->touchUp(touchGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
}
-static jstring RetrieveHref(JNIEnv* env, jobject obj, jint nativeClass,
- jint x, jint y)
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
WTF::String result = viewImpl->retrieveHref(x, y);
if (!result.isEmpty())
return wtfStringToJstring(env, result);
return 0;
}
-static jstring RetrieveAnchorText(JNIEnv* env, jobject obj, jint nativeClass,
- jint x, jint y)
+static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
WTF::String result = viewImpl->retrieveAnchorText(x, y);
if (!result.isEmpty())
return wtfStringToJstring(env, result);
return 0;
}
-static jstring RetrieveImageSource(JNIEnv* env, jobject obj, jint nativeClass,
- jint x, jint y)
+static jstring RetrieveImageSource(JNIEnv *env, jobject obj, jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- WTF::String result = viewImpl->retrieveImageSource(x, y);
+ WTF::String result = GET_NATIVE_VIEW(env, obj)->retrieveImageSource(x, y);
return !result.isEmpty() ? wtfStringToJstring(env, result) : 0;
}
-static void MoveMouse(JNIEnv* env, jobject obj, jint nativeClass, jint x, jint y)
+static void StopPaintingCaret(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setShouldPaintCaret(false);
+}
+
+static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
- viewImpl->moveMouse(x, y);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
}
-static jint GetContentMinPrefWidth(JNIEnv* env, jobject obj, jint nativeClass)
+static void MoveMouse(JNIEnv *env, jobject obj, jint frame,
+ jint x, jint y)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
+}
+
+static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint frame, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouseIfLatest(moveGeneration,
+ (WebCore::Frame*) frame, x, y);
+}
+
+static void UpdateFrameCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->updateFrameCache();
+}
+
+static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
WebCore::Frame* frame = viewImpl->mainFrame();
if (frame) {
@@ -4687,11 +4351,13 @@ static jint GetContentMinPrefWidth(JNIEnv* env, jobject obj, jint nativeClass)
return 0;
}
-static void SetViewportSettingsFromNative(JNIEnv* env, jobject obj,
- jint nativeClass)
+static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
if (!s)
@@ -4708,49 +4374,66 @@ static void SetViewportSettingsFromNative(JNIEnv* env, jobject obj,
#endif
}
-static void SetBackgroundColor(JNIEnv* env, jobject obj, jint nativeClass,
- jint color)
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
viewImpl->setBackgroundColor((SkColor) color);
}
-static void DumpDomTree(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean useFile)
+static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
viewImpl->dumpDomTree(useFile);
}
-static void DumpRenderTree(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean useFile)
+static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
viewImpl->dumpRenderTree(useFile);
}
-static void SetJsFlags(JNIEnv* env, jobject obj, jint nativeClass, jstring flags)
+static void DumpNavTree(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpNavTree();
+}
+
+static void DumpV8Counters(JNIEnv*, jobject)
{
+#if USE(V8)
+#ifdef ANDROID_INSTRUMENT
+ V8Counters::dumpCounters();
+#endif
+#endif
+}
+
+static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags)
+{
+#if USE(V8)
WTF::String flagsString = jstringToWtfString(env, flags);
WTF::CString utf8String = flagsString.utf8();
WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
+#endif
}
// Called from the Java side to set a new quota for the origin or new appcache
// max size in response to a notification that the original quota was exceeded or
// that the appcache has reached its maximum size.
-static void SetNewStorageLimit(JNIEnv* env, jobject obj, jint nativeClass,
- jlong quota)
-{
+static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) {
#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
Frame* frame = viewImpl->mainFrame();
// The main thread is blocked awaiting this response, so now we can wake it
@@ -4761,102 +4444,93 @@ static void SetNewStorageLimit(JNIEnv* env, jobject obj, jint nativeClass,
}
// Called from Java to provide a Geolocation permission state for the specified origin.
-static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj,
- jint nativeClass, jstring origin, jboolean allow, jboolean remember)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) {
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
Frame* frame = viewImpl->mainFrame();
ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember);
}
-static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jint nativeClass,
- jstring scheme)
-{
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
}
-static bool FocusBoundsChanged(JNIEnv* env, jobject obj, jint nativeClass)
+static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
{
- return reinterpret_cast<WebViewCore*>(nativeClass)->focusBoundsChanged();
+ return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
}
-static void SetIsPaused(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean isPaused)
+static void SetIsPaused(JNIEnv* env, jobject obj, jboolean isPaused)
{
// tell the webcore thread to stop thinking while we do other work
// (selection and scrolling). This has nothing to do with the lifecycle
// pause and resume.
- reinterpret_cast<WebViewCore*>(nativeClass)->setIsPaused(isPaused);
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(isPaused);
}
-static void Pause(JNIEnv* env, jobject obj, jint nativeClass)
+static void Pause(JNIEnv* env, jobject obj)
{
// This is called for the foreground tab when the browser is put to the
// background (and also for any tab when it is put to the background of the
// browser). The browser can only be killed by the system when it is in the
// background, so saving the Geolocation permission state now ensures that
// is maintained when the browser is killed.
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ChromeClient* chromeClient = viewImpl->mainFrame()->page()->chrome()->client();
+ ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
chromeClientAndroid->storeGeolocationPermissions();
- Frame* mainFrame = viewImpl->mainFrame();
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
if (geolocation)
geolocation->suspend();
}
- if (mainFrame)
- mainFrame->settings()->setMinDOMTimerInterval(BACKGROUND_TIMER_INTERVAL);
- viewImpl->deviceMotionAndOrientationManager()->maybeSuspendClients();
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients();
ANPEvent event;
SkANP::InitEvent(&event, kLifecycle_ANPEventType);
event.data.lifecycle.action = kPause_ANPLifecycleAction;
- viewImpl->sendPluginEvent(event);
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
- viewImpl->setIsPaused(true);
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
}
-static void Resume(JNIEnv* env, jobject obj, jint nativeClass)
+static void Resume(JNIEnv* env, jobject obj)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- Frame* mainFrame = viewImpl->mainFrame();
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
if (geolocation)
geolocation->resume();
}
- if (mainFrame)
- mainFrame->settings()->setMinDOMTimerInterval(FOREGROUND_TIMER_INTERVAL);
- viewImpl->deviceMotionAndOrientationManager()->maybeResumeClients();
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients();
ANPEvent event;
SkANP::InitEvent(&event, kLifecycle_ANPEventType);
event.data.lifecycle.action = kResume_ANPLifecycleAction;
- viewImpl->sendPluginEvent(event);
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
- viewImpl->setIsPaused(false);
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
}
-static void FreeMemory(JNIEnv* env, jobject obj, jint nativeClass)
+static void FreeMemory(JNIEnv* env, jobject obj)
{
ANPEvent event;
SkANP::InitEvent(&event, kLifecycle_ANPEventType);
event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
- reinterpret_cast<WebViewCore*>(nativeClass)->sendPluginEvent(event);
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
}
-static void ProvideVisitedHistory(JNIEnv* env, jobject obj, jint nativeClass,
- jobject hist)
+static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
jobjectArray array = static_cast<jobjectArray>(hist);
@@ -4871,38 +4545,78 @@ static void ProvideVisitedHistory(JNIEnv* env, jobject obj, jint nativeClass,
}
}
-static void PluginSurfaceReady(JNIEnv* env, jobject obj, jint nativeClass)
+static void PluginSurfaceReady(JNIEnv* env, jobject obj)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
if (viewImpl)
viewImpl->sendPluginSurfaceReady();
}
// Notification from the UI thread that the plugin's full-screen surface has been discarded
-static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint nativeClass,
- jint npp)
+static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
if (plugin)
plugin->exitFullScreen(false);
}
-static jobject HitTest(JNIEnv* env, jobject obj, jint nativeClass, jint x,
- jint y, jint slop, jboolean doMoveMouse)
+static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
+{
+ int L, T, R, B;
+ GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
+ return WebCore::IntRect(L, T, R - L, B - T);
+}
+
+static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node,
+ jobject rect)
+{
+ IntRect nativeRect = jrect_to_webrect(env, rect);
+ return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds(
+ reinterpret_cast<Frame*>(frame),
+ reinterpret_cast<Node*>(node), nativeRect);
+}
+
+static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop)
{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
if (!viewImpl)
return 0;
- AndroidHitTestResult result = viewImpl->hitTestAtPoint(x, y, slop, doMoveMouse);
- return result.createJavaObject(env);
+ Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop);
+ if (rects.isEmpty())
+ return 0;
+
+ jclass arrayClass = env->FindClass("java/util/ArrayList");
+ LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList");
+ jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for ArrayList");
+ jobject array = env->NewObject(arrayClass, init, rects.size());
+ LOG_ASSERT(array, "Could not create a new ArrayList");
+ jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z");
+ LOG_ASSERT(add, "Could not find add method on ArrayList");
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
+ jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ LOG_ASSERT(rectinit, "Could not find init method on Rect");
+
+ for (size_t i = 0; i < rects.size(); i++) {
+ jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(),
+ rects[i].y(), rects[i].maxX(), rects[i].maxY());
+ if (rect) {
+ env->CallBooleanMethod(array, add, rect);
+ env->DeleteLocalRef(rect);
+ }
+ }
+
+ env->DeleteLocalRef(rectClass);
+ env->DeleteLocalRef(arrayClass);
+ return array;
}
-static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass,
- jint queryId)
+static void AutoFillForm(JNIEnv* env, jobject obj, jint queryId)
{
#if ENABLE(WEB_AUTOFILL)
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
if (!viewImpl)
return;
@@ -4915,87 +4629,19 @@ static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass,
#endif
}
-static void CloseIdleConnections(JNIEnv* env, jobject obj, jint nativeClass)
+static void CloseIdleConnections(JNIEnv* env, jobject obj)
{
+#if USE(CHROME_NETWORK_STACK)
WebCache::get(true)->closeIdleConnections();
WebCache::get(false)->closeIdleConnections();
+#endif
}
-static void nativeCertTrustChanged(JNIEnv *env, jobject obj)
-{
- WebCache::get(true)->certTrustChanged();
- WebCache::get(false)->certTrustChanged();
-}
-
-static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint nativeClass,
- jint layer, jobject jRect)
+static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint layer, jobject jRect)
{
SkRect rect;
GraphicsJNI::jrect_to_rect(env, jRect, &rect);
- reinterpret_cast<WebViewCore*>(nativeClass)->scrollRenderLayer(layer, rect);
-}
-
-static void DeleteText(JNIEnv* env, jobject obj, jint nativeClass,
- jint startX, jint startY, jint endX, jint endY)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- viewImpl->deleteText(startX, startY, endX, endY);
-}
-
-static void InsertText(JNIEnv* env, jobject obj, jint nativeClass,
- jstring text)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- WTF::String wtfText = jstringToWtfString(env, text);
- viewImpl->insertText(wtfText);
-}
-
-static jobject GetText(JNIEnv* env, jobject obj, jint nativeClass,
- jint startX, jint startY, jint endX, jint endY)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- WTF::String text = viewImpl->getText(startX, startY, endX, endY);
- return text.isEmpty() ? 0 : wtfStringToJstring(env, text);
-}
-
-static void SelectText(JNIEnv* env, jobject obj, jint nativeClass,
- jint startX, jint startY, jint endX, jint endY)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- viewImpl->selectText(startX, startY, endX, endY);
-}
-
-static void ClearSelection(JNIEnv* env, jobject obj, jint nativeClass)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- viewImpl->focusedFrame()->selection()->clear();
-}
-
-static bool SelectWordAt(JNIEnv* env, jobject obj, jint nativeClass, jint x, jint y)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- return viewImpl->selectWordAt(x, y);
-}
-
-static void SelectAll(JNIEnv* env, jobject obj, jint nativeClass)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- viewImpl->focusedFrame()->selection()->selectAll();
-}
-
-static int FindAll(JNIEnv* env, jobject obj, jint nativeClass,
- jstring text)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- WTF::String wtfText = jstringToWtfString(env, text);
- return viewImpl->findTextOnPage(wtfText);
-}
-
-static int FindNext(JNIEnv* env, jobject obj, jint nativeClass,
- jboolean forward)
-{
- WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
- return viewImpl->findNextOnPage(forward);
+ GET_NATIVE_VIEW(env, obj)->scrollRenderLayer(layer, rect);
}
// ----------------------------------------------------------------------------
@@ -5004,159 +4650,164 @@ static int FindNext(JNIEnv* env, jobject obj, jint nativeClass,
* JNI registration.
*/
static JNINativeMethod gJavaWebViewCoreMethods[] = {
- { "nativeClearContent", "(I)V",
+ { "nativeClearContent", "()V",
(void*) ClearContent },
- { "nativeFocusBoundsChanged", "(I)Z",
+ { "nativeFocusBoundsChanged", "()Z",
(void*) FocusBoundsChanged } ,
- { "nativeKey", "(IIIIZZZZ)Z",
+ { "nativeKey", "(IIIZZZZ)Z",
(void*) Key },
- { "nativeContentInvalidateAll", "(I)V",
+ { "nativeClick", "(IIZ)V",
+ (void*) Click },
+ { "nativeContentInvalidateAll", "()V",
(void*) ContentInvalidateAll },
- { "nativeSendListBoxChoices", "(I[ZI)V",
+ { "nativeSendListBoxChoices", "([ZI)V",
(void*) SendListBoxChoices },
- { "nativeSendListBoxChoice", "(II)V",
+ { "nativeSendListBoxChoice", "(I)V",
(void*) SendListBoxChoice },
- { "nativeSetSize", "(IIIIFIIIIZ)V",
+ { "nativeSetSize", "(IIIFIIIIZ)V",
(void*) SetSize },
{ "nativeSetScrollOffset", "(IZII)V",
(void*) SetScrollOffset },
- { "nativeSetGlobalBounds", "(IIIII)V",
+ { "nativeSetGlobalBounds", "(IIII)V",
(void*) SetGlobalBounds },
- { "nativeSetSelection", "(III)V",
+ { "nativeSetSelection", "(II)V",
(void*) SetSelection } ,
- { "nativeModifySelection", "(III)Ljava/lang/String;",
+ { "nativeModifySelection", "(II)Ljava/lang/String;",
(void*) ModifySelection },
- { "nativeDeleteSelection", "(IIII)V",
+ { "nativeDeleteSelection", "(III)V",
(void*) DeleteSelection } ,
- { "nativeReplaceTextfieldText", "(IIILjava/lang/String;III)V",
+ { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
(void*) ReplaceTextfieldText } ,
+ { "nativeMoveFocus", "(II)V",
+ (void*) MoveFocus },
{ "nativeMoveMouse", "(III)V",
(void*) MoveMouse },
- { "passToJs", "(IILjava/lang/String;IIZZZZ)V",
+ { "nativeMoveMouseIfLatest", "(IIII)V",
+ (void*) MoveMouseIfLatest },
+ { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
(void*) PassToJs },
- { "nativeScrollFocusedTextInput", "(IFILandroid/graphics/Rect;)V",
+ { "nativeScrollFocusedTextInput", "(FI)V",
(void*) ScrollFocusedTextInput },
- { "nativeSetFocusControllerActive", "(IZ)V",
+ { "nativeSetFocusControllerActive", "(Z)V",
(void*) SetFocusControllerActive },
{ "nativeSaveDocumentState", "(I)V",
(void*) SaveDocumentState },
{ "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
(void*) FindAddress },
- { "nativeHandleTouchEvent", "(II[I[I[IIII)I",
- (void*) HandleTouchEvent },
- { "nativeMouseClick", "(I)Z",
- (void*) MouseClick },
- { "nativeRetrieveHref", "(III)Ljava/lang/String;",
+ { "nativeHandleTouchEvent", "(I[I[I[IIII)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIII)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
(void*) RetrieveHref },
- { "nativeRetrieveAnchorText", "(III)Ljava/lang/String;",
+ { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
(void*) RetrieveAnchorText },
- { "nativeRetrieveImageSource", "(III)Ljava/lang/String;",
+ { "nativeRetrieveImageSource", "(II)Ljava/lang/String;",
(void*) RetrieveImageSource },
- { "nativeGetContentMinPrefWidth", "(I)I",
+ { "nativeStopPaintingCaret", "()V",
+ (void*) StopPaintingCaret },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
(void*) GetContentMinPrefWidth },
+ { "nativeUpdateLayers", "(II)Z",
+ (void*) UpdateLayers },
{ "nativeNotifyAnimationStarted", "(I)V",
(void*) NotifyAnimationStarted },
- { "nativeRecordContent", "(ILandroid/graphics/Point;)I",
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
(void*) RecordContent },
- { "setViewportSettingsFromNative", "(I)V",
+ { "setViewportSettingsFromNative", "()V",
(void*) SetViewportSettingsFromNative },
- { "nativeSetBackgroundColor", "(II)V",
+ { "nativeSplitContent", "(I)V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
(void*) SetBackgroundColor },
- { "nativeRegisterURLSchemeAsLocal", "(ILjava/lang/String;)V",
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
(void*) RegisterURLSchemeAsLocal },
- { "nativeDumpDomTree", "(IZ)V",
+ { "nativeDumpDomTree", "(Z)V",
(void*) DumpDomTree },
- { "nativeDumpRenderTree", "(IZ)V",
+ { "nativeDumpRenderTree", "(Z)V",
(void*) DumpRenderTree },
- { "nativeSetNewStorageLimit", "(IJ)V",
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree },
+ { "nativeDumpV8Counters", "()V",
+ (void*) DumpV8Counters },
+ { "nativeSetNewStorageLimit", "(J)V",
(void*) SetNewStorageLimit },
- { "nativeGeolocationPermissionsProvide", "(ILjava/lang/String;ZZ)V",
+ { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
(void*) GeolocationPermissionsProvide },
- { "nativeSetIsPaused", "(IZ)V", (void*) SetIsPaused },
- { "nativePause", "(I)V", (void*) Pause },
- { "nativeResume", "(I)V", (void*) Resume },
- { "nativeFreeMemory", "(I)V", (void*) FreeMemory },
- { "nativeSetJsFlags", "(ILjava/lang/String;)V", (void*) SetJsFlags },
- { "nativeRequestLabel", "(III)Ljava/lang/String;",
+ { "nativeSetIsPaused", "(Z)V", (void*) SetIsPaused },
+ { "nativePause", "()V", (void*) Pause },
+ { "nativeResume", "()V", (void*) Resume },
+ { "nativeFreeMemory", "()V", (void*) FreeMemory },
+ { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags },
+ { "nativeRequestLabel", "(II)Ljava/lang/String;",
(void*) RequestLabel },
- { "nativeRevealSelection", "(I)V", (void*) RevealSelection },
- { "nativeProvideVisitedHistory", "(I[Ljava/lang/String;)V",
+ { "nativeRevealSelection", "()V", (void*) RevealSelection },
+ { "nativeUpdateFrameCacheIfLoading", "()V",
+ (void*) UpdateFrameCacheIfLoading },
+ { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
(void*) ProvideVisitedHistory },
- { "nativeFullScreenPluginHidden", "(II)V",
+ { "nativeFullScreenPluginHidden", "(I)V",
(void*) FullScreenPluginHidden },
- { "nativePluginSurfaceReady", "(I)V",
+ { "nativePluginSurfaceReady", "()V",
(void*) PluginSurfaceReady },
- { "nativeHitTest", "(IIIIZ)Landroid/webkit/WebViewCore$WebKitHitTest;",
- (void*) HitTest },
- { "nativeAutoFillForm", "(II)V",
+ { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
+ (void*) ValidNodeAndBounds },
+ { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;",
+ (void*) GetTouchHighlightRects },
+ { "nativeAutoFillForm", "(I)V",
(void*) AutoFillForm },
- { "nativeScrollLayer", "(IILandroid/graphics/Rect;)V",
+ { "nativeScrollLayer", "(ILandroid/graphics/Rect;)V",
(void*) ScrollRenderLayer },
- { "nativeCloseIdleConnections", "(I)V",
+ { "nativeCloseIdleConnections", "()V",
(void*) CloseIdleConnections },
- { "nativeDeleteText", "(IIIII)V",
- (void*) DeleteText },
- { "nativeInsertText", "(ILjava/lang/String;)V",
- (void*) InsertText },
- { "nativeGetText", "(IIIII)Ljava/lang/String;",
- (void*) GetText },
- { "nativeSelectText", "(IIIII)V",
- (void*) SelectText },
- { "nativeClearTextSelection", "(I)V",
- (void*) ClearSelection },
- { "nativeSelectWordAt", "(III)Z",
- (void*) SelectWordAt },
- { "nativeSelectAll", "(I)V",
- (void*) SelectAll },
- { "nativeCertTrustChanged","()V",
- (void*) nativeCertTrustChanged },
- { "nativeFindAll", "(ILjava/lang/String;)I",
- (void*) FindAll },
- { "nativeFindNext", "(IZ)I",
- (void*) FindNext },
- { "nativeSetInitialFocus", "(II)V", (void*) SetInitialFocus },
};
int registerWebViewCore(JNIEnv* env)
{
jclass widget = env->FindClass("android/webkit/WebViewCore");
- ALOG_ASSERT(widget,
+ LOG_ASSERT(widget,
"Unable to find class android/webkit/WebViewCore");
gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
"I");
- ALOG_ASSERT(gWebViewCoreFields.m_nativeClass,
+ LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
"Unable to find android/webkit/WebViewCore.mNativeClass");
gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
"mViewportWidth", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
"Unable to find android/webkit/WebViewCore.mViewportWidth");
gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
"mViewportHeight", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
"Unable to find android/webkit/WebViewCore.mViewportHeight");
gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
"mViewportInitialScale", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
"Unable to find android/webkit/WebViewCore.mViewportInitialScale");
gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
"mViewportMinimumScale", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
"Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
"mViewportMaximumScale", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
"Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
"mViewportUserScalable", "Z");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
"Unable to find android/webkit/WebViewCore.mViewportUserScalable");
gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
"mViewportDensityDpi", "I");
- ALOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
+ LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
"Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
+ gWebViewCoreFields.m_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget,
"mDrawIsPaused", "Z");
- ALOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
+ LOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
"Unable to find android/webkit/WebViewCore.mDrawIsPaused");
gWebViewCoreFields.m_lowMemoryUsageMb = env->GetFieldID(widget, "mLowMemoryUsageThresholdMb", "I");
gWebViewCoreFields.m_highMemoryUsageMb = env->GetFieldID(widget, "mHighMemoryUsageThresholdMb", "I");
diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h
index 5264f7f..a19a5b2 100644
--- a/Source/WebKit/android/jni/WebViewCore.h
+++ b/Source/WebKit/android/jni/WebViewCore.h
@@ -26,36 +26,29 @@
#ifndef WebViewCore_h
#define WebViewCore_h
+#include "CacheBuilder.h"
+#include "CachedHistory.h"
#include "DeviceMotionAndOrientationManager.h"
#include "DOMSelection.h"
#include "FileChooser.h"
-#include "FocusDirection.h"
-#include "HitTestResult.h"
-#include "PicturePile.h"
+#include "PictureSet.h"
#include "PlatformGraphicsContext.h"
-#include "Position.h"
-#include "ScrollTypes.h"
#include "SkColor.h"
#include "SkTDArray.h"
#include "SkRegion.h"
-#include "Text.h"
#include "Timer.h"
#include "WebCoreRefObject.h"
#include "WebCoreJni.h"
#include "WebRequestContext.h"
#include "android_npapi.h"
-#include "VisiblePosition.h"
-#include "SelectText.h"
#include <jni.h>
-#include <androidfw/KeycodeLabels.h>
+#include <ui/KeycodeLabels.h>
#include <ui/PixelFormat.h>
#include <utils/threads.h>
-#include <wtf/Threading.h>
namespace WebCore {
class Color;
- class GraphicsOperationCollection;
class FrameView;
class HTMLAnchorElement;
class HTMLElement;
@@ -75,7 +68,6 @@ namespace WebCore {
#if USE(ACCELERATED_COMPOSITING)
namespace WebCore {
class GraphicsLayerAndroid;
- class LayerAndroid;
}
#endif
@@ -104,8 +96,10 @@ namespace android {
AXIS_DOCUMENT = 6
};
+ class CachedFrame;
+ class CachedNode;
+ class CachedRoot;
class ListBoxReply;
- class AndroidHitTestResult;
class WebCoreReply : public WebCoreRefObject {
public:
@@ -122,7 +116,7 @@ namespace android {
};
// one instance of WebViewCore per page for calling into Java's WebViewCore
- class WebViewCore : public WebCoreRefObject, public WebCore::PicturePainter {
+ class WebViewCore : public WebCoreRefObject {
public:
/**
* Initialize the native WebViewCore with a JNI environment, a Java
@@ -137,7 +131,13 @@ namespace android {
// Followings are called from native WebCore to Java
- void focusNodeChanged(WebCore::Node*);
+ /**
+ * Notification that a form was blurred. Pass a message to hide the
+ * keyboard if it was showing for that Node.
+ * @param Node The Node that blurred.
+ */
+ void formDidBlur(const WebCore::Node*);
+ void focusNodeChanged(const WebCore::Node*);
/**
* Scroll to an absolute position.
@@ -161,8 +161,13 @@ namespace android {
*/
void contentDraw();
+ /**
+ * copy the layers to the UI side
+ */
+ void layersDraw();
+
#if USE(ACCELERATED_COMPOSITING)
- WebCore::GraphicsLayerAndroid* graphicsRootLayer() const;
+ GraphicsLayerAndroid* graphicsRootLayer() const;
#endif
/** Invalidate the view/screen, NOT the content/DOM, but expressed in
@@ -202,7 +207,7 @@ namespace android {
* Tell the java side to update the focused textfield
* @param pointer Pointer to the node for the input field.
* @param changeToPassword If true, we are changing the textfield to
- * a password field, and ignore the WTF::String
+ * a password field, and ignore the String
* @param text If changeToPassword is false, this is the new text that
* should go into the textfield.
*/
@@ -217,12 +222,6 @@ namespace android {
*/
void updateTextSelection();
- /**
- * Updates the java side with the node's content size and scroll
- * position.
- */
- void updateTextSizeAndScroll(WebCore::Node* node);
-
void clearTextEntry();
// JavaScript support
void jsAlert(const WTF::String& url, const WTF::String& text);
@@ -278,13 +277,12 @@ namespace android {
jobject getDeviceMotionService();
jobject getDeviceOrientationService();
- void addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel);
+ void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID, int msgLevel);
/**
* Tell the Java side of the scrollbar mode
*/
- void setScrollbarModes(WebCore::ScrollbarMode horizontalMode,
- WebCore::ScrollbarMode verticalMode);
+ void setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode);
//
// Followings support calls from Java to native WebCore
@@ -298,12 +296,16 @@ namespace android {
// If the focus is a textfield (<input>), textarea, or contentEditable,
// scroll the selection on screen (if necessary).
void revealSelection();
+ // Create a single picture to represent the drawn DOM (used by navcache)
+ void recordPicture(SkPicture* picture);
- void moveMouse(int x, int y, WebCore::HitTestResult* hoveredNode = 0,
- bool isClickCandidate = false);
+ void moveFocus(WebCore::Frame* frame, WebCore::Node* node);
+ void moveMouse(WebCore::Frame* frame, int x, int y);
+ void moveMouseIfLatest(int moveGeneration,
+ WebCore::Frame* frame, int x, int y);
// set the scroll amount that webview.java is currently showing
- void setScrollOffset(bool sendScrollEvent, int dx, int dy);
+ void setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy);
void setGlobalBounds(int x, int y, int h, int v);
@@ -316,24 +318,31 @@ namespace android {
* @return Whether keyCode was handled by this class.
*/
bool key(const WebCore::PlatformKeyboardEvent& event);
- bool chromeCanTakeFocus(WebCore::FocusDirection direction);
- void chromeTakeFocus(WebCore::FocusDirection direction);
- void setInitialFocus(const WebCore::PlatformKeyboardEvent& event);
+
+ /**
+ * Handle (trackball) click event / dpad center press from Java.
+ * Also used when typing into an unfocused textfield, in which case 'fake'
+ * will be true.
+ */
+ void click(WebCore::Frame* frame, WebCore::Node* node, bool fake);
/**
* Handle touch event
- * Returns an int with the following flags:
- * bit 0: hit an event handler
- * bit 1: preventDefault was called
*/
- int handleTouchEvent(int action, WTF::Vector<int>& ids,
- WTF::Vector<WebCore::IntPoint>& points,
- int actionIndex, int metaState);
+ bool handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState);
/**
- * Clicks the mouse at its current location
+ * Handle motionUp event from the UI thread (called touchUp in the
+ * WebCore thread).
+ * @param touchGeneration Generation number for touches so we can ignore
+ * touches when a newer one has been generated.
+ * @param frame Pointer to Frame containing the node that was touched.
+ * @param node Pointer to Node that was touched.
+ * @param x x-position of the touch.
+ * @param y y-position of the touch.
*/
- bool performMouseClick();
+ void touchUp(int touchGeneration, WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y);
/**
* Sets the index of the label from a popup
@@ -363,11 +372,11 @@ namespace android {
* direction - The direction in which to alter the selection.
* granularity - The granularity of the selection modification.
*
- * returns - The selected HTML as a WTF::String. This is not a well formed
+ * returns - The selected HTML as a string. This is not a well formed
* HTML, rather the selection annotated with the tags of all
* intermediary elements it crosses.
*/
- WTF::String modifySelection(const int direction, const int granularity);
+ String modifySelection(const int direction, const int granularity);
/**
* Moves the selection to the given node in a given frame i.e. selects that node.
@@ -377,11 +386,11 @@ namespace android {
* frame - The frame in which to select is the node to be selected.
* node - The node to be selected.
*
- * returns - The selected HTML as a WTF::String. This is not a well formed
+ * returns - The selected HTML as a string. This is not a well formed
* HTML, rather the selection annotated with the tags of all
* intermediary elements it crosses.
*/
- WTF::String moveSelection(WebCore::Frame* frame, WebCore::Node* node);
+ String moveSelection(WebCore::Frame* frame, WebCore::Node* node);
/**
* In the currently focused textfield, replace the characters from oldStart to oldEnd
@@ -396,7 +405,7 @@ namespace android {
/**
* Scroll the focused textfield to (x, y) in document space
*/
- WebCore::IntRect scrollFocusedTextInput(float x, int y);
+ void scrollFocusedTextInput(float x, int y);
/**
* Set the FocusController's active and focused states, so that
* the caret will draw (true) or not.
@@ -416,9 +425,11 @@ namespace android {
jobject getWebViewJavaObject();
void setBackgroundColor(SkColor c);
-
+ void updateFrameCache();
+ void updateCacheOnNodeChange();
void dumpDomTree(bool);
void dumpRenderTree(bool);
+ void dumpNavTree();
/* We maintain a list of active plugins. The list is edited by the
pluginview itself. The list is used to service invals to the plugin
@@ -438,7 +449,7 @@ namespace android {
void sendPluginSurfaceReady();
// send onLoad event to plugins who are descendents of the given frame
- void notifyPluginsOnFrameLoad(const WebCore::Frame*);
+ void notifyPluginsOnFrameLoad(const Frame*);
// gets a rect representing the current on-screen portion of the document
void getVisibleScreen(ANPRectI&);
@@ -449,6 +460,9 @@ namespace android {
// lookup the plugin widget struct given an NPP
PluginWidgetAndroid* getPluginWidget(NPP npp);
+ // return the cursorNode if it is a plugin
+ Node* cursorNodeIsPlugin();
+
// Notify the Java side whether it needs to pass down the touch events
void needTouchEvents(bool);
@@ -483,6 +497,8 @@ namespace android {
// Manages requests to keep the screen on while the WebView is visible
void keepScreenOn(bool screenOn);
+ bool validNodeAndBounds(Frame* , Node* , const IntRect& );
+
// Make the rect (left, top, width, height) visible. If it can be fully
// fit, center it on the screen. Otherwise make sure the point specified
// by (left + xPercentInDoc * width, top + yPercentInDoc * height)
@@ -496,11 +512,7 @@ namespace android {
void centerFitRect(int x, int y, int width, int height);
// return a list of rects matching the touch point (x, y) with the slop
- WTF::Vector<WebCore::IntRect> getTouchHighlightRects(int x, int y, int slop,
- WebCore::Node** node, WebCore::HitTestResult* hitTestResult);
- // This does a sloppy hit test
- AndroidHitTestResult hitTestAtPoint(int x, int y, int slop, bool doMoveMouse = false);
- static bool nodeIsClickableOrFocusable(WebCore::Node* node);
+ Vector<IntRect> getTouchHighlightRects(int x, int y, int slop);
// Open a file chooser for selecting a file to upload
void openFileChooser(PassRefPtr<WebCore::FileChooser> );
@@ -510,37 +522,31 @@ namespace android {
bool focusBoundsChanged();
- // record content in a new BaseLayerAndroid, copying the layer tree as well
- WebCore::BaseLayerAndroid* recordContent(SkIPoint* );
+ // record the inval area, and the picture size
+ BaseLayerAndroid* recordContent(SkRegion* , SkIPoint* );
// This creates a new BaseLayerAndroid by copying the current m_content
// and doing a copy of the layers. The layers' content may be updated
// as we are calling layersSync().
- WebCore::BaseLayerAndroid* createBaseLayer(GraphicsLayerAndroid* root);
- bool updateLayers(WebCore::LayerAndroid*);
+ BaseLayerAndroid* createBaseLayer(SkRegion*);
+ bool updateLayers(LayerAndroid*);
void notifyAnimationStarted();
int textWrapWidth() const { return m_textWrapWidth; }
float scale() const { return m_scale; }
float textWrapScale() const { return m_screenWidth * m_scale / m_textWrapWidth; }
WebCore::Frame* mainFrame() const { return m_mainFrame; }
- WebCore::Frame* focusedFrame() const;
-
- void notifyWebAppCanBeInstalled();
+ void updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode);
+ void updateFrameCacheIfLoading();
- void deleteText(int startX, int startY, int endX, int endY);
- WTF::String getText(int startX, int startY, int endX, int endY);
- void insertText(const WTF::String &text);
+ // utility to split slow parts of the picture set
+ void splitContent(PictureSet*);
- // find on page
- void resetFindOnPage();
- int findTextOnPage(const WTF::String &text);
- int findNextOnPage(bool forward);
- void updateMatchCount() const;
+ void notifyWebAppCanBeInstalled();
#if ENABLE(VIDEO)
void enterFullscreenForVideoLayer(int layerId, const WTF::String& url);
- void exitFullscreenVideo();
#endif
void setWebTextViewAutoFillable(int queryId, const string16& previewSummary);
@@ -550,15 +556,19 @@ namespace android {
void listBoxRequest(WebCoreReply* reply, const uint16_t** labels,
size_t count, const int enabled[], size_t enabledCount,
bool multiple, const int selected[], size_t selectedCountOrSelection);
+ bool shouldPaintCaret() { return m_shouldPaintCaret; }
+ void setShouldPaintCaret(bool should) { m_shouldPaintCaret = should; }
bool isPaused() const { return m_isPaused; }
void setIsPaused(bool isPaused) { m_isPaused = isPaused; }
bool drawIsPaused() const;
// The actual content (without title bar) size in doc coordinate
int screenWidth() const { return m_screenWidth; }
int screenHeight() const { return m_screenHeight; }
+#if USE(CHROME_NETWORK_STACK)
void setWebRequestContextUserAgent();
void setWebRequestContextCacheMode(int mode);
WebRequestContext* webRequestContext();
+#endif
// Attempts to scroll the layer to the x,y coordinates of rect. The
// layer is the id of the LayerAndroid.
void scrollRenderLayer(int layer, const SkRect& rect);
@@ -571,62 +581,35 @@ namespace android {
// Check whether a media mimeType is supported in Android media framework.
static bool isSupportedMediaMimeType(const WTF::String& mimeType);
- /**
- * Returns all text ranges consumed by the cursor points referred
- * to by startX, startY, endX, and endY. The vector will be empty
- * if no text is in the given area or if the positions are invalid.
- */
- Vector<WebCore::VisibleSelection> getTextRanges(
- int startX, int startY, int endX, int endY);
- static int platformLayerIdFromNode(WebCore::Node* node,
- WebCore::LayerAndroid** outLayer = 0);
- void selectText(int startX, int startY, int endX, int endY);
- bool selectWordAt(int x, int y);
-
- // Converts from the global content coordinates that WebView sends
- // to frame-local content coordinates using the focused frame
- WebCore::IntPoint convertGlobalContentToFrameContent(const WebCore::IntPoint& point, WebCore::Frame* frame = 0);
- static void layerToAbsoluteOffset(const WebCore::LayerAndroid* layer,
- WebCore::IntPoint& offset);
-
- // Retrieves the current locale from system properties
- void getLocale(String& language, WTF::String& region);
-
- // Handles changes in system locale
- void updateLocale();
-
// these members are shared with webview.cpp
+ static Mutex gFrameCacheMutex;
+ CachedRoot* m_frameCacheKit; // nav data being built by webcore
+ SkPicture* m_navPictureKit;
+ int m_moveGeneration; // copy of state in WebViewNative triggered by move
int m_touchGeneration; // copy of state in WebViewNative triggered by touch
int m_lastGeneration; // last action using up to date cache
+ bool m_updatedFrameCache;
+ bool m_findIsUp;
+ bool m_hasCursorBounds;
+ WebCore::IntRect m_cursorBounds;
+ WebCore::IntRect m_cursorHitBounds;
+ void* m_cursorFrame;
+ IntPoint m_cursorLocation;
+ void* m_cursorNode;
+ static Mutex gCursorBoundsMutex;
// end of shared members
- void setPrerenderingEnabled(bool enable);
-
// internal functions
private:
- enum InputType {
- NONE = -1,
- NORMAL_TEXT_FIELD = 0,
- TEXT_AREA = 1,
- PASSWORD = 2,
- SEARCH = 3,
- EMAIL = 4,
- NUMBER = 5,
- TELEPHONE = 6,
- URL = 7,
- };
-
+ CacheBuilder& cacheBuilder();
WebCore::Node* currentFocus();
- void layout();
// Create a set of pictures to represent the drawn DOM, driven by
// the invalidated region and the time required to draw (used to draw)
- void recordPicturePile();
+ void recordPictureSet(PictureSet* master);
- virtual void paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty);
- virtual SkCanvas* createPrerenderCanvas(WebCore::PrerenderedInval* prerendered);
-#ifdef CONTEXT_RECORDING
- WebCore::GraphicsOperationCollection* rebuildGraphicsOperationCollection(const SkIRect& inval);
-#endif
+ void doMaxScroll(CacheBuilder::Direction dir);
+ SkPicture* rebuildPicture(const SkIRect& inval);
+ void rebuildPictureSet(PictureSet* );
void sendNotifyProgressFinished();
/*
* Handle a mouse click, either from a touch or trackball press.
@@ -635,127 +618,47 @@ namespace android {
* @param fake This is a fake mouse click, used to put a textfield into focus. Do not
* open the IME.
*/
+ bool handleMouseClick(WebCore::Frame*, WebCore::Node*, bool fake);
WebCore::HTMLAnchorElement* retrieveAnchorElement(int x, int y);
WebCore::HTMLElement* retrieveElement(int x, int y,
const WebCore::QualifiedName& );
WebCore::HTMLImageElement* retrieveImageElement(int x, int y);
// below are members responsible for accessibility support
- WTF::String modifySelectionTextNavigationAxis(WebCore::DOMSelection* selection,
- int direction, int granularity);
- WTF::String modifySelectionDomNavigationAxis(WebCore::DOMSelection* selection,
- int direction, int granularity);
- WebCore::Text* traverseNextContentTextNode(WebCore::Node* fromNode,
- WebCore::Node* toNode,
- int direction);
- bool isVisible(WebCore::Node* node);
- bool isHeading(WebCore::Node* node);
- WTF::String formatMarkup(WebCore::DOMSelection* selection);
+ String modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int granularity);
+ String modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int granularity);
+ Text* traverseNextContentTextNode(Node* fromNode, Node* toNode ,int direction);
+ bool isVisible(Node* node);
+ bool isHeading(Node* node);
+ String formatMarkup(DOMSelection* selection);
void selectAt(int x, int y);
- void scrollNodeIntoView(WebCore::Frame* frame, WebCore::Node* node);
- bool isContentTextNode(WebCore::Node* node);
- WebCore::Node* getIntermediaryInputElement(WebCore::Node* fromNode,
- WebCore::Node* toNode,
- int direction);
- bool isContentInputElement(WebCore::Node* node);
- bool isDescendantOf(WebCore::Node* parent, WebCore::Node* node);
- void advanceAnchorNode(WebCore::DOMSelection* selection, int direction,
- WTF::String& markup, bool ignoreFirstNode,
- WebCore::ExceptionCode& ec);
- WebCore::Node* getNextAnchorNode(WebCore::Node* anchorNode,
- bool skipFirstHack, int direction);
- WebCore::Node* getImplicitBoundaryNode(WebCore::Node* node,
- unsigned offset, int direction);
- jobject createTextFieldInitData(WebCore::Node* node);
- /**
- * Calls into java to reset the text edit field with the
- * current contents and selection.
- */
- void initEditField(WebCore::Node* node);
-
- /**
- * If node is not a text input field or if it explicitly requests
- * not to have keyboard input, then the soft keyboard is closed. If
- * it is a text input field then initEditField is called and
- * auto-fill information is requested for HTML form input fields.
- */
- void initializeTextInput(WebCore::Node* node, bool fake);
-
- /**
- * Gets the input type a Node. NONE is returned if it isn't an
- * input field.
- */
- InputType getInputType(WebCore::Node* node);
-
- /**
- * If node is an input field, the spellcheck value for the
- * field is returned. Otherwise true is returned.
- */
- static bool isSpellCheckEnabled(WebCore::Node* node);
-
- /**
- * Returns the offsets of the selection area for both normal text
- * fields and content editable fields. start and end are modified
- * by this method.
- */
- static void getSelectionOffsets(WebCore::Node* node, int& start, int& end);
- /**
- * Gets the plain text of the specified editable text field. node
- * may be content-editable or a plain text fields.
- */
- static WTF::String getInputText(WebCore::Node* node);
- /**
- * Gets the RenderTextControl for the given node if it has one.
- * If its renderer isn't a RenderTextControl, then NULL is returned.
- */
- static WebCore::RenderTextControl* toRenderTextControl(WebCore::Node *node);
- /**
- * Sets the selection for node's editable field to the offsets
- * between start (inclusive) and end (exclusive).
- */
- static void setSelection(WebCore::Node* node, int start, int end);
- /**
- * Returns the Position for the given offset for an editable
- * field. The offset is relative to the node start.
- */
- static WebCore::Position getPositionForOffset(WebCore::Node* node, int offset);
-
- WebCore::VisiblePosition visiblePositionForContentPoint(int x, int y);
- WebCore::VisiblePosition visiblePositionForContentPoint(const WebCore::IntPoint& point);
- bool selectWordAroundPosition(WebCore::Frame* frame,
- WebCore::VisiblePosition pos);
- SelectText* createSelectText(const WebCore::VisibleSelection&);
- void setSelectionCaretInfo(SelectText* selectTextContainer,
- const WebCore::Position& position,
- const WebCore::IntPoint& frameOffset,
- SelectText::HandleId handleId, int offset,
- EAffinity affinity);
- static int getMaxLength(WebCore::Node* node);
- static WTF::String getFieldName(WebCore::Node* node);
- static bool isAutoCompleteEnabled(WebCore::Node* node);
- WebCore::IntRect absoluteContentRect(WebCore::Node* node,
- WebCore::LayerAndroid* layer);
- static WebCore::IntRect positionToTextRect(const WebCore::Position& position,
- WebCore::EAffinity affinity, const WebCore::IntPoint& offset);
- static bool isLtr(const WebCore::Position& position);
- static WebCore::Position trimSelectionPosition(
- const WebCore::Position& start, const WebCore::Position& stop);
+ void scrollNodeIntoView(Frame* frame, Node* node);
+ bool isContentTextNode(Node* node);
+ Node* getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction);
+ bool isContentInputElement(Node* node);
+ bool isDescendantOf(Node* parent, Node* node);
+ void advanceAnchorNode(DOMSelection* selection, int direction, String& markup, bool ignoreFirstNode, ExceptionCode& ec);
+ Node* getNextAnchorNode(Node* anchorNode, bool skipFirstHack, int direction);
+ Node* getImplicitBoundaryNode(Node* node, unsigned offset, int direction);
// called from constructor, to add this to a global list
static void addInstance(WebViewCore*);
// called from destructor, to remove this from a global list
static void removeInstance(WebViewCore*);
- bool prerenderingEnabled();
-
friend class ListBoxReply;
struct JavaGlue;
struct JavaGlue* m_javaGlue;
- struct TextFieldInitDataGlue;
- struct TextFieldInitDataGlue* m_textFieldInitDataGlue;
WebCore::Frame* m_mainFrame;
WebCoreReply* m_popupReply;
- WebCore::PicturePile m_content; // the set of pictures to draw
+ WebCore::Node* m_lastFocused;
+ WebCore::IntRect m_lastFocusedBounds;
+ int m_blurringNodePointer;
+ int m_lastFocusedSelStart;
+ int m_lastFocusedSelEnd;
+ PictureSet m_content; // the set of pictures to draw
+ SkRegion m_addInval; // the accumulated inval region (not yet drawn)
+ SkRegion m_rebuildInval; // the accumulated region for rebuilt pictures
// Used in passToJS to avoid updating the UI text field until after the
// key event has been processed.
bool m_blockTextfieldUpdates;
@@ -764,33 +667,26 @@ namespace android {
// Passed in with key events to know when they were generated. Store it
// with the cache so that we can ignore stale text changes.
int m_textGeneration;
+ CachedRoot* m_temp;
+ SkPicture* m_tempPict;
int m_maxXScroll;
int m_maxYScroll;
int m_scrollOffsetX; // webview.java's current scroll in X
int m_scrollOffsetY; // webview.java's current scroll in Y
- double m_scrollSetTime; // when the scroll was last set
WebCore::IntPoint m_mousePos;
- // This is the location at which we will click. This is tracked
- // separately from m_mousePos, because m_mousePos may be updated
- // in the interval between ACTION_UP and when the click fires since
- // that occurs after a delay. This also works around potential hardware
- // issues if we get onHoverEvents when using the touch screen, as that
- // will nullify the slop checking we do in hitTest (aka, ACTION_DOWN)
- WebCore::IntPoint m_mouseClickPos;
+ bool m_frameCacheOutOfDate;
+ bool m_progressDone;
+ CachedHistory m_history;
int m_screenWidth; // width of the visible rect in document coordinates
int m_screenHeight;// height of the visible rect in document coordinates
int m_textWrapWidth;
float m_scale;
- WebCore::PageGroup* m_groupForVisitedLinks;
+ unsigned m_domtree_version;
+ bool m_check_domtree_version;
+ PageGroup* m_groupForVisitedLinks;
bool m_isPaused;
int m_cacheMode;
- bool m_fullscreenVideoMode;
-
- // find on page data
- WTF::String m_searchText;
- int m_matchCount;
- int m_activeMatchIndex;
- RefPtr<WebCore::Range> m_activeMatch;
+ bool m_shouldPaintCaret;
SkTDArray<PluginWidgetAndroid*> m_plugins;
WebCore::Timer<WebViewCore> m_pluginInvalTimer;
@@ -799,17 +695,17 @@ namespace android {
}
int m_screenOnCounter;
- WebCore::Node* m_currentNodeDomNavigationAxis;
+ Node* m_currentNodeDomNavigationAxis;
DeviceMotionAndOrientationManager m_deviceMotionAndOrientationManager;
#if ENABLE(TOUCH_EVENTS)
bool m_forwardingTouchEvents;
#endif
+#if USE(CHROME_NETWORK_STACK)
scoped_refptr<WebRequestContext> m_webRequestContext;
+#endif
- WTF::Mutex m_prerenderLock;
- bool m_prerenderEnabled;
};
} // namespace android
diff --git a/Source/WebKit/android/nav/CacheBuilder.cpp b/Source/WebKit/android/nav/CacheBuilder.cpp
new file mode 100644
index 0000000..940991f
--- /dev/null
+++ b/Source/WebKit/android/nav/CacheBuilder.cpp
@@ -0,0 +1,3194 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "ColumnInfo.h"
+#include "Document.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+//#include "GraphicsContext.h"
+#include "HTMLAreaElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "InlineTextBox.h"
+#include "KURL.h"
+#include "LayerAndroid.h"
+#include "PluginView.h"
+#include "RegisteredEventListener.h"
+#include "RenderImage.h"
+#include "RenderInline.h"
+#include "RenderLayerBacking.h"
+#include "RenderListBox.h"
+#include "RenderSkinCombo.h"
+#include "RenderTextControl.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
+#include "SkCanvas.h"
+#include "SkPoint.h"
+#include "Text.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreViewBridge.h"
+#include "Widget.h"
+#include <wtf/unicode/Unicode.h>
+
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ FILE* gNavCacheLogFile = NULL;
+ android::Mutex gWriteLogMutex;
+#endif
+
+#include "CacheBuilder.h"
+
+#define MINIMUM_FOCUSABLE_WIDTH 3
+#define MINIMUM_FOCUSABLE_HEIGHT 3
+#define MAXIMUM_FOCUS_RING_COUNT 32
+
+namespace android {
+
+CacheBuilder* CacheBuilder::Builder(Frame* frame) {
+ return &((FrameLoaderClientAndroid*) frame->loader()->client())->getCacheBuilder();
+}
+
+Frame* CacheBuilder::FrameAnd(CacheBuilder* cacheBuilder) {
+ FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*)
+ ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder));
+ return loader->getFrame();
+}
+
+Frame* CacheBuilder::FrameAnd(const CacheBuilder* cacheBuilder) {
+ FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*)
+ ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder));
+ return loader->getFrame();
+}
+
+CacheBuilder::LayerTracker::~LayerTracker() {
+ // Check for a stacking context to prevent a crash in layers without a
+ // parent.
+ if (mRenderLayer && mRenderLayer->stackingContext())
+ // Restore the scroll position of the layer. Does not affect layers
+ // without overflow scroll as the layer will not be scrolled.
+ mRenderLayer->scrollToOffset(mScroll.x(), mScroll.y());
+}
+
+#if DUMP_NAV_CACHE
+
+static bool hasEventListener(Node* node, const AtomicString& eventType) {
+ if (!node->isElementNode())
+ return false;
+ Element* element = static_cast<Element*>(node);
+ EventListener* listener = element->getAttributeEventListener(eventType);
+ return 0 != listener;
+}
+
+#define DEBUG_BUFFER_SIZE 256
+#define DEBUG_WRAP_SIZE 150
+#define DEBUG_WRAP_MAX 170
+
+Frame* CacheBuilder::Debug::frameAnd() const {
+ CacheBuilder* nav = (CacheBuilder*) ((char*) this - OFFSETOF(CacheBuilder, mDebug));
+ return CacheBuilder::FrameAnd(nav);
+}
+
+void CacheBuilder::Debug::attr(const AtomicString& name, const AtomicString& value) {
+ if (name.isNull() || name.isEmpty() || value.isNull() || value.isEmpty())
+ return;
+ uChar(name.characters(), name.length(), false);
+ print("=");
+ wideString(value.characters(), value.length(), false);
+ print(" ");
+}
+
+void CacheBuilder::Debug::comma(const char* str) {
+ print(str);
+ print(", ");
+}
+
+void CacheBuilder::Debug::flush() {
+ int len;
+ do {
+ int limit = mIndex;
+ if (limit < DEBUG_WRAP_SIZE)
+ return;
+ if (limit < DEBUG_WRAP_MAX)
+ len = limit;
+ else {
+ limit = DEBUG_WRAP_MAX;
+ len = DEBUG_WRAP_SIZE;
+ while (len < limit) {
+ char test = mBuffer[len];
+ if (test < '/' || (test > '9' && test < 'A') || (test > 'Z' && test < 'a') || test > 'z')
+ break;
+ len++;
+ }
+ while (len > 0 && mBuffer[len - 1] == '\\')
+ len--;
+ while (mBuffer[len] == '"')
+ len++;
+ }
+ const char* prefix = mPrefix;
+ if (prefix[0] == '\"') {
+ // see if we're inside a quote
+ int quoteCount = 0;
+ for (int index = 0; index < len; index++) {
+ if (mBuffer[index] == '\\') {
+ index++;
+ continue;
+ }
+ if (mBuffer[index] == '\n') {
+ quoteCount = 0;
+ continue;
+ }
+ if (mBuffer[index] == '"')
+ quoteCount++;
+ }
+ if ((quoteCount & 1) == 0)
+ prefix = "\n";
+ }
+ DUMP_NAV_LOGD("%.*s", len, mBuffer);
+ int copy = mIndex - len;
+ strcpy(mBuffer, prefix);
+ memcpy(&mBuffer[strlen(prefix)], &mBuffer[len], copy);
+ mIndex = strlen(prefix) + copy;
+ } while (true);
+}
+
+void CacheBuilder::Debug::frameName(char*& namePtr, const char* max) const {
+ if (namePtr >= max)
+ return;
+ Frame* frame = frameAnd();
+ Frame* parent = frame->tree()->parent();
+ if (parent)
+ Builder(parent)->mDebug.frameName(namePtr, max);
+ const AtomicString& name = frame->tree()->name();
+ if (name.length() == 0)
+ return;
+ unsigned index = 0;
+ if (name.startsWith(AtomicString("opener")))
+ index = 6;
+ for (; index < name.length(); index++) {
+ char ch = name[index];
+ if (ch <= ' ')
+ ch = '_';
+ if (WTF::isASCIIAlphanumeric(ch) || ch == '_')
+ *namePtr++ = ch;
+ }
+}
+
+void CacheBuilder::Debug::frames() {
+ Frame* frame = frameAnd();
+ Document* doc = frame->document();
+ if (doc == NULL)
+ return;
+ bool top = frame->tree()->parent() == NULL;
+ if (top) {
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ gWriteLogMutex.lock();
+ ASSERT(gNavCacheLogFile == NULL);
+ gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a");
+#endif
+ groups();
+ }
+ Frame* child = frame->tree()->firstChild();
+ bool hasChild = child != NULL;
+ if (top && hasChild)
+ DUMP_NAV_LOGD("\nnamespace TEST_SPACE {\n\n");
+ while (child) {
+ Builder(child)->mDebug.frames();
+ child = child->tree()->nextSibling();
+ }
+ if (hasChild) {
+ child = frame->tree()->firstChild();
+ while (child) {
+ char childName[256];
+ char* childNamePtr = childName;
+ Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1);
+ *childNamePtr = '\0';
+ if (child == frame->tree()->firstChild())
+ DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP[] = {\n", childName);
+ Frame* next = child->tree()->nextSibling();
+ Document* doc = child->document();
+ if (doc != NULL) {
+ RenderObject* renderer = doc->renderer();
+ if (renderer != NULL) {
+ RenderLayer* layer = renderer->enclosingLayer();
+ if (layer != NULL) {
+ DUMP_NAV_LOGD("{ ");
+ DUMP_NAV_LOGD("TEST%s_RECTS, ", childName);
+ DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", childName);
+ DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", childName);
+ DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", childName);
+ DUMP_NAV_LOGD("TEST%s_WIDTH, ", childName);
+ DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", childName);
+ DUMP_NAV_LOGD("0, 0, 0, 0,\n");
+ DUMP_NAV_LOGD("TEST%s_FOCUS, ", childName);
+ Frame* grandChild = child->tree()->firstChild();
+ if (grandChild) {
+ char grandChildName[256];
+ char* grandChildNamePtr = grandChildName;
+ Builder(grandChild)->mDebug.frameName(grandChildNamePtr,
+ grandChildNamePtr + sizeof(grandChildName) - 1);
+ *grandChildNamePtr = '\0';
+ DUMP_NAV_LOGD("TEST%s_GROUP, ", grandChildName);
+ DUMP_NAV_LOGD("sizeof(TEST%s_GROUP) / sizeof(DebugTestFrameGroup), ", grandChildName);
+ } else
+ DUMP_NAV_LOGD("NULL, 0, ");
+ DUMP_NAV_LOGD("\"%s\"\n", childName);
+ DUMP_NAV_LOGD("}%c\n", next ? ',' : ' ');
+ }
+ }
+ }
+ child = next;
+ }
+ DUMP_NAV_LOGD("};\n");
+ }
+ if (top) {
+ if (hasChild)
+ DUMP_NAV_LOGD("\n} // end of namespace\n\n");
+ char name[256];
+ char* frameNamePtr = name;
+ frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1);
+ *frameNamePtr = '\0';
+ DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP = {\n", name);
+ DUMP_NAV_LOGD("TEST%s_RECTS, ", name);
+ DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", name);
+ DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", name);
+ DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", name);
+ DUMP_NAV_LOGD("TEST%s_WIDTH, ", name);
+ DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", name);
+ DUMP_NAV_LOGD("TEST%s_MAX_H, ", name);
+ DUMP_NAV_LOGD("TEST%s_MIN_H, ", name);
+ DUMP_NAV_LOGD("TEST%s_MAX_V, ", name);
+ DUMP_NAV_LOGD("TEST%s_MIN_V,\n", name);
+ DUMP_NAV_LOGD("TEST%s_FOCUS, ", name);
+ if (hasChild) {
+ child = frame->tree()->firstChild();
+ char childName[256];
+ char* childNamePtr = childName;
+ Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1);
+ *childNamePtr = '\0';
+ DUMP_NAV_LOGD("TEST_SPACE::TEST%s_GROUP, ", childName);
+ DUMP_NAV_LOGD("sizeof(TEST_SPACE::TEST%s_GROUP) / sizeof(DebugTestFrameGroup), \n" ,childName);
+ } else
+ DUMP_NAV_LOGD("NULL, 0, ");
+ DUMP_NAV_LOGD("\"%s\"\n", name);
+ DUMP_NAV_LOGD("};\n");
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ if (gNavCacheLogFile)
+ fclose(gNavCacheLogFile);
+ gNavCacheLogFile = NULL;
+ gWriteLogMutex.unlock();
+#endif
+ }
+}
+
+void CacheBuilder::Debug::init(char* buffer, size_t size) {
+ mBuffer = buffer;
+ mBufferSize = size;
+ mIndex = 0;
+ mPrefix = "";
+}
+
+void CacheBuilder::Debug::groups() {
+ Frame* frame = frameAnd();
+ Frame* child = frame->tree()->firstChild();
+ bool hasChild = child != NULL;
+ if (frame->tree()->parent() == NULL && hasChild)
+ DUMP_NAV_LOGD("namespace TEST_SPACE {\n\n");
+ while (child) {
+ Builder(child)->mDebug.groups();
+ child = child->tree()->nextSibling();
+ }
+ if (frame->tree()->parent() == NULL && hasChild)
+ DUMP_NAV_LOGD("\n} // end of namespace\n\n");
+ Document* doc = frame->document();
+ char name[256];
+ char* frameNamePtr = name;
+ frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1);
+ *frameNamePtr = '\0';
+ if (doc == NULL) {
+ DUMP_NAV_LOGD("// %s has no document\n", name);
+ return;
+ }
+ RenderObject* renderer = doc->renderer();
+ if (renderer == NULL) {
+ DUMP_NAV_LOGD("// %s has no renderer\n", name);
+ return;
+ }
+ RenderLayer* layer = renderer->enclosingLayer();
+ if (layer == NULL) {
+ DUMP_NAV_LOGD("// %s has no enclosingLayer\n", name);
+ return;
+ }
+ Node* node = doc;
+ Node* focus = doc->focusedNode();
+ bool atLeastOne = false;
+ do {
+ if ((atLeastOne |= isFocusable(node)) != false)
+ break;
+ } while ((node = node->traverseNextNode()) != NULL);
+ int focusIndex = -1;
+ if (atLeastOne == false) {
+ DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n"
+ "{{0, 0, 0, 0}, \"\", 0, -1, \"\", {0, 0, 0, 0}, false, 0}\n"
+ "};\n\n", name);
+ DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = 1;"
+ " // no focusable nodes\n", name);
+ DUMP_NAV_LOGD("#define TEST%s_RECTPARTS NULL\n", name);
+ } else {
+ node = doc;
+ int count = 1;
+ DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n", name);
+ do {
+ String properties;
+ if (hasEventListener(node, eventNames().clickEvent))
+ properties.append("ONCLICK | ");
+ if (hasEventListener(node, eventNames().mousedownEvent))
+ properties.append("MOUSEDOWN | ");
+ if (hasEventListener(node, eventNames().mouseupEvent))
+ properties.append("MOUSEUP | ");
+ if (hasEventListener(node, eventNames().mouseoverEvent))
+ properties.append("MOUSEOVER | ");
+ if (hasEventListener(node, eventNames().mouseoutEvent))
+ properties.append("MOUSEOUT | ");
+ if (hasEventListener(node, eventNames().keydownEvent))
+ properties.append("KEYDOWN | ");
+ if (hasEventListener(node, eventNames().keyupEvent))
+ properties.append("KEYUP | ");
+ if (CacheBuilder::HasFrame(node))
+ properties.append("FRAME | ");
+ if (focus == node) {
+ properties.append("FOCUS | ");
+ focusIndex = count;
+ }
+ if (node->isKeyboardFocusable(NULL))
+ properties.append("KEYBOARD_FOCUSABLE | ");
+ if (node->isMouseFocusable())
+ properties.append("MOUSE_FOCUSABLE | ");
+ if (node->isFocusable())
+ properties.append("SIMPLE_FOCUSABLE | ");
+ if (properties.isEmpty())
+ properties.append("0");
+ else
+ properties.truncate(properties.length() - 3);
+ IntRect rect = node->getRect();
+ if (node->hasTagName(HTMLNames::areaTag))
+ rect = getAreaRect(static_cast<HTMLAreaElement*>(node));
+ char buffer[DEBUG_BUFFER_SIZE];
+ memset(buffer, 0, sizeof(buffer));
+ mBuffer = buffer;
+ mBufferSize = sizeof(buffer);
+ mPrefix = "\"\n\"";
+ mIndex = snprintf(buffer, sizeof(buffer), "{{%d, %d, %d, %d}, ", rect.x(), rect.y(),
+ rect.width(), rect.height());
+ localName(node);
+ uChar(properties.characters(), properties.length(), false);
+ print(", ");
+ int parentIndex = ParentIndex(node, count, node->parentNode());
+ char scratch[256];
+ snprintf(scratch, sizeof(scratch), "%d", parentIndex);
+ comma(scratch);
+ Element* element = static_cast<Element*>(node);
+ if (node->isElementNode() && element->hasID())
+ wideString(element->getIdAttribute());
+ else if (node->isTextNode()) {
+ #if 01 // set to one to abbreviate text that can be omitted from the address detection code
+ if (rect.isEmpty() && node->textContent().length() > 100) {
+ wideString(node->textContent().characters(), 100, false);
+ snprintf(scratch, sizeof(scratch), "/* + %d bytes */",
+ node->textContent().length() - 100);
+ print(scratch);
+ } else
+ #endif
+ wideString(node->textContent().characters(), node->textContent().length(), true);
+ } else if (node->hasTagName(HTMLNames::aTag) ||
+ node->hasTagName(HTMLNames::areaTag))
+ {
+ HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(node);
+ wideString(anchor->href());
+ } else if (node->hasTagName(HTMLNames::imgTag)) {
+ HTMLImageElement* image = static_cast<HTMLImageElement*>(node);
+ wideString(image->src());
+ } else
+ print("\"\"");
+ RenderObject* renderer = node->renderer();
+ int tabindex = node->isElementNode() ? node->tabIndex() : 0;
+ RenderLayer* layer = 0;
+ if (renderer) {
+ const IntRect& absB = renderer->absoluteBoundingBoxRect();
+ bool hasLayer = renderer->hasLayer();
+ layer = hasLayer ? toRenderBoxModelObject(renderer)->layer() : 0;
+ snprintf(scratch, sizeof(scratch), ", {%d, %d, %d, %d}, %s"
+ ", %d, %s, %s},",
+ absB.x(), absB.y(), absB.width(), absB.height(),
+ renderer->hasOverflowClip() ? "true" : "false", tabindex,
+ hasLayer ? "true" : "false",
+ hasLayer && layer->isComposited() ? "true" : "false");
+ // TODO: add renderer->style()->visibility()
+ print(scratch);
+ } else
+ print(", {0, 0, 0, 0}, false, 0},");
+
+ flush();
+ snprintf(scratch, sizeof(scratch), "// %d: ", count);
+ mPrefix = "\n// ";
+ print(scratch);
+ //print(renderer ? renderer->information().ascii() : "NO_RENDER_INFO");
+ if (node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ NamedNodeMap* attrs = element->attributes();
+ unsigned length = attrs->length();
+ if (length > 0) {
+ newLine();
+ print("// attr: ");
+ for (unsigned i = 0; i < length; i++) {
+ Attribute* a = attrs->attributeItem(i);
+ attr(a->localName(), a->value());
+ }
+ }
+ }
+ if (renderer) {
+ RenderStyle* style = renderer->style();
+ snprintf(scratch, sizeof(scratch), "// renderStyle:"
+ " visibility=%s hasBackGround=%d"
+ " tapHighlightColor().alpha()=0x%02x"
+ " isTransparent()=%s",
+ style->visibility() == HIDDEN ? "HIDDEN" : "VISIBLE",
+ renderer->hasBackground(), style->tapHighlightColor().alpha(),
+ renderer->isTransparent() ? "true" : "false");
+ newLine();
+ print(scratch);
+ RenderBlock* renderBlock = static_cast<RenderBlock*>(renderer);
+ if (renderer->isRenderBlock() && renderBlock->hasColumns()) {
+ const RenderBox* box = static_cast<RenderBox*>(renderer);
+ const IntRect& oRect = box->visibleOverflowRect();
+ snprintf(scratch, sizeof(scratch), "// renderBlock:"
+ " columnCount=%d columnGap=%d direction=%d"
+ " hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)",
+ renderBlock->columnInfo()->columnCount(), renderBlock->columnGap(),
+ renderBlock->style()->direction(), renderer->hasOverflowClip(),
+ oRect.x(), oRect.y(), oRect.width(), oRect.height());
+ newLine();
+ print(scratch);
+ }
+ }
+ #if USE(ACCELERATED_COMPOSITING)
+ if (renderer && renderer->hasLayer()) {
+ RenderLayer* layer = toRenderBoxModelObject(renderer)->layer();
+ RenderLayerBacking* back = layer->backing();
+ GraphicsLayer* grLayer = back ? back->graphicsLayer() : 0;
+ LayerAndroid* aLayer = grLayer ? grLayer->platformLayer() : 0;
+ const SkPicture* pict = aLayer ? aLayer->picture() : 0;
+ const IntRect& r = renderer->absoluteBoundingBoxRect();
+ snprintf(scratch, sizeof(scratch), "// layer:%p back:%p"
+ " gLayer:%p aLayer:%p pict:%p r:(%d,%d,w=%d,h=%d)",
+ layer, back, grLayer, aLayer, pict, r.x(), r.y(),
+ r.width(), r.height());
+ newLine();
+ print(scratch);
+ }
+ #endif
+ count++;
+ newLine();
+ } while ((node = node->traverseNextNode()) != NULL);
+ DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1);
+ DUMP_NAV_LOGD("\n");
+ DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = %d;\n\n", name, count - 1);
+ // look for rects with multiple parts
+ node = doc;
+ count = 1;
+ bool hasRectParts = false;
+ int globalOffsetX, globalOffsetY;
+ GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY);
+ do {
+ IntRect rect;
+ bool _isFocusable = isFocusable(node) || (node->isTextNode()
+ && node->getRect().isEmpty() == false
+ );
+ int nodeIndex = count++;
+ if (_isFocusable == false)
+ continue;
+ RenderObject* renderer = node->renderer();
+ if (renderer == NULL)
+ continue;
+ WTF::Vector<IntRect> rects;
+ IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX);
+ IntRect focusBounds = IntRect(0, 0, INT_MAX, INT_MAX);
+ IntRect* rectPtr = &focusBounds;
+ int imageCount = 0;
+ if (node->isTextNode()) {
+ Text* textNode = (Text*) node;
+ if (CacheBuilder::ConstructTextRects(textNode, 0, textNode,
+ INT_MAX, globalOffsetX, globalOffsetY, rectPtr,
+ clipBounds, &rects) == false)
+ continue;
+ } else {
+ IntRect nodeBounds = node->getRect();
+ if (CacheBuilder::ConstructPartRects(node, nodeBounds, rectPtr,
+ globalOffsetX, globalOffsetY, &rects, &imageCount) == false)
+ continue;
+ }
+ unsigned arraySize = rects.size();
+ if (arraySize > 1 || (arraySize == 1 && (rectPtr->width() != rect.width())) ||
+ rectPtr->height() != rect.height()) {
+ if (hasRectParts == false) {
+ DUMP_NAV_LOGD("static DebugTestRectPart TEST%s_RECTPARTS[] = {\n", name);
+ hasRectParts = true;
+ }
+ if (node->isTextNode() == false) {
+ unsigned rectIndex = 0;
+ for (; rectIndex < arraySize; rectIndex++) {
+ rectPtr = &rects.at(rectIndex);
+ DUMP_NAV_LOGD("{ %d, %d, %d, %d, %d }, // %d\n", nodeIndex,
+ rectPtr->x(), rectPtr->y(), rectPtr->width(),
+ rectPtr->height(), rectIndex + 1);
+ }
+ } else {
+ RenderText* renderText = (RenderText*) node->renderer();
+ InlineTextBox* textBox = renderText->firstTextBox();
+ unsigned rectIndex = 0;
+ while (textBox) {
+ FloatPoint pt = renderText->localToAbsolute();
+ IntRect rect = textBox->selectionRect((int) pt.x(), (int) pt.y(), 0, INT_MAX);
+ mIndex = 0;
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "{ %d, %d, %d, %d, %d",
+ nodeIndex, rect.x(), rect.y(), rect.width(), rect.height());
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d",
+ textBox->len(), 0 /*textBox->selectionHeight()*/,
+ 0 /*textBox->selectionTop()*/);
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d",
+ 0 /*textBox->spaceAdd()*/, textBox->start(), 0 /*textBox->textPos()*/);
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d",
+ textBox->x(), textBox->y(), textBox->logicalWidth(), textBox->logicalHeight());
+ int baseline = textBox->renderer()->style(textBox->isFirstLineStyle())->font().ascent();
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d }, // %d ",
+ baseline, imageCount, ++rectIndex);
+ wideString(node->textContent().characters() + textBox->start(), textBox->len(), true);
+ DUMP_NAV_LOGD("%.*s\n", mIndex, mBuffer);
+ textBox = textBox->nextTextBox();
+ }
+ }
+ }
+ } while ((node = node->traverseNextNode()) != NULL);
+ if (hasRectParts)
+ DUMP_NAV_LOGD("{0}\n};\n\n");
+ else
+ DUMP_NAV_LOGD("static DebugTestRectPart* TEST%s_RECTPARTS = NULL;\n", name);
+ }
+ int contentsWidth = layer->width();
+ int contentsHeight = layer->height();
+ DUMP_NAV_LOGD("static int TEST%s_FOCUS = %d;\n", name, focusIndex);
+ DUMP_NAV_LOGD("static int TEST%s_WIDTH = %d;\n", name, contentsWidth);
+ DUMP_NAV_LOGD("static int TEST%s_HEIGHT = %d;\n\n", name, contentsHeight);
+}
+
+bool CacheBuilder::Debug::isFocusable(Node* node) {
+ if (node->hasTagName(HTMLNames::areaTag))
+ return true;
+ if (node->renderer() == false)
+ return false;
+ if (node->isKeyboardFocusable(NULL))
+ return true;
+ if (node->isMouseFocusable())
+ return true;
+ if (node->isFocusable())
+ return true;
+ if (CacheBuilder::AnyIsClick(node))
+ return false;
+ if (CacheBuilder::HasTriggerEvent(node))
+ return true;
+ return false;
+}
+
+void CacheBuilder::Debug::localName(Node* node) {
+ const AtomicString& local = node->localName();
+ if (node->isTextNode())
+ print("\"#text\"");
+ else
+ wideString(local.characters(), local.length(), false);
+ print(", ");
+}
+
+void CacheBuilder::Debug::newLine(int indent) {
+ if (mPrefix[0] != '\n')
+ print(&mPrefix[0], 1);
+ flush();
+ int lastnewline = mIndex - 1;
+ while (lastnewline >= 0 && mBuffer[lastnewline] != '\n')
+ lastnewline--;
+ lastnewline++;
+ char* buffer = mBuffer;
+ if (lastnewline > 0) {
+ DUMP_NAV_LOGD("%.*s", lastnewline, buffer);
+ mIndex -= lastnewline;
+ buffer += lastnewline;
+ }
+ size_t prefixLen = strlen(mPrefix);
+ int minPrefix = prefixLen - 1;
+ while (minPrefix >= 0 && mPrefix[minPrefix] != '\n')
+ minPrefix--;
+ minPrefix = prefixLen - minPrefix - 1;
+ if (mIndex > minPrefix)
+ DUMP_NAV_LOGD("%.*s\n", mIndex, buffer);
+ mIndex = 0;
+ setIndent(indent);
+}
+
+int CacheBuilder::Debug::ParentIndex(Node* node, int count, Node* parent)
+{
+ if (parent == NULL)
+ return -1;
+ ASSERT(node != parent);
+ int result = count;
+ Node* previous = node;
+ do {
+ result--;
+ previous = previous->traversePreviousNode();
+ } while (previous && previous != parent);
+ if (previous != NULL)
+ return result;
+ result = count;
+ do {
+ result++;
+ } while ((node = node->traverseNextNode()) != NULL && node != parent);
+ if (node != NULL)
+ return result;
+ ASSERT(0);
+ return -1;
+}
+
+void CacheBuilder::Debug::print(const char* name) {
+ print(name, strlen(name));
+}
+
+void CacheBuilder::Debug::print(const char* name, unsigned len) {
+ do {
+ if (mIndex + len >= DEBUG_BUFFER_SIZE)
+ flush();
+ int copyLen = mIndex + len < DEBUG_BUFFER_SIZE ?
+ len : DEBUG_BUFFER_SIZE - mIndex;
+ memcpy(&mBuffer[mIndex], name, copyLen);
+ mIndex += copyLen;
+ name += copyLen;
+ len -= copyLen;
+ } while (len > 0);
+ mBuffer[mIndex] = '\0';
+}
+
+void CacheBuilder::Debug::setIndent(int indent)
+{
+ char scratch[64];
+ snprintf(scratch, sizeof(scratch), "%.*s", indent,
+ " ");
+ print(scratch);
+}
+
+void CacheBuilder::Debug::uChar(const UChar* name, unsigned len, bool hex) {
+ const UChar* end = name + len;
+ bool wroteHex = false;
+ while (name < end) {
+ unsigned ch = *name++;
+ if (ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0)
+ ch = ' ';
+ if (ch < ' ' || ch == 0x7f) {
+ if (hex) {
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "\\x%02x", ch);
+ wroteHex = true;
+ } else
+ mBuffer[mIndex++] = '?';
+ } else if (ch >= 0x80) {
+ if (hex) {
+ if (ch < 0x800)
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex,
+ "\\x%02x\\x%02x", ch >> 6 | 0xc0, (ch & 0x3f) | 0x80);
+ else
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex,
+ "\\x%02x\\x%02x\\x%02x", ch >> 12 | 0xe0,
+ (ch >> 6 & 0x3f) | 0x80, (ch & 0x3f) | 0x80);
+ wroteHex = true;
+ } else
+ mBuffer[mIndex++] = '?';
+ } else {
+ if (wroteHex && WTF::isASCIIHexDigit((UChar) ch))
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex,
+ "\" \"");
+ else if (ch == '"' || ch == '\\')
+ mBuffer[mIndex++] = '\\';
+ mBuffer[mIndex++] = ch;
+ wroteHex = false;
+ }
+ if (mIndex + 1 >= DEBUG_BUFFER_SIZE)
+ flush();
+ }
+ flush();
+}
+
+void CacheBuilder::Debug::validateFrame() {
+ Frame* frame = frameAnd();
+ Page* page = frame->page();
+ ASSERT(page);
+ ASSERT((int) page > 0x10000);
+ Frame* child = frame->tree()->firstChild();
+ while (child) {
+ Builder(child)->mDebug.validateFrame();
+ child = child->tree()->nextSibling();
+ }
+}
+
+void CacheBuilder::Debug::wideString(const UChar* chars, int length, bool hex) {
+ if (length == 0)
+ print("\"\"");
+ else {
+ print("\"");
+ uChar(chars, length, hex);
+ print("\"");
+ }
+}
+
+void CacheBuilder::Debug::wideString(const String& str) {
+ wideString(str.characters(), str.length(), false);
+}
+
+#endif // DUMP_NAV_CACHE
+
+CacheBuilder::CacheBuilder()
+{
+ mAllowableTypes = ALL_CACHEDNODE_BITS;
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ gNavCacheLogFile = NULL;
+#endif
+}
+
+void CacheBuilder::adjustForColumns(const ClipColumnTracker& track,
+ CachedNode* node, IntRect* bounds, RenderBlock* renderer)
+{
+ if (!renderer->hasColumns())
+ return;
+ int x = 0;
+ int y = 0;
+ int tx = track.mBounds.x();
+ int ty = track.mBounds.y();
+ int columnGap = track.mColumnGap;
+ size_t limit = track.mColumnInfo->columnCount();
+ for (size_t index = 0; index < limit; index++) {
+ IntRect column = renderer->columnRectAt(track.mColumnInfo, index);
+ column.move(tx, ty);
+ IntRect test = *bounds;
+ test.move(x, y);
+ if (column.contains(test)) {
+ if ((x | y) == 0)
+ return;
+ *bounds = test;
+ node->move(x, y);
+ return;
+ }
+ int xOffset = column.width() + columnGap;
+ x += track.mDirection == LTR ? xOffset : -xOffset;
+ y -= column.height();
+ }
+}
+
+// Checks if a node has one of event listener types.
+bool CacheBuilder::NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length) {
+ for (int i = 0; i < length; ++i) {
+ if (!node->getEventListeners(eventTypes[i]).isEmpty())
+ return true;
+ }
+ return false;
+}
+
+bool CacheBuilder::AnyChildIsClick(Node* node)
+{
+ AtomicString eventTypes[5] = {
+ eventNames().clickEvent,
+ eventNames().mousedownEvent,
+ eventNames().mouseupEvent,
+ eventNames().keydownEvent,
+ eventNames().keyupEvent
+ };
+
+ Node* child = node->firstChild();
+ while (child != NULL) {
+ if (child->isFocusable() ||
+ NodeHasEventListeners(child, eventTypes, 5))
+ return true;
+ if (AnyChildIsClick(child))
+ return true;
+ child = child->nextSibling();
+ }
+ return false;
+}
+
+bool CacheBuilder::AnyIsClick(Node* node)
+{
+ if (node->hasTagName(HTMLNames::bodyTag))
+ return AnyChildIsClick(node);
+
+ AtomicString eventTypeSetOne[4] = {
+ eventNames().mouseoverEvent,
+ eventNames().mouseoutEvent,
+ eventNames().keydownEvent,
+ eventNames().keyupEvent
+ };
+
+ if (!NodeHasEventListeners(node, eventTypeSetOne, 4))
+ return false;
+
+ AtomicString eventTypeSetTwo[3] = {
+ eventNames().clickEvent,
+ eventNames().mousedownEvent,
+ eventNames().mouseupEvent
+ };
+
+ if (NodeHasEventListeners(node, eventTypeSetTwo, 3))
+ return false;
+
+ return AnyChildIsClick(node);
+}
+
+void CacheBuilder::buildCache(CachedRoot* root)
+{
+ Frame* frame = FrameAnd(this);
+ mPictureSetDisabled = false;
+ BuildFrame(frame, frame, root, (CachedFrame*) root);
+ root->finishInit(); // set up frame parent pointers, child pointers
+ setData((CachedFrame*) root);
+}
+
+static Node* ParentWithChildren(Node* node)
+{
+ Node* parent = node;
+ while ((parent = parent->parentNode())) {
+ if (parent->childNodeCount() > 1)
+ return parent;
+ }
+ return 0;
+}
+
+// FIXME
+// Probably this should check for null instead of the caller. If the
+// Tracker object is the last thing in the dom, checking for null in the
+// caller in some cases fails to set up Tracker state which may be useful
+// to the nodes parsed immediately after the tracked noe.
+static Node* OneAfter(Node* node)
+{
+ Node* parent = node;
+ Node* sibling = NULL;
+ while ((parent = parent->parentNode()) != NULL) {
+ sibling = parent->nextSibling();
+ if (sibling != NULL)
+ break;
+ }
+ return sibling;
+}
+
+// return true if this renderer is really a pluinview, and it wants
+// key-events (i.e. focus)
+static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) {
+ if (renderer->isWidget()) {
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (widget && (widget->isPluginView() || widget->isPluginViewBase())) {
+ // check if this plugin really wants key events (TODO)
+ return true;
+ }
+ }
+ return false;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, int id)
+{
+ DBG_NAV_LOGD("frame=%p index=%d loc=(%d,%d) id=%d", frame, index,
+ location.x(), location.y(), id);
+ CachedLayer cachedLayer;
+ cachedLayer.setCachedNodeIndex(index);
+ cachedLayer.setOffset(location);
+ cachedLayer.setUniqueId(id);
+ frame->add(cachedLayer);
+}
+#endif
+
+static int FindColorIndex(WTF::Vector<CachedColor>& colorTracker,
+ const CachedColor& cachedColor)
+{
+ CachedColor* work = colorTracker.begin() - 1;
+ CachedColor* end = colorTracker.end();
+ while (++work < end) {
+ if (*work == cachedColor)
+ return work - colorTracker.begin();
+ }
+ int result = colorTracker.size();
+ colorTracker.grow(result + 1);
+ CachedColor& newColor = colorTracker.last();
+ newColor = cachedColor;
+ return result;
+}
+
+static void InitColor(CachedColor* color)
+{
+ color->setFillColor(RenderStyle::initialRingFillColor());
+ color->setInnerWidth(RenderStyle::initialRingInnerWidth());
+ color->setOuterWidth(RenderStyle::initialRingOuterWidth());
+ color->setOutset(RenderStyle::initialRingOutset());
+ color->setPressedInnerColor(RenderStyle::initialRingPressedInnerColor());
+ color->setPressedOuterColor(RenderStyle::initialRingPressedOuterColor());
+ color->setRadius(RenderStyle::initialRingRadius());
+ color->setSelectedInnerColor(RenderStyle::initialRingSelectedInnerColor());
+ color->setSelectedOuterColor(RenderStyle::initialRingSelectedOuterColor());
+}
+
+// when new focus is found, push it's parent on a stack
+ // as long as more focii are found with the same (grand) parent, note it
+ // (which only requires retrieving the last parent on the stack)
+// when the parent's last child is found, pop the stack
+// different from Tracker in that Tracker only pushes focii with children
+
+// making this work with focus - child focus - grandchild focus is tricky
+// if I keep the generation number, I may be able to more quickly determine that
+// a node is a grandchild of the focus's parent
+// this additionally requires being able to find the grandchild's parent
+
+// keep nodes that are focusable
+void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
+ CachedRoot* cachedRoot, CachedFrame* cachedFrame)
+{
+ WTF::Vector<FocusTracker> tracker(1); // sentinel
+ {
+ FocusTracker* baseTracker = tracker.data();
+ bzero(baseTracker, sizeof(FocusTracker));
+ baseTracker->mCachedNodeIndex = -1;
+ }
+ WTF::Vector<LayerTracker> layerTracker(1); // sentinel
+ bzero(layerTracker.data(), sizeof(LayerTracker));
+ WTF::Vector<ClipColumnTracker> clipTracker(1); // sentinel
+ bzero(clipTracker.data(), sizeof(ClipColumnTracker));
+ WTF::Vector<TabIndexTracker> tabIndexTracker(1); // sentinel
+ bzero(tabIndexTracker.data(), sizeof(TabIndexTracker));
+ WTF::Vector<CachedColor> colorTracker(1);
+ InitColor(colorTracker.data());
+#if DUMP_NAV_CACHE
+ char* frameNamePtr = cachedFrame->mDebug.mFrameName;
+ Builder(frame)->mDebug.frameName(frameNamePtr, frameNamePtr +
+ sizeof(cachedFrame->mDebug.mFrameName) - 1);
+ *frameNamePtr = '\0';
+ int nodeIndex = 1;
+#endif
+ NodeWalk walk;
+ Document* doc = frame->document();
+ Node* parent = doc;
+ CachedNode cachedParentNode;
+ cachedParentNode.init(parent);
+#if DUMP_NAV_CACHE
+ cachedParentNode.mDebug.mNodeIndex = nodeIndex;
+#endif
+ cachedFrame->add(colorTracker[0]);
+ cachedFrame->add(cachedParentNode);
+ Node* node = parent;
+ int cacheIndex = 1;
+ int colorIndex = 0; // assume no special css ring colors
+ const void* lastStyleDataPtr = 0;
+ int textInputIndex = 0;
+ Node* focused = doc->focusedNode();
+ if (focused)
+ cachedRoot->setFocusBounds(focused->getRect());
+ int globalOffsetX, globalOffsetY;
+ GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY);
+#if USE(ACCELERATED_COMPOSITING)
+ // The frame itself might be composited so we need to track the layer. Do
+ // not track the base frame's layer as the main content is draw as part of
+ // BaseLayerAndroid's picture.
+ if (frame != root && frame->contentRenderer()
+ && frame->contentRenderer()->usesCompositing() && node->lastChild())
+ TrackLayer(layerTracker, frame->contentRenderer(), node->lastChild(),
+ globalOffsetX, globalOffsetY);
+#endif
+ while (walk.mMore || (node = node->traverseNextNode()) != NULL) {
+#if DUMP_NAV_CACHE
+ nodeIndex++;
+#endif
+ FocusTracker* last = &tracker.last();
+ int lastChildIndex = cachedFrame->size() - 1;
+ while (node == last->mLastChild) {
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
+ cacheIndex--;
+ tracker.removeLast();
+ lastChildIndex = last->mCachedNodeIndex;
+ last = &tracker.last();
+ }
+ do {
+ const ClipColumnTracker* lastClip = &clipTracker.last();
+ if (node != lastClip->mLastChild)
+ break;
+ clipTracker.removeLast();
+ } while (true);
+ do {
+ const LayerTracker* lastLayer = &layerTracker.last();
+ if (node != lastLayer->mLastChild)
+ break;
+ layerTracker.removeLast();
+ } while (true);
+ do {
+ const TabIndexTracker* lastTabIndex = &tabIndexTracker.last();
+ if (node != lastTabIndex->mLastChild)
+ break;
+ tabIndexTracker.removeLast();
+ } while (true);
+ Frame* child = HasFrame(node);
+ CachedNode cachedNode;
+ if (child != NULL) {
+ if (child->document() == NULL)
+ continue;
+ RenderObject* nodeRenderer = node->renderer();
+ if (nodeRenderer != NULL && nodeRenderer->style()->visibility() == HIDDEN)
+ continue;
+ CachedFrame cachedChild;
+ cachedChild.init(cachedRoot, cacheIndex, child);
+ int childFrameIndex = cachedFrame->childCount();
+ cachedFrame->addFrame(cachedChild);
+ cachedNode.init(node);
+ cachedNode.setIndex(cacheIndex++);
+ cachedNode.setDataIndex(childFrameIndex);
+ cachedNode.setType(FRAME_CACHEDNODETYPE);
+#if DUMP_NAV_CACHE
+ cachedNode.mDebug.mNodeIndex = nodeIndex;
+ cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex(
+ node, nodeIndex, NULL);
+#endif
+ cachedFrame->add(cachedNode);
+ CachedFrame* childPtr = cachedFrame->lastChild();
+ BuildFrame(root, child, cachedRoot, childPtr);
+ continue;
+ }
+ int tabIndex = node->tabIndex();
+ Node* lastChild = node->lastChild();
+ if (tabIndex <= 0)
+ tabIndex = tabIndexTracker.last().mTabIndex;
+ else if (tabIndex > 0 && lastChild) {
+ DBG_NAV_LOGD("tabIndex=%d node=%p", tabIndex, node);
+ tabIndexTracker.grow(tabIndexTracker.size() + 1);
+ TabIndexTracker& indexTracker = tabIndexTracker.last();
+ indexTracker.mTabIndex = tabIndex;
+ indexTracker.mLastChild = OneAfter(lastChild);
+ }
+ RenderObject* nodeRenderer = node->renderer();
+ bool isTransparent = false;
+ bool hasCursorRing = true;
+ if (nodeRenderer != NULL) {
+ RenderStyle* style = nodeRenderer->style();
+ if (style->visibility() == HIDDEN)
+ continue;
+ isTransparent = nodeRenderer->hasBackground() == false;
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ hasCursorRing = style->tapHighlightColor().alpha() > 0;
+#endif
+#if USE(ACCELERATED_COMPOSITING)
+ // If this renderer has its own layer and the layer is composited,
+ // start tracking it.
+ if (lastChild && nodeRenderer->hasLayer() && toRenderBoxModelObject(nodeRenderer)->layer()->backing())
+ TrackLayer(layerTracker, nodeRenderer, lastChild, globalOffsetX, globalOffsetY);
+#endif
+ }
+ bool more = walk.mMore;
+ walk.reset();
+ // GetGlobalBounds(node, &bounds, false);
+ bool computeCursorRings = false;
+ bool hasClip = false;
+ bool hasMouseOver = false;
+ bool isUnclipped = false;
+ bool isFocus = node == focused;
+ bool takesFocus = false;
+ int columnGap = 0;
+ int imageCount = 0;
+ TextDirection direction = LTR;
+ String exported;
+ CachedNodeType type = NORMAL_CACHEDNODETYPE;
+ CachedColor cachedColor;
+ CachedInput cachedInput;
+ IntRect bounds;
+ IntRect absBounds;
+ IntRect originalAbsBounds;
+ ColumnInfo* columnInfo = NULL;
+ if (node->hasTagName(HTMLNames::areaTag)) {
+ type = AREA_CACHEDNODETYPE;
+ HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
+ bounds = getAreaRect(area);
+ originalAbsBounds = bounds;
+ bounds.move(globalOffsetX, globalOffsetY);
+ absBounds = bounds;
+ isUnclipped = true; // FIXME: areamaps require more effort to detect
+ // assume areamaps are always visible for now
+ takesFocus = true;
+ goto keepNode;
+ }
+ if (nodeRenderer == NULL)
+ continue;
+
+ // some common setup
+ absBounds = nodeRenderer->absoluteBoundingBoxRect();
+ originalAbsBounds = absBounds;
+ absBounds.move(globalOffsetX, globalOffsetY);
+ hasClip = nodeRenderer->hasOverflowClip();
+
+ if (checkForPluginViewThatWantsFocus(nodeRenderer)) {
+ bounds = absBounds;
+ isUnclipped = true;
+ takesFocus = true;
+ type = PLUGIN_CACHEDNODETYPE;
+ goto keepNode;
+ }
+ // Only use the root contentEditable element
+ if (node->rendererIsEditable() && !node->parentOrHostNode()->rendererIsEditable()) {
+ bounds = absBounds;
+ takesFocus = true;
+ type = CONTENT_EDITABLE_CACHEDNODETYPE;
+ goto keepNode;
+ }
+ if (nodeRenderer->isRenderBlock()) {
+ RenderBlock* renderBlock = (RenderBlock*) nodeRenderer;
+ if (renderBlock->hasColumns()) {
+ columnInfo = renderBlock->columnInfo();
+ columnGap = renderBlock->columnGap();
+ direction = renderBlock->style()->direction();
+ }
+ }
+ if ((hasClip != false || columnInfo != NULL) && lastChild) {
+ clipTracker.grow(clipTracker.size() + 1);
+ ClipColumnTracker& clip = clipTracker.last();
+ clip.mBounds = absBounds;
+ clip.mLastChild = OneAfter(lastChild);
+ clip.mNode = node;
+ clip.mColumnInfo = columnInfo;
+ clip.mColumnGap = columnGap;
+ clip.mHasClip = hasClip;
+ clip.mDirection = direction;
+ if (columnInfo != NULL) {
+ const IntRect& oRect = ((RenderBox*)nodeRenderer)->visualOverflowRect();
+ clip.mBounds.move(oRect.x(), oRect.y());
+ }
+ }
+ if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODE_BITS) {
+ if (last->mSomeParentTakesFocus) // don't look at text inside focusable node
+ continue;
+ CachedNodeType checkType;
+ if (isFocusableText(&walk, more, node, &checkType,
+ &exported) == false)
+ continue;
+ #if DUMP_NAV_CACHE
+ {
+ char buffer[DEBUG_BUFFER_SIZE];
+ mDebug.init(buffer, sizeof(buffer));
+ mDebug.print("text link found: ");
+ mDebug.wideString(exported);
+ DUMP_NAV_LOGD("%s\n", buffer);
+ }
+ #endif
+ type = checkType;
+ // !!! test ! is the following line correctly needed for frames to work?
+ cachedNode.init(node);
+ const ClipColumnTracker& clipTrack = clipTracker.last();
+ const IntRect& clip = clipTrack.mHasClip ? clipTrack.mBounds :
+ IntRect(0, 0, INT_MAX, INT_MAX);
+ if (ConstructTextRects((WebCore::Text*) node, walk.mStart,
+ (WebCore::Text*) walk.mFinalNode, walk.mEnd, globalOffsetX,
+ globalOffsetY, &bounds, clip, &cachedNode.mCursorRing) == false)
+ continue;
+ absBounds = bounds;
+ cachedNode.setBounds(bounds);
+ if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH)
+ continue;
+ if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT)
+ continue;
+ computeCursorRings = true;
+ isUnclipped = true; // FIXME: to hide or partially occlude synthesized links, each
+ // focus ring will also need the offset and length of characters
+ // used to produce it
+ goto keepTextNode;
+ }
+ if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ if (input->isTextField()) {
+ if (input->readOnly())
+ continue;
+ type = TEXT_INPUT_CACHEDNODETYPE;
+ cachedInput.init();
+ cachedInput.setAutoComplete(input->autoComplete());
+ cachedInput.setSpellcheck(input->spellcheck());
+ cachedInput.setFormPointer(input->form());
+ cachedInput.setIsTextField(true);
+ exported = input->value().threadsafeCopy();
+ cachedInput.setMaxLength(input->maxLength());
+ cachedInput.setTypeFromElement(input);
+ // If this does not need to be threadsafe, we can use crossThreadString().
+ // See http://trac.webkit.org/changeset/49160.
+ cachedInput.setName(input->name().string().threadsafeCopy());
+ // can't detect if this is drawn on top (example: deviant.com login parts)
+ isUnclipped = isTransparent;
+ } else if (input->isInputTypeHidden())
+ continue;
+ else if (input->isRadioButton() || input->isCheckbox())
+ isTransparent = false;
+ } else if (node->hasTagName(HTMLNames::textareaTag)) {
+ HTMLTextAreaElement* area = static_cast<HTMLTextAreaElement*>(node);
+ if (area->readOnly())
+ continue;
+ cachedInput.init();
+ type = TEXT_INPUT_CACHEDNODETYPE;
+ cachedInput.setFormPointer(area->form());
+ cachedInput.setIsTextArea(true);
+ cachedInput.setSpellcheck(area->spellcheck());
+ exported = area->value().threadsafeCopy();
+ } else if (node->hasTagName(HTMLNames::aTag)) {
+ const HTMLAnchorElement* anchorNode =
+ (const HTMLAnchorElement*) node;
+ if (!anchorNode->isFocusable() && !HasTriggerEvent(node))
+ continue;
+ if (node->disabled())
+ continue;
+ hasMouseOver = NodeHasEventListeners(node, &eventNames().mouseoverEvent, 1);
+ type = ANCHOR_CACHEDNODETYPE;
+ KURL href = anchorNode->href();
+ if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string()))
+ // Set the exported string for all non-javascript anchors.
+ exported = href.string().threadsafeCopy();
+ } else if (node->hasTagName(HTMLNames::selectTag)) {
+ type = SELECT_CACHEDNODETYPE;
+ }
+ if (type == TEXT_INPUT_CACHEDNODETYPE) {
+ RenderTextControl* renderText =
+ static_cast<RenderTextControl*>(nodeRenderer);
+ if (isFocus)
+ cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd());
+ // FIXME: Are we sure there will always be a style and font, and it's correct?
+ RenderStyle* style = nodeRenderer->style();
+ if (style) {
+ isUnclipped |= !style->hasAppearance();
+ int lineHeight = -1;
+ Length lineHeightLength = style->lineHeight();
+ // If the lineHeight is negative, WebTextView will calculate it
+ // based on the text size, using the Paint.
+ // See RenderStyle.computedLineHeight.
+ if (lineHeightLength.isPositive())
+ lineHeight = style->computedLineHeight();
+ cachedInput.setLineHeight(lineHeight);
+ cachedInput.setTextSize(style->font().size());
+ cachedInput.setIsRtlText(style->direction() == RTL
+ || 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;
+ if (type != ANCHOR_CACHEDNODETYPE) {
+ bool isFocusable = node->isKeyboardFocusable(NULL) ||
+ node->isMouseFocusable() || node->isFocusable();
+ if (isFocusable == false) {
+ if (node->disabled())
+ continue;
+ bool overOrOut = HasOverOrOut(node);
+ bool hasTrigger = HasTriggerEvent(node);
+ if (overOrOut == false && hasTrigger == false)
+ continue;
+ takesFocus = hasTrigger;
+ }
+ }
+ computeCursorRings = true;
+ keepNode:
+ cachedNode.init(node);
+ if (computeCursorRings == false) {
+ cachedNode.setBounds(bounds);
+ cachedNode.mCursorRing.append(bounds);
+ } else if (ConstructPartRects(node, bounds, &cachedNode.mBounds,
+ globalOffsetX, globalOffsetY, &cachedNode.mCursorRing,
+ &imageCount) == false)
+ continue;
+ keepTextNode:
+ if (nodeRenderer) { // area tags' node->renderer() == 0
+ RenderStyle* style = nodeRenderer->style();
+ const void* styleDataPtr = style->ringData();
+ // to save time, see if we're pointing to the same style data as before
+ if (lastStyleDataPtr != styleDataPtr) {
+ lastStyleDataPtr = styleDataPtr;
+ cachedColor.setFillColor(style->ringFillColor());
+ cachedColor.setInnerWidth(style->ringInnerWidth());
+ cachedColor.setOuterWidth(style->ringOuterWidth());
+ cachedColor.setOutset(style->ringOutset());
+ cachedColor.setPressedInnerColor(style->ringPressedInnerColor());
+ cachedColor.setPressedOuterColor(style->ringPressedOuterColor());
+ cachedColor.setRadius(style->ringRadius());
+ cachedColor.setSelectedInnerColor(style->ringSelectedInnerColor());
+ cachedColor.setSelectedOuterColor(style->ringSelectedOuterColor());
+ int oldSize = colorTracker.size();
+ colorIndex = FindColorIndex(colorTracker, cachedColor);
+ if (colorIndex == oldSize)
+ cachedFrame->add(cachedColor);
+ }
+ } else
+ colorIndex = 0;
+ IntRect clip = hasClip ? bounds : absBounds;
+ size_t clipIndex = clipTracker.size();
+ if (clipTracker.last().mNode == node)
+ clipIndex -= 1;
+ while (--clipIndex > 0) {
+ const ClipColumnTracker& clipTrack = clipTracker.at(clipIndex);
+ if (clipTrack.mHasClip == false) {
+ adjustForColumns(clipTrack, &cachedNode, &absBounds, static_cast<RenderBlock*>(nodeRenderer));
+ continue;
+ }
+ const IntRect& parentClip = clipTrack.mBounds;
+ if (hasClip == false && type == ANCHOR_CACHEDNODETYPE)
+ clip = parentClip;
+ else
+ clip.intersect(parentClip);
+ hasClip = true;
+ }
+ bool isInLayer = false;
+#if USE(ACCELERATED_COMPOSITING)
+ // If this renderer has a composited parent layer (including itself),
+ // add the node to the cached layer.
+ // FIXME: does not work for area rects
+ RenderLayer* enclosingLayer = nodeRenderer->enclosingLayer();
+ if (enclosingLayer && enclosingLayer->enclosingCompositingLayer()) {
+ LayerAndroid* layer = layerTracker.last().mLayer;
+ if (layer) {
+ const IntRect& layerClip = layerTracker.last().mBounds;
+ if (!layerClip.isEmpty() && !cachedNode.clip(layerClip)) {
+ DBG_NAV_LOGD("skipped on layer clip %d", cacheIndex);
+ continue; // skip this node if outside of the clip
+ }
+ isInLayer = true;
+ isUnclipped = true; // assume that layers do not have occluded nodes
+ hasClip = false;
+ AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(),
+ layer->uniqueId());
+ }
+ }
+#endif
+ if (hasClip) {
+ if (clip.isEmpty())
+ continue; // skip this node if clip prevents all drawing
+ else if (cachedNode.clip(clip) == false)
+ continue; // skip this node if outside of the clip
+ }
+ cachedNode.setColorIndex(colorIndex);
+ cachedNode.setExport(exported);
+ cachedNode.setHasCursorRing(hasCursorRing);
+ cachedNode.setHasMouseOver(hasMouseOver);
+ cachedNode.setHitBounds(absBounds);
+ cachedNode.setIndex(cacheIndex);
+ cachedNode.setIsFocus(isFocus);
+ cachedNode.setIsInLayer(isInLayer);
+ cachedNode.setIsTransparent(isTransparent);
+ cachedNode.setIsUnclipped(isUnclipped);
+ cachedNode.setOriginalAbsoluteBounds(originalAbsBounds);
+ cachedNode.setParentIndex(last->mCachedNodeIndex);
+ cachedNode.setParentGroup(ParentWithChildren(node));
+ cachedNode.setSingleImage(imageCount == 1);
+ cachedNode.setTabIndex(tabIndex);
+ cachedNode.setType(type);
+ if (type == TEXT_INPUT_CACHEDNODETYPE) {
+ cachedFrame->add(cachedInput);
+ cachedNode.setDataIndex(textInputIndex);
+ textInputIndex++;
+ } else
+ cachedNode.setDataIndex(-1);
+#if DUMP_NAV_CACHE
+ cachedNode.mDebug.mNodeIndex = nodeIndex;
+ cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex(
+ node, nodeIndex, (Node*) cachedNode.parentGroup());
+#endif
+ cachedFrame->add(cachedNode);
+ {
+ int lastIndex = cachedFrame->size() - 1;
+ if (node == focused) {
+ CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex);
+ cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr);
+ }
+ if (lastChild != NULL) {
+ tracker.grow(tracker.size() + 1);
+ FocusTracker& working = tracker.last();
+ working.mCachedNodeIndex = lastIndex;
+ working.mLastChild = OneAfter(lastChild);
+ last = &tracker.at(tracker.size() - 2);
+ working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus;
+ }
+ }
+ cacheIndex++;
+ }
+ while (tracker.size() > 1) {
+ FocusTracker* last = &tracker.last();
+ int lastChildIndex = cachedFrame->size() - 1;
+ if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
+ cacheIndex--;
+ tracker.removeLast();
+ }
+}
+
+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
+ // or if inner is keyboard focusable, disable outer
+ // else disable inner by removing it
+ int childCount = lastChildIndex - last->mCachedNodeIndex;
+ if (childCount == 0)
+ return false;
+ CachedNode* lastCached = cachedFrame->getIndex(last->mCachedNodeIndex);
+ Node* lastNode = (Node*) lastCached->nodePointer();
+ if ((childCount > 1 && lastNode->hasTagName(HTMLNames::selectTag) == false) ||
+ lastNode->hasTagName(HTMLNames::bodyTag) ||
+ lastNode->hasTagName(HTMLNames::formTag)) {
+ lastCached->setBounds(IntRect(0, 0, 0, 0));
+ lastCached->mCursorRing.clear();
+ return false;
+ }
+ CachedNode* onlyChildCached = cachedFrame->lastNode();
+ Node* onlyChild = (Node*) onlyChildCached->nodePointer();
+ bool outerIsMouseMoveOnly =
+ lastNode->isKeyboardFocusable(NULL) == false &&
+ lastNode->isMouseFocusable() == false &&
+ lastNode->isFocusable() == false &&
+ HasOverOrOut(lastNode) == true &&
+ HasTriggerEvent(lastNode) == false;
+ if (onlyChildCached->parent() == lastCached)
+ onlyChildCached->setParentIndex(lastCached->parentIndex());
+ bool hasFocus = lastCached->isFocus() || onlyChildCached->isFocus();
+ if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL)
+ || onlyChildCached->isPlugin()) {
+ int index = lastCached->index();
+ *lastCached = *onlyChildCached;
+ lastCached->setIndex(index);
+ CachedFrame* frame = cachedFrame->hasFrame(lastCached);
+ if (frame)
+ frame->setIndexInParent(index);
+ }
+ cachedFrame->removeLast();
+ if (hasFocus)
+ cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode());
+ return true;
+}
+
+Node* CacheBuilder::currentFocus() const
+{
+ Frame* frame = FrameAnd(this);
+ Document* doc = frame->document();
+ if (doc != NULL) {
+ Node* focus = doc->focusedNode();
+ if (focus != NULL)
+ return focus;
+ }
+ Frame* child = frame->tree()->firstChild();
+ while (child) {
+ CacheBuilder* cacheBuilder = Builder(child);
+ Node* focus = cacheBuilder->currentFocus();
+ if (focus)
+ return focus;
+ child = child->tree()->nextSibling();
+ }
+ return NULL;
+}
+
+static bool strCharCmp(const char* matches, const UChar* test, int wordLength,
+ int wordCount)
+{
+ for (int index = 0; index < wordCount; index++) {
+ for (int inner = 0; inner < wordLength; inner++) {
+ if (matches[inner] != test[inner]) {
+ matches += wordLength;
+ goto next;
+ }
+ }
+ return true;
+next:
+ ;
+ }
+ return false;
+}
+
+static const int stateTwoLetter[] = {
+ 0x02060c00, // A followed by: [KLRSZ]
+ 0x00000000, // B
+ 0x00084001, // C followed by: [AOT]
+ 0x00000014, // D followed by: [CE]
+ 0x00000000, // E
+ 0x00001800, // F followed by: [LM]
+ 0x00100001, // G followed by: [AU]
+ 0x00000100, // H followed by: [I]
+ 0x00002809, // I followed by: [ADLN]
+ 0x00000000, // J
+ 0x01040000, // K followed by: [SY]
+ 0x00000001, // L followed by: [A]
+ 0x000ce199, // M followed by: [ADEHINOPST]
+ 0x0120129c, // N followed by: [CDEHJMVY]
+ 0x00020480, // O followed by: [HKR]
+ 0x00420001, // P followed by: [ARW]
+ 0x00000000, // Q
+ 0x00000100, // R followed by: [I]
+ 0x0000000c, // S followed by: [CD]
+ 0x00802000, // T followed by: [NX]
+ 0x00080000, // U followed by: [T]
+ 0x00080101, // V followed by: [AIT]
+ 0x01200101 // W followed by: [AIVY]
+};
+
+static const char firstIndex[] = {
+ 0, 5, 5, 8, 10, 10, 12, 14,
+ 15, 19, 19, 21, 22, 32, 40, 43,
+ 46, 46, 47, 49, 51, 52, 55, 59
+};
+
+// from http://infolab.stanford.edu/~manku/bitcount/bitcount.html
+#define TWO(c) (0x1u << (c))
+#define MASK(c) (((unsigned int)(-1)) / (TWO(TWO(c)) + 1u))
+#define COUNT(x,c) ((x) & MASK(c)) + (((x) >> (TWO(c))) & MASK(c))
+
+int bitcount (unsigned int n)
+{
+ n = COUNT(n, 0);
+ n = COUNT(n, 1);
+ n = COUNT(n, 2);
+ n = COUNT(n, 3);
+ return COUNT(n, 4);
+}
+
+#undef TWO
+#undef MASK
+#undef COUNT
+
+static bool isUnicodeSpace(UChar ch)
+{
+ return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0;
+}
+
+static bool validZip(int stateIndex, const UChar* zipPtr)
+{
+ static const struct {
+ char mLow;
+ char mHigh;
+ char mException1;
+ char mException2;
+ } zipRange[] = {
+ { 99, 99, -1, -1 }, // AK Alaska
+ { 35, 36, -1, -1 }, // AL Alabama
+ { 71, 72, -1, -1 }, // AR Arkansas
+ { 96, 96, -1, -1 }, // AS American Samoa
+ { 85, 86, -1, -1 }, // AZ Arizona
+ { 90, 96, -1, -1 }, // CA California
+ { 80, 81, -1, -1 }, // CO Colorado
+ { 6, 6, -1, -1 }, // CT Connecticut
+ { 20, 20, -1, -1 }, // DC District of Columbia
+ { 19, 19, -1, -1 }, // DE Delaware
+ { 32, 34, -1, -1 }, // FL Florida
+ { 96, 96, -1, -1 }, // FM Federated States of Micronesia
+ { 30, 31, -1, -1 }, // GA Georgia
+ { 96, 96, -1, -1 }, // GU Guam
+ { 96, 96, -1, -1 }, // HI Hawaii
+ { 50, 52, -1, -1 }, // IA Iowa
+ { 83, 83, -1, -1 }, // ID Idaho
+ { 60, 62, -1, -1 }, // IL Illinois
+ { 46, 47, -1, -1 }, // IN Indiana
+ { 66, 67, 73, -1 }, // KS Kansas
+ { 40, 42, -1, -1 }, // KY Kentucky
+ { 70, 71, -1, -1 }, // LA Louisiana
+ { 1, 2, -1, -1 }, // MA Massachusetts
+ { 20, 21, -1, -1 }, // MD Maryland
+ { 3, 4, -1, -1 }, // ME Maine
+ { 96, 96, -1, -1 }, // MH Marshall Islands
+ { 48, 49, -1, -1 }, // MI Michigan
+ { 55, 56, -1, -1 }, // MN Minnesota
+ { 63, 65, -1, -1 }, // MO Missouri
+ { 96, 96, -1, -1 }, // MP Northern Mariana Islands
+ { 38, 39, -1, -1 }, // MS Mississippi
+ { 55, 56, -1, -1 }, // MT Montana
+ { 27, 28, -1, -1 }, // NC North Carolina
+ { 58, 58, -1, -1 }, // ND North Dakota
+ { 68, 69, -1, -1 }, // NE Nebraska
+ { 3, 4, -1, -1 }, // NH New Hampshire
+ { 7, 8, -1, -1 }, // NJ New Jersey
+ { 87, 88, 86, -1 }, // NM New Mexico
+ { 88, 89, 96, -1 }, // NV Nevada
+ { 10, 14, 0, 6 }, // NY New York
+ { 43, 45, -1, -1 }, // OH Ohio
+ { 73, 74, -1, -1 }, // OK Oklahoma
+ { 97, 97, -1, -1 }, // OR Oregon
+ { 15, 19, -1, -1 }, // PA Pennsylvania
+ { 6, 6, 0, 9 }, // PR Puerto Rico
+ { 96, 96, -1, -1 }, // PW Palau
+ { 2, 2, -1, -1 }, // RI Rhode Island
+ { 29, 29, -1, -1 }, // SC South Carolina
+ { 57, 57, -1, -1 }, // SD South Dakota
+ { 37, 38, -1, -1 }, // TN Tennessee
+ { 75, 79, 87, 88 }, // TX Texas
+ { 84, 84, -1, -1 }, // UT Utah
+ { 22, 24, 20, -1 }, // VA Virginia
+ { 6, 9, -1, -1 }, // VI Virgin Islands
+ { 5, 5, -1, -1 }, // VT Vermont
+ { 98, 99, -1, -1 }, // WA Washington
+ { 53, 54, -1, -1 }, // WI Wisconsin
+ { 24, 26, -1, -1 }, // WV West Virginia
+ { 82, 83, -1, -1 } // WY Wyoming
+ };
+
+ int zip = zipPtr[0] - '0';
+ zip *= 10;
+ zip += zipPtr[1] - '0';
+ int low = zipRange[stateIndex].mLow;
+ int high = zipRange[stateIndex].mHigh;
+ if (zip >= low && zip <= high)
+ return true;
+ if (zip == zipRange[stateIndex].mException1)
+ return true;
+ if (zip == zipRange[stateIndex].mException2)
+ return true;
+ return false;
+}
+
+#define MAX_PLACE_NAME_LENGTH 25 // the longest allowable one word place name
+
+CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars,
+ unsigned length, int* start, int* end, bool caseInsensitive)
+{
+ FindState addressState;
+ FindReset(&addressState);
+ addressState.mWords[0] = addressState.mStarts[0] = chars;
+ addressState.mCaseInsensitive = caseInsensitive;
+ FoundState state = FindPartialAddress(chars, chars, length, &addressState);
+ if (state == FOUND_PARTIAL && addressState.mProgress == ZIP_CODE &&
+ addressState.mNumberCount == 0) {
+ addressState.mProgress = FIND_STREET;
+ state = FindPartialAddress(NULL, NULL, 0, &addressState);
+ }
+ *start = addressState.mStartResult;
+ *end = addressState.mEndResult;
+ return state;
+}
+
+CacheBuilder::FoundState CacheBuilder::FindPartialAddress(const UChar* baseChars,
+ const UChar* chars, unsigned length, FindState* s)
+{
+ // lower case letters are optional; trailing asterisk is optional 's'
+ static char const* const longStreetNames[] = {
+ "\x04" "LleY" "\x04" "NneX" "\x05" "RCade" "\x05" "VEnue" "\x06" "LAMEDA", // A
+ "\x04" "aYoU" "\x04" "eaCH" "\x03" "eND" "\x05" "LuFf*" "\x05" "oTtoM"
+ "\x08" "ouLeVarD" "\x05" "Ranch" "\x05" "RidGe" "\x05" "RooK*"
+ "\x04" "urG*" "\x05" "YPass" "\x07" "roadWAY", // B
+ "\x05" "AMINO"
+ "\x03" "amP" "\x05" "anYoN" "\x03" "aPE" "\x07" "auSeWaY" "\x06" "enTeR*"
+ "\x06" "IRcle*" "\x05" "LiFf*" "\x03" "LuB" "\x05" "oMmoN" "\x06" "ORner*"
+ "\x05" "ouRSE" "\x05" "ourT*" "\x04" "oVe*" "\x04" "ReeK" "\x07" "REScent"
+ "\x04" "ReST" "\x07" "ROSSING" "\x08" "ROSSROAD" "\x04" "URVe"
+ "\x05" "AMINO" "\x06" "IRCULO" "\x07" "REScent", // C
+ "\x03" "aLe" "\x02" "aM" "\x05" "iVide" "\x05" "Rive*", // D
+ "\x06" "STate*" "\x09" "XPresswaY" "\x09" "XTension*", // E
+ "\x04" "ALL*" "\x04" "eRrY" "\x05" "ieLD*" "\x04" "LaT*" "\x04" "oRD*"
+ "\x05" "oReST" "\x05" "oRGe*" "\x04" "oRK*" "\x03" "orT" "\x06" "reeWaY", // F
+ "\x06" "arDeN*" "\x06" "aTeWaY" "\x04" "LeN*" "\x05" "ReeN*" "\x05" "RoVe*", // G
+ "\x06" "arBoR*" "\x04" "aVeN" "\x06" "eighTS" "\x06" "ighWaY" "\x04" "iLl*"
+ "\x05" "OLloW", // H
+ "\x04" "NLeT" "\x06" "Sland*" "\x03" "SLE", // I
+ "\x08" "unCTion*", // J
+ "\x03" "eY*" "\x05" "NoLl*", // K
+ "\x04" "aKe*" "\x03" "AND" "\x06" "aNDinG" "\x03" "aNe" "\x05" "iGhT*"
+ "\x03" "oaF" "\x04" "oCK*" "\x04" "oDGe" "\x03" "OOP", // L
+ "\x03" "ALL" "\x05" "aNoR*" "\x06" "eaDoW*" "\x03" "EWS" "\x04" "iLl*"
+ "\x06" "iSsioN" "\x07" "oTorWaY" "\x04" "ounT" "\x08" "ounTaiN*", // M
+ "\x03" "eCK", // N
+ "\x06" "RCHard" "\x03" "VAL" "\x07" "verPASs", // O
+ "\x04" "ARK*" "\x07" "arKWaY*" "\x03" "ASS" "\x06" "aSsaGE" "\x03" "ATH"
+ "\x03" "IKE" "\x04" "iNE*" "\x04" "Lace" "\x05" "LaiN*" "\x04" "LaZa"
+ "\x05" "oinT*" "\x04" "oRT*" "\x06" "Rairie" "\x06" "RIVADA", // P
+ NULL,
+ "\x05" "ADiaL" "\x03" "AMP" "\x04" "aNCH" "\x05" "aPiD*"
+ "\x03" "eST"
+ "\x05" "iDGe*" "\x04" "IVer" "\x04" "oaD*" "\x04" "ouTE" "\x02" "OW"
+ "\x02" "UE" "\x02" "UN", // R
+ "\x05" "HoaL*" "\x05" "HoRe*" "\x05" "KyWaY" "\x06" "PrinG*" "\x04" "PUR*"
+ "\x06" "Quare*" "\x06" "TAtion" "\x08" "TRAvenue" "\x05" "TReaM"
+ "\x06" "Treet*" "\x05" "uMmiT" "\x07" "PeeDWaY", // S
+ "\x06" "ERrace" "\x09" "hRoughWaY" "\x04" "RaCE" "\x04" "RAcK" "\x09" "RaFficwaY"
+ "\x04" "RaiL" "\x05" "UNneL" "\x07" "urnPiKE", // T
+ "\x08" "nderPASs" "\x05" "Nion*", // U
+ "\x06" "aLleY*" "\x06" "IAduct" "\x04" "ieW*" "\x07" "iLlaGe*" "\x04" "iLle"
+ "\x04" "ISta", // V
+ "\x04" "ALK*" "\x03" "ALL" "\x03" "AY*" "\x04" "eLl*", // W
+ "\x03" "ING" "\x02" "RD", // X
+ };
+
+ static char const* const longStateNames[] = {
+ "\x8e" "la" "\x85" "bama" "\x02" "\x84" "ska" "\x01" "\x8f" "merican Samoa" "\x04"
+ "\x91" "r" "\x86" "izona" "\x05" "\x87" "kansas" "\x03",
+ NULL,
+ "\x8b" "alifornia" "\x06" "\x95" "o" "\x87" "lorado" "\x07" "\x8a" "nnecticut" "\x08",
+ "\x89" "elaware" "\x0a" "\x95" "istrict of Columbia" "\x09",
+ NULL,
+ "\x9f" "ederated States of Micronesia" "\x0c" "\x88" "lorida" "\x0b",
+ "\x85" "uam" "\x0e" "\x88" "eorgia" "\x0d",
+ "\x87" "awaii" "\x0f",
+ "\x86" "daho" "\x11" "\x89" "llinois" "\x12" "\x88" "ndiana" "\x13" "\x85"
+ "owa" "\x10",
+ NULL,
+ "\x87" "ansas" "\x14" "\x89" "entucky" "\x15",
+ "\x8a" "ouisiana" "\x16",
+ "\x86" "aine" "\x19" "\x99" "ar" "\x8e" "shall Islands" "\x1a" "\x86" "yland" "\x18"
+ "\x8e" "assachusetts" "\x17" "\x93" "i" "\x87" "chigan" "\x1b"
+ "\x88" "nnesota" "\x1c" "\x93" "iss" "\x88" "issippi" "\x1f" "\x85"
+ "ouri" "\x1d" "\x88" "ontana" "\x20",
+ "\x90" "e" "\x87" "braska" "\x23" "\x85" "vada" "\x27" "\xa5" "ew " "\x8a"
+ "Hampshire" "\x24" "\x87" "Jersey" "\x25" "\x87" "Mexico" "\x26"
+ "\x85" "York" "\x28" "\x98" "orth " "\x89" "Carolina" "\x21" "\x87"
+ "Dakota" "\x22" "\x99" "orthern Mariana Islands" "\x1e",
+ "\x85" "hio" "\x29" "\x89" "klahoma" "\x2a" "\x87" "regon" "\x2b",
+ "\x86" "alau" "\x2e" "\x8d" "ennsylvania" "\x2c" "\x8c" "uerto Rico" "\x2d",
+ NULL,
+ "\x8d" "hode Island" "\x2f",
+ "\x98" "outh " "\x89" "Carolina" "\x30" "\x87" "Dakota" "\x31",
+ "\x90" "e" "\x88" "nnessee" "\x32" "\x84" "xas" "\x33",
+ "\x85" "tah" "\x34",
+ "\x88" "ermont" "\x37" "\x94" "irgin" "\x89" " Islands" "\x36" "\x83" "ia" "\x35",
+ "\x8b" "ashington" "\x38" "\x8e" "est Virginia" "\x3a" "\x8a" "isconsin" "\x39"
+ "\x88" "yoming" "\x3b"
+ };
+
+#if 0 // DEBUG_NAV_UI
+ static char const* const progressNames[] = {
+ "NO_ADDRESS",
+ "SKIP_TO_SPACE",
+ "HOUSE_NUMBER",
+ "NUMBER_TRAILING_SPACE",
+ "ADDRESS_LINE",
+ "STATE_NAME",
+ "SECOND_HALF",
+ "ZIP_CODE",
+ "PLUS_4",
+ "FIND_STREET"
+ };
+#endif
+ // strategy: US only support at first
+ // look for a 1 - 5 digit number for a street number (no support for 'One Microsoft Way')
+ // ignore if preceded by '#', Suite, Ste, Rm
+ // look for two or more words (up to 5? North Frank Lloyd Wright Blvd)
+ // note: "The Circle at North Hills St." has six words, and a lower 'at' -- allow at, by, of, in, the, and, ... ?
+ // if a word starts with a lowercase letter, no match
+ // allow: , . - # / (for 1/2) ' "
+ // don't look for street name type yet
+ // look for one or two delimiters to represent possible 2nd addr line and city name
+ // look for either full state name, or state two letters, and/or zip code (5 or 9 digits)
+ // now look for street suffix, either in full or abbreviated form, with optional 's' if there's an asterisk
+
+ s->mCurrentStart = chars;
+ s->mEnd = chars + length;
+ int candIndex = 0;
+ bool retryState;
+ bool mustBeAllUpper = false;
+ bool secondHalf = false;
+ chars -= 1;
+ UChar ch = s->mCurrent;
+ while (++chars <= s->mEnd) {
+ UChar prior = ch;
+ ch = chars < s->mEnd ? *chars : ' ';
+ switch (s->mProgress) {
+ case NO_ADDRESS:
+ if (WTF::isASCIIDigit(ch) == false) {
+ if (ch != 'O') // letter 'O', not zero
+ continue;
+ if (s->mEnd - chars < 3)
+ continue;
+ prior = *++chars;
+ ch = *++chars;
+ if ((prior != 'n' || ch != 'e') && (prior != 'N' || ch != 'E'))
+ continue;
+ if (isUnicodeSpace(*++chars) == false)
+ continue;
+ s->mProgress = ADDRESS_LINE;
+ s->mStartResult = chars - 3 - s->mCurrentStart;
+ continue;
+ }
+ if (isUnicodeSpace(prior) == false) {
+ s->mProgress = SKIP_TO_SPACE;
+ continue;
+ }
+ s->mNumberCount = 1;
+ s->mProgress = HOUSE_NUMBER;
+ s->mStartResult = chars - s->mCurrentStart;
+ continue;
+ case SKIP_TO_SPACE:
+ if (isUnicodeSpace(ch) == false)
+ continue;
+ break;
+ case HOUSE_NUMBER:
+ if (WTF::isASCIIDigit(ch)) {
+ if (++s->mNumberCount >= 6)
+ s->mProgress = SKIP_TO_SPACE;
+ continue;
+ }
+ if (WTF::isASCIIUpper(ch)) { // allow one letter after house number, e.g. 12A SKOLFIELD PL, HARPSWELL, ME 04079
+ if (WTF::isASCIIDigit(prior) == false)
+ s->mProgress = SKIP_TO_SPACE;
+ continue;
+ }
+ if (ch == '-') {
+ if (s->mNumberCount > 0) { // permit 21-23 ELM ST
+ ++s->mNumberCount;
+ continue;
+ }
+ }
+ s->mNumberCount = 0;
+ s->mProgress = NUMBER_TRAILING_SPACE;
+ case NUMBER_TRAILING_SPACE:
+ if (isUnicodeSpace(ch))
+ continue;
+ if (0 && WTF::isASCIIDigit(ch)) {
+ s->mNumberCount = 1;
+ s->mProgress = HOUSE_NUMBER;
+ s->mStartResult = chars - s->mCurrentStart;
+ continue;
+ }
+ if (WTF::isASCIIDigit(ch) == false && WTF::isASCIIUpper(ch) == false)
+ break;
+ s->mProgress = ADDRESS_LINE;
+ case ADDRESS_LINE:
+ if (WTF::isASCIIAlpha(ch) || ch == '\'' || ch == '-' || ch == '&' || ch == '(' || ch == ')') {
+ if (++s->mLetterCount > 1) {
+ s->mNumberWords &= ~(1 << s->mWordCount);
+ continue;
+ }
+ if (s->mNumberCount >= 5)
+ break;
+// FIXME: the test below was added to give up on a non-address, but it
+// incorrectly discards addresses where the house number is in one node
+// and the street name is in the next; I don't recall what the failing case
+// is that suggested this fix.
+// if (s->mWordCount == 0 && s->mContinuationNode)
+// return FOUND_NONE;
+ s->newWord(baseChars, chars);
+ if (WTF::isASCIILower(ch) && s->mNumberCount == 0)
+ s->mFirstLower = chars;
+ s->mNumberCount = 0;
+ if (WTF::isASCIILower(ch) || (WTF::isASCIIAlpha(ch) == false && ch != '-'))
+ s->mNumberWords &= ~(1 << s->mWordCount);
+ s->mUnparsed = true;
+ continue;
+ } else if (s->mLetterCount >= MAX_PLACE_NAME_LENGTH) {
+ break;
+ } else if (s->mFirstLower != NULL) {
+ if (s->mCaseInsensitive)
+ goto resetWord;
+ size_t length = chars - s->mFirstLower;
+ if (length > 3)
+ break;
+ if (length == 3 && strCharCmp("and" "the", s->mFirstLower, 3, 2) == false)
+ break;
+ if (length == 2 && strCharCmp("at" "by" "el" "in" "of", s->mFirstLower, 2, 5) == false)
+ break;
+ goto resetWord;
+ }
+ if (ch == ',' || ch == '*') { // delimits lines
+ // asterisk as delimiter: http://www.sa.sc.edu/wellness/members.html
+ if (++s->mLineCount > 5)
+ break;
+ goto lookForState;
+ }
+ if (isUnicodeSpace(ch) || prior == '-') {
+ lookForState:
+ if (s->mUnparsed == false)
+ continue;
+ const UChar* candidate = s->mWords[s->mWordCount];
+ UChar firstLetter = candidate[0];
+ if (WTF::isASCIIUpper(firstLetter) == false && WTF::isASCIIDigit(firstLetter) == false)
+ goto resetWord;
+ s->mWordCount++;
+ if ((s->mWordCount == 2 && s->mNumberWords == 3 && WTF::isASCIIDigit(s->mWords[1][1])) || // choose second of 8888 333 Main
+ (s->mWordCount >= sizeof(s->mWords) / sizeof(s->mWords[0]) - 1)) { // subtract 1 since state names may have two parts
+ // search for simple number already stored since first potential house # didn't pan out
+ if (s->mNumberWords == 0)
+ break;
+ int shift = 0;
+ while ((s->mNumberWords & (1 << shift)) == 0)
+ shift++;
+ s->mNumberWords >>= ++shift;
+ if (s->mBases[0] != s->mBases[shift]) // if we're past the original node, bail
+ break;
+ s->shiftWords(shift);
+ s->mStartResult = s->mWords[0] - s->mStarts[0];
+ s->mWordCount -= shift;
+ // FIXME: need to adjust lineCount to account for discarded delimiters
+ }
+ if (s->mWordCount < 4)
+ goto resetWord;
+ firstLetter -= 'A';
+ if (firstLetter > 'W' - 'A')
+ goto resetWord;
+ UChar secondLetter = candidate[1];
+ if (prior == '-')
+ s->mLetterCount--; // trim trailing dashes, to accept CA-94043
+ if (s->mLetterCount == 2) {
+ secondLetter -= 'A';
+ if (secondLetter > 'Z' - 'A')
+ goto resetWord;
+ if ((stateTwoLetter[firstLetter] & 1 << secondLetter) != 0) {
+ // special case to ignore 'et al'
+ if (strCharCmp("ET", s->mWords[s->mWordCount - 2], 2, 1) == false) {
+ s->mStateWord = s->mWordCount - 1;
+ s->mZipHint = firstIndex[firstLetter] +
+ bitcount(stateTwoLetter[firstLetter] & ((1 << secondLetter) - 1));
+ goto foundStateName;
+ }
+ }
+ goto resetWord;
+ }
+ s->mStates = longStateNames[firstLetter];
+ if (s->mStates == NULL)
+ goto resetWord;
+ mustBeAllUpper = false;
+ s->mProgress = STATE_NAME;
+ unsigned char section = s->mStates[0];
+ ASSERT(section > 0x80);
+ s->mSectionLength = section & 0x7f;
+ candIndex = 1;
+ secondHalf = false;
+ s->mStateWord = s->mWordCount - 1;
+ goto stateName;
+ }
+ if (WTF::isASCIIDigit(ch)) {
+ if (s->mLetterCount == 0) {
+ if (++s->mNumberCount > 1)
+ continue;
+ if (s->mWordCount == 0 && s->mContinuationNode)
+ return FOUND_NONE;
+ s->newWord(baseChars, chars);
+ s->mNumberWords |= 1 << s->mWordCount;
+ s->mUnparsed = true;
+ }
+ continue;
+ }
+ if (ch == '.') { // optionally can follow letters
+ if (s->mLetterCount == 0)
+ break;
+ if (s->mNumberCount > 0)
+ break;
+ continue;
+ }
+ if (ch == '/') // between numbers (1/2) between words (12 Main / Ste 4d)
+ goto resetWord;
+ if (ch == '#') // can precede numbers, allow it to appear randomly
+ goto resetWord;
+ if (ch == '"') // sometimes parts of addresses are quoted (FIXME: cite an example here)
+ continue;
+ break;
+ case SECOND_HALF:
+ if (WTF::isASCIIAlpha(ch)) {
+ if (s->mLetterCount == 0) {
+ s->newWord(baseChars, chars);
+ s->mWordCount++;
+ }
+ s->mLetterCount++;
+ continue;
+ }
+ if (WTF::isASCIIDigit(ch) == false) {
+ if (s->mLetterCount > 0) {
+ s->mProgress = STATE_NAME;
+ candIndex = 0;
+ secondHalf = true;
+ goto stateName;
+ }
+ continue;
+ }
+ s->mProgress = ADDRESS_LINE;
+ goto resetState;
+ case STATE_NAME:
+ stateName:
+ // pick up length of first section
+ do {
+ int stateIndex = 1;
+ int skip = 0;
+ int prefix = 0;
+ bool subStr = false;
+ do {
+ unsigned char match = s->mStates[stateIndex];
+ if (match >= 0x80) {
+ if (stateIndex == s->mSectionLength)
+ break;
+ subStr = true;
+ // if (skip > 0)
+ // goto foundStateName;
+ prefix = candIndex;
+ skip = match & 0x7f;
+ match = s->mStates[++stateIndex];
+ }
+ UChar candChar = s->mWords[s->mWordCount - 1][candIndex];
+ if (mustBeAllUpper && WTF::isASCIILower(candChar))
+ goto skipToNext;
+ if (match != candChar) {
+ if (match != WTF::toASCIILower(candChar)) {
+ skipToNext:
+ if (subStr == false)
+ break;
+ if (stateIndex == s->mSectionLength) {
+ if (secondHalf) {
+ s->mProgress = ADDRESS_LINE;
+ goto resetState;
+ }
+ break;
+ }
+ stateIndex += skip;
+ skip = 0;
+ candIndex = prefix;
+ continue; // try next substring
+ }
+ mustBeAllUpper = true;
+ }
+ int nextindex = stateIndex + 1;
+ if (++candIndex >= s->mLetterCount && s->mStates[nextindex] == ' ') {
+ s->mProgress = SECOND_HALF;
+ s->mStates += nextindex;
+ s->mSectionLength -= nextindex;
+ goto resetWord;
+ }
+ if (nextindex + 1 == s->mSectionLength || skip == 2) {
+ s->mZipHint = s->mStates[nextindex] - 1;
+ goto foundStateName;
+ }
+ stateIndex += 1;
+ skip -= 1;
+ } while (true);
+ s->mStates += s->mSectionLength;
+ ASSERT(s->mStates[0] == 0 || (unsigned) s->mStates[0] > 0x80);
+ s->mSectionLength = s->mStates[0] & 0x7f;
+ candIndex = 1;
+ subStr = false;
+ } while (s->mSectionLength != 0);
+ s->mProgress = ADDRESS_LINE;
+ goto resetState;
+ foundStateName:
+ s->mEndResult = chars - s->mCurrentStart;
+ s->mEndWord = s->mWordCount - 1;
+ s->mProgress = ZIP_CODE;
+ // a couple of delimiters is an indication that the state name is good
+ // or, a non-space / non-alpha-digit is also good
+ s->mZipDelimiter = s->mLineCount > 2
+ || isUnicodeSpace(ch) == false
+ || chars == s->mEnd;
+ if (WTF::isASCIIDigit(ch))
+ s->mZipStart = chars;
+ goto resetState;
+ case ZIP_CODE:
+ if (WTF::isASCIIDigit(ch)) {
+ int count = ++s->mNumberCount;
+ if (count == 1) {
+ if (WTF::isASCIIDigit(prior))
+ ++s->mNumberCount;
+ else
+ s->mZipStart = chars;
+ }
+ if (count <= 9)
+ continue;
+ } else if (isUnicodeSpace(ch)) {
+ if (s->mNumberCount == 0) {
+ s->mZipDelimiter = true; // two spaces delimit state name
+ continue;
+ }
+ } else if (ch == '-') {
+ if (s->mNumberCount == 5 && validZip(s->mZipHint, s->mZipStart)) {
+ s->mNumberCount = 0;
+ s->mProgress = PLUS_4;
+ continue;
+ }
+ if (s->mNumberCount == 0)
+ s->mZipDelimiter = true;
+ } else if (WTF::isASCIIAlpha(ch) == false)
+ s->mZipDelimiter = true;
+ else {
+ if (s->mLetterCount == 0) {
+ s->newWord(baseChars, chars);
+ s->mUnparsed = true;
+ }
+ ++s->mLetterCount;
+ }
+ if (s->mNumberCount == 5 || s->mNumberCount == 9) {
+ if (validZip(s->mZipHint, s->mZipStart) == false)
+ goto noZipMatch;
+ s->mEndResult = chars - s->mCurrentStart;
+ s->mEndWord = s->mWordCount - 1;
+ } else if (s->mZipDelimiter == false) {
+ noZipMatch:
+ --chars;
+ s->mProgress = ADDRESS_LINE;
+ continue;
+ }
+ s->mProgress = FIND_STREET;
+ goto findStreet;
+ case PLUS_4:
+ if (WTF::isASCIIDigit(ch)) {
+ if (++s->mNumberCount <= 4)
+ continue;
+ }
+ if (isUnicodeSpace(ch)) {
+ if (s->mNumberCount == 0)
+ continue;
+ }
+ if (s->mNumberCount == 4) {
+ if (WTF::isASCIIAlpha(ch) == false) {
+ s->mEndResult = chars - s->mCurrentStart;
+ s->mEndWord = s->mWordCount - 1;
+ }
+ } else if (s->mNumberCount != 0)
+ break;
+ s->mProgress = FIND_STREET;
+ case FIND_STREET:
+ findStreet:
+ retryState = false;
+ for (int wordsIndex = s->mStateWord - 1; wordsIndex >= 0; --wordsIndex) {
+ const UChar* test = s->mWords[wordsIndex];
+ UChar letter = test[0];
+ letter -= 'A';
+ if (letter > 'X' - 'A')
+ continue;
+ const char* names = longStreetNames[letter];
+ if (names == NULL)
+ continue;
+ int offset;
+ while ((offset = *names++) != 0) {
+ int testIndex = 1;
+ bool abbr = false;
+ for (int idx = 0; idx < offset; idx++) {
+ char nameLetter = names[idx];
+ char testUpper = WTF::toASCIIUpper(test[testIndex]);
+ if (nameLetter == '*') {
+ if (testUpper == 'S')
+ testIndex++;
+ break;
+ }
+ bool fullOnly = WTF::isASCIILower(nameLetter);
+ nameLetter = WTF::toASCIIUpper(nameLetter);
+ if (testUpper == nameLetter) {
+ if (abbr && fullOnly)
+ goto nextTest;
+ testIndex++;
+ continue;
+ }
+ if (fullOnly == false)
+ goto nextTest;
+ abbr = true;
+ }
+ letter = &test[testIndex] < s->mEnds[wordsIndex] ?
+ test[testIndex] : ' ';
+ if (WTF::isASCIIAlpha(letter) == false && WTF::isASCIIDigit(letter) == false) {
+ if (s->mNumberWords != 0) {
+ int shift = 0;
+ int wordReduction = -1;
+ do {
+ while ((s->mNumberWords & (1 << shift)) == 0)
+ shift++;
+ if (shift > wordsIndex)
+ break;
+ wordReduction = shift;
+ } while (s->mNumberWords >> ++shift != 0);
+ if (wordReduction >= 0) {
+ if (s->mContinuationNode) {
+ if (retryState)
+ break;
+ return FOUND_NONE;
+ }
+ s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction];
+ }
+ }
+ if (wordsIndex != s->mStateWord - 1)
+ return FOUND_COMPLETE;
+ retryState = true;
+ }
+ nextTest:
+ names += offset;
+ }
+ }
+ if (retryState) {
+ s->mProgress = ADDRESS_LINE;
+ s->mStates = NULL;
+ continue;
+ }
+ if (s->mNumberWords != 0) {
+ unsigned shift = 0;
+ while ((s->mNumberWords & (1 << shift)) == 0)
+ shift++;
+ s->mNumberWords >>= ++shift;
+ if (s->mBases[0] != s->mBases[shift])
+ return FOUND_NONE;
+ s->shiftWords(shift);
+ s->mStartResult = s->mWords[0] - s->mStarts[0];
+ s->mWordCount -= shift;
+ s->mProgress = ADDRESS_LINE;
+ --chars;
+ continue;
+ }
+ break;
+ }
+ if (s->mContinuationNode)
+ return FOUND_NONE;
+ s->mProgress = NO_ADDRESS;
+ s->mWordCount = s->mLineCount = 0;
+ s->mNumberWords = 0;
+ resetState:
+ s->mStates = NULL;
+ resetWord:
+ s->mNumberCount = s->mLetterCount = 0;
+ s->mFirstLower = NULL;
+ s->mUnparsed = false;
+ }
+ s->mCurrent = ch;
+ return s->mProgress == NO_ADDRESS ? FOUND_NONE : FOUND_PARTIAL;
+}
+
+// Recogize common email patterns only. Currently has lots of state, walks text forwards and backwards -- will be
+// a real challenge to adapt to walk text across multiple nodes, I imagine
+// FIXME: it's too hard for the caller to call these incrementally -- it's probably best for this to
+// either walk the node tree directly or make a callout to get the next or previous node, if there is one
+// walking directly will avoid adding logic in caller to track the multiple partial or full nodes that compose this
+// text pattern.
+CacheBuilder::FoundState CacheBuilder::FindPartialEMail(const UChar* chars, unsigned length,
+ FindState* s)
+{
+ // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp
+ // hand-edit at your own risk
+ static const int domainTwoLetter[] = {
+ 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz]
+ 0x036e73fb, // b followed by: [abdefghijmnorstvwyz]
+ 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz]
+ 0x02005610, // d followed by: [ejkmoz]
+ 0x001e00d4, // e followed by: [ceghrstu]
+ 0x00025700, // f followed by: [ijkmor]
+ 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy]
+ 0x001a3400, // h followed by: [kmnrtu]
+ 0x000f7818, // i followed by: [delmnoqrst]
+ 0x0000d010, // j followed by: [emop]
+ 0x0342b1d0, // k followed by: [eghimnprwyz]
+ 0x013e0507, // l followed by: [abcikrstuvy]
+ 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz]
+ 0x0212c975, // n followed by: [acefgilopruz]
+ 0x00001000, // o followed by: [m]
+ 0x014e3cf1, // p followed by: [aefghklmnrstwy]
+ 0x00000001, // q followed by: [a]
+ 0x00504010, // r followed by: [eouw]
+ 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz]
+ 0x026afeec, // t followed by: [cdfghjklmnoprtvwz]
+ 0x03041441, // u followed by: [agkmsyz]
+ 0x00102155, // v followed by: [aceginu]
+ 0x00040020, // w followed by: [fs]
+ 0x00000000, // x
+ 0x00180010, // y followed by: [etu]
+ 0x00401001, // z followed by: [amw]
+ };
+
+ static char const* const longDomainNames[] = {
+ "\x03" "ero" "\x03" "rpa", // aero, arpa
+ "\x02" "iz", // biz
+ "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop
+ NULL, // d
+ "\x02" "du", // edu
+ NULL, // f
+ "\x02" "ov", // gov
+ NULL, // h
+ "\x03" "nfo" "\x02" "nt", // info, int
+ "\x03" "obs", // jobs
+ NULL, // k
+ NULL, // l
+ "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum
+ "\x03" "ame" "\x02" "et", // name, net
+ "\x02" "rg", // , org
+ "\x02" "ro", // pro
+ NULL, // q
+ NULL, // r
+ NULL, // s
+ "\x05" "ravel", // travel
+ NULL, // u
+ NULL, // v
+ NULL, // w
+ NULL, // x
+ NULL, // y
+ NULL, // z
+ };
+
+ const UChar* start = chars;
+ const UChar* end = chars + length;
+ while (chars < end) {
+ UChar ch = *chars++;
+ if (ch != '@')
+ continue;
+ const UChar* atLocation = chars - 1;
+ // search for domain
+ ch = *chars++ | 0x20; // convert uppercase to lower
+ if (ch < 'a' || ch > 'z')
+ continue;
+ while (chars < end) {
+ ch = *chars++;
+ if (IsDomainChar(ch) == false)
+ goto nextAt;
+ if (ch != '.')
+ continue;
+ UChar firstLetter = *chars++ | 0x20; // first letter of the domain
+ if (chars >= end)
+ return FOUND_NONE; // only one letter; must be at least two
+ firstLetter -= 'a';
+ if (firstLetter > 'z' - 'a')
+ continue; // non-letter followed '.'
+ int secondLetterMask = domainTwoLetter[firstLetter];
+ ch = *chars | 0x20; // second letter of the domain
+ ch -= 'a';
+ if (ch >= 'z' - 'a')
+ continue;
+ bool secondMatch = (secondLetterMask & 1 << ch) != 0;
+ const char* wordMatch = longDomainNames[firstLetter];
+ int wordIndex = 0;
+ while (wordMatch != NULL) {
+ int len = *wordMatch++;
+ char match;
+ do {
+ match = wordMatch[wordIndex];
+ if (match < 0x20)
+ goto foundDomainStart;
+ if (chars[wordIndex] != match)
+ break;
+ wordIndex++;
+ } while (true);
+ wordMatch += len;
+ if (*wordMatch == '\0')
+ break;
+ wordIndex = 0;
+ }
+ if (secondMatch) {
+ wordIndex = 1;
+ foundDomainStart:
+ chars += wordIndex;
+ if (chars < end) {
+ ch = *chars;
+ if (ch != '.') {
+ if (IsDomainChar(ch))
+ goto nextDot;
+ } else if (chars + 1 < end && IsDomainChar(chars[1]))
+ goto nextDot;
+ }
+ // found domain. Search backwards from '@' for beginning of email address
+ s->mEndResult = chars - start;
+ chars = atLocation;
+ if (chars <= start)
+ goto nextAt;
+ ch = *--chars;
+ if (ch == '.')
+ goto nextAt; // mailbox can't end in period
+ do {
+ if (IsMailboxChar(ch) == false) {
+ chars++;
+ break;
+ }
+ if (chars == start)
+ break;
+ ch = *--chars;
+ } while (true);
+ UChar firstChar = *chars;
+ if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty
+ goto nextAt;
+ s->mStartResult = chars - start;
+ return FOUND_COMPLETE;
+ }
+ nextDot:
+ ;
+ }
+nextAt:
+ chars = atLocation + 1;
+ }
+ return FOUND_NONE;
+}
+
+#define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" // poor man's regex: parens optional, any one of punct, digit smallest allowed
+
+CacheBuilder::FoundState CacheBuilder::FindPartialNumber(const UChar* chars, unsigned length,
+ FindState* s)
+{
+ char* pattern = s->mPattern;
+ UChar* store = s->mStorePtr;
+ const UChar* start = chars;
+ const UChar* end = chars + length;
+ const UChar* lastDigit = NULL;
+ do {
+ bool initialized = s->mInitialized;
+ while (chars < end) {
+ if (initialized == false) {
+ s->mBackTwo = s->mBackOne;
+ s->mBackOne = s->mCurrent;
+ }
+ UChar ch = s->mCurrent = *chars;
+ do {
+ char patternChar = *pattern;
+ switch (patternChar) {
+ case '2':
+ if (initialized == false) {
+ s->mStartResult = chars - start;
+ initialized = true;
+ }
+ case '0':
+ case '1':
+ if (ch < patternChar || ch > '9')
+ goto resetPattern;
+ *store++ = ch;
+ pattern++;
+ lastDigit = chars;
+ goto nextChar;
+ case '\0':
+ if (WTF::isASCIIDigit(ch) == false) {
+ *store = '\0';
+ goto checkMatch;
+ }
+ goto resetPattern;
+ case ' ':
+ if (ch == patternChar)
+ goto nextChar;
+ break;
+ case '(':
+ if (ch == patternChar) {
+ s->mStartResult = chars - start;
+ initialized = true;
+ s->mOpenParen = true;
+ }
+ goto commonPunctuation;
+ case ')':
+ if ((ch == patternChar) ^ s->mOpenParen)
+ goto resetPattern;
+ default:
+ commonPunctuation:
+ if (ch == patternChar) {
+ pattern++;
+ goto nextChar;
+ }
+ }
+ } while (++pattern); // never false
+ nextChar:
+ chars++;
+ }
+ break;
+resetPattern:
+ if (s->mContinuationNode)
+ return FOUND_NONE;
+ FindResetNumber(s);
+ pattern = s->mPattern;
+ store = s->mStorePtr;
+ } while (++chars < end);
+checkMatch:
+ if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo))
+ return FOUND_NONE;
+ *store = '\0';
+ s->mStorePtr = store;
+ s->mPattern = pattern;
+ s->mEndResult = lastDigit - start + 1;
+ char pState = pattern[0];
+ return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ?
+ FOUND_NONE : FOUND_PARTIAL;
+}
+
+CacheBuilder::FoundState CacheBuilder::FindPhoneNumber(const UChar* chars, unsigned length,
+ int* start, int* end)
+{
+ FindState state;
+ FindReset(&state);
+ FoundState result = FindPartialNumber(chars, length, &state);
+ *start = state.mStartResult;
+ *end = state.mEndResult;
+ return result;
+}
+
+void CacheBuilder::FindReset(FindState* state)
+{
+ memset(state, 0, sizeof(FindState));
+ state->mCurrent = ' ';
+ FindResetNumber(state);
+}
+
+void CacheBuilder::FindResetNumber(FindState* state)
+{
+ state->mOpenParen = false;
+ state->mPattern = (char*) PHONE_PATTERN;
+ state->mStorePtr = state->mStore;
+}
+
+IntRect CacheBuilder::getAreaRect(const HTMLAreaElement* area)
+{
+ Node* node = area->document();
+ while ((node = node->traverseNextNode()) != NULL) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->isRenderImage()) {
+ RenderImage* image = static_cast<RenderImage*>(renderer);
+ HTMLMapElement* map = image->imageMap();
+ if (map) {
+ Node* n;
+ for (n = map->firstChild(); n;
+ n = n->traverseNextNode(map)) {
+ if (n == area) {
+ if (area->isDefault())
+ return image->absoluteBoundingBoxRect();
+ return area->computeRect(image);
+ }
+ }
+ }
+ }
+ }
+ return IntRect();
+}
+
+void CacheBuilder::GetGlobalOffset(Node* node, int* x, int * y)
+{
+ GetGlobalOffset(node->document()->frame(), x, y);
+}
+
+void CacheBuilder::GetGlobalOffset(Frame* frame, int* x, int* y)
+{
+// TIMER_PROBE(__FUNCTION__);
+ ASSERT(x);
+ ASSERT(y);
+ *x = 0;
+ *y = 0;
+ if (!frame->view())
+ return;
+ Frame* parent;
+ while ((parent = frame->tree()->parent()) != NULL) {
+ const WebCore::IntRect& rect = frame->view()->platformWidget()->getBounds();
+ *x += rect.x();
+ *y += rect.y();
+ frame = parent;
+ }
+ // TIMER_PROBE_END();
+}
+
+Frame* CacheBuilder::HasFrame(Node* node)
+{
+ RenderObject* renderer = node->renderer();
+ if (renderer == NULL)
+ return NULL;
+ if (renderer->isWidget() == false)
+ return NULL;
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (widget == NULL)
+ return NULL;
+ if (widget->isFrameView() == false)
+ return NULL;
+ return static_cast<FrameView*>(widget)->frame();
+}
+
+bool CacheBuilder::HasOverOrOut(Node* node)
+{
+ // eventNames are thread-local data, I avoid using 'static' variable here.
+ AtomicString eventTypes[2] = {
+ eventNames().mouseoverEvent,
+ eventNames().mouseoutEvent
+ };
+
+ return NodeHasEventListeners(node, eventTypes, 2);
+}
+
+bool CacheBuilder::HasTriggerEvent(Node* node)
+{
+ AtomicString eventTypes[5] = {
+ eventNames().clickEvent,
+ eventNames().mousedownEvent,
+ eventNames().mouseupEvent,
+ eventNames().keydownEvent,
+ eventNames().keyupEvent
+ };
+
+ return NodeHasEventListeners(node, eventTypes, 5);
+}
+
+// #define EMAIL_PATTERN "x@y.d" // where 'x' is letters, numbers, and '-', '.', '_' ; 'y' is 'x' without the underscore, and 'd' is a valid domain
+// - 0x2D . 0x2E 0-9 0x30-39 A-Z 0x41-5A _ 0x5F a-z 0x61-7A
+
+bool CacheBuilder::IsDomainChar(UChar ch)
+{
+ static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z
+ ch -= 0x20;
+ if (ch > 'z' - 0x20)
+ return false;
+ return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
+}
+
+bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node,
+ CachedNodeType* type, String* exported) const
+{
+ Text* textNode = static_cast<Text*>(node);
+ StringImpl* string = textNode->dataImpl();
+ const UChar* baseChars = string->characters();
+// const UChar* originalBase = baseChars;
+ int length = string->length();
+ int index = 0;
+ while (index < length && isUnicodeSpace(baseChars[index]))
+ index++;
+ if (index >= length)
+ return false;
+ if (more == false) {
+ walk->mStart = 0;
+ walk->mEnd = 0;
+ walk->mFinalNode = node;
+ walk->mLastInline = NULL;
+ }
+ // starting with this node, search forward for email, phone number, and address
+ // if any of the three is found, track it so that the remaining can be looked for later
+ FoundState state = FOUND_NONE;
+ RenderText* renderer = (RenderText*) node->renderer();
+ bool foundBetter = false;
+ InlineTextBox* baseInline = walk->mLastInline != NULL ? walk->mLastInline :
+ renderer->firstTextBox();
+ if (baseInline == NULL)
+ return false;
+ int start = walk->mEnd;
+ InlineTextBox* saveInline;
+ int baseStart, firstStart = start;
+ saveInline = baseInline;
+ baseStart = start;
+ for (CachedNodeType checkType = ADDRESS_CACHEDNODETYPE;
+ checkType <= PHONE_CACHEDNODETYPE;
+ checkType = static_cast<CachedNodeType>(checkType + 1))
+ {
+ if ((1 << (checkType - 1) & mAllowableTypes) == 0)
+ continue;
+ InlineTextBox* inlineTextBox = baseInline;
+ FindState findState;
+ FindReset(&findState);
+ start = baseStart;
+ if (checkType == ADDRESS_CACHEDNODETYPE) {
+ findState.mBases[0] = baseChars;
+ findState.mWords[0] = baseChars + start;
+ findState.mStarts[0] = baseChars + start;
+ }
+ Node* lastPartialNode = NULL;
+ int lastPartialEnd = -1;
+ bool lastPartialMore = false;
+ bool firstPartial = true;
+ InlineTextBox* lastPartialInline = NULL;
+ do {
+ do {
+ const UChar* chars = baseChars + start;
+ length = inlineTextBox == NULL ? 0 :
+ inlineTextBox->end() - start + 1;
+ bool wasInitialized = findState.mInitialized;
+ switch (checkType) {
+ case ADDRESS_CACHEDNODETYPE:
+ state = FindPartialAddress(baseChars, chars, length, &findState);
+ break;
+ case EMAIL_CACHEDNODETYPE:
+ state = FindPartialEMail(chars, length, &findState);
+ break;
+ case PHONE_CACHEDNODETYPE:
+ state = FindPartialNumber(chars, length, &findState);
+ break;
+ default:
+ ASSERT(0);
+ }
+ findState.mInitialized = state != FOUND_NONE;
+ if (wasInitialized != findState.mInitialized)
+ firstStart = start;
+ if (state == FOUND_PARTIAL) {
+ lastPartialNode = node;
+ lastPartialEnd = findState.mEndResult + start;
+ lastPartialMore = firstPartial &&
+ lastPartialEnd < (int) string->length();
+ firstPartial = false;
+ lastPartialInline = inlineTextBox;
+ findState.mContinuationNode = true;
+ } else if (state == FOUND_COMPLETE) {
+ if (foundBetter == false || walk->mStart > findState.mStartResult) {
+ walk->mStart = findState.mStartResult + firstStart;
+ if (findState.mEndResult > 0) {
+ walk->mFinalNode = node;
+ walk->mEnd = findState.mEndResult + start;
+ walk->mMore = node == textNode &&
+ walk->mEnd < (int) string->length();
+ walk->mLastInline = inlineTextBox;
+ } else {
+ walk->mFinalNode = lastPartialNode;
+ walk->mEnd = lastPartialEnd;
+ walk->mMore = lastPartialMore;
+ walk->mLastInline = lastPartialInline;
+ }
+ *type = checkType;
+ if (checkType == PHONE_CACHEDNODETYPE) {
+ const UChar* store = findState.mStore;
+ *exported = String(store);
+ } else {
+ Node* temp = textNode;
+ length = 1;
+ start = walk->mStart;
+ exported->truncate(0);
+ do {
+ Text* tempText = static_cast<Text*>(temp);
+ StringImpl* string = tempText->dataImpl();
+ int end = tempText == walk->mFinalNode ?
+ walk->mEnd : string->length();
+ exported->append(String(string->substring(
+ start, end - start)));
+ ASSERT(end > start);
+ length += end - start + 1;
+ if (temp == walk->mFinalNode)
+ break;
+ start = 0;
+ do {
+ temp = temp->traverseNextNode();
+ ASSERT(temp);
+ } while (temp->isTextNode() == false);
+ // add a space in between text nodes to avoid
+ // words collapsing together
+ exported->append(" ");
+ } while (true);
+ }
+ foundBetter = true;
+ }
+ goto tryNextCheckType;
+ } else if (findState.mContinuationNode)
+ break;
+ if (inlineTextBox == NULL)
+ break;
+ inlineTextBox = inlineTextBox->nextTextBox();
+ if (inlineTextBox == NULL)
+ break;
+ start = inlineTextBox->start();
+ if (state == FOUND_PARTIAL && node == textNode)
+ findState.mContinuationNode = false;
+ } while (true);
+ if (state == FOUND_NONE)
+ break;
+ // search for next text node, if any
+ Text* nextNode;
+ do {
+ do {
+ do {
+ if (node)
+ node = node->traverseNextNode();
+ if (node == NULL || node->hasTagName(HTMLNames::aTag)
+ || node->hasTagName(HTMLNames::inputTag)
+ || node->hasTagName(HTMLNames::textareaTag)) {
+ if (state == FOUND_PARTIAL &&
+ checkType == ADDRESS_CACHEDNODETYPE &&
+ findState.mProgress == ZIP_CODE &&
+ findState.mNumberCount == 0) {
+ baseChars = NULL;
+ inlineTextBox = NULL;
+ start = 0;
+ findState.mProgress = FIND_STREET;
+ goto finalNode;
+ }
+ goto tryNextCheckType;
+ }
+ } while (node->isTextNode() == false);
+ nextNode = static_cast<Text*>(node);
+ renderer = (RenderText*) nextNode->renderer();
+ } while (renderer == NULL);
+ baseInline = renderer->firstTextBox();
+ } while (baseInline == NULL);
+ string = nextNode->dataImpl();
+ baseChars = string->characters();
+ inlineTextBox = baseInline;
+ start = inlineTextBox->start();
+ finalNode:
+ findState.mEndResult = 0;
+ } while (true);
+tryNextCheckType:
+ node = textNode;
+ baseInline = saveInline;
+ string = textNode->dataImpl();
+ baseChars = string->characters();
+ }
+ if (foundBetter) {
+ CachedNodeType temp = *type;
+ switch (temp) {
+ case ADDRESS_CACHEDNODETYPE: {
+ static const char geoString[] = "geo:0,0?q=";
+ exported->insert(String(geoString), 0);
+ int index = sizeof(geoString) - 1;
+ String escapedComma("%2C");
+ while ((index = exported->find(',', index)) >= 0)
+ exported->replace(index, 1, escapedComma);
+ } break;
+ case EMAIL_CACHEDNODETYPE: {
+ String encoded = WebCore::encodeWithURLEscapeSequences(*exported);
+ exported->swap(encoded);
+ exported->insert(WTF::String("mailto:"), 0);
+ } break;
+ case PHONE_CACHEDNODETYPE:
+ exported->insert(WTF::String("tel:"), 0);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+noTextMatch:
+ walk->reset();
+ return false;
+}
+
+bool CacheBuilder::IsMailboxChar(UChar ch)
+{
+ // According to http://en.wikipedia.org/wiki/Email_address
+ // ! # $ % & ' * + - . / 0-9 = ?
+ // A-Z ^ _
+ // ` a-z { | } ~
+ static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff};
+ ch -= 0x20;
+ if (ch > '~' - 0x20)
+ return false;
+ return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
+}
+
+bool CacheBuilder::setData(CachedFrame* cachedFrame)
+{
+ Frame* frame = FrameAnd(this);
+ Document* doc = frame->document();
+ if (doc == NULL)
+ return false;
+ RenderObject* renderer = doc->renderer();
+ if (renderer == NULL)
+ return false;
+ RenderLayer* layer = renderer->enclosingLayer();
+ if (layer == NULL)
+ return false;
+ if (!frame->view())
+ return false;
+ int x, y;
+ GetGlobalOffset(frame, &x, &y);
+ WebCore::IntRect viewBounds = frame->view()->platformWidget()->getBounds();
+ if ((x | y) != 0)
+ viewBounds.setLocation(WebCore::IntPoint(x, y));
+ cachedFrame->setLocalViewBounds(viewBounds);
+ cachedFrame->setContentsSize(layer->scrollWidth(), layer->scrollHeight());
+ if (cachedFrame->childCount() == 0)
+ return true;
+ CachedFrame* lastCachedFrame = cachedFrame->lastChild();
+ cachedFrame = cachedFrame->firstChild();
+ do {
+ CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer());
+ cacheBuilder->setData(cachedFrame);
+ } while (cachedFrame++ != lastCachedFrame);
+ return true;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker,
+ RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY)
+{
+ RenderLayer* layer = nodeRenderer->enclosingLayer();
+ RenderLayerBacking* back = layer->backing();
+ if (!back)
+ return;
+ GraphicsLayer* grLayer = back->graphicsLayer();
+ if (back->hasContentsLayer())
+ grLayer = back->foregroundLayer();
+ if (!grLayer)
+ return;
+ LayerAndroid* aLayer = grLayer->platformLayer();
+ if (!aLayer)
+ return;
+ IntPoint scroll(layer->scrollXOffset(), layer->scrollYOffset());
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ // If this is an overflow element, track the content layer.
+ if (layer->hasOverflowScroll() && aLayer->getChild(0))
+ aLayer = aLayer->getChild(0)->getChild(0);
+ if (!aLayer)
+ return;
+ // Prevent a crash when scrolling a layer that does not have a parent.
+ if (layer->stackingContext())
+ layer->scrollToOffset(0, 0);
+#endif
+ layerTracker.grow(layerTracker.size() + 1);
+ LayerTracker& indexTracker = layerTracker.last();
+ indexTracker.mLayer = aLayer;
+ indexTracker.mRenderLayer = layer;
+ indexTracker.mBounds = enclosingIntRect(aLayer->bounds());
+ // Use the absolute location of the layer as the bounds location. This
+ // provides the original offset of nodes in the layer so that we can
+ // translate nodes between their original location and the layer's new
+ // location.
+ indexTracker.mBounds.setLocation(layer->absoluteBoundingBox().location());
+ indexTracker.mBounds.move(offsetX, offsetY);
+ indexTracker.mScroll = scroll;
+ indexTracker.mLastChild = OneAfter(lastChild);
+ DBG_NAV_LOGD("layer=%p [%d] bounds=(%d,%d,w=%d,h=%d)", aLayer,
+ aLayer->uniqueId(), indexTracker.mBounds.x(), indexTracker.mBounds.y(),
+ indexTracker.mBounds.width(), indexTracker.mBounds.height());
+}
+#endif
+
+bool CacheBuilder::validNode(Frame* startFrame, void* matchFrame,
+ void* matchNode)
+{
+ if (matchFrame == startFrame) {
+ if (matchNode == NULL)
+ return true;
+ Node* node = startFrame->document();
+ while (node != NULL) {
+ if (node == matchNode) {
+ const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ?
+ getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect();
+ // Consider nodes with empty rects that are not at the origin
+ // to be valid, since news.google.com has valid nodes like this
+ if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty())
+ return false;
+ return true;
+ }
+ node = node->traverseNextNode();
+ }
+ DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode);
+ return false;
+ }
+ Frame* child = startFrame->tree()->firstChild();
+ while (child) {
+ bool result = validNode(child, matchFrame, matchNode);
+ if (result)
+ return result;
+ child = child->tree()->nextSibling();
+ }
+#if DEBUG_NAV_UI
+ if (startFrame->tree()->parent() == NULL)
+ DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode);
+#endif
+ return false;
+}
+
+static int Area(const IntRect& rect)
+{
+ return rect.width() * rect.height();
+}
+
+bool CacheBuilder::AddPartRect(IntRect& bounds, int x, int y,
+ WTF::Vector<IntRect>* result, IntRect* focusBounds)
+{
+ if (bounds.isEmpty())
+ return true;
+ bounds.move(x, y);
+ if (bounds.maxX() <= 0 || bounds.maxY() <= 0)
+ return true;
+ IntRect* work = result->begin() - 1;
+ IntRect* end = result->end();
+ while (++work < end) {
+ if (work->contains(bounds))
+ return true;
+ if (bounds.contains(*work)) {
+ *work = bounds;
+ focusBounds->unite(bounds);
+ return true;
+ }
+ if ((bounds.x() != work->x() || bounds.width() != work->width()) &&
+ (bounds.y() != work->y() || bounds.height() != work->height()))
+ continue;
+ IntRect test = *work;
+ test.unite(bounds);
+ if (Area(test) > Area(*work) + Area(bounds))
+ continue;
+ *work = test;
+ focusBounds->unite(bounds);
+ return true;
+ }
+ if (result->size() >= MAXIMUM_FOCUS_RING_COUNT)
+ return false;
+ result->append(bounds);
+ if (focusBounds->isEmpty())
+ *focusBounds = bounds;
+ else
+ focusBounds->unite(bounds);
+ return true;
+}
+
+bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds,
+ IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result,
+ int* imageCountPtr)
+{
+ WTF::Vector<ClipColumnTracker> clipTracker(1);
+ ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel
+ bzero(baseTracker, sizeof(ClipColumnTracker));
+ if (node->hasChildNodes() && node->hasTagName(HTMLNames::buttonTag) == false
+ && node->hasTagName(HTMLNames::selectTag) == false) {
+ // collect all text rects from first to last child
+ Node* test = node->firstChild();
+ Node* last = NULL;
+ Node* prior = node;
+ while ((prior = prior->lastChild()) != NULL)
+ last = prior;
+ ASSERT(last != NULL);
+ bool nodeIsAnchor = node->hasTagName(HTMLNames::aTag);
+ do {
+ do {
+ const ClipColumnTracker* lastClip = &clipTracker.last();
+ if (test != lastClip->mLastChild)
+ break;
+ clipTracker.removeLast();
+ } while (true);
+ RenderObject* renderer = test->renderer();
+ if (renderer == NULL)
+ continue;
+ EVisibility vis = renderer->style()->visibility();
+ if (vis == HIDDEN)
+ continue;
+ bool hasClip = renderer->hasOverflowClip();
+ size_t clipIndex = clipTracker.size();
+ IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX);
+ if (hasClip || --clipIndex > 0) {
+ clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() :
+ clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect
+ }
+ if (test->isTextNode()) {
+ RenderText* renderText = (RenderText*) renderer;
+ InlineTextBox *textBox = renderText->firstTextBox();
+ if (textBox == NULL)
+ continue;
+ if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX,
+ x, y, focusBounds, clipBounds, result) == false) {
+ return false;
+ }
+ continue;
+ }
+ if (test->hasTagName(HTMLNames::imgTag)) {
+ IntRect bounds = test->getRect();
+ bounds.intersect(clipBounds);
+ if (AddPartRect(bounds, x, y, result, focusBounds) == false)
+ return false;
+ *imageCountPtr += 1;
+ continue;
+ }
+ if (hasClip == false) {
+ if (nodeIsAnchor && test->hasTagName(HTMLNames::divTag)) {
+ IntRect bounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by AddPartRect
+ RenderBox* renderBox = static_cast<RenderBox*>(renderer);
+ int left = bounds.x() + renderBox->paddingLeft() + renderBox->borderLeft();
+ int top = bounds.y() + renderBox->paddingTop() + renderBox->borderTop();
+ int right = bounds.maxX() - renderBox->paddingRight() - renderBox->borderRight();
+ int bottom = bounds.maxY() - renderBox->paddingBottom() - renderBox->borderBottom();
+ if (left >= right || top >= bottom)
+ continue;
+ bounds = IntRect(left, top, right - left, bottom - top);
+ if (AddPartRect(bounds, x, y, result, focusBounds) == false)
+ return false;
+ }
+ continue;
+ }
+ Node* lastChild = test->lastChild();
+ if (lastChild == NULL)
+ continue;
+ clipTracker.grow(clipTracker.size() + 1);
+ ClipColumnTracker& clip = clipTracker.last();
+ clip.mBounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by ConstructTextRect
+ clip.mLastChild = OneAfter(lastChild);
+ clip.mNode = test;
+ } while (test != last && (test = test->traverseNextNode()) != NULL);
+ }
+ if (result->size() == 0 || focusBounds->width() < MINIMUM_FOCUSABLE_WIDTH
+ || focusBounds->height() < MINIMUM_FOCUSABLE_HEIGHT) {
+ if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH)
+ return false;
+ if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT)
+ return false;
+ result->append(bounds);
+ *focusBounds = bounds;
+ }
+ return true;
+}
+
+static inline bool isNotSpace(UChar c)
+{
+ return c <= 0xA0 ? isUnicodeSpace(c) == false :
+ WTF::Unicode::direction(c) != WTF::Unicode::WhiteSpaceNeutral;
+}
+
+bool CacheBuilder::ConstructTextRect(Text* textNode,
+ InlineTextBox* textBox, int start, int relEnd, int x, int y,
+ IntRect* focusBounds, const IntRect& clipBounds, WTF::Vector<IntRect>* result)
+{
+ RenderText* renderText = (RenderText*) textNode->renderer();
+ EVisibility vis = renderText->style()->visibility();
+ StringImpl* string = textNode->dataImpl();
+ const UChar* chars = string->characters();
+ FloatPoint pt = renderText->localToAbsolute();
+ do {
+ int textBoxStart = textBox->start();
+ int textBoxEnd = textBoxStart + textBox->len();
+ if (textBoxEnd <= start)
+ continue;
+ if (textBoxEnd > relEnd)
+ textBoxEnd = relEnd;
+ IntRect bounds = textBox->selectionRect((int) pt.x(), (int) pt.y(),
+ start, textBoxEnd);
+ bounds.intersect(clipBounds);
+ if (bounds.isEmpty())
+ continue;
+ bool drawable = false;
+ for (int index = start; index < textBoxEnd; index++)
+ if ((drawable |= isNotSpace(chars[index])) != false)
+ break;
+ if (drawable && vis != HIDDEN) {
+ if (AddPartRect(bounds, x, y, result, focusBounds) == false)
+ return false;
+ }
+ if (textBoxEnd == relEnd)
+ break;
+ } while ((textBox = textBox->nextTextBox()) != NULL);
+ return true;
+}
+
+bool CacheBuilder::ConstructTextRects(Text* node, int start,
+ Text* last, int end, int x, int y, IntRect* focusBounds,
+ const IntRect& clipBounds, WTF::Vector<IntRect>* result)
+{
+ result->clear();
+ *focusBounds = IntRect(0, 0, 0, 0);
+ do {
+ RenderText* renderText = (RenderText*) node->renderer();
+ int relEnd = node == last ? end : renderText->textLength();
+ InlineTextBox *textBox = renderText->firstTextBox();
+ if (textBox != NULL) {
+ do {
+ if ((int) textBox->end() >= start)
+ break;
+ } while ((textBox = textBox->nextTextBox()) != NULL);
+ if (textBox && ConstructTextRect(node, textBox, start, relEnd,
+ x, y, focusBounds, clipBounds, result) == false)
+ return false;
+ }
+ start = 0;
+ do {
+ if (node == last)
+ return true;
+ node = (Text*) node->traverseNextNode();
+ ASSERT(node != NULL);
+ } while (node->isTextNode() == false || node->renderer() == NULL);
+ } while (true);
+}
+
+}
diff --git a/Source/WebKit/android/nav/CacheBuilder.h b/Source/WebKit/android/nav/CacheBuilder.h
new file mode 100644
index 0000000..8f1cc72
--- /dev/null
+++ b/Source/WebKit/android/nav/CacheBuilder.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2006, 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 CacheBuilder_h
+#define CacheBuilder_h
+
+#include "CachedDebug.h"
+#include "CachedNodeType.h"
+#include "IntRect.h"
+#include "PlatformString.h"
+#include "TextDirection.h"
+#include <wtf/Forward.h>
+#include <wtf/Vector.h>
+
+#define NAVIGATION_MAX_PHONE_LENGTH 14
+
+using namespace WebCore;
+
+namespace WebCore {
+
+class ColumnInfo;
+class Document;
+class Frame;
+class HTMLAreaElement;
+class InlineTextBox;
+class LayerAndroid;
+class Node;
+class PlatformGraphicsContext;
+class RenderBlock;
+class RenderFlow;
+class RenderLayer;
+class RenderObject;
+class Text;
+
+}
+
+namespace android {
+
+class CachedFrame;
+class CachedNode;
+class CachedRoot;
+
+class CacheBuilder {
+public:
+ enum Direction {
+ UNINITIALIZED = -1,
+ LEFT,
+ RIGHT,
+ UP,
+ DOWN,
+ DIRECTION_COUNT,
+ UP_DOWN = UP & DOWN, // mask and result
+ RIGHT_DOWN = RIGHT & DOWN, // mask and result
+ };
+ enum FoundState {
+ FOUND_NONE,
+ FOUND_PARTIAL,
+ FOUND_COMPLETE
+ };
+ CacheBuilder();
+ void allowAllTextDetection() { mAllowableTypes = ALL_CACHEDNODE_BITS; }
+ void buildCache(CachedRoot* root);
+ static bool ConstructPartRects(Node* node, const IntRect& bounds,
+ IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result,
+ int* imageCountPtr);
+ Node* currentFocus() const;
+ void disallowAddressDetection() { mAllowableTypes = (CachedNodeBits) (
+ mAllowableTypes & ~ADDRESS_CACHEDNODE_BIT); }
+ void disallowEmailDetection() { mAllowableTypes = (CachedNodeBits) (
+ mAllowableTypes & ~EMAIL_CACHEDNODE_BIT); }
+ void disallowPhoneDetection() { mAllowableTypes = (CachedNodeBits) (
+ mAllowableTypes & ~PHONE_CACHEDNODE_BIT); }
+ static FoundState FindAddress(const UChar* , unsigned length, int* start,
+ int* end, bool caseInsensitive);
+ static IntRect getAreaRect(const HTMLAreaElement* area);
+ static void GetGlobalOffset(Frame* , int* x, int * y);
+ static void GetGlobalOffset(Node* , int* x, int * y);
+ bool pictureSetDisabled() { return mPictureSetDisabled; }
+ static bool validNode(Frame* startFrame, void* framePtr, void* nodePtr);
+private:
+ enum AddressProgress {
+ NO_ADDRESS,
+ SKIP_TO_SPACE,
+ HOUSE_NUMBER,
+ NUMBER_TRAILING_SPACE,
+ ADDRESS_LINE,
+ STATE_NAME,
+ SECOND_HALF,
+ ZIP_CODE,
+ PLUS_4,
+ FIND_STREET
+ };
+ struct NodeWalk {
+ NodeWalk() { reset(); }
+ int mStart;
+ int mEnd;
+ Node* mFinalNode;
+ InlineTextBox* mLastInline;
+ bool mMore;
+ void reset() { mMore = false; }
+ };
+ struct BoundsPart {
+ IntRect mRect;
+ int mStart;
+ int mEnd;
+ };
+ struct Bounds {
+ typedef bool (*FindText)(BoundsPart* result, InlineTextBox* , const String& match);
+ IntRect mNodeBounds;
+ BoundsPart mPart;
+ WTF::Vector<BoundsPart> mParts;
+ char mStore[NAVIGATION_MAX_PHONE_LENGTH + 1];
+ int mPartIndex;
+ Node* mNode;
+ Node* mFinalNode;
+ void reset() { mNode = NULL; }
+ };
+ struct FindState {
+ int mStartResult;
+ int mEndResult;
+ const UChar* mCurrentStart;
+ const UChar* mEnd;
+ AddressProgress mProgress;
+ int mNumberCount;
+ int mLetterCount;
+ unsigned mWordCount;
+ int mLineCount;
+ const UChar* mFirstLower;
+ const UChar* mZipStart;
+ const UChar* mBases[16]; // FIXME: random guess, maybe too small, maybe too big
+ const UChar* mWords[16];
+ const UChar* mEnds[16];
+ const UChar* mStarts[16]; // text is not necessarily contiguous
+ const char* mStates;
+ int mEndWord;
+ int mStateWord;
+ int mZipHint;
+ int mSectionLength;
+ unsigned mNumberWords; // must contain as many bits as mWords contains elements
+ char* mPattern;
+ UChar mStore[NAVIGATION_MAX_PHONE_LENGTH + 1];
+ UChar* mStorePtr;
+ UChar mBackOne;
+ UChar mBackTwo;
+ UChar mCurrent;
+ bool mUnparsed;
+ bool mZipDelimiter;
+ bool mOpenParen;
+ bool mInitialized;
+ bool mContinuationNode;
+ bool mCaseInsensitive;
+ void shiftWords(int shift) {
+ memmove(mBases, &mBases[shift], (sizeof(mBases) /
+ sizeof(mBases[0]) - shift) * sizeof(mBases[0]));
+ memmove(mWords, &mWords[shift], (sizeof(mWords) /
+ sizeof(mWords[0]) - shift) * sizeof(mWords[0]));
+ memmove(mEnds, &mEnds[shift], (sizeof(mEnds) /
+ sizeof(mEnds[0]) - shift) * sizeof(mEnds[0]));
+ memmove(mStarts, &mStarts[shift], (sizeof(mStarts) /
+ sizeof(mStarts[0]) - shift) * sizeof(mStarts[0]));
+ }
+ void newWord(const UChar* baseChars, const UChar* chars) {
+ mBases[mWordCount] = baseChars;
+ mWords[mWordCount] = chars;
+ mEnds[mWordCount] = mEnd;
+ mStarts[mWordCount] = mCurrentStart;
+ }
+ };
+ struct Tracker {
+ Node* mLastChild;
+ };
+ struct ClipColumnTracker : Tracker {
+ Node* mNode;
+ IntRect mBounds;
+ ColumnInfo* mColumnInfo;
+ int mColumnGap;
+ TextDirection mDirection;
+ bool mHasClip;
+ };
+ struct LayerTracker : Tracker {
+ LayerAndroid* mLayer;
+ RenderLayer* mRenderLayer;
+ IntRect mBounds;
+ IntPoint mScroll;
+ ~LayerTracker();
+ };
+ struct TabIndexTracker : Tracker {
+ int mTabIndex;
+ };
+ struct FocusTracker : TabIndexTracker {
+ int mCachedNodeIndex;
+ bool mSomeParentTakesFocus;
+ };
+ void adjustForColumns(const ClipColumnTracker& track,
+ CachedNode* node, IntRect* bounds, RenderBlock*);
+ static bool AddPartRect(IntRect& bounds, int x, int y,
+ WTF::Vector<IntRect>* result, IntRect* focusBounds);
+ static bool AnyIsClick(Node* node);
+ static bool AnyChildIsClick(Node* node);
+ static bool NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length);
+ void BuildFrame(Frame* root, Frame* frame,
+ CachedRoot* cachedRoot, CachedFrame* cachedFrame);
+ bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame,
+ const FocusTracker* last, int lastChildIndex);
+ static bool ConstructTextRect(Text* textNode,
+ InlineTextBox* textBox, int start, int relEnd, int x, int y,
+ IntRect* focusBounds, const IntRect& clip, WTF::Vector<IntRect>* result);
+ static bool ConstructTextRects(Text* node, int start,
+ Text* last, int end, int x, int y, IntRect* focusBounds,
+ const IntRect& clip, WTF::Vector<IntRect>* result);
+ static FoundState FindPartialAddress(const UChar* , const UChar* , unsigned length, FindState* );
+ static FoundState FindPartialEMail(const UChar* , unsigned length, FindState* );
+ static FoundState FindPartialNumber(const UChar* , unsigned length, FindState* );
+ static FoundState FindPhoneNumber(const UChar* chars, unsigned length, int* start, int* end);
+ static void FindReset(FindState* );
+ static void FindResetNumber(FindState* );
+ static Frame* FrameAnd(CacheBuilder* focusNav);
+ static Frame* FrameAnd(const CacheBuilder* focusNav);
+ static CacheBuilder* Builder(Frame* );
+ static Frame* HasFrame(Node* );
+ static bool HasOverOrOut(Node* );
+ static bool HasTriggerEvent(Node* );
+ static bool IsDomainChar(UChar ch);
+ bool isFocusableText(NodeWalk* , bool oldMore, Node* , CachedNodeType* type,
+ String* exported) const; //returns true if it is focusable
+ static bool IsMailboxChar(UChar ch);
+ static bool IsRealNode(Frame* , Node* );
+ int overlap(int left, int right); // returns distance scale factor as 16.16 scalar
+ bool setData(CachedFrame* );
+#if USE(ACCELERATED_COMPOSITING)
+ void TrackLayer(WTF::Vector<LayerTracker>& layerTracker,
+ RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY);
+#endif
+ Node* tryFocus(Direction direction);
+ Node* trySegment(Direction direction, int mainStart, int mainEnd);
+ CachedNodeBits mAllowableTypes;
+ bool mPictureSetDisabled;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ void frameName(char*& namePtr, const char* max) const;
+ void init(char* buffer, size_t size);
+ static int ParentIndex(Node* node, int count, Node* parent);
+ void print() { frames(); }
+ void print(const char* name);
+ void wideString(const String& str);
+private:
+ void attr(const AtomicString& name, const AtomicString& value);
+ void comma(const char* str);
+ void flush();
+ Frame* frameAnd() const;
+ void frames();
+ void groups();
+ bool isFocusable(Node* node);
+ void localName(Node* node);
+ void newLine(int indent = 0);
+ void print(const char* name, unsigned len);
+ void setIndent(int );
+ void uChar(const UChar* name, unsigned len, bool hex);
+ void validateFrame();
+ void validateStringData();
+ void wideString(const UChar* chars, int length, bool hex);
+ char* mBuffer;
+ size_t mBufferSize;
+ int mIndex;
+ const char* mPrefix;
+ int mMinPrefix;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedColor.cpp b/Source/WebKit/android/nav/CachedColor.cpp
new file mode 100644
index 0000000..c610022
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedColor.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "CachedColor.h"
+
+namespace android {
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_COLOR(field) \
+ DUMP_NAV_LOGD("// SkColor " #field "=0x%08x;\n", b->field)
+
+CachedColor* CachedColor::Debug::base() const {
+ CachedColor* nav = (CachedColor*) ((char*) this - OFFSETOF(CachedColor, mDebug));
+ return nav;
+}
+
+void CachedColor::Debug::print() const
+{
+ CachedColor* b = base();
+ DEBUG_PRINT_COLOR(mFillColor);
+ DUMP_NAV_LOGD("// int mInnerWidth=%d;\n", b->mInnerWidth);
+ DUMP_NAV_LOGD("// int mOuterWidth=%d;\n", b->mOuterWidth);
+ DUMP_NAV_LOGD("// int mOutset=%d;\n", b->mOutset);
+ DEBUG_PRINT_COLOR(mPressedInnerColor);
+ DEBUG_PRINT_COLOR(mPressedOuterColor);
+ DUMP_NAV_LOGD("// int mRadius=%d;\n", b->mRadius);
+ DEBUG_PRINT_COLOR(mSelectedInnerColor);
+ DEBUG_PRINT_COLOR(mSelectedOuterColor);
+}
+
+#endif
+
+}
+
diff --git a/Source/WebKit/android/nav/CachedColor.h b/Source/WebKit/android/nav/CachedColor.h
new file mode 100644
index 0000000..2ba9b18
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedColor.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CachedColor_h
+#define CachedColor_h
+
+#include "CachedDebug.h"
+#include "Color.h"
+#include "Length.h"
+#include "SkColor.h"
+
+using namespace WebCore;
+
+namespace android {
+
+class CachedColor {
+public:
+ CachedColor() {
+ // Initiaized to 0 in its array, so nothing to do in the
+ // constructor
+ }
+ bool operator==(const CachedColor& o) const {
+ return memcmp(&o, this, sizeof(this)) == 0; }
+ SkColor fillColor() const { return mFillColor; }
+ void init();
+ int innerWidth() const { return mInnerWidth; }
+ int outerWidth() const { return mOuterWidth; }
+ int outset() const { return mOutset; }
+ SkColor pressedInnerColor() const { return mPressedInnerColor; }
+ SkColor pressedOuterColor() const { return mPressedOuterColor; }
+ int radius() const { return mRadius; }
+ SkColor selectedInnerColor() const { return mSelectedInnerColor; }
+ SkColor selectedOuterColor() const { return mSelectedOuterColor; }
+ void setFillColor(const Color& c) { mFillColor = c.rgb(); }
+ void setInnerWidth(Length l) { mInnerWidth = l.value(); }
+ void setOuterWidth(Length l) { mOuterWidth = l.value(); }
+ void setOutset(Length l) { mOutset = l.value(); }
+ void setPressedInnerColor(const Color& c) { mPressedInnerColor = c.rgb(); }
+ void setPressedOuterColor(const Color& c) { mPressedOuterColor = c.rgb(); }
+ void setRadius(Length l) { mRadius = l.value(); }
+ void setSelectedInnerColor(const Color& c) { mSelectedInnerColor = c.rgb(); }
+ void setSelectedOuterColor(const Color& c) { mSelectedOuterColor = c.rgb(); }
+private:
+ SkColor mFillColor;
+ int mInnerWidth;
+ int mOuterWidth;
+ int mOutset;
+ SkColor mPressedInnerColor;
+ SkColor mPressedOuterColor;
+ int mRadius;
+ SkColor mSelectedInnerColor;
+ SkColor mSelectedOuterColor;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedColor* base() const;
+ void print() const;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedDebug.h b/Source/WebKit/android/nav/CachedDebug.h
new file mode 100644
index 0000000..f77c07a
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedDebug.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007, 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 CachedDebug_h
+#define CachedDebug_h
+
+#define DUMP_NAV_CACHE 0
+#define DEBUG_NAV_UI 0
+#define DEBUG_NAV_UI_VERBOSE 0
+
+#if DEBUG_NAV_UI
+#define DBG_NAV_LOG(message) LOGD("%s %s", __FUNCTION__, message)
+#define DBG_NAV_LOGD(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#define DEBUG_NAV_UI_LOGD(...) LOGD(__VA_ARGS__)
+#else
+#define DBG_NAV_LOG(message) ((void)0)
+#define DBG_NAV_LOGD(format, ...) ((void)0)
+#define DEBUG_NAV_UI_LOGD(...) ((void)0)
+#endif
+
+#if DEBUG_NAV_UI_VERBOSE
+#define DBG_NAV_LOGV(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#else
+#define DBG_NAV_LOGV(format, ...) ((void)0)
+#endif
+
+#if DUMP_NAV_CACHE != 0 && !defined DUMP_NAV_CACHE_USING_PRINTF && defined NDEBUG
+#define DUMP_NAV_CACHE_USING_PRINTF
+#endif
+
+#if DUMP_NAV_CACHE
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+#include <stdio.h>
+extern FILE* gNavCacheLogFile;
+#define NAV_CACHE_LOG_FILE "/data/data/com.android.browser/navlog"
+#define DUMP_NAV_LOGD(...) do { if (gNavCacheLogFile) \
+ fprintf(gNavCacheLogFile, __VA_ARGS__); else LOGD(__VA_ARGS__); } while (false)
+#define DUMP_NAV_LOGX(format, ...) do { if (gNavCacheLogFile) \
+ fprintf(gNavCacheLogFile, format, __VA_ARGS__); \
+ else LOGD("%s " format, __FUNCTION__, __VA_ARGS__); } while (false)
+#else
+#define DUMP_NAV_LOGD(...) LOGD(__VA_ARGS__)
+#define DUMP_NAV_LOGX(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#endif
+#else
+#define DUMP_NAV_LOGD(...) ((void)0)
+#define DUMP_NAV_LOGX(...) ((void)0)
+#endif
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedFrame.cpp b/Source/WebKit/android/nav/CachedFrame.cpp
new file mode 100644
index 0000000..c51944e
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedFrame.cpp
@@ -0,0 +1,1511 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "CachedHistory.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "LayerAndroid.h"
+
+#include "CachedFrame.h"
+
+#define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning
+
+#define MIN_OVERLAP 3 // if rects overlap by 2 pixels or fewer, treat them as non-intersecting
+
+namespace android {
+
+WebCore::IntRect CachedFrame::adjustBounds(const CachedNode* node,
+ const WebCore::IntRect& rect) const
+{
+ DBG_NAV_LOGV("node=%p [%d] rect=(%d,%d,w=%d,h=%d) view=(%d,%d,w=%d,h=%d)"
+ " local=(%d,%d,w=%d,h=%d) root=(%d,%d,w=%d,h=%d)",
+ node, node->index(), rect.x(), rect.y(), rect.width(), rect.height(),
+ mViewBounds.x(), mViewBounds.y(),
+ mViewBounds.width(), mViewBounds.height(),
+ mLocalViewBounds.x(), mLocalViewBounds.y(),
+ mLocalViewBounds.width(), mLocalViewBounds.height(),
+ mRoot->mViewBounds.x(), mRoot->mViewBounds.y(),
+ mRoot->mViewBounds.width(), mRoot->mViewBounds.height());
+#if USE(ACCELERATED_COMPOSITING)
+ if (!mRoot)
+ return rect;
+
+ const CachedLayer* cachedLayer = layer(node);
+ if (!cachedLayer)
+ return rect;
+
+ const WebCore::LayerAndroid* rootLayer = mRoot->rootLayer();
+ const LayerAndroid* aLayer = cachedLayer->layer(rootLayer);
+ if (aLayer)
+ return cachedLayer->adjustBounds(rootLayer, rect);
+#endif
+ return rect;
+}
+
+bool CachedFrame::CheckBetween(Direction direction, const WebCore::IntRect& bestRect,
+ const WebCore::IntRect& prior, WebCore::IntRect* result)
+{
+ int left, top, width, height;
+ if (direction & UP_DOWN) {
+ top = direction == UP ? bestRect.maxY() : prior.maxY();
+ int bottom = direction == UP ? prior.y() : bestRect.y();
+ height = bottom - top;
+ if (height < 0)
+ return false;
+ left = prior.x();
+ int testLeft = bestRect.x();
+ if (left > testLeft)
+ left = testLeft;
+ int right = prior.maxX();
+ int testRight = bestRect.maxX();
+ if (right < testRight)
+ right = testRight;
+ width = right - left;
+ } else {
+ left = direction == LEFT ? bestRect.maxX() : prior.maxX();
+ int right = direction == LEFT ? prior.x() : bestRect.x();
+ width = right - left;
+ if (width < 0)
+ return false;
+ top = prior.y();
+ int testTop = bestRect.y();
+ if (top > testTop)
+ top = testTop;
+ int bottom = prior.maxY();
+ int testBottom = bestRect.maxY();
+ if (bottom < testBottom)
+ bottom = testBottom;
+ height = bottom - top;
+ }
+ *result = WebCore::IntRect(left, top, width, height);
+ return true;
+}
+
+bool CachedFrame::checkBetween(BestData* best, Direction direction)
+{
+ const WebCore::IntRect& bestRect = best->bounds();
+ BestData test;
+ test.mDistance = INT_MAX;
+ test.mNode = NULL;
+ int index = direction;
+ int limit = index + DIRECTION_COUNT;
+ do {
+ WebCore::IntRect edges;
+ Direction check = (Direction) (index & DIRECTION_MASK);
+ if (CheckBetween(check, bestRect,
+ history()->priorBounds(), &edges) == false)
+ continue;
+ WebCore::IntRect clip = mRoot->scrolledBounds();
+ clip.intersect(edges);
+ if (clip.isEmpty())
+ continue;
+ findClosest(&test, direction, check, &clip);
+ if (test.mNode == NULL)
+ continue;
+ if (direction == check)
+ break;
+ } while (++index < limit);
+ if (test.mNode == NULL)
+ return false;
+ *best = test;
+ return true;
+}
+
+bool CachedFrame::checkRings(const CachedNode* node,
+ const WebCore::IntRect& testBounds) const
+{
+ return mRoot->checkRings(picture(node), node, testBounds);
+}
+
+bool CachedFrame::checkVisited(const CachedNode* node, Direction direction) const
+{
+ return history()->checkVisited(node, direction);
+}
+
+void CachedFrame::clearCursor()
+{
+ DBG_NAV_LOGD("mCursorIndex=%d", mCursorIndex);
+ if (mCursorIndex < CURSOR_SET)
+ return;
+ CachedNode& cursor = mCachedNodes[mCursorIndex];
+ cursor.clearCursor(this);
+ mCursorIndex = CURSOR_CLEARED; // initialized and explicitly cleared
+}
+
+// returns 0 if test is preferable to best, 1 if not preferable, or -1 if unknown
+int CachedFrame::compare(BestData& testData, const BestData& bestData) const
+{
+ if (testData.mNode->tabIndex() != bestData.mNode->tabIndex()) {
+ if (testData.mNode->tabIndex() < bestData.mNode->tabIndex()
+ || (mRoot->mCursor && mRoot->mCursor->tabIndex() < bestData.mNode->tabIndex())) {
+ testData.mNode->setCondition(CachedNode::HIGHER_TAB_INDEX);
+ return REJECT_TEST;
+ }
+ return TEST_IS_BEST;
+ }
+ // if the test minor axis line intersects the line segment between cursor
+ // center and best center, choose it
+ // give more weight to exact major axis alignment (rows, columns)
+ if (testData.mInNav != bestData.mInNav) {
+ if (bestData.mInNav) {
+ testData.mNode->setCondition(CachedNode::IN_CURSOR);
+ return REJECT_TEST;
+ }
+ return TEST_IS_BEST;
+ }
+ if (testData.mInNav) {
+ if (bestData.mMajorDelta < testData.mMajorDelta) {
+ testData.mNode->setCondition(CachedNode::CLOSER_IN_CURSOR);
+ return REJECT_TEST;
+ }
+ if (testData.mMajorDelta < bestData.mMajorDelta)
+ return TEST_IS_BEST;
+ }
+ if (testData.mMajorDelta < 0 && bestData.mMajorDelta >= 0) {
+ testData.mNode->setCondition(CachedNode::FURTHER);
+ return REJECT_TEST;
+ }
+ if ((testData.mMajorDelta ^ bestData.mMajorDelta) < 0) // one above, one below (or one left, one right)
+ return TEST_IS_BEST;
+ bool bestInWorking = bestData.inOrSubsumesWorking();
+ bool testInWorking = testData.inOrSubsumesWorking();
+ if (bestInWorking && testData.mWorkingOutside && testData.mNavOutside) {
+ testData.mNode->setCondition(CachedNode::IN_WORKING);
+ return REJECT_TEST;
+ }
+ if (testInWorking && bestData.mWorkingOutside && bestData.mNavOutside)
+ return TEST_IS_BEST;
+ bool bestInNav = directionChange() && bestData.inOrSubsumesNav();
+ bool testInNav = directionChange() && testData.inOrSubsumesNav();
+ if (bestInWorking == false && testInWorking == false) {
+ if (bestInNav && testData.mNavOutside) {
+ testData.mNode->setCondition(CachedNode::IN_UMBRA);
+ return REJECT_TEST;
+ }
+ if (testInNav && bestData.mNavOutside)
+ return TEST_IS_BEST;
+ }
+#if 01 // hopefully butt test will remove need for this
+ if (testData.mCursorChild != bestData.mCursorChild) {
+ if (bestData.mCursorChild) {
+ testData.mNode->setCondition(CachedNode::IN_CURSOR_CHILDREN);
+ return REJECT_TEST;
+ }
+ return TEST_IS_BEST;
+ }
+#endif
+ bool bestTestIn = (bestInWorking || bestInNav) && (testInWorking || testInNav);
+ bool testOverlap = bestTestIn || (testData.mWorkingOverlap != 0 && bestData.mWorkingOverlap == 0);
+ bool bestOverlap = bestTestIn || (testData.mWorkingOverlap == 0 && bestData.mWorkingOverlap != 0);
+#if 01 // this isn't working?
+ if (testOverlap == bestOverlap) {
+ if (bestData.mMajorButt < 10 && testData.mMajorButt >= 40) {
+ testData.mNode->setCondition(CachedNode::BUTTED_UP);
+ return REJECT_TEST;
+ }
+ if (testData.mMajorButt < 10 && bestData.mMajorButt >= 40)
+ return TEST_IS_BEST;
+ }
+#endif
+ if (bestOverlap && bestData.mMajorDelta < testData.mMajorDelta) { // choose closest major axis center
+ testData.mNode->setCondition(CachedNode::CLOSER);
+ return REJECT_TEST;
+ }
+ if (testOverlap && testData.mMajorDelta < bestData.mMajorDelta)
+ return TEST_IS_BEST;
+ if (bestOverlap && bestData.mMajorDelta2 < testData.mMajorDelta2) {
+ testData.mNode->setCondition(CachedNode::CLOSER_TOP);
+ return REJECT_TEST;
+ }
+ if (testOverlap && testData.mMajorDelta2 < bestData.mMajorDelta2)
+ return TEST_IS_BEST;
+#if 01
+ if (bestOverlap && ((bestData.mSideDistance <= 0 && testData.mSideDistance > 0) ||
+ abs(bestData.mSideDistance) < abs(testData.mSideDistance))) {
+ testData.mNode->setCondition(CachedNode::LEFTMOST);
+ return REJECT_TEST;
+ }
+ if (testOverlap && ((testData.mSideDistance <= 0 && bestData.mSideDistance > 0) ||
+ abs(testData.mSideDistance) < abs(bestData.mSideDistance)))
+ return TEST_IS_BEST;
+// fix me : the following ASSERT fires -- not sure if this case should be handled or not
+// ASSERT(bestOverlap == false && testOverlap == false);
+#endif
+ SkFixed testMultiplier = testData.mWorkingOverlap > testData.mNavOverlap ?
+ testData.mWorkingOverlap : testData.mNavOverlap;
+ SkFixed bestMultiplier = bestData.mWorkingOverlap > bestData.mNavOverlap ?
+ bestData.mWorkingOverlap : bestData.mNavOverlap;
+ int testDistance = testData.mDistance;
+ int bestDistance = bestData.mDistance;
+// start here;
+ // this fails if they're off by 1
+ // try once again to implement sliding scale so that off by 1 is nearly like zero,
+ // and off by a lot causes sideDistance to have little or no effect
+ // try elliptical distance -- lengthen side contribution
+ // these ASSERTs should not fire, but do fire on mail.google.com
+ // can't debug yet, won't reproduce
+ ASSERT(testDistance >= 0);
+ ASSERT(bestDistance >= 0);
+ testDistance += testDistance; // multiply by 2
+ testDistance *= testDistance;
+ bestDistance += bestDistance; // multiply by 2
+ bestDistance *= bestDistance;
+ int side = testData.mSideDistance;
+ int negative = side < 0 && bestData.mSideDistance > 0;
+ side *= side;
+ if (negative)
+ side = -side;
+ testDistance += side;
+ side = bestData.mSideDistance;
+ negative = side < 0 && testData.mSideDistance > 0;
+ side *= side;
+ if (negative)
+ side = -side;
+ bestDistance += side;
+ if (testMultiplier > (SK_Fixed1 >> 1) || bestMultiplier > (SK_Fixed1 >> 1)) { // considerable working overlap?
+ testDistance = SkFixedMul(testDistance, bestMultiplier);
+ bestDistance = SkFixedMul(bestDistance, testMultiplier);
+ }
+ if (bestDistance < testDistance) {
+ testData.mNode->setCondition(CachedNode::CLOSER_OVERLAP);
+ return REJECT_TEST;
+ }
+ if (testDistance < bestDistance)
+ return TEST_IS_BEST;
+#if 0
+ int distance = testData.mDistance + testData.mSideDistance;
+ int best = bestData.mDistance + bestData.mSideDistance;
+ if (distance > best) {
+ testData.mNode->setCondition(CachedNode::CLOSER_RAW_DISTANCE);
+ return REJECT_TEST;
+ }
+ else if (distance < best)
+ return TEST_IS_BEST;
+ best = bestData.mSideDistance;
+ if (testData.mSideDistance > best) {
+ testData.mNode->setCondition(CachedNode::SIDE_DISTANCE);
+ return REJECT_TEST;
+ }
+ if (testData.mSideDistance < best)
+ return TEST_IS_BEST;
+#endif
+ if (testData.mPreferred < bestData.mPreferred) {
+ testData.mNode->setCondition(CachedNode::PREFERRED);
+ return REJECT_TEST;
+ }
+ if (testData.mPreferred > bestData.mPreferred)
+ return TEST_IS_BEST;
+ return UNDECIDED;
+}
+
+const CachedNode* CachedFrame::currentCursor(const CachedFrame** framePtr) const
+{
+ if (framePtr)
+ *framePtr = this;
+ if (mCursorIndex < CURSOR_SET)
+ return NULL;
+ const CachedNode* result = &mCachedNodes[mCursorIndex];
+ const CachedFrame* frame = hasFrame(result);
+ if (frame != NULL)
+ return frame->currentCursor(framePtr);
+ (const_cast<CachedNode*>(result))->fixUpCursorRects(this);
+ return result;
+}
+
+const CachedNode* CachedFrame::currentFocus(const CachedFrame** framePtr) const
+{
+ if (framePtr)
+ *framePtr = this;
+ if (mFocusIndex < 0)
+ return NULL;
+ const CachedNode* result = &mCachedNodes[mFocusIndex];
+ const CachedFrame* frame = hasFrame(result);
+ if (frame != NULL)
+ return frame->currentFocus(framePtr);
+ return result;
+}
+
+bool CachedFrame::directionChange() const
+{
+ return history()->directionChange();
+}
+
+#ifdef BROWSER_DEBUG
+CachedNode* CachedFrame::find(WebCore::Node* node) // !!! probably debugging only
+{
+ for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++)
+ if (node == test->webCoreNode())
+ return test;
+ for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
+ frame++) {
+ CachedNode* result = frame->find(node);
+ if (result != NULL)
+ return result;
+ }
+ return NULL;
+}
+#endif
+
+const CachedNode* CachedFrame::findBestAt(const WebCore::IntRect& rect,
+ int* best, bool* inside, const CachedNode** directHit,
+ const CachedFrame** directHitFramePtr,
+ const CachedFrame** framePtr, int* x, int* y,
+ bool checkForHiddenStart) const
+{
+ const CachedNode* result = NULL;
+ int rectWidth = rect.width();
+ WebCore::IntPoint center = WebCore::IntPoint(rect.x() + (rectWidth >> 1),
+ rect.y() + (rect.height() >> 1));
+ mRoot->setupScrolledBounds();
+ for (const CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) {
+ if (test->disabled())
+ continue;
+ size_t parts = test->navableRects();
+ BestData testData;
+ testData.mNode = test;
+ testData.mFrame = this;
+ WebCore::IntRect bounds = test->bounds(this);
+ testData.setMouseBounds(bounds);
+ testData.setNodeBounds(bounds);
+ bool checkForHidden = checkForHiddenStart;
+ for (size_t part = 0; part < parts; part++) {
+ WebCore::IntRect testRect = test->ring(this, part);
+ if (testRect.intersects(rect)) {
+#if DEBUG_NAV_UI
+ if (test->isInLayer()) {
+ DBG_NAV_LOGD("[%d] intersects=%s testRect=(%d,%d,w=%d,h=%d)"
+ " rect=(%d,%d,w=%d,h=%d)", test->index(),
+ testRect.intersects(rect) ? "true" : "false",
+ testRect.x(), testRect.y(),
+ testRect.width(), testRect.height(),
+ rect.x(), rect.y(), rect.width(), rect.height());
+ }
+#endif
+ if (checkForHidden && mRoot->maskIfHidden(&testData) == true) {
+ DBG_NAV_LOGD("hidden [%d]", test->index());
+ break;
+ }
+ checkForHidden = false;
+ testRect.intersect(testData.mouseBounds());
+ if (testRect.contains(center)) {
+ // We have a direct hit.
+ if (*directHit == NULL) {
+ DBG_NAV_LOGD("direct hit 1 [%d]", test->index());
+ *directHit = test;
+ *directHitFramePtr = this;
+ IntRect r(center, IntSize(0, 0));
+ *x = r.x();
+ *y = r.y();
+ } else {
+ DBG_NAV_LOGD("direct hit 2 [%d]", test->index());
+ // We have hit another one before
+ const CachedNode* d = *directHit;
+ if (d->bounds(this).contains(testRect)) {
+ // This rectangle is inside the other one, so it is
+ // the best one.
+ *directHit = test;
+ *directHitFramePtr = this;
+ }
+ }
+ }
+ if (NULL != *directHit) {
+ // If we have a direct hit already, there is no need to
+ // calculate the distances, or check the other parts
+ break;
+ }
+ DBG_NAV_LOGD("indirect hit [%d]", test->index());
+ WebCore::IntRect both = rect;
+ int smaller = testRect.width() < testRect.height() ?
+ testRect.width() : testRect.height();
+ smaller -= rectWidth;
+ int inset = smaller < rectWidth ? smaller : rectWidth;
+ inset >>= 1; // inflate doubles the width decrease
+ if (inset > 1)
+ both.inflate(1 - inset);
+ both.intersect(testRect);
+ if (both.isEmpty())
+ continue;
+ bool testInside = testRect.contains(center);
+ if (*inside && !testInside)
+ continue;
+ WebCore::IntPoint testCenter = WebCore::IntPoint(testRect.x() +
+ (testRect.width() >> 1), testRect.y() + (testRect.height() >> 1));
+ int dx = testCenter.x() - center.x();
+ int dy = testCenter.y() - center.y();
+ int distance = dx * dx + dy * dy;
+ if ((!*inside && testInside) || *best >= distance) {
+ *best = distance;
+ *inside = testInside;
+ result = test;
+ *framePtr = this;
+ *x = both.x() + (both.width() >> 1);
+ *y = both.y() + (both.height() >> 1);
+ }
+ }
+ }
+ }
+ for (const CachedFrame* frame = mCachedFrames.begin();
+ frame != mCachedFrames.end(); frame++) {
+ const CachedNode* frameResult = frame->findBestAt(rect, best, inside,
+ directHit, directHitFramePtr, framePtr, x, y, checkForHiddenStart);
+ if (NULL != frameResult)
+ result = frameResult;
+ }
+ if (NULL != *directHit) {
+ result = *directHit;
+ *framePtr = *directHitFramePtr;
+ }
+ return result;
+}
+
+const CachedFrame* CachedFrame::findBestFrameAt(int x, int y) const
+{
+ if (mLocalViewBounds.contains(x, y) == false)
+ return NULL;
+ const CachedFrame* result = this;
+ for (const CachedFrame* frame = mCachedFrames.begin();
+ frame != mCachedFrames.end(); frame++) {
+ const CachedFrame* frameResult = frame->findBestFrameAt(x, y);
+ if (NULL != frameResult)
+ result = frameResult;
+ }
+ return result;
+}
+
+const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect,
+ const CachedFrame** framePtr, int* x, int* y) const
+{
+ mRoot->setupScrolledBounds();
+ for (const CachedFrame* frame = mCachedFrames.end() - 1;
+ frame != mCachedFrames.begin() - 1; frame--) {
+ const CachedNode* frameResult = frame->findBestHitAt(rect,
+ framePtr, x, y);
+ if (NULL != frameResult)
+ return frameResult;
+ }
+ for (const CachedNode* test = mCachedNodes.end() - 1;
+ test != mCachedNodes.begin() - 1; test--) {
+ if (test->disabled())
+ continue;
+ WebCore::IntRect testRect = test->hitBounds(this);
+ if (testRect.intersects(rect) == false)
+ continue;
+ BestData testData;
+ testData.mNode = test;
+ testData.mFrame = this;
+ testData.setMouseBounds(testRect);
+ testData.setNodeBounds(testRect);
+ if (mRoot->maskIfHidden(&testData) == true)
+ continue;
+ DBG_NAV_LOGD("candidate %d rect=(%d,%d,r=%d,b=%d)"
+ " testRect=(%d,%d,r=%d,b=%d)",
+ test->index(), rect.x(), rect.y(), rect.maxX(), rect.maxY(),
+ testRect.x(), testRect.y(), testRect.maxX(), testRect.maxY());
+ for (int i = 0; i < test->navableRects(); i++) {
+ WebCore::IntRect cursorRect = test->ring(this, i);
+ DBG_NAV_LOGD("candidate %d cursorRect=(%d,%d,r=%d,b=%d)",
+ i, cursorRect.x(), cursorRect.y(), cursorRect.maxX(),
+ cursorRect.maxY());
+ if (cursorRect.intersects(rect)) {
+ WebCore::IntRect intersection(cursorRect);
+ intersection.intersect(rect);
+ *x = intersection.x() + (intersection.width() >> 1);
+ *y = intersection.y() + (intersection.height() >> 1);
+ *framePtr = this;
+ return test;
+ }
+ }
+ testRect.intersect(rect);
+ *x = testRect.x() + (testRect.width() >> 1);
+ *y = testRect.y() + (testRect.height() >> 1);
+ *framePtr = this;
+ return test;
+ }
+ return NULL;
+}
+
+void CachedFrame::findClosest(BestData* bestData, Direction originalDirection,
+ Direction direction, WebCore::IntRect* clip) const
+{
+ const CachedNode* test = mCachedNodes.begin();
+ while ((test = test->traverseNextNode()) != NULL) {
+ const CachedFrame* child = hasFrame(test);
+ if (child != NULL) {
+ const CachedNode* childDoc = child->validDocument();
+ if (childDoc == NULL)
+ continue;
+ child->findClosest(bestData, originalDirection, direction, clip);
+ }
+ if (test->noSecondChance())
+ continue;
+ if (test->isNavable(this, *clip) == false)
+ continue;
+ if (checkVisited(test, originalDirection) == false)
+ continue;
+ size_t partMax = test->navableRects();
+ for (size_t part = 0; part < partMax; part++) {
+ WebCore::IntRect testBounds = test->ring(this, part);
+ if (clip->intersects(testBounds) == false)
+ continue;
+ if (clip->contains(testBounds) == false) {
+ if (direction & UP_DOWN) {
+// if (testBounds.x() > clip->x() || testBounds.right() < clip->right())
+// continue;
+ testBounds.setX(clip->x());
+ testBounds.setWidth(clip->width());
+ } else {
+// if (testBounds.y() > clip->y() || testBounds.bottom() < clip->bottom())
+// continue;
+ testBounds.setY(clip->y());
+ testBounds.setHeight(clip->height());
+ }
+ if (clip->contains(testBounds) == false)
+ continue;
+ }
+ int distance;
+ // seems like distance for UP for instance needs to be 'test top closest to
+ // clip bottom' -- keep the old code but try this instead
+ switch (direction) {
+#if 0
+ case LEFT:
+ distance = testBounds.x() - clip->x();
+ break;
+ case RIGHT:
+ distance = clip->right() - testBounds.right();
+ break;
+ case UP:
+ distance = testBounds.y() - clip->y();
+ break;
+ case DOWN:
+ distance = clip->bottom() - testBounds.bottom();
+ break;
+#else
+ case LEFT:
+ distance = clip->maxX() - testBounds.x();
+ break;
+ case RIGHT:
+ distance = testBounds.maxX() - clip->x();
+ break;
+ case UP:
+ distance = clip->maxY() - testBounds.y();
+ break;
+ case DOWN:
+ distance = testBounds.maxY() - clip->y();
+ break;
+#endif
+ default:
+ distance = 0;
+ ASSERT(false);
+ }
+ if (distance < bestData->mDistance) {
+ bestData->mNode = test;
+ bestData->mFrame = this;
+ bestData->mDistance = distance;
+ WebCore::IntRect rect = test->ring(this, part);
+ bestData->setMouseBounds(rect);
+ bestData->setNodeBounds(rect);
+ CachedHistory* cachedHistory = history();
+ switch (direction) {
+ case LEFT:
+ bestData->setLeftDirection(cachedHistory);
+ break;
+ case RIGHT:
+ bestData->setRightDirection(cachedHistory);
+ break;
+ case UP:
+ bestData->setUpDirection(cachedHistory);
+ break;
+ case DOWN:
+ bestData->setDownDirection(cachedHistory);
+ break;
+ default:
+ ASSERT(0);
+ }
+ }
+ }
+ }
+}
+
+void CachedFrame::finishInit()
+{
+ CachedNode* lastCached = lastNode();
+ lastCached->setLast();
+ CachedFrame* child = mCachedFrames.begin();
+ while (child != mCachedFrames.end()) {
+ child->mParent = this;
+ child->finishInit();
+ child++;
+ }
+ CachedFrame* frameParent;
+ if (mFocusIndex >= 0 && (frameParent = parent()))
+ frameParent->setFocusIndex(indexInParent());
+}
+
+const CachedNode* CachedFrame::frameDown(const CachedNode* test,
+ const CachedNode* limit, BestData* bestData) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameDown, test, bestData))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, DOWN) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.setNodeBounds(test->ring(this, part));
+ if (testData.setDownDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData);
+ if (result == REJECT_TEST)
+ continue;
+ if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
+ BestData innerData = testData;
+ frameDown(document(), test, &innerData);
+ if (checkVisited(innerData.mNode, DOWN)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, DOWN))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit);
+ ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
+ // does the best contain something (or, is it contained by an area which is not the cursor?)
+ // if so, is the conainer/containee should have been chosen, but wasn't -- so there's a better choice
+ // in the doc list prior to this choice
+ //
+ return bestData->mNode;
+}
+
+const CachedNode* CachedFrame::frameLeft(const CachedNode* test,
+ const CachedNode* limit, BestData* bestData) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameLeft, test, bestData))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, LEFT) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.setNodeBounds(test->ring(this, part));
+ if (testData.setLeftDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData);
+ if (result == REJECT_TEST)
+ continue;
+ if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
+ BestData innerData = testData;
+ frameLeft(document(), test, &innerData);
+ if (checkVisited(innerData.mNode, LEFT)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, LEFT))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit); // FIXME ??? left and up should use traversePreviousNode to choose reverse document order
+ ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
+ return bestData->mNode;
+}
+
+int CachedFrame::frameNodeCommon(BestData& testData, const CachedNode* test,
+ BestData* bestData, BestData* originalData) const
+{
+ testData.mFrame = this;
+ testData.mNode = test;
+ test->clearCondition();
+ if (test->disabled()) {
+ testData.mNode->setCondition(CachedNode::DISABLED);
+ return REJECT_TEST;
+ }
+ WebCore::IntRect bounds = test->bounds(this);
+ if (bounds.isEmpty()) {
+ testData.mNode->setCondition(CachedNode::NAVABLE);
+ return REJECT_TEST;
+ }
+ if (mRoot->scrolledBounds().intersects(bounds) == false) {
+ testData.mNode->setCondition(CachedNode::NAVABLE);
+ return REJECT_TEST;
+ }
+ if (mRoot->rootLayer() && !test->isInLayer()
+ && !mRoot->baseUncovered().intersects(bounds)) {
+ testData.mNode->setCondition(CachedNode::UNDER_LAYER);
+ return REJECT_TEST;
+ }
+// if (isNavable(test, &testData.mNodeBounds, walk) == false) {
+// testData.mNode->setCondition(CachedNode::NAVABLE);
+// return REJECT_TEST;
+// }
+//
+ if (test == mRoot->mCursor) {
+ testData.mNode->setCondition(CachedNode::NOT_CURSOR_NODE);
+ return REJECT_TEST;
+ }
+// if (test->bounds().contains(mRoot->mCursorBounds)) {
+// testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+// return REJECT_TEST;
+// }
+ void* par = mRoot->mCursor ? mRoot->mCursor->parentGroup() : NULL;
+ testData.mCursorChild = par ? test->parentGroup() == par : false;
+ if (bestData->mNode == NULL)
+ return TEST_IS_BEST;
+ if (mRoot->mCursor && testData.mNode->parentIndex() != bestData->mNode->parentIndex()) {
+ int cursorParentIndex = mRoot->mCursor->parentIndex();
+ if (cursorParentIndex >= 0) {
+ if (bestData->mNode->parentIndex() == cursorParentIndex)
+ return REJECT_TEST;
+ if (testData.mNode->parentIndex() == cursorParentIndex)
+ return TEST_IS_BEST;
+ }
+ }
+ if (testData.mNode->parent() == bestData->mNode) {
+ testData.mNode->setCondition(CachedNode::CHILD);
+ return REJECT_TEST;
+ }
+ if (testData.mNode == bestData->mNode->parent())
+ return TEST_IS_BEST;
+ int testInBest = testData.isContainer(bestData); /* -1 pick best over test, 0 no containership, 1 pick test over best */
+ if (testInBest == 1) {
+ if (test->isArea() || bestData->mNode->isArea())
+ return UNDECIDED;
+ bestData->mNode = NULL; // force part tests to be ignored, yet still set up remaining test data for later comparisons
+ return TEST_IS_BEST;
+ }
+ if (testInBest == -1) {
+ testData.mNode->setCondition(CachedNode::OUTSIDE_OF_BEST);
+ return REJECT_TEST;
+ }
+ if (originalData->mNode != NULL) { // test is best case
+ testInBest = testData.isContainer(originalData);
+ if (testInBest == -1) { /* test is inside best */
+ testData.mNode->setCondition(CachedNode::OUTSIDE_OF_ORIGINAL);
+ return REJECT_TEST;
+ }
+ }
+ return UNDECIDED;
+}
+
+int CachedFrame::framePartCommon(BestData& testData,
+ const CachedNode* test, BestData* bestData) const
+{
+ if (mRoot->mCursor
+ && testData.bounds().contains(mRoot->mCursorBounds)
+ && !test->wantsKeyEvents()) {
+ testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+ return REJECT_TEST;
+ }
+ testData.setDistances();
+ if (bestData->mNode != NULL) {
+ int compared = compare(testData, *bestData);
+ if (compared == 0 && test->isArea() == false && bestData->mNode->isArea() == false)
+ goto pickTest;
+ if (compared >= 0)
+ return compared;
+ }
+pickTest:
+ return -1; // pick test
+}
+
+const CachedNode* CachedFrame::frameRight(const CachedNode* test,
+ const CachedNode* limit, BestData* bestData) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameRight, test, bestData))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, RIGHT) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.setNodeBounds(test->ring(this, part));
+ if (testData.setRightDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData);
+ if (result == REJECT_TEST)
+ continue;
+ if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
+ BestData innerData = testData;
+ frameRight(document(), test, &innerData);
+ if (checkVisited(innerData.mNode, RIGHT)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, RIGHT))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit);
+ ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
+ return bestData->mNode;
+}
+
+const CachedNode* CachedFrame::frameUp(const CachedNode* test,
+ const CachedNode* limit, BestData* bestData) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameUp, test, bestData))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, UP) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.setNodeBounds(test->ring(this, part));
+ if (testData.setUpDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData);
+ if (result == REJECT_TEST)
+ continue;
+ if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger
+ BestData innerData = testData;
+ frameUp(document(), test, &innerData);
+ if (checkVisited(innerData.mNode, UP)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, UP))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit); // FIXME ??? left and up should use traversePreviousNode to choose reverse document order
+ ASSERT(mRoot->mCursor == NULL || bestData->mNode != mRoot->mCursor);
+ return bestData->mNode;
+}
+
+CachedFrame* CachedFrame::hasFrame(const CachedNode* node)
+{
+ return node->isFrame() ? &mCachedFrames[node->childFrameIndex()] : NULL;
+}
+
+void CachedFrame::hideCursor()
+{
+ DBG_NAV_LOGD("mCursorIndex=%d", mCursorIndex);
+ if (mCursorIndex < CURSOR_SET)
+ return;
+ CachedNode& cursor = mCachedNodes[mCursorIndex];
+ cursor.hideCursor(this);
+}
+
+CachedHistory* CachedFrame::history() const
+{
+ return mRoot->rootHistory();
+}
+
+void CachedFrame::init(const CachedRoot* root, int childFrameIndex,
+ WebCore::Frame* frame)
+{
+ mContents = WebCore::IntRect(0, 0, 0, 0); // fixed up for real in setData()
+ mLocalViewBounds = WebCore::IntRect(0, 0, 0, 0);
+ mViewBounds = WebCore::IntRect(0, 0, 0, 0);
+ mRoot = root;
+ mCursorIndex = CURSOR_UNINITIALIZED; // not explicitly cleared
+ mFocusIndex = -1;
+ mFrame = frame;
+ mParent = NULL; // set up parents after stretchy arrays are set up
+ mIndexInParent = childFrameIndex;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+const CachedLayer* CachedFrame::layer(const CachedNode* node) const
+{
+ if (!node->isInLayer())
+ return 0;
+ CachedLayer test;
+ test.setCachedNodeIndex(node->index());
+ return std::lower_bound(mCachedLayers.begin(), mCachedLayers.end(), test);
+}
+#endif
+
+WebCore::IntRect CachedFrame::localBounds(const CachedNode* node,
+ const WebCore::IntRect& rect) const
+{
+ DBG_NAV_LOGD("node=%p [%d] rect=(%d,%d,w=%d,h=%d)",
+ node, node->index(), rect.x(), rect.y(), rect.width(), rect.height());
+#if USE(ACCELERATED_COMPOSITING)
+ return layer(node)->localBounds(mRoot->rootLayer(), rect);
+#else
+ return rect;
+#endif
+}
+
+int CachedFrame::minWorkingHorizontal() const
+{
+ return history()->minWorkingHorizontal();
+}
+
+int CachedFrame::minWorkingVertical() const
+{
+ return history()->minWorkingVertical();
+}
+
+int CachedFrame::maxWorkingHorizontal() const
+{
+ return history()->maxWorkingHorizontal();
+}
+
+int CachedFrame::maxWorkingVertical() const
+{
+ return history()->maxWorkingVertical();
+}
+
+const CachedNode* CachedFrame::nextTextField(const CachedNode* start,
+ const CachedFrame** framePtr, bool* startFound) const
+{
+ const CachedNode* test = mCachedNodes.begin();
+ while ((test = test->traverseNextNode())) {
+ const CachedFrame* frame = hasFrame(test);
+ if (frame) {
+ if (!frame->validDocument())
+ continue;
+ const CachedNode* node
+ = frame->nextTextField(start, framePtr, startFound);
+ if (node)
+ return node;
+ } else if (test->isTextInput()) {
+ if (test == start)
+ *startFound = true;
+ else if (*startFound) {
+ if (framePtr)
+ *framePtr = this;
+ return test;
+ }
+ }
+ }
+ return 0;
+}
+
+bool CachedFrame::moveInFrame(MoveInDirection moveInDirection,
+ const CachedNode* test, BestData* bestData) const
+{
+ const CachedFrame* frame = hasFrame(test);
+ if (frame == NULL)
+ return false; // if it's not a frame, let the caller have another swing at it
+ const CachedNode* childDoc = frame->validDocument();
+ if (childDoc == NULL)
+ return true;
+ (frame->*moveInDirection)(childDoc, NULL, bestData);
+ return true;
+}
+
+const WebCore::IntRect& CachedFrame::_navBounds() const
+{
+ return history()->navBounds();
+}
+
+SkPicture* CachedFrame::picture(const CachedNode* node) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (node->isInLayer())
+ return layer(node)->picture(mRoot->rootLayer());
+#endif
+ return mRoot->mPicture;
+}
+
+SkPicture* CachedFrame::picture(const CachedNode* node, int* xPtr, int* yPtr) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (node->isInLayer()) {
+ const CachedLayer* cachedLayer = layer(node);
+ const LayerAndroid* rootLayer = mRoot->rootLayer();
+ cachedLayer->toLocal(rootLayer, xPtr, yPtr);
+ return cachedLayer->picture(rootLayer);
+ }
+#endif
+ return mRoot->mPicture;
+}
+
+void CachedFrame::resetClippedOut()
+{
+ for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++)
+ {
+ if (test->clippedOut()) {
+ test->setDisabled(false);
+ test->setClippedOut(false);
+ }
+ }
+ for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
+ frame++) {
+ frame->resetClippedOut();
+ }
+}
+
+void CachedFrame::resetLayers()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end();
+ frame++) {
+ frame->resetLayers();
+ }
+#endif
+}
+
+bool CachedFrame::sameFrame(const CachedFrame* test) const
+{
+ ASSERT(test);
+ if (mIndexInParent != test->mIndexInParent)
+ return false;
+ if (mIndexInParent == -1) // index within parent's array of children, or -1 if root
+ return true;
+ return mParent->sameFrame(test->mParent);
+}
+
+void CachedFrame::setData()
+{
+ if (this != mRoot) {
+ mViewBounds = mLocalViewBounds;
+ mViewBounds.intersect(mRoot->mViewBounds);
+ }
+ int x, y;
+ if (parent() == NULL)
+ x = y = 0;
+ else {
+ x = mLocalViewBounds.x();
+ y = mLocalViewBounds.y();
+ }
+ mContents.setX(x);
+ mContents.setY(y);
+ CachedFrame* child = mCachedFrames.begin();
+ while (child != mCachedFrames.end()) {
+ child->setData();
+ child++;
+ }
+}
+
+bool CachedFrame::setCursor(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y)
+{
+ if (NULL == node) {
+ const_cast<CachedRoot*>(mRoot)->setCursor(NULL, NULL);
+ return true;
+ }
+ if (mFrame != frame) {
+ for (CachedFrame* testF = mCachedFrames.begin(); testF != mCachedFrames.end();
+ testF++) {
+ if (testF->setCursor(frame, node, x, y))
+ return true;
+ }
+ DBG_NAV_LOGD("no frame frame=%p node=%p", frame, node);
+ return false;
+ }
+ bool first = true;
+ CachedNode const * const end = mCachedNodes.end();
+ do {
+ for (CachedNode* test = mCachedNodes.begin(); test != end; test++) {
+ if (test->nodePointer() != node && first)
+ continue;
+ size_t partMax = test->navableRects();
+ for (size_t part = 0; part < partMax; part++) {
+ WebCore::IntRect testBounds = test->ring(this, part);
+ if (testBounds.contains(x, y) == false)
+ continue;
+ if (test->isCursor()) {
+ DBG_NAV_LOGD("already set? test=%d frame=%p node=%p x=%d y=%d",
+ test->index(), frame, node, x, y);
+ return false;
+ }
+ const_cast<CachedRoot*>(mRoot)->setCursor(this, test);
+ return true;
+ }
+ }
+ DBG_NAV_LOGD("moved? frame=%p node=%p x=%d y=%d", frame, node, x, y);
+ } while ((first ^= true) == false);
+failed:
+ DBG_NAV_LOGD("no match frame=%p node=%p", frame, node);
+ return false;
+}
+
+const CachedNode* CachedFrame::validDocument() const
+{
+ const CachedNode* doc = document();
+ return doc != NULL && mViewBounds.isEmpty() == false ? doc : NULL;
+}
+
+bool CachedFrame::BestData::canBeReachedByAnotherDirection()
+{
+ if (mMajorButt > -MIN_OVERLAP)
+ return false;
+ mMajorButt = -mMajorButt;
+ return mNavOutside;
+}
+
+int CachedFrame::BestData::isContainer(CachedFrame::BestData* other)
+{
+ int _x = x();
+ int otherRight = other->right();
+ if (_x >= otherRight)
+ return 0; // does not intersect
+ int _y = y();
+ int otherBottom = other->bottom();
+ if (_y >= otherBottom)
+ return 0; // does not intersect
+ int _right = right();
+ int otherX = other->x();
+ if (otherX >= _right)
+ return 0; // does not intersect
+ int _bottom = bottom();
+ int otherY = other->y();
+ if (otherY >= _bottom)
+ return 0; // does not intersect
+ int intoX = otherX - _x;
+ int intoY = otherY - _y;
+ int intoRight = otherRight - _right;
+ int intoBottom = otherBottom - _bottom;
+ bool contains = intoX >= 0 && intoY >= 0 && intoRight <= 0 && intoBottom <= 0;
+ if (contains && mNode->partRectsContains(other->mNode)) {
+// if (mIsArea == false && hasMouseOver())
+// other->mMouseOver = mNode;
+ return mNode->isArea() ? 1 : -1;
+ }
+ bool containedBy = intoX <= 0 && intoY <= 0 && intoRight >= 0 && intoBottom >= 0;
+ if (containedBy && other->mNode->partRectsContains(mNode)) {
+// if (other->mIsArea == false && other->hasMouseOver())
+// mMouseOver = other->mNode;
+ return other->mNode->isArea() ? -1 : 1;
+ }
+ return 0;
+}
+
+// distance scale factor factor as a 16.16 scalar
+SkFixed CachedFrame::BestData::Overlap(int span, int left, int right)
+{
+ unsigned result;
+ if (left > 0 && left < span && right > span)
+ result = (unsigned) left;
+ else if (right > 0 && right < span && left > span)
+ result = (unsigned) right;
+ else if (left > 0 && right > 0)
+ return SK_Fixed1;
+ else
+ return 0;
+ result = (result << 16) / (unsigned) span; // linear proportion, always less than fixed 1
+ return (SkFixed) result;
+// !!! experiment with weight -- enable if overlaps are preferred too much
+// or reverse weighting if overlaps are preferred to little
+// return (SkFixed) (result * result >> 16); // but fall off with square
+}
+
+void CachedFrame::BestData::setDistances()
+{
+ mDistance = abs(mMajorDelta);
+ int sideDistance = mWorkingDelta;
+ if (mWorkingOverlap < SK_Fixed1) {
+ if (mPreferred > 0)
+ sideDistance = mWorkingDelta2;
+ } else if (sideDistance >= 0 && mWorkingDelta2 >=- 0)
+ sideDistance = 0;
+ else {
+ ASSERT(sideDistance <= 0 && mWorkingDelta2 <= 0);
+ if (sideDistance < mWorkingDelta2)
+ sideDistance = mWorkingDelta2;
+ }
+ // if overlap, smaller abs mWorkingDelta is better, smaller abs majorDelta is better
+ // if not overlap, positive mWorkingDelta is better
+ mSideDistance = sideDistance;
+}
+
+bool CachedFrame::BestData::setDownDirection(const CachedHistory* history)
+{
+ const WebCore::IntRect& navBounds = history->navBounds();
+ mMajorButt = mNodeBounds.y() - navBounds.maxY();
+ int testX = mNodeBounds.x();
+ int testRight = mNodeBounds.maxX();
+ setNavOverlap(navBounds.width(), navBounds.maxX() - testX,
+ testRight - navBounds.x());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavTop = mNodeBounds.y() - navBounds.y();
+ mMajorDelta2 = inNavTop;
+ mMajorDelta = mMajorDelta2 + ((mNodeBounds.height() -
+ navBounds.height()) >> 1);
+ if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
+ mNode->setCondition(CachedNode::CENTER_FURTHER); // never move up or sideways
+ return REJECT_TEST;
+ }
+ int inNavBottom = navBounds.maxY() - mNodeBounds.maxY();
+ setNavInclusion(testRight - navBounds.maxX(), navBounds.x() - testX);
+ bool subsumes = navBounds.height() > 0 && inOrSubsumesNav();
+ if (inNavTop <= 0 && inNavBottom <= 0 && subsumes && !mNode->wantsKeyEvents()) {
+ mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+ return REJECT_TEST;
+ }
+ int maxV = history->maxWorkingVertical();
+ int minV = history->minWorkingVertical();
+ setWorkingOverlap(testRight - testX, maxV - testX, testRight - minV);
+ setWorkingInclusion(testRight - maxV, minV - testX);
+ if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavBottom >= 0) {
+ mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
+ return REJECT_TEST;
+ }
+ mInNav = history->directionChange() && inNavTop >= 0 &&
+ inNavBottom > 0 && subsumes;
+ return false;
+}
+
+bool CachedFrame::BestData::setLeftDirection(const CachedHistory* history)
+{
+ const WebCore::IntRect& navBounds = history->navBounds();
+ mMajorButt = navBounds.x() - mNodeBounds.maxX();
+ int testY = mNodeBounds.y();
+ int testBottom = mNodeBounds.maxY();
+ setNavOverlap(navBounds.height(), navBounds.maxY() - testY,
+ testBottom - navBounds.y());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavRight = navBounds.maxX() - mNodeBounds.maxX();
+ mMajorDelta2 = inNavRight;
+ mMajorDelta = mMajorDelta2 - ((navBounds.width() -
+ mNodeBounds.width()) >> 1);
+ if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
+ mNode->setCondition(CachedNode::CENTER_FURTHER); // never move right or sideways
+ return REJECT_TEST;
+ }
+ int inNavLeft = mNodeBounds.x() - navBounds.x();
+ setNavInclusion(navBounds.y() - testY, testBottom - navBounds.maxY());
+ bool subsumes = navBounds.width() > 0 && inOrSubsumesNav();
+ if (inNavLeft <= 0 && inNavRight <= 0 && subsumes && !mNode->wantsKeyEvents()) {
+ mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+ return REJECT_TEST;
+ }
+ int maxH = history->maxWorkingHorizontal();
+ int minH = history->minWorkingHorizontal();
+ setWorkingOverlap(testBottom - testY, maxH - testY, testBottom - minH);
+ setWorkingInclusion(minH - testY, testBottom - maxH);
+ if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavLeft >= 0) {
+ mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
+ return REJECT_TEST;
+ }
+ mInNav = history->directionChange() && inNavLeft >= 0 &&
+ inNavRight > 0 && subsumes; /* both L/R in or out */
+ return false;
+}
+
+bool CachedFrame::BestData::setRightDirection(const CachedHistory* history)
+{
+ const WebCore::IntRect& navBounds = history->navBounds();
+ mMajorButt = mNodeBounds.x() - navBounds.maxX();
+ int testY = mNodeBounds.y();
+ int testBottom = mNodeBounds.maxY();
+ setNavOverlap(navBounds.height(), navBounds.maxY() - testY,
+ testBottom - navBounds.y());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavLeft = mNodeBounds.x() - navBounds.x();
+ mMajorDelta2 = inNavLeft;
+ mMajorDelta = mMajorDelta2 + ((mNodeBounds.width() -
+ navBounds.width()) >> 1);
+ if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
+ mNode->setCondition(CachedNode::CENTER_FURTHER); // never move left or sideways
+ return REJECT_TEST;
+ }
+ int inNavRight = navBounds.maxX() - mNodeBounds.maxX();
+ setNavInclusion(testBottom - navBounds.maxY(), navBounds.y() - testY);
+ bool subsumes = navBounds.width() > 0 && inOrSubsumesNav();
+ if (inNavLeft <= 0 && inNavRight <= 0 && subsumes && !mNode->wantsKeyEvents()) {
+ mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+ return REJECT_TEST;
+ }
+ int maxH = history->maxWorkingHorizontal();
+ int minH = history->minWorkingHorizontal();
+ setWorkingOverlap(testBottom - testY, testBottom - minH, maxH - testY);
+ setWorkingInclusion(testBottom - maxH, minH - testY);
+ if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavRight >= 0) {
+ mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
+ return REJECT_TEST;
+ }
+ mInNav = history->directionChange() && inNavLeft >= 0 &&
+ inNavRight > 0 && subsumes; /* both L/R in or out */
+ return false;
+}
+
+bool CachedFrame::BestData::setUpDirection(const CachedHistory* history)
+{
+ const WebCore::IntRect& navBounds = history->navBounds();
+ mMajorButt = navBounds.y() - mNodeBounds.maxY();
+ int testX = mNodeBounds.x();
+ int testRight = mNodeBounds.maxX();
+ setNavOverlap(navBounds.width(), navBounds.maxX() - testX,
+ testRight - navBounds.x());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavBottom = navBounds.maxY() - mNodeBounds.maxY();
+ mMajorDelta2 = inNavBottom;
+ mMajorDelta = mMajorDelta2 - ((navBounds.height() -
+ mNodeBounds.height()) >> 1);
+ if (mMajorDelta2 <= 1 && mMajorDelta <= 1) {
+ mNode->setCondition(CachedNode::CENTER_FURTHER); // never move down or sideways
+ return REJECT_TEST;
+ }
+ int inNavTop = mNodeBounds.y() - navBounds.y();
+ setNavInclusion(navBounds.x() - testX, testRight - navBounds.maxX());
+ bool subsumes = navBounds.height() > 0 && inOrSubsumesNav();
+ if (inNavTop <= 0 && inNavBottom <= 0 && subsumes && !mNode->wantsKeyEvents()) {
+ mNode->setCondition(CachedNode::NOT_ENCLOSING_CURSOR);
+ return REJECT_TEST;
+ }
+ int maxV = history->maxWorkingVertical();
+ int minV = history->minWorkingVertical();
+ setWorkingOverlap(testRight - testX, testRight - minV, maxV - testX);
+ setWorkingInclusion(minV - testX, testRight - maxV);
+ if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavTop >= 0) {
+ mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER);
+ return REJECT_TEST;
+ }
+ mInNav = history->directionChange() && inNavTop >= 0 &&
+ inNavBottom > 0 && subsumes; /* both L/R in or out */
+ return false;
+}
+
+void CachedFrame::BestData::setNavInclusion(int left, int right)
+{
+ // if left and right <= 0, test node is completely in umbra of cursor
+ // prefer leftmost center
+ // if left and right > 0, test node subsumes cursor
+ mNavDelta = left;
+ mNavDelta2 = right;
+}
+
+void CachedFrame::BestData::setNavOverlap(int span, int left, int right)
+{
+ // if left or right < 0, test node is not in umbra of cursor
+ mNavOutside = left < MIN_OVERLAP || right < MIN_OVERLAP;
+ mNavOverlap = Overlap(span, left, right); // prefer smallest negative left
+}
+
+void CachedFrame::BestData::setWorkingInclusion(int left, int right)
+{
+ mWorkingDelta = left;
+ mWorkingDelta2 = right;
+}
+
+// distance scale factor factor as a 16.16 scalar
+void CachedFrame::BestData::setWorkingOverlap(int span, int left, int right)
+{
+ // if left or right < 0, test node is not in umbra of cursor
+ mWorkingOutside = left < MIN_OVERLAP || right < MIN_OVERLAP;
+ mWorkingOverlap = Overlap(span, left, right);
+ mPreferred = left <= 0 ? 0 : left;
+}
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_RECT(prefix, debugName, field) \
+ { const WebCore::IntRect& r = b->field; \
+ DUMP_NAV_LOGD("%s DebugTestRect TEST%s_" #debugName "={%d, %d, %d, %d}; //" #field "\n", \
+ prefix, mFrameName, r.x(), r.y(), r.width(), r.height()); }
+
+CachedFrame* CachedFrame::Debug::base() const {
+ CachedFrame* nav = (CachedFrame*) ((char*) this - OFFSETOF(CachedFrame, mDebug));
+ return nav;
+}
+
+void CachedFrame::Debug::print() const
+{
+ CachedFrame* b = base();
+ DEBUG_PRINT_RECT("//", CONTENTS, mContents);
+ DEBUG_PRINT_RECT("", BOUNDS, mLocalViewBounds);
+ DEBUG_PRINT_RECT("//", VIEW, mViewBounds);
+
+ DUMP_NAV_LOGD("// CachedNode mCachedNodes={ // count=%d\n", b->mCachedNodes.size());
+ for (CachedNode* node = b->mCachedNodes.begin();
+ node != b->mCachedNodes.end(); node++) {
+ node->mDebug.print();
+ const CachedInput* input = b->textInput(node);
+ if (input)
+ input->mDebug.print();
+ DUMP_NAV_LOGD("\n");
+ }
+ DUMP_NAV_LOGD("// }; // end of nodes\n");
+#if USE(ACCELERATED_COMPOSITING)
+ DUMP_NAV_LOGD("// CachedLayer mCachedLayers={ // count=%d\n", b->mCachedLayers.size());
+ for (CachedLayer* layer = b->mCachedLayers.begin();
+ layer != b->mCachedLayers.end(); layer++) {
+ layer->mDebug.print();
+ }
+ DUMP_NAV_LOGD("// }; // end of layers\n");
+#endif // USE(ACCELERATED_COMPOSITING)
+ DUMP_NAV_LOGD("// CachedColor mCachedColors={ // count=%d\n", b->mCachedColors.size());
+ for (CachedColor* color = b->mCachedColors.begin();
+ color != b->mCachedColors.end(); color++) {
+ color->mDebug.print();
+ }
+ DUMP_NAV_LOGD("// }; // end of colors\n");
+ DUMP_NAV_LOGD("// CachedFrame mCachedFrames={ // count=%d\n", b->mCachedFrames.size());
+ for (CachedFrame* child = b->mCachedFrames.begin();
+ child != b->mCachedFrames.end(); child++)
+ {
+ child->mDebug.print();
+ }
+ DUMP_NAV_LOGD("// }; // end of child frames\n");
+ DUMP_NAV_LOGD("// void* mFrame=(void*) %p;\n", b->mFrame);
+ DUMP_NAV_LOGD("// CachedFrame* mParent=%p;\n", b->mParent);
+ DUMP_NAV_LOGD("// int mIndexInParent=%d;\n", b->mIndexInParent);
+ DUMP_NAV_LOGD("// const CachedRoot* mRoot=%p;\n", b->mRoot);
+ DUMP_NAV_LOGD("// int mCursorIndex=%d;\n", b->mCursorIndex);
+ DUMP_NAV_LOGD("// int mFocusIndex=%d;\n", b->mFocusIndex);
+}
+
+bool CachedFrame::Debug::validate(const CachedNode* node) const
+{
+ const CachedFrame* b = base();
+ if (b->mCachedNodes.size() == 0)
+ return false;
+ if (node >= b->mCachedNodes.begin() && node < b->mCachedNodes.end())
+ return true;
+ for (const CachedFrame* child = b->mCachedFrames.begin();
+ child != b->mCachedFrames.end(); child++)
+ if (child->mDebug.validate(node))
+ return true;
+ return false;
+}
+
+#undef DEBUG_PRINT_RECT
+
+#endif
+
+}
diff --git a/Source/WebKit/android/nav/CachedFrame.h b/Source/WebKit/android/nav/CachedFrame.h
new file mode 100644
index 0000000..da86521
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedFrame.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2007, 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.
+ */
+
+// FIXME: A file of this name already exists in WebCore/history.
+// This file should be renamed.
+#ifndef AndroidCachedFrame_h
+#define AndroidCachedFrame_h
+
+#include "CachedColor.h"
+#include "CachedInput.h"
+#include "CachedLayer.h"
+#include "CachedNode.h"
+#include "IntRect.h"
+#include "SkFixed.h"
+#include "wtf/Vector.h"
+
+class SkPicture;
+
+namespace WebCore {
+ class Frame;
+ class Node;
+}
+
+namespace android {
+
+class CachedHistory;
+class CachedRoot;
+
+ // first node referenced by cache is always document
+class CachedFrame {
+public:
+ enum Direction {
+ UNINITIALIZED = -1,
+ LEFT,
+ RIGHT,
+ UP,
+ DOWN,
+ DIRECTION_COUNT,
+ DIRECTION_MASK = DIRECTION_COUNT - 1,
+ UP_DOWN = UP & DOWN, // mask and result
+ RIGHT_DOWN = RIGHT & DOWN, // mask and result
+ };
+ enum Compare {
+ UNDECIDED = -1,
+ TEST_IS_BEST,
+ REJECT_TEST
+ };
+ enum CursorInit {
+ CURSOR_UNINITIALIZED = -2,
+ CURSOR_CLEARED = -1,
+ CURSOR_SET = 0
+ };
+ CachedFrame() {}
+ void add(CachedColor& color) { mCachedColors.append(color); }
+ void add(CachedInput& input) { mCachedTextInputs.append(input); }
+#if USE(ACCELERATED_COMPOSITING)
+ void add(CachedLayer& layer) { mCachedLayers.append(layer); }
+#endif
+ void add(CachedNode& node) { mCachedNodes.append(node); }
+ void addFrame(CachedFrame& child) { mCachedFrames.append(child); }
+ WebCore::IntRect adjustBounds(const CachedNode* ,
+ const WebCore::IntRect& ) const;
+ bool checkRings(const CachedNode* node,
+ const WebCore::IntRect& testBounds) const;
+ bool checkVisited(const CachedNode* , CachedFrame::Direction ) const;
+ size_t childCount() { return mCachedFrames.size(); }
+ void clearCursor();
+ const CachedColor& color(const CachedNode* node) const {
+ return mCachedColors[node->colorIndex()];
+ }
+ const CachedNode* currentCursor() const { return currentCursor(NULL); }
+ const CachedNode* currentCursor(const CachedFrame** ) const;
+ const CachedNode* currentFocus() const { return currentFocus(NULL); }
+ const CachedNode* currentFocus(const CachedFrame** ) const;
+ bool directionChange() const;
+ const CachedNode* document() const { return mCachedNodes.begin(); }
+ bool empty() const { return mCachedNodes.size() < 2; } // must have 1 past doc
+ const CachedNode* findBestAt(const WebCore::IntRect& , int* best,
+ bool* inside, const CachedNode** , const CachedFrame** directFrame,
+ const CachedFrame** resultFrame, int* x,
+ int* y, bool checkForHidden) const;
+ const CachedFrame* findBestFrameAt(int x, int y) const;
+ const CachedNode* findBestHitAt(const WebCore::IntRect& ,
+ const CachedFrame** , int* x, int* y) const;
+ void finishInit();
+ CachedFrame* firstChild() { return mCachedFrames.begin(); }
+ const CachedFrame* firstChild() const { return mCachedFrames.begin(); }
+ void* framePointer() const { return mFrame; }
+ CachedNode* getIndex(int index) { return index >= 0 ?
+ &mCachedNodes[index] : NULL; }
+ const CachedFrame* hasFrame(const CachedNode* node) const {
+ return const_cast<CachedFrame*>(this)->hasFrame(node);
+ }
+ CachedFrame* hasFrame(const CachedNode* node);
+ void hideCursor();
+ int indexInParent() const { return mIndexInParent; }
+ void init(const CachedRoot* root, int index, WebCore::Frame* frame);
+ const CachedFrame* lastChild() const { return &mCachedFrames.last(); }
+#if USE(ACCELERATED_COMPOSITING)
+ const CachedLayer* lastLayer() const { return &mCachedLayers.last(); }
+#endif
+ CachedNode* lastNode() { return &mCachedNodes.last(); }
+ CachedFrame* lastChild() { return &mCachedFrames.last(); }
+#if USE(ACCELERATED_COMPOSITING)
+ const CachedLayer* layer(const CachedNode* ) const;
+ size_t layerCount() const { return mCachedLayers.size(); }
+#endif
+ WebCore::IntRect localBounds(const CachedNode* ,
+ const WebCore::IntRect& ) const;
+ const CachedFrame* parent() const { return mParent; }
+ CachedFrame* parent() { return mParent; }
+ SkPicture* picture(const CachedNode* ) const;
+ SkPicture* picture(const CachedNode* , int* xPtr, int* yPtr) const;
+ void resetLayers();
+ bool sameFrame(const CachedFrame* ) const;
+ void removeLast() { mCachedNodes.removeLast(); }
+ void resetClippedOut();
+ void setContentsSize(int width, int height) { mContents.setWidth(width);
+ mContents.setHeight(height); }
+ bool setCursor(WebCore::Frame* , WebCore::Node* , int x, int y);
+ void setCursorIndex(int index) { mCursorIndex = index; }
+ void setData();
+ bool setFocus(WebCore::Frame* , WebCore::Node* , int x, int y);
+ void setFocusIndex(int index) { mFocusIndex = index; }
+ void setIndexInParent(int index) { mIndexInParent = index; }
+ void setLocalViewBounds(const WebCore::IntRect& bounds) { mLocalViewBounds = bounds; }
+ int size() { return mCachedNodes.size(); }
+ const CachedInput* textInput(const CachedNode* node) const {
+ return node->isTextInput() ? &mCachedTextInputs[node->textInputIndex()]
+ : 0;
+ }
+ const CachedNode* validDocument() const;
+protected:
+ const CachedNode* nextTextField(const CachedNode* start,
+ const CachedFrame** framePtr, bool* found) const;
+ struct BestData {
+ int mDistance;
+ int mSideDistance;
+ int mMajorDelta; // difference of center of object
+ // used only when leading and trailing edges contain another set of edges
+ int mMajorDelta2; // difference of leading edge (only used when center is same)
+ int mMajorButt; // checks for next cell butting up against or close to previous one
+ int mWorkingDelta;
+ int mWorkingDelta2;
+ int mNavDelta;
+ int mNavDelta2;
+ const CachedFrame* mFrame;
+ const CachedNode* mNode;
+ SkFixed mWorkingOverlap; // this and below are fuzzy answers instead of bools
+ SkFixed mNavOverlap;
+ SkFixed mPreferred;
+ bool mCursorChild;
+ bool mInNav;
+ bool mNavOutside;
+ bool mWorkingOutside;
+ int bottom() const { return bounds().maxY(); }
+ const WebCore::IntRect& bounds() const { return mNodeBounds; }
+ bool canBeReachedByAnotherDirection();
+ int height() const { return bounds().height(); }
+ bool inOrSubsumesNav() const { return (mNavDelta ^ mNavDelta2) >= 0; }
+ bool inOrSubsumesWorking() const { return (mWorkingDelta ^ mWorkingDelta2) >= 0; }
+ int isContainer(BestData* );
+ const WebCore::IntRect& mouseBounds() const { return mMouseBounds; }
+ static SkFixed Overlap(int span, int left, int right);
+ void reset() { mNode = NULL; }
+ int right() const { return bounds().maxX(); }
+ void setMouseBounds(const WebCore::IntRect& b) { mMouseBounds = b; }
+ void setNodeBounds(const WebCore::IntRect& b) { mNodeBounds = b; }
+ void setDistances();
+ bool setDownDirection(const CachedHistory* );
+ bool setLeftDirection(const CachedHistory* );
+ bool setRightDirection(const CachedHistory* );
+ bool setUpDirection(const CachedHistory* );
+ void setNavInclusion(int left, int right);
+ void setNavOverlap(int span, int left, int right);
+ void setWorkingInclusion(int left, int right);
+ void setWorkingOverlap(int span, int left, int right);
+ int width() const { return bounds().width(); }
+ int x() const { return bounds().x(); }
+ int y() const { return bounds().y(); }
+private: // since computing these is complicated, protect them so that the
+ // are only written by appropriate helpers
+ WebCore::IntRect mMouseBounds;
+ WebCore::IntRect mNodeBounds;
+ };
+ typedef const CachedNode* (CachedFrame::*MoveInDirection)(
+ const CachedNode* test, const CachedNode* limit, BestData* ) const;
+ void adjustToTextColumn(int* delta) const;
+ static bool CheckBetween(Direction , const WebCore::IntRect& bestRect,
+ const WebCore::IntRect& prior, WebCore::IntRect* result);
+ bool checkBetween(BestData* , Direction );
+ int compare(BestData& testData, const BestData& bestData) const;
+ void findClosest(BestData* , Direction original, Direction test,
+ WebCore::IntRect* clip) const;
+ int frameNodeCommon(BestData& testData, const CachedNode* test,
+ BestData* bestData, BestData* originalData) const;
+ int framePartCommon(BestData& testData, const CachedNode* test,
+ BestData* ) const;
+ const CachedNode* frameDown(const CachedNode* test, const CachedNode* limit,
+ BestData* ) const;
+ const CachedNode* frameLeft(const CachedNode* test, const CachedNode* limit,
+ BestData* ) const;
+ const CachedNode* frameRight(const CachedNode* test, const CachedNode* limit,
+ BestData* ) const;
+ const CachedNode* frameUp(const CachedNode* test, const CachedNode* limit,
+ BestData* ) const;
+ int minWorkingHorizontal() const;
+ int minWorkingVertical() const;
+ int maxWorkingHorizontal() const;
+ int maxWorkingVertical() const;
+ bool moveInFrame(MoveInDirection , const CachedNode* test, BestData* ) const;
+ const WebCore::IntRect& _navBounds() const;
+ WebCore::IntRect mContents;
+ WebCore::IntRect mLocalViewBounds;
+ WebCore::IntRect mViewBounds;
+ WTF::Vector<CachedColor> mCachedColors;
+ WTF::Vector<CachedNode> mCachedNodes;
+ WTF::Vector<CachedFrame> mCachedFrames;
+ WTF::Vector<CachedInput> mCachedTextInputs;
+#if USE(ACCELERATED_COMPOSITING)
+ WTF::Vector<CachedLayer> mCachedLayers;
+#endif
+ void* mFrame; // WebCore::Frame*, used only to compare pointers
+ CachedFrame* mParent;
+ int mCursorIndex;
+ int mFocusIndex;
+ int mIndexInParent; // index within parent's array of children, or -1 if root
+ const CachedRoot* mRoot;
+private:
+ CachedHistory* history() const;
+#ifdef BROWSER_DEBUG
+public:
+ CachedNode* find(WebCore::Node* ); // !!! probably debugging only
+ int mDebugIndex;
+ int mDebugLoopbackOffset;
+#endif
+#if !defined NDEBUG || DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ Debug() {
+#if DUMP_NAV_CACHE
+ mFrameName[0] = '\0';
+#endif
+#if !defined NDEBUG
+ mInUse = true;
+#endif
+ }
+#if !defined NDEBUG
+ ~Debug() { mInUse = false; }
+ bool mInUse;
+#endif
+#if DUMP_NAV_CACHE
+ CachedFrame* base() const;
+ void print() const;
+ bool validate(const CachedNode* ) const;
+ char mFrameName[256];
+#endif
+ } mDebug;
+#endif
+};
+
+}
+
+#endif // AndroidCachedFrame_h
diff --git a/Source/WebKit/android/nav/CachedHistory.cpp b/Source/WebKit/android/nav/CachedHistory.cpp
new file mode 100644
index 0000000..d132cc3
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedHistory.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "CachedFrame.h"
+#include "CachedNode.h"
+#if DUMP_NAV_CACHE
+#include "CachedRoot.h"
+#endif
+
+#include "CachedHistory.h"
+
+namespace android {
+
+CachedHistory::CachedHistory() {
+ memset(this, 0, sizeof(CachedHistory)); // this assume the class has no virtuals
+ mLastMove = CachedFrame::UNINITIALIZED;
+ mPriorMove = CachedFrame::UNINITIALIZED;
+}
+
+
+void CachedHistory::addToVisited(const CachedNode* node, CachedFrame::Direction direction)
+{
+ memmove(&mVisited[1], &mVisited[0], sizeof(mVisited) - sizeof(mVisited[0]));
+ mVisited[0].mNode = node;
+ mVisited[0].mDirection = direction;
+}
+
+bool CachedHistory::checkVisited(const CachedNode* node, CachedFrame::Direction direction) const
+{
+ // if the direction is unchanged and we've already visited this node, don't visit it again
+ int index = 0;
+ while (index < NAVIGATION_VISIT_DEPTH - 1) {
+ if (direction != mVisited[index].mDirection)
+ break;
+ index++; // compare with last direction, previous to last node (where the arrow took us from)
+ if (node == mVisited[index].mNode)
+ return false;
+ }
+ return true;
+}
+
+void CachedHistory::pinMaxMin(const WebCore::IntRect& viewBounds)
+{
+ if (mMinWorkingHorizontal < viewBounds.y() || mMinWorkingHorizontal >= viewBounds.maxY())
+ mMinWorkingHorizontal = viewBounds.y();
+ if (mMaxWorkingHorizontal > viewBounds.maxY() || mMaxWorkingHorizontal <= viewBounds.y())
+ mMaxWorkingHorizontal = viewBounds.maxY();
+ if (mMinWorkingVertical < viewBounds.x() || mMinWorkingVertical >= viewBounds.maxX())
+ mMinWorkingVertical = viewBounds.x();
+ if (mMaxWorkingVertical > viewBounds.maxX() || mMaxWorkingVertical <= viewBounds.x())
+ mMaxWorkingVertical = viewBounds.maxX();
+}
+
+void CachedHistory::reset()
+{
+ memset(mVisited, 0, sizeof(mVisited));
+// mLastScroll = 0;
+ mPriorBounds = WebCore::IntRect(0, 0, 0, 0);
+ mDirectionChange = false;
+ mDidFirstLayout = false;
+ mPriorMove = mLastMove = CachedFrame::UNINITIALIZED;
+ mMinWorkingHorizontal = mMinWorkingVertical = INT_MIN;
+ mMaxWorkingHorizontal = mMaxWorkingVertical = INT_MAX;
+}
+
+void CachedHistory::setWorking(CachedFrame::Direction newMove,
+ const CachedFrame* cursorFrame, const CachedNode* cursor,
+ const WebCore::IntRect& viewBounds)
+{
+ CachedFrame::Direction lastAxis = (CachedFrame::Direction) (mLastMove & ~CachedFrame::RIGHT_DOWN); // up, left or uninitialized
+ CachedFrame::Direction newAxis = (CachedFrame::Direction) (newMove & ~CachedFrame::RIGHT_DOWN);
+ bool change = newAxis != lastAxis;
+ mDirectionChange = change && mLastMove != CachedFrame::UNINITIALIZED;
+ if (cursor != NULL || mLastMove != CachedFrame::UNINITIALIZED) {
+ mPriorMove = mLastMove;
+ mLastMove = newMove;
+ }
+ const WebCore::IntRect* navBounds = &mNavBounds;
+ if (cursor != NULL) {
+ WebCore::IntRect cursorBounds = cursor->bounds(cursorFrame);
+ if (cursorBounds.isEmpty() == false)
+ mNavBounds = cursorBounds;
+ }
+ if (change) { // uninitialized or change in direction
+ if (lastAxis != CachedFrame::LEFT && navBounds->height() > 0) {
+ mMinWorkingHorizontal = navBounds->y();
+ mMaxWorkingHorizontal = navBounds->maxY();
+ }
+ if (lastAxis != CachedFrame::UP && navBounds->width() > 0) {
+ mMinWorkingVertical = navBounds->x();
+ mMaxWorkingVertical = navBounds->maxX();
+ }
+ }
+ pinMaxMin(viewBounds);
+}
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_BOOL(field) \
+ DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
+
+#define DEBUG_PRINT_RECT(field) \
+ { const WebCore::IntRect& r = b->field; \
+ DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
+ r.x(), r.y(), r.width(), r.height()); }
+
+CachedHistory* CachedHistory::Debug::base() const {
+ CachedHistory* nav = (CachedHistory*) ((char*) this - OFFSETOF(CachedHistory, mDebug));
+ return nav;
+}
+
+const char* CachedHistory::Debug::direction(CachedFrame::Direction d) const
+{
+ switch (d) {
+ case CachedFrame::LEFT: return "LEFT"; break;
+ case CachedFrame::RIGHT: return "RIGHT"; break;
+ case CachedFrame::UP: return "UP"; break;
+ case CachedFrame::DOWN: return "DOWN"; break;
+ default: return "UNINITIALIZED";
+ }
+}
+
+void CachedHistory::Debug::print(CachedRoot* root) const
+{
+ CachedHistory* b = base();
+ DUMP_NAV_LOGD("// Visited mVisited[]={\n");
+ for (size_t i = 0; i < NAVIGATION_VISIT_DEPTH; i++) {
+ const Visited& visit = b->mVisited[i];
+ const CachedNode* node = visit.mNode;
+ int index = root != NULL && root->CachedFrame::mDebug.validate(node) ?
+ node->index() : -1;
+ DUMP_NAV_LOGD(" // { 0x%p (%d), %s },\n", node, index, direction(visit.mDirection));
+ }
+ DUMP_NAV_LOGD("// };\n");
+// DUMP_NAV_LOGD("// int mLastScroll=%d;\n", b->mLastScroll);
+ DEBUG_PRINT_RECT(mMouseBounds);
+ DEBUG_PRINT_RECT(mNavBounds);
+ DEBUG_PRINT_RECT(mPriorBounds);
+ DEBUG_PRINT_BOOL(mDirectionChange);
+ DEBUG_PRINT_BOOL(mDidFirstLayout);
+ DUMP_NAV_LOGD("// CachedFrame::Direction mLastMove=%s, mPriorMove=%s;\n",
+ direction(b->mLastMove), direction(b->mPriorMove));
+ int max = b->mMaxWorkingHorizontal;
+ DUMP_NAV_LOGD("static int TEST_MAX_H = %d;\n", max);
+ int min = b->mMinWorkingHorizontal;
+ if (min == INT_MIN)
+ min++;
+ DUMP_NAV_LOGD("static int TEST_MIN_H = %d;\n", min);
+ max = b->mMaxWorkingVertical;
+ DUMP_NAV_LOGD("static int TEST_MAX_V = %d;\n", max);
+ min = b->mMinWorkingVertical;
+ if (min == INT_MIN)
+ min++;
+ DUMP_NAV_LOGD("static int TEST_MIN_V = %d;\n", min);
+ DUMP_NAV_LOGD("\n");
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/nav/CachedHistory.h b/Source/WebKit/android/nav/CachedHistory.h
new file mode 100644
index 0000000..96975ca
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedHistory.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2007, 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 CachedHistory_h
+#define CachedHistory_h
+
+#include "CachedFrame.h"
+
+#define NAVIGATION_VISIT_DEPTH 8 // the number of nodes last visited -- used to detect ping-ponging (number should be tuned)
+
+namespace android {
+
+class CachedRoot;
+
+// CachedHistory is maintained even if DOM is rebuilt by running script.
+// It uses blind pointers for comparison in the previously visited nodes.
+class CachedHistory {
+public:
+ CachedHistory();
+ void addToVisited(const CachedNode* , CachedFrame::Direction );
+ bool checkVisited(const CachedNode* , CachedFrame::Direction ) const;
+ bool didFirstLayout() const { return mDidFirstLayout; }
+ bool directionChange() const { return mDirectionChange; }
+ int minWorkingHorizontal() const { return mMinWorkingHorizontal; }
+ int minWorkingVertical() const { return mMinWorkingVertical; }
+ int maxWorkingHorizontal() const { return mMaxWorkingHorizontal; }
+ int maxWorkingVertical() const { return mMaxWorkingVertical; }
+ const WebCore::IntRect& navBounds() const { return mNavBounds; }
+ const WebCore::IntRect& priorBounds() const { return mPriorBounds; }
+ void setDidFirstLayout(bool did) { mDidFirstLayout = did; }
+ void setMouseBounds(const WebCore::IntRect& loc) { mMouseBounds = loc; }
+ void setNavBounds(const WebCore::IntRect& loc) { mNavBounds = loc; }
+ void setWorking(CachedFrame::Direction , const CachedFrame* ,
+ const CachedNode* , const WebCore::IntRect& viewBounds);
+ void reset();
+private:
+ void pinMaxMin(const WebCore::IntRect& viewBounds);
+ struct Visited {
+ const CachedNode* mNode;
+ CachedFrame::Direction mDirection;
+ } mVisited[NAVIGATION_VISIT_DEPTH];
+ WebCore::IntRect mMouseBounds; // constricted bounds, if cursor ring is partially visible
+ WebCore::IntRect mNavBounds; // cursor ring bounds plus optional keystroke movement
+ WebCore::IntRect mPriorBounds; // prior chosen cursor ring (for reversing narrowing)
+ bool mDirectionChange;
+ bool mDidFirstLayout; // set true when page is newly laid out
+ CachedFrame::Direction mLastMove;
+ CachedFrame::Direction mPriorMove;
+ int mMinWorkingHorizontal;
+ int mMaxWorkingHorizontal;
+ int mMinWorkingVertical;
+ int mMaxWorkingVertical;
+ friend class CachedRoot;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedHistory* base() const;
+ const char* direction(CachedFrame::Direction d) const;
+ void print(CachedRoot* ) const;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedInput.cpp b/Source/WebKit/android/nav/CachedInput.cpp
new file mode 100644
index 0000000..a6a57ef
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedInput.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "CachedInput.h"
+
+namespace android {
+
+void CachedInput::init() {
+ bzero(this, sizeof(CachedInput));
+ mName = WTF::String();
+}
+
+void CachedInput::setTypeFromElement(WebCore::HTMLInputElement* element)
+{
+ ASSERT(element);
+
+ if (element->isPasswordField())
+ mType = PASSWORD;
+ else if (element->isSearchField())
+ mType = SEARCH;
+ else if (element->isEmailField())
+ mType = EMAIL;
+ else if (element->isNumberField())
+ mType = NUMBER;
+ else if (element->isTelephoneField())
+ mType = TELEPHONE;
+ else if (element->isURLField())
+ mType = URL;
+ else
+ mType = NORMAL_TEXT_FIELD;
+}
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_BOOL(field) \
+ DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
+
+CachedInput* CachedInput::Debug::base() const {
+ CachedInput* nav = (CachedInput*) ((char*) this - OFFSETOF(CachedInput, mDebug));
+ return nav;
+}
+
+static void printWebCoreString(const char* label,
+ const WTF::String& string) {
+ char scratch[256];
+ size_t index = snprintf(scratch, sizeof(scratch), label);
+ const UChar* ch = string.characters();
+ while (ch && *ch && index < sizeof(scratch)) {
+ UChar c = *ch++;
+ if (c < ' ' || c >= 0x7f) c = ' ';
+ scratch[index++] = c;
+ }
+ DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
+}
+
+void CachedInput::Debug::print() const
+{
+ CachedInput* b = base();
+ DEBUG_PRINT_BOOL(mAutoComplete);
+ DUMP_NAV_LOGD("// void* mForm=%p;\n", b->mForm);
+ printWebCoreString("// char* mName=\"", b->mName);
+ DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength);
+ 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);
+ DUMP_NAV_LOGD("// float mTextSize=%f;\n", b->mTextSize);
+ DUMP_NAV_LOGD("// int mLineHeight=%d;\n", b->mLineHeight);
+ DUMP_NAV_LOGD("// Type mType=%d;\n", b->mType);
+ DEBUG_PRINT_BOOL(mIsRtlText);
+ DEBUG_PRINT_BOOL(mIsTextField);
+ DEBUG_PRINT_BOOL(mIsTextArea);
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/nav/CachedInput.h b/Source/WebKit/android/nav/CachedInput.h
new file mode 100644
index 0000000..77ae57b
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedInput.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2009, 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 CachedInput_h
+#define CachedInput_h
+
+#include "CachedDebug.h"
+#include "HTMLInputElement.h"
+#include "PlatformString.h"
+
+namespace android {
+
+class CachedInput {
+public:
+ CachedInput() {
+ // Initiaized to 0 in its array, so nothing to do in the
+ // constructor
+ }
+
+ enum Type {
+ NONE = -1,
+ NORMAL_TEXT_FIELD = 0,
+ TEXT_AREA = 1,
+ PASSWORD = 2,
+ SEARCH = 3,
+ EMAIL = 4,
+ NUMBER = 5,
+ TELEPHONE = 6,
+ URL = 7
+ };
+
+ bool autoComplete() const { return mAutoComplete; }
+ void* formPointer() const { return mForm; }
+ void init();
+ void setTypeFromElement(WebCore::HTMLInputElement*);
+ Type getType() const { return mType; }
+ bool isRtlText() const { return mIsRtlText; }
+ bool isTextField() const { return mIsTextField; }
+ bool isTextArea() const { return mIsTextArea; }
+ int lineHeight() const { return mLineHeight; }
+ int maxLength() const { return mMaxLength; };
+ const WTF::String& name() const { return mName; }
+ int paddingBottom() const { return mPaddingBottom; }
+ int paddingLeft() const { return mPaddingLeft; }
+ int paddingRight() const { return mPaddingRight; }
+ int paddingTop() const { return mPaddingTop; }
+ void setAutoComplete(bool autoComplete) { mAutoComplete = autoComplete; }
+ void setFormPointer(void* form) { mForm = form; }
+ void setIsRtlText(bool isRtlText) { mIsRtlText = isRtlText; }
+ void setIsTextField(bool isTextField) { mIsTextField = isTextField; }
+ void setIsTextArea(bool isTextArea) { mIsTextArea = isTextArea; }
+ void setLineHeight(int height) { mLineHeight = height; }
+ void setMaxLength(int maxLength) { mMaxLength = maxLength; }
+ void setName(const WTF::String& name) { mName = name; }
+ void setPaddingBottom(int bottom) { mPaddingBottom = bottom; }
+ void setPaddingLeft(int left) { mPaddingLeft = left; }
+ void setPaddingRight(int right) { mPaddingRight = right; }
+ void setPaddingTop(int top) { mPaddingTop = top; }
+ void setSpellcheck(bool spellcheck) { mSpellcheck = spellcheck; }
+ void setTextSize(float textSize) { mTextSize = textSize; }
+ bool spellcheck() const { return mSpellcheck; }
+ float textSize() const { return mTextSize; }
+
+private:
+
+ void* mForm;
+ int mLineHeight;
+ int mMaxLength;
+ WTF::String mName;
+ int mPaddingBottom;
+ int mPaddingLeft;
+ int mPaddingRight;
+ int mPaddingTop;
+ float mTextSize;
+ Type mType;
+ bool mAutoComplete : 1;
+ bool mSpellcheck : 1;
+ bool mIsRtlText : 1;
+ bool mIsTextField : 1;
+ bool mIsTextArea : 1;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedInput* base() const;
+ void print() const;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedLayer.cpp b/Source/WebKit/android/nav/CachedLayer.cpp
new file mode 100644
index 0000000..f6dfb88
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedLayer.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+
+#include "CachedLayer.h"
+#include "FloatRect.h"
+#include "LayerAndroid.h"
+
+namespace android {
+
+#if USE(ACCELERATED_COMPOSITING)
+
+IntRect CachedLayer::adjustBounds(const LayerAndroid* root,
+ const IntRect& bounds) const
+{
+ const LayerAndroid* aLayer = layer(root);
+ if (!aLayer) {
+ DBG_NAV_LOGD("no layer in root=%p uniqueId=%d", root, mUniqueId);
+#if DUMP_NAV_CACHE
+ if (root)
+ mDebug.printRootLayerAndroid(root);
+#endif
+ return bounds;
+ }
+ FloatRect temp = bounds;
+ // First, remove the original offset from the bounds.
+ temp.move(-mOffset.x(), -mOffset.y());
+
+ // Next, add in the new position of the layer (could be different due to a
+ // fixed position layer).
+ FloatPoint position = getGlobalPosition(aLayer);
+ temp.move(position.x(), position.y());
+
+ // Add in any layer translation.
+ // FIXME: Should use bounds() and apply the entire transformation matrix.
+ const FloatPoint& translation = aLayer->translation();
+ temp.move(translation.x(), translation.y());
+
+ SkRect clip;
+ aLayer->bounds(&clip);
+
+ // Do not try to traverse the parent chain if this is the root as the parent
+ // will not be a LayerAndroid.
+ if (aLayer != root) {
+ LayerAndroid* parent = static_cast<LayerAndroid*>(aLayer->getParent());
+ while (parent) {
+ SkRect pClip;
+ parent->bounds(&pClip);
+
+ // Move our position into our parent's coordinate space.
+ clip.offset(pClip.fLeft, pClip.fTop);
+ // Clip our visible rectangle to the parent.
+ clip.intersect(pClip);
+
+ // Stop at the root.
+ if (parent == root)
+ break;
+ parent = static_cast<LayerAndroid*>(parent->getParent());
+ }
+ }
+
+ // Intersect the result with the visible clip.
+ temp.intersect(clip);
+
+ IntRect result = enclosingIntRect(temp);
+
+ DBG_NAV_LOGV("root=%p aLayer=%p [%d]"
+ " bounds=(%d,%d,w=%d,h=%d) trans=(%g,%g) pos=(%f,%f)"
+ " offset=(%d,%d)"
+ " result=(%d,%d,w=%d,h=%d)",
+ root, aLayer, aLayer->uniqueId(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ translation.x(), translation.y(), position.x(), position.y(),
+ mOffset.x(), mOffset.y(),
+ result.x(), result.y(), result.width(), result.height());
+ return result;
+}
+
+FloatPoint CachedLayer::getGlobalPosition(const LayerAndroid* aLayer) const
+{
+ SkPoint result = aLayer->getPosition();
+ const Layer* parent = aLayer->getParent();
+ while (parent) {
+ result += parent->getPosition();
+ DBG_NAV_LOGV("result=(%g,%g) parent=%p [%d]", result.fX, result.fY,
+ parent, ((LayerAndroid*) parent)->uniqueId());
+ parent = parent->getParent();
+ }
+ return result;
+}
+
+const LayerAndroid* CachedLayer::layer(const LayerAndroid* root) const
+{
+ if (!root)
+ return 0;
+ return root->findById(mUniqueId);
+}
+
+// return bounds relative to the layer as recorded when walking the dom
+IntRect CachedLayer::localBounds(const LayerAndroid* root,
+ const IntRect& bounds) const
+{
+ IntRect temp = bounds;
+ // Remove the original offset from the bounds.
+ temp.move(-mOffset.x(), -mOffset.y());
+
+#if DEBUG_NAV_UI
+ const LayerAndroid* aLayer = layer(root);
+ DBG_NAV_LOGD("aLayer=%p [%d] bounds=(%d,%d,w=%d,h=%d) offset=(%d,%d)"
+ " result=(%d,%d,w=%d,h=%d)",
+ aLayer, aLayer ? aLayer->uniqueId() : 0,
+ bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ mOffset.x(), mOffset.y(),
+ temp.x(), temp.y(), temp.width(), temp.height());
+#endif
+
+ return temp;
+}
+
+SkPicture* CachedLayer::picture(const LayerAndroid* root) const
+{
+ const LayerAndroid* aLayer = layer(root);
+ if (!aLayer)
+ return 0;
+ DBG_NAV_LOGD("root=%p aLayer=%p [%d] picture=%p",
+ root, aLayer, aLayer->uniqueId(), aLayer->picture());
+ return aLayer->picture();
+}
+
+void CachedLayer::toLocal(const LayerAndroid* root, int* xPtr, int* yPtr) const
+{
+ const LayerAndroid* aLayer = layer(root);
+ if (!aLayer)
+ return;
+ DBG_NAV_LOGD("root=%p aLayer=%p [%d]", root, aLayer, aLayer->uniqueId());
+ SkRect localBounds;
+ aLayer->bounds(&localBounds);
+ *xPtr -= localBounds.fLeft;
+ *yPtr -= localBounds.fTop;
+}
+
+#if DUMP_NAV_CACHE
+
+CachedLayer* CachedLayer::Debug::base() const {
+ return (CachedLayer*) ((char*) this - OFFSETOF(CachedLayer, mDebug));
+}
+
+void CachedLayer::Debug::print() const
+{
+ CachedLayer* b = base();
+ DUMP_NAV_LOGD(" // int mCachedNodeIndex=%d;\n", b->mCachedNodeIndex);
+ DUMP_NAV_LOGD(" // int mOffset=(%d, %d);\n",
+ b->mOffset.x(), b->mOffset.y());
+ DUMP_NAV_LOGD(" // int mUniqueId=%p;\n", b->mUniqueId);
+ DUMP_NAV_LOGD("%s\n", "");
+}
+
+#endif
+
+#if DUMP_NAV_CACHE
+
+int CachedLayer::Debug::spaces;
+
+void CachedLayer::Debug::printLayerAndroid(const LayerAndroid* layer)
+{
+ ++spaces;
+ SkRect bounds;
+ layer->bounds(&bounds);
+ DBG_NAV_LOGD("%.*s layer=%p [%d] (%g,%g,%g,%g)"
+ " position=(%g,%g) translation=(%g,%g) anchor=(%g,%g)"
+ " matrix=(%g,%g) childMatrix=(%g,%g) picture=%p clipped=%s"
+ " scrollable=%s\n",
+ spaces, " ", layer, layer->uniqueId(),
+ bounds.fLeft, bounds.fTop, bounds.width(), bounds.height(),
+ layer->getPosition().fX, layer->getPosition().fY,
+ layer->translation().x(), layer->translation().y(),
+ layer->getAnchorPoint().fX, layer->getAnchorPoint().fY,
+ layer->getMatrix().getTranslateX(), layer->getMatrix().getTranslateY(),
+ layer->getChildrenMatrix().getTranslateX(),
+ layer->getChildrenMatrix().getTranslateY(),
+ layer->picture(), layer->m_haveClip ? "true" : "false",
+ layer->contentIsScrollable() ? "true" : "false");
+ for (int i = 0; i < layer->countChildren(); i++)
+ printLayerAndroid(layer->getChild(i));
+ --spaces;
+}
+
+void CachedLayer::Debug::printRootLayerAndroid(const LayerAndroid* layer)
+{
+ spaces = 0;
+ printLayerAndroid(layer);
+}
+#endif
+
+#endif // USE(ACCELERATED_COMPOSITING)
+
+}
+
diff --git a/Source/WebKit/android/nav/CachedLayer.h b/Source/WebKit/android/nav/CachedLayer.h
new file mode 100644
index 0000000..fa427d2
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedLayer.h
@@ -0,0 +1,86 @@
+/*
+ * 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 CachedLayer_h
+#define CachedLayer_h
+
+#include "CachedDebug.h"
+#include "IntRect.h"
+
+class SkPicture;
+
+namespace WebCore {
+ class FloatPoint;
+ class LayerAndroid;
+}
+
+using namespace WebCore;
+
+namespace android {
+
+class CachedLayer {
+public:
+#if USE(ACCELERATED_COMPOSITING)
+ bool operator<(const CachedLayer& l) const {
+ return mCachedNodeIndex < l.mCachedNodeIndex;
+ }
+ // FIXME: adjustBounds should be renamed globalBounds or toGlobal
+ IntRect adjustBounds(const LayerAndroid* root, const IntRect& bounds) const;
+ int cachedNodeIndex() const { return mCachedNodeIndex; }
+ FloatPoint getGlobalPosition(const LayerAndroid* ) const;
+ const LayerAndroid* layer(const LayerAndroid* root) const;
+ IntRect localBounds(const LayerAndroid* root, const IntRect& bounds) const;
+ SkPicture* picture(const LayerAndroid* root) const;
+ void toLocal(const LayerAndroid* root, int* xPtr, int* yPtr) const;
+ void setCachedNodeIndex(int index) { mCachedNodeIndex = index; }
+ // Set the global position of the layer. This is recorded by the nav cache
+ // and corresponds to RenderLayer::absoluteBoundingBox() which is in
+ // document coordinates. This can be different from the global position of
+ // the layer if the layer is fixed positioned or scrollable.
+ void setOffset(const IntPoint& offset) { mOffset = offset; }
+ void setUniqueId(int uniqueId) { mUniqueId = uniqueId; }
+ int uniqueId() const { return mUniqueId; }
+private:
+ int mCachedNodeIndex;
+ IntPoint mOffset;
+ int mUniqueId;
+
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedLayer* base() const;
+ void print() const;
+ static void printLayerAndroid(const LayerAndroid* );
+ static void printRootLayerAndroid(const LayerAndroid* );
+ static int spaces;
+ } mDebug;
+#endif
+#endif // USE(ACCELERATED_COMPOSITING)
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedNode.cpp b/Source/WebKit/android/nav/CachedNode.cpp
new file mode 100644
index 0000000..e500875
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedNode.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "android_graphics.h"
+#include "CachedFrame.h"
+#include "CachedHistory.h"
+#include "Node.h"
+#include "PlatformString.h"
+
+#include "CachedNode.h"
+
+namespace android {
+
+WebCore::IntRect CachedNode::bounds(const CachedFrame* frame) const
+{
+ return mIsInLayer ? frame->adjustBounds(this, mBounds) : mBounds;
+}
+
+void CachedNode::clearCursor(CachedFrame* parent)
+{
+ if (isFrame()) {
+ CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
+ child->clearCursor();
+ }
+ mIsCursor = false;
+}
+
+bool CachedNode::Clip(const WebCore::IntRect& outer, WebCore::IntRect* inner,
+ WTF::Vector<WebCore::IntRect>* rings)
+{
+ if (outer.contains(*inner))
+ return true;
+// DBG_NAV_LOGD("outer:{%d,%d,%d,%d} does not contain inner:{%d,%d,%d,%d}",
+// outer.x(), outer.y(), outer.width(), outer.height(),
+// inner->x(), inner->y(), inner->width(), inner->height());
+ bool intersects = outer.intersects(*inner);
+ size_t size = intersects ? rings->size() : 0;
+ *inner = WebCore::IntRect(0, 0, 0, 0);
+ if (intersects) {
+ WebCore::IntRect * const start = rings->begin();
+ WebCore::IntRect* ring = start + size - 1;
+ do {
+ ring->intersect(outer);
+ if (ring->isEmpty()) {
+ if ((size_t) (ring - start) != --size)
+ *ring = start[size];
+ } else
+ inner->unite(*ring);
+ } while (ring-- != start);
+ }
+ rings->shrink(size);
+// DBG_NAV_LOGD("size:%d", size);
+ return size != 0;
+}
+
+bool CachedNode::clip(const WebCore::IntRect& bounds)
+{
+ return Clip(bounds, &mBounds, &mCursorRing);
+}
+
+
+void CachedNode::cursorRings(const CachedFrame* frame,
+ WTF::Vector<WebCore::IntRect>* rings) const
+{
+ rings->clear();
+ for (unsigned index = 0; index < mCursorRing.size(); index++)
+ rings->append(ring(frame, index));
+}
+
+WebCore::IntRect CachedNode::cursorRingBounds(const CachedFrame* frame) const
+{
+ int partMax = navableRects();
+ WebCore::IntRect bounds;
+ for (int partIndex = 0; partIndex < partMax; partIndex++)
+ bounds.unite(mCursorRing[partIndex]);
+ bounds.inflate(CURSOR_RING_HIT_TEST_RADIUS);
+ return mIsInLayer ? frame->adjustBounds(this, bounds) : bounds;
+}
+
+#define OVERLAP 3
+
+void CachedNode::fixUpCursorRects(const CachedFrame* frame)
+{
+ if (mFixedUpCursorRects)
+ return;
+ mFixedUpCursorRects = true;
+ // if the hit-test rect doesn't intersect any other rect, use it
+ if (mHitBounds != mBounds && mHitBounds.contains(mBounds) &&
+ frame->checkRings(this, mHitBounds)) {
+ DBG_NAV_LOGD("use mHitBounds (%d,%d,%d,%d)", mHitBounds.x(),
+ mHitBounds.y(), mHitBounds.width(), mHitBounds.height());
+ mUseHitBounds = true;
+ return;
+ }
+ if (navableRects() <= 1)
+ return;
+ // if there is more than 1 rect, and the bounds doesn't intersect
+ // any other cursor ring bounds, use it
+ IntRect sloppyBounds = mBounds;
+ sloppyBounds.inflate(2); // give it a couple of extra pixels
+ if (frame->checkRings(this, sloppyBounds)) {
+ DBG_NAV_LOGD("use mBounds (%d,%d,%d,%d)", mBounds.x(),
+ mBounds.y(), mBounds.width(), mBounds.height());
+ mUseBounds = true;
+ return;
+ }
+#if DEBUG_NAV_UI
+ {
+ WebCore::IntRect* boundsPtr = mCursorRing.begin() - 1;
+ const WebCore::IntRect* const boundsEnd = mCursorRing.begin() + mCursorRing.size();
+ while (++boundsPtr < boundsEnd)
+ LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, boundsPtr - mCursorRing.begin(),
+ boundsPtr->x(), boundsPtr->y(), boundsPtr->width(), boundsPtr->height());
+ }
+#endif
+ // q: need to know when rects are for drawing and hit-testing, but not mouse down calcs?
+ bool again;
+ do {
+ again = false;
+ size_t size = mCursorRing.size();
+ WebCore::IntRect* unitBoundsPtr = mCursorRing.begin() - 1;
+ const WebCore::IntRect* const unitBoundsEnd = mCursorRing.begin() + size;
+ while (++unitBoundsPtr < unitBoundsEnd) {
+ // any other unitBounds to the left or right of this one?
+ int unitTop = unitBoundsPtr->y();
+ int unitBottom = unitBoundsPtr->maxY();
+ int unitLeft = unitBoundsPtr->x();
+ int unitRight = unitBoundsPtr->maxX();
+ WebCore::IntRect* testBoundsPtr = mCursorRing.begin() - 1;
+ while (++testBoundsPtr < unitBoundsEnd) {
+ if (unitBoundsPtr == testBoundsPtr)
+ continue;
+ int testTop = testBoundsPtr->y();
+ int testBottom = testBoundsPtr->maxY();
+ int testLeft = testBoundsPtr->x();
+ int testRight = testBoundsPtr->maxX();
+ int candidateTop = unitTop > testTop ? unitTop : testTop;
+ int candidateBottom = unitBottom < testBottom ? unitBottom : testBottom;
+ int candidateLeft = unitRight < testLeft ? unitRight : testRight;
+ int candidateRight = unitRight > testLeft ? unitLeft : testLeft;
+ bool leftRight = true;
+ if (candidateTop + OVERLAP >= candidateBottom ||
+ candidateLeft + OVERLAP >= candidateRight) {
+ candidateTop = unitBottom < testTop ? unitBottom : testBottom;
+ candidateBottom = unitBottom > testTop ? unitTop : testTop;
+ candidateLeft = unitLeft > testLeft ? unitLeft : testLeft;
+ candidateRight = unitRight < testRight ? unitRight : testRight;
+ if (candidateTop + OVERLAP >= candidateBottom ||
+ candidateLeft + OVERLAP >= candidateRight)
+ continue;
+ leftRight = false;
+ }
+ // construct candidate to add
+ WebCore::IntRect candidate = WebCore::IntRect(candidateLeft, candidateTop,
+ candidateRight - candidateLeft, candidateBottom - candidateTop);
+ // does a different unit bounds intersect the candidate? if so, don't add
+ WebCore::IntRect* checkBoundsPtr = mCursorRing.begin() - 1;
+ while (++checkBoundsPtr < unitBoundsEnd) {
+ if (checkBoundsPtr->intersects(candidate) == false)
+ continue;
+ if (leftRight) {
+ if (candidateTop >= checkBoundsPtr->y() &&
+ candidateBottom > checkBoundsPtr->maxY())
+ candidateTop = checkBoundsPtr->maxY();
+ else if (candidateTop < checkBoundsPtr->y() &&
+ candidateBottom <= checkBoundsPtr->maxY())
+ candidateBottom = checkBoundsPtr->y();
+ else
+ goto nextCheck;
+ } else {
+ if (candidateLeft >= checkBoundsPtr->x() &&
+ candidateRight > checkBoundsPtr->maxX())
+ candidateLeft = checkBoundsPtr->maxX();
+ else if (candidateLeft < checkBoundsPtr->x() &&
+ candidateRight <= checkBoundsPtr->maxX())
+ candidateRight = checkBoundsPtr->x();
+ else
+ goto nextCheck;
+ }
+ }
+ candidate = WebCore::IntRect(candidateLeft, candidateTop,
+ candidateRight - candidateLeft, candidateBottom - candidateTop);
+ ASSERT(candidate.isEmpty() == false);
+#if DEBUG_NAV_UI
+ LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, mCursorRing.size(),
+ candidate.x(), candidate.y(), candidate.width(), candidate.height());
+#endif
+ mCursorRing.append(candidate);
+ again = true;
+ goto tryAgain;
+ nextCheck:
+ continue;
+ }
+ }
+tryAgain:
+ ;
+ } while (again);
+}
+
+
+void CachedNode::hideCursor(CachedFrame* parent)
+{
+ if (isFrame()) {
+ CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
+ child->hideCursor();
+ }
+ mIsHidden = true;
+}
+
+WebCore::IntRect CachedNode::hitBounds(const CachedFrame* frame) const
+{
+ return mIsInLayer ? frame->adjustBounds(this, mHitBounds) : mHitBounds;
+}
+
+void CachedNode::init(WebCore::Node* node)
+{
+ bzero(this, sizeof(CachedNode));
+ mExport = WTF::String();
+ mNode = node;
+ mParentIndex = mDataIndex = -1;
+ mType = android::NORMAL_CACHEDNODETYPE;
+}
+
+bool CachedNode::isTextField(const CachedFrame* frame) const
+{
+ const CachedInput* input = frame->textInput(this);
+ return input ? input->isTextField() : false;
+}
+
+void CachedNode::localCursorRings(const CachedFrame* frame,
+ WTF::Vector<WebCore::IntRect>* rings) const
+{
+ rings->clear();
+ for (unsigned index = 0; index < mCursorRing.size(); index++)
+ rings->append(localRing(frame, index));
+}
+
+WebCore::IntRect CachedNode::localBounds(const CachedFrame* frame) const
+{
+ return mIsInLayer ? frame->localBounds(this, mBounds) : mBounds;
+}
+
+WebCore::IntRect CachedNode::localHitBounds(const CachedFrame* frame) const
+{
+ return mIsInLayer ? frame->localBounds(this, mHitBounds) : mHitBounds;
+}
+
+WebCore::IntRect CachedNode::localRing(const CachedFrame* frame,
+ size_t part) const
+{
+ const WebCore::IntRect& rect = mCursorRing.at(part);
+ return mIsInLayer ? frame->localBounds(this, rect) : rect;
+}
+
+void CachedNode::move(int x, int y)
+{
+ mBounds.move(x, y);
+ // mHitTestBounds will be moved by caller
+ WebCore::IntRect* first = mCursorRing.begin();
+ WebCore::IntRect* last = first + mCursorRing.size();
+ --first;
+ while (++first != last)
+ first->move(x, y);
+}
+
+bool CachedNode::partRectsContains(const CachedNode* other) const
+{
+ int outerIndex = 0;
+ int outerMax = navableRects();
+ int innerMax = other->navableRects();
+ do {
+ const WebCore::IntRect& outerBounds = mCursorRing[outerIndex];
+ int innerIndex = 0;
+ do {
+ const WebCore::IntRect& innerBounds = other->mCursorRing[innerIndex];
+ if (innerBounds.contains(outerBounds))
+ return true;
+ } while (++innerIndex < innerMax);
+ } while (++outerIndex < outerMax);
+ return false;
+}
+
+WebCore::IntRect CachedNode::ring(const CachedFrame* frame, size_t part) const
+{
+ const WebCore::IntRect& rect = mCursorRing.at(part);
+ return mIsInLayer ? frame->adjustBounds(this, rect) : rect;
+}
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_BOOL(field) \
+ DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
+
+#define DEBUG_PRINT_RECT(field) \
+ { const WebCore::IntRect& r = b->field; \
+ DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
+ r.x(), r.y(), r.width(), r.height()); }
+
+CachedNode* CachedNode::Debug::base() const {
+ CachedNode* nav = (CachedNode*) ((char*) this - OFFSETOF(CachedNode, mDebug));
+ return nav;
+}
+
+const char* CachedNode::Debug::condition(Condition t) const
+{
+ switch (t) {
+ case NOT_REJECTED: return "NOT_REJECTED"; break;
+ case BUTTED_UP: return "BUTTED_UP"; break;
+ case CENTER_FURTHER: return "CENTER_FURTHER"; break;
+ case CLOSER: return "CLOSER"; break;
+ case CLOSER_IN_CURSOR: return "CLOSER_IN_CURSOR"; break;
+ case CLOSER_OVERLAP: return "CLOSER_OVERLAP"; break;
+ case CLOSER_TOP: return "CLOSER_TOP"; break;
+ case NAVABLE: return "NAVABLE"; break;
+ case FURTHER: return "FURTHER"; break;
+ case IN_UMBRA: return "IN_UMBRA"; break;
+ case IN_WORKING: return "IN_WORKING"; break;
+ case LEFTMOST: return "LEFTMOST"; break;
+ case OVERLAP_OR_EDGE_FURTHER: return "OVERLAP_OR_EDGE_FURTHER"; break;
+ case PREFERRED: return "PREFERRED"; break;
+ case ANCHOR_IN_ANCHOR: return "ANCHOR_IN_ANCHOR"; break;
+ case BEST_DIRECTION: return "BEST_DIRECTION"; break;
+ case CHILD: return "CHILD"; break;
+ case DISABLED: return "DISABLED"; break;
+ case HIGHER_TAB_INDEX: return "HIGHER_TAB_INDEX"; break;
+ case IN_CURSOR: return "IN_CURSOR"; break;
+ case IN_CURSOR_CHILDREN: return "IN_CURSOR_CHILDREN"; break;
+ case NOT_ENCLOSING_CURSOR: return "NOT_ENCLOSING_CURSOR"; break;
+ case NOT_CURSOR_NODE: return "NOT_CURSOR_NODE"; break;
+ case OUTSIDE_OF_BEST: return "OUTSIDE_OF_BEST"; break;
+ case OUTSIDE_OF_ORIGINAL: return "OUTSIDE_OF_ORIGINAL"; break;
+ default: return "???";
+ }
+}
+
+const char* CachedNode::Debug::type(android::CachedNodeType t) const
+{
+ switch (t) {
+ case NORMAL_CACHEDNODETYPE: return "NORMAL"; break;
+ case ADDRESS_CACHEDNODETYPE: return "ADDRESS"; break;
+ case EMAIL_CACHEDNODETYPE: return "EMAIL"; break;
+ case PHONE_CACHEDNODETYPE: return "PHONE"; break;
+ case ANCHOR_CACHEDNODETYPE: return "ANCHOR"; break;
+ case AREA_CACHEDNODETYPE: return "AREA"; break;
+ case FRAME_CACHEDNODETYPE: return "FRAME"; break;
+ case PLUGIN_CACHEDNODETYPE: return "PLUGIN"; break;
+ case TEXT_INPUT_CACHEDNODETYPE: return "INPUT"; break;
+ case SELECT_CACHEDNODETYPE: return "SELECT"; break;
+ case CONTENT_EDITABLE_CACHEDNODETYPE: return "CONTENT_EDITABLE"; break;
+ default: return "???";
+ }
+}
+
+void CachedNode::Debug::print() const
+{
+ CachedNode* b = base();
+ char scratch[256];
+ size_t index = snprintf(scratch, sizeof(scratch), "// char* mExport=\"");
+ const UChar* ch = b->mExport.characters();
+ while (ch && *ch && index < sizeof(scratch)) {
+ UChar c = *ch++;
+ if (c < ' ' || c >= 0x7f) c = ' ';
+ scratch[index++] = c;
+ }
+ DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
+ DEBUG_PRINT_RECT(mBounds);
+ DEBUG_PRINT_RECT(mHitBounds);
+ DEBUG_PRINT_RECT(mOriginalAbsoluteBounds);
+ const WTF::Vector<WebCore::IntRect>* rects = &b->mCursorRing;
+ size_t size = rects->size();
+ DUMP_NAV_LOGD("// IntRect cursorRings={ // size=%d\n", size);
+ for (size_t i = 0; i < size; i++) {
+ const WebCore::IntRect& rect = (*rects)[i];
+ DUMP_NAV_LOGD(" // {%d, %d, %d, %d}, // %d\n", rect.x(), rect.y(),
+ rect.width(), rect.height(), i);
+ }
+ DUMP_NAV_LOGD("// };\n");
+ DUMP_NAV_LOGD("// void* mNode=%p; // (%d) \n", b->mNode, mNodeIndex);
+ DUMP_NAV_LOGD("// void* mParentGroup=%p; // (%d) \n", b->mParentGroup, mParentGroupIndex);
+ DUMP_NAV_LOGD("// int mDataIndex=%d;\n", b->mDataIndex);
+ DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex);
+ DUMP_NAV_LOGD("// int navableRects()=%d;\n", b->navableRects());
+ DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex);
+ DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex);
+ DUMP_NAV_LOGD("// int mColorIndex=%d;\n", b->mColorIndex);
+ DUMP_NAV_LOGD("// Condition mCondition=%s;\n", condition(b->mCondition));
+ DUMP_NAV_LOGD("// Type mType=%s;\n", type(b->mType));
+ DEBUG_PRINT_BOOL(mClippedOut);
+ DEBUG_PRINT_BOOL(mDisabled);
+ DEBUG_PRINT_BOOL(mFixedUpCursorRects);
+ DEBUG_PRINT_BOOL(mHasCursorRing);
+ DEBUG_PRINT_BOOL(mHasMouseOver);
+ DEBUG_PRINT_BOOL(mIsCursor);
+ DEBUG_PRINT_BOOL(mIsFocus);
+ DEBUG_PRINT_BOOL(mIsHidden);
+ DEBUG_PRINT_BOOL(mIsInLayer);
+ DEBUG_PRINT_BOOL(mIsParentAnchor);
+ DEBUG_PRINT_BOOL(mIsTransparent);
+ DEBUG_PRINT_BOOL(mIsUnclipped);
+ DEBUG_PRINT_BOOL(mLast);
+ DEBUG_PRINT_BOOL(mUseBounds);
+ DEBUG_PRINT_BOOL(mUseHitBounds);
+ DEBUG_PRINT_BOOL(mSingleImage);
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/nav/CachedNode.h b/Source/WebKit/android/nav/CachedNode.h
new file mode 100644
index 0000000..321b7fd
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedNode.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2007, 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 CachedNode_h
+#define CachedNode_h
+
+#include "CachedDebug.h"
+#include "CachedNodeType.h"
+#include "IntRect.h"
+#include "PlatformString.h"
+
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+
+class SkPicture;
+
+namespace WebCore {
+ class Node;
+}
+
+namespace android {
+
+class CachedFrame;
+class CachedRoot;
+
+class CachedNode {
+public:
+// Nodes are rejected because either they are spacially not the best (first set)
+// or because they have the wrong DOM attribute (in focus, a focused child, etc)
+// findClosest() gives only spacially rejected nodes a second chance
+ enum Condition { // if bigger than 32, increase bitfield size below
+ // rejections that get a second chance
+ NOT_REJECTED = 0,
+ SECOND_CHANCE_START = NOT_REJECTED, // must be first in list
+ BUTTED_UP,
+ CENTER_FURTHER,
+ CLOSER,
+ CLOSER_IN_CURSOR,
+ CLOSER_OVERLAP,
+ CLOSER_TOP,
+ NAVABLE,
+ FURTHER,
+ IN_UMBRA,
+ IN_WORKING,
+ LEFTMOST,
+ NOT_ENCLOSING_CURSOR,
+ OVERLAP_OR_EDGE_FURTHER,
+ PREFERRED, // better overlap measure
+ SECOND_CHANCE_END = PREFERRED, // must be last in list
+ // rejections that don't get a second chance
+ ANCHOR_IN_ANCHOR,
+ BEST_DIRECTION, // can be reached by another direction
+ CHILD,
+ DISABLED,
+ HIGHER_TAB_INDEX,
+ IN_CURSOR,
+ IN_CURSOR_CHILDREN,
+ NOT_CURSOR_NODE,
+ OUTSIDE_OF_BEST, // containership
+ OUTSIDE_OF_ORIGINAL, // containership
+ UNDER_LAYER,
+ CONDITION_SIZE // FIXME: test that CONDITION_SIZE fits in mCondition
+ };
+ CachedNode() {
+ // The node is initiaized to 0 in its array, so nothing to do in the
+ // constructor
+ }
+
+ WebCore::IntRect bounds(const CachedFrame* ) const;
+ int childFrameIndex() const { return isFrame() ? mDataIndex : -1; }
+ void clearCondition() const { mCondition = NOT_REJECTED; }
+ void clearCursor(CachedFrame* );
+ static bool Clip(const WebCore::IntRect& outer, WebCore::IntRect* inner,
+ WTF::Vector<WebCore::IntRect>* rings);
+ bool clip(const WebCore::IntRect& );
+ bool clippedOut() { return mClippedOut; }
+ int colorIndex() const { return mColorIndex; }
+ WebCore::IntRect cursorRingBounds(const CachedFrame* ) const;
+ void cursorRings(const CachedFrame* , WTF::Vector<WebCore::IntRect>* ) const;
+ bool disabled() const { return mDisabled; }
+ const CachedNode* document() const { return &this[-mIndex]; }
+ void fixUpCursorRects(const CachedFrame* frame);
+ const WTF::String& getExport() const { return mExport; }
+ bool hasCursorRing() const { return mHasCursorRing; }
+ bool hasMouseOver() const { return mHasMouseOver; }
+ void hideCursor(CachedFrame* );
+ WebCore::IntRect hitBounds(const CachedFrame* ) const;
+ int index() const { return mIndex; }
+ void init(WebCore::Node* node);
+ bool isAnchor() const { return mType == ANCHOR_CACHEDNODETYPE; }
+ bool isContentEditable() const { return mType == CONTENT_EDITABLE_CACHEDNODETYPE; }
+ bool isCursor() const { return mIsCursor; }
+ bool isArea() const { return mType == AREA_CACHEDNODETYPE; }
+ bool isFocus() const { return mIsFocus; }
+ bool isFrame() const { return mType == FRAME_CACHEDNODETYPE; }
+ bool isHidden() const { return mIsHidden; }
+ bool isInLayer() const { return mIsInLayer; }
+ bool isNavable(const CachedFrame* frame, const WebCore::IntRect& clip) const {
+ return clip.intersects(bounds(frame));
+ }
+ bool isPlugin() const { return mType == PLUGIN_CACHEDNODETYPE; }
+ bool isSelect() const { return mType == SELECT_CACHEDNODETYPE; }
+ bool isSyntheticLink() const {
+ return mType >= ADDRESS_CACHEDNODETYPE && mType <= PHONE_CACHEDNODETYPE;
+ }
+ bool isTextField(const CachedFrame*) const;
+ bool isTextInput() const { return mType == TEXT_INPUT_CACHEDNODETYPE; }
+ bool isTransparent() const { return mIsTransparent; }
+ bool isUnclipped() const { return mIsUnclipped; }
+ // localXXX functions are used only for drawing cursor rings
+ WebCore::IntRect localBounds(const CachedFrame* ) const;
+ void localCursorRings(const CachedFrame* ,
+ WTF::Vector<WebCore::IntRect>* ) const;
+ WebCore::IntRect localHitBounds(const CachedFrame* ) const;
+ WebCore::IntRect localRing(const CachedFrame* , size_t part) const;
+ void move(int x, int y);
+ int navableRects() const { return mCursorRing.size(); }
+ void* nodePointer() const { return mNode; }
+ bool noSecondChance() const { return mCondition > SECOND_CHANCE_END; }
+ const WebCore::IntRect& originalAbsoluteBounds() const {
+ return mOriginalAbsoluteBounds; }
+ const CachedNode* parent() const { return document() + mParentIndex; }
+ void* parentGroup() const { return mParentGroup; }
+ int parentIndex() const { return mParentIndex; }
+ bool partRectsContains(const CachedNode* other) const;
+ const WebCore::IntRect& rawBounds() const { return mBounds; }
+ void reset();
+ WebCore::IntRect ring(const CachedFrame* , size_t part) const;
+ const WTF::Vector<WebCore::IntRect>& rings() const { return mCursorRing; }
+ void setBounds(const WebCore::IntRect& bounds) { mBounds = bounds; }
+ void setClippedOut(bool clipped) { mClippedOut = clipped; }
+ void setColorIndex(int index) { mColorIndex = index; }
+ void setCondition(Condition condition) const { mCondition = condition; }
+ void setDataIndex(int index) { mDataIndex = index; }
+ void setDisabled(bool disabled) { mDisabled = disabled; }
+ void setExport(const WTF::String& exported) { mExport = exported; }
+ void setHasCursorRing(bool hasRing) { mHasCursorRing = hasRing; }
+ void setHasMouseOver(bool hasMouseOver) { mHasMouseOver = hasMouseOver; }
+ void setHitBounds(const WebCore::IntRect& bounds) { mHitBounds = bounds; }
+ void setOriginalAbsoluteBounds(const WebCore::IntRect& bounds) {
+ mOriginalAbsoluteBounds = bounds; }
+ void setIndex(int index) { mIndex = index; }
+ void setIsCursor(bool isCursor) { mIsCursor = isCursor; }
+ void setIsFocus(bool isFocus) { mIsFocus = isFocus; }
+ void setIsInLayer(bool isInLayer) { mIsInLayer = isInLayer; }
+ void setIsParentAnchor(bool isAnchor) { mIsParentAnchor = isAnchor; }
+ void setIsTransparent(bool isTransparent) { mIsTransparent = isTransparent; }
+ void setIsUnclipped(bool unclipped) { mIsUnclipped = unclipped; }
+ void setLast() { mLast = true; }
+ void setParentGroup(void* group) { mParentGroup = group; }
+ void setParentIndex(int parent) { mParentIndex = parent; }
+ void setSingleImage(bool single) { mSingleImage = single; }
+ void setTabIndex(int index) { mTabIndex = index; }
+ void setType(CachedNodeType type) { mType = type; }
+ void show() { mIsHidden = false; }
+ bool singleImage() const { return mSingleImage; }
+ int tabIndex() const { return mTabIndex; }
+ int textInputIndex() const { return isTextInput() ? mDataIndex : -1; }
+ const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; }
+ bool useBounds() const { return mUseBounds; }
+ bool useHitBounds() const { return mUseHitBounds; }
+ bool wantsKeyEvents() const { return isTextInput() || isPlugin()
+ || isContentEditable() || isFrame(); }
+private:
+ friend class CacheBuilder;
+ WTF::String mExport;
+ WebCore::IntRect mBounds;
+ WebCore::IntRect mHitBounds;
+ WebCore::IntRect mOriginalAbsoluteBounds;
+ WTF::Vector<WebCore::IntRect> mCursorRing;
+ void* mNode; // WebCore::Node*, only used to match pointers
+ void* mParentGroup; // WebCore::Node*, only used to match pointers
+ int mDataIndex; // child frame if a frame; input data index; or -1
+ int mIndex; // index of itself, to find first in array (document)
+ int mParentIndex;
+ int mTabIndex;
+ int mColorIndex; // index to ring color and other stylable properties
+ mutable Condition mCondition : 5; // why the node was not chosen on the first pass
+ CachedNodeType mType : 4;
+ bool mClippedOut : 1;
+ bool mDisabled : 1;
+ bool mFixedUpCursorRects : 1;
+ bool mHasCursorRing : 1;
+ bool mHasMouseOver : 1;
+ bool mIsCursor : 1;
+ bool mIsFocus : 1;
+ bool mIsHidden : 1;
+ bool mIsInLayer : 1;
+ bool mIsParentAnchor : 1;
+ bool mIsTransparent : 1;
+ bool mIsUnclipped : 1;
+ bool mLast : 1; // true if this is the last node in a group
+ bool mSingleImage : 1;
+ bool mUseBounds : 1;
+ bool mUseHitBounds : 1;
+#ifdef BROWSER_DEBUG
+public:
+ WebCore::Node* webCoreNode() const { return (WebCore::Node*) mNode; }
+ bool mDisplayMeasure;
+ mutable bool mInCompare;
+ int mSideDistance;
+ int mSecondSide;
+#endif
+#if DEBUG_NAV_UI || DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedNode* base() const;
+ const char* condition(Condition t) const;
+ void print() const;
+ const char* type(CachedNodeType t) const;
+#if DUMP_NAV_CACHE
+ int mNodeIndex;
+ int mParentGroupIndex;
+#endif
+ } mDebug;
+ friend class CachedNode::Debug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedNodeType.h b/Source/WebKit/android/nav/CachedNodeType.h
new file mode 100644
index 0000000..f922946
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedNodeType.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007, 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 CachedNodeType_h
+#define CachedNodeType_h
+
+namespace android {
+
+enum CachedNodeType {
+ NORMAL_CACHEDNODETYPE,
+ ADDRESS_CACHEDNODETYPE,
+ EMAIL_CACHEDNODETYPE,
+ PHONE_CACHEDNODETYPE,
+ ANCHOR_CACHEDNODETYPE,
+ AREA_CACHEDNODETYPE,
+ FRAME_CACHEDNODETYPE,
+ PLUGIN_CACHEDNODETYPE,
+ TEXT_INPUT_CACHEDNODETYPE,
+ SELECT_CACHEDNODETYPE,
+ CONTENT_EDITABLE_CACHEDNODETYPE
+};
+
+enum CachedNodeBits {
+ NORMAL_CACHEDNODE_BITS = 0,
+ ADDRESS_CACHEDNODE_BIT = 1 << (ADDRESS_CACHEDNODETYPE - 1),
+ EMAIL_CACHEDNODE_BIT = 1 << (EMAIL_CACHEDNODETYPE - 1),
+ PHONE_CACHEDNODE_BIT = 1 << (PHONE_CACHEDNODETYPE - 1),
+ ALL_CACHEDNODE_BITS = ADDRESS_CACHEDNODE_BIT | EMAIL_CACHEDNODE_BIT
+ | PHONE_CACHEDNODE_BIT
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedPrefix.h b/Source/WebKit/android/nav/CachedPrefix.h
new file mode 100644
index 0000000..576aa4a
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedPrefix.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007, 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 CachedPrefix_h
+#define CachedPrefix_h
+
+#ifndef LOG_TAG
+#define LOG_TAG "navcache"
+#endif
+
+#include "config.h"
+#include "CachedDebug.h"
+
+#ifndef _LIBS_CUTILS_LOG_H
+ #ifdef LOG
+ #undef LOG
+ #endif
+
+ #include <utils/Log.h>
+#endif
+
+#define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning
+
+#ifndef BZERO_DEFINED
+#define BZERO_DEFINED
+// http://www.opengroup.org/onlinepubs/000095399/functions/bzero.html
+// For maximum portability, it is recommended to replace the function call to bzero() as follows:
+#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
+#endif
+
+#endif
diff --git a/Source/WebKit/android/nav/CachedRoot.cpp b/Source/WebKit/android/nav/CachedRoot.cpp
new file mode 100644
index 0000000..2371c4f
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedRoot.cpp
@@ -0,0 +1,1815 @@
+/*
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "CachedPrefix.h"
+#include "android_graphics.h"
+#include "CachedHistory.h"
+#include "CachedInput.h"
+#include "CachedLayer.h"
+#include "CachedNode.h"
+#include "FindCanvas.h"
+#include "FloatRect.h"
+#include "LayerAndroid.h"
+#include "ParseCanvas.h"
+#include "SkBitmap.h"
+#include "SkBounder.h"
+#include "SkPixelRef.h"
+#include "SkRegion.h"
+
+#include "CachedRoot.h"
+
+#if DEBUG_NAV_UI
+#include "wtf/text/CString.h"
+#endif
+
+#define DONT_CENTER_IF_ALREADY_VISIBLE
+
+using std::min;
+using std::max;
+
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ extern android::Mutex gWriteLogMutex;
+#endif
+
+namespace android {
+
+class CommonCheck : public SkBounder {
+public:
+ enum Type {
+ kNo_Type,
+ kDrawBitmap_Type,
+ kDrawGlyph_Type,
+ kDrawPaint_Type,
+ kDrawPath_Type,
+ kDrawPicture_Type,
+ kDrawPoints_Type,
+ kDrawPosText_Type,
+ kDrawPosTextH_Type,
+ kDrawRect_Type,
+ kDrawSprite_Type,
+ kDrawText_Type,
+ kDrawTextOnPath_Type,
+ kPopLayer_Type,
+ kPushLayer_Type,
+ kPushSave_Type
+ };
+
+ static bool isTextType(Type t) {
+ return t == kDrawPosTextH_Type || t == kDrawText_Type;
+ }
+
+ CommonCheck() : mType(kNo_Type), mAllOpaque(true), mIsOpaque(true) {
+ setEmpty();
+ }
+
+ bool doRect(Type type) {
+ mType = type;
+ return doIRect(mUnion);
+ }
+
+ bool isEmpty() { return mUnion.isEmpty(); }
+
+ bool joinGlyphs(const SkIRect& rect) {
+ bool isGlyph = mType == kDrawGlyph_Type;
+ if (isGlyph)
+ mUnion.join(rect);
+ return isGlyph;
+ }
+
+ void setAllOpaque(bool opaque) { mAllOpaque = opaque; }
+ void setEmpty() { mUnion.setEmpty(); }
+ void setIsOpaque(bool opaque) { mIsOpaque = opaque; }
+ void setType(Type type) { mType = type; }
+
+ Type mType;
+ SkIRect mUnion;
+ bool mAllOpaque;
+ bool mIsOpaque;
+};
+
+#if DEBUG_NAV_UI
+ static const char* TypeNames[] = {
+ "kNo_Type",
+ "kDrawBitmap_Type",
+ "kDrawGlyph_Type",
+ "kDrawPaint_Type",
+ "kDrawPath_Type",
+ "kDrawPicture_Type",
+ "kDrawPoints_Type",
+ "kDrawPosText_Type",
+ "kDrawPosTextH_Type",
+ "kDrawRect_Type",
+ "kDrawSprite_Type",
+ "kDrawText_Type",
+ "kDrawTextOnPath_Type",
+ "kPopLayer_Type",
+ "kPushLayer_Type",
+ "kPushSave_Type"
+ };
+#endif
+
+#define kMargin 16
+#define kSlop 2
+
+class BoundsCanvas : public ParseCanvas {
+public:
+
+ BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) {
+ mTransparentLayer = 0;
+ setBounder(bounder);
+ }
+
+ virtual void drawPaint(const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPaint_Type);
+ INHERITED::drawPaint(paint);
+ }
+
+ virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPoints_Type);
+ INHERITED::drawPoints(mode, count, pts, paint);
+ }
+
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawRect_Type);
+ INHERITED::drawRect(rect, paint);
+ }
+
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPath_Type);
+ INHERITED::drawPath(path, paint);
+ }
+
+ virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawBitmap_Type);
+ mBounder.setIsOpaque(bitmap.isOpaque());
+ INHERITED::commonDrawBitmap(bitmap, rect, matrix, paint);
+ }
+
+ virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) {
+ mBounder.setType(CommonCheck::kDrawSprite_Type);
+ mBounder.setIsOpaque(bitmap.isOpaque() &&
+ (!paint || paint->getAlpha() == 255));
+ INHERITED::drawSprite(bitmap, left, top, paint);
+ }
+
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ mBounder.setEmpty();
+ mBounder.setType(CommonCheck::kDrawGlyph_Type);
+ INHERITED::drawText(text, byteLength, x, y, paint);
+ mBounder.doRect(CommonCheck::kDrawText_Type);
+ }
+
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ mBounder.setEmpty();
+ mBounder.setType(CommonCheck::kDrawGlyph_Type);
+ INHERITED::drawPosText(text, byteLength, pos, paint);
+ if (!mBounder.isEmpty())
+ mBounder.doRect(CommonCheck::kDrawPosText_Type);
+ }
+
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ mBounder.setEmpty();
+ mBounder.setType(CommonCheck::kDrawGlyph_Type);
+ INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+ if (mBounder.mUnion.isEmpty()) {
+ DBG_NAV_LOGD("empty constY=%g", SkScalarToFloat(constY));
+ return;
+ }
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+ SkPoint upDown[2] = { {xpos[0], constY + metrics.fAscent},
+ {xpos[0], constY + metrics.fDescent} };
+ const SkMatrix& matrix = getTotalMatrix();
+ matrix.mapPoints(upDown, 2);
+ if (upDown[0].fX == upDown[1].fX) {
+ mBounder.mUnion.fTop = SkScalarFloor(upDown[0].fY);
+ mBounder.mUnion.fBottom = SkScalarFloor(upDown[1].fY);
+ }
+ mBounder.doRect(CommonCheck::kDrawPosTextH_Type);
+ }
+
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ mBounder.setEmpty();
+ mBounder.setType(CommonCheck::kDrawGlyph_Type);
+ INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
+ mBounder.doRect(CommonCheck::kDrawTextOnPath_Type);
+ }
+
+ virtual void drawPicture(SkPicture& picture) {
+ mBounder.setType(CommonCheck::kDrawPicture_Type);
+ INHERITED::drawPicture(picture);
+ }
+
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ int depth = INHERITED::saveLayer(bounds, paint, flags);
+ if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) {
+ mTransparentLayer = depth;
+ mBounder.setAllOpaque(false);
+ }
+ return depth;
+ }
+
+ virtual void restore() {
+ mBounder.setType(CommonCheck::kDrawSprite_Type); // for layer draws
+ int depth = getSaveCount();
+ if (depth == mTransparentLayer) {
+ mTransparentLayer = 0;
+ mBounder.setAllOpaque(true);
+ }
+ INHERITED::restore();
+ }
+
+ int mTransparentLayer;
+ CommonCheck& mBounder;
+private:
+ typedef ParseCanvas INHERITED;
+};
+
+/*
+LeftCheck examines the text in a picture, within a viewable rectangle,
+and returns via left() the position of the left edge of the paragraph.
+It first looks at the left edge of the test point, then looks above and below
+it for more lines of text to determine the div's left edge.
+*/
+class LeftCheck : public CommonCheck {
+public:
+ LeftCheck(int x, int y) : mX(x), mY(y), mHitLeft(INT_MAX),
+ mMostLeft(INT_MAX) {
+ mHit.set(x - (HIT_SLOP << 1), y - HIT_SLOP, x, y + HIT_SLOP);
+ mPartial.setEmpty();
+ mBounds.setEmpty();
+ mPartialType = kNo_Type;
+ }
+
+ int left() {
+ if (isTextType(mType))
+ doRect(); // process the final line of text
+ return mMostLeft != INT_MAX ? mMostLeft : mX >> 1;
+ }
+
+ // FIXME: this is identical to CenterCheck::onIRect()
+ // refactor so that LeftCheck and CenterCheck inherit common functions
+ virtual bool onIRect(const SkIRect& rect) {
+ bool opaqueBitmap = mType == kDrawBitmap_Type && mIsOpaque;
+ if (opaqueBitmap && rect.contains(mX, mY)) {
+ mMostLeft = rect.fLeft;
+ return false;
+ }
+ if (joinGlyphs(rect)) // assembles glyphs into a text string
+ return false;
+ if (!isTextType(mType) && !opaqueBitmap)
+ return false;
+ /* Text on one line may be broken into several parts. Reassemble
+ the text into a rectangle before considering it. */
+ if (rect.fTop < mPartial.fBottom
+ && rect.fBottom > mPartial.fTop
+ && mPartial.fRight + JOIN_SLOP_X >= rect.fLeft
+ && (mPartialType != kDrawBitmap_Type
+ || mPartial.height() <= rect.height() + JOIN_SLOP_Y)) {
+ DBG_NAV_LOGD("LeftCheck join mPartial=(%d, %d, %d, %d)"
+ " rect=(%d, %d, %d, %d)",
+ mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+ mPartial.join(rect);
+ return false;
+ }
+ if (mPartial.isEmpty() == false) {
+ doRect(); // process the previous line of text
+#if DEBUG_NAV_UI
+ if (mHitLeft == INT_MAX)
+ DBG_NAV_LOGD("LeftCheck disabled rect=(%d, %d, %d, %d)",
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+#endif
+ }
+ mPartial = rect;
+ mPartialType = mType;
+ return false;
+ }
+
+ void doRect()
+ {
+ /* Record the outer bounds of the lines of text that intersect the
+ touch coordinates, given some slop */
+ if (SkIRect::Intersects(mPartial, mHit)) {
+ if (mHitLeft > mPartial.fLeft)
+ mHitLeft = mPartial.fLeft;
+ DBG_NAV_LOGD("LeftCheck mHitLeft=%d", mHitLeft);
+ } else if (mHitLeft == INT_MAX)
+ return; // wait for intersect success
+ /* If text is too far away vertically, don't consider it */
+ if (!mBounds.isEmpty() && (mPartial.fTop > mBounds.fBottom + HIT_SLOP
+ || mPartial.fBottom < mBounds.fTop - HIT_SLOP)) {
+ DBG_NAV_LOGD("LeftCheck stop mPartial=(%d, %d, %d, %d)"
+ " mBounds=(%d, %d, %d, %d)",
+ mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
+ mBounds.fLeft, mBounds.fTop, mBounds.fRight, mBounds.fBottom);
+ mHitLeft = INT_MAX; // and disable future comparisons
+ return;
+ }
+ /* If the considered text is completely to the left or right of the
+ touch coordinates, skip it, turn off further detection */
+ if (mPartial.fLeft > mX || mPartial.fRight < mX) {
+ DBG_NAV_LOGD("LeftCheck stop mX=%d mPartial=(%d, %d, %d, %d)", mX,
+ mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom);
+ mHitLeft = INT_MAX;
+ return;
+ }
+ /* record the smallest margins on the left and right */
+ if (mMostLeft > mPartial.fLeft) {
+ DBG_NAV_LOGD("LeftCheck new mMostLeft=%d (old=%d)", mPartial.fLeft,
+ mMostLeft);
+ mMostLeft = mPartial.fLeft;
+ }
+ if (mBounds.isEmpty())
+ mBounds = mPartial;
+ else if (mPartial.fBottom > mBounds.fBottom) {
+ DBG_NAV_LOGD("LeftCheck new bottom=%d (old=%d)", mPartial.fBottom,
+ mBounds.fBottom);
+ mBounds.fBottom = mPartial.fBottom;
+ }
+ }
+
+ static const int JOIN_SLOP_X = 30; // horizontal space between text parts
+ static const int JOIN_SLOP_Y = 5; // vertical space between text lines
+ static const int HIT_SLOP = 30; // diameter allowing for tap size
+ /* const */ SkIRect mHit; // sloppy hit rectangle
+ SkIRect mBounds; // reference bounds
+ SkIRect mPartial; // accumulated text bounds, per line
+ const int mX; // touch location
+ const int mY;
+ int mHitLeft; // touched text extremes
+ int mMostLeft; // paragraph extremes
+ Type mPartialType;
+};
+
+/*
+CenterCheck examines the text in a picture, within a viewable rectangle,
+and returns via center() the optimal amount to scroll in x to display the
+paragraph of text.
+
+The caller of CenterCheck has configured (but not allocated) a bitmap
+the height and three times the width of the view. The picture is drawn centered
+in the bitmap, so text that would be revealed, if the view was scrolled up to
+a view-width to the left or right, is considered.
+*/
+class CenterCheck : public CommonCheck {
+public:
+ CenterCheck(int x, int y, int width) : mX(x), mY(y),
+ mHitLeft(x), mHitRight(x), mMostLeft(INT_MAX), mMostRight(-INT_MAX),
+ mViewLeft(width), mViewRight(width << 1) {
+ mHit.set(x - CENTER_SLOP, y - CENTER_SLOP,
+ x + CENTER_SLOP, y + CENTER_SLOP);
+ mPartial.setEmpty();
+ }
+
+ int center() {
+ doRect(); // process the final line of text
+ /* If the touch coordinates aren't near any text, return 0 */
+ if (mHitLeft == mHitRight) {
+ DBG_NAV_LOGD("abort: mHitLeft=%d ==mHitRight", mHitLeft);
+ return 0;
+ }
+ int leftOver = mHitLeft - mViewLeft;
+ int rightOver = mHitRight - mViewRight;
+ int center;
+ /* If the touched text is too large to entirely fit on the screen,
+ center it. */
+ if (leftOver < 0 && rightOver > 0) {
+ center = (leftOver + rightOver) >> 1;
+ DBG_NAV_LOGD("overlap: leftOver=%d rightOver=%d center=%d",
+ leftOver, rightOver, center);
+ return center;
+ }
+ center = (mMostLeft + mMostRight) >> 1; // the paragraph center
+ if (leftOver > 0 && rightOver >= 0) { // off to the right
+ if (center > mMostLeft) // move to center loses left-most text?
+ center = mMostLeft;
+ } else if (rightOver < 0 && leftOver <= 0) { // off to the left
+ if (center < mMostRight) // move to center loses right-most text?
+ center = mMostRight;
+ } else {
+#ifdef DONT_CENTER_IF_ALREADY_VISIBLE
+ center = 0; // paragraph is already fully visible
+#endif
+ }
+ DBG_NAV_LOGD("scroll: leftOver=%d rightOver=%d center=%d",
+ leftOver, rightOver, center);
+ return center;
+ }
+
+protected:
+ virtual bool onIRect(const SkIRect& rect) {
+ if (joinGlyphs(rect)) // assembles glyphs into a text string
+ return false;
+ if (!isTextType(mType))
+ return false;
+ /* Text on one line may be broken into several parts. Reassemble
+ the text into a rectangle before considering it. */
+ if (rect.fTop < mPartial.fBottom && rect.fBottom >
+ mPartial.fTop && mPartial.fRight + CENTER_SLOP >= rect.fLeft) {
+ DBG_NAV_LOGD("join mPartial=(%d, %d, %d, %d) rect=(%d, %d, %d, %d)",
+ mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+ mPartial.join(rect);
+ return false;
+ }
+ if (mPartial.isEmpty() == false)
+ doRect(); // process the previous line of text
+ mPartial = rect;
+ return false;
+ }
+
+ void doRect()
+ {
+ /* Record the outer bounds of the lines of text that was 'hit' by the
+ touch coordinates, given some slop */
+ if (SkIRect::Intersects(mPartial, mHit)) {
+ if (mHitLeft > mPartial.fLeft)
+ mHitLeft = mPartial.fLeft;
+ if (mHitRight < mPartial.fRight)
+ mHitRight = mPartial.fRight;
+ DBG_NAV_LOGD("mHitLeft=%d mHitRight=%d", mHitLeft, mHitRight);
+ }
+ /* If the considered text is completely to the left or right of the
+ touch coordinates, skip it */
+ if (mPartial.fLeft > mX || mPartial.fRight < mX)
+ return;
+ int leftOver = mPartial.fLeft - mViewLeft;
+ int rightOver = mPartial.fRight - mViewRight;
+ /* If leftOver <= 0, the text starts off the screen.
+ If rightOver >= 0, the text ends off the screen.
+ */
+ if (leftOver <= 0 && rightOver >= 0) // discard wider than screen
+ return;
+#ifdef DONT_CENTER_IF_ALREADY_VISIBLE
+ if (leftOver > 0 && rightOver < 0) // discard already visible
+ return;
+#endif
+ /* record the smallest margins on the left and right */
+ if (mMostLeft > leftOver)
+ mMostLeft = leftOver;
+ if (mMostRight < rightOver)
+ mMostRight = rightOver;
+ DBG_NAV_LOGD("leftOver=%d rightOver=%d mMostLeft=%d mMostRight=%d",
+ leftOver, rightOver, mMostLeft, mMostRight);
+ }
+
+ static const int CENTER_SLOP = 10; // space between text parts and lines
+ /* const */ SkIRect mHit; // sloppy hit rectangle
+ SkIRect mPartial; // accumulated text bounds, per line
+ const int mX; // touch location
+ const int mY;
+ int mHitLeft; // touched text extremes
+ int mHitRight;
+ int mMostLeft; // paragraph extremes
+ int mMostRight;
+ const int mViewLeft; // middle third of 3x-wide view
+ const int mViewRight;
+};
+
+class ImageCanvas : public ParseCanvas {
+public:
+ ImageCanvas(SkBounder* bounder) : mURI(NULL) {
+ setBounder(bounder);
+ }
+
+ const char* getURI() { return mURI; }
+
+protected:
+// Currently webkit's bitmap draws always seem to be cull'd before this entry
+// point is called, so we assume that any bitmap that gets here is inside our
+// tiny clip (may not be true in the future)
+ virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
+ const SkMatrix& , const SkPaint& ) {
+ SkPixelRef* pixelRef = bitmap.pixelRef();
+ if (pixelRef != NULL) {
+ mURI = pixelRef->getURI();
+ }
+ }
+
+private:
+ const char* mURI;
+};
+
+class ImageCheck : public SkBounder {
+public:
+ virtual bool onIRect(const SkIRect& rect) {
+ return false;
+ }
+};
+
+class JiggleCheck : public CommonCheck {
+public:
+ JiggleCheck(int delta, int width) : mDelta(delta), mMaxX(width) {
+ mMaxJiggle = 0;
+ mMinX = mMinJiggle = abs(delta);
+ mMaxWidth = width + mMinX;
+ }
+
+ int jiggle() {
+ if (mMinJiggle > mMaxJiggle)
+ return mDelta;
+ int avg = (mMinJiggle + mMaxJiggle + 1) >> 1;
+ return mDelta < 0 ? -avg : avg;
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ if (joinGlyphs(rect))
+ return false;
+ if (mType != kDrawBitmap_Type && !isTextType(mType))
+ return false;
+ int min, max;
+ if (mDelta < 0) {
+ min = mMinX - rect.fLeft;
+ max = mMaxWidth - rect.fRight;
+ } else {
+ min = rect.fRight - mMaxX;
+ max = rect.fLeft;
+ }
+ if (min <= 0)
+ return false;
+ if (max >= mMinX)
+ return false;
+ if (mMinJiggle > min)
+ mMinJiggle = min;
+ if (mMaxJiggle < max)
+ mMaxJiggle = max;
+ return false;
+ }
+
+ int mDelta;
+ int mMaxJiggle;
+ int mMaxX;
+ int mMinJiggle;
+ int mMinX;
+ int mMaxWidth;
+};
+
+class RingCheck : public CommonCheck {
+public:
+ RingCheck(const WTF::Vector<WebCore::IntRect>& rings,
+ const WebCore::IntRect& bitBounds, const WebCore::IntRect& testBounds,
+ bool singleImage)
+ : mTestBounds(testBounds)
+ , mBitBounds(bitBounds)
+ , mPushPop(false)
+ , mSingleImage(singleImage)
+ {
+ const WebCore::IntRect* r;
+ for (r = rings.begin(); r != rings.end(); r++) {
+ SkIRect fatter = {r->x(), r->y(), r->maxX(), r->maxY()};
+ fatter.inset(-CURSOR_RING_HIT_TEST_RADIUS, -CURSOR_RING_HIT_TEST_RADIUS);
+ DBG_NAV_LOGD("RingCheck fat=(%d,%d,r=%d,b=%d)", fatter.fLeft, fatter.fTop,
+ fatter.fRight, fatter.fBottom);
+ mTextSlop.op(fatter, SkRegion::kUnion_Op);
+ mTextTest.op(*r, SkRegion::kUnion_Op);
+ }
+ int dx = -bitBounds.x();
+ int dy = -bitBounds.y();
+ DBG_NAV_LOGD("RingCheck translate=(%d,%d)", dx, dy);
+ mTextSlop.translate(dx, dy);
+ mTextTest.translate(dx, dy);
+ mTestBounds.translate(dx, dy);
+ mEmpty.setEmpty();
+ }
+
+ bool hiddenRings(SkRegion* clipped)
+ {
+ findBestLayer();
+ if (!mBestLayer) {
+ DBG_NAV_LOG("RingCheck empty");
+ clipped->setEmpty();
+ return true;
+ }
+ const SkRegion* layersEnd = mLayers.end();
+ const Type* layerTypes = &mLayerTypes[mBestLayer - mLayers.begin()];
+ bool collectGlyphs = true;
+ bool collectOvers = false;
+ SkRegion over;
+ for (const SkRegion* layers = mBestLayer; layers != layersEnd; layers++) {
+ Type layerType = *layerTypes++;
+ DBG_NAV_LOGD("RingCheck #%d %s (%d,%d,r=%d,b=%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom);
+ if (collectGlyphs && (layerType == kDrawGlyph_Type
+ || ((layerType == kDrawRect_Type && mTextTest.contains(*layers))
+ || (layerType == kDrawBitmap_Type && mTextSlop.contains(*layers))))) {
+ DBG_NAV_LOGD("RingCheck #%d collectOvers", layers - mLayers.begin());
+ collectOvers = true;
+ clipped->op(*layers, SkRegion::kUnion_Op);
+ continue;
+ }
+ collectGlyphs &= layerType != kPushLayer_Type;
+ if (collectOvers && (layerType == kDrawRect_Type
+ || layerType == kDrawBitmap_Type
+ || (!collectGlyphs && layerType == kDrawSprite_Type))) {
+ DBG_NAV_LOGD("RingCheck #%d over.op", layers - mLayers.begin());
+ over.op(*layers, SkRegion::kUnion_Op);
+ }
+ }
+ bool result = !collectOvers || clipped->intersects(over);
+ const SkIRect t = clipped->getBounds();
+ const SkIRect o = over.getBounds();
+ clipped->op(over, SkRegion::kDifference_Op);
+ clipped->translate(mBitBounds.x(), mBitBounds.y());
+ const SkIRect c = clipped->getBounds();
+ DBG_NAV_LOGD("RingCheck intersects=%s text=(%d,%d,r=%d,b=%d)"
+ " over=(%d,%d,r=%d,b=%d) clipped=(%d,%d,r=%d,b=%d)",
+ result ? "true" : "false",
+ t.fLeft, t.fTop, t.fRight, t.fBottom,
+ o.fLeft, o.fTop, o.fRight, o.fBottom,
+ c.fLeft, c.fTop, c.fRight, c.fBottom);
+ return result;
+ }
+
+ void push(Type type, const SkIRect& bounds)
+ {
+#if DEBUG_NAV_UI
+ // this caches the push string and subquently ignores if pushSave
+ // is immediately followed by popLayer. Push/pop pairs happen
+ // frequently and just add noise to the log.
+ static String lastLog;
+ String currentLog = String("RingCheck append #")
+ + String::number(mLayers.size())
+ + " type=" + TypeNames[type] + " bounds=("
+ + String::number(bounds.fLeft)
+ + "," + String::number(bounds.fTop) + ","
+ + String::number(bounds.fRight) + ","
+ + String::number(bounds.fBottom) + ")";
+ if (lastLog.length() == 0 || type != kPopLayer_Type) {
+ if (lastLog.length() != 0)
+ DBG_NAV_LOGD("%s", lastLog.latin1().data());
+ if (type == kPushSave_Type)
+ lastLog = currentLog;
+ else
+ DBG_NAV_LOGD("%s", currentLog.latin1().data());
+ } else
+ lastLog = "";
+#endif
+ popEmpty();
+ mPushPop |= type >= kPopLayer_Type;
+ if (type == kPopLayer_Type) {
+ Type last = mLayerTypes.last();
+ // remove empty brackets
+ if (last == kPushLayer_Type || last == kPushSave_Type) {
+ mLayers.removeLast();
+ mLayerTypes.removeLast();
+ return;
+ }
+ // remove push/pop from push/bitmap/pop
+ size_t pushIndex = mLayerTypes.size() - 2;
+ if (last == kDrawBitmap_Type
+ && mLayerTypes.at(pushIndex) == kPushLayer_Type) {
+ mLayers.at(pushIndex) = mLayers.last();
+ mLayerTypes.at(pushIndex) = kDrawBitmap_Type;
+ mLayers.removeLast();
+ mLayerTypes.removeLast();
+ return;
+ }
+ // remove non-layer brackets
+ int stack = 0;
+ Type* types = mLayerTypes.end();
+ while (types != mLayerTypes.begin()) {
+ Type type = *--types;
+ if (type == kPopLayer_Type) {
+ stack++;
+ continue;
+ }
+ if (type != kPushLayer_Type && type != kPushSave_Type)
+ continue;
+ if (--stack >= 0)
+ continue;
+ if (type == kPushLayer_Type)
+ break;
+ int remove = types - mLayerTypes.begin();
+ DBG_NAV_LOGD("RingCheck remove=%d mLayers.size=%d"
+ " mLayerTypes.size=%d", remove, mLayers.size(),
+ mLayerTypes.size());
+ mLayers.remove(remove);
+ mLayerTypes.remove(remove);
+ mAppendLikeTypes = false;
+ return;
+ }
+ }
+ mLayers.append(bounds);
+ mLayerTypes.append(type);
+ }
+
+ void startText(const SkPaint& paint)
+ {
+ mPaint = &paint;
+ if (!mLayerTypes.isEmpty() && mLayerTypes.last() == kDrawGlyph_Type
+ && !mLayers.last().isEmpty()) {
+ push(kDrawGlyph_Type, mEmpty);
+ }
+ }
+
+ bool textOutsideRings()
+ {
+ findBestLayer();
+ if (!mBestLayer) {
+ DBG_NAV_LOG("RingCheck empty");
+ return false;
+ }
+ const SkRegion* layers = mBestLayer;
+ const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ // back up to include text drawn before the best layer
+ SkRegion active = SkRegion(mBitBounds);
+ active.translate(-mBitBounds.x(), -mBitBounds.y());
+ while (layers != mLayers.begin()) {
+ --layers;
+ Type layerType = *--layerTypes;
+ DBG_NAV_LOGD("RingCheck #%d %s"
+ " mTestBounds=(%d,%d,r=%d,b=%d) layers=(%d,%d,r=%d,b=%d)"
+ " active=(%d,%d,r=%d,b=%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
+ mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom,
+ active.getBounds().fLeft, active.getBounds().fTop,
+ active.getBounds().fRight, active.getBounds().fBottom);
+ if (layerType == kDrawRect_Type || layerType == kDrawBitmap_Type) {
+ SkRegion temp = *layers;
+ temp.op(mTestBounds, SkRegion::kIntersect_Op);
+ active.op(temp, SkRegion::kDifference_Op);
+ if (active.isEmpty()) {
+ DBG_NAV_LOGD("RingCheck #%d empty", layers - mLayers.begin());
+ break;
+ }
+ } else if (layerType == kDrawGlyph_Type) {
+ SkRegion temp = *layers;
+ temp.op(active, SkRegion::kIntersect_Op);
+ if (!mTestBounds.intersects(temp))
+ continue;
+ if (!mTestBounds.contains(temp))
+ return false;
+ } else
+ break;
+ }
+ layers = mBestLayer;
+ layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ bool foundGlyph = false;
+ bool collectGlyphs = true;
+ do {
+ Type layerType = *layerTypes++;
+ DBG_NAV_LOGD("RingCheck #%d %s mTestBounds=(%d,%d,r=%d,b=%d)"
+ " layers=(%d,%d,r=%d,b=%d) collects=%s intersects=%s contains=%s",
+ layers - mLayers.begin(), TypeNames[layerType],
+ mTestBounds.getBounds().fLeft, mTestBounds.getBounds().fTop,
+ mTestBounds.getBounds().fRight, mTestBounds.getBounds().fBottom,
+ layers->getBounds().fLeft, layers->getBounds().fTop,
+ layers->getBounds().fRight, layers->getBounds().fBottom,
+ collectGlyphs ? "true" : "false",
+ mTestBounds.intersects(*layers) ? "true" : "false",
+ mTextSlop.contains(*layers) ? "true" : "false");
+ if (collectGlyphs && layerType == kDrawGlyph_Type) {
+ if (!mTestBounds.intersects(*layers))
+ continue;
+ if (!mTextSlop.contains(*layers))
+ return false;
+ foundGlyph = true;
+ }
+ collectGlyphs &= layerType != kPushLayer_Type;
+ } while (++layers != mLayers.end());
+ DBG_NAV_LOGD("RingCheck foundGlyph=%s", foundGlyph ? "true" : "false");
+ return foundGlyph;
+ }
+
+protected:
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ joinGlyphs(rect);
+ if (mType != kDrawGlyph_Type && mType != kDrawRect_Type
+ && mType != kDrawSprite_Type && mType != kDrawBitmap_Type)
+ return false;
+ if (mLayerTypes.isEmpty() || mLayerTypes.last() != mType
+ || !mAppendLikeTypes || mPushPop || mSingleImage
+ // if the last and current were not glyphs,
+ // and the two bounds have a gap between, don't join them -- push
+ // an empty between them
+ || (mType != kDrawGlyph_Type && !joinable(rect))) {
+ push(mType, mEmpty);
+ }
+ DBG_NAV_LOGD("RingCheck join %s (%d,%d,r=%d,b=%d) '%c'",
+ TypeNames[mType], rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ mCh);
+ mLayers.last().op(rect, SkRegion::kUnion_Op);
+ mAppendLikeTypes = true;
+ mPushPop = false;
+ return false;
+ }
+
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ mCh = ' ';
+ if (mPaint) {
+ SkUnichar unichar;
+ SkPaint utfPaint = *mPaint;
+ utfPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ utfPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
+ mCh = unichar < 0x7f ? unichar : '?';
+ }
+ return onIRect(rect);
+ }
+
+private:
+ int calcOverlap(SkRegion& testRegion)
+ {
+ if (testRegion.isEmpty())
+ return INT_MAX;
+ testRegion.op(mTextTest, SkRegion::kXOR_Op);
+ SkRegion::Iterator iter(testRegion);
+ int area = 0;
+ while (!iter.done()) {
+ const SkIRect& cr = iter.rect();
+ area += cr.width() * cr.height();
+ iter.next();
+ }
+ DBG_NAV_LOGD("RingCheck area=%d", area);
+ return area;
+ }
+
+ void findBestLayer()
+ {
+ popEmpty();
+ mBestLayer = 0;
+ const SkRegion* layers = mLayers.begin();
+ const SkRegion* layersEnd = mLayers.end();
+ if (layers == layersEnd) {
+ DBG_NAV_LOG("RingCheck empty");
+ return;
+ }
+ // find text most like focus rings by xoring found with original
+ int bestArea = INT_MAX;
+ const SkRegion* testLayer = 0;
+ SkRegion testRegion;
+ const Type* layerTypes = &mLayerTypes[layers - mLayers.begin()];
+ for (; layers != mLayers.end(); layers++) {
+ Type layerType = *layerTypes++;
+#if DEBUG_NAV_UI
+ const SkIRect& gb = layers->getBounds();
+ const SkIRect& tb = mTextSlop.getBounds();
+ DBG_NAV_LOGD("RingCheck #%d %s mTextSlop=(%d,%d,%d,%d)"
+ " contains=%s bounds=(%d,%d,%d,%d)",
+ layers - mLayers.begin(), TypeNames[layerType],
+ tb.fLeft, tb.fTop, tb.fRight, tb.fBottom,
+ mTextSlop.contains(*layers) ? "true" : "false",
+ gb.fLeft, gb.fTop, gb.fRight, gb.fBottom);
+#endif
+ if (((layerType == kDrawGlyph_Type || layerType == kDrawBitmap_Type)
+ && mTextSlop.contains(*layers))
+ || (layerType == kDrawRect_Type
+ && mTextTest.contains(*layers))) {
+ if (!testLayer)
+ testLayer = layers;
+ testRegion.op(*layers, SkRegion::kUnion_Op);
+ continue;
+ }
+ if (testLayer) {
+ int area = calcOverlap(testRegion);
+ if (bestArea > area) {
+ bestArea = area;
+ mBestLayer = testLayer;
+ }
+ DBG_NAV_LOGD("RingCheck #%d push test=%d best=%d",
+ layers - mLayers.begin(), testLayer - mLayers.begin(),
+ mBestLayer ? mBestLayer - mLayers.begin() : -1);
+ testRegion.setEmpty();
+ testLayer = 0;
+ }
+ }
+ if (testLayer && bestArea > calcOverlap(testRegion)) {
+ DBG_NAV_LOGD("RingCheck last best=%d", testLayer - mLayers.begin());
+ mBestLayer = testLayer;
+ }
+ }
+
+ bool joinable(const SkIRect& rect)
+ {
+ SkRegion region = mLayers.last();
+ if (!region.isRect())
+ return false;
+ const SkIRect& bounds1 = region.getBounds();
+ int area1 = bounds1.width() * bounds1.height();
+ area1 += rect.width() * rect.height();
+ region.op(rect, SkRegion::kUnion_Op);
+ const SkIRect& bounds2 = region.getBounds();
+ int area2 = bounds2.width() * bounds2.height();
+ return area2 <= area1;
+ }
+
+ void popEmpty()
+ {
+ if (mLayerTypes.size() == 0)
+ return;
+ Type last = mLayerTypes.last();
+ if (last >= kPopLayer_Type)
+ return;
+ const SkRegion& area = mLayers.last();
+ if (!area.isEmpty())
+ return;
+ DBG_NAV_LOGD("RingCheck #%d %s", mLayers.size() - 1, TypeNames[last]);
+ mLayers.removeLast();
+ mLayerTypes.removeLast();
+ }
+
+ SkRegion mTestBounds;
+ IntRect mBitBounds;
+ SkIRect mEmpty;
+ const SkRegion* mBestLayer;
+ SkRegion mTextSlop; // outset rects for inclusion test
+ SkRegion mTextTest; // exact rects for xor area test
+ Type mLastType;
+ Vector<SkRegion> mLayers;
+ Vector<Type> mLayerTypes;
+ const SkPaint* mPaint;
+ char mCh;
+ bool mAppendLikeTypes;
+ bool mPushPop;
+ bool mSingleImage;
+};
+
+class RingCanvas : public BoundsCanvas {
+public:
+ RingCanvas(RingCheck* bounder)
+ : INHERITED(bounder)
+ {
+ }
+
+protected:
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawText(text, byteLength, x, y, paint);
+ }
+
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawPosText(text, byteLength, pos, paint);
+ }
+
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
+ }
+
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ static_cast<RingCheck&>(mBounder).startText(paint);
+ INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+ }
+
+ virtual int save(SaveFlags flags)
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPushSave_Type, getTotalClip().getBounds());
+ return INHERITED::save(flags);
+ }
+
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags)
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPushLayer_Type, getTotalClip().getBounds());
+ return INHERITED::save(flags);
+ }
+
+ virtual void restore()
+ {
+ RingCheck& bounder = static_cast<RingCheck&>(mBounder);
+ bounder.push(CommonCheck::kPopLayer_Type, getTotalClip().getBounds());
+ INHERITED::restore();
+ }
+
+private:
+ typedef BoundsCanvas INHERITED;
+};
+
+bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction,
+ WebCore::IntPoint* scrollPtr, bool findClosest)
+{
+ WebCore::IntRect newOutset;
+ const CachedNode* newNode = best->mNode;
+ // see if there's a middle node
+ // if the middle node is in the visited list,
+ // or if none was computed and the newNode is in the visited list,
+ // treat result as NULL
+ if (newNode != NULL && findClosest) {
+ if (best->bounds().intersects(mHistory->mPriorBounds) == false &&
+ checkBetween(best, direction))
+ newNode = best->mNode;
+ if (findClosest && maskIfHidden(best)) {
+ innerMove(document(), best, direction, scrollPtr, false);
+ return true;
+ }
+ newOutset = newNode->cursorRingBounds(best->mFrame);
+ }
+ int delta;
+ bool newNodeInView = scrollDelta(newOutset, direction, &delta);
+ if (delta && scrollPtr && (newNode == NULL || newNodeInView == false ||
+ (best->mNavOutside && best->mWorkingOutside)))
+ *scrollPtr = WebCore::IntPoint(direction & UP_DOWN ? 0 : delta,
+ direction & UP_DOWN ? delta : 0);
+ return false;
+}
+
+void CachedRoot::calcBitBounds(const IntRect& nodeBounds, IntRect* bitBounds) const
+{
+ IntRect contentBounds = IntRect(0, 0, mPicture->width(), mPicture->height());
+ IntRect overBounds = nodeBounds;
+ overBounds.inflate(kMargin);
+ IntRect viewableBounds = mScrolledBounds;
+ viewableBounds.unite(mViewBounds);
+ *bitBounds = contentBounds;
+ bitBounds->intersect(overBounds);
+ if (!bitBounds->intersects(viewableBounds))
+ *bitBounds = IntRect(0, 0, 0, 0);
+ DBG_NAV_LOGD("contentBounds=(%d,%d,r=%d,b=%d) overBounds=(%d,%d,r=%d,b=%d)"
+ " mScrolledBounds=(%d,%d,r=%d,b=%d) mViewBounds=(%d,%d,r=%d,b=%d)"
+ " bitBounds=(%d,%d,r=%d,b=%d)",
+ contentBounds.x(), contentBounds.y(), contentBounds.maxX(),
+ contentBounds.maxY(),
+ overBounds.x(), overBounds.y(), overBounds.maxX(), overBounds.maxY(),
+ mScrolledBounds.x(), mScrolledBounds.y(), mScrolledBounds.maxX(),
+ mScrolledBounds.maxY(),
+ mViewBounds.x(), mViewBounds.y(), mViewBounds.maxX(),
+ mViewBounds.maxY(),
+ bitBounds->x(), bitBounds->y(), bitBounds->maxX(),
+ bitBounds->maxY());
+}
+
+
+int CachedRoot::checkForCenter(int x, int y) const
+{
+ int width = mViewBounds.width();
+ SkPicture* picture = pictureAt(&x, &y);
+ CenterCheck centerCheck(x + width - mViewBounds.x(), y - mViewBounds.y(),
+ width);
+ BoundsCanvas checker(&centerCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width * 3,
+ mViewBounds.height());
+ checker.setBitmapDevice(bitmap);
+ checker.translate(SkIntToScalar(width - mViewBounds.x()),
+ SkIntToScalar(-mViewBounds.y()));
+ checker.drawPicture(*picture);
+ return centerCheck.center();
+}
+
+void CachedRoot::checkForJiggle(int* xDeltaPtr) const
+{
+ int xDelta = *xDeltaPtr;
+ JiggleCheck jiggleCheck(xDelta, mViewBounds.width());
+ BoundsCanvas checker(&jiggleCheck);
+ SkBitmap bitmap;
+ int absDelta = abs(xDelta);
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, mViewBounds.width() +
+ absDelta, mViewBounds.height());
+ checker.setBitmapDevice(bitmap);
+ int x = -mViewBounds.x() - (xDelta < 0 ? xDelta : 0);
+ int y = -mViewBounds.y();
+ SkPicture* picture = pictureAt(&x, &y);
+ checker.translate(SkIntToScalar(x), SkIntToScalar(y));
+ checker.drawPicture(*picture);
+ *xDeltaPtr = jiggleCheck.jiggle();
+}
+
+bool CachedRoot::checkRings(SkPicture* picture, const CachedNode* node,
+ const WebCore::IntRect& testBounds) const
+{
+ if (!picture)
+ return false;
+ const WTF::Vector<WebCore::IntRect>& rings = node->rings();
+ const WebCore::IntRect& nodeBounds = node->rawBounds();
+ IntRect bitBounds;
+ calcBitBounds(nodeBounds, &bitBounds);
+ RingCheck ringCheck(rings, bitBounds, testBounds, node->singleImage());
+ RingCanvas checker(&ringCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
+ bitBounds.height());
+ checker.setBitmapDevice(bitmap);
+ checker.translate(SkIntToScalar(-bitBounds.x()),
+ SkIntToScalar(-bitBounds.y()));
+ checker.drawPicture(*picture);
+ bool result = ringCheck.textOutsideRings();
+ DBG_NAV_LOGD("bitBounds=(%d,%d,r=%d,b=%d) nodeBounds=(%d,%d,r=%d,b=%d)"
+ " testBounds=(%d,%d,r=%d,b=%d) success=%s",
+ bitBounds.x(), bitBounds.y(), bitBounds.maxX(), bitBounds.maxY(),
+ nodeBounds.x(), nodeBounds.y(), nodeBounds.maxX(), nodeBounds.maxY(),
+ testBounds.x(), testBounds.y(), testBounds.maxX(), testBounds.maxY(),
+ result ? "true" : "false");
+ return result;
+}
+
+void CachedRoot::draw(FindCanvas& canvas) const
+{
+ canvas.setLayerId(-1); // overlays change the ID as their pictures draw
+ canvas.drawPicture(*mPicture);
+#if USE(ACCELERATED_COMPOSITING)
+ if (!mRootLayer)
+ return;
+ canvas.drawLayers(mRootLayer);
+#endif
+}
+
+const CachedNode* CachedRoot::findAt(const WebCore::IntRect& rect,
+ const CachedFrame** framePtr, int* x, int* y, bool checkForHidden) const
+{
+#if DEBUG_NAV_UI
+ DBG_NAV_LOGD("rect=(%d,%d,w=%d,h=%d) xy=(%d,%d)", rect.x(), rect.y(),
+ rect.width(), rect.height(), *x, *y);
+#if DUMP_NAV_CACHE
+ if (mRootLayer) CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
+#endif
+#endif
+ int best = INT_MAX;
+ bool inside = false;
+ (const_cast<CachedRoot*>(this))->resetClippedOut();
+ const CachedFrame* directHitFramePtr;
+ const CachedNode* directHit = NULL;
+ const CachedNode* node = findBestAt(rect, &best, &inside, &directHit,
+ &directHitFramePtr, framePtr, x, y, checkForHidden);
+ DBG_NAV_LOGD("node=%d (%p) xy=(%d,%d)", node == NULL ? 0 : node->index(),
+ node == NULL ? NULL : node->nodePointer(), *x, *y);
+ if (node == NULL) {
+ node = findBestHitAt(rect, framePtr, x, y);
+ DBG_NAV_LOGD("node=%d (%p)", node == NULL ? 0 : node->index(),
+ node == NULL ? NULL : node->nodePointer());
+ }
+ if (node == NULL) {
+ *framePtr = findBestFrameAt(rect.x() + (rect.width() >> 1),
+ rect.y() + (rect.height() >> 1));
+ }
+ return node;
+}
+
+WebCore::IntPoint CachedRoot::cursorLocation() const
+{
+ const WebCore::IntRect& bounds = mHistory->mNavBounds;
+ return WebCore::IntPoint(bounds.x() + (bounds.width() >> 1),
+ bounds.y() + (bounds.height() >> 1));
+}
+
+WebCore::IntPoint CachedRoot::focusLocation() const
+{
+ return WebCore::IntPoint(mFocusBounds.x() + (mFocusBounds.width() >> 1),
+ mFocusBounds.y() + (mFocusBounds.height() >> 1));
+}
+
+// These reset the values because we only want to get the selection the first time.
+// After that, the selection is no longer accurate.
+int CachedRoot::getAndResetSelectionEnd()
+{
+ int end = mSelectionEnd;
+ mSelectionEnd = -1;
+ return end;
+}
+
+int CachedRoot::getAndResetSelectionStart()
+{
+ int start = mSelectionStart;
+ mSelectionStart = -1;
+ return start;
+}
+
+int CachedRoot::getBlockLeftEdge(int x, int y, float scale) const
+{
+ DBG_NAV_LOGD("x=%d y=%d scale=%g mViewBounds=(%d,%d,%d,%d)", x, y, scale,
+ mViewBounds.x(), mViewBounds.y(), mViewBounds.width(),
+ mViewBounds.height());
+ // if (x, y) is in a textArea or textField, return that
+ const int slop = 1;
+ WebCore::IntRect rect = WebCore::IntRect(x - slop, y - slop,
+ slop * 2, slop * 2);
+ const CachedFrame* frame;
+ int fx, fy;
+ const CachedNode* node = findAt(rect, &frame, &fx, &fy, true);
+ if (node && node->wantsKeyEvents()) {
+ DBG_NAV_LOGD("x=%d (%s)", node->bounds(frame).x(),
+ node->isTextInput() ? "text" : "plugin");
+ return node->bounds(frame).x();
+ }
+ SkPicture* picture = node ? frame->picture(node, &x, &y) : pictureAt(&x, &y);
+ if (!picture)
+ return x;
+ int halfW = (int) (mViewBounds.width() * scale * 0.5f);
+ int fullW = halfW << 1;
+ int halfH = (int) (mViewBounds.height() * scale * 0.5f);
+ int fullH = halfH << 1;
+ LeftCheck leftCheck(fullW, halfH);
+ BoundsCanvas checker(&leftCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, fullW, fullH);
+ checker.setBitmapDevice(bitmap);
+ checker.translate(SkIntToScalar(fullW - x), SkIntToScalar(halfH - y));
+ checker.drawPicture(*picture);
+ int result = x + leftCheck.left() - fullW;
+ DBG_NAV_LOGD("halfW=%d halfH=%d mMostLeft=%d x=%d",
+ halfW, halfH, leftCheck.mMostLeft, result);
+ return result;
+}
+
+void CachedRoot::getSimulatedMousePosition(WebCore::IntPoint* point) const
+{
+#ifndef NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+ const WebCore::IntRect& mouseBounds = mHistory->mMouseBounds;
+ int x = mouseBounds.x();
+ int y = mouseBounds.y();
+ int width = mouseBounds.width();
+ int height = mouseBounds.height();
+ point->setX(x + (width >> 1)); // default to box center
+ point->setY(y + (height >> 1));
+#if DEBUG_NAV_UI
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ DBG_NAV_LOGD("mHistory->mNavBounds={%d,%d,%d,%d} "
+ "mHistory->mMouseBounds={%d,%d,%d,%d} point={%d,%d}",
+ navBounds.x(), navBounds.y(), navBounds.width(), navBounds.height(),
+ mouseBounds.x(), mouseBounds.y(), mouseBounds.width(),
+ mouseBounds.height(), point->x(), point->y());
+#endif
+}
+
+void CachedRoot::init(WebCore::Frame* frame, CachedHistory* history)
+{
+ CachedFrame::init(this, -1, frame);
+ reset();
+ mHistory = history;
+ mPicture = NULL;
+}
+
+bool CachedRoot::innerDown(const CachedNode* test, BestData* bestData) const
+{
+ ASSERT(minWorkingVertical() >= mViewBounds.x());
+ ASSERT(maxWorkingVertical() <= mViewBounds.maxX());
+ setupScrolledBounds();
+ // (line up)
+ mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
+ int testTop = mScrolledBounds.y();
+ int viewBottom = mViewBounds.maxY();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.maxY() > viewBottom && viewBottom < mContents.height())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navTop = navBounds.y();
+ int scrollBottom;
+ if (testTop < navTop && navTop < (scrollBottom = mScrolledBounds.maxY())) {
+ mScrolledBounds.setHeight(scrollBottom - navTop);
+ mScrolledBounds.setY(navTop);
+ }
+ }
+ setCursorCache(0, mMaxYScroll);
+ frameDown(test, NULL, bestData);
+ return true;
+}
+
+bool CachedRoot::innerLeft(const CachedNode* test, BestData* bestData) const
+{
+ ASSERT(minWorkingHorizontal() >= mViewBounds.y());
+ ASSERT(maxWorkingHorizontal() <= mViewBounds.maxY());
+ setupScrolledBounds();
+ mScrolledBounds.setX(mScrolledBounds.x() - mMaxXScroll);
+ mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
+ int testRight = mScrolledBounds.maxX();
+ int viewLeft = mViewBounds.x();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.x() < viewLeft && viewLeft > mContents.x())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navRight = navBounds.maxX();
+ int scrollLeft;
+ if (testRight > navRight && navRight > (scrollLeft = mScrolledBounds.x()))
+ mScrolledBounds.setWidth(navRight - scrollLeft);
+ }
+ setCursorCache(-mMaxXScroll, 0);
+ frameLeft(test, NULL, bestData);
+ return true;
+}
+
+
+void CachedRoot::innerMove(const CachedNode* node, BestData* bestData,
+ Direction direction, WebCore::IntPoint* scroll, bool firstCall)
+{
+ bestData->reset();
+ bool outOfCursor = mCursorIndex == CURSOR_CLEARED;
+ DBG_NAV_LOGD("mHistory->didFirstLayout()=%s && mCursorIndex=%d",
+ mHistory->didFirstLayout() ? "true" : "false", mCursorIndex);
+ if (mHistory->didFirstLayout() && mCursorIndex < CURSOR_SET) {
+ mHistory->reset();
+ outOfCursor = true;
+ }
+ const CachedFrame* cursorFrame;
+ const CachedNode* cursor = currentCursor(&cursorFrame);
+ mHistory->setWorking(direction, cursorFrame, cursor, mViewBounds);
+ bool findClosest = false;
+ if (mScrollOnly == false) {
+ switch (direction) {
+ case LEFT:
+ if (outOfCursor)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.maxX(),
+ mViewBounds.y(), 1, mViewBounds.height());
+ findClosest = innerLeft(node, bestData);
+ break;
+ case RIGHT:
+ if (outOfCursor)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x() - 1,
+ mViewBounds.y(), 1, mViewBounds.height());
+ findClosest = innerRight(node, bestData);
+ break;
+ case UP:
+ if (outOfCursor)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(),
+ mViewBounds.maxY(), mViewBounds.width(), 1);
+ findClosest = innerUp(node, bestData);
+ break;
+ case DOWN:
+ if (outOfCursor)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(),
+ mViewBounds.y() - 1, mViewBounds.width(), 1);
+ findClosest = innerDown(node, bestData);
+ break;
+ case UNINITIALIZED:
+ default:
+ ASSERT(0);
+ }
+ }
+ if (firstCall)
+ mHistory->mPriorBounds = mHistory->mNavBounds; // bounds always advances, even if new node is ultimately NULL
+ bestData->setMouseBounds(bestData->bounds());
+ if (adjustForScroll(bestData, direction, scroll, findClosest))
+ return;
+ if (bestData->mNode != NULL) {
+ mHistory->addToVisited(bestData->mNode, direction);
+ mHistory->mNavBounds = bestData->bounds();
+ mHistory->mMouseBounds = bestData->mouseBounds();
+ } else if (scroll->x() != 0 || scroll->y() != 0) {
+ WebCore::IntRect newBounds = mHistory->mNavBounds;
+ int offsetX = scroll->x();
+ int offsetY = scroll->y();
+ newBounds.move(offsetX, offsetY);
+ if (mViewBounds.x() > newBounds.x())
+ offsetX = mViewBounds.x() - mHistory->mNavBounds.x();
+ else if (mViewBounds.maxX() < newBounds.maxX())
+ offsetX = mViewBounds.maxX() - mHistory->mNavBounds.maxX();
+ if (mViewBounds.y() > newBounds.y())
+ offsetY = mViewBounds.y() - mHistory->mNavBounds.y();
+ else if (mViewBounds.maxY() < newBounds.maxY())
+ offsetY = mViewBounds.maxY() - mHistory->mNavBounds.maxY();
+ mHistory->mNavBounds.move(offsetX, offsetY);
+ }
+ mHistory->setDidFirstLayout(false);
+}
+
+bool CachedRoot::innerRight(const CachedNode* test, BestData* bestData) const
+{
+ ASSERT(minWorkingHorizontal() >= mViewBounds.y());
+ ASSERT(maxWorkingHorizontal() <= mViewBounds.maxY());
+ setupScrolledBounds();
+ // (align)
+ mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
+ int testLeft = mScrolledBounds.x();
+ int viewRight = mViewBounds.maxX();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.maxX() > viewRight && viewRight < mContents.width())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navLeft = navBounds.x();
+ int scrollRight;
+ if (testLeft < navLeft && navLeft < (scrollRight = mScrolledBounds.maxX())) {
+ mScrolledBounds.setWidth(scrollRight - navLeft);
+ mScrolledBounds.setX(navLeft);
+ }
+ }
+ setCursorCache(mMaxXScroll, 0);
+ frameRight(test, NULL, bestData);
+ return true;
+}
+
+bool CachedRoot::innerUp(const CachedNode* test, BestData* bestData) const
+{
+ ASSERT(minWorkingVertical() >= mViewBounds.x());
+ ASSERT(maxWorkingVertical() <= mViewBounds.maxX());
+ setupScrolledBounds();
+ mScrolledBounds.setY(mScrolledBounds.y() - mMaxYScroll);
+ mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
+ int testBottom = mScrolledBounds.maxY();
+ int viewTop = mViewBounds.y();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.y() < viewTop && viewTop > mContents.y())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navBottom = navBounds.maxY();
+ int scrollTop;
+ if (testBottom > navBottom && navBottom > (scrollTop = mScrolledBounds.y()))
+ mScrolledBounds.setHeight(navBottom - scrollTop);
+ }
+ setCursorCache(0, -mMaxYScroll);
+ frameUp(test, NULL, bestData);
+ return true;
+}
+
+WTF::String CachedRoot::imageURI(int x, int y) const
+{
+ DBG_NAV_LOGD("x/y=(%d,%d)", x, y);
+ ImageCheck imageCheck;
+ ImageCanvas checker(&imageCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ checker.setBitmapDevice(bitmap);
+ SkPicture* picture = pictureAt(&x, &y);
+ checker.translate(SkIntToScalar(-x), SkIntToScalar(-y));
+ checker.drawPicture(*picture);
+ DBG_NAV_LOGD("uri=%s", checker.getURI());
+ return WTF::String(checker.getURI());
+}
+
+bool CachedRoot::maskIfHidden(BestData* best) const
+{
+ const CachedNode* bestNode = best->mNode;
+ if (bestNode->isUnclipped())
+ return false;
+ const CachedFrame* frame = best->mFrame;
+ SkPicture* picture = frame->picture(bestNode);
+ if (picture == NULL) {
+ DBG_NAV_LOG("missing picture");
+ return false;
+ }
+ Vector<IntRect> rings;
+ bestNode->cursorRings(frame, &rings);
+ const WebCore::IntRect& bounds = bestNode->bounds(frame);
+ IntRect bitBounds;
+ calcBitBounds(bounds, &bitBounds);
+ RingCheck ringCheck(rings, bitBounds, bounds, bestNode->singleImage());
+ RingCanvas checker(&ringCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, bitBounds.width(),
+ bitBounds.height());
+ checker.setBitmapDevice(bitmap);
+ checker.translate(SkIntToScalar(-bitBounds.x()),
+ SkIntToScalar(-bitBounds.y()));
+ checker.drawPicture(*picture);
+ SkRegion clipRgn;
+ bool clipped = ringCheck.hiddenRings(&clipRgn);
+ CachedNode* node = const_cast<CachedNode*>(best->mNode);
+ DBG_NAV_LOGD("clipped=%s clipRgn.isEmpty=%s", clipped ? "true" : "false",
+ clipRgn.isEmpty() ? "true" : "false");
+ if (clipped && clipRgn.isEmpty()) {
+ node->setDisabled(true);
+ IntRect clippedBounds = bounds;
+ clippedBounds.intersect(bitBounds);
+ node->setClippedOut(clippedBounds != bounds);
+ return true;
+ }
+ // was it partially occluded by later drawing?
+ // if partially occluded, modify the bounds so that the mouse click has a better x,y
+ if (clipped) {
+ DBG_NAV_LOGD("clipped clipRgn={%d,%d,r=%d,b=%d}",
+ clipRgn.getBounds().fLeft, clipRgn.getBounds().fTop,
+ clipRgn.getBounds().fRight, clipRgn.getBounds().fBottom);
+ best->setMouseBounds(clipRgn.getBounds());
+ if (!node->clip(best->mouseBounds())) {
+ node->setDisabled(true);
+ node->setClippedOut(true);
+ return true;
+ }
+ } else
+ node->fixUpCursorRects(frame);
+ return false;
+}
+
+const CachedNode* CachedRoot::moveCursor(Direction direction, const CachedFrame** framePtr,
+ WebCore::IntPoint* scroll)
+{
+#ifndef NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+ CachedRoot* frame = this;
+ const CachedNode* node = frame->document();
+ if (node == NULL)
+ return NULL;
+ if (mViewBounds.isEmpty())
+ return NULL;
+ resetClippedOut();
+ setData();
+ BestData bestData;
+ innerMove(node, &bestData, direction, scroll, true);
+ // if node is partially or fully concealed by layer, scroll it into view
+ if (mRootLayer && bestData.mNode && !bestData.mNode->isInLayer()) {
+#if USE(ACCELERATED_COMPOSITING)
+#if DUMP_NAV_CACHE
+ CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
+#endif
+ SkIRect original = bestData.mNode->cursorRingBounds(bestData.mFrame);
+ DBG_NAV_LOGD("original=(%d,%d,w=%d,h=%d) scroll=(%d,%d)",
+ original.fLeft, original.fTop, original.width(), original.height(),
+ scroll->x(), scroll->y());
+ original.offset(-scroll->x(), -scroll->y());
+ SkRegion rings(original);
+ SkTDArray<SkRect> region;
+ mRootLayer->clipArea(&region);
+ SkRegion layers;
+ for (int index = 0; index < region.count(); index++) {
+ SkIRect enclosing;
+ region[index].round(&enclosing);
+ rings.op(enclosing, SkRegion::kDifference_Op);
+ layers.op(enclosing, SkRegion::kUnion_Op);
+ }
+ SkIRect layerBounds(layers.getBounds());
+ SkIRect ringBounds(rings.getBounds());
+ int scrollX = scroll->x();
+ int scrollY = scroll->y();
+ if (rings.getBounds() != original) {
+ int topOverlap = layerBounds.fBottom - original.fTop;
+ int bottomOverlap = original.fBottom - layerBounds.fTop;
+ int leftOverlap = layerBounds.fRight - original.fLeft;
+ int rightOverlap = original.fRight - layerBounds.fLeft;
+ if (direction & UP_DOWN) {
+ if (layerBounds.fLeft < original.fLeft && leftOverlap < 0)
+ scroll->setX(leftOverlap);
+ if (original.fRight < layerBounds.fRight && rightOverlap > 0
+ && -leftOverlap > rightOverlap)
+ scroll->setX(rightOverlap);
+ bool topSet = scrollY > topOverlap && (direction == UP
+ || !scrollY);
+ if (topSet)
+ scroll->setY(topOverlap);
+ if (scrollY < bottomOverlap && (direction == DOWN || (!scrollY
+ && (!topSet || -topOverlap > bottomOverlap))))
+ scroll->setY(bottomOverlap);
+ } else {
+ if (layerBounds.fTop < original.fTop && topOverlap < 0)
+ scroll->setY(topOverlap);
+ if (original.fBottom < layerBounds.fBottom && bottomOverlap > 0
+ && -topOverlap > bottomOverlap)
+ scroll->setY(bottomOverlap);
+ bool leftSet = scrollX > leftOverlap && (direction == LEFT
+ || !scrollX);
+ if (leftSet)
+ scroll->setX(leftOverlap);
+ if (scrollX < rightOverlap && (direction == RIGHT || (!scrollX
+ && (!leftSet || -leftOverlap > rightOverlap))))
+ scroll->setX(rightOverlap);
+ }
+ DBG_NAV_LOGD("rings=(%d,%d,w=%d,h=%d) layers=(%d,%d,w=%d,h=%d)"
+ " scroll=(%d,%d)",
+ ringBounds.fLeft, ringBounds.fTop, ringBounds.width(), ringBounds.height(),
+ layerBounds.fLeft, layerBounds.fTop, layerBounds.width(), layerBounds.height(),
+ scroll->x(), scroll->y());
+ }
+#endif
+ }
+ *framePtr = bestData.mFrame;
+ return const_cast<CachedNode*>(bestData.mNode);
+}
+
+const CachedNode* CachedRoot::nextTextField(const CachedNode* start,
+ const CachedFrame** framePtr) const
+{
+ bool startFound = false;
+ return CachedFrame::nextTextField(start, framePtr, &startFound);
+}
+
+SkPicture* CachedRoot::pictureAt(int* xPtr, int* yPtr, int* id) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (mRootLayer) {
+ const LayerAndroid* layer = mRootLayer->find(xPtr, yPtr, mPicture);
+ if (layer) {
+ SkPicture* picture = layer->picture();
+ DBG_NAV_LOGD("layer %d picture=%p (%d,%d)", layer->uniqueId(),
+ picture, picture ? picture->width() : 0,
+ picture ? picture->height() : 0);
+ if (picture) {
+ if (id)
+ *id = layer->uniqueId();
+ return picture;
+ }
+ }
+ }
+#endif
+ DBG_NAV_LOGD("root mPicture=%p (%d,%d)", mPicture, mPicture ?
+ mPicture->width() : 0, mPicture ? mPicture->height() : 0);
+ if (id)
+ *id = -1;
+ return mPicture;
+}
+
+void CachedRoot::reset()
+{
+#ifndef NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+ mContents = mViewBounds = WebCore::IntRect(0, 0, 0, 0);
+ mMaxXScroll = mMaxYScroll = 0;
+ mRootLayer = 0;
+ mSelectionStart = mSelectionEnd = -1;
+ mScrollOnly = false;
+}
+
+bool CachedRoot::scrollDelta(WebCore::IntRect& newOutset, Direction direction, int* delta)
+{
+ switch (direction) {
+ case LEFT:
+ *delta = -mMaxXScroll;
+ return newOutset.x() >= mViewBounds.x();
+ case RIGHT:
+ *delta = mMaxXScroll;
+ return newOutset.maxX() <= mViewBounds.maxX();
+ case UP:
+ *delta = -mMaxYScroll;
+ return newOutset.y() >= mViewBounds.y();
+ case DOWN:
+ *delta = mMaxYScroll;
+ return newOutset.maxY() <= mViewBounds.maxY();
+ default:
+ *delta = 0;
+ ASSERT(0);
+ }
+ return false;
+}
+
+void CachedRoot::setCachedFocus(CachedFrame* frame, CachedNode* node)
+{
+ mFocusBounds = WebCore::IntRect(0, 0, 0, 0);
+ if (node == NULL)
+ return;
+ node->setIsFocus(true);
+ mFocusBounds = node->bounds(frame);
+ frame->setFocusIndex(node - frame->document());
+ CachedFrame* parent;
+ while ((parent = frame->parent()) != NULL) {
+ parent->setFocusIndex(frame->indexInParent());
+ frame = parent;
+ }
+#if DEBUG_NAV_UI
+ const CachedFrame* focusFrame;
+ const CachedNode* focus = currentFocus(&focusFrame);
+ WebCore::IntRect bounds = WebCore::IntRect(0, 0, 0, 0);
+ if (focus)
+ bounds = focus->bounds(focusFrame);
+ DBG_NAV_LOGD("new focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+#endif
+}
+
+void CachedRoot::setCursor(CachedFrame* frame, CachedNode* node)
+{
+#if DEBUG_NAV_UI
+ const CachedFrame* cursorFrame;
+ const CachedNode* cursor = currentCursor(&cursorFrame);
+ WebCore::IntRect bounds;
+ if (cursor)
+ bounds = cursor->bounds(cursorFrame);
+ DBG_NAV_LOGD("old cursor %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
+ cursor ? cursor->index() : 0,
+ cursor ? cursor->nodePointer() : NULL, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+#endif
+ clearCursor();
+ if (node == NULL)
+ return;
+ node->setIsCursor(true);
+ node->show();
+ frame->setCursorIndex(node - frame->document());
+ CachedFrame* parent;
+ while ((parent = frame->parent()) != NULL) {
+ parent->setCursorIndex(frame->indexInParent());
+ frame = parent;
+ }
+#if DEBUG_NAV_UI
+ cursor = currentCursor(&cursorFrame);
+ bounds = WebCore::IntRect(0, 0, 0, 0);
+ if (cursor)
+ bounds = cursor->bounds(cursorFrame);
+ DBG_NAV_LOGD("new cursor %d (nodePointer=%p) bounds={%d,%d,%d,%d}",
+ cursor ? cursor->index() : 0,
+ cursor ? cursor->nodePointer() : NULL, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+#endif
+}
+
+void CachedRoot::setCursorCache(int scrollX, int scrollY) const
+{
+ mCursor = currentCursor();
+ if (mCursor)
+ mCursorBounds = mCursor->bounds(this);
+ if (!mRootLayer)
+ return;
+ SkRegion baseScrolled(mScrolledBounds);
+ mBaseUncovered = SkRegion(mScrolledBounds);
+#if USE(ACCELERATED_COMPOSITING)
+#if DUMP_NAV_CACHE
+ CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
+#endif
+ SkTDArray<SkRect> region;
+ mRootLayer->clipArea(&region);
+ WebCore::IntSize offset(
+ copysign(min(max(0, mContents.width() - mScrolledBounds.width()),
+ abs(scrollX)), scrollX),
+ copysign(min(max(0, mContents.height() - mScrolledBounds.height()),
+ abs(scrollY)), scrollY));
+ bool hasOffset = offset.width() || offset.height();
+ // restrict scrollBounds to that which is not under layer
+ for (int index = 0; index < region.count(); index++) {
+ SkIRect less;
+ region[index].round(&less);
+ DBG_NAV_LOGD("less=(%d,%d,w=%d,h=%d)", less.fLeft, less.fTop,
+ less.width(), less.height());
+ mBaseUncovered.op(less, SkRegion::kDifference_Op);
+ if (!hasOffset)
+ continue;
+ less.offset(offset.width(), offset.height());
+ baseScrolled.op(less, SkRegion::kDifference_Op);
+ }
+ if (hasOffset)
+ mBaseUncovered.op(baseScrolled, SkRegion::kUnion_Op);
+#endif
+}
+
+#if DUMP_NAV_CACHE
+
+#define DEBUG_PRINT_BOOL(field) \
+ DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false")
+
+#define DEBUG_PRINT_RECT(field) \
+ { const WebCore::IntRect& r = b->field; \
+ DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \
+ r.x(), r.y(), r.width(), r.height()); }
+
+CachedRoot* CachedRoot::Debug::base() const {
+ CachedRoot* nav = (CachedRoot*) ((char*) this - OFFSETOF(CachedRoot, mDebug));
+ return nav;
+}
+
+void CachedRoot::Debug::print() const
+{
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ gWriteLogMutex.lock();
+ ASSERT(gNavCacheLogFile == NULL);
+ gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a");
+#endif
+ CachedRoot* b = base();
+ b->CachedFrame::mDebug.print();
+ b->mHistory->mDebug.print(b);
+ DUMP_NAV_LOGD("// int mMaxXScroll=%d, mMaxYScroll=%d;\n",
+ b->mMaxXScroll, b->mMaxYScroll);
+ if (b->mRootLayer)
+ CachedLayer::Debug::printRootLayerAndroid(b->mRootLayer);
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ if (gNavCacheLogFile)
+ fclose(gNavCacheLogFile);
+ gNavCacheLogFile = NULL;
+ gWriteLogMutex.unlock();
+#endif
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/nav/CachedRoot.h b/Source/WebKit/android/nav/CachedRoot.h
new file mode 100644
index 0000000..65c6062
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedRoot.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2007, 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 CachedRoot_h
+#define CachedRoot_h
+
+#include "CachedFrame.h"
+#include "IntRect.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "wtf/Vector.h"
+
+class SkRect;
+
+namespace WebCore {
+ class LayerAndroid;
+}
+
+namespace android {
+
+class CachedHistory;
+class CachedNode;
+class FindCanvas;
+
+class CachedRoot : public CachedFrame {
+public:
+ bool adjustForScroll(BestData* , Direction , WebCore::IntPoint* scrollPtr,
+ bool findClosest);
+ const SkRegion& baseUncovered() const { return mBaseUncovered; }
+ void calcBitBounds(const IntRect& , IntRect* ) const;
+ int checkForCenter(int x, int y) const;
+ void checkForJiggle(int* ) const;
+ bool checkRings(SkPicture* , const CachedNode* ,
+ const WebCore::IntRect& testBounds) const;
+ WebCore::IntPoint cursorLocation() const;
+ int documentHeight() { return mContents.height(); }
+ int documentWidth() { return mContents.width(); }
+ void draw(FindCanvas& ) const;
+ const CachedNode* findAt(const WebCore::IntRect& , const CachedFrame** ,
+ int* x, int* y, bool checkForHidden) const;
+ const WebCore::IntRect& focusBounds() const { return mFocusBounds; }
+ WebCore::IntPoint focusLocation() const;
+ int getAndResetSelectionEnd();
+ int getAndResetSelectionStart();
+ int getBlockLeftEdge(int x, int y, float scale) const;
+ void getSimulatedMousePosition(WebCore::IntPoint* ) const;
+ void init(WebCore::Frame* , CachedHistory* );
+ bool innerDown(const CachedNode* , BestData* ) const;
+ bool innerLeft(const CachedNode* , BestData* ) const;
+ void innerMove(const CachedNode* ,BestData* bestData, Direction ,
+ WebCore::IntPoint* scroll, bool firstCall);
+ bool innerRight(const CachedNode* , BestData* ) const;
+ bool innerUp(const CachedNode* , BestData* ) const;
+ WTF::String imageURI(int x, int y) const;
+ bool maskIfHidden(BestData* ) const;
+ const CachedNode* moveCursor(Direction , const CachedFrame** , WebCore::IntPoint* scroll);
+ /**
+ * Find the next textfield/textarea
+ * @param start The textfield/textarea to search from.
+ * @param framePtr If non-zero, returns CachedFrame* containing result.
+ * @return CachedNode* Next textfield/textarea or null (0) if none.
+ */
+ const CachedNode* nextTextField(const CachedNode* start,
+ const CachedFrame** framePtr) const;
+ SkPicture* pictureAt(int* xPtr, int* yPtr, int* id) const;
+ SkPicture* pictureAt(int* xPtr, int* yPtr) const {
+ return pictureAt(xPtr, yPtr, 0); }
+ void reset();
+ CachedHistory* rootHistory() const { return mHistory; }
+ WebCore::LayerAndroid* rootLayer() const { return mRootLayer; }
+ bool scrollDelta(WebCore::IntRect& cursorRingBounds, Direction , int* delta);
+ const WebCore::IntRect& scrolledBounds() const { return mScrolledBounds; }
+ void setCursor(CachedFrame* , CachedNode* );
+ void setCursorCache(int scrollX, int scrollY) const; // compute cached state used to find next cursor
+ void setCachedFocus(CachedFrame* , CachedNode* );
+ void setFocusBounds(const WebCore::IntRect& r) { mFocusBounds = r; }
+ void setTextGeneration(int textGeneration) { mTextGeneration = textGeneration; }
+ void setMaxScroll(int x, int y) { mMaxXScroll = x; mMaxYScroll = y; }
+ void setPicture(SkPicture* picture) { mPicture = picture; }
+ void setRootLayer(WebCore::LayerAndroid* layer) {
+ mRootLayer = layer;
+ resetLayers();
+ }
+ void setScrollOnly(bool state) { mScrollOnly = state; }
+ void setSelection(int start, int end) { mSelectionStart = start; mSelectionEnd = end; }
+ void setupScrolledBounds() const { mScrolledBounds = mViewBounds; }
+ void setVisibleRect(const WebCore::IntRect& r) { mViewBounds = r; }
+ int textGeneration() const { return mTextGeneration; }
+ int width() const { return mPicture ? mPicture->width() : 0; }
+private:
+ friend class CachedFrame;
+ CachedHistory* mHistory;
+ SkPicture* mPicture;
+ WebCore::LayerAndroid* mRootLayer;
+ WebCore::IntRect mFocusBounds; // dom text input focus node bounds
+ mutable WebCore::IntRect mScrolledBounds; // view bounds + amount visible as result of scroll
+ int mTextGeneration;
+ int mMaxXScroll;
+ int mMaxYScroll;
+ // These two are ONLY used when the tree is rebuilt and the focus is a textfield/area
+ int mSelectionStart;
+ int mSelectionEnd;
+ // these four set up as cache for use by frameDown/Up/Left/Right etc
+ mutable WebCore::IntRect mCursorBounds;
+ mutable const CachedNode* mCursor;
+ mutable SkRegion mBaseUncovered;
+ bool mScrollOnly;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedRoot* base() const;
+ void print() const;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/DrawExtra.cpp b/Source/WebKit/android/nav/DrawExtra.cpp
deleted file mode 100644
index 2f57dc1..0000000
--- a/Source/WebKit/android/nav/DrawExtra.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#include "DrawExtra.h"
-#include "GLExtras.h"
-#include "LayerAndroid.h"
-#include "SkCanvas.h"
-#include "SkRegion.h"
-#include "WebViewCore.h"
-
-RegionLayerDrawExtra::RegionLayerDrawExtra()
- : m_highlightColor(COLOR_HOLO_LIGHT)
-{}
-
-RegionLayerDrawExtra::~RegionLayerDrawExtra()
-{
- HighlightRegionMap::iterator end = m_highlightRegions.end();
- for (HighlightRegionMap::iterator it = m_highlightRegions.begin(); it != end; ++it) {
- delete it->second;
- it->second = 0;
- }
-}
-
-SkRegion* RegionLayerDrawExtra::getHighlightRegionsForLayer(const LayerAndroid* layer)
-{
- int layerId = layer ? layer->uniqueId() : 0;
- return m_highlightRegions.get(layerId);
-}
-
-void RegionLayerDrawExtra::addHighlightRegion(const LayerAndroid* layer, const Vector<IntRect>& rects,
- const IntPoint& additionalOffset)
-{
- if (rects.isEmpty())
- return;
- int layerId = layer ? layer->uniqueId() : 0;
- SkRegion* region = m_highlightRegions.get(layerId);
- if (!region) {
- region = new SkRegion();
- m_highlightRegions.set(layerId, region);
- }
- IntPoint offset = additionalOffset;
- WebViewCore::layerToAbsoluteOffset(layer, offset);
- for (size_t i = 0; i < rects.size(); i++) {
- IntRect r = rects.at(i);
- r.move(-offset.x(), -offset.y());
- region->op(r.x(), r.y(), r.maxX(), r.maxY(), SkRegion::kUnion_Op);
- }
-}
-
-void RegionLayerDrawExtra::draw(SkCanvas* canvas, LayerAndroid* layer)
-{
- SkRegion* region = getHighlightRegionsForLayer(layer);
- if (!region || region->isEmpty())
- return;
- SkRegion::Iterator rgnIter(*region);
- SkPaint paint;
- paint.setColor(m_highlightColor.rgb());
- while (!rgnIter.done()) {
- const SkIRect& rect = rgnIter.rect();
- canvas->drawIRect(rect, paint);
- rgnIter.next();
- }
-}
-
-void RegionLayerDrawExtra::drawGL(GLExtras* glExtras, const LayerAndroid* layer)
-{
- SkRegion* region = getHighlightRegionsForLayer(layer);
- if (!region || region->isEmpty())
- return;
- const TransformationMatrix* transform = layer ? layer->drawTransform() : 0;
- glExtras->drawRegion(*region, true, false, transform, m_highlightColor);
-}
diff --git a/Source/WebKit/android/nav/DrawExtra.h b/Source/WebKit/android/nav/DrawExtra.h
index cc94476..6716a65 100644
--- a/Source/WebKit/android/nav/DrawExtra.h
+++ b/Source/WebKit/android/nav/DrawExtra.h
@@ -26,25 +26,11 @@
#ifndef DrawExtra_h
#define DrawExtra_h
-#include "config.h"
-
-#include "Color.h"
-#include "IntPoint.h"
-#include "IntRect.h"
-#include "wtf/HashMap.h"
-#include "wtf/Vector.h"
-
-// Color of the ring copied from framework's holo_light
-#define COLOR_HOLO_LIGHT 0x6633B5E5
-// Color of the ring copied from framework's holo_dark
-#define COLOR_HOLO_DARK 0x660099CC
-
class SkCanvas;
-class SkRegion;
namespace WebCore {
+ class IntRect;
class LayerAndroid;
- class GLExtras;
}
using namespace WebCore;
@@ -54,27 +40,7 @@ namespace android {
class DrawExtra {
public:
virtual ~DrawExtra() {}
- virtual void draw(SkCanvas*, LayerAndroid*) {}
- virtual void drawGL(GLExtras*, const LayerAndroid*) {}
-};
-
-// A helper extra that has a SkRegion per LayerAndroid
-class RegionLayerDrawExtra : public DrawExtra {
-public:
- RegionLayerDrawExtra();
- virtual ~RegionLayerDrawExtra();
-
- void addHighlightRegion(const LayerAndroid* layer, const Vector<IntRect>& rects,
- const IntPoint& additionalOffset = IntPoint());
- virtual void draw(SkCanvas*, LayerAndroid*);
- virtual void drawGL(GLExtras*, const LayerAndroid*);
-
-private:
- SkRegion* getHighlightRegionsForLayer(const LayerAndroid* layer);
-
- typedef HashMap<int, SkRegion* > HighlightRegionMap;
- HighlightRegionMap m_highlightRegions;
- Color m_highlightColor;
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ) = 0;
};
}
diff --git a/Source/WebKit/android/nav/FindCanvas.cpp b/Source/WebKit/android/nav/FindCanvas.cpp
new file mode 100644
index 0000000..ca3cfba
--- /dev/null
+++ b/Source/WebKit/android/nav/FindCanvas.cpp
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2008, 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 "webviewglue"
+
+#include "config.h"
+#include "FindCanvas.h"
+#include "LayerAndroid.h"
+#include "IntRect.h"
+#include "SelectText.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCornerPathEffect.h"
+#include "SkRect.h"
+#include "SkUtils.h"
+
+#include <utils/Log.h>
+
+#define INTEGER_OUTSET 2
+
+namespace android {
+
+// MatchInfo methods
+////////////////////////////////////////////////////////////////////////////////
+
+MatchInfo::MatchInfo() {
+ m_picture = 0;
+}
+
+MatchInfo::~MatchInfo() {
+ SkSafeUnref(m_picture);
+}
+
+MatchInfo::MatchInfo(const MatchInfo& src) {
+ m_layerId = src.m_layerId;
+ m_location = src.m_location;
+ m_picture = src.m_picture;
+ SkSafeRef(m_picture);
+}
+
+void MatchInfo::set(const SkRegion& region, SkPicture* pic, int layerId) {
+ SkSafeUnref(m_picture);
+ m_layerId = layerId;
+ m_location = region;
+ m_picture = pic;
+ SkASSERT(pic);
+ pic->ref();
+}
+
+// GlyphSet methods
+////////////////////////////////////////////////////////////////////////////////
+
+GlyphSet::GlyphSet(const SkPaint& paint, const UChar* lower, const UChar* upper,
+ size_t byteLength) {
+ SkPaint clonePaint(paint);
+ clonePaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ mTypeface = paint.getTypeface();
+ mCount = clonePaint.textToGlyphs(lower, byteLength, NULL);
+ if (mCount > MAX_STORAGE_COUNT) {
+ mLowerGlyphs = new uint16_t[2*mCount];
+ } else {
+ mLowerGlyphs = &mStorage[0];
+ }
+ // Use one array, and have mUpperGlyphs point to a portion of it,
+ // so that we can reduce the number of new/deletes
+ mUpperGlyphs = mLowerGlyphs + mCount;
+ int count2 = clonePaint.textToGlyphs(lower, byteLength, mLowerGlyphs);
+ SkASSERT(mCount == count2);
+ count2 = clonePaint.textToGlyphs(upper, byteLength, mUpperGlyphs);
+ SkASSERT(mCount == count2);
+}
+
+GlyphSet::~GlyphSet() {
+ // Do not need to delete mTypeface, which is not owned by us.
+ if (mCount > MAX_STORAGE_COUNT) {
+ delete[] mLowerGlyphs;
+ } // Otherwise, we just used local storage space, so no need to delete
+ // Also do not need to delete mUpperGlyphs, which simply points to a
+ // part of mLowerGlyphs
+}
+
+GlyphSet& GlyphSet::operator=(GlyphSet& src) {
+ mTypeface = src.mTypeface;
+ mCount = src.mCount;
+ if (mCount > MAX_STORAGE_COUNT) {
+ mLowerGlyphs = new uint16_t[2*mCount];
+ } else {
+ mLowerGlyphs = &mStorage[0];
+ }
+ memcpy(mLowerGlyphs, src.mLowerGlyphs, 2*mCount*sizeof(uint16_t));
+ mUpperGlyphs = mLowerGlyphs + mCount;
+ return *this;
+}
+
+bool GlyphSet::characterMatches(uint16_t c, int index) {
+ SkASSERT(index < mCount && index >= 0);
+ return c == mLowerGlyphs[index] || c == mUpperGlyphs[index];
+}
+
+// FindCanvas methods
+////////////////////////////////////////////////////////////////////////////////
+
+FindCanvas::FindCanvas(int width, int height, const UChar* lower,
+ const UChar* upper, size_t byteLength)
+ : mLowerText(lower)
+ , mUpperText(upper)
+ , mLength(byteLength)
+ , mNumFound(0) {
+ // the text has been provided in read order. Reverse as needed so the
+ // result contains left-to-right characters.
+ const uint16_t* start = mLowerText;
+ size_t count = byteLength >> 1;
+ const uint16_t* end = mLowerText + count;
+ 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) {
+ mLowerReversed.clear();
+ mLowerReversed.append(mLowerText, count);
+ WebCore::ReverseBidi(mLowerReversed.begin(), count);
+ mLowerText = mLowerReversed.begin();
+ mUpperReversed.clear();
+ mUpperReversed.append(mUpperText, count);
+ WebCore::ReverseBidi(mUpperReversed.begin(), count);
+ mUpperText = mUpperReversed.begin();
+ break;
+ }
+ }
+
+ setBounder(&mBounder);
+ mOutset = -SkIntToScalar(INTEGER_OUTSET);
+ mMatches = new WTF::Vector<MatchInfo>();
+ mWorkingIndex = 0;
+ mWorkingCanvas = 0;
+ mWorkingPicture = 0;
+}
+
+FindCanvas::~FindCanvas() {
+ setBounder(NULL);
+ /* Just in case getAndClear was not called. */
+ delete mMatches;
+ SkSafeUnref(mWorkingPicture);
+}
+
+// Each version of addMatch returns a rectangle for a match.
+// Not all of the parameters are used by each version.
+SkRect FindCanvas::addMatchNormal(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar pos[], SkScalar y) {
+ const uint16_t* lineStart = glyphs - index;
+ /* Use the original paint, since "text" is in glyphs */
+ SkScalar before = paint.measureText(lineStart, index * sizeof(uint16_t), 0);
+ SkRect rect;
+ rect.fLeft = pos[0] + before;
+ int countInBytes = count * sizeof(uint16_t);
+ rect.fRight = paint.measureText(glyphs, countInBytes, 0) + rect.fLeft;
+ SkPaint::FontMetrics fontMetrics;
+ paint.getFontMetrics(&fontMetrics);
+ SkScalar baseline = y;
+ rect.fTop = baseline + fontMetrics.fAscent;
+ rect.fBottom = baseline + fontMetrics.fDescent;
+ const SkMatrix& matrix = getTotalMatrix();
+ matrix.mapRect(&rect);
+ // Add the text to our picture.
+ SkCanvas* canvas = getWorkingCanvas();
+ int saveCount = canvas->save();
+ canvas->concat(matrix);
+ canvas->drawText(glyphs, countInBytes, pos[0] + before, y, paint);
+ canvas->restoreToCount(saveCount);
+ return rect;
+}
+
+SkRect FindCanvas::addMatchPos(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar xPos[], SkScalar /* y */) {
+ SkRect r;
+ r.setEmpty();
+ const SkPoint* temp = reinterpret_cast<const SkPoint*> (xPos);
+ const SkPoint* points = &temp[index];
+ int countInBytes = count * sizeof(uint16_t);
+ SkPaint::FontMetrics fontMetrics;
+ paint.getFontMetrics(&fontMetrics);
+ // Need to check each character individually, since the heights may be
+ // different.
+ for (int j = 0; j < count; j++) {
+ SkRect bounds;
+ bounds.fLeft = points[j].fX;
+ bounds.fRight = bounds.fLeft +
+ paint.measureText(&glyphs[j], sizeof(uint16_t), 0);
+ SkScalar baseline = points[j].fY;
+ bounds.fTop = baseline + fontMetrics.fAscent;
+ bounds.fBottom = baseline + fontMetrics.fDescent;
+ /* Accumulate and then add the resulting rect to mMatches */
+ r.join(bounds);
+ }
+ SkMatrix matrix = getTotalMatrix();
+ matrix.mapRect(&r);
+ SkCanvas* canvas = getWorkingCanvas();
+ int saveCount = canvas->save();
+ canvas->concat(matrix);
+ canvas->drawPosText(glyphs, countInBytes, points, paint);
+ canvas->restoreToCount(saveCount);
+ return r;
+}
+
+SkRect FindCanvas::addMatchPosH(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar position[], SkScalar constY) {
+ SkRect r;
+ // We only care about the positions starting at the index of our match
+ const SkScalar* xPos = &position[index];
+ // This assumes that the position array is monotonic increasing
+ // The left bounds will be the position of the left most character
+ r.fLeft = xPos[0];
+ // The right bounds will be the position of the last character plus its
+ // width
+ int lastIndex = count - 1;
+ r.fRight = paint.measureText(&glyphs[lastIndex], sizeof(uint16_t), 0)
+ + xPos[lastIndex];
+ // Grab font metrics to determine the top and bottom of the bounds
+ SkPaint::FontMetrics fontMetrics;
+ paint.getFontMetrics(&fontMetrics);
+ r.fTop = constY + fontMetrics.fAscent;
+ r.fBottom = constY + fontMetrics.fDescent;
+ const SkMatrix& matrix = getTotalMatrix();
+ matrix.mapRect(&r);
+ SkCanvas* canvas = getWorkingCanvas();
+ int saveCount = canvas->save();
+ canvas->concat(matrix);
+ canvas->drawPosTextH(glyphs, count * sizeof(uint16_t), xPos, constY, paint);
+ canvas->restoreToCount(saveCount);
+ return r;
+}
+
+void FindCanvas::drawLayers(LayerAndroid* layer) {
+#if USE(ACCELERATED_COMPOSITING)
+ SkPicture* picture = layer->picture();
+ if (picture) {
+ setLayerId(layer->uniqueId());
+ drawPicture(*picture);
+ }
+ for (int i = 0; i < layer->countChildren(); i++)
+ drawLayers(layer->getChild(i));
+#endif
+}
+
+void FindCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ findHelper(text, byteLength, paint, &x, y, &FindCanvas::addMatchNormal);
+}
+
+void FindCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ // Pass in the first y coordinate for y so that we can check to see whether
+ // it is lower than the last draw call (to check if we are continuing to
+ // another line).
+ findHelper(text, byteLength, paint, (const SkScalar*) pos, pos[0].fY,
+ &FindCanvas::addMatchPos);
+}
+
+void FindCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ findHelper(text, byteLength, paint, xpos, constY,
+ &FindCanvas::addMatchPosH);
+}
+
+/* The current behavior is to skip substring matches. This means that in the
+ * string
+ * batbatbat
+ * a search for
+ * batbat
+ * will return 1 match. If the desired behavior is to return 2 matches, define
+ * INCLUDE_SUBSTRING_MATCHES to be 1.
+ */
+#define INCLUDE_SUBSTRING_MATCHES 0
+
+// Need a quick way to know a maximum distance between drawText calls to know if
+// they are part of the same logical phrase when searching. By crude
+// inspection, half the point size seems a good guess at the width of a space
+// character.
+static inline SkScalar approximateSpaceWidth(const SkPaint& paint) {
+ return SkScalarHalf(paint.getTextSize());
+}
+
+void FindCanvas::findHelper(const void* text, size_t byteLength,
+ const SkPaint& paint, const SkScalar positions[],
+ SkScalar y,
+ SkRect (FindCanvas::*addMatch)(int index,
+ const SkPaint& paint, int count,
+ const uint16_t* glyphs,
+ const SkScalar positions[], SkScalar y)) {
+ SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ SkASSERT(mMatches);
+ GlyphSet* glyphSet = getGlyphs(paint);
+ const int count = glyphSet->getCount();
+ int numCharacters = byteLength >> 1;
+ const uint16_t* chars = (const uint16_t*) text;
+ // This block will check to see if we are continuing from another line. If
+ // so, the user needs to have added a space, which we do not draw.
+ if (mWorkingIndex) {
+ SkPoint newY;
+ getTotalMatrix().mapXY(0, y, &newY);
+ SkIRect workingBounds = mWorkingRegion.getBounds();
+ int newYInt = SkScalarRound(newY.fY);
+ if (workingBounds.fTop > newYInt) {
+ // The new text is above the working region, so we know it's not
+ // a continuation.
+ resetWorkingCanvas();
+ mWorkingIndex = 0;
+ mWorkingRegion.setEmpty();
+ } else if (workingBounds.fBottom < newYInt) {
+ // Now we know that this line is lower than our partial match.
+ SkPaint clonePaint(paint);
+ clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ uint16_t space;
+ clonePaint.textToGlyphs(" ", 1, &space);
+ if (glyphSet->characterMatches(space, mWorkingIndex)) {
+ mWorkingIndex++;
+ if (mWorkingIndex == count) {
+ // We already know that it is not clipped out because we
+ // checked for that before saving the working region.
+ insertMatchInfo(mWorkingRegion);
+
+ resetWorkingCanvas();
+ mWorkingIndex = 0;
+ mWorkingRegion.setEmpty();
+ // We have found a match, so continue on this line from
+ // scratch.
+ }
+ } else {
+ resetWorkingCanvas();
+ mWorkingIndex = 0;
+ mWorkingRegion.setEmpty();
+ }
+ }
+ // If neither one is true, then we are likely continuing on the same
+ // line, but are in a new draw call because the paint has changed. In
+ // this case, we can continue without adding a space.
+ }
+ // j is the position in the search text
+ // Start off with mWorkingIndex in case we are continuing from a prior call
+ int j = mWorkingIndex;
+ // index is the position in the drawn text
+ int index = 0;
+ for ( ; index != numCharacters; index++) {
+ if (glyphSet->characterMatches(chars[index], j)) {
+ // The jth character in the search text matches the indexth position
+ // in the drawn text, so increase j.
+ j++;
+ if (j != count) {
+ continue;
+ }
+ // The last count characters match, so we found the entire
+ // search string.
+ int remaining = count - mWorkingIndex;
+ int matchIndex = index - remaining + 1;
+ // Set up a pointer to the matching text in 'chars'.
+ const uint16_t* glyphs = chars + matchIndex;
+ SkRect rect = (this->*addMatch)(matchIndex, paint,
+ remaining, glyphs, positions, y);
+ // We need an SkIRect for SkRegion operations.
+ SkIRect iRect;
+ rect.roundOut(&iRect);
+ // Want to outset the drawn rectangle by the same amount as
+ // mOutset
+ iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET);
+ SkRegion regionToAdd(iRect);
+ if (!mWorkingRegion.isEmpty()) {
+ // If this is on the same line as our working region, make
+ // sure that they are close enough together that they are
+ // supposed to be part of the same text string.
+ // The width of two spaces has arbitrarily been chosen.
+ const SkIRect& workingBounds = mWorkingRegion.getBounds();
+ if (workingBounds.fTop <= iRect.fBottom &&
+ workingBounds.fBottom >= iRect.fTop &&
+ SkIntToScalar(iRect.fLeft - workingBounds.fRight) >
+ approximateSpaceWidth(paint)) {
+ index = -1; // Will increase to 0 on next run
+ // In this case, we need to start from the beginning of
+ // the text being searched and our search term.
+ j = 0;
+ mWorkingIndex = 0;
+ mWorkingRegion.setEmpty();
+ continue;
+ }
+ // Add the mWorkingRegion, which contains rectangles from
+ // the previous line(s).
+ regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op);
+ }
+ insertMatchInfo(regionToAdd);
+#if INCLUDE_SUBSTRING_MATCHES
+ // Reset index to the location of the match and reset j to the
+ // beginning, so that on the next iteration of the loop, index
+ // will advance by 1 and we will compare the next character in
+ // chars to the first character in the GlyphSet.
+ index = matchIndex;
+#endif
+ // Whether the clip contained it or not, we need to start over
+ // with our recording canvas
+ resetWorkingCanvas();
+ } else {
+ // Index needs to be set to index - j + 1.
+ // This is a ridiculous case, but imagine the situation where the
+ // user is looking for the string "jjog" in the drawText call for
+ // "jjjog". The first two letters match. However, when the index
+ // is 2, and we discover that 'o' and 'j' do not match, we should go
+ // back to 1, where we do, in fact, have a match
+ // FIXME: This does not work if (as in our example) "jj" is in one
+ // draw call and "jog" is in the next. Doing so would require a
+ // stack, keeping track of multiple possible working indeces and
+ // regions. This is likely an uncommon case.
+ index = index - j; // index will be increased by one on the next
+ // iteration
+ }
+ // We reach here in one of two cases:
+ // 1) We just completed a match, so any working rectangle/index is no
+ // longer needed, and we will start over from the beginning
+ // 2) The glyphs do not match, so we start over at the beginning of
+ // the search string.
+ j = 0;
+ mWorkingIndex = 0;
+ mWorkingRegion.setEmpty();
+ }
+ // At this point, we have searched all of the text in the current drawText
+ // call.
+ // Keep track of a partial match that may start on this line.
+ if (j > 0) { // if j is greater than 0, we have a partial match
+ int relativeCount = j - mWorkingIndex; // Number of characters in this
+ // part of the match.
+ int partialIndex = index - relativeCount; // Index that starts our
+ // partial match.
+ const uint16_t* partialGlyphs = chars + partialIndex;
+ SkRect partial = (this->*addMatch)(partialIndex, paint, relativeCount,
+ partialGlyphs, positions, y);
+ partial.inset(mOutset, mOutset);
+ SkIRect dest;
+ partial.roundOut(&dest);
+ mWorkingRegion.op(dest, SkRegion::kUnion_Op);
+ mWorkingIndex = j;
+ return;
+ }
+ // This string doesn't go into the next drawText, so reset our working
+ // variables
+ mWorkingRegion.setEmpty();
+ mWorkingIndex = 0;
+}
+
+SkCanvas* FindCanvas::getWorkingCanvas() {
+ if (!mWorkingPicture) {
+ mWorkingPicture = new SkPicture;
+ mWorkingCanvas = mWorkingPicture->beginRecording(0,0);
+ }
+ return mWorkingCanvas;
+}
+
+GlyphSet* FindCanvas::getGlyphs(const SkPaint& paint) {
+ SkTypeface* typeface = paint.getTypeface();
+ GlyphSet* end = mGlyphSets.end();
+ for (GlyphSet* ptr = mGlyphSets.begin();ptr != end; ptr++) {
+ if (ptr->getTypeface() == typeface) {
+ return ptr;
+ }
+ }
+
+ GlyphSet set(paint, mLowerText, mUpperText, mLength);
+ *mGlyphSets.append() = set;
+ return &(mGlyphSets.top());
+}
+
+void FindCanvas::insertMatchInfo(const SkRegion& region) {
+ mNumFound++;
+ mWorkingPicture->endRecording();
+ MatchInfo matchInfo;
+ mMatches->append(matchInfo);
+ LOGD("%s region=%p pict=%p layer=%d", __FUNCTION__,
+ &region, mWorkingPicture, mLayerId);
+ mMatches->last().set(region, mWorkingPicture, mLayerId);
+}
+
+void FindCanvas::resetWorkingCanvas() {
+ mWorkingPicture->unref();
+ mWorkingPicture = 0;
+ // Do not need to reset mWorkingCanvas itself because we only access it via
+ // getWorkingCanvas.
+}
+
+// This function sets up the paints that are used to draw the matches.
+void FindOnPage::setUpFindPaint() {
+ // Set up the foreground paint
+ m_findPaint.setAntiAlias(true);
+ const SkScalar roundiness = SkIntToScalar(2);
+ SkCornerPathEffect* cornerEffect = new SkCornerPathEffect(roundiness);
+ m_findPaint.setPathEffect(cornerEffect);
+ m_findPaint.setARGB(255, 132, 190, 0);
+
+ // Set up the background blur paint.
+ m_findBlurPaint.setAntiAlias(true);
+ m_findBlurPaint.setARGB(204, 0, 0, 0);
+ m_findBlurPaint.setPathEffect(cornerEffect);
+ cornerEffect->unref();
+ SkMaskFilter* blurFilter = SkBlurMaskFilter::Create(SK_Scalar1,
+ SkBlurMaskFilter::kNormal_BlurStyle);
+ m_findBlurPaint.setMaskFilter(blurFilter)->unref();
+ m_isFindPaintSetUp = true;
+}
+
+IntRect FindOnPage::currentMatchBounds() const {
+ IntRect noBounds = IntRect(0, 0, 0, 0);
+ if (!m_matches || !m_matches->size())
+ return noBounds;
+ MatchInfo& info = (*m_matches)[m_findIndex];
+ return info.getLocation().getBounds();
+}
+
+bool FindOnPage::currentMatchIsInLayer() const {
+ if (!m_matches || !m_matches->size())
+ return false;
+ MatchInfo& info = (*m_matches)[m_findIndex];
+ return info.isInLayer();
+}
+
+int FindOnPage::currentMatchLayerId() const {
+ return (*m_matches)[m_findIndex].layerId();
+}
+
+// This function is only used by findNext and setMatches. In it, we store
+// upper left corner of the match specified by m_findIndex in
+// m_currentMatchLocation.
+void FindOnPage::storeCurrentMatchLocation() {
+ SkASSERT(m_findIndex < m_matches->size());
+ const SkIRect& bounds = (*m_matches)[m_findIndex].getLocation().getBounds();
+ m_currentMatchLocation.set(bounds.fLeft, bounds.fTop);
+ m_hasCurrentLocation = true;
+}
+
+// Put a cap on the number of matches to draw. If the current page has more
+// matches than this, only draw the focused match.
+#define MAX_NUMBER_OF_MATCHES_TO_DRAW 101
+
+void FindOnPage::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval) {
+ if (!m_lastBounds.isEmpty()) {
+ inval->unite(m_lastBounds);
+ m_lastBounds.setEmpty();
+ }
+ if (!m_hasCurrentLocation || !m_matches || !m_matches->size())
+ return;
+ int layerId = layer->uniqueId();
+ if (m_findIndex >= m_matches->size())
+ m_findIndex = 0;
+ const MatchInfo& matchInfo = (*m_matches)[m_findIndex];
+ const SkRegion& currentMatchRegion = matchInfo.getLocation();
+
+ // Set up the paints used for drawing the matches
+ if (!m_isFindPaintSetUp)
+ setUpFindPaint();
+
+ // Draw the current match
+ if (matchInfo.layerId() == layerId) {
+ drawMatch(currentMatchRegion, canvas, true);
+ // Now draw the picture, so that it shows up on top of the rectangle
+ int saveCount = canvas->save();
+ SkPath matchPath;
+ currentMatchRegion.getBoundaryPath(&matchPath);
+ canvas->clipPath(matchPath);
+ canvas->drawPicture(*matchInfo.getPicture());
+ canvas->restoreToCount(saveCount);
+ const SkMatrix& matrix = canvas->getTotalMatrix();
+ const SkRect& localBounds = matchPath.getBounds();
+ SkRect globalBounds;
+ matrix.mapRect(&globalBounds, localBounds);
+ globalBounds.round(&m_lastBounds);
+ inval->unite(m_lastBounds);
+ }
+ // Draw the rest
+ unsigned numberOfMatches = m_matches->size();
+ if (numberOfMatches > 1
+ && numberOfMatches < MAX_NUMBER_OF_MATCHES_TO_DRAW) {
+ for (unsigned i = 0; i < numberOfMatches; i++) {
+ // The current match has already been drawn
+ if (i == m_findIndex)
+ continue;
+ if ((*m_matches)[i].layerId() != layerId)
+ continue;
+ const SkRegion& region = (*m_matches)[i].getLocation();
+ // Do not draw matches which intersect the current one, or if it is
+ // offscreen
+ if (currentMatchRegion.intersects(region))
+ continue;
+ SkRect bounds;
+ bounds.set(region.getBounds());
+ if (canvas->quickReject(bounds, SkCanvas::kAA_EdgeType))
+ continue;
+ drawMatch(region, canvas, false);
+ }
+ }
+}
+
+// Draw the match specified by region to the canvas.
+void FindOnPage::drawMatch(const SkRegion& region, SkCanvas* canvas,
+ bool focused)
+{
+ // For the match which has focus, use a filled paint. For the others, use
+ // a stroked paint.
+ if (focused) {
+ m_findPaint.setStyle(SkPaint::kFill_Style);
+ m_findBlurPaint.setStyle(SkPaint::kFill_Style);
+ } else {
+ m_findPaint.setStyle(SkPaint::kStroke_Style);
+ m_findPaint.setStrokeWidth(SK_Scalar1);
+ m_findBlurPaint.setStyle(SkPaint::kStroke_Style);
+ m_findBlurPaint.setStrokeWidth(SkIntToScalar(2));
+ }
+ // Find the path for the current match
+ SkPath matchPath;
+ region.getBoundaryPath(&matchPath);
+ // Offset the path for a blurred shadow
+ SkPath blurPath;
+ matchPath.offset(SK_Scalar1, SkIntToScalar(2), &blurPath);
+ int saveCount = 0;
+ if (!focused) {
+ saveCount = canvas->save();
+ canvas->clipPath(matchPath, SkRegion::kDifference_Op);
+ }
+ // Draw the blurred background
+ canvas->drawPath(blurPath, m_findBlurPaint);
+ if (!focused)
+ canvas->restoreToCount(saveCount);
+ // Draw the foreground
+ canvas->drawPath(matchPath, m_findPaint);
+}
+
+void FindOnPage::findNext(bool forward)
+{
+ if (!m_matches || !m_matches->size() || !m_hasCurrentLocation)
+ return;
+ if (forward) {
+ m_findIndex++;
+ if (m_findIndex == m_matches->size())
+ m_findIndex = 0;
+ } else {
+ if (m_findIndex == 0) {
+ m_findIndex = m_matches->size() - 1;
+ } else {
+ m_findIndex--;
+ }
+ }
+ storeCurrentMatchLocation();
+}
+
+// With this call, WebView takes ownership of matches, and is responsible for
+// deleting it.
+void FindOnPage::setMatches(WTF::Vector<MatchInfo>* matches)
+{
+ if (m_matches)
+ delete m_matches;
+ m_matches = matches;
+ if (m_matches->size()) {
+ if (m_hasCurrentLocation) {
+ for (unsigned i = 0; i < m_matches->size(); i++) {
+ const SkIRect& rect = (*m_matches)[i].getLocation().getBounds();
+ if (rect.fLeft == m_currentMatchLocation.fX
+ && rect.fTop == m_currentMatchLocation.fY) {
+ m_findIndex = i;
+ return;
+ }
+ }
+ }
+ // If we did not have a stored location, or if we were unable to restore
+ // it, store the new one.
+ m_findIndex = 0;
+ storeCurrentMatchLocation();
+ } else {
+ m_hasCurrentLocation = false;
+ }
+}
+
+}
diff --git a/Source/WebKit/android/nav/FindCanvas.h b/Source/WebKit/android/nav/FindCanvas.h
new file mode 100644
index 0000000..0fa095c
--- /dev/null
+++ b/Source/WebKit/android/nav/FindCanvas.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2008, 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 Find_Canvas_h
+#define Find_Canvas_h
+
+#include "DrawExtra.h"
+#include "IntRect.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkTDArray.h"
+
+#include <unicode/umachine.h>
+#include <wtf/Vector.h>
+
+namespace android {
+
+// Stores both region information and an SkPicture of the match, so that the
+// region can be drawn, followed by drawing the matching text on top of it.
+// This class owns its SkPicture
+class MatchInfo {
+public:
+ MatchInfo();
+ ~MatchInfo();
+ MatchInfo(const MatchInfo& src);
+ const SkRegion& getLocation() const { return m_location; }
+ // Return a pointer to our picture, representing the matching text. Does
+ // not transfer ownership of the picture.
+ SkPicture* getPicture() const { return m_picture; }
+ // This will make a copy of the region, and increase the ref count on the
+ // SkPicture. If this MatchInfo already had one, unref it.
+ void set(const SkRegion& region, SkPicture* pic, int layerId);
+ bool isInLayer() const { return m_layerId >= 0; }
+ int layerId() const { return m_layerId; }
+private:
+ MatchInfo& operator=(MatchInfo& src);
+ SkRegion m_location;
+ SkPicture* m_picture;
+ int m_layerId;
+};
+
+// A class containing a typeface for reference, the length in glyphs, and
+// the upper and lower case representations of the search string.
+class GlyphSet {
+public:
+ GlyphSet(const SkPaint& paint, const UChar* lower, const UChar* upper,
+ size_t byteLength);
+ ~GlyphSet();
+ GlyphSet& operator=(GlyphSet& src);
+
+ // Return true iff c matches one of our glyph arrays at index
+ bool characterMatches(uint16_t c, int index);
+
+ int getCount() const { return mCount; }
+
+ const SkTypeface* getTypeface() const { return mTypeface; }
+
+private:
+ // Disallow copy constructor
+ GlyphSet(GlyphSet& src) { }
+
+ // mTypeface is used for comparison only
+ const SkTypeface* mTypeface;
+ // mLowerGlyphs points to all of our storage space: the lower set followed
+ // by the upper set. mUpperGlyphs is purely a convenience pointer to the
+ // start of the upper case glyphs.
+ uint16_t* mLowerGlyphs;
+ uint16_t* mUpperGlyphs;
+ // mCount is the number of glyphs of the search string. Must be the same
+ // for both the lower case set and the upper case set.
+ int mCount;
+
+ // Arbitrarily chose the maximum storage to use in the GlyphSet. This is
+ // based on the length of the word being searched. If users are always
+ // searching for 3 letter words (for example), an ideal number would be 3.
+ // Each time the user searches for a word longer than (in this case, 3) that
+ // will result in calling new/delete.
+ enum Storage {
+ MAX_STORAGE_COUNT = 16
+ };
+ // In order to eliminate new/deletes, create storage that will be enough
+ // most of the time
+ uint16_t mStorage[2*MAX_STORAGE_COUNT];
+};
+
+class FindBounder : public SkBounder {
+public:
+ FindBounder() {}
+ ~FindBounder() {}
+protected:
+ virtual bool onIRect(const SkIRect&) { return false; }
+};
+
+class FindCanvas : public SkCanvas {
+public:
+ FindCanvas(int width, int height, const UChar* , const UChar*,
+ size_t byteLength);
+
+ virtual ~FindCanvas();
+
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint);
+
+ /* FIXME: This path has not been tested. */
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint);
+
+ /* Also untested */
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint);
+
+ /* Not sure what to do here or for drawTextOnPathHV */
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ }
+
+ void drawLayers(LayerAndroid*);
+ int found() const { return mNumFound; }
+ void setLayerId(int layerId) { mLayerId = layerId; }
+
+ // This method detaches our array of matches and passes ownership to
+ // the caller, who is then responsible for deleting them.
+ WTF::Vector<MatchInfo>* detachMatches() {
+ WTF::Vector<MatchInfo>* array = mMatches;
+ mMatches = NULL;
+ return array;
+ }
+
+private:
+ // These calls are made by findHelper to store information about each match
+ // that is found. They return a rectangle which is used to highlight the
+ // match. They also add to our SkPicture (which can be accessed with
+ // getDrawnMatches) a draw of each match. This way it can be drawn after
+ // the rectangle. The rect that is returned is in device coordinates.
+ SkRect addMatchNormal(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar pos[], SkScalar y);
+
+ SkRect addMatchPos(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar xPos[], SkScalar /* y */);
+
+ SkRect addMatchPosH(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar position[], SkScalar constY);
+
+ // Helper for each of our draw calls
+ void findHelper(const void* text, size_t byteLength, const SkPaint& paint,
+ const SkScalar xPos[], SkScalar y,
+ SkRect (FindCanvas::*addMatch)(int index,
+ const SkPaint& paint, int count, const uint16_t* glyphs,
+ const SkScalar pos[], SkScalar y));
+
+ // If we already have a working canvas, grab it. Otherwise, create a new
+ // one.
+ SkCanvas* getWorkingCanvas();
+
+ // Return the set of glyphs and its count for the text being searched for
+ // and the parameter paint. If one has already been created and cached
+ // for this paint, use it. If not, create a new one and cache it.
+ GlyphSet* getGlyphs(const SkPaint& paint);
+
+ // Store all the accumulated info about a match in our vector.
+ void insertMatchInfo(const SkRegion& region);
+
+ // Throw away our cumulative information about our working SkCanvas. After
+ // this call, next call to getWorkingCanvas will create a new one.
+ void resetWorkingCanvas();
+
+ // Since we may transfer ownership of this array (see detachRects()), we
+ // hold a pointer to the array instead of just the array itself.
+ WTF::Vector<MatchInfo>* mMatches;
+ const UChar* mLowerText;
+ const UChar* mUpperText;
+ Vector<UChar> mLowerReversed;
+ Vector<UChar> mUpperReversed;
+ size_t mLength;
+ FindBounder mBounder;
+ int mNumFound;
+ SkScalar mOutset;
+ SkTDArray<GlyphSet> mGlyphSets;
+
+ SkPicture* mWorkingPicture;
+ SkCanvas* mWorkingCanvas;
+ SkRegion mWorkingRegion;
+ int mWorkingIndex;
+ int mLayerId;
+};
+
+class FindOnPage : public DrawExtra {
+public:
+ FindOnPage() {
+ m_matches = 0;
+ m_hasCurrentLocation = false;
+ m_isFindPaintSetUp = false;
+ m_lastBounds.setEmpty();
+ }
+ virtual ~FindOnPage() { if (m_matches) delete m_matches; }
+ void clearCurrentLocation() { m_hasCurrentLocation = false; }
+ IntRect currentMatchBounds() const;
+ int currentMatchIndex() const { return m_findIndex; }
+ bool currentMatchIsInLayer() const;
+ // This requires the current match to be in a layer. See
+ // currentMatchIsInLayer().
+ int currentMatchLayerId() const;
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* );
+ void findNext(bool forward);
+ bool isCurrentLocationValid() { return m_hasCurrentLocation; }
+ void setMatches(WTF::Vector<MatchInfo>* matches);
+ WTF::Vector<MatchInfo>* matches() { return m_matches; }
+private:
+ void drawMatch(const SkRegion& region, SkCanvas* canvas, bool focused);
+ void setUpFindPaint();
+ void storeCurrentMatchLocation();
+ WTF::Vector<MatchInfo>* m_matches;
+ // Stores the location of the current match.
+ SkIPoint m_currentMatchLocation;
+ // Tells whether the value in m_currentMatchLocation is valid.
+ bool m_hasCurrentLocation;
+ // Tells whether we have done the setup to draw the Find matches.
+ bool m_isFindPaintSetUp;
+ // Paint used to draw our Find matches.
+ SkPaint m_findPaint;
+ // Paint used for the background of our Find matches.
+ SkPaint m_findBlurPaint;
+ unsigned m_findIndex;
+ SkIRect m_lastBounds;
+};
+
+}
+
+#endif // Find_Canvas_h
diff --git a/Source/WebKit/android/nav/SelectText.cpp b/Source/WebKit/android/nav/SelectText.cpp
index 7ce32c3..d20c44a 100644
--- a/Source/WebKit/android/nav/SelectText.cpp
+++ b/Source/WebKit/android/nav/SelectText.cpp
@@ -25,20 +25,23 @@
#define LOG_TAG "webviewglue"
-#include "config.h"
-
+#include "CachedPrefix.h"
#include "BidiResolver.h"
#include "BidiRunList.h"
-#include "GLExtras.h"
+#include "CachedRoot.h"
#include "LayerAndroid.h"
+#include "ParseCanvas.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"
#include "SkPoint.h"
#include "SkRect.h"
#include "SkRegion.h"
+#include "SkUtils.h"
#include "TextRun.h"
#ifdef DEBUG_NAV_UI
@@ -49,7 +52,7 @@
// #define EXTRA_NOISY_LOGGING 1
#define DEBUG_TOUCH_HANDLES 0
#if DEBUG_TOUCH_HANDLES
-#define DBG_HANDLE_LOG(format, ...) ALOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#define DBG_HANDLE_LOG(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
#else
#define DBG_HANDLE_LOG(...)
#endif
@@ -144,3 +147,1921 @@ void ReverseBidi(UChar* chars, int len) {
}
+namespace android {
+
+#define HYPHEN_MINUS 0x2D // ASCII hyphen
+#define SOLIDUS 0x2F // ASCII slash
+#define REVERSE_SOLIDUS 0x5C // ASCII backslash
+#define HYPHEN 0x2010 // unicode hyphen, first in range of dashes
+#define HORZ_BAR 0x2015 // unicode horizontal bar, last in range of dashes
+#define TOUCH_SLOP 10 // additional distance from character rect when hit
+
+class CommonCheck : public SkBounder {
+public:
+ CommonCheck(const SkIRect& area)
+ : mArea(area)
+ , mLastUni(0)
+ {
+ mLastGlyph.fGlyphID = static_cast<uint16_t>(-1);
+ mLastCandidate.fGlyphID = static_cast<uint16_t>(-1);
+ mMatrix.reset();
+ reset();
+ }
+
+ /* called only while the picture is parsed */
+ int base() {
+ if (mBase == INT_MAX) {
+ SkPoint result;
+ mMatrix.mapXY(0, mY, &result);
+ mBase = SkScalarFloor(result.fY);
+ }
+ return mBase;
+ }
+
+ /* called only while the picture is parsed */
+ int bottom() {
+ if (mBottom == INT_MAX) {
+ SkPoint result;
+ SkPaint::FontMetrics metrics;
+ mPaint.getFontMetrics(&metrics);
+ mMatrix.mapXY(0, metrics.fDescent + mY, &result);
+ mBottom = SkScalarCeil(result.fY);
+ }
+ return mBottom;
+ }
+
+#if DEBUG_NAV_UI
+ // make current (possibily uncomputed) value visible for debugging
+ int bottomDebug() const
+ {
+ return mBottom;
+ }
+#endif
+
+ bool addNewLine(const SkBounder::GlyphRec& rec)
+ {
+ SkFixed lineSpacing = SkFixedAbs(mLastGlyph.fLSB.fY - rec.fLSB.fY);
+ SkFixed lineHeight = SkIntToFixed(bottom() - top());
+ return lineSpacing >= lineHeight + (lineHeight >> 1); // 1.5
+ }
+
+ bool addSpace(const SkBounder::GlyphRec& rec)
+ {
+ bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
+ if (((mLastUni >= HYPHEN && mLastUni <= HORZ_BAR)
+ || mLastUni == HYPHEN_MINUS || mLastUni == SOLIDUS
+ || mLastUni == REVERSE_SOLIDUS) && newBaseLine)
+ {
+ return false;
+ }
+ return isSpace(rec);
+ }
+
+ void finishGlyph()
+ {
+ mLastGlyph = mLastCandidate;
+ mLastUni = mLastUniCandidate;
+ mLastPaint = mLastPaintCandidate;
+ }
+
+ const SkIRect& getArea() const {
+ return mArea;
+ }
+
+ /* called only while the picture is parsed */
+ SkUnichar getUniChar(const SkBounder::GlyphRec& rec)
+ {
+ SkUnichar unichar;
+ SkPaint::TextEncoding save = mPaint.getTextEncoding();
+ mPaint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ mPaint.glyphsToUnichars(&rec.fGlyphID, 1, &unichar);
+ mPaint.setTextEncoding(save);
+ return unichar;
+ }
+
+ bool isSpace(const SkBounder::GlyphRec& rec)
+ {
+ if (mLastGlyph.fGlyphID == static_cast<uint16_t>(-1))
+ return true;
+ DBG_NAV_LOGD("mLastGlyph=((%g, %g),(%g, %g), %d)"
+ " rec=((%g, %g),(%g, %g), %d) mLastUni=0x%04x '%c'",
+ SkFixedToScalar(mLastGlyph.fLSB.fX),
+ SkFixedToScalar(mLastGlyph.fLSB.fY),
+ SkFixedToScalar(mLastGlyph.fRSB.fX),
+ SkFixedToScalar(mLastGlyph.fRSB.fY), mLastGlyph.fGlyphID,
+ SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fLSB.fY),
+ SkFixedToScalar(rec.fRSB.fX), SkFixedToScalar(rec.fRSB.fY),
+ rec.fGlyphID,
+ mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
+ bool newBaseLine = mLastGlyph.fLSB.fY != rec.fLSB.fY;
+ if (newBaseLine)
+ return true;
+ SkFixed gapOne = mLastGlyph.fLSB.fX - rec.fRSB.fX;
+ SkFixed gapTwo = rec.fLSB.fX - mLastGlyph.fRSB.fX;
+ if (gapOne < 0 && gapTwo < 0)
+ return false; // overlaps
+ const SkBounder::GlyphRec& first = mLastGlyph.fLSB.fX < rec.fLSB.fX
+ ? mLastGlyph : rec;
+ const SkBounder::GlyphRec& second = mLastGlyph.fLSB.fX < rec.fLSB.fX
+ ? rec : mLastGlyph;
+ uint16_t firstGlyph = first.fGlyphID;
+ SkScalar firstWidth = mLastPaint.measureText(&firstGlyph, sizeof(firstGlyph));
+ SkFixed ceilWidth = SkIntToFixed(SkScalarCeil(firstWidth));
+ SkFixed posNoSpace = first.fLSB.fX + ceilWidth;
+ SkFixed ceilSpace = SkIntToFixed(SkFixedCeil(minSpaceWidth(mLastPaint)));
+ SkFixed posWithSpace = posNoSpace + ceilSpace;
+ SkFixed diffNoSpace = SkFixedAbs(second.fLSB.fX - posNoSpace);
+ SkFixed diffWithSpace = SkFixedAbs(second.fLSB.fX - posWithSpace);
+ DBG_NAV_LOGD("second=%g width=%g (%g) noSpace=%g (%g) withSpace=%g (%g)"
+ " fontSize=%g",
+ SkFixedToScalar(second.fLSB.fX),
+ firstWidth, SkFixedToScalar(ceilWidth),
+ SkFixedToScalar(posNoSpace), SkFixedToScalar(diffNoSpace),
+ SkFixedToScalar(posWithSpace), SkFixedToScalar(diffWithSpace),
+ mLastPaint.getTextSize());
+ return diffWithSpace <= diffNoSpace;
+ }
+
+ SkFixed minSpaceWidth(SkPaint& paint)
+ {
+ if (mMinSpaceWidth == SK_FixedMax) {
+ SkPaint::TextEncoding save = paint.getTextEncoding();
+ paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ SkScalar width = paint.measureText(" ", 1);
+ mMinSpaceWidth = SkScalarToFixed(width * mMatrix.getScaleX());
+ paint.setTextEncoding(save);
+ DBG_NAV_LOGV("width=%g matrix sx/sy=(%g, %g) tx/ty=(%g, %g)"
+ " mMinSpaceWidth=%g", width,
+ mMatrix.getScaleX(), mMatrix.getScaleY(),
+ mMatrix.getTranslateX(), mMatrix.getTranslateY(),
+ SkFixedToScalar(mMinSpaceWidth));
+ }
+ return mMinSpaceWidth;
+ }
+
+ void recordGlyph(const SkBounder::GlyphRec& rec)
+ {
+ mLastCandidate = rec;
+ mLastUniCandidate = getUniChar(rec);
+ mLastPaintCandidate = mPaint;
+ }
+
+ void reset()
+ {
+ mMinSpaceWidth = SK_FixedMax; // mark as uninitialized
+ mBase = mBottom = mTop = INT_MAX; // mark as uninitialized
+ }
+
+ void set(CommonCheck& check)
+ {
+ mLastGlyph = check.mLastGlyph;
+ mLastUni = check.mLastUni;
+ mMatrix = check.mMatrix;
+ mLastPaint = check.mLastPaint;
+ reset();
+ }
+
+ void setGlyph(CommonCheck& check)
+ {
+ mLastGlyph = check.mLastGlyph;
+ mLastUni = check.mLastUni;
+ mLastPaint = check.mLastPaint;
+ }
+
+ void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y,
+ const void* text)
+ {
+ mMatrix = matrix;
+ mPaint = paint;
+ mText = static_cast<const uint16_t*>(text);
+ mY = y;
+ reset();
+ }
+
+ /* called only while the picture is parsed */
+ int top() {
+ if (mTop == INT_MAX) {
+ SkPoint result;
+ SkPaint::FontMetrics metrics;
+ mPaint.getFontMetrics(&metrics);
+ mMatrix.mapXY(0, metrics.fAscent + mY, &result);
+ mTop = SkScalarFloor(result.fY);
+ }
+ return mTop;
+ }
+
+#if DEBUG_NAV_UI
+ // make current (possibily uncomputed) value visible for debugging
+ int topDebug() const
+ {
+ return mTop;
+ }
+#endif
+
+protected:
+ SkIRect mArea;
+ SkBounder::GlyphRec mLastCandidate;
+ SkBounder::GlyphRec mLastGlyph;
+ SkPaint mLastPaint; // available after picture has been parsed
+ SkPaint mLastPaintCandidate; // associated with candidate glyph
+ SkUnichar mLastUni;
+ SkUnichar mLastUniCandidate;
+ SkMatrix mMatrix;
+ SkPaint mPaint; // only set up while the picture is parsed
+ const uint16_t* mText;
+ SkScalar mY;
+private:
+ int mBase;
+ int mBottom;
+ SkFixed mMinSpaceWidth;
+ int mTop;
+ friend class EdgeCheck;
+};
+
+// generate the limit area for the new selection
+class LineCheck : public CommonCheck {
+public:
+ LineCheck(int x, int y, const SkIRect& area)
+ : INHERITED(area)
+ , mX(x)
+ , mY(y)
+ , mInBetween(false)
+ {
+ mLast.setEmpty();
+ }
+
+ void finish(const SkRegion& selectedRgn)
+ {
+ if (!mParagraphs.count() && mLast.isEmpty())
+ return;
+ processLine();
+ bool above = false;
+ bool below = false;
+ bool selected = false;
+ SkRegion localRgn(selectedRgn);
+ localRgn.translate(-mArea.fLeft, -mArea.fTop, &localRgn);
+ DBG_NAV_LOGD("localRgn=(%d,%d,%d,%d)",
+ localRgn.getBounds().fLeft, localRgn.getBounds().fTop,
+ localRgn.getBounds().fRight, localRgn.getBounds().fBottom);
+ for (int index = 0; index < mParagraphs.count(); index++) {
+ const SkIRect& rect = mParagraphs[index];
+ bool localSelected = localRgn.intersects(rect);
+ DBG_NAV_LOGD("[%d] rect=(%d,%d,%d,%d)", index, rect.fLeft, rect.fTop,
+ rect.fRight, rect.fBottom);
+ if (localSelected) {
+ DBG_NAV_LOGD("[%d] localSelected=true", index);
+ *mSelected.append() = rect;
+ }
+ if (rect.fRight <= mX || rect.fLeft >= mX)
+ continue;
+ if (mY > rect.fBottom) {
+ below = true;
+ selected |= localSelected;
+ DBG_NAV_LOGD("[%d] below=true localSelected=%s", index,
+ localSelected ? "true" : "false");
+ }
+ if (mY < rect.fTop) {
+ above = true;
+ selected |= localSelected;
+ DBG_NAV_LOGD("[%d] above=true localSelected=%s", index,
+ localSelected ? "true" : "false");
+ }
+ }
+ DBG_NAV_LOGD("mX=%d mY=%d above=%s below=%s selected=%s",
+ mX, mY, above ? "true" : "false", below ? "true" : "false",
+ selected ? "true" : "false");
+ mInBetween = above && below && selected;
+ }
+
+ bool inBetween() const
+ {
+ return mInBetween;
+ }
+
+ bool inColumn(const SkIRect& test) const
+ {
+ for (int index = 0; index < mSelected.count(); index++) {
+ const SkIRect& rect = mSelected[index];
+ if (rect.fRight > test.fLeft && rect.fLeft < test.fRight)
+ return true;
+ }
+ return false;
+ }
+
+ bool inColumn(int x, int y) const
+ {
+ for (int index = 0; index < mSelected.count(); index++) {
+ const SkIRect& rect = mSelected[index];
+ if (rect.contains(x, y))
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ SkIRect bounds;
+ bounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ // assume that characters must be consecutive to describe spaces
+ // (i.e., don't join rects drawn at different times)
+ if (bounds.fTop != mLast.fTop || bounds.fBottom != mLast.fBottom
+ || bounds.fLeft > mLast.fRight + minSpaceWidth(mPaint)
+ || bounds.fLeft < mLast.fLeft) {
+ processLine();
+ mLast = bounds;
+ } else
+ mLast.join(bounds);
+ return false;
+ }
+
+ void processLine()
+ {
+ // assume line spacing of 1.5
+ int lineHeight = bottom() - top();
+ mLast.inset(0, -lineHeight >> 1);
+ // collect arrays of rectangles making up glyphs below or above this one
+ for (int index = 0; index < mParagraphs.count(); index++) {
+ SkIRect& rect = mParagraphs[index];
+ if (SkIRect::Intersects(rect, mLast)) {
+ rect.join(mLast);
+ return;
+ }
+ }
+ *mParagraphs.append() = mLast;
+ }
+
+protected:
+ int mX;
+ int mY;
+ SkIRect mLast;
+ SkTDArray<SkIRect> mParagraphs;
+ SkTDArray<SkIRect> mSelected;
+ bool mInBetween;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class SelectText::FirstCheck : public CommonCheck {
+public:
+ FirstCheck(int x, int y, const SkIRect& area)
+ : INHERITED(area)
+ , mLineCheck(0)
+ , mFocusX(x - area.fLeft)
+ , mFocusY(y - area.fTop)
+ , mBestInColumn(false)
+ , mRecordGlyph(false)
+ {
+ reset();
+ }
+
+ const SkIRect& adjustedBounds(int* base)
+ {
+ *base = mBestBase + mArea.fTop;
+ mBestBounds.offset(mArea.fLeft, mArea.fTop);
+ DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
+ mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
+ mBestBounds.fBottom, topDebug(), bottomDebug());
+ return mBestBounds;
+ }
+
+ int focusX() const { return mFocusX; }
+ int focusY() const { return mFocusY; }
+
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ /* compute distance from rectangle center.
+ * centerX = (rect.L + rect.R) / 2
+ * multiply centerX and comparison x by 2 to retain better precision
+ */
+ SkIRect testBounds = {rect.fLeft, top(), rect.fRight, bottom()};
+ // dx and dy are the distances from the tested edge
+ // The edge distance is paramount if the test point is far away
+ int dx = std::max(0, std::max(testBounds.fLeft - mFocusX,
+ mFocusX - testBounds.fRight));
+ int dy = std::max(0, std::max(testBounds.fTop - mFocusY,
+ mFocusY - testBounds.fBottom));
+ bool testInColumn = false;
+ bool inBetween = false;
+ bool inFocus = false;
+ if (mLineCheck) {
+ testInColumn = mLineCheck->inColumn(testBounds);
+ inBetween = mLineCheck->inBetween();
+ inFocus = mLineCheck->inColumn(mFocusX, mFocusY);
+ }
+#ifdef EXTRA_NOISY_LOGGING
+ if (dy < 10) {
+ SkUnichar ch = getUniChar(rec);
+ DBG_NAV_LOGD("FC dx/y=%d,%d mDx/y=%d,%d test=%d,%d,%d,%d"
+ " best=%d,%d,%d,%d bestIn=%s tween=%s testIn=%s focus=%s ch=%c",
+ dx, dy, mDx, mDy,
+ testBounds.fLeft, testBounds.fTop, testBounds.fRight,
+ testBounds.fBottom, mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom,
+ mBestInColumn ? "true" : "false", inBetween ? "true" : "false",
+ testInColumn ? "true" : "false", inFocus ? "true" : "false",
+ ch < 0x7f ? ch : '?');
+ }
+#endif
+ if ((mBestInColumn || inBetween) && !testInColumn) {
+#ifdef EXTRA_NOISY_LOGGING
+ if (dy < 10) DBG_NAV_LOG("FirstCheck reject column");
+#endif
+ return false;
+ }
+ bool ignoreColumn = mBestInColumn == testInColumn || !inFocus;
+ if (ignoreColumn && dy > 0 && (mDy < dy
+ || (mDy == dy && dx > 0 && mDx <= dx))) {
+#ifdef EXTRA_NOISY_LOGGING
+ if (dy < 10) DBG_NAV_LOG("FirstCheck reject edge");
+#endif
+ return false;
+ }
+ // cx and cy are the distances from the tested center
+ // The center distance is used when the test point is over the text
+ int cx = std::abs(((testBounds.fLeft + testBounds.fRight) >> 1)
+ - mFocusX);
+ int cy = std::abs(((testBounds.fTop + testBounds.fBottom) >> 1)
+ - mFocusY);
+ if (ignoreColumn && dy == 0 && mDy == 0) {
+ if (mCy < cy) {
+#ifdef EXTRA_NOISY_LOGGING
+ DBG_NAV_LOGD("FirstCheck reject cy=%d mCy=%d", cy, mCy);
+#endif
+ return false;
+ }
+ if (mCy == cy) {
+ if (dx == 0 && mDx == 0) {
+ if (mCx < cx) {
+#ifdef EXTRA_NOISY_LOGGING
+ DBG_NAV_LOGD("FirstCheck reject cx=%d mCx=%d", cx, mCx);
+#endif
+ return false;
+ }
+ } else if (dx > 0 && mDx <= dx) {
+#ifdef EXTRA_NOISY_LOGGING
+ DBG_NAV_LOGD("FirstCheck reject dx=%d mDx=%d", dx, mDx);
+#endif
+ return false;
+ }
+ }
+ }
+#ifdef EXTRA_NOISY_LOGGING
+ if (dy < 10) {
+ DBG_NAV_LOGD("FirstCheck cx/y=(%d,%d)", cx, cy);
+ }
+#endif
+ mBestBase = base();
+ mBestBounds = testBounds;
+ mBestInColumn = testInColumn;
+#ifndef EXTRA_NOISY_LOGGING
+ if (dy < 10 && dx < 10)
+#endif
+ {
+#if DEBUG_NAV_UI
+ SkUnichar ch = getUniChar(rec);
+#endif
+ DBG_NAV_LOGD("FirstCheck dx/y=(%d,%d) mFocus=(%d,%d)"
+ " mBestBounds={%d,%d,r=%d,b=%d} inColumn=%s ch=%c",
+ dx, dy, mFocusX, mFocusY,
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom,
+ mBestInColumn ? "true" : "false", ch < 0x7f ? ch : '?');
+ }
+ mCx = cx;
+ mCy = cy;
+ mDx = dx;
+ mDy = dy;
+ if (mRecordGlyph)
+ recordGlyph(rec);
+ return false;
+ }
+
+ void reset()
+ {
+ mBestBounds.setEmpty();
+ mDx = mDy = mCx = mCy = INT_MAX;
+ }
+
+ void setLines(const LineCheck* lineCheck) { mLineCheck = lineCheck; }
+ void setRecordGlyph() { mRecordGlyph = true; }
+
+protected:
+ const LineCheck* mLineCheck;
+ int mBestBase;
+ SkIRect mBestBounds;
+ int mCx;
+ int mCy;
+ int mDx;
+ int mDy;
+ int mFocusX;
+ int mFocusY;
+ bool mBestInColumn;
+ bool mRecordGlyph;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class SelectText::EdgeCheck : public SelectText::FirstCheck {
+public:
+ EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left)
+ : INHERITED(x, y, area)
+ , mLast(area)
+ , mLeft(left)
+ {
+ mLast.set(last); // CommonCheck::set()
+ setGlyph(last);
+ }
+
+ bool adjacent()
+ {
+ return !mLast.isSpace(mLastGlyph);
+ }
+
+ const SkIRect& bestBounds(int* base)
+ {
+ *base = mBestBase;
+ return mBestBounds;
+ }
+
+ virtual bool onIRectGlyph(const SkIRect& rect,
+ const SkBounder::GlyphRec& rec)
+ {
+ int dx = mLeft ? mFocusX - rect.fRight : rect.fLeft - mFocusX;
+ int dy = ((top() + bottom()) >> 1) - mFocusY;
+ dx = abs(dx);
+ dy = abs(dy);
+ if (mLeft ? mFocusX <= rect.fLeft : mFocusX >= rect.fRight) {
+ if (dx <= 10 && dy <= 10) {
+ DBG_NAV_LOGD("EdgeCheck fLeft=%d fRight=%d mFocusX=%d dx=%d dy=%d",
+ rect.fLeft, rect.fRight, mFocusX, dx, dy);
+ }
+ return false;
+ }
+ if (mDy > dy || (mDy == dy && mDx > dx)) {
+ if (rec.fLSB == mLastGlyph.fLSB && rec.fRSB == mLastGlyph.fRSB) {
+ DBG_NAV_LOGD("dup rec.fLSB.fX=%g rec.fRSB.fX=%g",
+ SkFixedToScalar(rec.fLSB.fX), SkFixedToScalar(rec.fRSB.fX));
+ return false;
+ }
+ recordGlyph(rec);
+ mDx = dx;
+ mDy = dy;
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ if (dx <= 10 && dy <= 10) {
+ DBG_NAV_LOGD("EdgeCheck mBestBounds={%d,%d,r=%d,b=%d} dx/y=(%d, %d)",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom, dx, dy);
+ }
+ }
+ return false;
+ }
+
+ void shiftStart(SkIRect bounds)
+ {
+ DBG_NAV_LOGD("EdgeCheck mFocusX=%d mLeft=%s bounds.fLeft=%d bounds.fRight=%d",
+ mFocusX, mLeft ? "true" : "false", bounds.fLeft, bounds.fRight);
+ reset();
+ mFocusX = mLeft ? bounds.fLeft : bounds.fRight;
+ mLast.set(*this); // CommonCheck::set()
+ }
+
+protected:
+ CommonCheck mLast;
+ bool mLeft;
+private:
+ typedef SelectText::FirstCheck INHERITED;
+};
+
+class FindFirst : public CommonCheck {
+public:
+ FindFirst(const SkIRect& area)
+ : INHERITED(area)
+ {
+ mBestBounds.set(area.width(), area.height(), area.width(), area.height());
+ }
+
+ const SkIRect& bestBounds(int* base)
+ {
+ *base = mBestBase;
+ return mBestBounds;
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ if (mBestBounds.isEmpty()) {
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ }
+ return false;
+ }
+
+protected:
+ int mBestBase;
+ SkIRect mBestBounds;
+private:
+ typedef CommonCheck INHERITED;
+};
+
+class FindLast : public FindFirst {
+public:
+ FindLast(const SkIRect& area)
+ : INHERITED(area)
+ {
+ mBestBounds.setEmpty();
+ }
+
+ virtual bool onIRect(const SkIRect& rect)
+ {
+ mBestBase = base();
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+ return false;
+ }
+
+private:
+ typedef FindFirst INHERITED;
+};
+
+static bool baseLinesAgree(const SkIRect& rectA, int baseA,
+ const SkIRect& rectB, int baseB)
+{
+ return (rectA.fTop < baseB && rectA.fBottom >= baseB)
+ || (rectB.fTop < baseA && rectB.fBottom >= baseA);
+}
+
+class BuilderCheck : public CommonCheck {
+protected:
+ enum IntersectionType {
+ NO_INTERSECTION, // debugging printf expects this to equal zero
+ LAST_INTERSECTION, // debugging printf expects this to equal one
+ WAIT_FOR_INTERSECTION
+ };
+
+ BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area)
+ : INHERITED(area)
+ , mCapture(false)
+ , mEnd(end)
+ , mEndBase(endBase)
+ , mStart(start)
+ , mStartBase(startBase)
+ {
+ mEnd.offset(-area.fLeft, -area.fTop);
+ mEndBase -= area.fTop;
+ mEndExtra.setEmpty();
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mSelectRect.setEmpty();
+ mStart.offset(-area.fLeft, -area.fTop);
+ mStartBase -= area.fTop;
+ mStartExtra.setEmpty();
+ DBG_NAV_LOGD(" mStart=(%d,%d,r=%d,b=%d) mStartBase=%d"
+ " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
+ mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase,
+ mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
+ }
+
+ int checkFlipRect(const SkIRect& full, int fullBase) {
+ mCollectFull = false;
+ // is the text to collect between the selection top and bottom?
+ if (fullBase < mStart.fTop || fullBase > mEnd.fBottom) {
+ if (VERBOSE_LOGGING && !mLast.isEmpty()) DBG_NAV_LOGD("%s 1"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase);
+ return mLastIntersects;
+ }
+ // is the text to the left of the selection start?
+ if (baseLinesAgree(mStart, mStartBase, full, fullBase)
+ && full.fLeft < mStart.fLeft) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 2"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mStart=(%d,%d,r=%d,b=%d) mStartBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mStart.fLeft, mStart.fTop, mStart.fRight, mStart.fBottom, mStartBase);
+ mStartExtra.join(full);
+ return mLastIntersects;
+ }
+ // is the text to the right of the selection end?
+ if (baseLinesAgree(mEnd, mEndBase, full, fullBase)
+ && full.fRight > mEnd.fRight) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 3"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mEnd=(%d,%d,r=%d,b=%d) mEndBase=%d",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mEnd.fLeft, mEnd.fTop, mEnd.fRight, mEnd.fBottom, mEndBase);
+ mEndExtra.join(full);
+ return mLastIntersects;
+ }
+ int spaceGap = SkFixedRound(minSpaceWidth(mPaint) * 3);
+ // should text to the left of the start be added to the selection bounds?
+ if (!mStartExtra.isEmpty()) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mStartExtra=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom);
+ if (mStartExtra.fRight + spaceGap >= mStart.fLeft)
+ mSelectRect.join(mStartExtra);
+ mStartExtra.setEmpty();
+ }
+ // should text to the right of the end be added to the selection bounds?
+ if (!mEndExtra.isEmpty()) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mEndExtra=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
+ if (mEndExtra.fLeft - spaceGap <= mEnd.fRight)
+ mSelectRect.join(mEndExtra);
+ mEndExtra.setEmpty();
+ }
+ bool sameBaseLine = baseLinesAgree(mLast, mLastBase, full, fullBase);
+ bool adjacent = (full.fLeft - mLast.fRight) < spaceGap;
+ // is this the first, or are there more characters on the same line?
+ if (mLast.isEmpty() || (sameBaseLine && adjacent)) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("WAIT_FOR_INTERSECTION"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " mSelectRect=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
+ mLast.join(full);
+ mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
+ return WAIT_FOR_INTERSECTION;
+ }
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("%s 4"
+ " mLast=(%d,%d,r=%d,b=%d) mLastBase=%d"
+ " full=(%d,%d,r=%d,b=%d) fullBase=%d"
+ " mSelectRect=(%d,%d,r=%d,b=%d)"
+ " mStartExtra=(%d,%d,r=%d,b=%d)"
+ " mEndExtra=(%d,%d,r=%d,b=%d)",
+ mLastIntersects ? "LAST_INTERSECTION" : "NO_INTERSECTION",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom, mLastBase,
+ full.fLeft, full.fTop, full.fRight, full.fBottom, fullBase,
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom,
+ mStartExtra.fLeft, mStartExtra.fTop, mStartExtra.fRight, mStartExtra.fBottom,
+ mEndExtra.fLeft, mEndExtra.fTop, mEndExtra.fRight, mEndExtra.fBottom);
+ // after the caller determines what to do with the last collection,
+ // start the collection over with full and fullBase.
+ mCollectFull = true;
+ return mLastIntersects;
+ }
+
+ bool resetLast(const SkIRect& full, int fullBase)
+ {
+ if (mCollectFull) {
+ mLast = full;
+ mLastBase = fullBase;
+ mLastIntersects = SkIRect::Intersects(mLast, mSelectRect);
+ } else {
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mLastIntersects = false;
+ }
+ return mCollectFull;
+ }
+
+ void setFlippedState()
+ {
+ mSelectRect = mStart;
+ mSelectRect.join(mEnd);
+ DBG_NAV_LOGD("mSelectRect=(%d,%d,r=%d,b=%d)",
+ mSelectRect.fLeft, mSelectRect.fTop, mSelectRect.fRight, mSelectRect.fBottom);
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mLastIntersects = NO_INTERSECTION;
+ }
+
+ bool mCapture;
+ bool mCollectFull;
+ SkIRect mEnd;
+ int mEndBase;
+ SkIRect mEndExtra;
+ bool mFlipped;
+ SkIRect mLast;
+ int mLastBase;
+ int mLastIntersects;
+ SkIRect mSelectRect;
+ SkIRect mStart;
+ SkIRect mStartExtra;
+ int mStartBase;
+private:
+ typedef CommonCheck INHERITED;
+
+};
+
+class MultilineBuilder : public BuilderCheck {
+public:
+ MultilineBuilder(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, SkRegion* region)
+ : INHERITED(start, startBase, end, endBase, area)
+ , mSelectRegion(region)
+ {
+ mFlipped = false;
+ }
+
+ void addLastToRegion() {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD(" mLast=(%d,%d,r=%d,b=%d)",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
+ mSelectRegion->op(mLast, SkRegion::kUnion_Op);
+ }
+
+ void finish() {
+ if (!mFlipped || !mLastIntersects)
+ return;
+ addLastToRegion();
+ }
+
+ // return true if capture end was not found after capture begin
+ bool flipped() {
+ DBG_NAV_LOGD("flipped=%s", mCapture ? "true" : "false");
+ if (!mCapture)
+ return false;
+ mFlipped = true;
+ setFlippedState();
+ mSelectRegion->setEmpty();
+ return true;
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ SkIRect full;
+ full.set(rect.fLeft, top(), rect.fRight, bottom());
+ int fullBase = base();
+ if (mFlipped) {
+ int intersectType = checkFlipRect(full, fullBase);
+ if (intersectType == LAST_INTERSECTION)
+ addLastToRegion();
+ if (intersectType != WAIT_FOR_INTERSECTION)
+ resetLast(full, fullBase);
+ return false;
+ }
+ if (full == mStart) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mStart full=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom);
+ mCapture = true;
+ }
+ if (mCapture) {
+ bool sameLines = baseLinesAgree(mLast, mLastBase, full, fullBase);
+ if (sameLines)
+ mLast.join(full);
+ if (!sameLines || full == mEnd) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("finish mLast=(%d,%d,r=%d,b=%d)",
+ mLast.fLeft, mLast.fTop, mLast.fRight, mLast.fBottom);
+ addLastToRegion();
+ mLast = full;
+ mLastBase = fullBase;
+ }
+ }
+ if (full == mEnd) {
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("full == mEnd full=(%d,%d,r=%d,b=%d)",
+ full.fLeft, full.fTop, full.fRight, full.fBottom);
+ mCapture = false;
+ if (full == mStart)
+ addLastToRegion();
+ }
+ return false;
+ }
+
+protected:
+ SkRegion* mSelectRegion;
+private:
+ typedef BuilderCheck INHERITED;
+};
+
+static inline bool compareBounds(const SkIRect* first, const SkIRect* second)
+{
+ return first->fTop < second->fTop;
+}
+
+class TextExtractor : public BuilderCheck {
+public:
+ TextExtractor(const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, const SkIRect& area, bool flipped)
+ : INHERITED(start, startBase, end, endBase, area)
+ , mSelectStartIndex(-1)
+ , mSkipFirstSpace(true) // don't start with a space
+ {
+ mFlipped = flipped;
+ if (flipped)
+ setFlippedState();
+ }
+
+ void addCharacter(const SkBounder::GlyphRec& rec)
+ {
+ if (mSelectStartIndex < 0)
+ mSelectStartIndex = mSelectText.count();
+ if (!mSkipFirstSpace) {
+ if (addNewLine(rec)) {
+ DBG_NAV_LOG("write new line");
+ *mSelectText.append() = '\n';
+ *mSelectText.append() = '\n';
+ } else if (addSpace(rec)) {
+ DBG_NAV_LOG("write space");
+ *mSelectText.append() = ' ';
+ }
+ } else
+ mSkipFirstSpace = false;
+ recordGlyph(rec);
+ finishGlyph();
+ if (VERBOSE_LOGGING) DBG_NAV_LOGD("glyphID=%d uni=%d '%c'", rec.fGlyphID,
+ mLastUni, mLastUni && mLastUni < 0x7f ? mLastUni : '?');
+ if (mLastUni) {
+ uint16_t chars[2];
+ size_t count = SkUTF16_FromUnichar(mLastUni, chars);
+ *mSelectText.append() = chars[0];
+ if (count == 2)
+ *mSelectText.append() = chars[1];
+ }
+ }
+
+ void addLast()
+ {
+ *mSelectBounds.append() = mLast;
+ *mSelectStart.append() = mSelectStartIndex;
+ *mSelectEnd.append() = mSelectText.count();
+ }
+
+ /* Text characters are collected before it's been determined that the
+ characters are part of the selection. The bounds describe valid parts
+ of the selection, but the bounds are out of order.
+
+ This sorts the characters by sorting the bounds, then copying the
+ characters that were captured.
+ */
+ void finish()
+ {
+ if (mLastIntersects)
+ addLast();
+ Vector<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,
+ const SkBounder::GlyphRec& rec)
+ {
+ SkIRect full;
+ full.set(rect.fLeft, top(), rect.fRight, bottom());
+ int fullBase = base();
+ if (mFlipped) {
+ int intersectType = checkFlipRect(full, fullBase);
+ if (WAIT_FOR_INTERSECTION == intersectType)
+ addCharacter(rec); // may not be copied
+ else {
+ if (LAST_INTERSECTION == intersectType)
+ addLast();
+ else
+ mSkipFirstSpace = true;
+ mSelectStartIndex = -1;
+ if (resetLast(full, fullBase))
+ addCharacter(rec); // may not be copied
+ }
+ return false;
+ }
+ if (full == mStart)
+ mCapture = true;
+ if (mCapture)
+ addCharacter(rec);
+ else
+ mSkipFirstSpace = true;
+ if (full == mEnd)
+ mCapture = false;
+ return false;
+ }
+
+ WTF::String text() {
+ if (mFlipped)
+ finish();
+ // the text has been copied in visual order. Reverse as needed if
+ // result contains right-to-left characters.
+ const uint16_t* start = mSelectText.begin();
+ const uint16_t* end = mSelectText.end();
+ while (start < end) {
+ SkUnichar ch = SkUTF16_NextUnichar(&start);
+ WTF::Unicode::Direction charDirection = WTF::Unicode::direction(ch);
+ if (WTF::Unicode::RightToLeftArabic == charDirection
+ || WTF::Unicode::RightToLeft == charDirection) {
+ WebCore::ReverseBidi(mSelectText.begin(), mSelectText.count());
+ break;
+ }
+ }
+ return WTF::String(mSelectText.begin(), mSelectText.count());
+ }
+
+protected:
+ SkIRect mEmpty;
+ SkTDArray<SkIRect> mSelectBounds;
+ SkTDArray<int> mSelectEnd;
+ SkTDArray<int> mSelectStart;
+ int mSelectStartIndex;
+ SkTDArray<uint16_t> mSelectText;
+ bool mSkipFirstSpace;
+private:
+ typedef BuilderCheck INHERITED;
+};
+
+class TextCanvas : public ParseCanvas {
+public:
+
+ TextCanvas(CommonCheck* bounder)
+ : mBounder(*bounder) {
+ setBounder(bounder);
+ SkBitmap bitmap;
+ const SkIRect& area = bounder->getArea();
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
+ area.height());
+ setBitmapDevice(bitmap);
+ translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
+#ifdef DEBUG_NAV_UI
+ const SkIRect& clip = getTotalClip().getBounds();
+ const SkMatrix& matrix = getTotalMatrix();
+ DBG_NAV_LOGD("bitmap=(%d,%d) clip=(%d,%d,%d,%d) matrix=(%g,%g)",
+ bitmap.width(), bitmap.height(), clip.fLeft, clip.fTop,
+ clip.fRight, clip.fBottom, matrix.getTranslateX(), matrix.getTranslateY());
+#endif
+ }
+
+ virtual void drawPaint(const SkPaint& paint) {
+ }
+
+ virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ }
+
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
+ }
+
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) {
+ }
+
+ virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ }
+
+ virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint = NULL) {
+ }
+
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) {
+ mBounder.setUp(paint, getTotalMatrix(), y, text);
+ INHERITED::drawText(text, byteLength, x, y, paint);
+ }
+
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ mBounder.setUp(paint, getTotalMatrix(), constY, text);
+ INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+ }
+
+ virtual void drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ }
+
+ CommonCheck& mBounder;
+private:
+ typedef ParseCanvas INHERITED;
+};
+
+static bool buildSelection(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& selStart, int startBase,
+ const SkIRect& selEnd, int endBase, SkRegion* region)
+{
+ DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)"
+ " selEnd=(%d, %d, %d, %d)",
+ area.fLeft, area.fTop, area.fRight, area.fBottom,
+ selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom,
+ selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom);
+ MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region);
+ TextCanvas checker(&builder);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ bool flipped = builder.flipped();
+ if (flipped) {
+ TextCanvas checker(&builder);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ }
+ builder.finish();
+ region->translate(area.fLeft, area.fTop);
+ return flipped;
+}
+
+static SkIRect findFirst(const SkPicture& picture, int* base)
+{
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ FindFirst finder(area);
+ TextCanvas checker(&finder);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static SkIRect findLast(const SkPicture& picture, int* base)
+{
+ SkIRect area;
+ area.set(0, 0, picture.width(), picture.height());
+ FindLast finder(area);
+ TextCanvas checker(&finder);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return finder.bestBounds(base);
+}
+
+static WTF::String text(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& start, int startBase, const SkIRect& end,
+ int endBase, bool flipped)
+{
+ TextExtractor extractor(start, startBase, end, endBase, area, flipped);
+ TextCanvas checker(&extractor);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ return extractor.text();
+}
+
+#define CONTROL_NOTCH 16
+// TODO: Now that java is the one actually drawing these, get the real values
+// from the drawable itself
+#define CONTROL_HEIGHT 47
+#define CONTROL_WIDTH 26
+#define CONTROL_SLOP 5
+#define STROKE_WIDTH 1.0f
+#define STROKE_OUTSET 3.5f
+#define STROKE_I_OUTSET 4 // (int) ceil(STROKE_OUTSET)
+#define STROKE_COLOR 0x66000000
+#define OUTER_COLOR 0x33000000
+#define INNER_COLOR 0xe6aae300
+
+SelectText::SelectText()
+ : m_controlWidth(CONTROL_WIDTH)
+ , m_controlHeight(CONTROL_HEIGHT)
+ , m_controlSlop(CONTROL_SLOP)
+{
+ m_picture = 0;
+ reset();
+ SkPaint paint;
+ SkRect oval;
+
+ SkPath startOuterPath;
+ oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET,
+ -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET);
+ startOuterPath.arcTo(oval, 180, 45, true);
+ oval.set(-STROKE_OUTSET, -STROKE_OUTSET, STROKE_OUTSET, STROKE_OUTSET);
+ startOuterPath.arcTo(oval, 180 + 45, 135, false);
+ oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
+ STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
+ startOuterPath.arcTo(oval, 0, 90, false);
+ oval.set(-CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
+ -CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
+ startOuterPath.arcTo(oval, 90, 90, false);
+ startOuterPath.close();
+ SkPath startInnerPath;
+ startInnerPath.moveTo(-CONTROL_WIDTH, CONTROL_NOTCH);
+ startInnerPath.lineTo(-CONTROL_WIDTH, CONTROL_HEIGHT);
+ startInnerPath.lineTo(0, CONTROL_HEIGHT);
+ startInnerPath.lineTo(0, 0);
+ startInnerPath.close();
+ startOuterPath.addPath(startInnerPath, 0, 0);
+
+ SkCanvas* canvas = m_startControl.beginRecording(
+ CONTROL_WIDTH + STROKE_OUTSET * 2,
+ CONTROL_HEIGHT + STROKE_OUTSET * 2);
+ paint.setAntiAlias(true);
+ paint.setColor(INNER_COLOR);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawPath(startInnerPath, paint);
+ paint.setColor(OUTER_COLOR);
+ canvas->drawPath(startOuterPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(STROKE_COLOR);
+ paint.setStrokeWidth(STROKE_WIDTH);
+ canvas->drawPath(startInnerPath, paint);
+ m_startControl.endRecording();
+
+ SkPath endOuterPath;
+ oval.set(-STROKE_OUTSET, -STROKE_OUTSET, STROKE_OUTSET, STROKE_OUTSET);
+ endOuterPath.arcTo(oval, 180, 135, true);
+ oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_NOTCH - STROKE_OUTSET,
+ CONTROL_WIDTH + STROKE_OUTSET, CONTROL_NOTCH + STROKE_OUTSET);
+ endOuterPath.arcTo(oval, 360 - 45, 45, false);
+ oval.set(CONTROL_WIDTH - STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
+ CONTROL_WIDTH + STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
+ endOuterPath.arcTo(oval, 0, 90, false);
+ oval.set(-STROKE_OUTSET, CONTROL_HEIGHT - STROKE_OUTSET,
+ STROKE_OUTSET, CONTROL_HEIGHT + STROKE_OUTSET);
+ endOuterPath.arcTo(oval, 90, 90, false);
+ startOuterPath.close();
+ SkPath endInnerPath;
+ endInnerPath.moveTo(0, 0);
+ endInnerPath.lineTo(0, CONTROL_HEIGHT);
+ endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_HEIGHT);
+ endInnerPath.lineTo(CONTROL_WIDTH, CONTROL_NOTCH);
+ endInnerPath.close();
+ endOuterPath.addPath(endInnerPath, 0, 0);
+
+ canvas = m_endControl.beginRecording(CONTROL_WIDTH + STROKE_OUTSET * 2,
+ CONTROL_HEIGHT + STROKE_OUTSET * 2);
+ paint.setColor(INNER_COLOR);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawPath(endInnerPath, paint);
+ paint.setColor(OUTER_COLOR);
+ canvas->drawPath(endOuterPath, paint);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(STROKE_COLOR);
+ paint.setStrokeWidth(STROKE_WIDTH);
+ canvas->drawPath(endInnerPath, paint);
+ m_endControl.endRecording();
+}
+
+SelectText::~SelectText()
+{
+ SkSafeUnref(m_picture);
+}
+
+void SelectText::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval)
+{
+ if (m_layerId != layer->uniqueId())
+ return;
+ // reset m_picture to match m_layerId
+ SkSafeUnref(m_picture);
+ m_picture = layer->picture();
+ SkSafeRef(m_picture);
+ DBG_NAV_LOGD("m_extendSelection=%d m_drawPointer=%d layer [%d]",
+ m_extendSelection, m_drawPointer, layer->uniqueId());
+ if (m_extendSelection)
+ drawSelectionRegion(canvas, inval);
+ if (m_drawPointer)
+ drawSelectionPointer(canvas, inval);
+}
+
+static void addInval(IntRect* inval, const SkCanvas* canvas,
+ const SkRect& bounds) {
+ const SkMatrix& matrix = canvas->getTotalMatrix();
+ SkRect transformed;
+ matrix.mapRect(&transformed, bounds);
+ SkIRect iTrans;
+ transformed.round(&iTrans);
+ inval->unite(iTrans);
+}
+
+void SelectText::drawSelectionPointer(SkCanvas* canvas, IntRect* inval)
+{
+ SkPath path;
+ if (m_extendSelection)
+ getSelectionCaret(&path);
+ else
+ getSelectionArrow(&path);
+ SkPixelXorXfermode xorMode(SK_ColorWHITE);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorBLACK);
+ if (m_extendSelection)
+ paint.setXfermode(&xorMode);
+ else
+ paint.setStrokeWidth(SK_Scalar1 * 2);
+ int sc = canvas->save();
+ canvas->scale(m_inverseScale, m_inverseScale);
+ canvas->translate(m_selectX, m_selectY);
+ canvas->drawPath(path, paint);
+ if (!m_extendSelection) {
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorWHITE);
+ canvas->drawPath(path, paint);
+ }
+ SkRect bounds = path.getBounds();
+ bounds.inset(-SK_Scalar1 * 2, -SK_Scalar1 * 2); // stroke width
+ addInval(inval, canvas, bounds);
+ canvas->restoreToCount(sc);
+}
+
+static void addStart(SkRegion* diff, const SkIRect& rect)
+{
+ SkIRect bounds;
+ bounds.set(rect.fLeft - CONTROL_WIDTH - STROKE_I_OUTSET,
+ rect.fBottom - STROKE_I_OUTSET, rect.fLeft + STROKE_I_OUTSET,
+ rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET);
+ diff->op(bounds, SkRegion::kUnion_Op);
+}
+
+static void addEnd(SkRegion* diff, const SkIRect& rect)
+{
+ SkIRect bounds;
+ bounds.set(rect.fRight - STROKE_I_OUTSET, rect.fBottom - STROKE_I_OUTSET,
+ rect.fRight + CONTROL_WIDTH + STROKE_I_OUTSET,
+ rect.fBottom + CONTROL_HEIGHT + STROKE_I_OUTSET);
+ diff->op(bounds, SkRegion::kUnion_Op);
+}
+
+void SelectText::getSelectionRegion(const IntRect& vis, SkRegion *region,
+ LayerAndroid* root)
+{
+ SkIRect ivisBounds = vis;
+ ivisBounds.join(m_selStart);
+ ivisBounds.join(m_selEnd);
+ region->setEmpty();
+ buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
+ m_selEnd, m_endBase, region);
+ if (root && m_layerId) {
+ Layer* layer = root->findById(m_layerId);
+ while (layer) {
+ const SkPoint& pos = layer->getPosition();
+ region->translate(pos.fX, pos.fY);
+ layer = layer->getParent();
+ }
+ }
+}
+
+void SelectText::drawSelectionRegion(SkCanvas* canvas, IntRect* inval)
+{
+ if (!m_picture)
+ return;
+ SkIRect ivisBounds = m_visibleRect;
+ ivisBounds.join(m_selStart);
+ ivisBounds.join(m_selEnd);
+ DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)"
+ " ivisBounds=(%d,%d,r=%d,b=%d)",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom,
+ ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom);
+ if (m_lastSelRegion != m_selRegion)
+ m_lastSelRegion.set(m_selRegion);
+ SkRegion diff(m_lastSelRegion);
+ m_selRegion.setEmpty();
+ m_flipped = buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
+ m_selEnd, m_endBase, &m_selRegion);
+ SkPath path;
+ m_selRegion.getBoundaryPath(&path);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SkColorSetARGB(0x80, 0x83, 0xCC, 0x39));
+ canvas->drawPath(path, paint);
+ // experiment to draw touchable controls that resize the selection
+ float scale = m_controlHeight / (float)CONTROL_HEIGHT;
+ canvas->save();
+ canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
+ canvas->scale(scale, scale);
+ canvas->drawPicture(m_startControl);
+ canvas->restore();
+ canvas->save();
+ canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
+ canvas->scale(scale, scale);
+ canvas->drawPicture(m_endControl);
+ canvas->restore();
+
+#if DEBUG_TOUCH_HANDLES
+ SkRect touchHandleRect;
+ paint.setColor(SkColorSetARGB(0x60, 0xFF, 0x00, 0x00));
+ touchHandleRect.set(0, m_selStart.fBottom, m_selStart.fLeft, 0);
+ touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight;
+ touchHandleRect.fLeft = touchHandleRect.fRight - m_controlWidth;
+ canvas->drawRect(touchHandleRect, paint);
+ touchHandleRect.inset(-m_controlSlop, -m_controlSlop);
+ canvas->drawRect(touchHandleRect, paint);
+ touchHandleRect.set(m_selEnd.fRight, m_selEnd.fBottom, 0, 0);
+ touchHandleRect.fBottom = touchHandleRect.fTop + m_controlHeight;
+ touchHandleRect.fRight = touchHandleRect.fLeft + m_controlWidth;
+ canvas->drawRect(touchHandleRect, paint);
+ touchHandleRect.inset(-m_controlSlop, -m_controlSlop);
+ canvas->drawRect(touchHandleRect, paint);
+#endif
+
+ SkIRect a = diff.getBounds();
+ SkIRect b = m_selRegion.getBounds();
+ diff.op(m_selRegion, SkRegion::kXOR_Op);
+ SkIRect c = diff.getBounds();
+ DBG_NAV_LOGD("old=(%d,%d,r=%d,b=%d) new=(%d,%d,r=%d,b=%d) diff=(%d,%d,r=%d,b=%d)",
+ a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom,
+ c.fLeft, c.fTop, c.fRight, c.fBottom);
+ DBG_NAV_LOGD("lastStart=(%d,%d,r=%d,b=%d) m_lastEnd=(%d,%d,r=%d,b=%d)",
+ m_lastStart.fLeft, m_lastStart.fTop, m_lastStart.fRight, m_lastStart.fBottom,
+ m_lastEnd.fLeft, m_lastEnd.fTop, m_lastEnd.fRight, m_lastEnd.fBottom);
+ if (!m_lastDrawnStart.isEmpty())
+ addStart(&diff, m_lastDrawnStart);
+ if (m_lastStart != m_selStart) {
+ m_lastDrawnStart = m_lastStart;
+ m_lastStart = m_selStart;
+ }
+ addStart(&diff, m_selStart);
+ if (!m_lastDrawnEnd.isEmpty())
+ addEnd(&diff, m_lastDrawnEnd);
+ if (m_lastEnd != m_selEnd) {
+ m_lastDrawnEnd = m_lastEnd;
+ m_lastEnd = m_selEnd;
+ }
+ addEnd(&diff, m_selEnd);
+ SkIRect iBounds = diff.getBounds();
+ DBG_NAV_LOGD("diff=(%d,%d,r=%d,b=%d)",
+ iBounds.fLeft, iBounds.fTop, iBounds.fRight, iBounds.fBottom);
+ SkRect bounds;
+ bounds.set(iBounds);
+ addInval(inval, canvas, bounds);
+}
+
+void SelectText::extendSelection(const IntRect& vis, int x, int y)
+{
+ if (!m_picture)
+ return;
+ setVisibleRect(vis);
+ SkIRect clipRect = m_visibleRect;
+ int base;
+ DBG_NAV_LOGD("extend x/y=%d,%d m_startOffset=%d,%d", x, y,
+ m_startOffset.fX, m_startOffset.fY);
+ x -= m_startOffset.fX;
+ y -= m_startOffset.fY;
+ if (m_startSelection) {
+ if (!clipRect.contains(x, y)
+ || !clipRect.contains(m_original.fX, m_original.fY)) {
+ clipRect.set(m_original.fX, m_original.fY, x, y);
+ clipRect.sort();
+ clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
+ }
+ FirstCheck center(m_original.fX, m_original.fY, clipRect);
+ m_selStart = m_selEnd = findClosest(center, *m_picture, &base);
+ if (m_selStart.isEmpty())
+ return;
+ DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d) m_original=%d,%d"
+ " m_selStart=(%d,%d,%d,%d)", clipRect.fLeft, clipRect.fTop,
+ clipRect.fRight, clipRect.fBottom, m_original.fX, m_original.fY,
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom);
+ m_startBase = m_endBase = base;
+ m_startSelection = false;
+ m_extendSelection = true;
+ m_original.fX = m_original.fY = 0;
+ }
+ DBG_NAV_LOGD("extend x/y=%d,%d m_original=%d,%d", x, y,
+ m_original.fX, m_original.fY);
+ x -= m_original.fX;
+ y -= m_original.fY;
+ if (!clipRect.contains(x, y) || !clipRect.contains(m_selStart)) {
+ clipRect.set(m_selStart.fLeft, m_selStart.fTop, x, y);
+ clipRect.sort();
+ clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height());
+ }
+ DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d) x/y=%d,%d wordSel=%s outsideWord=%s",
+ clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, x, y,
+ m_wordSelection ? "true" : "false", m_outsideWord ? "true" : "false");
+ FirstCheck extension(x, y, clipRect);
+ SkIRect found = findClosest(extension, *m_picture, &base);
+ if (m_wordSelection) {
+ SkIRect wordBounds = m_wordBounds;
+ if (!m_outsideWord)
+ wordBounds.inset(-TOUCH_SLOP, -TOUCH_SLOP);
+ DBG_NAV_LOGD("x=%d y=%d wordBounds=(%d,%d,r=%d,b=%d)"
+ " found=(%d,%d,r=%d,b=%d)", x, y, wordBounds.fLeft, wordBounds.fTop,
+ wordBounds.fRight, wordBounds.fBottom, found.fLeft, found.fTop,
+ found.fRight, found.fBottom);
+ if (wordBounds.contains(x, y)) {
+ DBG_NAV_LOG("wordBounds.contains=true");
+ m_outsideWord = false;
+ return;
+ }
+ m_outsideWord = true;
+ if (found.fBottom <= wordBounds.fTop)
+ m_hitTopLeft = true;
+ else if (found.fTop >= wordBounds.fBottom)
+ m_hitTopLeft = false;
+ else
+ m_hitTopLeft = (found.fLeft + found.fRight)
+ < (wordBounds.fLeft + wordBounds.fRight);
+ }
+ DBG_NAV_LOGD("x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)"
+ " m_extendSelection=%s",
+ x, y, m_startSelection ? "true" : "false",
+ m_hitTopLeft ? "m_selStart" : "m_selEnd",
+ found.fLeft, found.fTop, found.fRight, found.fBottom,
+ m_extendSelection ? "true" : "false");
+ if (m_hitTopLeft) {
+ m_startBase = base;
+ m_selStart = found;
+ } else {
+ m_endBase = base;
+ m_selEnd = found;
+ }
+ swapAsNeeded();
+}
+
+SkIRect SelectText::findClosest(FirstCheck& check, const SkPicture& picture,
+ int* base)
+{
+ LineCheck lineCheck(check.focusX(), check.focusY(), check.getArea());
+ TextCanvas lineChecker(&lineCheck);
+ lineChecker.drawPicture(const_cast<SkPicture&>(picture));
+ lineCheck.finish(m_selRegion);
+ check.setLines(&lineCheck);
+ TextCanvas checker(&check);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ check.finishGlyph();
+ return check.adjustedBounds(base);
+}
+
+SkIRect SelectText::findEdge(const SkPicture& picture, const SkIRect& area,
+ int x, int y, bool left, int* base)
+{
+ SkIRect result;
+ result.setEmpty();
+ FirstCheck center(x, y, area);
+ center.setRecordGlyph();
+ int closestBase;
+ SkIRect closest = findClosest(center, picture, &closestBase);
+ SkIRect sloppy = closest;
+ sloppy.inset(-TOUCH_SLOP, -TOUCH_SLOP);
+ if (!sloppy.contains(x, y)) {
+ DBG_NAV_LOGD("sloppy=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d",
+ sloppy.fLeft, sloppy.fTop, sloppy.fRight, sloppy.fBottom,
+ area.fLeft, area.fTop, area.fRight, area.fBottom, x, y);
+ return result;
+ }
+ EdgeCheck edge(x, y, area, center, left);
+ do { // detect left or right until there's a gap
+ DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d",
+ &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom);
+ TextCanvas checker(&edge);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ edge.finishGlyph();
+ if (!edge.adjacent()) {
+ if (result.isEmpty()) {
+ *base = closestBase;
+ DBG_NAV_LOGD("closest=%d,%d,%d,%d", closest.fLeft,
+ closest.fTop, closest.fRight, closest.fBottom);
+ return closest;
+ }
+ DBG_NAV_LOG("adjacent break");
+ break;
+ }
+ int nextBase;
+ const SkIRect& next = edge.bestBounds(&nextBase);
+ if (next.isEmpty()) {
+ DBG_NAV_LOG("empty");
+ break;
+ }
+ if (result == next) {
+ DBG_NAV_LOG("result == next");
+ break;
+ }
+ *base = nextBase;
+ result = next;
+ edge.shiftStart(result);
+ } while (true);
+ if (!result.isEmpty()) {
+ *base += area.fTop;
+ result.offset(area.fLeft, area.fTop);
+ }
+ return result;
+}
+
+SkIRect SelectText::findLeft(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, true, base);
+}
+
+SkIRect SelectText::findRight(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base)
+{
+ return findEdge(picture, area, x, y, false, base);
+}
+
+const String SelectText::getSelection()
+{
+ if (!m_picture)
+ return String();
+ SkIRect clipRect;
+ clipRect.set(0, 0, m_picture->width(), m_picture->height());
+ String result = text(*m_picture, clipRect, m_selStart, m_startBase,
+ m_selEnd, m_endBase, m_flipped);
+ DBG_NAV_LOGD("clip=(%d,%d,%d,%d)"
+ " m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
+ clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom,
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+ DBG_NAV_LOGD("text=%s", result.latin1().data()); // uses CString
+ return result;
+}
+
+void SelectText::getSelectionArrow(SkPath* path)
+{
+ const int arrow[] = {
+ 0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11
+ };
+ for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2)
+ path->lineTo(arrow[index], arrow[index + 1]);
+ path->close();
+}
+
+void SelectText::getSelectionCaret(SkPath* path)
+{
+ SkScalar height = m_selStart.fBottom - m_selStart.fTop;
+ SkScalar dist = height / 4;
+ path->moveTo(0, -height / 2);
+ path->rLineTo(0, height);
+ path->rLineTo(-dist, dist);
+ path->rMoveTo(0, -0.5f);
+ path->rLineTo(dist * 2, 0);
+ path->rMoveTo(0, 0.5f);
+ path->rLineTo(-dist, -dist);
+}
+
+bool SelectText::hitCorner(int cx, int cy, int x, int y) const
+{
+ SkIRect test;
+ test.set(cx, cy, cx + m_controlWidth, cy + m_controlHeight);
+ test.inset(-m_controlSlop, -m_controlSlop);
+ DBG_HANDLE_LOG("checking if %dx%d,%d-%d contains %dx%d",
+ cx, cy, m_controlWidth, m_controlHeight, x, y);
+ return test.contains(x, y);
+}
+
+bool SelectText::hitStartHandle(int x, int y) const
+{
+ int left = m_selStart.fLeft - m_controlWidth;
+ return hitCorner(left, m_selStart.fBottom, x, y);
+}
+
+bool SelectText::hitEndHandle(int x, int y) const
+{
+ int left = m_selEnd.fRight;
+ return hitCorner(left, m_selEnd.fBottom, x, y);
+}
+
+bool SelectText::hitSelection(int x, int y) const
+{
+ x -= m_startOffset.fX;
+ y -= m_startOffset.fY;
+ if (hitStartHandle(x, y))
+ return true;
+ if (hitEndHandle(x, y))
+ return true;
+ return m_selRegion.contains(x, y);
+}
+
+void SelectText::getSelectionHandles(int* handles, LayerAndroid* root)
+{
+ handles[0] = m_selStart.fLeft;
+ handles[1] = m_selStart.fBottom;
+ handles[2] = m_selEnd.fRight;
+ handles[3] = m_selEnd.fBottom;
+ if (root && m_layerId) {
+ Layer* layer = root->findById(m_layerId);
+ while (layer) {
+ const SkPoint& pos = layer->getPosition();
+ handles[0] += pos.fX;
+ handles[2] += pos.fX;
+ handles[1] += pos.fY;
+ handles[3] += pos.fY;
+ layer = layer->getParent();
+ }
+ }
+}
+
+void SelectText::moveSelection(const IntRect& vis, int x, int y)
+{
+ if (!m_picture)
+ return;
+ x -= m_startOffset.fX;
+ y -= m_startOffset.fY;
+ setVisibleRect(vis);
+ SkIRect clipRect = m_visibleRect;
+ clipRect.join(m_selStart);
+ clipRect.join(m_selEnd);
+ FirstCheck center(x, y, clipRect);
+ int base;
+ SkIRect found = findClosest(center, *m_picture, &base);
+ if (m_hitTopLeft || !m_extendSelection) {
+ m_startBase = base;
+ m_selStart = found;
+ }
+ if (!m_hitTopLeft || !m_extendSelection) {
+ m_endBase = base;
+ m_selEnd = found;
+ }
+ swapAsNeeded();
+ DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)"
+ " m_selEnd=(%d, %d, %d, %d)", x, y, m_extendSelection ? "true" : "false",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+}
+
+void SelectText::reset()
+{
+ DBG_NAV_LOG("m_extendSelection=false");
+ m_selStart.setEmpty();
+ m_lastStart.setEmpty();
+ m_lastDrawnStart.setEmpty();
+ m_selEnd.setEmpty();
+ m_lastEnd.setEmpty();
+ m_lastDrawnEnd.setEmpty();
+ m_extendSelection = false;
+ m_startSelection = false;
+ SkSafeUnref(m_picture);
+ m_picture = 0;
+ m_layerId = 0;
+}
+
+IntPoint SelectText::selectableText(const CachedRoot* root)
+{
+ int x = 0;
+ int y = 0;
+ SkPicture* picture = root->pictureAt(&x, &y, &m_layerId);
+ if (!picture) {
+ DBG_NAV_LOG("picture==0");
+ return IntPoint(0, 0);
+ }
+ int width = picture->width();
+ int height = picture->height();
+ IntRect vis(0, 0, width, height);
+ FirstCheck center(width >> 1, height >> 1, vis);
+ int base;
+ const SkIRect& closest = findClosest(center, *picture, &base);
+ return IntPoint((closest.fLeft + closest.fRight) >> 1,
+ (closest.fTop + closest.fBottom) >> 1);
+}
+
+void SelectText::selectAll()
+{
+ if (!m_picture)
+ return;
+ m_selStart = findFirst(*m_picture, &m_startBase);
+ m_selEnd = findLast(*m_picture, &m_endBase);
+ m_extendSelection = true;
+}
+
+int SelectText::selectionX() const
+{
+ return (m_hitTopLeft ? m_selStart.fLeft : m_selEnd.fRight) + m_startOffset.fX;
+}
+
+int SelectText::selectionY() const
+{
+ const SkIRect& rect = m_hitTopLeft ? m_selStart : m_selEnd;
+ return ((rect.fTop + rect.fBottom) >> 1) + m_startOffset.fY;
+}
+
+void SelectText::setVisibleRect(const IntRect& vis)
+{
+ DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%d,%d)",
+ vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX,
+ m_startOffset.fY);
+ m_visibleRect = vis;
+ m_visibleRect.offset(-m_startOffset.fX, -m_startOffset.fY);
+}
+
+bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis,
+ int x, int y)
+{
+ m_wordSelection = false;
+ m_startOffset.set(x, y);
+ DBG_NAV_LOGD("x/y=(%d,%d)", x, y);
+ SkSafeUnref(m_picture);
+ m_picture = root->pictureAt(&x, &y, &m_layerId);
+ DBG_NAV_LOGD("m_picture=%p m_layerId=%d x/y=(%d,%d)", m_picture, m_layerId,
+ x, y);
+ if (!m_picture) {
+ DBG_NAV_LOG("picture==0");
+ return false;
+ }
+ m_picture->ref();
+ m_startOffset.fX -= x;
+ m_startOffset.fY -= y;
+ m_original.fX = x;
+ m_original.fY = y;
+ setVisibleRect(vis);
+ if (m_selStart.isEmpty()) {
+ DBG_NAV_LOGD("empty start picture=(%d,%d) x=%d y=%d",
+ m_picture->width(), m_picture->height(), x, y);
+ m_startSelection = true;
+ return true;
+ }
+ m_hitTopLeft = hitStartHandle(x, y);
+ bool hitBottomRight = hitEndHandle(x, y);
+ DBG_NAV_LOGD("picture=(%d,%d) left=%d top=%d right=%d bottom=%d x=%d y=%d",
+ m_picture->width(), m_picture->height(),left, top, right, bottom, x, y);
+ if (m_hitTopLeft) {
+ DBG_NAV_LOG("hit top left");
+ m_original.fX -= m_selStart.fLeft;
+ m_original.fY -= (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ } else if (hitBottomRight) {
+ DBG_NAV_LOG("hit bottom right");
+ m_original.fX -= m_selEnd.fRight;
+ m_original.fY -= (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
+ }
+ return m_hitTopLeft || hitBottomRight;
+}
+
+void SelectText::updateHandleScale(float handleScale)
+{
+ m_controlHeight = CONTROL_HEIGHT * handleScale;
+ m_controlWidth = CONTROL_WIDTH * handleScale;
+ m_controlSlop = CONTROL_SLOP * handleScale;
+}
+
+/* selects the word at (x, y)
+* a word is normally delimited by spaces
+* a string of digits (even with inside spaces) is a word (for phone numbers)
+* FIXME: digit find isn't implemented yet
+* returns true if a word was selected
+*/
+bool SelectText::wordSelection(const CachedRoot* root, const IntRect& vis,
+ int x, int y)
+{
+ IntRect tapArea = IntRect(x - TOUCH_SLOP, y - TOUCH_SLOP, TOUCH_SLOP * 2,
+ TOUCH_SLOP * 2);
+ if (!startSelection(root, tapArea, x, y))
+ return false;
+ extendSelection(tapArea, x, y);
+ if (m_selStart.isEmpty())
+ return false;
+ setDrawPointer(false);
+ setVisibleRect(vis);
+ SkIRect ivisBounds = m_visibleRect;
+ ivisBounds.join(m_selStart);
+ ivisBounds.join(m_selEnd);
+ DBG_NAV_LOGD("m_selStart=(%d,%d,r=%d,b=%d) m_selEnd=(%d,%d,r=%d,b=%d)"
+ " ivisBounds=(%d,%d,r=%d,b=%d)",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom,
+ ivisBounds.fLeft, ivisBounds.fTop, ivisBounds.fRight, ivisBounds.fBottom);
+ m_selRegion.setEmpty();
+ buildSelection(*m_picture, ivisBounds, m_selStart, m_startBase,
+ m_selEnd, m_endBase, &m_selRegion);
+ x = m_selStart.fLeft;
+ y = (m_selStart.fTop + m_selStart.fBottom) >> 1;
+ SkIRect clipRect = m_visibleRect;
+ clipRect.fLeft -= m_visibleRect.width() >> 1;
+ clipRect.fLeft = std::max(clipRect.fLeft, 0);
+ int base;
+ SkIRect left = findLeft(*m_picture, clipRect, x, y, &base);
+ if (!left.isEmpty()) {
+ m_startBase = base;
+ m_selStart = left;
+ }
+ x = m_selEnd.fRight;
+ y = (m_selEnd.fTop + m_selEnd.fBottom) >> 1;
+ clipRect = m_visibleRect;
+ clipRect.fRight += m_visibleRect.width() >> 1;
+ SkIRect right = findRight(*m_picture, clipRect, x, y, &base);
+ if (!right.isEmpty()) {
+ m_endBase = base;
+ m_selEnd = right;
+ }
+ DBG_NAV_LOGD("m_selStart=(%d, %d, %d, %d) m_selEnd=(%d, %d, %d, %d)",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+ if (!left.isEmpty() || !right.isEmpty()) {
+ m_wordBounds = m_selStart;
+ m_wordBounds.join(m_selEnd);
+ m_extendSelection = m_wordSelection = true;
+ m_outsideWord = false;
+ return true;
+ }
+ 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/Source/WebKit/android/nav/SelectText.h b/Source/WebKit/android/nav/SelectText.h
index aaaf3bb..b454b8e 100644
--- a/Source/WebKit/android/nav/SelectText.h
+++ b/Source/WebKit/android/nav/SelectText.h
@@ -27,33 +27,90 @@
#define SelectText_h
#include "DrawExtra.h"
+#include "IntPoint.h"
#include "IntRect.h"
#include "PlatformString.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
namespace android {
-class SelectText : public RegionLayerDrawExtra {
-public:
- enum HandleId {
- LeftHandle = 0,
- RightHandle = 1,
- };
-
- IntRect& caretRect(HandleId id) { return m_caretRects[id]; }
- void setCaretRect(HandleId id, const IntRect& rect) { m_caretRects[id] = rect; }
- IntRect& textRect(HandleId id) { return m_textRects[id]; }
- void setTextRect(HandleId id, const IntRect& rect) { m_textRects[id] = rect; }
- int caretLayerId(HandleId id) { return m_caretLayerId[id]; }
- void setCaretLayerId(HandleId id, int layerId) { m_caretLayerId[id] = layerId; }
-
- void setText(const String& text) { m_text = text.threadsafeCopy(); }
- String& getText() { return m_text; }
+class CachedRoot;
+class SelectText : public DrawExtra {
+public:
+ SelectText();
+ virtual ~SelectText();
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* );
+ void extendSelection(const IntRect& vis, int x, int y);
+ const String getSelection();
+ bool hitSelection(int x, int y) const;
+ void moveSelection(const IntRect& vis, int x, int y);
+ void reset();
+ IntPoint selectableText(const CachedRoot* );
+ void selectAll();
+ int selectionX() const;
+ int selectionY() const;
+ void setDrawPointer(bool drawPointer) { m_drawPointer = drawPointer; }
+ void setExtendSelection(bool extend) { m_extendSelection = extend; }
+ bool startSelection(const CachedRoot* , const IntRect& vis, int x, int y);
+ bool wordSelection(const CachedRoot* , const IntRect& vis, int x, int y);
+ void getSelectionRegion(const IntRect& vis, SkRegion *region, LayerAndroid* root);
+ void updateHandleScale(float handleScale);
+ void getSelectionHandles(int* handles, LayerAndroid* root);
+public:
+ float m_inverseScale; // inverse scale, x, y used for drawing select path
+ int m_selectX;
+ int m_selectY;
private:
- IntRect m_caretRects[2];
- IntRect m_textRects[2];
- int m_caretLayerId[2];
- String m_text;
+ int m_controlWidth;
+ int m_controlHeight;
+ int m_controlSlop;
+ class FirstCheck;
+ class EdgeCheck;
+ void drawSelectionPointer(SkCanvas* , IntRect* );
+ void drawSelectionRegion(SkCanvas* , IntRect* );
+ SkIRect findClosest(FirstCheck& , const SkPicture& , int* base);
+ SkIRect findEdge(const SkPicture& , const SkIRect& area,
+ int x, int y, bool left, int* base);
+ SkIRect findLeft(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base);
+ SkIRect findRight(const SkPicture& picture, const SkIRect& area,
+ int x, int y, int* base);
+ static void getSelectionArrow(SkPath* );
+ void getSelectionCaret(SkPath* );
+ bool hitCorner(int cx, int cy, int x, int y) const;
+ bool hitStartHandle(int x, int y) const;
+ bool hitEndHandle(int x, int y) const;
+ void setVisibleRect(const IntRect& );
+ void swapAsNeeded();
+ SkIPoint m_original; // computed start of extend selection
+ SkIPoint m_startOffset; // difference from global to layer
+ SkIRect m_selStart;
+ SkIRect m_selEnd;
+ SkIRect m_lastStart;
+ SkIRect m_lastEnd;
+ SkIRect m_lastDrawnStart;
+ SkIRect m_lastDrawnEnd;
+ SkIRect m_wordBounds;
+ int m_startBase;
+ int m_endBase;
+ int m_layerId;
+ SkIRect m_visibleRect; // constrains picture computations to visible area
+ SkRegion m_lastSelRegion;
+ SkRegion m_selRegion; // computed from sel start, end
+ SkPicture m_startControl;
+ SkPicture m_endControl;
+ const SkPicture* m_picture;
+ bool m_drawPointer;
+ bool m_extendSelection; // false when trackball is moving pointer
+ bool m_flipped;
+ bool m_hitTopLeft;
+ bool m_startSelection;
+ bool m_wordSelection;
+ bool m_outsideWord;
};
}
diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp
index a67b5fd..7cb41d9 100644
--- a/Source/WebKit/android/nav/WebView.cpp
+++ b/Source/WebKit/android/nav/WebView.cpp
@@ -30,16 +30,17 @@
#include "AndroidAnimation.h"
#include "AndroidLog.h"
#include "BaseLayerAndroid.h"
-#include "BaseRenderer.h"
+#include "CachedFrame.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
#include "DrawExtra.h"
+#include "FindCanvas.h"
#include "Frame.h"
-#include "GLWebViewState.h"
#include "GraphicsJNI.h"
#include "HTMLInputElement.h"
#include "IntPoint.h"
#include "IntRect.h"
#include "LayerAndroid.h"
-#include "LayerContent.h"
#include "Node.h"
#include "utils/Functor.h"
#include "private/hwui/DrawGlInfo.h"
@@ -52,8 +53,10 @@
#include "SkPicture.h"
#include "SkRect.h"
#include "SkTime.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
#include "TilesManager.h"
-#include "TransferQueue.h"
#include "WebCoreJni.h"
#include "WebRequestContext.h"
#include "WebViewCore.h"
@@ -68,7 +71,7 @@
#include <JNIUtility.h>
#include <JNIHelp.h>
#include <jni.h>
-#include <androidfw/KeycodeLabels.h>
+#include <ui/KeycodeLabels.h>
#include <wtf/text/AtomicString.h>
#include <wtf/text/CString.h>
@@ -92,7 +95,7 @@ static jfieldID gWebViewField;
static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
{
jmethodID m = env->GetMethodID(clazz, name, signature);
- ALOG_ASSERT(m, "Could not find method %s", name);
+ LOG_ASSERT(m, "Could not find method %s", name);
return m;
}
@@ -107,81 +110,105 @@ enum FrameCachePermission {
AllowNewer
};
-#define DRAW_EXTRAS_SIZE 2
enum DrawExtras { // keep this in sync with WebView.java
DrawExtrasNone = 0,
- DrawExtrasSelection = 1,
- DrawExtrasCursorRing = 2
+ DrawExtrasFind = 1,
+ DrawExtrasSelection = 2,
+ DrawExtrasCursorRing = 3
};
struct JavaGlue {
jweak m_obj;
+ jmethodID m_overrideLoading;
jmethodID m_scrollBy;
+ jmethodID m_sendMoveFocus;
+ jmethodID m_sendMoveMouse;
+ jmethodID m_sendMoveMouseIfLatest;
+ jmethodID m_sendMotionUp;
+ jmethodID m_domChangedFocus;
jmethodID m_getScaledMaxXScroll;
jmethodID m_getScaledMaxYScroll;
- jmethodID m_updateRectsForGL;
+ jmethodID m_getVisibleRect;
+ jmethodID m_rebuildWebTextView;
jmethodID m_viewInvalidate;
jmethodID m_viewInvalidateRect;
jmethodID m_postInvalidateDelayed;
jmethodID m_pageSwapCallback;
+ jmethodID m_inFullScreenMode;
jfieldID m_rectLeft;
jfieldID m_rectTop;
jmethodID m_rectWidth;
jmethodID m_rectHeight;
- jfieldID m_quadFP1;
- jfieldID m_quadFP2;
- jfieldID m_quadFP3;
- jfieldID m_quadFP4;
+ jfieldID m_rectFLeft;
+ jfieldID m_rectFTop;
+ jmethodID m_rectFWidth;
+ jmethodID m_rectFHeight;
+ jmethodID m_getTextHandleScale;
AutoJObject object(JNIEnv* env) {
return getRealObject(env, m_obj);
}
} m_javaGlue;
WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir,
- bool isHighEndGfx)
- : m_isHighEndGfx(isHighEndGfx)
+ bool isHighEndGfx) :
+ m_ring((WebViewCore*) viewImpl)
+ , m_isHighEndGfx(isHighEndGfx)
{
- memset(m_extras, 0, DRAW_EXTRAS_SIZE * sizeof(DrawExtra*));
- jclass clazz = env->FindClass("android/webkit/WebViewClassic");
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ // m_javaGlue = new JavaGlue;
m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView);
m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z");
+ m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V");
+ m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V");
+ m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V");
+ m_javaGlue.m_sendMoveMouseIfLatest = GetJMethod(env, clazz, "sendMoveMouseIfLatest", "(ZZ)V");
+ m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIII)V");
+ m_javaGlue.m_domChangedFocus = GetJMethod(env, clazz, "domChangedFocus", "()V");
m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I");
m_javaGlue.m_getScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I");
- m_javaGlue.m_updateRectsForGL = GetJMethod(env, clazz, "updateRectsForGL", "()V");
+ m_javaGlue.m_getVisibleRect = GetJMethod(env, clazz, "sendOurVisibleRect", "()Landroid/graphics/Rect;");
+ m_javaGlue.m_rebuildWebTextView = GetJMethod(env, clazz, "rebuildWebTextView", "()V");
m_javaGlue.m_viewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V");
m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V");
m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz,
"viewInvalidateDelayed", "(JIIII)V");
m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "(Z)V");
+ m_javaGlue.m_inFullScreenMode = GetJMethod(env, clazz, "inFullScreenMode", "()Z");
+ m_javaGlue.m_getTextHandleScale = GetJMethod(env, clazz, "getTextHandleScale", "()F");
env->DeleteLocalRef(clazz);
jclass rectClass = env->FindClass("android/graphics/Rect");
- ALOG_ASSERT(rectClass, "Could not find Rect class");
+ LOG_ASSERT(rectClass, "Could not find Rect class");
m_javaGlue.m_rectLeft = env->GetFieldID(rectClass, "left", "I");
m_javaGlue.m_rectTop = env->GetFieldID(rectClass, "top", "I");
m_javaGlue.m_rectWidth = GetJMethod(env, rectClass, "width", "()I");
m_javaGlue.m_rectHeight = GetJMethod(env, rectClass, "height", "()I");
env->DeleteLocalRef(rectClass);
- jclass quadFClass = env->FindClass("android/webkit/QuadF");
- ALOG_ASSERT(quadFClass, "Could not find QuadF class");
- m_javaGlue.m_quadFP1 = env->GetFieldID(quadFClass, "p1", "Landroid/graphics/PointF;");
- m_javaGlue.m_quadFP2 = env->GetFieldID(quadFClass, "p2", "Landroid/graphics/PointF;");
- m_javaGlue.m_quadFP3 = env->GetFieldID(quadFClass, "p3", "Landroid/graphics/PointF;");
- m_javaGlue.m_quadFP4 = env->GetFieldID(quadFClass, "p4", "Landroid/graphics/PointF;");
- env->DeleteLocalRef(quadFClass);
+ jclass rectClassF = env->FindClass("android/graphics/RectF");
+ LOG_ASSERT(rectClassF, "Could not find RectF class");
+ m_javaGlue.m_rectFLeft = env->GetFieldID(rectClassF, "left", "F");
+ m_javaGlue.m_rectFTop = env->GetFieldID(rectClassF, "top", "F");
+ m_javaGlue.m_rectFWidth = GetJMethod(env, rectClassF, "width", "()F");
+ m_javaGlue.m_rectFHeight = GetJMethod(env, rectClassF, "height", "()F");
+ env->DeleteLocalRef(rectClassF);
env->SetIntField(javaWebView, gWebViewField, (jint)this);
m_viewImpl = (WebViewCore*) viewImpl;
+ m_frameCacheUI = 0;
+ m_navPictureUI = 0;
m_generation = 0;
m_heightCanMeasure = false;
m_lastDx = 0;
m_lastDxTime = 0;
+ m_ringAnimationEnd = 0;
m_baseLayer = 0;
m_glDrawFunctor = 0;
m_isDrawingPaused = false;
+ m_buttonSkin = drawableDir.isEmpty() ? 0 : new RenderSkinButton(drawableDir);
#if USE(ACCELERATED_COMPOSITING)
m_glWebViewState = 0;
+ m_pageSwapCallbackRegistered = false;
#endif
}
@@ -199,17 +226,11 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir,
// deallocated base layer.
stopGL();
#endif
+ delete m_frameCacheUI;
+ delete m_navPictureUI;
SkSafeUnref(m_baseLayer);
delete m_glDrawFunctor;
- for (int i = 0; i < DRAW_EXTRAS_SIZE; i++)
- delete m_extras[i];
-}
-
-DrawExtra* getDrawExtra(DrawExtras extras)
-{
- if (extras == DrawExtrasNone)
- return 0;
- return m_extras[extras - 1];
+ delete m_buttonSkin;
}
void stopGL()
@@ -224,6 +245,126 @@ WebViewCore* getWebViewCore() const {
return m_viewImpl;
}
+float getTextHandleScale()
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return 0;
+ float result = env->CallFloatMethod(javaObject.get(), m_javaGlue.m_getTextHandleScale);
+ checkException(env);
+ return result;
+}
+
+void updateSelectionHandles()
+{
+ if (!m_baseLayer)
+ return;
+ // Adjust for device density & scale
+ m_selectText.updateHandleScale(getTextHandleScale());
+}
+
+// removes the cursor altogether (e.g., when going to a new page)
+void clearCursor()
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ DBG_NAV_LOG("");
+ m_viewImpl->m_hasCursorBounds = false;
+ root->clearCursor();
+ viewInvalidate();
+}
+
+// leaves the cursor where it is, but suppresses drawing it
+void hideCursor()
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ DBG_NAV_LOG("");
+ hideCursor(root);
+ viewInvalidate();
+}
+
+void hideCursor(CachedRoot* root)
+{
+ DBG_NAV_LOG("inner");
+ m_viewImpl->m_hasCursorBounds = false;
+ root->hideCursor();
+}
+
+#if DUMP_NAV_CACHE
+void debugDump()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root)
+ root->mDebug.print();
+}
+#endif
+
+void scrollToCurrentMatch()
+{
+ if (!m_findOnPage.currentMatchIsInLayer()) {
+ scrollRectOnScreen(m_findOnPage.currentMatchBounds());
+ return;
+ }
+
+ SkRect matchBounds = m_findOnPage.currentMatchBounds();
+ LayerAndroid* rootLayer = getFrameCache(DontAllowNewer)->rootLayer();
+ Layer* layerContainingMatch = rootLayer->findById(m_findOnPage.currentMatchLayerId());
+ ASSERT(layerContainingMatch);
+
+ // If the match is in a fixed position layer, there's nothing to do.
+ if (layerContainingMatch->shouldInheritFromRootTransform())
+ return;
+
+ // If the match is in a scrollable layer or a descendant of such a layer,
+ // there may be a range of of scroll configurations that will make the
+ // current match visible. Our approach is the simplest possible. Starting at
+ // the layer in which the match is found, we move up the layer tree,
+ // scrolling any scrollable layers as little as possible to make sure that
+ // the current match is in view. This approach has the disadvantage that we
+ // may end up scrolling a larger number of elements than is necessary, which
+ // may be visually jarring. However, minimising the number of layers
+ // scrolled would complicate the code significantly.
+
+ bool didScrollLayer = false;
+ for (Layer* layer = layerContainingMatch; layer; layer = layer->getParent()) {
+ ASSERT(layer->getParent() || layer == rootLayer);
+
+ if (layer->contentIsScrollable()) {
+ // Convert the match location to layer's local space and scroll it.
+ // Repeatedly calling Layer::localToAncestor() is inefficient as
+ // each call repeats part of the calculation. It would be more
+ // efficient to maintain the transform here and update it on each
+ // iteration, but that would mean duplicating logic from
+ // Layer::localToAncestor() and would complicate things.
+ SkMatrix transform;
+ layerContainingMatch->localToAncestor(layer, &transform);
+ SkRect transformedMatchBounds;
+ transform.mapRect(&transformedMatchBounds, matchBounds);
+ SkIRect roundedTransformedMatchBounds;
+ transformedMatchBounds.roundOut(&roundedTransformedMatchBounds);
+ // Only ScrollableLayerAndroid returns true for contentIsScrollable().
+ didScrollLayer |= static_cast<ScrollableLayerAndroid*>(layer)->scrollRectIntoView(roundedTransformedMatchBounds);
+ }
+ }
+ // Invalidate, as the call below to scroll the main page may be a no-op.
+ if (didScrollLayer)
+ viewInvalidate();
+
+ // Convert matchBounds to the global space so we can scroll the main page.
+ SkMatrix transform;
+ layerContainingMatch->localToGlobal(&transform);
+ SkRect transformedMatchBounds;
+ transform.mapRect(&transformedMatchBounds, matchBounds);
+ SkIRect roundedTransformedMatchBounds;
+ transformedMatchBounds.roundOut(&roundedTransformedMatchBounds);
+ scrollRectOnScreen(roundedTransformedMatchBounds);
+}
+
void scrollRectOnScreen(const IntRect& rect)
{
if (rect.isEmpty())
@@ -231,59 +372,147 @@ void scrollRectOnScreen(const IntRect& rect)
int dx = 0;
int left = rect.x();
int right = rect.maxX();
- if (left < m_visibleContentRect.fLeft)
- dx = left - m_visibleContentRect.fLeft;
+ if (left < m_visibleRect.fLeft)
+ dx = left - m_visibleRect.fLeft;
// Only scroll right if the entire width can fit on screen.
- else if (right > m_visibleContentRect.fRight
- && right - left < m_visibleContentRect.width())
- dx = right - m_visibleContentRect.fRight;
+ else if (right > m_visibleRect.fRight
+ && right - left < m_visibleRect.width())
+ dx = right - m_visibleRect.fRight;
int dy = 0;
int top = rect.y();
int bottom = rect.maxY();
- if (top < m_visibleContentRect.fTop)
- dy = top - m_visibleContentRect.fTop;
+ if (top < m_visibleRect.fTop)
+ dy = top - m_visibleRect.fTop;
// Only scroll down if the entire height can fit on screen
- else if (bottom > m_visibleContentRect.fBottom
- && bottom - top < m_visibleContentRect.height())
- dy = bottom - m_visibleContentRect.fBottom;
+ else if (bottom > m_visibleRect.fBottom
+ && bottom - top < m_visibleRect.height())
+ dy = bottom - m_visibleRect.fBottom;
if ((dx|dy) == 0 || !scrollBy(dx, dy))
return;
viewInvalidate();
}
-int drawGL(WebCore::IntRect& invScreenRect, WebCore::IntRect* invalRect,
- WebCore::IntRect& screenRect, int titleBarHeight,
- WebCore::IntRect& screenClip, float scale, int extras, bool shouldDraw)
+void resetCursorRing()
{
+ m_ringAnimationEnd = 0;
+ m_viewImpl->m_hasCursorBounds = false;
+}
+
+bool drawCursorPreamble(CachedRoot* root)
+{
+ if (!root) return false;
+ const CachedFrame* frame;
+ const CachedNode* node = root->currentCursor(&frame);
+ if (!node) {
+ DBG_NAV_LOGV("%s", "!node");
+ resetCursorRing();
+ return false;
+ }
+ m_ring.setIsButton(node);
+ if (node->isHidden()) {
+ DBG_NAV_LOG("node->isHidden()");
+ m_viewImpl->m_hasCursorBounds = false;
+ return false;
+ }
#if USE(ACCELERATED_COMPOSITING)
- if (!m_baseLayer)
- return 0;
+ if (node->isInLayer() && root->rootLayer()) {
+ LayerAndroid* layer = root->rootLayer();
+ layer->updateFixedLayersPositions(m_visibleRect);
+ layer->updatePositions();
+ }
+#endif
+ setVisibleRect(root);
+ m_ring.m_root = root;
+ m_ring.m_frame = frame;
+ m_ring.m_node = node;
+ SkMSec time = SkTime::GetMSecs();
+ m_ring.m_isPressed = time < m_ringAnimationEnd
+ && m_ringAnimationEnd != UINT_MAX;
+ return true;
+}
- if (m_viewImpl)
- m_viewImpl->setPrerenderingEnabled(!m_isDrawingPaused);
+void drawCursorPostamble()
+{
+ if (m_ringAnimationEnd == UINT_MAX)
+ return;
+ SkMSec time = SkTime::GetMSecs();
+ if (time < m_ringAnimationEnd) {
+ // views assume that inval bounds coordinates are non-negative
+ WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX);
+ invalBounds.intersect(m_ring.m_absBounds);
+ postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds);
+ } else {
+ hideCursor(const_cast<CachedRoot*>(m_ring.m_root));
+ }
+}
+
+bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect,
+ WebCore::IntRect& webViewRect, int titleBarHeight,
+ WebCore::IntRect& clip, float scale, int extras)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (!m_baseLayer || inFullScreenMode())
+ return false;
if (!m_glWebViewState) {
- TilesManager::instance()->setHighEndGfx(m_isHighEndGfx);
m_glWebViewState = new GLWebViewState();
- m_glWebViewState->setBaseLayer(m_baseLayer, false, true);
+ m_glWebViewState->setHighEndGfx(m_isHighEndGfx);
+ m_glWebViewState->glExtras()->setCursorRingExtra(&m_ring);
+ m_glWebViewState->glExtras()->setFindOnPageExtra(&m_findOnPage);
+ if (m_baseLayer->content()) {
+ SkRegion region;
+ SkIRect rect;
+ rect.set(0, 0, m_baseLayer->content()->width(), m_baseLayer->content()->height());
+ region.setRect(rect);
+ m_glWebViewState->setBaseLayer(m_baseLayer, region, false, true);
+ }
}
- DrawExtra* extra = getDrawExtra((DrawExtras) extras);
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ }
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ // This will involve a JNI call, but under normal circumstances we will
+ // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled
+ // in WebView.java will we hit this (so really debug only)
+ updateSelectionHandles();
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX)
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+ unsigned int pic = m_glWebViewState->currentPictureCounter();
m_glWebViewState->glExtras()->setDrawExtra(extra);
// Make sure we have valid coordinates. We might not have valid coords
// if the zoom manager is still initializing. We will be redrawn
// once the correct scale is set
- if (!m_visibleContentRect.isFinite())
- return 0;
+ if (!m_visibleRect.isFinite())
+ return false;
bool treesSwapped = false;
bool newTreeHasAnim = false;
- int ret = m_glWebViewState->drawGL(invScreenRect, m_visibleContentRect, invalRect,
- screenRect, titleBarHeight, screenClip, scale,
- &treesSwapped, &newTreeHasAnim, shouldDraw);
- if (treesSwapped) {
- ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ bool ret = m_glWebViewState->drawGL(viewRect, m_visibleRect, invalRect,
+ webViewRect, titleBarHeight, clip, scale,
+ &treesSwapped, &newTreeHasAnim);
+ if (treesSwapped && (m_pageSwapCallbackRegistered || newTreeHasAnim)) {
+ m_pageSwapCallbackRegistered = false;
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
if (javaObject.get()) {
@@ -291,45 +520,237 @@ int drawGL(WebCore::IntRect& invScreenRect, WebCore::IntRect* invalRect,
checkException(env);
}
}
- return m_isDrawingPaused ? 0 : ret;
+ if (ret || m_glWebViewState->currentPictureCounter() != pic)
+ return !m_isDrawingPaused;
#endif
- return 0;
+ return false;
}
-void draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras)
+PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
{
+ PictureSet* ret = 0;
if (!m_baseLayer) {
canvas->drawColor(bgColor);
- return;
+ return ret;
}
// draw the content of the base layer first
- LayerContent* content = m_baseLayer->content();
+ PictureSet* content = m_baseLayer->content();
int sc = canvas->save(SkCanvas::kClip_SaveFlag);
- if (content) {
- canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(), content->height()),
- SkRegion::kDifference_Op);
- }
- Color c = m_baseLayer->getBackgroundColor();
- canvas->drawColor(SkColorSetARGBInline(c.alpha(), c.red(), c.green(), c.blue()));
+ canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
+ content->height()), SkRegion::kDifference_Op);
+ canvas->drawColor(bgColor);
canvas->restoreToCount(sc);
+ if (content->draw(canvas))
+ ret = split ? new PictureSet(*content) : 0;
+
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ }
+ LayerAndroid mainPicture(m_navPictureUI);
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ // This will involve a JNI call, but under normal circumstances we will
+ // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled
+ // in WebView.java will we hit this (so really debug only)
+ updateSelectionHandles();
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (compositeLayer) {
+ // call this to be sure we've adjusted for any scrolling or animations
+ // before we actually draw
+ compositeLayer->updateFixedLayersPositions(m_visibleRect);
+ compositeLayer->updatePositions();
+ // We have to set the canvas' matrix on the base layer
+ // (to have fixed layers work as intended)
+ SkAutoCanvasRestore restore(canvas, true);
+ m_baseLayer->setMatrix(canvas->getTotalMatrix());
+ canvas->resetMatrix();
+ m_baseLayer->draw(canvas);
+ }
+#endif
+ if (extra) {
+ IntRect dummy; // inval area, unused for now
+ extra->draw(canvas, &mainPicture, &dummy);
+ }
+ return ret;
+}
- // call this to be sure we've adjusted for any scrolling or animations
- // before we actually draw
- m_baseLayer->updatePositionsRecursive(m_visibleContentRect);
- m_baseLayer->updatePositions();
- // We have to set the canvas' matrix on the base layer
- // (to have fixed layers work as intended)
- SkAutoCanvasRestore restore(canvas, true);
- m_baseLayer->setMatrix(canvas->getTotalMatrix());
- canvas->resetMatrix();
- m_baseLayer->draw(canvas, getDrawExtra(extras));
+bool cursorIsTextInput(FrameCachePermission allowNewer)
+{
+ CachedRoot* root = getFrameCache(allowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* cursor = root->currentCursor();
+ if (!cursor) {
+ DBG_NAV_LOG("!cursor");
+ return false;
+ }
+ DBG_NAV_LOGD("%s", cursor->isTextInput() ? "true" : "false");
+ return cursor->isTextInput();
+}
+
+void cursorRingBounds(WebCore::IntRect* bounds)
+{
+ DBG_NAV_LOGD("%s", "");
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedNode = root->currentCursor(&cachedFrame);
+ if (cachedNode) {
+ *bounds = cachedNode->cursorRingBounds(cachedFrame);
+ DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(),
+ bounds->width(), bounds->height());
+ return;
+ }
+ }
+ *bounds = WebCore::IntRect(0, 0, 0, 0);
+}
+
+void fixCursor()
+{
+ m_viewImpl->gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_viewImpl->m_hasCursorBounds;
+ IntRect bounds = m_viewImpl->m_cursorBounds;
+ m_viewImpl->gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds)
+ return;
+ int x, y;
+ const CachedFrame* frame;
+ const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, true);
+ if (!node)
+ return;
+ // require that node have approximately the same bounds (+/- 4) and the same
+ // center (+/- 2)
+ IntPoint oldCenter = IntPoint(bounds.x() + (bounds.width() >> 1),
+ bounds.y() + (bounds.height() >> 1));
+ IntRect newBounds = node->bounds(frame);
+ IntPoint newCenter = IntPoint(newBounds.x() + (newBounds.width() >> 1),
+ newBounds.y() + (newBounds.height() >> 1));
+ DBG_NAV_LOGD("oldCenter=(%d,%d) newCenter=(%d,%d)"
+ " bounds=(%d,%d,w=%d,h=%d) newBounds=(%d,%d,w=%d,h=%d)",
+ oldCenter.x(), oldCenter.y(), newCenter.x(), newCenter.y(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height());
+ if (abs(oldCenter.x() - newCenter.x()) > 2)
+ return;
+ if (abs(oldCenter.y() - newCenter.y()) > 2)
+ return;
+ if (abs(bounds.x() - newBounds.x()) > 4)
+ return;
+ if (abs(bounds.y() - newBounds.y()) > 4)
+ return;
+ if (abs(bounds.maxX() - newBounds.maxX()) > 4)
+ return;
+ if (abs(bounds.maxY() - newBounds.maxY()) > 4)
+ return;
+ DBG_NAV_LOGD("node=%p frame=%p x=%d y=%d bounds=(%d,%d,w=%d,h=%d)",
+ node, frame, x, y, bounds.x(), bounds.y(), bounds.width(),
+ bounds.height());
+ m_frameCacheUI->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(node));
+}
+
+CachedRoot* getFrameCache(FrameCachePermission allowNewer)
+{
+ if (!m_viewImpl->m_updatedFrameCache) {
+ DBG_NAV_LOGV("%s", "!m_viewImpl->m_updatedFrameCache");
+ return m_frameCacheUI;
+ }
+ if (allowNewer == DontAllowNewer && m_viewImpl->m_lastGeneration < m_generation) {
+ DBG_NAV_LOGD("allowNewer==DontAllowNewer m_viewImpl->m_lastGeneration=%d"
+ " < m_generation=%d", m_viewImpl->m_lastGeneration, m_generation);
+ return m_frameCacheUI;
+ }
+ DBG_NAV_LOGD("%s", "m_viewImpl->m_updatedFrameCache == true");
+ const CachedFrame* oldCursorFrame;
+ const CachedNode* oldCursorNode = m_frameCacheUI ?
+ m_frameCacheUI->currentCursor(&oldCursorFrame) : 0;
+#if USE(ACCELERATED_COMPOSITING)
+ int layerId = -1;
+ if (oldCursorNode && oldCursorNode->isInLayer()) {
+ const LayerAndroid* cursorLayer = oldCursorFrame->layer(oldCursorNode)
+ ->layer(m_frameCacheUI->rootLayer());
+ if (cursorLayer)
+ layerId = cursorLayer->uniqueId();
+ }
+#endif
+ // get id from old layer and use to find new layer
+ bool oldFocusIsTextInput = false;
+ void* oldFocusNodePointer = 0;
+ if (m_frameCacheUI) {
+ const CachedNode* oldFocus = m_frameCacheUI->currentFocus();
+ if (oldFocus) {
+ oldFocusIsTextInput = oldFocus->isTextInput();
+ oldFocusNodePointer = oldFocus->nodePointer();
+ }
+ }
+ m_viewImpl->gFrameCacheMutex.lock();
+ delete m_frameCacheUI;
+ SkSafeUnref(m_navPictureUI);
+ m_viewImpl->m_updatedFrameCache = false;
+ m_frameCacheUI = m_viewImpl->m_frameCacheKit;
+ m_navPictureUI = m_viewImpl->m_navPictureKit;
+ m_viewImpl->m_frameCacheKit = 0;
+ m_viewImpl->m_navPictureKit = 0;
+ m_viewImpl->gFrameCacheMutex.unlock();
+ if (m_frameCacheUI)
+ m_frameCacheUI->setRootLayer(compositeRoot());
+#if USE(ACCELERATED_COMPOSITING)
+ if (layerId >= 0) {
+ LayerAndroid* layer = const_cast<LayerAndroid*>(
+ m_frameCacheUI->rootLayer());
+ if (layer) {
+ layer->updateFixedLayersPositions(m_visibleRect);
+ layer->updatePositions();
+ }
+ }
+#endif
+ fixCursor();
+ if (oldFocusIsTextInput) {
+ const CachedNode* newFocus = m_frameCacheUI->currentFocus();
+ if (newFocus && oldFocusNodePointer != newFocus->nodePointer()
+ && newFocus->isTextInput()
+ && newFocus != m_frameCacheUI->currentCursor()) {
+ // The focus has changed. We may need to update things.
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (javaObject.get()) {
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_domChangedFocus);
+ checkException(env);
+ }
+ }
+ }
+ if (oldCursorNode && (!m_frameCacheUI || !m_frameCacheUI->currentCursor()))
+ viewInvalidate(); // redraw in case cursor ring is still visible
+ return m_frameCacheUI;
}
int getScaledMaxXScroll()
{
- ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
if (!javaObject.get())
@@ -341,7 +762,7 @@ int getScaledMaxXScroll()
int getScaledMaxYScroll()
{
- ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
if (!javaObject.get())
@@ -351,37 +772,294 @@ int getScaledMaxYScroll()
return result;
}
-// Call through JNI to ask Java side to update the rectangles for GL functor.
-// This is called at every draw when it is not in process mode, so we should
-// keep this route as efficient as possible. Currently, its average cost on Xoom
-// is about 0.1ms - 0.2ms.
-// Alternatively, this can be achieved by adding more listener on Java side, but
-// that will be more likely causing jank when triggering GC.
-void updateRectsForGL()
+IntRect getVisibleRect()
{
+ IntRect rect;
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
if (!javaObject.get())
- return;
- env->CallVoidMethod(javaObject.get(), m_javaGlue.m_updateRectsForGL);
+ return rect;
+ jobject jRect = env->CallObjectMethod(javaObject.get(), m_javaGlue.m_getVisibleRect);
+ checkException(env);
+ rect.setX(env->GetIntField(jRect, m_javaGlue.m_rectLeft));
+ checkException(env);
+ rect.setY(env->GetIntField(jRect, m_javaGlue.m_rectTop));
+ checkException(env);
+ rect.setWidth(env->CallIntMethod(jRect, m_javaGlue.m_rectWidth));
checkException(env);
+ rect.setHeight(env->CallIntMethod(jRect, m_javaGlue.m_rectHeight));
+ checkException(env);
+ env->DeleteLocalRef(jRect);
+ checkException(env);
+ return rect;
+}
+
+static CachedFrame::Direction KeyToDirection(int32_t keyCode)
+{
+ switch (keyCode) {
+ case AKEYCODE_DPAD_RIGHT:
+ DBG_NAV_LOGD("keyCode=%s", "right");
+ return CachedFrame::RIGHT;
+ case AKEYCODE_DPAD_LEFT:
+ DBG_NAV_LOGD("keyCode=%s", "left");
+ return CachedFrame::LEFT;
+ case AKEYCODE_DPAD_DOWN:
+ DBG_NAV_LOGD("keyCode=%s", "down");
+ return CachedFrame::DOWN;
+ case AKEYCODE_DPAD_UP:
+ DBG_NAV_LOGD("keyCode=%s", "up");
+ return CachedFrame::UP;
+ default:
+ DBG_NAV_LOGD("bad key %d sent", keyCode);
+ return CachedFrame::UNINITIALIZED;
+ }
+}
+
+WTF::String imageURI(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ return root ? root->imageURI(x, y) : WTF::String();
+}
+
+bool cursorWantsKeyEvents()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedNode* focus = root->currentCursor();
+ if (focus)
+ return focus->wantsKeyEvents();
+ }
+ return false;
+}
+
+
+/* returns true if the key had no effect (neither scrolled nor changed cursor) */
+bool moveCursor(int keyCode, int count, bool ignoreScroll)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return true;
+ }
+
+ m_viewImpl->m_moveGeneration++;
+ CachedFrame::Direction direction = KeyToDirection(keyCode);
+ const CachedFrame* cachedFrame, * oldFrame = 0;
+ const CachedNode* cursor = root->currentCursor(&oldFrame);
+ WebCore::IntPoint cursorLocation = root->cursorLocation();
+ DBG_NAV_LOGD("old cursor %d (nativeNode=%p) cursorLocation={%d, %d}",
+ cursor ? cursor->index() : 0,
+ cursor ? cursor->nodePointer() : 0, cursorLocation.x(), cursorLocation.y());
+ WebCore::IntRect visibleRect = setVisibleRect(root);
+ int xMax = getScaledMaxXScroll();
+ int yMax = getScaledMaxYScroll();
+ root->setMaxScroll(xMax, yMax);
+ const CachedNode* cachedNode = 0;
+ int dx = 0;
+ int dy = 0;
+ int counter = count;
+ while (--counter >= 0) {
+ WebCore::IntPoint scroll = WebCore::IntPoint(0, 0);
+ cachedNode = root->moveCursor(direction, &cachedFrame, &scroll);
+ dx += scroll.x();
+ dy += scroll.y();
+ }
+ DBG_NAV_LOGD("new cursor %d (nativeNode=%p) cursorLocation={%d, %d}"
+ "bounds={%d,%d,w=%d,h=%d}", cachedNode ? cachedNode->index() : 0,
+ cachedNode ? cachedNode->nodePointer() : 0,
+ root->cursorLocation().x(), root->cursorLocation().y(),
+ cachedNode ? cachedNode->bounds(cachedFrame).x() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).y() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).width() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).height() : 0);
+ // If !m_heightCanMeasure (such as in the browser), we want to scroll no
+ // matter what
+ if (!ignoreScroll && (!m_heightCanMeasure ||
+ !cachedNode ||
+ (cursor && cursor->nodePointer() == cachedNode->nodePointer())))
+ {
+ if (count == 1 && dx != 0 && dy == 0 && -m_lastDx == dx &&
+ SkTime::GetMSecs() - m_lastDxTime < 1000)
+ root->checkForJiggle(&dx);
+ DBG_NAV_LOGD("scrollBy %d,%d", dx, dy);
+ if ((dx | dy))
+ this->scrollBy(dx, dy);
+ m_lastDx = dx;
+ m_lastDxTime = SkTime::GetMSecs();
+ }
+ bool result = false;
+ if (cachedNode) {
+ showCursorUntimed();
+ m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode);
+ root->setCursor(const_cast<CachedFrame*>(cachedFrame),
+ const_cast<CachedNode*>(cachedNode));
+ const CachedNode* focus = root->currentFocus();
+ bool clearTextEntry = cachedNode != focus && focus
+ && cachedNode->nodePointer() != focus->nodePointer() && focus->isTextInput();
+ // Stop painting the caret if the old focus was a text input and so is the new cursor.
+ bool stopPaintingCaret = clearTextEntry && cachedNode->wantsKeyEvents();
+ sendMoveMouseIfLatest(clearTextEntry, stopPaintingCaret);
+ } else {
+ int docHeight = root->documentHeight();
+ int docWidth = root->documentWidth();
+ if (visibleRect.maxY() + dy > docHeight)
+ dy = docHeight - visibleRect.maxY();
+ else if (visibleRect.y() + dy < 0)
+ dy = -visibleRect.y();
+ if (visibleRect.maxX() + dx > docWidth)
+ dx = docWidth - visibleRect.maxX();
+ else if (visibleRect.x() < 0)
+ dx = -visibleRect.x();
+ result = direction == CachedFrame::LEFT ? dx >= 0 :
+ direction == CachedFrame::RIGHT ? dx <= 0 :
+ direction == CachedFrame::UP ? dy >= 0 : dy <= 0;
+ }
+ return result;
+}
+
+void notifyProgressFinished()
+{
+ DBG_NAV_LOGD("cursorIsTextInput=%d", cursorIsTextInput(DontAllowNewer));
+ rebuildWebTextView();
+#if DEBUG_NAV_UI
+ if (m_frameCacheUI) {
+ const CachedNode* focus = m_frameCacheUI->currentFocus();
+ DBG_NAV_LOGD("focus %d (nativeNode=%p)",
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : 0);
+ }
+#endif
+}
+
+const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect,
+ const CachedFrame** framePtr, int* rxPtr, int* ryPtr)
+{
+ *rxPtr = 0;
+ *ryPtr = 0;
+ *framePtr = 0;
+ if (!root)
+ return 0;
+ setVisibleRect(root);
+ return root->findAt(rect, framePtr, rxPtr, ryPtr, true);
+}
+
+IntRect setVisibleRect(CachedRoot* root)
+{
+ IntRect visibleRect = getVisibleRect();
+ DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
+ visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
+ root->setVisibleRect(visibleRect);
+ return visibleRect;
+}
+
+void selectBestAt(const WebCore::IntRect& rect)
+{
+ const CachedFrame* frame;
+ int rx, ry;
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ const CachedNode* node = findAt(root, rect, &frame, &rx, &ry);
+ if (!node) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ root->rootHistory()->setMouseBounds(rect);
+ m_viewImpl->m_hasCursorBounds = false;
+ root->setCursor(0, 0);
+ viewInvalidate();
+ } else {
+ DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index());
+ WebCore::IntRect bounds = node->bounds(frame);
+ root->rootHistory()->setMouseBounds(bounds);
+ m_viewImpl->updateCursorBounds(root, frame, node);
+ showCursorTimed();
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(node));
+ }
+ sendMoveMouseIfLatest(false, false);
+}
+
+const CachedNode* m_cacheHitNode;
+const CachedFrame* m_cacheHitFrame;
+
+bool pointInNavCache(int x, int y, int slop)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return false;
+ IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
+ int rx, ry;
+ return (m_cacheHitNode = findAt(root, rect, &m_cacheHitFrame, &rx, &ry));
+}
+
+bool motionUp(int x, int y, int slop)
+{
+ bool pageScrolled = false;
+ IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
+ int rx, ry;
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ const CachedNode* result = findAt(root, rect, &frame, &rx, &ry);
+ CachedHistory* history = root->rootHistory();
+ if (!result) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ history->setNavBounds(rect);
+ m_viewImpl->m_hasCursorBounds = false;
+ root->hideCursor();
+ int dx = root->checkForCenter(x, y);
+ if (dx) {
+ scrollBy(dx, 0);
+ pageScrolled = true;
+ }
+ sendMotionUp(frame ? (WebCore::Frame*) frame->framePointer() : 0,
+ 0, x, y);
+ viewInvalidate();
+ return pageScrolled;
+ }
+ DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result,
+ result->index(), x, y, rx, ry);
+ WebCore::IntRect navBounds = WebCore::IntRect(rx, ry, 1, 1);
+ history->setNavBounds(navBounds);
+ history->setMouseBounds(navBounds);
+ m_viewImpl->updateCursorBounds(root, frame, result);
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(result));
+ if (result->isSyntheticLink())
+ overrideUrlLoading(result->getExport());
+ else {
+ sendMotionUp(
+ (WebCore::Frame*) frame->framePointer(),
+ (WebCore::Node*) result->nodePointer(), rx, ry);
+ }
+ if (result->isTextInput() || result->isSelect()
+ || result->isContentEditable()) {
+ showCursorUntimed();
+ } else
+ showCursorTimed();
+ return pageScrolled;
}
#if USE(ACCELERATED_COMPOSITING)
static const ScrollableLayerAndroid* findScrollableLayer(
const LayerAndroid* parent, int x, int y, SkIRect* foundBounds) {
- IntRect bounds = enclosingIntRect(parent->fullContentAreaMapped());
-
+ SkRect bounds;
+ parent->bounds(&bounds);
// Check the parent bounds first; this will clip to within a masking layer's
// bounds.
if (parent->masksToBounds() && !bounds.contains(x, y))
return 0;
-
+ // Move the hit test local to parent.
+ x -= bounds.fLeft;
+ y -= bounds.fTop;
int count = parent->countChildren();
while (count--) {
const LayerAndroid* child = parent->getChild(count);
- const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y, foundBounds);
+ const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y,
+ foundBounds);
if (result) {
+ foundBounds->offset(bounds.fLeft, bounds.fTop);
if (parent->masksToBounds()) {
if (bounds.width() < foundBounds->width())
foundBounds->fRight = foundBounds->fLeft + bounds.width();
@@ -392,7 +1070,7 @@ static const ScrollableLayerAndroid* findScrollableLayer(
}
}
if (parent->contentIsScrollable()) {
- foundBounds->set(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ foundBounds->set(0, 0, bounds.width(), bounds.height());
return static_cast<const ScrollableLayerAndroid*>(parent);
}
return 0;
@@ -402,9 +1080,11 @@ static const ScrollableLayerAndroid* findScrollableLayer(
int scrollableLayer(int x, int y, SkIRect* layerRect, SkIRect* bounds)
{
#if USE(ACCELERATED_COMPOSITING)
- if (!m_baseLayer)
+ const LayerAndroid* layerRoot = compositeRoot();
+ if (!layerRoot)
return 0;
- const ScrollableLayerAndroid* result = findScrollableLayer(m_baseLayer, x, y, bounds);
+ const ScrollableLayerAndroid* result = findScrollableLayer(layerRoot, x, y,
+ bounds);
if (result) {
result->getScrollRect(layerRect);
return result->uniqueId();
@@ -419,6 +1099,52 @@ void scrollLayer(int layerId, int x, int y)
m_glWebViewState->scrollLayer(layerId, x, y);
}
+int getBlockLeftEdge(int x, int y, float scale)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (root)
+ return root->getBlockLeftEdge(x, y, scale);
+ return -1;
+}
+
+void overrideUrlLoading(const WTF::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ jstring jName = wtfStringToJstring(env, url);
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_overrideLoading, jName);
+ env->DeleteLocalRef(jName);
+}
+
+void setFindIsUp(bool up)
+{
+ DBG_NAV_LOGD("up=%d", up);
+ m_viewImpl->m_findIsUp = up;
+}
+
+void setFindIsEmpty()
+{
+ DBG_NAV_LOG("");
+ m_findOnPage.clearCurrentLocation();
+}
+
+void showCursorTimed()
+{
+ DBG_NAV_LOG("");
+ m_ringAnimationEnd = SkTime::GetMSecs() + PRESSED_STATE_DURATION;
+ viewInvalidate();
+}
+
+void showCursorUntimed()
+{
+ DBG_NAV_LOG("");
+ m_ring.m_isPressed = false;
+ m_ringAnimationEnd = UINT_MAX;
+ viewInvalidate();
+}
+
void setHeightCanMeasure(bool measure)
{
m_heightCanMeasure = measure;
@@ -426,16 +1152,170 @@ void setHeightCanMeasure(bool measure)
String getSelection()
{
- SelectText* select = static_cast<SelectText*>(
- getDrawExtra(WebView::DrawExtrasSelection));
- if (select)
- return select->getText();
- return String();
+ return m_selectText.getSelection();
+}
+
+void moveSelection(int x, int y)
+{
+ m_selectText.moveSelection(getVisibleRect(), x, y);
+}
+
+IntPoint selectableText()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return IntPoint(0, 0);
+ return m_selectText.selectableText(root);
+}
+
+void selectAll()
+{
+ m_selectText.selectAll();
+}
+
+int selectionX()
+{
+ return m_selectText.selectionX();
+}
+
+int selectionY()
+{
+ return m_selectText.selectionY();
+}
+
+void resetSelection()
+{
+ m_selectText.reset();
+}
+
+bool startSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ updateSelectionHandles();
+ return m_selectText.startSelection(root, getVisibleRect(), x, y);
+}
+
+bool wordSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ updateSelectionHandles();
+ return m_selectText.wordSelection(root, getVisibleRect(), x, y);
+}
+
+bool extendSelection(int x, int y)
+{
+ m_selectText.extendSelection(getVisibleRect(), x, y);
+ return true;
+}
+
+bool hitSelection(int x, int y)
+{
+ updateSelectionHandles();
+ return m_selectText.hitSelection(x, y);
+}
+
+void setExtendSelection()
+{
+ m_selectText.setExtendSelection(true);
+}
+
+void setSelectionPointer(bool set, float scale, int x, int y)
+{
+ m_selectText.setDrawPointer(set);
+ if (!set)
+ return;
+ m_selectText.m_inverseScale = scale;
+ m_selectText.m_selectX = x;
+ m_selectText.m_selectY = y;
+}
+
+void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
+{
+ DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveFocus, (jint) framePtr, (jint) nodePtr);
+ checkException(env);
+}
+
+void sendMoveMouse(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
+{
+ DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", framePtr, nodePtr, x, y);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouse, reinterpret_cast<jint>(framePtr), reinterpret_cast<jint>(nodePtr), x, y);
+ checkException(env);
+}
+
+void sendMoveMouseIfLatest(bool clearTextEntry, bool stopPaintingCaret)
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouseIfLatest, clearTextEntry, stopPaintingCaret);
+ checkException(env);
+}
+
+void sendMotionUp(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
+{
+ DBG_NAV_LOGD("m_generation=%d framePtr=%p nodePtr=%p x=%d y=%d", m_generation, framePtr, nodePtr, x, y);
+ LOG_ASSERT(m_javaGlue.m_obj, "A WebView was not associated with this WebViewNative!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ m_viewImpl->m_touchGeneration = ++m_generation;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMotionUp, m_generation, (jint) framePtr, (jint) nodePtr, x, y);
+ checkException(env);
+}
+
+void findNext(bool forward)
+{
+ m_findOnPage.findNext(forward);
+ scrollToCurrentMatch();
+ viewInvalidate();
+}
+
+// With this call, WebView takes ownership of matches, and is responsible for
+// deleting it.
+void setMatches(WTF::Vector<MatchInfo>* matches, jboolean sameAsLastSearch)
+{
+ // If this search is the same as the last one, check against the old
+ // location to determine whether to scroll. If the same word is found
+ // in the same place, then do not scroll.
+ IntRect oldLocation;
+ bool checkAgainstOldLocation = false;
+ if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) {
+ oldLocation = m_findOnPage.currentMatchBounds();
+ checkAgainstOldLocation = true;
+ }
+
+ m_findOnPage.setMatches(matches);
+
+ if (!checkAgainstOldLocation || oldLocation != m_findOnPage.currentMatchBounds())
+ scrollToCurrentMatch();
+ viewInvalidate();
+}
+
+int currentMatchIndex()
+{
+ return m_findOnPage.currentMatchIndex();
}
bool scrollBy(int dx, int dy)
{
- ALOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
JNIEnv* env = JSC::Bindings::getJNIEnv();
AutoJObject javaObject = m_javaGlue.object(env);
@@ -454,6 +1334,44 @@ void setIsScrolling(bool isScrolling)
#endif
}
+bool hasCursorNode()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* cursorNode = root->currentCursor();
+ DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p)",
+ cursorNode ? cursorNode->index() : -1,
+ cursorNode ? cursorNode->nodePointer() : 0);
+ return cursorNode;
+}
+
+bool hasFocusNode()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* focusNode = root->currentFocus();
+ DBG_NAV_LOGD("focusNode=%d (nodePointer=%p)",
+ focusNode ? focusNode->index() : -1,
+ focusNode ? focusNode->nodePointer() : 0);
+ return focusNode;
+}
+
+void rebuildWebTextView()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return;
+ env->CallVoidMethod(javaObject.get(), m_javaGlue.m_rebuildWebTextView);
+ checkException(env);
+}
+
void viewInvalidate()
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
@@ -485,64 +1403,112 @@ void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds)
checkException(env);
}
+bool inFullScreenMode()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject javaObject = m_javaGlue.object(env);
+ if (!javaObject.get())
+ return false;
+ jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue.m_inFullScreenMode);
+ checkException(env);
+ return result;
+}
+
+int moveGeneration()
+{
+ return m_viewImpl->m_moveGeneration;
+}
+
+LayerAndroid* compositeRoot() const
+{
+ LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1,
+ "base layer can't have more than one child %s", __FUNCTION__);
+ if (m_baseLayer && m_baseLayer->countChildren() == 1)
+ return static_cast<LayerAndroid*>(m_baseLayer->getChild(0));
+ else
+ return 0;
+}
+
#if ENABLE(ANDROID_OVERFLOW_SCROLL)
-static void copyScrollPosition(const LayerAndroid* fromRoot,
- LayerAndroid* toRoot, int layerId)
+static void copyScrollPositionRecursive(const LayerAndroid* from,
+ LayerAndroid* root)
{
- if (!fromRoot || !toRoot)
- return;
- const LayerAndroid* from = fromRoot->findById(layerId);
- LayerAndroid* to = toRoot->findById(layerId);
- if (!from || !to || !from->contentIsScrollable() || !to->contentIsScrollable())
- return;
- // TODO: Support this for iframes.
- if (to->isIFrameContent() || from->isIFrameContent())
+ if (!from || !root)
return;
- to->setScrollOffset(from->getScrollOffset());
+ for (int i = 0; i < from->countChildren(); i++) {
+ const LayerAndroid* l = from->getChild(i);
+ if (l->contentIsScrollable()) {
+ const SkPoint& pos = l->getPosition();
+ LayerAndroid* match = root->findById(l->uniqueId());
+ if (match && match->contentIsScrollable())
+ match->setPosition(pos.fX, pos.fY);
+ }
+ copyScrollPositionRecursive(l, root);
+ }
}
#endif
-BaseLayerAndroid* getBaseLayer() const { return m_baseLayer; }
+void registerPageSwapCallback()
+{
+ m_pageSwapCallbackRegistered = true;
+}
-bool setBaseLayer(BaseLayerAndroid* newBaseLayer, bool showVisualIndicator,
- bool isPictureAfterFirstLayout, int scrollingLayer)
+void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndicator,
+ bool isPictureAfterFirstLayout, bool registerPageSwapCallback)
{
- bool queueFull = false;
#if USE(ACCELERATED_COMPOSITING)
if (m_glWebViewState)
- queueFull = m_glWebViewState->setBaseLayer(newBaseLayer, showVisualIndicator,
- isPictureAfterFirstLayout);
+ m_glWebViewState->setBaseLayer(layer, inval, showVisualIndicator,
+ isPictureAfterFirstLayout);
+ m_pageSwapCallbackRegistered |= registerPageSwapCallback;
#endif
#if ENABLE(ANDROID_OVERFLOW_SCROLL)
- copyScrollPosition(m_baseLayer, newBaseLayer, scrollingLayer);
+ if (layer) {
+ // TODO: the below tree copies are only necessary in software rendering
+ LayerAndroid* newCompositeRoot = static_cast<LayerAndroid*>(layer->getChild(0));
+ copyScrollPositionRecursive(compositeRoot(), newCompositeRoot);
+ }
#endif
SkSafeUnref(m_baseLayer);
- m_baseLayer = newBaseLayer;
-
- return queueFull;
+ m_baseLayer = layer;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ root->resetLayers();
+ root->setRootLayer(compositeRoot());
}
-void copyBaseContentToPicture(SkPicture* picture)
+void getTextSelectionRegion(SkRegion *region)
{
- if (!m_baseLayer || !m_baseLayer->content())
- return;
- LayerContent* content = m_baseLayer->content();
- SkCanvas* canvas = picture->beginRecording(content->width(), content->height(),
- SkPicture::kUsePathBoundsForClip_RecordingFlag);
+ m_selectText.getSelectionRegion(getVisibleRect(), region, compositeRoot());
+}
- // clear the BaseLayerAndroid's previous matrix (set at each draw)
- SkMatrix baseMatrix;
- baseMatrix.reset();
- m_baseLayer->setMatrix(baseMatrix);
+void getTextSelectionHandles(int* handles)
+{
+ m_selectText.getSelectionHandles(handles, compositeRoot());
+}
- m_baseLayer->draw(canvas, 0);
+void replaceBaseContent(PictureSet* set)
+{
+ if (!m_baseLayer)
+ return;
+ m_baseLayer->setContent(*set);
+ delete set;
+}
+void copyBaseContentToPicture(SkPicture* picture)
+{
+ if (!m_baseLayer)
+ return;
+ PictureSet* content = m_baseLayer->content();
+ m_baseLayer->drawCanvas(picture->beginRecording(content->width(), content->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag));
picture->endRecording();
}
bool hasContent() {
- if (!m_baseLayer || !m_baseLayer->content())
+ if (!m_baseLayer)
return false;
return !m_baseLayer->content()->isEmpty();
}
@@ -556,197 +1522,37 @@ Functor* getFunctor() {
return m_glDrawFunctor;
}
-void setVisibleContentRect(SkRect& visibleContentRect) {
- m_visibleContentRect = visibleContentRect;
-}
-
-void setDrawExtra(DrawExtra *extra, DrawExtras type)
-{
- if (type == DrawExtrasNone)
- return;
- DrawExtra* old = m_extras[type - 1];
- m_extras[type - 1] = extra;
- if (old != extra) {
- delete old;
- }
-}
-
-void setTextSelection(SelectText *selection) {
- setDrawExtra(selection, DrawExtrasSelection);
-}
-
-const TransformationMatrix* getLayerTransform(int layerId) {
- if (layerId != -1 && m_baseLayer) {
- LayerAndroid* layer = m_baseLayer->findById(layerId);
- // We need to make sure the drawTransform is up to date as this is
- // called before a draw() or drawGL()
- if (layer) {
- m_baseLayer->updatePositionsRecursive(m_visibleContentRect);
- return layer->drawTransform();
- }
- }
- return 0;
-}
-
-int getHandleLayerId(SelectText::HandleId handleId, SkIPoint& cursorPoint,
- FloatQuad& textBounds) {
- SelectText* selectText = static_cast<SelectText*>(getDrawExtra(DrawExtrasSelection));
- if (!selectText || !m_baseLayer)
- return -1;
- int layerId = selectText->caretLayerId(handleId);
- IntRect cursorRect = selectText->caretRect(handleId);
- IntRect textRect = selectText->textRect(handleId);
- // Rects exclude the last pixel on right/bottom. We want only included pixels.
- cursorPoint.set(cursorRect.x(), cursorRect.maxY() - 1);
- textRect.setHeight(std::max(1, textRect.height() - 1));
- textRect.setWidth(std::max(1, textRect.width() - 1));
- textBounds = FloatQuad(textRect);
-
- const TransformationMatrix* transform = getLayerTransform(layerId);
- if (transform) {
- // We're overloading the concept of Rect to be just the two
- // points (bottom-left and top-right.
- cursorPoint = transform->mapPoint(cursorPoint);
- textBounds = transform->mapQuad(textBounds);
- }
- return layerId;
-}
-
-void mapLayerRect(int layerId, SkIRect& rect) {
- const TransformationMatrix* transform = getLayerTransform(layerId);
- if (transform)
- rect = transform->mapRect(rect);
-}
-
-void floatQuadToQuadF(JNIEnv* env, const FloatQuad& nativeTextQuad,
- jobject textQuad)
-{
- jobject p1 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP1);
- jobject p2 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP2);
- jobject p3 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP3);
- jobject p4 = env->GetObjectField(textQuad, m_javaGlue.m_quadFP4);
- GraphicsJNI::point_to_jpointf(nativeTextQuad.p1(), env, p1);
- GraphicsJNI::point_to_jpointf(nativeTextQuad.p2(), env, p2);
- GraphicsJNI::point_to_jpointf(nativeTextQuad.p3(), env, p3);
- GraphicsJNI::point_to_jpointf(nativeTextQuad.p4(), env, p4);
- env->DeleteLocalRef(p1);
- env->DeleteLocalRef(p2);
- env->DeleteLocalRef(p3);
- env->DeleteLocalRef(p4);
+BaseLayerAndroid* getBaseLayer() {
+ return m_baseLayer;
}
-// This is called when WebView switches rendering modes in a more permanent fashion
-// such as when the layer type is set or the view is attached/detached from the window
-int setHwAccelerated(bool hwAccelerated) {
- if (!m_glWebViewState)
- return 0;
- LayerAndroid* root = m_baseLayer;
- if (root)
- return root->setHwAccelerated(hwAccelerated);
- return 0;
+void setVisibleRect(SkRect& visibleRect) {
+ m_visibleRect = visibleRect;
}
-void setDrawingPaused(bool isPaused)
-{
- m_isDrawingPaused = isPaused;
- if (m_viewImpl)
- m_viewImpl->setPrerenderingEnabled(!isPaused);
-}
-
-// Finds the rectangles within world to the left, right, top, and bottom
-// of rect and adds them to rects. If no intersection exists, false is returned.
-static bool findMaskedRects(const FloatRect& world,
- const FloatRect& rect, Vector<FloatRect>& rects) {
- if (!world.intersects(rect))
- return false; // nothing to subtract
-
- // left rectangle
- if (rect.x() > world.x())
- rects.append(FloatRect(world.x(), world.y(),
- rect.x() - world.x(), world.height()));
- // top rectangle
- if (rect.y() > world.y())
- rects.append(FloatRect(world.x(), world.y(),
- world.width(), rect.y() - world.y()));
- // right rectangle
- if (rect.maxX() < world.maxX())
- rects.append(FloatRect(rect.maxX(), world.y(),
- world.maxX() - rect.maxX(), world.height()));
- // bottom rectangle
- if (rect.maxY() < world.maxY())
- rects.append(FloatRect(world.x(), rect.maxY(),
- world.width(), world.maxY() - rect.maxY()));
- return true;
-}
-
-// Returns false if layerId is a fixed position layer, otherwise
-// all fixed position layer rectangles are subtracted from those within
-// rects. Rects will be modified to contain rectangles that don't include
-// the fixed position layer rectangles.
-static bool findMaskedRectsForLayer(LayerAndroid* layer,
- Vector<FloatRect>& rects, int layerId)
-{
- if (layer->isPositionFixed()) {
- if (layerId == layer->uniqueId())
- return false;
- FloatRect layerRect = layer->fullContentAreaMapped();
- for (int i = rects.size() - 1; i >= 0; i--)
- if (findMaskedRects(rects[i], layerRect, rects))
- rects.remove(i);
- }
-
- int childIndex = 0;
- while (LayerAndroid* child = layer->getChild(childIndex++))
- if (!findMaskedRectsForLayer(child, rects, layerId))
- return false;
-
- return true;
-}
-
-// Finds the largest rectangle not masked by any fixed layer.
-void findMaxVisibleRect(int movingLayerId, SkIRect& visibleContentRect)
-{
- if (!m_baseLayer)
- return;
-
- FloatRect visibleContentFloatRect(visibleContentRect);
- m_baseLayer->updatePositionsRecursive(visibleContentFloatRect);
- Vector<FloatRect> rects;
- rects.append(visibleContentFloatRect);
- if (findMaskedRectsForLayer(m_baseLayer, rects, movingLayerId)) {
- float maxSize = 0.0;
- const FloatRect* largest = 0;
- for (int i = 0; i < rects.size(); i++) {
- const FloatRect& rect = rects[i];
- float size = rect.width() * rect.height();
- if (size > maxSize) {
- maxSize = size;
- largest = &rect;
- }
- }
- if (largest) {
- SkRect largeRect = *largest;
- largeRect.round(&visibleContentRect);
- }
- }
-}
-
-private: // local state for WebView
bool m_isDrawingPaused;
+private: // local state for WebView
// private to getFrameCache(); other functions operate in a different thread
+ CachedRoot* m_frameCacheUI; // navigation data ready for use
WebViewCore* m_viewImpl;
int m_generation; // associate unique ID with sent kit focus to match with ui
+ SkPicture* m_navPictureUI;
+ SkMSec m_ringAnimationEnd;
// Corresponds to the same-named boolean on the java side.
bool m_heightCanMeasure;
int m_lastDx;
SkMSec m_lastDxTime;
- DrawExtra* m_extras[DRAW_EXTRAS_SIZE];
+ SelectText m_selectText;
+ FindOnPage m_findOnPage;
+ CursorRing m_ring;
BaseLayerAndroid* m_baseLayer;
Functor* m_glDrawFunctor;
#if USE(ACCELERATED_COMPOSITING)
GLWebViewState* m_glWebViewState;
+ bool m_pageSwapCallbackRegistered;
#endif
- SkRect m_visibleContentRect;
+ RenderSkinButton* m_buttonSkin;
+ SkRect m_visibleRect;
bool m_isHighEndGfx;
}; // end of WebView class
@@ -759,54 +1565,45 @@ private: // local state for WebView
class GLDrawFunctor : Functor {
public:
GLDrawFunctor(WebView* _wvInstance,
- int (WebView::*_funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
- WebCore::IntRect&, int, WebCore::IntRect&, jfloat, jint, bool),
- WebCore::IntRect _invScreenRect, float _scale, int _extras) {
+ bool(WebView::*_funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
+ WebCore::IntRect&, int, WebCore::IntRect&,
+ jfloat, jint),
+ WebCore::IntRect _viewRect, float _scale, int _extras) {
wvInstance = _wvInstance;
funcPtr = _funcPtr;
- invScreenRect = _invScreenRect;
+ viewRect = _viewRect;
scale = _scale;
extras = _extras;
};
-
status_t operator()(int messageId, void* data) {
- TRACE_METHOD();
- bool shouldDraw = (messageId == uirenderer::DrawGlInfo::kModeDraw);
- if (shouldDraw)
- wvInstance->updateRectsForGL();
-
- if (invScreenRect.isEmpty()) {
+ if (viewRect.isEmpty()) {
// NOOP operation if viewport is empty
return 0;
}
WebCore::IntRect inval;
- int titlebarHeight = screenRect.height() - invScreenRect.height();
+ int titlebarHeight = webViewRect.height() - viewRect.height();
uirenderer::DrawGlInfo* info = reinterpret_cast<uirenderer::DrawGlInfo*>(data);
- WebCore::IntRect screenClip(info->clipLeft, info->clipTop,
- info->clipRight - info->clipLeft,
- info->clipBottom - info->clipTop);
-
- WebCore::IntRect localInvScreenRect = invScreenRect;
- if (info->isLayer) {
- // When webview is on a layer, we need to use the viewport relative
- // to the FBO, rather than the screen(which will use invScreenRect).
- localInvScreenRect.setX(screenClip.x());
- localInvScreenRect.setY(info->height - screenClip.y() - screenClip.height());
- }
- // Send the necessary info to the shader.
- TilesManager::instance()->shader()->setGLDrawInfo(info);
-
- int returnFlags = (*wvInstance.*funcPtr)(localInvScreenRect, &inval, screenRect,
- titlebarHeight, screenClip, scale, extras, shouldDraw);
- if ((returnFlags & uirenderer::DrawGlInfo::kStatusDraw) != 0) {
+ WebCore::IntRect localViewRect = viewRect;
+ if (info->isLayer)
+ localViewRect.move(-1 * localViewRect.x(), -1 * localViewRect.y());
+
+ WebCore::IntRect clip(info->clipLeft, info->clipTop,
+ info->clipRight - info->clipLeft,
+ info->clipBottom - info->clipTop);
+ TilesManager::instance()->shader()->setWebViewMatrix(info->transform, info->isLayer);
+
+ bool retVal = (*wvInstance.*funcPtr)(localViewRect, &inval, webViewRect,
+ titlebarHeight, clip, scale, extras);
+ if (retVal) {
IntRect finalInval;
- if (inval.isEmpty())
- finalInval = screenRect;
- else {
- finalInval.setX(screenRect.x() + inval.x());
- finalInval.setY(screenRect.y() + titlebarHeight + inval.y());
+ if (inval.isEmpty()) {
+ finalInval = webViewRect;
+ retVal = true;
+ } else {
+ finalInval.setX(webViewRect.x() + inval.x());
+ finalInval.setY(webViewRect.y() + titlebarHeight + inval.y());
finalInval.setWidth(inval.width());
finalInval.setHeight(inval.height());
}
@@ -815,45 +1612,201 @@ class GLDrawFunctor : Functor {
info->dirtyRight = finalInval.maxX();
info->dirtyBottom = finalInval.maxY();
}
- // return 1 if invalidation needed, 2 to request non-drawing functor callback, 0 otherwise
- ALOGV("returnFlags are %d, shouldDraw %d", returnFlags, shouldDraw);
- return returnFlags;
- }
- void updateScreenRect(WebCore::IntRect& _screenRect) {
- screenRect = _screenRect;
+ // return 1 if invalidation needed, 0 otherwise
+ return retVal ? 1 : 0;
}
- void updateInvScreenRect(WebCore::IntRect& _invScreenRect) {
- invScreenRect = _invScreenRect;
+ void updateRect(WebCore::IntRect& _viewRect) {
+ viewRect = _viewRect;
}
- void updateScale(float _scale) {
- scale = _scale;
- }
- void updateExtras(jint _extras) {
- extras = _extras;
+ void updateViewRect(WebCore::IntRect& _viewRect) {
+ webViewRect = _viewRect;
}
private:
WebView* wvInstance;
- int (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
- WebCore::IntRect&, int, WebCore::IntRect&, float, int, bool);
- WebCore::IntRect invScreenRect;
- WebCore::IntRect screenRect;
+ bool (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
+ WebCore::IntRect&, int, WebCore::IntRect&, float, int);
+ WebCore::IntRect viewRect;
+ WebCore::IntRect webViewRect;
jfloat scale;
jint extras;
};
+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);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
/*
* Native JNI methods
*/
+static int nativeCacheHitFramePointer(JNIEnv *env, jobject obj)
+{
+ return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitFrame->framePointer());
+}
+
+static jobject nativeCacheHitNodeBounds(JNIEnv *env, jobject obj)
+{
+ WebCore::IntRect bounds = GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitNode->originalAbsoluteBounds();
+ return createJavaRect(env, bounds.x(), bounds.y(),
+ bounds.maxX(), bounds.maxY());
+}
+
+static int nativeCacheHitNodePointer(JNIEnv *env, jobject obj)
+{
+ return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitNode->nodePointer());
+}
+
+static bool nativeCacheHitIsPlugin(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->m_cacheHitNode->isPlugin();
+}
+
+static void nativeClearCursor(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->clearCursor();
+}
static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl,
jstring drawableDir, jboolean isHighEndGfx)
{
WTF::String dir = jstringToWtfString(env, drawableDir);
- new WebView(env, obj, viewImpl, dir, isHighEndGfx);
+ WebView* webview = new WebView(env, obj, viewImpl, dir, isHighEndGfx);
// NEED THIS OR SOMETHING LIKE IT!
//Release(obj);
}
+static jint nativeCursorFramePointer(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ (void) root->currentCursor(&frame);
+ return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
+}
+
+static const CachedNode* getCursorNode(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentCursor() : 0;
+}
+
+static const CachedNode* getCursorNode(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentCursor(frame) : 0;
+}
+
+static const CachedNode* getFocusCandidate(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedNode* cursor = root->currentCursor(frame);
+ if (cursor && cursor->wantsKeyEvents())
+ return cursor;
+ return root->currentFocus(frame);
+}
+
+static bool focusCandidateHasNextTextfield(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return false;
+ const CachedNode* cursor = root->currentCursor();
+ if (!cursor || !cursor->isTextInput())
+ cursor = root->currentFocus();
+ if (!cursor || !cursor->isTextInput()) return false;
+ return root->nextTextField(cursor, 0);
+}
+
+static const CachedNode* getFocusNode(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentFocus() : 0;
+}
+
+static const CachedNode* getFocusNode(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentFocus(frame) : 0;
+}
+
+static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame;
+ const CachedNode* cursor = root->currentCursor(&frame);
+ if (!cursor || !cursor->wantsKeyEvents())
+ cursor = root->currentFocus(&frame);
+ return cursor ? frame->textInput(cursor) : 0;
+}
+
+static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj)
+{
+ const CachedNode* focus = getFocusNode(env, obj);
+ if (!focus) return false;
+ // Plugins handle shift and arrows whether or not they have focus.
+ if (focus->isPlugin()) return true;
+ const CachedNode* cursor = getCursorNode(env, obj);
+ // ContentEditable nodes should only receive shift and arrows if they have
+ // both the cursor and the focus.
+ return cursor && cursor->nodePointer() == focus->nodePointer()
+ && cursor->isContentEditable();
+}
+
+static jobject nativeCursorNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getCursorNode(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ return createJavaRect(env, bounds.x(), bounds.y(),
+ bounds.maxX(), bounds.maxY());
+}
+
+static jint nativeCursorNodePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return reinterpret_cast<int>(node ? node->nodePointer() : 0);
+}
+
+static jobject nativeCursorPosition(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ const CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ WebCore::IntPoint pos = WebCore::IntPoint(0, 0);
+ if (root)
+ root->getSimulatedMousePosition(&pos);
+ jclass pointClass = env->FindClass("android/graphics/Point");
+ jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
+ jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
+ env->DeleteLocalRef(pointClass);
+ return point;
+}
+
static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
{
if (obj) {
@@ -872,67 +1825,88 @@ static SkRect jrectf_to_rect(JNIEnv* env, jobject obj)
return rect;
}
-static void nativeDraw(JNIEnv *env, jobject obj, jobject canv,
+static bool nativeCursorIntersects(JNIEnv *env, jobject obj, jobject visRect)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getCursorNode(env, obj, &frame);
+ return node ? node->bounds(frame).intersects(
+ jrect_to_webrect(env, visRect)) : false;
+}
+
+static bool nativeCursorIsAnchor(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return node ? node->isAnchor() : false;
+}
+
+static bool nativeCursorIsTextInput(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return node ? node->isTextInput() : false;
+}
+
+static jobject nativeCursorText(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ if (!node)
+ return 0;
+ WTF::String value = node->getExport();
+ return wtfStringToJstring(env, value);
+}
+
+static void nativeDebugDump(JNIEnv *env, jobject obj)
+{
+#if DUMP_NAV_CACHE
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->debugDump();
+#endif
+}
+
+static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv,
jobject visible, jint color,
- jint extras) {
+ jint extras, jboolean split) {
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
WebView* webView = GET_NATIVE_VIEW(env, obj);
- SkRect visibleContentRect = jrectf_to_rect(env, visible);
- webView->setVisibleContentRect(visibleContentRect);
- webView->draw(canvas, color, static_cast<WebView::DrawExtras>(extras));
-}
-
-static jint nativeCreateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView,
- jobject jinvscreenrect, jobject jscreenrect,
- jobject jvisiblecontentrect,
- jfloat scale, jint extras) {
- WebCore::IntRect invScreenRect = jrect_to_webrect(env, jinvscreenrect);
- WebView *wvInstance = reinterpret_cast<WebView*>(nativeView);
- SkRect visibleContentRect = jrectf_to_rect(env, jvisiblecontentrect);
- wvInstance->setVisibleContentRect(visibleContentRect);
-
- GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor();
- if (!functor) {
- functor = new GLDrawFunctor(wvInstance, &android::WebView::drawGL,
- invScreenRect, scale, extras);
- wvInstance->setFunctor((Functor*) functor);
- } else {
- functor->updateInvScreenRect(invScreenRect);
- functor->updateScale(scale);
- functor->updateExtras(extras);
- }
+ SkRect visibleRect = jrectf_to_rect(env, visible);
+ webView->setVisibleRect(visibleRect);
+ PictureSet* pictureSet = webView->draw(canvas, color, extras, split);
+ return reinterpret_cast<jint>(pictureSet);
+}
- WebCore::IntRect rect = jrect_to_webrect(env, jscreenrect);
- functor->updateScreenRect(rect);
+static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView,
+ jobject jrect, jobject jviewrect,
+ jobject jvisiblerect,
+ jfloat scale, jint extras) {
+ WebCore::IntRect viewRect = jrect_to_webrect(env, jrect);
+ WebView *wvInstance = (WebView*) nativeView;
+ SkRect visibleRect = jrectf_to_rect(env, jvisiblerect);
+ wvInstance->setVisibleRect(visibleRect);
- return (jint)functor;
-}
+ GLDrawFunctor* functor = new GLDrawFunctor(wvInstance,
+ &android::WebView::drawGL, viewRect, scale, extras);
+ wvInstance->setFunctor((Functor*) functor);
-static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView) {
- WebView *wvInstance = reinterpret_cast<WebView*>(nativeView);
- if (!wvInstance)
- return 0;
+ WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect);
+ functor->updateViewRect(webViewRect);
- return (jint) wvInstance->getFunctor();
+ return (jint)functor;
}
-static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView,
- jobject jinvscreenrect, jobject jscreenrect,
- jobject jvisiblecontentrect, jfloat scale) {
- WebView *wvInstance = reinterpret_cast<WebView*>(nativeView);
+static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect,
+ jobject jviewrect, jobject jvisiblerect) {
+ WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
if (wvInstance) {
GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor();
if (functor) {
- WebCore::IntRect invScreenRect = jrect_to_webrect(env, jinvscreenrect);
- functor->updateInvScreenRect(invScreenRect);
-
- SkRect visibleContentRect = jrectf_to_rect(env, jvisiblecontentrect);
- wvInstance->setVisibleContentRect(visibleContentRect);
+ WebCore::IntRect viewRect = jrect_to_webrect(env, jrect);
+ functor->updateRect(viewRect);
- WebCore::IntRect screenRect = jrect_to_webrect(env, jscreenrect);
- functor->updateScreenRect(screenRect);
+ SkRect visibleRect = jrectf_to_rect(env, jvisiblerect);
+ wvInstance->setVisibleRect(visibleRect);
- functor->updateScale(scale);
+ WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect);
+ functor->updateViewRect(webViewRect);
}
}
}
@@ -941,29 +1915,56 @@ static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj, jint native
{
// only call in software rendering, initialize and evaluate animations
#if USE(ACCELERATED_COMPOSITING)
- BaseLayerAndroid* baseLayer = reinterpret_cast<WebView*>(nativeView)->getBaseLayer();
- if (baseLayer) {
- baseLayer->initAnimations();
- return baseLayer->evaluateAnimations();
+ LayerAndroid* root = ((WebView*)nativeView)->compositeRoot();
+ if (root) {
+ root->initAnimations();
+ return root->evaluateAnimations();
}
#endif
return false;
}
-static bool nativeSetBaseLayer(JNIEnv *env, jobject obj, jint nativeView, jint layer,
- jboolean showVisualIndicator,
- jboolean isPictureAfterFirstLayout,
- jint scrollingLayer)
+static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer, jobject inval,
+ jboolean showVisualIndicator,
+ jboolean isPictureAfterFirstLayout,
+ jboolean registerPageSwapCallback)
{
BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
- return reinterpret_cast<WebView*>(nativeView)->setBaseLayer(layerImpl, showVisualIndicator,
- isPictureAfterFirstLayout,
- scrollingLayer);
+ SkRegion invalRegion;
+ if (inval)
+ invalRegion = *GraphicsJNI::getNativeRegion(env, inval);
+ GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, invalRegion, showVisualIndicator,
+ isPictureAfterFirstLayout,
+ registerPageSwapCallback);
+}
+
+static void nativeGetTextSelectionRegion(JNIEnv *env, jobject obj, jint view,
+ jobject region)
+{
+ if (!region)
+ return;
+ SkRegion* nregion = GraphicsJNI::getNativeRegion(env, region);
+ ((WebView*)view)->getTextSelectionRegion(nregion);
+}
+
+static void nativeGetSelectionHandles(JNIEnv *env, jobject obj, jint view,
+ jintArray arr)
+{
+ int handles[4];
+ ((WebView*)view)->getTextSelectionHandles(handles);
+ env->SetIntArrayRegion(arr, 0, 4, handles);
+ checkException(env);
+}
+
+static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->getBaseLayer();
}
-static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj, jint nativeView)
+static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
{
- return reinterpret_cast<WebView*>(nativeView)->getBaseLayer();
+ PictureSet* set = reinterpret_cast<PictureSet*>(content);
+ GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
}
static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
@@ -977,40 +1978,549 @@ static bool nativeHasContent(JNIEnv *env, jobject obj)
return GET_NATIVE_VIEW(env, obj)->hasContent();
}
+static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WTF::String uri = view->imageURI(x, y);
+ return wtfStringToJstring(env, uri);
+}
+
+static jint nativeFocusCandidateFramePointer(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ const CachedNode* cursor = root->currentCursor(&frame);
+ if (!cursor || !cursor->wantsKeyEvents())
+ (void) root->currentFocus(&frame);
+ return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
+}
+
+static bool nativeFocusCandidateIsPassword(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input && input->getType() == CachedInput::PASSWORD;
+}
+
+static bool nativeFocusCandidateIsRtlText(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->isRtlText() : false;
+}
+
+static bool nativeFocusCandidateIsTextInput(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ return node ? node->isTextInput() : false;
+}
+
+static jint nativeFocusCandidateMaxLength(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->maxLength() : false;
+}
+
+static jint nativeFocusCandidateIsAutoComplete(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->autoComplete() : false;
+}
+
+static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return 0;
+ const WTF::String& name = input->name();
+ return wtfStringToJstring(env, name);
+}
+
+static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getFocusCandidate(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->originalAbsoluteBounds()
+ : WebCore::IntRect(0, 0, 0, 0);
+ // Inset the rect by 1 unit, so that the focus candidate's border can still
+ // be seen behind it.
+ return createJavaRect(env, bounds.x(), bounds.y(),
+ bounds.maxX(), bounds.maxY());
+}
+
+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)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ return reinterpret_cast<int>(node ? node->nodePointer() : 0);
+}
+
+static jint nativeFocusCandidateIsSpellcheck(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->spellcheck() : false;
+}
+
+static jobject nativeFocusCandidateText(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ if (!node)
+ return 0;
+ WTF::String value = node->getExport();
+ return wtfStringToJstring(env, value);
+}
+
+static int nativeFocusCandidateLineHeight(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->lineHeight() : 0;
+}
+
+static jfloat nativeFocusCandidateTextSize(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->textSize() : 0.f;
+}
+
+static int nativeFocusCandidateType(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return CachedInput::NONE;
+
+ if (input->isTextArea())
+ return CachedInput::TEXT_AREA;
+
+ return input->getType();
+}
+
+static int nativeFocusCandidateLayerId(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame = 0;
+ const CachedNode* node = getFocusNode(env, obj, &frame);
+ if (!node || !frame)
+ return -1;
+ const CachedLayer* layer = frame->layer(node);
+ if (!layer)
+ return -1;
+ return layer->uniqueId();
+}
+
+static bool nativeFocusIsPlugin(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusNode(env, obj);
+ return node ? node->isPlugin() : false;
+}
+
+static jobject nativeFocusNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getFocusNode(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ return createJavaRect(env, bounds.x(), bounds.y(),
+ bounds.maxX(), bounds.maxY());
+}
+
+static jint nativeFocusNodePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusNode(env, obj);
+ return node ? reinterpret_cast<int>(node->nodePointer()) : 0;
+}
+
+static bool nativeCursorWantsKeyEvents(JNIEnv* env, jobject jwebview) {
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->cursorWantsKeyEvents();
+}
+
+static void nativeHideCursor(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->hideCursor();
+}
+
+static void nativeInstrumentReport(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::reportNow();
+#endif
+}
+
+static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WebCore::IntRect rect = jrect_to_webrect(env, jrect);
+ view->selectBestAt(rect);
+}
+
+static void nativeSelectAt(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WebCore::IntRect rect = IntRect(x, y , 1, 1);
+ view->selectBestAt(rect);
+ if (view->hasCursorNode())
+ view->showCursorUntimed();
+}
+
+static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer)
+{
+ SkRect r;
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* layer = (LayerAndroid*) jlayer;
+ r = layer->bounds();
+#else
+ r.setEmpty();
+#endif
+ SkIRect irect;
+ r.round(&irect);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
+ irect.fRight, irect.fBottom);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect)
+{
+ SkIRect irect = jrect_to_webrect(env, jrect);
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
+ if (root) {
+ SkRect rect;
+ rect.set(irect);
+ rect = root->subtractLayers(rect);
+ rect.round(&irect);
+ }
+#endif
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
+ irect.fRight, irect.fBottom);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jint nativeTextGeneration(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->textGeneration() : 0;
+}
+
+static bool nativePointInNavCache(JNIEnv *env, jobject obj,
+ int x, int y, int slop)
+{
+ return GET_NATIVE_VIEW(env, obj)->pointInNavCache(x, y, slop);
+}
+
+static bool nativeMotionUp(JNIEnv *env, jobject obj,
+ int x, int y, int slop)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->motionUp(x, y, slop);
+}
+
+static bool nativeHasCursorNode(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasCursorNode();
+}
+
+static bool nativeHasFocusNode(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasFocusNode();
+}
+
+static bool nativeMoveCursor(JNIEnv *env, jobject obj,
+ int key, int count, bool ignoreScroll)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ DBG_NAV_LOGD("env=%p obj=%p view=%p", env, obj, view);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->moveCursor(key, count, ignoreScroll);
+}
+
+static void nativeSetFindIsUp(JNIEnv *env, jobject obj, jboolean isUp)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setFindIsUp(isUp);
+}
+
+static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setFindIsEmpty();
+}
+
+static void nativeShowCursorTimed(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->showCursorTimed();
+}
+
static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure)
{
WebView* view = GET_NATIVE_VIEW(env, obj);
- ALOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
+ LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
view->setHeightCanMeasure(measure);
}
-static void nativeDestroy(JNIEnv *env, jobject obj, jint ptr)
+static jobject nativeGetCursorRingBounds(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find Rect class!");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ LOG_ASSERT(init, "Could not find constructor for Rect");
+ WebCore::IntRect webRect;
+ view->cursorRingBounds(&webRect);
+ jobject rect = env->NewObject(rectClass, init, webRect.x(),
+ webRect.y(), webRect.maxX(), webRect.maxY());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
+ jstring findUpper, jboolean sameAsLastSearch)
+{
+ // If one or the other is null, do not search.
+ if (!(findLower && findUpper))
+ return 0;
+ // Obtain the characters for both the lower case string and the upper case
+ // string representing the same word.
+ const jchar* findLowerChars = env->GetStringChars(findLower, 0);
+ const jchar* findUpperChars = env->GetStringChars(findUpper, 0);
+ // If one or the other is null, do not search.
+ if (!(findLowerChars && findUpperChars)) {
+ if (findLowerChars)
+ env->ReleaseStringChars(findLower, findLowerChars);
+ if (findUpperChars)
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindAll");
+ CachedRoot* root = view->getFrameCache(WebView::AllowNewer);
+ if (!root) {
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ int length = env->GetStringLength(findLower);
+ // If the lengths of the strings do not match, then they are not the same
+ // word, so do not search.
+ if (!length || env->GetStringLength(findUpper) != length) {
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ int width = root->documentWidth();
+ int height = root->documentHeight();
+ // Create a FindCanvas, which allows us to fake draw into it so we can
+ // figure out where our search string is rendered (and how many times).
+ FindCanvas canvas(width, height, (const UChar*) findLowerChars,
+ (const UChar*) findUpperChars, length << 1);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ canvas.setBitmapDevice(bitmap);
+ root->draw(canvas);
+ WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
+ // With setMatches, the WebView takes ownership of matches
+ view->setMatches(matches, sameAsLastSearch);
+
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return canvas.found();
+}
+
+static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
{
- WebView* view = reinterpret_cast<WebView*>(ptr);
- ALOGD("nativeDestroy view: %p", view);
- ALOG_ASSERT(view, "view not set in nativeDestroy");
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindNext");
+ 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);
+ LOG_ASSERT(view, "view not set in nativeUpdateCachedTextfield");
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return;
+ const CachedNode* cachedFocusNode = root->currentFocus();
+ if (!cachedFocusNode || !cachedFocusNode->isTextInput())
+ return;
+ WTF::String webcoreString = jstringToWtfString(env, updatedText);
+ (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString);
+ root->setTextGeneration(generation);
+ checkException(env);
+}
+
+static jint nativeGetBlockLeftEdge(JNIEnv *env, jobject obj, jint x, jint y,
+ jfloat scale)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ if (!view)
+ return -1;
+ return view->getBlockLeftEdge(x, y, scale);
+}
+
+static void nativeDestroy(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOGD("nativeDestroy view: %p", view);
+ LOG_ASSERT(view, "view not set in nativeDestroy");
delete view;
}
-static void nativeStopGL(JNIEnv *env, jobject obj, jint ptr)
+static void nativeStopGL(JNIEnv *env, jobject obj)
{
- if (ptr)
- reinterpret_cast<WebView*>(ptr)->stopGL();
+ GET_NATIVE_VIEW(env, obj)->stopGL();
+}
+
+static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return false;
+ const CachedNode* current = root->currentCursor();
+ if (!current || !current->isTextInput())
+ current = root->currentFocus();
+ if (!current || !current->isTextInput())
+ return false;
+ const CachedFrame* frame;
+ const CachedNode* next = root->nextTextField(current, &frame);
+ if (!next)
+ return false;
+ const WebCore::IntRect& bounds = next->bounds(frame);
+ root->rootHistory()->setMouseBounds(bounds);
+ view->getWebViewCore()->updateCursorBounds(root, frame, next);
+ view->showCursorUntimed();
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(next));
+ view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()),
+ static_cast<WebCore::Node*>(next->nodePointer()));
+ if (!next->isInLayer())
+ view->scrollRectOnScreen(bounds);
+ view->getWebViewCore()->m_moveGeneration++;
+ return true;
+}
+
+static int nativeMoveGeneration(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view)
+ return 0;
+ return view->moveGeneration();
+}
+
+static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
+}
+
+static void nativeResetSelection(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->resetSelection();
+}
+
+static jobject nativeSelectableText(JNIEnv* env, jobject obj)
+{
+ IntPoint pos = GET_NATIVE_VIEW(env, obj)->selectableText();
+ jclass pointClass = env->FindClass("android/graphics/Point");
+ jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
+ jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
+ env->DeleteLocalRef(pointClass);
+ return point;
+}
+
+static void nativeSelectAll(JNIEnv* env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->selectAll();
+}
+
+static void nativeSetExtendSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setExtendSelection();
+}
+
+static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->startSelection(x, y);
+}
+
+static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y);
+}
+
+static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ GET_NATIVE_VIEW(env, obj)->extendSelection(x, y);
}
static jobject nativeGetSelection(JNIEnv *env, jobject obj)
{
WebView* view = GET_NATIVE_VIEW(env, obj);
- ALOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
String selection = view->getSelection();
return wtfStringToJstring(env, selection);
}
-static void nativeDiscardAllTextures(JNIEnv *env, jobject obj)
+static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
+}
+
+static jint nativeSelectionX(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionX();
+}
+
+static jint nativeSelectionY(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionY();
+}
+
+static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jint nativeView,
+ jboolean set, jfloat scale, jint x, jint y)
+{
+ ((WebView*)nativeView)->setSelectionPointer(set, scale, x, y);
+}
+
+static void nativeRegisterPageSwapCallback(JNIEnv *env, jobject obj)
{
- //discard all textures for debugging/test purposes, but not gl backing memory
- bool allTextures = true, deleteGLTextures = false;
- TilesManager::instance()->discardTextures(allTextures, deleteGLTextures);
+ GET_NATIVE_VIEW(env, obj)->registerPageSwapCallback();
}
static void nativeTileProfilingStart(JNIEnv *env, jobject obj)
@@ -1071,14 +2581,15 @@ static void dumpToFile(const char text[], void* file) {
}
#endif
-// Return true to view invalidate WebView
static bool nativeSetProperty(JNIEnv *env, jobject obj, jstring jkey, jstring jvalue)
{
WTF::String key = jstringToWtfString(env, jkey);
WTF::String value = jstringToWtfString(env, jvalue);
if (key == "inverted") {
- bool shouldInvert = (value == "true");
- TilesManager::instance()->setInvertedScreen(shouldInvert);
+ if (value == "true")
+ TilesManager::instance()->setInvertedScreen(true);
+ else
+ TilesManager::instance()->setInvertedScreen(false);
return true;
}
else if (key == "inverted_contrast") {
@@ -1089,47 +2600,25 @@ static bool nativeSetProperty(JNIEnv *env, jobject obj, jstring jkey, jstring jv
else if (key == "enable_cpu_upload_path") {
TilesManager::instance()->transferQueue()->setTextureUploadType(
value == "true" ? CpuUpload : GpuUpload);
+ return true;
}
else if (key == "use_minimal_memory") {
TilesManager::instance()->setUseMinimalMemory(value == "true");
- }
- else if (key == "use_double_buffering") {
- TilesManager::instance()->setUseDoubleBuffering(value == "true");
- }
- else if (key == "tree_updates") {
- TilesManager::instance()->clearContentUpdates();
+ return true;
}
return false;
}
-static jstring nativeGetProperty(JNIEnv *env, jobject obj, jstring jkey)
+static jstring nativeGetProperty(JNIEnv *env, jobject obj, jstring key)
{
- WTF::String key = jstringToWtfString(env, jkey);
- if (key == "tree_updates") {
- int updates = TilesManager::instance()->getContentUpdates();
- WTF::String wtfUpdates = WTF::String::number(updates);
- return wtfStringToJstring(env, wtfUpdates);
- }
return 0;
}
static void nativeOnTrimMemory(JNIEnv *env, jobject obj, jint level)
{
if (TilesManager::hardwareAccelerationEnabled()) {
- // When we got TRIM_MEMORY_MODERATE or TRIM_MEMORY_COMPLETE, we should
- // make sure the transfer queue is empty and then abandon the Surface
- // Texture to avoid ANR b/c framework may destroy the EGL context.
- // Refer to WindowManagerImpl.java for conditions we followed.
- TilesManager* tilesManager = TilesManager::instance();
- if ((level >= TRIM_MEMORY_MODERATE
- && !tilesManager->highEndGfx())
- || level >= TRIM_MEMORY_COMPLETE) {
- ALOGD("OnTrimMemory with EGL Context %p", eglGetCurrentContext());
- tilesManager->cleanupGLResources();
- }
-
- bool freeAllTextures = (level > TRIM_MEMORY_UI_HIDDEN), glTextures = true;
- tilesManager->discardTextures(freeAllTextures, glTextures);
+ bool freeAllTextures = (level > TRIM_MEMORY_UI_HIDDEN);
+ TilesManager::instance()->deallocateTextures(freeAllTextures);
}
}
@@ -1137,7 +2626,7 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
{
#ifdef ANDROID_DUMP_DISPLAY_TREE
WebView* view = GET_NATIVE_VIEW(env, jwebview);
- ALOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
if (view && view->getWebViewCore()) {
FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w");
@@ -1154,17 +2643,17 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
SkDumpCanvas canvas(&dumper);
// this will playback the picture into the canvas, which will
// spew its contents to the dumper
- view->draw(&canvas, 0, WebView::DrawExtrasNone);
+ view->draw(&canvas, 0, 0, false);
// we're done with the file now
fwrite("\n", 1, 1, file);
fclose(file);
}
#if USE(ACCELERATED_COMPOSITING)
- const LayerAndroid* baseLayer = view->getBaseLayer();
- if (baseLayer) {
+ const LayerAndroid* rootLayer = view->compositeRoot();
+ if (rootLayer) {
FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
if (file) {
- baseLayer->dumpLayers(file, 0);
+ rootLayer->dumpLayers(file, 0);
fclose(file);
}
}
@@ -1173,13 +2662,13 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
#endif
}
-static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint nativeView,
- jint x, jint y, jobject rect, jobject bounds)
+static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y,
+ jobject rect, jobject bounds)
{
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- ALOG_ASSERT(webview, "webview not set in %s", __FUNCTION__);
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
SkIRect nativeRect, nativeBounds;
- int id = webview->scrollableLayer(x, y, &nativeRect, &nativeBounds);
+ int id = view->scrollableLayer(x, y, &nativeRect, &nativeBounds);
if (rect)
GraphicsJNI::irect_to_jrect(nativeRect, env, rect);
if (bounds)
@@ -1187,18 +2676,18 @@ static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint nativeView,
return id;
}
-static bool nativeScrollLayer(JNIEnv* env, jobject obj,
- jint nativeView, jint layerId, jint x, jint y)
+static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint layerId, jint x,
+ jint y)
{
#if ENABLE(ANDROID_OVERFLOW_SCROLL)
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- webview->scrollLayer(layerId, x, y);
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ view->scrollLayer(layerId, x, y);
//TODO: the below only needed for the SW rendering path
- LayerAndroid* baseLayer = webview->getBaseLayer();
- if (!baseLayer)
+ LayerAndroid* root = view->compositeRoot();
+ if (!root)
return false;
- LayerAndroid* layer = baseLayer->findById(layerId);
+ LayerAndroid* layer = root->findById(layerId);
if (!layer || !layer->contentIsScrollable())
return false;
return static_cast<ScrollableLayerAndroid*>(layer)->scrollTo(x, y);
@@ -1208,10 +2697,9 @@ static bool nativeScrollLayer(JNIEnv* env, jobject obj,
static void nativeSetIsScrolling(JNIEnv* env, jobject jwebview, jboolean isScrolling)
{
- // TODO: Pass in the native pointer instead
WebView* view = GET_NATIVE_VIEW(env, jwebview);
- if (view)
- view->setIsScrolling(isScrolling);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setIsScrolling(isScrolling);
}
static void nativeUseHardwareAccelSkia(JNIEnv*, jobject, jboolean enabled)
@@ -1219,9 +2707,9 @@ static void nativeUseHardwareAccelSkia(JNIEnv*, jobject, jboolean enabled)
BaseRenderer::setCurrentRendererType(enabled ? BaseRenderer::Ganesh : BaseRenderer::Raster);
}
-static int nativeGetBackgroundColor(JNIEnv* env, jobject obj, jint nativeView)
+static int nativeGetBackgroundColor(JNIEnv* env, jobject obj)
{
- WebView* view = reinterpret_cast<WebView*>(nativeView);
+ WebView* view = GET_NATIVE_VIEW(env, obj);
BaseLayerAndroid* baseLayer = view->getBaseLayer();
if (baseLayer) {
WebCore::Color color = baseLayer->getBackgroundColor();
@@ -1235,93 +2723,179 @@ static int nativeGetBackgroundColor(JNIEnv* env, jobject obj, jint nativeView)
static void nativeSetPauseDrawing(JNIEnv *env, jobject obj, jint nativeView,
jboolean pause)
{
- reinterpret_cast<WebView*>(nativeView)->setDrawingPaused(pause);
-}
-
-static void nativeSetTextSelection(JNIEnv *env, jobject obj, jint nativeView,
- jint selectionPtr)
-{
- SelectText* selection = reinterpret_cast<SelectText*>(selectionPtr);
- reinterpret_cast<WebView*>(nativeView)->setTextSelection(selection);
-}
-
-static jint nativeGetHandleLayerId(JNIEnv *env, jobject obj, jint nativeView,
- jint handleIndex, jobject cursorPoint,
- jobject textQuad)
-{
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- SkIPoint nativePoint;
- FloatQuad nativeTextQuad;
- int layerId = webview->getHandleLayerId((SelectText::HandleId) handleIndex,
- nativePoint, nativeTextQuad);
- if (cursorPoint)
- GraphicsJNI::ipoint_to_jpoint(nativePoint, env, cursorPoint);
- if (textQuad)
- webview->floatQuadToQuadF(env, nativeTextQuad, textQuad);
- return layerId;
-}
-
-static void nativeMapLayerRect(JNIEnv *env, jobject obj, jint nativeView,
- jint layerId, jobject rect)
-{
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- SkIRect nativeRect;
- GraphicsJNI::jrect_to_irect(env, rect, &nativeRect);
- webview->mapLayerRect(layerId, nativeRect);
- GraphicsJNI::irect_to_jrect(nativeRect, env, rect);
-}
-
-static jint nativeSetHwAccelerated(JNIEnv *env, jobject obj, jint nativeView,
- jboolean hwAccelerated)
-{
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- return webview->setHwAccelerated(hwAccelerated);
-}
-
-static void nativeFindMaxVisibleRect(JNIEnv *env, jobject obj, jint nativeView,
- jint movingLayerId, jobject visibleContentRect)
-{
- WebView* webview = reinterpret_cast<WebView*>(nativeView);
- SkIRect nativeRect;
- GraphicsJNI::jrect_to_irect(env, visibleContentRect, &nativeRect);
- webview->findMaxVisibleRect(movingLayerId, nativeRect);
- GraphicsJNI::irect_to_jrect(nativeRect, env, visibleContentRect);
+ ((WebView*)nativeView)->m_isDrawingPaused = pause;
}
/*
* JNI registration
*/
static JNINativeMethod gJavaWebViewMethods[] = {
+ { "nativeCacheHitFramePointer", "()I",
+ (void*) nativeCacheHitFramePointer },
+ { "nativeCacheHitIsPlugin", "()Z",
+ (void*) nativeCacheHitIsPlugin },
+ { "nativeCacheHitNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeCacheHitNodeBounds },
+ { "nativeCacheHitNodePointer", "()I",
+ (void*) nativeCacheHitNodePointer },
+ { "nativeClearCursor", "()V",
+ (void*) nativeClearCursor },
{ "nativeCreate", "(ILjava/lang/String;Z)V",
(void*) nativeCreate },
- { "nativeDestroy", "(I)V",
+ { "nativeCursorFramePointer", "()I",
+ (void*) nativeCursorFramePointer },
+ { "nativePageShouldHandleShiftAndArrows", "()Z",
+ (void*) nativePageShouldHandleShiftAndArrows },
+ { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeCursorNodeBounds },
+ { "nativeCursorNodePointer", "()I",
+ (void*) nativeCursorNodePointer },
+ { "nativeCursorIntersects", "(Landroid/graphics/Rect;)Z",
+ (void*) nativeCursorIntersects },
+ { "nativeCursorIsAnchor", "()Z",
+ (void*) nativeCursorIsAnchor },
+ { "nativeCursorIsTextInput", "()Z",
+ (void*) nativeCursorIsTextInput },
+ { "nativeCursorPosition", "()Landroid/graphics/Point;",
+ (void*) nativeCursorPosition },
+ { "nativeCursorText", "()Ljava/lang/String;",
+ (void*) nativeCursorText },
+ { "nativeCursorWantsKeyEvents", "()Z",
+ (void*)nativeCursorWantsKeyEvents },
+ { "nativeDebugDump", "()V",
+ (void*) nativeDebugDump },
+ { "nativeDestroy", "()V",
(void*) nativeDestroy },
- { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;II)V",
+ { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;IIZ)I",
(void*) nativeDraw },
- { "nativeCreateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I",
- (void*) nativeCreateDrawGLFunction },
- { "nativeGetDrawGLFunction", "(I)I",
+ { "nativeGetDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I",
(void*) nativeGetDrawGLFunction },
- { "nativeUpdateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;F)V",
+ { "nativeUpdateDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;)V",
(void*) nativeUpdateDrawGLFunction },
{ "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
(void*) nativeDumpDisplayTree },
{ "nativeEvaluateLayersAnimations", "(I)Z",
(void*) nativeEvaluateLayersAnimations },
+ { "nativeExtendSelection", "(II)V",
+ (void*) nativeExtendSelection },
+ { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;Z)I",
+ (void*) nativeFindAll },
+ { "nativeFindNext", "(Z)V",
+ (void*) nativeFindNext },
+ { "nativeFindIndex", "()I",
+ (void*) nativeFindIndex},
+ { "nativeFocusCandidateFramePointer", "()I",
+ (void*) nativeFocusCandidateFramePointer },
+ { "nativeFocusCandidateHasNextTextfield", "()Z",
+ (void*) focusCandidateHasNextTextfield },
+ { "nativeFocusCandidateIsPassword", "()Z",
+ (void*) nativeFocusCandidateIsPassword },
+ { "nativeFocusCandidateIsRtlText", "()Z",
+ (void*) nativeFocusCandidateIsRtlText },
+ { "nativeFocusCandidateIsTextInput", "()Z",
+ (void*) nativeFocusCandidateIsTextInput },
+ { "nativeFocusCandidateLineHeight", "()I",
+ (void*) nativeFocusCandidateLineHeight },
+ { "nativeFocusCandidateMaxLength", "()I",
+ (void*) nativeFocusCandidateMaxLength },
+ { "nativeFocusCandidateIsAutoComplete", "()Z",
+ (void*) nativeFocusCandidateIsAutoComplete },
+ { "nativeFocusCandidateIsSpellcheck", "()Z",
+ (void*) nativeFocusCandidateIsSpellcheck },
+ { "nativeFocusCandidateName", "()Ljava/lang/String;",
+ (void*) nativeFocusCandidateName },
+ { "nativeFocusCandidateNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusCandidateNodeBounds },
+ { "nativeFocusCandidatePaddingRect", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusCandidatePaddingRect },
+ { "nativeFocusCandidatePointer", "()I",
+ (void*) nativeFocusCandidatePointer },
+ { "nativeFocusCandidateText", "()Ljava/lang/String;",
+ (void*) nativeFocusCandidateText },
+ { "nativeFocusCandidateTextSize", "()F",
+ (void*) nativeFocusCandidateTextSize },
+ { "nativeFocusCandidateType", "()I",
+ (void*) nativeFocusCandidateType },
+ { "nativeFocusCandidateLayerId", "()I",
+ (void*) nativeFocusCandidateLayerId },
+ { "nativeFocusIsPlugin", "()Z",
+ (void*) nativeFocusIsPlugin },
+ { "nativeFocusNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusNodeBounds },
+ { "nativeFocusNodePointer", "()I",
+ (void*) nativeFocusNodePointer },
+ { "nativeGetCursorRingBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeGetCursorRingBounds },
{ "nativeGetSelection", "()Ljava/lang/String;",
(void*) nativeGetSelection },
+ { "nativeHasCursorNode", "()Z",
+ (void*) nativeHasCursorNode },
+ { "nativeHasFocusNode", "()Z",
+ (void*) nativeHasFocusNode },
+ { "nativeHideCursor", "()V",
+ (void*) nativeHideCursor },
+ { "nativeHitSelection", "(II)Z",
+ (void*) nativeHitSelection },
+ { "nativeImageURI", "(II)Ljava/lang/String;",
+ (void*) nativeImageURI },
+ { "nativeInstrumentReport", "()V",
+ (void*) nativeInstrumentReport },
+ { "nativeLayerBounds", "(I)Landroid/graphics/Rect;",
+ (void*) nativeLayerBounds },
+ { "nativeMotionUp", "(III)Z",
+ (void*) nativeMotionUp },
+ { "nativeMoveCursor", "(IIZ)Z",
+ (void*) nativeMoveCursor },
+ { "nativeMoveCursorToNextTextInput", "()Z",
+ (void*) nativeMoveCursorToNextTextInput },
+ { "nativeMoveGeneration", "()I",
+ (void*) nativeMoveGeneration },
+ { "nativeMoveSelection", "(II)V",
+ (void*) nativeMoveSelection },
+ { "nativePointInNavCache", "(III)Z",
+ (void*) nativePointInNavCache },
+ { "nativeResetSelection", "()V",
+ (void*) nativeResetSelection },
+ { "nativeSelectableText", "()Landroid/graphics/Point;",
+ (void*) nativeSelectableText },
+ { "nativeSelectAll", "()V",
+ (void*) nativeSelectAll },
+ { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
+ (void*) nativeSelectBestAt },
+ { "nativeSelectAt", "(II)V",
+ (void*) nativeSelectAt },
+ { "nativeSelectionX", "()I",
+ (void*) nativeSelectionX },
+ { "nativeSelectionY", "()I",
+ (void*) nativeSelectionY },
+ { "nativeSetExtendSelection", "()V",
+ (void*) nativeSetExtendSelection },
+ { "nativeSetFindIsEmpty", "()V",
+ (void*) nativeSetFindIsEmpty },
+ { "nativeSetFindIsUp", "(Z)V",
+ (void*) nativeSetFindIsUp },
{ "nativeSetHeightCanMeasure", "(Z)V",
(void*) nativeSetHeightCanMeasure },
- { "nativeSetBaseLayer", "(IIZZI)Z",
+ { "nativeSetBaseLayer", "(ILandroid/graphics/Region;ZZZ)V",
(void*) nativeSetBaseLayer },
- { "nativeGetBaseLayer", "(I)I",
+ { "nativeGetTextSelectionRegion", "(ILandroid/graphics/Region;)V",
+ (void*) nativeGetTextSelectionRegion },
+ { "nativeGetSelectionHandles", "(I[I)V",
+ (void*) nativeGetSelectionHandles },
+ { "nativeGetBaseLayer", "()I",
(void*) nativeGetBaseLayer },
+ { "nativeReplaceBaseContent", "(I)V",
+ (void*) nativeReplaceBaseContent },
{ "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
(void*) nativeCopyBaseContentToPicture },
{ "nativeHasContent", "()Z",
(void*) nativeHasContent },
- { "nativeDiscardAllTextures", "()V",
- (void*) nativeDiscardAllTextures },
+ { "nativeSetSelectionPointer", "(IZFII)V",
+ (void*) nativeSetSelectionPointer },
+ { "nativeShowCursorTimed", "()V",
+ (void*) nativeShowCursorTimed },
+ { "nativeRegisterPageSwapCallback", "()V",
+ (void*) nativeRegisterPageSwapCallback },
{ "nativeTileProfilingStart", "()V",
(void*) nativeTileProfilingStart },
{ "nativeTileProfilingStop", "()F",
@@ -1336,17 +2910,29 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeTileProfilingGetInt },
{ "nativeTileProfilingGetFloat", "(IILjava/lang/String;)F",
(void*) nativeTileProfilingGetFloat },
- { "nativeStopGL", "(I)V",
+ { "nativeStartSelection", "(II)Z",
+ (void*) nativeStartSelection },
+ { "nativeStopGL", "()V",
(void*) nativeStopGL },
- { "nativeScrollableLayer", "(IIILandroid/graphics/Rect;Landroid/graphics/Rect;)I",
+ { "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 },
+ { "nativeScrollableLayer", "(IILandroid/graphics/Rect;Landroid/graphics/Rect;)I",
(void*) nativeScrollableLayer },
- { "nativeScrollLayer", "(IIII)Z",
+ { "nativeScrollLayer", "(III)Z",
(void*) nativeScrollLayer },
{ "nativeSetIsScrolling", "(Z)V",
(void*) nativeSetIsScrolling },
{ "nativeUseHardwareAccelSkia", "(Z)V",
(void*) nativeUseHardwareAccelSkia },
- { "nativeGetBackgroundColor", "(I)I",
+ { "nativeGetBackgroundColor", "()I",
(void*) nativeGetBackgroundColor },
{ "nativeSetProperty", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void*) nativeSetProperty },
@@ -1356,27 +2942,17 @@ static JNINativeMethod gJavaWebViewMethods[] = {
(void*) nativeOnTrimMemory },
{ "nativeSetPauseDrawing", "(IZ)V",
(void*) nativeSetPauseDrawing },
- { "nativeSetTextSelection", "(II)V",
- (void*) nativeSetTextSelection },
- { "nativeGetHandleLayerId", "(IILandroid/graphics/Point;Landroid/webkit/QuadF;)I",
- (void*) nativeGetHandleLayerId },
- { "nativeMapLayerRect", "(IILandroid/graphics/Rect;)V",
- (void*) nativeMapLayerRect },
- { "nativeSetHwAccelerated", "(IZ)I",
- (void*) nativeSetHwAccelerated },
- { "nativeFindMaxVisibleRect", "(IILandroid/graphics/Rect;)V",
- (void*) nativeFindMaxVisibleRect },
};
int registerWebView(JNIEnv* env)
{
- jclass clazz = env->FindClass("android/webkit/WebViewClassic");
- ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebViewClassic");
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebView");
gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I");
- ALOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebViewClassic.mNativeClass");
+ LOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebView.mNativeClass");
env->DeleteLocalRef(clazz);
- return jniRegisterNativeMethods(env, "android/webkit/WebViewClassic", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
+ return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
}
} // namespace android
diff --git a/Source/WebKit/android/plugins/ANPSoundInterface.cpp b/Source/WebKit/android/plugins/ANPSoundInterface.cpp
index 9d19443..c238872 100644
--- a/Source/WebKit/android/plugins/ANPSoundInterface.cpp
+++ b/Source/WebKit/android/plugins/ANPSoundInterface.cpp
@@ -38,7 +38,7 @@ struct ANPAudioTrack {
android::AudioTrack* mTrack;
};
-static ANPSampleFormat toANPFormat(audio_format_t fm) {
+static ANPSampleFormat toANPFormat(int fm) {
switch (fm) {
case AUDIO_FORMAT_PCM_16_BIT:
return kPCM16Bit_ANPSampleFormat;
@@ -49,7 +49,7 @@ static ANPSampleFormat toANPFormat(audio_format_t fm) {
}
}
-static audio_format_t fromANPFormat(ANPSampleFormat fm) {
+static int fromANPFormat(ANPSampleFormat fm) {
switch (fm) {
case kPCM16Bit_ANPSampleFormat:
return AUDIO_FORMAT_PCM_16_BIT;
@@ -71,7 +71,7 @@ static void callbackProc(int event, void* user, void* info) {
src = reinterpret_cast<android::AudioTrack::Buffer*>(info);
dst.bufferData = src->raw;
dst.channelCount = src->channelCount;
- dst.format = toANPFormat((audio_format_t) src->format);
+ dst.format = toANPFormat(src->format);
dst.size = src->size;
track->mProc(kMoreData_ANPAudioEvent, track->mUser, &dst);
// return the updated size field
@@ -102,7 +102,7 @@ static ANPAudioTrack* ANPCreateTrack(uint32_t sampleRate,
fromANPFormat(format),
(channelCount > 1) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
0, // frameCount
- (audio_output_flags_t) 0, // AUDIO_OUTPUT_FLAG_NONE,
+ 0, // flags
callbackProc,
track,
0);
diff --git a/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp
index 92dbbcd..4b99b31 100644
--- a/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp
+++ b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp
@@ -32,7 +32,7 @@
#include "SkANP.h"
#include "android_graphics.h"
#include <JNIUtility.h>
-#include <gui/Surface.h>
+#include <surfaceflinger/Surface.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <utils/RefBase.h>
@@ -47,7 +47,7 @@ static struct ANPSurfaceInterfaceJavaGlue {
jfieldID surfacePointer;
} gSurfaceJavaGlue;
-static inline sp<android::Surface> getSurface(JNIEnv* env, jobject view) {
+static inline sp<Surface> getSurface(JNIEnv* env, jobject view) {
if (!env || !view) {
return NULL;
}
@@ -80,7 +80,7 @@ static inline sp<android::Surface> getSurface(JNIEnv* env, jobject view) {
env->DeleteLocalRef(holder);
env->DeleteLocalRef(surface);
- return sp<android::Surface>((android::Surface*) surfacePointer);
+ return sp<Surface>((Surface*) surfacePointer);
}
static inline ANPBitmapFormat convertPixelFormat(PixelFormat format) {
@@ -96,9 +96,9 @@ static bool anp_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRec
return false;
}
- sp<android::Surface> surface = getSurface(env, surfaceView);
+ sp<Surface> surface = getSurface(env, surfaceView);
- if (!bitmap || !android::Surface::isValid(surface)) {
+ if (!bitmap || !Surface::isValid(surface)) {
return false;
}
@@ -112,7 +112,7 @@ static bool anp_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRec
dirtyRegion.set(Rect(0x3FFF, 0x3FFF));
}
- android::Surface::SurfaceInfo info;
+ Surface::SurfaceInfo info;
status_t err = surface->lock(&info, &dirtyRegion);
if (err < 0) {
return false;
@@ -150,9 +150,9 @@ static void anp_unlock(JNIEnv* env, jobject surfaceView) {
return;
}
- sp<android::Surface> surface = getSurface(env, surfaceView);
+ sp<Surface> surface = getSurface(env, surfaceView);
- if (!android::Surface::isValid(surface)) {
+ if (!Surface::isValid(surface)) {
return;
}
diff --git a/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp b/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp
index 09bb24e..fc98837 100644
--- a/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp
+++ b/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp
@@ -160,8 +160,7 @@ bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
if (model == kOpenGL_ANPDrawingModel && m_layer == 0) {
jobject webview = m_core->getWebViewJavaObject();
- AutoJObject webViewCore = m_core->getJavaObject();
- m_layer = new WebCore::MediaLayer(webview, webViewCore.get());
+ m_layer = new WebCore::MediaLayer(webview);
}
else if (model != kOpenGL_ANPDrawingModel && m_layer != 0) {
m_layer->unref();
@@ -597,9 +596,9 @@ void PluginWidgetAndroid::scrollToVisiblePluginRect() {
android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
#if DEBUG_VISIBLE_RECTS
PLUGIN_LOG("%s call scrollTo (%d,%d) to center (%d,%d)", __FUNCTION__,
- scrollDocX, scrollDocY, rectCenterX, rectCenterY);
+ scrollDocX, scrollDocX, rectCenterX, rectCenterY);
#endif
- core->scrollTo(scrollDocX, scrollDocY, true);
+ core->scrollTo(scrollDocX, scrollDocX, true);
}
void PluginWidgetAndroid::requestFullScreen() {
diff --git a/Source/WebKit/android/smoke/MessageThread.cpp b/Source/WebKit/android/smoke/MessageThread.cpp
index 97ab18c..48f2222 100644
--- a/Source/WebKit/android/smoke/MessageThread.cpp
+++ b/Source/WebKit/android/smoke/MessageThread.cpp
@@ -79,7 +79,7 @@ void MessageQueue::post(Message* message) {
AutoMutex lock(m_mutex);
double when = message->m_when;
- ALOG_ASSERT(when > 0, "Message time may not be 0");
+ LOG_ASSERT(when > 0, "Message time may not be 0");
list<Message*>::iterator it;
for (it = m_messages.begin(); it != m_messages.end(); ++it) {
diff --git a/Source/WebKit/android/wds/Command.cpp b/Source/WebKit/android/wds/Command.cpp
index 1a365e5..bd8536f 100644
--- a/Source/WebKit/android/wds/Command.cpp
+++ b/Source/WebKit/android/wds/Command.cpp
@@ -95,7 +95,7 @@ public:
virtual ~InternalCommand() { delete m_connection; }
void doCommand() const {
- ALOGD("Executing command '%s' (%s)", m_name, m_description);
+ LOGD("Executing command '%s' (%s)", m_name, m_description);
if (!m_dispatch(m_frame, m_connection))
// XXX: Have useful failure messages
m_connection->write("EPIC FAIL!\n", 11);
diff --git a/Source/WebKit/android/wds/Connection.cpp b/Source/WebKit/android/wds/Connection.cpp
index 52193e5..d7e55ac 100644
--- a/Source/WebKit/android/wds/Connection.cpp
+++ b/Source/WebKit/android/wds/Connection.cpp
@@ -35,7 +35,7 @@
#if ENABLE(WDS)
#define MAX_CONNECTION_QUEUE 5
-#define log_errno(x) ALOGE("%s: %d", x, strerror(errno))
+#define log_errno(x) LOGE("%s: %d", x, strerror(errno))
namespace android {
diff --git a/Source/WebKit/android/wds/DebugServer.cpp b/Source/WebKit/android/wds/DebugServer.cpp
index 2fde6bd..f33a65b 100644
--- a/Source/WebKit/android/wds/DebugServer.cpp
+++ b/Source/WebKit/android/wds/DebugServer.cpp
@@ -42,7 +42,7 @@
#if ENABLE(WDS)
#define DEFAULT_PORT 9999
-#define log_errno(x) ALOGE("%s: %d", x, strerror(errno))
+#define log_errno(x) LOGE("%s: %d", x, strerror(errno))
namespace android {
@@ -70,7 +70,7 @@ DebugServer::DebugServer() {
char buf[PROPERTY_VALUE_MAX];
int ret = property_get("webcore.wds.enable", buf, NULL);
if (ret != -1 && strcmp(buf, "1") == 0) {
- ALOGD("WDS Enabled");
+ LOGD("WDS Enabled");
m_threadId = createThread(mainThread, this, "WDS");
}
// Initialize the available commands.
@@ -78,26 +78,26 @@ DebugServer::DebugServer() {
}
void DebugServer::start() {
- ALOGD("DebugServer thread started");
+ LOGD("DebugServer thread started");
ConnectionServer cs;
if (!cs.connect(DEFAULT_PORT)) {
- ALOGE("Failed to start the server socket connection");
+ LOGE("Failed to start the server socket connection");
return;
}
while (true ) {
- ALOGD("Waiting for incoming connections...");
+ LOGD("Waiting for incoming connections...");
Connection* conn = cs.accept();
if (!conn) {
log_errno("Failed to accept new connections");
return;
}
- ALOGD("...Connection established");
+ LOGD("...Connection established");
Command* c = Command::Find(conn);
if (!c) {
- ALOGE("Could not find matching command");
+ LOGE("Could not find matching command");
delete conn;
} else {
// Dispatch the command, it will handle cleaning up the connection
@@ -106,7 +106,7 @@ void DebugServer::start() {
}
}
- ALOGD("DebugServer thread finished");
+ LOGD("DebugServer thread finished");
}
} // end namespace WDS
diff --git a/Source/WebKit/android/wds/client/AdbConnection.cpp b/Source/WebKit/android/wds/client/AdbConnection.cpp
index 7d02ecc..465f9c3 100644
--- a/Source/WebKit/android/wds/client/AdbConnection.cpp
+++ b/Source/WebKit/android/wds/client/AdbConnection.cpp
@@ -78,7 +78,7 @@ bool AdbConnection::connect() {
bool AdbConnection::sendRequest(const char* fmt, ...) const {
if (m_fd == -1) {
- ALOGE("Connection is closed");
+ LOGE("Connection is closed");
return false;
}
@@ -89,7 +89,7 @@ bool AdbConnection::sendRequest(const char* fmt, ...) const {
int res = vsnprintf(buf, MAX_COMMAND_LENGTH, fmt, args);
va_end(args);
- ALOGV("Sending command: %04X%.*s", res, res, buf);
+ LOGV("Sending command: %04X%.*s", res, res, buf);
// Construct the payload length
char payloadLen[PAYLOAD_LENGTH + 1];
@@ -115,7 +115,7 @@ static void printFailureMessage(int fd) {
// Grab the payload length
char lenStr[PAYLOAD_LENGTH + 1];
int payloadLen = recv(fd, lenStr, sizeof(lenStr) - 1, 0);
- ALOG_ASSERT(payloadLen == PAYLOAD_LENGTH, "Incorrect payload size");
+ LOG_ASSERT(payloadLen == PAYLOAD_LENGTH, "Incorrect payload size");
lenStr[PAYLOAD_LENGTH] = 0;
// Parse the hex payload
@@ -130,13 +130,13 @@ static void printFailureMessage(int fd) {
log_errno("Failure reading failure message from adb");
return;
} else if (res != payloadLen) {
- ALOGE("Incorrect payload length %d - expected %d", res, payloadLen);
+ LOGE("Incorrect payload length %d - expected %d", res, payloadLen);
return;
}
msg[res] = 0;
// Tell somebody about it
- ALOGE("Received failure from adb: %s", msg);
+ LOGE("Received failure from adb: %s", msg);
// Cleanup
delete[] msg;
@@ -145,7 +145,7 @@ static void printFailureMessage(int fd) {
#define ADB_RESPONSE_LENGTH 4
bool AdbConnection::checkOkayResponse() const {
- ALOG_ASSERT(m_fd != -1, "Connection has been closed!");
+ LOG_ASSERT(m_fd != -1, "Connection has been closed!");
char buf[ADB_RESPONSE_LENGTH];
int res = recv(m_fd, buf, sizeof(buf), 0);
@@ -156,14 +156,14 @@ bool AdbConnection::checkOkayResponse() const {
// Check for a response other than OKAY/FAIL
if ((res == ADB_RESPONSE_LENGTH) && (strncmp(buf, "OKAY", res) == 0)) {
- ALOGV("Command OKAY");
+ LOGV("Command OKAY");
return true;
} else if (strncmp(buf, "FAIL", ADB_RESPONSE_LENGTH) == 0) {
// Something happened, print out the reason for failure
printFailureMessage(m_fd);
return false;
}
- ALOGE("Incorrect response from adb - '%.*s'", res, buf);
+ LOGE("Incorrect response from adb - '%.*s'", res, buf);
return false;
}
@@ -178,13 +178,13 @@ const DeviceList& AdbConnection::getDeviceList() {
clearDevices();
if (m_fd == -1) {
- ALOGE("Connection is closed");
+ LOGE("Connection is closed");
return m_devices;
}
// Try to send the device list request
if (!sendRequest("host:devices")) {
- ALOGE("Failed to get device list from adb");
+ LOGE("Failed to get device list from adb");
return m_devices;
}
@@ -210,7 +210,7 @@ const DeviceList& AdbConnection::getDeviceList() {
log_errno("Failure reading the device list");
return m_devices;
} else if (res != payloadLen) {
- ALOGE("Incorrect payload length %d - expected %d", res, payloadLen);
+ LOGE("Incorrect payload length %d - expected %d", res, payloadLen);
return m_devices;
}
msg[res] = 0;
@@ -224,7 +224,7 @@ const DeviceList& AdbConnection::getDeviceList() {
static const char emulator[] = "emulator-";
if (strncmp(serial, emulator, sizeof(emulator) - 1) == 0)
t = Device::EMULATOR;
- ALOGV("Adding device %s (%s)", serial, state);
+ LOGV("Adding device %s (%s)", serial, state);
m_devices.add(new Device(serial, t, this));
// Reset for the next line
diff --git a/Source/WebKit/android/wds/client/ClientUtils.h b/Source/WebKit/android/wds/client/ClientUtils.h
index 7c4b9ce..7d0db30 100644
--- a/Source/WebKit/android/wds/client/ClientUtils.h
+++ b/Source/WebKit/android/wds/client/ClientUtils.h
@@ -37,7 +37,7 @@
#endif
// Callers need to include Log.h and errno.h to use this macro
-#define log_errno(str) ALOGE("%s: %s", str, strerror(errno))
+#define log_errno(str) LOGE("%s: %s", str, strerror(errno))
// Fill in the sockaddr_in structure for binding to the localhost on the given
// port
diff --git a/Source/WebKit/android/wds/client/main.cpp b/Source/WebKit/android/wds/client/main.cpp
index 276affe..1c7d856 100644
--- a/Source/WebKit/android/wds/client/main.cpp
+++ b/Source/WebKit/android/wds/client/main.cpp
@@ -74,7 +74,7 @@ int main(int argc, char** argv) {
Device::DeviceType type = Device::NONE;
if (argc <= 1) {
- ALOGE("wdsclient takes at least 1 argument");
+ LOGE("wdsclient takes at least 1 argument");
return 1;
} else {
// Parse the options, look for -e or -d to choose a device.
@@ -94,7 +94,7 @@ int main(int argc, char** argv) {
}
}
if (optind == argc) {
- ALOGE("No command specified");
+ LOGE("No command specified");
return 1;
}
}
@@ -109,10 +109,10 @@ int main(int argc, char** argv) {
// No device specified and more than one connected, bail
if (type == Device::NONE && devices.size() > 1) {
- ALOGE("More than one device/emulator, please specify with -e or -d");
+ LOGE("More than one device/emulator, please specify with -e or -d");
return 1;
} else if (devices.size() == 0) {
- ALOGE("No devices connected");
+ LOGE("No devices connected");
return 1;
}
@@ -131,23 +131,23 @@ int main(int argc, char** argv) {
}
if (!device) {
- ALOGE("No device found!");
+ LOGE("No device found!");
return 1;
}
// Forward tcp:9999
if (!device->sendRequest("forward:tcp:" PORT_STR ";tcp:" PORT_STR)) {
- ALOGE("Failed to send forwarding request");
+ LOGE("Failed to send forwarding request");
return 1;
}
- ALOGV("Connecting to localhost port " PORT_STR);
+ LOGV("Connecting to localhost port " PORT_STR);
const char* command = argv[optind];
int commandLen = strlen(command);
#define WDS_COMMAND_LENGTH 4
if (commandLen != WDS_COMMAND_LENGTH) {
- ALOGE("Commands must be 4 characters '%s'", command);
+ LOGE("Commands must be 4 characters '%s'", command);
return 1;
}