summaryrefslogtreecommitdiffstats
path: root/Source/WebKit
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit')
-rw-r--r--Source/WebKit/Android.mk132
-rw-r--r--Source/WebKit/android/AndroidLog.h48
-rw-r--r--Source/WebKit/android/JavaVM/jni.h32
-rw-r--r--Source/WebKit/android/RenderSkinAndroid.cpp73
-rw-r--r--Source/WebKit/android/RenderSkinAndroid.h74
-rw-r--r--Source/WebKit/android/RenderSkinButton.cpp90
-rw-r--r--Source/WebKit/android/RenderSkinButton.h55
-rw-r--r--Source/WebKit/android/RenderSkinCombo.cpp146
-rw-r--r--Source/WebKit/android/RenderSkinCombo.h69
-rw-r--r--Source/WebKit/android/RenderSkinMediaButton.cpp215
-rw-r--r--Source/WebKit/android/RenderSkinMediaButton.h63
-rw-r--r--Source/WebKit/android/RenderSkinNinePatch.cpp89
-rw-r--r--Source/WebKit/android/RenderSkinNinePatch.h48
-rw-r--r--Source/WebKit/android/RenderSkinRadio.cpp100
-rw-r--r--Source/WebKit/android/RenderSkinRadio.h61
-rw-r--r--Source/WebKit/android/TimeCounter.cpp198
-rw-r--r--Source/WebKit/android/TimeCounter.h120
-rw-r--r--Source/WebKit/android/TimerClient.h42
-rw-r--r--Source/WebKit/android/WebCoreSupport/CacheResult.cpp251
-rw-r--r--Source/WebKit/android/WebCoreSupport/CacheResult.h86
-rw-r--r--Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp66
-rw-r--r--Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h63
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp605
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h216
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h104
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp74
-rw-r--r--Source/WebKit/android/WebCoreSupport/ChromiumInit.h38
-rw-r--r--Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp51
-rw-r--r--Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h51
-rw-r--r--Source/WebKit/android/WebCoreSupport/CookieClient.h46
-rw-r--r--Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp84
-rw-r--r--Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h65
-rw-r--r--Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp84
-rw-r--r--Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h65
-rw-r--r--Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp46
-rw-r--r--Source/WebKit/android/WebCoreSupport/DragClientAndroid.h51
-rw-r--r--Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp280
-rw-r--r--Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h135
-rw-r--r--Source/WebKit/android/WebCoreSupport/FileSystemClient.h41
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp1351
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h271
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp52
-rw-r--r--Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h54
-rwxr-xr-xSource/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp419
-rw-r--r--Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h178
-rw-r--r--Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h54
-rw-r--r--Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h46
-rw-r--r--Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp654
-rw-r--r--Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp81
-rw-r--r--Source/WebKit/android/WebCoreSupport/MemoryUsage.h45
-rw-r--r--Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp261
-rw-r--r--Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp71
-rw-r--r--Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp130
-rw-r--r--Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h70
-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.cpp237
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCache.h88
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp263
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebCookieJar.h78
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequest.cpp531
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequest.h125
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp130
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebRequestContext.h64
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp96
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebResourceRequest.h86
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebResponse.cpp167
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebResponse.h91
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp87
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebUrlLoader.h57
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp482
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h130
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp132
-rw-r--r--Source/WebKit/android/WebCoreSupport/WebViewClientError.h74
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp51
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h54
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp139
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h96
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp871
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h187
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp35
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h37
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h73
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp296
-rw-r--r--Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h127
-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/icu/unicode/ucnv.cpp50
-rw-r--r--Source/WebKit/android/icu/unicode/ucnv.h47
-rw-r--r--Source/WebKit/android/jni/CacheManager.cpp143
-rw-r--r--Source/WebKit/android/jni/CookieManager.cpp201
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp171
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h68
-rw-r--r--Source/WebKit/android/jni/DeviceMotionClientImpl.cpp130
-rw-r--r--Source/WebKit/android/jni/DeviceMotionClientImpl.h70
-rw-r--r--Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp130
-rw-r--r--Source/WebKit/android/jni/DeviceOrientationClientImpl.h70
-rwxr-xr-xSource/WebKit/android/jni/GeolocationPermissionsBridge.cpp113
-rw-r--r--Source/WebKit/android/jni/JavaBridge.cpp514
-rw-r--r--Source/WebKit/android/jni/JavaSharedClient.cpp137
-rw-r--r--Source/WebKit/android/jni/JavaSharedClient.h65
-rw-r--r--Source/WebKit/android/jni/JniUtil.cpp58
-rw-r--r--Source/WebKit/android/jni/MIMETypeRegistry.cpp67
-rwxr-xr-xSource/WebKit/android/jni/MockGeolocation.cpp84
-rw-r--r--Source/WebKit/android/jni/PictureSet.cpp676
-rw-r--r--Source/WebKit/android/jni/PictureSet.h104
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.cpp2125
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.h173
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.cpp117
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.h96
-rw-r--r--Source/WebKit/android/jni/WebCoreJniOnLoad.cpp319
-rw-r--r--Source/WebKit/android/jni/WebCoreRefObject.h46
-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.h106
-rw-r--r--Source/WebKit/android/jni/WebFrameView.cpp104
-rw-r--r--Source/WebKit/android/jni/WebFrameView.h65
-rw-r--r--Source/WebKit/android/jni/WebHistory.cpp857
-rw-r--r--Source/WebKit/android/jni/WebHistory.h68
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.cpp237
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.h75
-rw-r--r--Source/WebKit/android/jni/WebSettings.cpp599
-rw-r--r--Source/WebKit/android/jni/WebStorage.cpp188
-rw-r--r--Source/WebKit/android/jni/WebViewCore.cpp4598
-rw-r--r--Source/WebKit/android/jni/WebViewCore.h714
-rw-r--r--Source/WebKit/android/nav/CacheBuilder.cpp3201
-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.cpp1494
-rw-r--r--Source/WebKit/android/nav/CachedFrame.h285
-rw-r--r--Source/WebKit/android/nav/CachedHistory.cpp187
-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.h112
-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.cpp432
-rw-r--r--Source/WebKit/android/nav/CachedNode.h247
-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.cpp1813
-rw-r--r--Source/WebKit/android/nav/CachedRoot.h142
-rw-r--r--Source/WebKit/android/nav/DrawExtra.h48
-rw-r--r--Source/WebKit/android/nav/FindCanvas.cpp699
-rw-r--r--Source/WebKit/android/nav/FindCanvas.h255
-rw-r--r--Source/WebKit/android/nav/ParseCanvas.h52
-rw-r--r--Source/WebKit/android/nav/SelectText.cpp1983
-rw-r--r--Source/WebKit/android/nav/SelectText.h116
-rw-r--r--Source/WebKit/android/nav/WebView.cpp2673
-rw-r--r--Source/WebKit/android/plugins/ANPBitmapInterface.cpp71
-rw-r--r--Source/WebKit/android/plugins/ANPCanvasInterface.cpp197
-rw-r--r--Source/WebKit/android/plugins/ANPEventInterface.cpp84
-rw-r--r--Source/WebKit/android/plugins/ANPKeyCodes.h229
-rw-r--r--Source/WebKit/android/plugins/ANPLogInterface.cpp60
-rw-r--r--Source/WebKit/android/plugins/ANPMatrixInterface.cpp167
-rw-r--r--Source/WebKit/android/plugins/ANPOpenGLInterface.cpp121
-rw-r--r--Source/WebKit/android/plugins/ANPOpenGL_npapi.h63
-rw-r--r--Source/WebKit/android/plugins/ANPPaintInterface.cpp212
-rw-r--r--Source/WebKit/android/plugins/ANPPathInterface.cpp112
-rw-r--r--Source/WebKit/android/plugins/ANPSoundInterface.cpp163
-rw-r--r--Source/WebKit/android/plugins/ANPSurfaceInterface.cpp174
-rw-r--r--Source/WebKit/android/plugins/ANPSurface_npapi.h48
-rw-r--r--Source/WebKit/android/plugins/ANPSystemInterface.cpp222
-rw-r--r--Source/WebKit/android/plugins/ANPSystem_npapi.h72
-rw-r--r--Source/WebKit/android/plugins/ANPTypefaceInterface.cpp104
-rw-r--r--Source/WebKit/android/plugins/ANPVideoInterface.cpp83
-rw-r--r--Source/WebKit/android/plugins/ANPVideo_npapi.h61
-rw-r--r--Source/WebKit/android/plugins/ANPWindowInterface.cpp103
-rw-r--r--Source/WebKit/android/plugins/PluginDebugAndroid.cpp137
-rw-r--r--Source/WebKit/android/plugins/PluginDebugAndroid.h58
-rw-r--r--Source/WebKit/android/plugins/PluginTimer.cpp135
-rw-r--r--Source/WebKit/android/plugins/PluginTimer.h82
-rw-r--r--Source/WebKit/android/plugins/PluginViewBridgeAndroid.cpp38
-rw-r--r--Source/WebKit/android/plugins/PluginViewBridgeAndroid.h48
-rw-r--r--Source/WebKit/android/plugins/PluginWidgetAndroid.cpp690
-rw-r--r--Source/WebKit/android/plugins/PluginWidgetAndroid.h219
-rw-r--r--Source/WebKit/android/plugins/SkANP.cpp106
-rw-r--r--Source/WebKit/android/plugins/SkANP.h79
-rw-r--r--Source/WebKit/android/plugins/SurfaceCallback.h42
-rw-r--r--Source/WebKit/android/plugins/android_npapi.h987
-rw-r--r--Source/WebKit/android/smoke/MessageThread.cpp146
-rw-r--r--Source/WebKit/android/smoke/MessageThread.h108
-rw-r--r--Source/WebKit/android/smoke/MessageTypes.h159
-rw-r--r--Source/WebKit/android/wds/Command.cpp154
-rw-r--r--Source/WebKit/android/wds/Command.h108
-rw-r--r--Source/WebKit/android/wds/Connection.cpp93
-rw-r--r--Source/WebKit/android/wds/Connection.h86
-rw-r--r--Source/WebKit/android/wds/DebugServer.cpp116
-rw-r--r--Source/WebKit/android/wds/DebugServer.h77
-rw-r--r--Source/WebKit/android/wds/client/AdbConnection.cpp237
-rw-r--r--Source/WebKit/android/wds/client/AdbConnection.h47
-rw-r--r--Source/WebKit/android/wds/client/Android.mk39
-rw-r--r--Source/WebKit/android/wds/client/ClientUtils.cpp35
-rw-r--r--Source/WebKit/android/wds/client/ClientUtils.h46
-rw-r--r--Source/WebKit/android/wds/client/Device.cpp31
-rw-r--r--Source/WebKit/android/wds/client/Device.h62
-rw-r--r--Source/WebKit/android/wds/client/DeviceList.h35
-rw-r--r--Source/WebKit/android/wds/client/main.cpp173
204 files changed, 48905 insertions, 0 deletions
diff --git a/Source/WebKit/Android.mk b/Source/WebKit/Android.mk
new file mode 100644
index 0000000..5998227
--- /dev/null
+++ b/Source/WebKit/Android.mk
@@ -0,0 +1,132 @@
+##
+##
+## Copyright 2008, The Android Open Source Project
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+LOCAL_SRC_FILES := \
+ android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp \
+ android/WebCoreSupport/ChromeClientAndroid.cpp \
+ android/WebCoreSupport/ContextMenuClientAndroid.cpp \
+ android/WebCoreSupport/DeviceMotionClientAndroid.cpp \
+ android/WebCoreSupport/DeviceOrientationClientAndroid.cpp \
+ android/WebCoreSupport/DragClientAndroid.cpp \
+ android/WebCoreSupport/EditorClientAndroid.cpp \
+ android/WebCoreSupport/FrameLoaderClientAndroid.cpp \
+ android/WebCoreSupport/FrameNetworkingContextAndroid.cpp \
+ android/WebCoreSupport/GeolocationPermissions.cpp \
+ android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp \
+ android/WebCoreSupport/MemoryUsage.cpp \
+ android/WebCoreSupport/PlatformBridge.cpp \
+ android/WebCoreSupport/ResourceLoaderAndroid.cpp \
+ android/WebCoreSupport/UrlInterceptResponse.cpp \
+ android/WebCoreSupport/V8Counters.cpp
+
+ifeq ($(HTTP_STACK),chrome)
+LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
+ android/WebCoreSupport/ChromiumInit.cpp \
+ android/WebCoreSupport/CacheResult.cpp \
+ android/WebCoreSupport/WebCache.cpp \
+ android/WebCoreSupport/WebCookieJar.cpp \
+ android/WebCoreSupport/WebUrlLoader.cpp \
+ android/WebCoreSupport/WebUrlLoaderClient.cpp \
+ android/WebCoreSupport/WebRequest.cpp \
+ android/WebCoreSupport/WebRequestContext.cpp \
+ android/WebCoreSupport/WebResourceRequest.cpp \
+ android/WebCoreSupport/WebResponse.cpp \
+ android/WebCoreSupport/WebViewClientError.cpp
+endif # HTTP_STACK == chrome
+
+LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
+ android/RenderSkinAndroid.cpp \
+ android/RenderSkinButton.cpp \
+ android/RenderSkinCombo.cpp \
+ android/RenderSkinMediaButton.cpp \
+ android/RenderSkinNinePatch.cpp \
+ android/RenderSkinRadio.cpp \
+ android/TimeCounter.cpp \
+ \
+ android/benchmark/Intercept.cpp \
+ android/benchmark/MyJavaVM.cpp \
+ \
+ android/icu/unicode/ucnv.cpp \
+ \
+ android/jni/CacheManager.cpp \
+ android/jni/CookieManager.cpp \
+ android/jni/DeviceMotionAndOrientationManager.cpp \
+ android/jni/DeviceMotionClientImpl.cpp \
+ android/jni/DeviceOrientationClientImpl.cpp \
+ android/jni/GeolocationPermissionsBridge.cpp \
+ android/jni/JavaBridge.cpp \
+ android/jni/JavaSharedClient.cpp \
+ android/jni/JniUtil.cpp \
+ android/jni/MIMETypeRegistry.cpp \
+ android/jni/MockGeolocation.cpp \
+ android/jni/PictureSet.cpp \
+ android/jni/WebCoreFrameBridge.cpp \
+ android/jni/WebCoreJni.cpp \
+ android/jni/WebCoreResourceLoader.cpp \
+ android/jni/WebFrameView.cpp \
+ android/jni/WebHistory.cpp \
+ android/jni/WebIconDatabase.cpp \
+ android/jni/WebStorage.cpp \
+ android/jni/WebSettings.cpp \
+ android/jni/WebViewCore.cpp \
+ \
+ android/nav/CacheBuilder.cpp \
+ android/nav/CachedColor.cpp \
+ android/nav/CachedFrame.cpp \
+ android/nav/CachedHistory.cpp \
+ android/nav/CachedInput.cpp \
+ android/nav/CachedLayer.cpp \
+ android/nav/CachedNode.cpp \
+ android/nav/CachedRoot.cpp \
+ android/nav/FindCanvas.cpp \
+ android/nav/SelectText.cpp \
+ android/nav/WebView.cpp \
+ \
+ android/plugins/ANPBitmapInterface.cpp \
+ android/plugins/ANPCanvasInterface.cpp \
+ android/plugins/ANPEventInterface.cpp \
+ android/plugins/ANPLogInterface.cpp \
+ android/plugins/ANPMatrixInterface.cpp \
+ android/plugins/ANPOpenGLInterface.cpp \
+ android/plugins/ANPPaintInterface.cpp \
+ android/plugins/ANPPathInterface.cpp \
+ android/plugins/ANPSoundInterface.cpp \
+ android/plugins/ANPSurfaceInterface.cpp \
+ android/plugins/ANPSystemInterface.cpp \
+ android/plugins/ANPTypefaceInterface.cpp \
+ android/plugins/ANPVideoInterface.cpp \
+ android/plugins/ANPWindowInterface.cpp \
+ android/plugins/PluginDebugAndroid.cpp \
+ android/plugins/PluginTimer.cpp \
+ android/plugins/PluginViewBridgeAndroid.cpp \
+ android/plugins/PluginWidgetAndroid.cpp \
+ android/plugins/SkANP.cpp \
+ \
+ android/wds/Command.cpp \
+ android/wds/Connection.cpp \
+ android/wds/DebugServer.cpp
+
+# Needed for autofill.
+ifeq ($(ENABLE_AUTOFILL),true)
+LOCAL_CFLAGS += -DENABLE_WEB_AUTOFILL
+
+LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
+ android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp \
+ android/WebCoreSupport/autofill/FormFieldAndroid.cpp \
+ android/WebCoreSupport/autofill/FormManagerAndroid.cpp \
+ android/WebCoreSupport/autofill/WebAutoFill.cpp
+endif # ENABLE_AUTOFILL == true
diff --git a/Source/WebKit/android/AndroidLog.h b/Source/WebKit/android/AndroidLog.h
new file mode 100644
index 0000000..a69dce6
--- /dev/null
+++ b/Source/WebKit/android/AndroidLog.h
@@ -0,0 +1,48 @@
+/*
+ * 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 ANDROIDLOG_H_
+#define ANDROIDLOG_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 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 LOGD(__VA_ARGS__); }
+#else
+#define DUMP_DOM_LOGD(...) ((void)0)
+#define DUMP_RENDER_LOGD(...) ((void)0)
+#endif /* ANDROID_DOM_LOGGING */
+
+#define DISPLAY_TREE_LOG_FILE "/sdcard/displayTree.txt"
+#define LAYERS_TREE_LOG_FILE "/sdcard/layersTree.plist"
+
+#endif /* ANDROIDLOG_H_ */
diff --git a/Source/WebKit/android/JavaVM/jni.h b/Source/WebKit/android/JavaVM/jni.h
new file mode 100644
index 0000000..da02603
--- /dev/null
+++ b/Source/WebKit/android/JavaVM/jni.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _JNI_COVER_H_
+#define _JNI_COVER_H_
+
+#include "nativehelper/jni.h"
+#define AttachCurrentThread(a, b) AttachCurrentThread((JNIEnv**) a, b)
+
+#endif
diff --git a/Source/WebKit/android/RenderSkinAndroid.cpp b/Source/WebKit/android/RenderSkinAndroid.cpp
new file mode 100644
index 0000000..9383a9c
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinAndroid.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "RenderSkinAndroid.h"
+#include "RenderSkinButton.h"
+#include "RenderSkinCombo.h"
+#include "RenderSkinMediaButton.h"
+#include "RenderSkinRadio.h"
+#include "SkImageDecoder.h"
+
+#include "utils/AssetManager.h"
+#include "utils/Asset.h"
+
+namespace WebCore {
+
+RenderSkinAndroid::~RenderSkinAndroid()
+{
+ delete m_button;
+}
+RenderSkinAndroid::RenderSkinAndroid(android::AssetManager* am, String drawableDirectory)
+{
+ m_button = new RenderSkinButton(am, drawableDirectory);
+ RenderSkinCombo::Init(am, drawableDirectory);
+ RenderSkinMediaButton::Init(am, drawableDirectory);
+ RenderSkinRadio::Init(am, drawableDirectory);
+}
+
+bool RenderSkinAndroid::DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap)
+{
+ android::Asset* asset = am->open(fileName, android::Asset::ACCESS_BUFFER);
+ if (!asset) {
+ asset = am->openNonAsset(fileName, android::Asset::ACCESS_BUFFER);
+ if (!asset) {
+ LOGD("RenderSkinAndroid: File \"%s\" not found.\n", fileName);
+ return false;
+ }
+ }
+
+ bool success = SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), bitmap);
+ if (!success) {
+ LOGD("RenderSkinAndroid: Failed to decode %s\n", fileName);
+ }
+
+ delete asset;
+ return success;
+}
+
+} // namespace WebCore
diff --git a/Source/WebKit/android/RenderSkinAndroid.h b/Source/WebKit/android/RenderSkinAndroid.h
new file mode 100644
index 0000000..73773ea
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinAndroid.h
@@ -0,0 +1,74 @@
+/*
+ * 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 RenderSkinAndroid_h
+#define RenderSkinAndroid_h
+
+#include "PlatformString.h"
+
+namespace android {
+ class AssetManager;
+}
+
+class SkBitmap;
+
+namespace WebCore {
+class Node;
+class RenderSkinButton;
+
+class RenderSkinAndroid
+{
+public:
+ enum State {
+ kDisabled,
+ kNormal,
+ kFocused,
+ kPressed,
+
+ kNumStates
+ };
+
+ /**
+ * Initialize the Android skinning system. The AssetManager may be used to find resources used
+ * in rendering.
+ */
+ RenderSkinAndroid(android::AssetManager*, String drawableDirectory);
+ ~RenderSkinAndroid();
+
+ /* DecodeBitmap determines which file to use, with the given fileName of the form
+ * "images/bitmap.png", and uses the asset manager to select the exact one. It
+ * returns true if it successfully decoded the bitmap, false otherwise.
+ */
+ static bool DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap);
+
+ const RenderSkinButton* renderSkinButton() const { return m_button; }
+
+private:
+ RenderSkinButton* m_button;
+};
+
+} // WebCore
+
+#endif
diff --git a/Source/WebKit/android/RenderSkinButton.cpp b/Source/WebKit/android/RenderSkinButton.cpp
new file mode 100644
index 0000000..6a0ae54
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinButton.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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>
+
+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(android::AssetManager* am, String drawableDirectory)
+{
+ m_decoded = true;
+ for (size_t i = 0; i < 4; i++) {
+ String path = String(drawableDirectory.impl());
+ path.append(String(gFiles[i]));
+ if (!RenderSkinNinePatch::decodeAsset(am, path.utf8().data(), &m_buttons[i])) {
+ m_decoded = false;
+ LOGE("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ return;
+ }
+ }
+
+ // 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::draw(SkCanvas* canvas, const IntRect& r,
+ RenderSkinAndroid::State newState) const
+{
+ // 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..e9db74c
--- /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:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any
+ * bitmaps the class may use.
+ */
+ RenderSkinButton(android::AssetManager*, String drawableDirectory);
+ /**
+ * Draw the skin to the canvas, using the rectangle for its bounds and the
+ * State to determine which skin to use, i.e. focused or not focused.
+ */
+ void draw(SkCanvas* , const IntRect& , RenderSkinAndroid::State) const;
+private:
+ bool m_decoded;
+ NinePatch m_buttons[4];
+};
+
+} // WebCore
+#endif
diff --git a/Source/WebKit/android/RenderSkinCombo.cpp b/Source/WebKit/android/RenderSkinCombo.cpp
new file mode 100644
index 0000000..b30dc29
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinCombo.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 <wtf/text/CString.h>
+
+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
+};
+
+// There are 2.5 different concepts of a 'border' here, which results
+// in rather a lot of magic constants. In each case, there are 2
+// numbers, one for medium res and one for high-res. All sizes are in pixels.
+
+// Firstly, we have the extra padding that webkit needs to know about,
+// which defines how much bigger this element is made by the
+// asset. This is actually a bit broader than the actual border on the
+// asset, to make things look less cramped. The border is the same
+// width on all sides, except on the right when it's significantly
+// wider to allow for the arrow.
+const int RenderSkinCombo::arrowMargin[2] = {22, 34};
+const int RenderSkinCombo::padMargin[2] = {2, 5};
+
+// Then we have the borders used for the 9-patch stretch. The
+// rectangle at the centre of these borders is entirely below and to
+// the left of the arrow in the asset. Hence the border widths are the
+// same for the bottom and left, but are different for the top. The
+// right hand border width happens to be the same as arrowMargin
+// defined above.
+static const int stretchMargin[2] = {3, 5}; // border width for the bottom and left of the 9-patch
+static const int stretchTop[2] = {15, 23}; // border width for the top of the 9-patch
+
+// Finally, if the border is defined by the CSS, we only draw the
+// arrow and not the border. We do this by drawing the relevant subset
+// of the bitmap, which must now be precisely determined by what's in
+// the asset with no extra padding to make things look properly
+// spaced. The border to remove at the top, right and bottom of the
+// image is the same as stretchMargin above, but we need to know the width
+// of the arrow.
+static const int arrowWidth[2] = {22, 31};
+
+RenderSkinCombo::Resolution RenderSkinCombo::resolution = MedRes;
+
+const SkIRect RenderSkinCombo::margin[2][2] = {{{ stretchMargin[MedRes], stretchTop[MedRes],
+ RenderSkinCombo::arrowMargin[MedRes] + stretchMargin[MedRes], stretchMargin[MedRes] },
+ {0, stretchTop[MedRes], 0, stretchMargin[MedRes]}},
+ {{ stretchMargin[HighRes], stretchTop[HighRes],
+ RenderSkinCombo::arrowMargin[HighRes] + stretchMargin[HighRes], stretchMargin[HighRes] },
+ {0, stretchTop[HighRes], 0, stretchMargin[HighRes]}}};
+static SkBitmap bitmaps[2][2]; // Collection of assets for a combo box
+static bool isDecoded; // True if all assets were decoded
+
+void RenderSkinCombo::Init(android::AssetManager* am, String drawableDirectory)
+{
+ if (isDecoded)
+ return;
+
+ if (drawableDirectory[drawableDirectory.length() - 5] == 'h')
+ resolution = HighRes;
+
+ isDecoded = RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_nohighlight.png").utf8().data(), &bitmaps[kNormal][FullAsset]);
+ isDecoded &= RenderSkinAndroid::DecodeBitmap(am, (drawableDirectory + "combobox_disabled.png").utf8().data(), &bitmaps[kDisabled][FullAsset]);
+
+ int width = bitmaps[kNormal][FullAsset].width();
+ int height = bitmaps[kNormal][FullAsset].height();
+ SkIRect subset;
+ subset.set(width - arrowWidth[resolution], 0, width, height);
+ bitmaps[kNormal][FullAsset].extractSubset(&bitmaps[kNormal][NoBorder], subset);
+ bitmaps[kDisabled][FullAsset].extractSubset(&bitmaps[kDisabled][NoBorder], subset);
+}
+
+
+bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
+{
+ if (!isDecoded)
+ return true;
+
+ State state = (element->isElementNode() && static_cast<Element*>(element)->isEnabledFormControl()) ? kNormal : kDisabled;
+ height = std::max(height, (stretchMargin[resolution]<<1) + 1);
+
+ SkRect bounds;
+ BorderStyle drawBorder = FullAsset;
+
+ bounds.set(SkIntToScalar(x+1), SkIntToScalar(y+1), SkIntToScalar(x + width-1), SkIntToScalar(y + height-1));
+ RenderStyle* style = element->renderStyle();
+ 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;
+ }
+ SkNinePatch::DrawNine(canvas, bounds, bitmaps[state][drawBorder], margin[resolution][drawBorder]);
+ return false;
+}
+
+} //WebCore
diff --git a/Source/WebKit/android/RenderSkinCombo.h b/Source/WebKit/android/RenderSkinCombo.h
new file mode 100644
index 0000000..38cd048
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinCombo.h
@@ -0,0 +1,69 @@
+/*
+ * 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:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*, String drawableDirectory);
+
+ /**
+ * Draw the 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[resolution]; }
+ static int padding() { return padMargin[resolution]; }
+
+ enum Resolution {
+ MedRes,
+ HighRes
+ };
+private:
+ static Resolution resolution;
+ const static int arrowMargin[2];
+ const static int padMargin[2];
+ const static SkIRect margin[2][2];
+};
+
+} // WebCore
+
+#endif
diff --git a/Source/WebKit/android/RenderSkinMediaButton.cpp b/Source/WebKit/android/RenderSkinMediaButton.cpp
new file mode 100644
index 0000000..090d55e
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinMediaButton.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderObject.h"
+#include "RenderSkinMediaButton.h"
+#include "RenderSlider.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include "SkRect.h"
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <wtf/text/CString.h>
+
+struct PatchData {
+ const char* name;
+ int8_t outset, margin;
+};
+
+static const PatchData gFiles[] =
+ {
+ { "scrubber_primary_holo.9.png", 0, 0 }, // SLIDER_TRACK, left of the SLIDER_THUMB
+ { "ic_media_pause.png", 0, 0}, // PAUSE
+ { "ic_media_play.png", 0, 0 }, // PLAY
+ { "ic_media_pause.png", 0, 0 }, // MUTE
+ { "ic_media_rew.png", 0, 0 }, // REWIND
+ { "ic_media_ff.png", 0, 0 }, // FORWARD
+ { "ic_media_fullscreen.png", 0, 0 }, // FULLSCREEN
+ { "spinner_76_outer_holo.png", 0, 0 }, // SPINNER_OUTER
+ { "spinner_76_inner_holo.png", 0, 0 }, // SPINNER_INNER
+ { "ic_media_video_poster.png", 0, 0 }, // VIDEO
+ { "btn_media_player_disabled.9.png", 0, 0 }, // BACKGROUND_SLIDER
+ { "scrubber_track_holo_dark.9.png", 0, 0 }, // SLIDER_TRACK
+ { "scrubber_control_holo.png", 0, 0 } // SLIDER_THUMB
+ };
+
+static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])];
+static bool gDecoded;
+static bool gHighRes;
+
+namespace WebCore {
+
+void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirectory)
+{
+ static bool gInited;
+ if (gInited)
+ return;
+
+ gInited = true;
+ gDecoded = true;
+ gHighRes = drawableDirectory[drawableDirectory.length() - 5] == 'h';
+ for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) {
+ String path = drawableDirectory + gFiles[i].name;
+ if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) {
+ gDecoded = false;
+ LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ break;
+ }
+ }
+}
+
+void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType,
+ bool translucent, RenderObject* o)
+{
+ // If we failed to decode, do nothing. This way the browser still works,
+ // and webkit will still draw the label and layout space for us.
+ if (!gDecoded) {
+ return;
+ }
+
+ bool drawsNinePatch = false;
+ bool drawsImage = true;
+ bool drawsBackgroundColor = true;
+
+ int ninePatchIndex = 0;
+ int imageIndex = 0;
+
+ SkRect bounds(r);
+ SkScalar imageMargin = 8;
+ SkPaint paint;
+
+ int alpha = 255;
+ if (translucent)
+ alpha = 190;
+
+ SkColor backgroundColor = SkColorSetARGB(alpha, 34, 34, 34);
+ SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100);
+ paint.setColor(backgroundColor);
+ paint.setFlags(SkPaint::kFilterBitmap_Flag);
+
+ switch (buttonType) {
+ case PAUSE:
+ case PLAY:
+ case MUTE:
+ case REWIND:
+ case FORWARD:
+ case FULLSCREEN:
+ {
+ imageIndex = buttonType + 1;
+ paint.setColor(backgroundColor);
+ break;
+ }
+ case SPINNER_OUTER:
+ case SPINNER_INNER:
+ case VIDEO:
+ {
+ drawsBackgroundColor = false;
+ imageIndex = buttonType + 1;
+ break;
+ }
+ case BACKGROUND_SLIDER:
+ {
+ drawsBackgroundColor = false;
+ drawsImage = false;
+ break;
+ }
+ case SLIDER_TRACK:
+ {
+ drawsNinePatch = true;
+ drawsImage = false;
+ ninePatchIndex = buttonType + 1;
+ break;
+ }
+ case SLIDER_THUMB:
+ {
+ drawsBackgroundColor = false;
+ imageMargin = 0;
+ imageIndex = buttonType + 1;
+ break;
+ }
+ default:
+ return;
+ }
+
+ if (drawsBackgroundColor) {
+ canvas->drawRect(r, paint);
+ }
+
+ if (drawsNinePatch) {
+ const PatchData& pd = gFiles[ninePatchIndex];
+ int marginValue = pd.margin + pd.outset;
+
+ SkIRect margin;
+ margin.set(marginValue, marginValue, marginValue, marginValue);
+ if (buttonType == SLIDER_TRACK) {
+ // Cut the height in half (with some extra slop determined by trial
+ // and error to get the placement just right.
+ SkScalar quarterHeight = SkScalarHalf(SkScalarHalf(bounds.height()));
+ bounds.fTop += quarterHeight + SkScalarHalf(3);
+ bounds.fBottom += -quarterHeight + SK_ScalarHalf;
+ if (o && o->isSlider()) {
+ RenderSlider* slider = toRenderSlider(o);
+ IntRect thumb = slider->thumbRect();
+ // Inset the track by half the width of the thumb, so the track
+ // does not appear to go beyond the space where the thumb can
+ // be.
+ SkScalar thumbHalfWidth = SkIntToScalar(thumb.width()/2);
+ bounds.fLeft += thumbHalfWidth;
+ bounds.fRight -= thumbHalfWidth;
+ if (thumb.x() > 0) {
+ // The video is past the starting point. Show the area to
+ // left of the thumb as having been played.
+ SkScalar alreadyPlayed = SkIntToScalar(thumb.center().x() + r.x());
+ SkRect playedRect(bounds);
+ playedRect.fRight = alreadyPlayed;
+ SkNinePatch::DrawNine(canvas, playedRect, gButton[0], margin);
+ bounds.fLeft = alreadyPlayed;
+ }
+
+ }
+ }
+ SkNinePatch::DrawNine(canvas, bounds, gButton[ninePatchIndex], margin);
+ }
+
+ if (drawsImage) {
+ SkScalar SIZE = gButton[imageIndex].width();
+ SkScalar width = r.width();
+ SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE);
+ int saveScaleCount = canvas->save();
+ canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin);
+ canvas->scale(scale, scale);
+ canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint);
+ canvas->restoreToCount(saveScaleCount);
+ }
+}
+
+} // WebCore
diff --git a/Source/WebKit/android/RenderSkinMediaButton.h b/Source/WebKit/android/RenderSkinMediaButton.h
new file mode 100644
index 0000000..6aa9c4e
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinMediaButton.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RenderSkinMediaButton_h
+#define RenderSkinMediaButton_h
+
+#include "RenderSkinAndroid.h"
+
+class SkCanvas;
+
+namespace WebCore {
+class IntRect;
+class RenderObject;
+
+class RenderSkinMediaButton {
+public:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any
+ * bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*, String drawableDirectory);
+ /**
+ * Draw the skin to the canvas, using the rectangle for its bounds and the
+ * State to determine which skin to use, i.e. focused or not focused.
+ */
+ static void Draw(SkCanvas* , const IntRect& , int buttonType, bool translucent = false,
+ RenderObject* o = 0);
+ /**
+ * Button types
+ */
+ enum { PAUSE, PLAY, MUTE, REWIND, FORWARD, FULLSCREEN, SPINNER_OUTER, SPINNER_INNER , VIDEO, BACKGROUND_SLIDER, SLIDER_TRACK, SLIDER_THUMB };
+ /**
+ * Slider dimensions
+ */
+ static int sliderThumbWidth() { return 32; }
+ static int sliderThumbHeight() { return 32; }
+
+};
+
+} // WebCore
+#endif // RenderSkinMediaButton_h
diff --git a/Source/WebKit/android/RenderSkinNinePatch.cpp b/Source/WebKit/android/RenderSkinNinePatch.cpp
new file mode 100644
index 0000000..0c915c0
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinNinePatch.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+
+#include "RenderSkinNinePatch.h"
+#include "NinePatchPeeker.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include <utils/Asset.h>
+#include <utils/AssetManager.h>
+#include <utils/Log.h>
+#include <utils/ResourceTypes.h>
+
+class SkPaint;
+class SkRegion;
+
+using namespace android;
+
+extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
+ const SkBitmap& bitmap, const Res_png_9patch& chunk,
+ const SkPaint* paint, SkRegion** outRegion);
+
+bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, NinePatch* ninepatch) {
+ Asset* asset = am->open(filename, android::Asset::ACCESS_BUFFER);
+ if (!asset) {
+ asset = am->openNonAsset(filename, android::Asset::ACCESS_BUFFER);
+ if (!asset) {
+ return false;
+ }
+ }
+
+ SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
+ SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ SkStream* stream = new SkMemoryStream(asset->getBuffer(false), asset->getLength());
+ SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+ if (!decoder) {
+ asset->close();
+ LOGE("RenderSkinNinePatch::Failed to create an image decoder");
+ return false;
+ }
+
+ decoder->setSampleSize(1);
+ decoder->setDitherImage(true);
+ decoder->setPreferQualityOverSpeed(false);
+
+ NinePatchPeeker peeker(decoder);
+
+ SkAutoTDelete<SkImageDecoder> add(decoder);
+
+ decoder->setPeeker(&peeker);
+ if (!decoder->decode(stream, &ninepatch->m_bitmap, prefConfig, mode, true)) {
+ asset->close();
+ LOGE("RenderSkinNinePatch::Failed to decode nine patch asset");
+ return false;
+ }
+
+ asset->close();
+ if (!peeker.fPatchIsValid) {
+ LOGE("RenderSkinNinePatch::Patch data not valid");
+ return false;
+ }
+ void** data = &ninepatch->m_serializedPatchData;
+ *data = malloc(peeker.fPatch->serializedSize());
+ peeker.fPatch->serialize(*data);
+ return true;
+}
+
+void RenderSkinNinePatch::DrawNinePatch(SkCanvas* canvas, const SkRect& bounds,
+ const NinePatch& patch) {
+ Res_png_9patch* data = Res_png_9patch::deserialize(patch.m_serializedPatchData);
+ NinePatch_Draw(canvas, bounds, patch.m_bitmap, *data, 0, 0);
+}
diff --git a/Source/WebKit/android/RenderSkinNinePatch.h b/Source/WebKit/android/RenderSkinNinePatch.h
new file mode 100644
index 0000000..e4db260
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinNinePatch.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RenderSkinNinePatch_h
+#define RenderSkinNinePatch_h
+
+#include "SkBitmap.h"
+#include "utils/Asset.h"
+
+namespace android {
+ class AssetManager;
+}
+
+class SkCanvas;
+class SkRect;
+
+struct NinePatch {
+ SkBitmap m_bitmap;
+ void* m_serializedPatchData;
+ NinePatch() {
+ m_serializedPatchData = 0;
+ }
+ ~NinePatch() {
+ if (m_serializedPatchData)
+ free(m_serializedPatchData);
+ }
+};
+
+class RenderSkinNinePatch {
+public:
+ static bool decodeAsset(android::AssetManager*, const char* fileName, NinePatch*);
+ static void DrawNinePatch(SkCanvas*, const SkRect&, const NinePatch&);
+};
+
+#endif // RenderSkinNinePatch_h
diff --git a/Source/WebKit/android/RenderSkinRadio.cpp b/Source/WebKit/android/RenderSkinRadio.cpp
new file mode 100644
index 0000000..5dfee4a
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinRadio.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 <wtf/text/CString.h>
+
+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 SIZE;
+
+namespace WebCore {
+
+static SkBitmap s_bitmap[4];
+static bool s_decoded;
+
+void RenderSkinRadio::Init(android::AssetManager* am, String drawableDirectory)
+{
+ if (s_decoded)
+ return;
+ String path = drawableDirectory + checks[0];
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[0]);
+ path = drawableDirectory + checks[1];
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[1]) && s_decoded;
+ path = drawableDirectory + checks[2];
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[2]) && s_decoded;
+ path = drawableDirectory + checks[3];
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &s_bitmap[3]) && s_decoded;
+ SIZE = SkIntToScalar(s_bitmap[0].width());
+}
+
+void RenderSkinRadio::Draw(SkCanvas* canvas, Node* element, const IntRect& ir,
+ bool isCheckBox)
+{
+ if (!s_decoded || !element) {
+ 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, SIZE);
+ saveScaleCount = canvas->save();
+ canvas->translate(r.fLeft, r.fTop);
+ canvas->scale(scale, scale);
+
+ bool checked = false;
+ if (InputElement* inputElement = toInputElement(static_cast<Element*>(element))) {
+ checked = inputElement->isChecked();
+ }
+
+ canvas->drawBitmap(s_bitmap[checked + 2*(!isCheckBox)],
+ 0, 0, &paint);
+ canvas->restoreToCount(saveScaleCount);
+}
+
+} //WebCore
diff --git a/Source/WebKit/android/RenderSkinRadio.h b/Source/WebKit/android/RenderSkinRadio.h
new file mode 100644
index 0000000..f77e1be
--- /dev/null
+++ b/Source/WebKit/android/RenderSkinRadio.h
@@ -0,0 +1,61 @@
+/*
+ * 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 RenderSkinRadio_h
+#define RenderSkinRadio_h
+
+#include "PlatformString.h"
+
+class SkCanvas;
+
+namespace android {
+ class AssetManager;
+}
+
+namespace WebCore {
+
+class Node;
+class IntRect;
+
+/* RenderSkin for a radio button or a checkbox
+ */
+class RenderSkinRadio
+{
+public:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*, String drawableDirectory);
+
+ /**
+ * Draw the 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);
+};
+
+} // 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..64908d1
--- /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 TIME_COUNTER_H
+#define TIME_COUNTER_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/TimerClient.h b/Source/WebKit/android/TimerClient.h
new file mode 100644
index 0000000..0c3c84c
--- /dev/null
+++ b/Source/WebKit/android/TimerClient.h
@@ -0,0 +1,42 @@
+/*
+ * 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 TIMER_CLIENT_H
+#define TIMER_CLIENT_H
+
+namespace android {
+
+ class TimerClient
+ {
+ public:
+ virtual ~TimerClient() {}
+ virtual void setSharedTimerCallback(void(*f)()) = 0;
+ virtual void setSharedTimer(long long timemillis) = 0;
+ virtual void stopSharedTimer() = 0;
+ virtual void signalServiceFuncPtrQueue() = 0;
+ };
+
+}
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/CacheResult.cpp b/Source/WebKit/android/WebCoreSupport/CacheResult.cpp
new file mode 100644
index 0000000..5309c66
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/CacheResult.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2011, 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 "CacheResult.h"
+
+#include "WebResponse.h"
+#include "WebUrlLoaderClient.h"
+#include <platform/FileSystem.h>
+#include <wtf/text/CString.h>
+
+using namespace base;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+// All public methods are called on a UI thread but we do work on the
+// Chromium thread. However, because we block the WebCore thread while this
+// work completes, we can never receive new public method calls while the
+// Chromium thread work is in progress.
+
+// Copied from HttpCache
+enum {
+ kResponseInfoIndex = 0,
+ kResponseContentIndex
+};
+
+CacheResult::CacheResult(disk_cache::Entry* entry, String url)
+ : m_entry(entry)
+ , m_onResponseHeadersDoneCallback(this, &CacheResult::onResponseHeadersDone)
+ , m_onReadNextChunkDoneCallback(this, &CacheResult::onReadNextChunkDone)
+ , m_url(url)
+{
+ ASSERT(m_entry);
+}
+
+CacheResult::~CacheResult()
+{
+ m_entry->Close();
+ // TODO: Should we also call DoneReadingFromEntry() on the cache for our
+ // entry?
+}
+
+int64 CacheResult::contentSize() const
+{
+ // The android stack does not take the content length from the HTTP response
+ // headers but calculates it when writing the content to disk. It can never
+ // overflow a long because we limit the cache size.
+ return m_entry->GetDataSize(kResponseContentIndex);
+}
+
+bool CacheResult::firstResponseHeader(const char* name, String* result, bool allowEmptyString) const
+{
+ string value;
+ if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, name, &value) && (!value.empty() || allowEmptyString)) {
+ *result = String(value.c_str());
+ return true;
+ }
+ return false;
+}
+
+String CacheResult::mimeType() const
+{
+ string mimeType;
+ if (responseHeaders())
+ responseHeaders()->GetMimeType(&mimeType);
+ if (!mimeType.length() && m_url.length())
+ mimeType = WebResponse::resolveMimeType(std::string(m_url.utf8().data(), m_url.length()), "");
+ return String(mimeType.c_str());
+}
+
+int64 CacheResult::expires() const
+{
+ // We have to do this manually, rather than using HttpResponseHeaders::GetExpiresValue(),
+ // to handle the "-1" and "0" special cases.
+ string expiresString;
+ if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, "expires", &expiresString)) {
+ wstring expiresStringWide(expiresString.begin(), expiresString.end()); // inflate ascii
+ // We require the time expressed as ms since the epoch.
+ Time time;
+ if (Time::FromString(expiresStringWide.c_str(), &time)) {
+ // Will not overflow for a very long time!
+ return static_cast<int64>(1000.0 * time.ToDoubleT());
+ }
+
+ if (expiresString == "-1" || expiresString == "0")
+ return 0;
+ }
+
+ // TODO
+ // The Android stack applies a heuristic to set an expiry date if the
+ // expires header is not set or can't be parsed. I'm not sure whether the Chromium cache
+ // does this, and if so, it may not be possible for us to get hold of it
+ // anyway to set it on the result.
+ return -1;
+}
+
+int CacheResult::responseCode() const
+{
+ return responseHeaders() ? responseHeaders()->response_code() : 0;
+}
+
+bool CacheResult::writeToFile(const String& filePath) const
+{
+ // Getting the headers is potentially async, so post to the Chromium thread
+ // and block here.
+ MutexLocker lock(m_mutex);
+
+ base::Thread* thread = WebUrlLoaderClient::ioThread();
+ if (!thread)
+ return false;
+
+ CacheResult* me = const_cast<CacheResult*>(this);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::writeToFileImpl));
+
+ m_filePath = filePath.threadsafeCopy();
+ m_isAsyncOperationInProgress = true;
+ while (m_isAsyncOperationInProgress)
+ m_condition.wait(m_mutex);
+
+ return m_wasWriteToFileSuccessful;
+}
+
+void CacheResult::writeToFileImpl()
+{
+ m_bufferSize = m_entry->GetDataSize(kResponseContentIndex);
+ m_readOffset = 0;
+ m_wasWriteToFileSuccessful = false;
+ readNextChunk();
+}
+
+void CacheResult::readNextChunk()
+{
+ m_buffer = new IOBuffer(m_bufferSize);
+ int rv = m_entry->ReadData(kResponseInfoIndex, m_readOffset, m_buffer, m_bufferSize, &m_onReadNextChunkDoneCallback);
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ onReadNextChunkDone(rv);
+};
+
+void CacheResult::onReadNextChunkDone(int size)
+{
+ if (size > 0) {
+ // Still more reading to be done.
+ if (writeChunkToFile()) {
+ // TODO: I assume that we need to clear and resize the buffer for the next read?
+ m_readOffset += size;
+ m_bufferSize -= size;
+ readNextChunk();
+ } else
+ onWriteToFileDone();
+ return;
+ }
+
+ if (!size) {
+ // Reached end of file.
+ if (writeChunkToFile())
+ m_wasWriteToFileSuccessful = true;
+ }
+ onWriteToFileDone();
+}
+
+bool CacheResult::writeChunkToFile()
+{
+ PlatformFileHandle file;
+ file = openFile(m_filePath, OpenForWrite);
+ if (!isHandleValid(file))
+ return false;
+ return WebCore::writeToFile(file, m_buffer->data(), m_bufferSize) == m_bufferSize;
+}
+
+void CacheResult::onWriteToFileDone()
+{
+ MutexLocker lock(m_mutex);
+ m_isAsyncOperationInProgress = false;
+ m_condition.signal();
+}
+
+HttpResponseHeaders* CacheResult::responseHeaders() const
+{
+ MutexLocker lock(m_mutex);
+ if (m_responseHeaders)
+ return m_responseHeaders;
+
+ // Getting the headers is potentially async, so post to the Chromium thread
+ // and block here.
+ base::Thread* thread = WebUrlLoaderClient::ioThread();
+ if (!thread)
+ return 0;
+
+ CacheResult* me = const_cast<CacheResult*>(this);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::responseHeadersImpl));
+
+ m_isAsyncOperationInProgress = true;
+ while (m_isAsyncOperationInProgress)
+ m_condition.wait(m_mutex);
+
+ return m_responseHeaders;
+}
+
+void CacheResult::responseHeadersImpl()
+{
+ m_bufferSize = m_entry->GetDataSize(kResponseInfoIndex);
+ m_buffer = new IOBuffer(m_bufferSize);
+
+ int rv = m_entry->ReadData(kResponseInfoIndex, 0, m_buffer, m_bufferSize, &m_onResponseHeadersDoneCallback);
+ if (rv == ERR_IO_PENDING)
+ return;
+
+ onResponseHeadersDone(rv);
+};
+
+void CacheResult::onResponseHeadersDone(int size)
+{
+ MutexLocker lock(m_mutex);
+ // It's OK to throw away the HttpResponseInfo object as we hold our own ref
+ // to the headers.
+ HttpResponseInfo response;
+ bool truncated = false; // TODO: Waht is this param for?
+ if (size == m_bufferSize && HttpCache::ParseResponseInfo(m_buffer->data(), m_bufferSize, &response, &truncated))
+ m_responseHeaders = response.headers;
+ m_isAsyncOperationInProgress = false;
+ m_condition.signal();
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/CacheResult.h b/Source/WebKit/android/WebCoreSupport/CacheResult.h
new file mode 100644
index 0000000..c39570c
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/CacheResult.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011, 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 CacheResult_h
+#define CacheResult_h
+
+#include "ChromiumIncludes.h"
+
+#include <wtf/RefCounted.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/text/WTFString.h>
+
+namespace android {
+
+// A wrapper around a disk_cache::Entry. Provides fields appropriate for constructing a Java CacheResult object.
+class CacheResult : public base::RefCountedThreadSafe<CacheResult> {
+public:
+ // Takes ownership of the Entry passed to the constructor.
+ CacheResult(disk_cache::Entry*, String url);
+ ~CacheResult();
+
+ int64 contentSize() const;
+ bool firstResponseHeader(const char* name, WTF::String* result, bool allowEmptyString) const;
+ // The Android stack uses the empty string if no Content-Type headers are
+ // found, so we use the same default here.
+ WTF::String mimeType() const;
+ // Returns the value of the expires header as milliseconds since the epoch.
+ int64 expires() const;
+ int responseCode() const;
+ bool writeToFile(const WTF::String& filePath) const;
+private:
+ net::HttpResponseHeaders* responseHeaders() const;
+ void responseHeadersImpl();
+ void onResponseHeadersDone(int size);
+
+ void writeToFileImpl();
+ void readNextChunk();
+ void onReadNextChunkDone(int size);
+ bool writeChunkToFile();
+ void onWriteToFileDone();
+
+ disk_cache::Entry* m_entry;
+
+ scoped_refptr<net::HttpResponseHeaders> m_responseHeaders;
+
+ int m_readOffset;
+ bool m_wasWriteToFileSuccessful;
+ mutable String m_filePath;
+
+ int m_bufferSize;
+ scoped_refptr<net::IOBuffer> m_buffer;
+ mutable bool m_isAsyncOperationInProgress;
+ mutable WTF::Mutex m_mutex;
+ mutable WTF::ThreadCondition m_condition;
+
+ net::CompletionCallbackImpl<CacheResult> m_onResponseHeadersDoneCallback;
+ net::CompletionCallbackImpl<CacheResult> m_onReadNextChunkDoneCallback;
+
+ String m_url;
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp
new file mode 100644
index 0000000..30f374f
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "config.h"
+
+#include "CachedFramePlatformDataAndroid.h"
+#include "Settings.h"
+
+namespace android {
+
+CachedFramePlatformDataAndroid::CachedFramePlatformDataAndroid(WebCore::Settings* settings)
+{
+#ifdef ANDROID_META_SUPPORT
+ m_viewport_width = settings->viewportWidth();
+ m_viewport_height = settings->viewportHeight();
+ m_viewport_initial_scale = settings->viewportInitialScale();
+ m_viewport_minimum_scale = settings->viewportMinimumScale();
+ m_viewport_maximum_scale = settings->viewportMaximumScale();
+ m_viewport_target_densitydpi = settings->viewportTargetDensityDpi();
+ m_viewport_user_scalable = settings->viewportUserScalable();
+ m_format_detection_address = settings->formatDetectionAddress();
+ m_format_detection_email = settings->formatDetectionEmail();
+ m_format_detection_telephone = settings->formatDetectionTelephone();
+#endif
+
+}
+
+#ifdef ANDROID_META_SUPPORT
+void CachedFramePlatformDataAndroid::restoreMetadata(WebCore::Settings* settings)
+{
+ settings->setViewportWidth(m_viewport_width);
+ settings->setViewportHeight(m_viewport_height);
+ settings->setViewportInitialScale(m_viewport_initial_scale);
+ settings->setViewportMinimumScale(m_viewport_minimum_scale);
+ settings->setViewportMaximumScale(m_viewport_maximum_scale);
+ settings->setViewportTargetDensityDpi(m_viewport_target_densitydpi);
+ settings->setViewportUserScalable(m_viewport_user_scalable);
+ settings->setFormatDetectionAddress(m_format_detection_address);
+ settings->setFormatDetectionEmail(m_format_detection_email);
+ settings->setFormatDetectionTelephone(m_format_detection_telephone);
+}
+#endif
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h
new file mode 100644
index 0000000..20c7be4
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h
@@ -0,0 +1,63 @@
+/*
+ * 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 CachedFramePlatformDatatAndroid_h
+#define CachedFramePlatformDatatAndroid_h
+
+#include "CachedFramePlatformData.h"
+
+namespace WebCore {
+ class Settings;
+}
+
+namespace android {
+
+class CachedFramePlatformDataAndroid : public WebCore::CachedFramePlatformData {
+public:
+ CachedFramePlatformDataAndroid(WebCore::Settings* settings);
+
+#ifdef ANDROID_META_SUPPORT
+ void restoreMetadata(WebCore::Settings* settings);
+#endif
+
+private:
+#ifdef ANDROID_META_SUPPORT
+ // meta data of the frame
+ int m_viewport_width;
+ int m_viewport_height;
+ int m_viewport_initial_scale;
+ int m_viewport_minimum_scale;
+ int m_viewport_maximum_scale;
+ int m_viewport_target_densitydpi;
+ bool m_viewport_user_scalable : 1;
+ bool m_format_detection_address : 1;
+ bool m_format_detection_email : 1;
+ bool m_format_detection_telephone : 1;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
new file mode 100644
index 0000000..6f872b8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
@@ -0,0 +1,605 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+
+#include "ApplicationCacheStorage.h"
+#include "ChromeClientAndroid.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "PlatformString.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "Geolocation.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "Icon.h"
+#include "LayerAndroid.h"
+#include "Page.h"
+#include "PopupMenuAndroid.h"
+#include "ScriptController.h"
+#include "SearchPopupMenuAndroid.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreViewBridge.h"
+#include "WebViewCore.h"
+#include "WindowFeatures.h"
+#include "Settings.h"
+#include "UserGestureIndicator.h"
+#include <wtf/text/CString.h>
+
+namespace android {
+
+#if ENABLE(DATABASE)
+static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota);
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+
+WebCore::GraphicsLayer* ChromeClientAndroid::layersSync()
+{
+ if (m_rootGraphicsLayer && m_needsLayerSync && m_webFrame) {
+ if (FrameView* frameView = m_webFrame->page()->mainFrame()->view())
+ frameView->syncCompositingStateRecursive();
+ }
+ m_needsLayerSync = false;
+ return m_rootGraphicsLayer;
+}
+
+void ChromeClientAndroid::scheduleCompositingLayerSync()
+{
+ if (m_needsLayerSync)
+ return;
+ m_needsLayerSync = true;
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view());
+ if (webViewCore)
+ webViewCore->layersDraw();
+}
+
+void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization()
+{
+ // This should not be needed
+}
+
+void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* layer)
+{
+ // frame is not used in Android as we should only get root graphics layer for the main frame
+ m_rootGraphicsLayer = layer;
+ if (!layer)
+ return;
+ scheduleCompositingLayerSync();
+}
+
+#endif
+
+void ChromeClientAndroid::setWebFrame(android::WebFrame* webframe)
+{
+ Release(m_webFrame);
+ m_webFrame = webframe;
+ Retain(m_webFrame);
+}
+
+void ChromeClientAndroid::chromeDestroyed()
+{
+ Release(m_webFrame);
+ delete this;
+}
+
+void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); }
+
+FloatRect ChromeClientAndroid::windowRect() {
+ ASSERT(m_webFrame);
+ if (!m_webFrame)
+ return FloatRect();
+ FrameView* frameView = m_webFrame->page()->mainFrame()->view();
+ if (!frameView)
+ return FloatRect();
+ const WebCoreViewBridge* bridge = frameView->platformWidget();
+ const IntRect& rect = bridge->getWindowBounds();
+ FloatRect fRect(rect.x(), rect.y(), rect.width(), rect.height());
+ return fRect;
+}
+
+FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); }
+
+float ChromeClientAndroid::scaleFactor()
+{
+ ASSERT(m_webFrame);
+ return m_webFrame->density();
+}
+
+void ChromeClientAndroid::focus()
+{
+ ASSERT(m_webFrame);
+ bool isUserGesture = UserGestureIndicator::processingUserGesture();
+
+ // Ask the application to focus this WebView if the action is intiated by the user
+ if (isUserGesture)
+ m_webFrame->requestFocus();
+}
+void ChromeClientAndroid::unfocus() { notImplemented(); }
+
+bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; }
+void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); }
+
+void ChromeClientAndroid::focusedNodeChanged(Node* node)
+{
+ android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->focusNodeChanged(node);
+}
+
+void ChromeClientAndroid::focusedFrameChanged(Frame*) { notImplemented(); }
+
+Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&,
+ const WindowFeatures& features, const NavigationAction&)
+{
+ ASSERT(frame);
+#ifdef ANDROID_MULTIPLE_WINDOWS
+ if (frame->settings() && !(frame->settings()->supportMultipleWindows()))
+ // If the client doesn't support multiple windows, just return the current page
+ return frame->page();
+#endif
+
+ const WebCoreViewBridge* bridge = frame->view()->platformWidget();
+ bool dialog = features.dialog || !features.resizable
+ || (features.heightSet && features.height < bridge->height()
+ && features.widthSet && features.width < bridge->width())
+ || (!features.menuBarVisible && !features.statusBarVisible
+ && !features.toolBarVisible && !features.locationBarVisible
+ && !features.scrollbarsVisible);
+ // fullscreen definitely means no dialog
+ if (features.fullscreen)
+ dialog = false;
+ WebCore::Frame* newFrame = m_webFrame->createWindow(dialog,
+ ScriptController::processingUserGesture());
+ if (newFrame) {
+ WebCore::Page* page = newFrame->page();
+ page->setGroupName(frame->page()->groupName());
+ return page;
+ }
+ return NULL;
+}
+
+void ChromeClientAndroid::show() { notImplemented(); }
+
+bool ChromeClientAndroid::canRunModal() { notImplemented(); return false; }
+void ChromeClientAndroid::runModal() { notImplemented(); }
+
+void ChromeClientAndroid::setToolbarsVisible(bool) { notImplemented(); }
+bool ChromeClientAndroid::toolbarsVisible() { notImplemented(); return false; }
+
+void ChromeClientAndroid::setStatusbarVisible(bool) { notImplemented(); }
+bool ChromeClientAndroid::statusbarVisible() { notImplemented(); return false; }
+
+void ChromeClientAndroid::setScrollbarsVisible(bool) { notImplemented(); }
+bool ChromeClientAndroid::scrollbarsVisible() { notImplemented(); return false; }
+
+void ChromeClientAndroid::setMenubarVisible(bool) { notImplemented(); }
+bool ChromeClientAndroid::menubarVisible() { notImplemented(); return false; }
+
+void ChromeClientAndroid::setResizable(bool) { notImplemented(); }
+
+#if ENABLE(CONTEXT_MENUS)
+void ChromeClientAndroid::showContextMenu() { notImplemented(); }
+#endif
+
+// This function is called by the JavaScript bindings to print usually an error to
+// a message console. Pass the message to the java side so that the client can
+// handle it as it sees fit.
+void ChromeClientAndroid::addMessageToConsole(MessageSource, MessageType, MessageLevel msgLevel, const String& message, unsigned int lineNumber, const String& sourceID) {
+ android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->addMessageToConsole(message, lineNumber, sourceID, msgLevel);
+}
+
+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();
+ return android::WebViewCore::getWebViewCore(frame->view())->jsUnload(url, message);
+}
+
+void ChromeClientAndroid::closeWindowSoon()
+{
+ ASSERT(m_webFrame);
+ Page* page = m_webFrame->page();
+ Frame* mainFrame = page->mainFrame();
+ // This will prevent javascript cross-scripting during unload
+ page->setGroupName(String());
+ // Stop loading but do not send the unload event
+ mainFrame->loader()->stopLoading(UnloadEventPolicyNone);
+ // Cancel all pending loaders
+ mainFrame->loader()->stopAllLoaders();
+ // Remove all event listeners so that no javascript can execute as a result
+ // of mouse/keyboard events.
+ mainFrame->document()->removeAllEventListeners();
+ // Close the window.
+ m_webFrame->closeWindow(android::WebViewCore::getWebViewCore(mainFrame->view()));
+}
+
+void ChromeClientAndroid::runJavaScriptAlert(Frame* frame, const String& message)
+{
+ String url = frame->document()->documentURI();
+
+ android::WebViewCore::getWebViewCore(frame->view())->jsAlert(url, message);
+}
+
+bool ChromeClientAndroid::runJavaScriptConfirm(Frame* frame, const String& message)
+{
+ String url = frame->document()->documentURI();
+
+ return android::WebViewCore::getWebViewCore(frame->view())->jsConfirm(url, message);
+}
+
+/* This function is called for the javascript method Window.prompt(). A dialog should be shown on
+ * the screen with an input put box. First param is the text, the second is the default value for
+ * the input box, third is return param. If the function returns true, the value set in the third parameter
+ * is provided to javascript, else null is returned to the script.
+ */
+bool ChromeClientAndroid::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
+{
+ String url = frame->document()->documentURI();
+ return android::WebViewCore::getWebViewCore(frame->view())->jsPrompt(url, message, defaultValue, result);
+}
+void ChromeClientAndroid::setStatusbarText(const String&) { notImplemented(); }
+
+// This is called by the JavaScript interpreter when a script has been running for a long
+// time. A dialog should be shown to the user asking them if they would like to cancel the
+// Javascript. If true is returned, the script is cancelled.
+// To make a device more responsive, we default to return true to disallow long running script.
+// This implies that some of scripts will not be completed.
+bool ChromeClientAndroid::shouldInterruptJavaScript() {
+ FrameView* frameView = m_webFrame->page()->mainFrame()->view();
+ return android::WebViewCore::getWebViewCore(frameView)->jsInterrupt();
+}
+
+bool ChromeClientAndroid::tabsToLinks() const { return false; }
+
+IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); }
+
+void ChromeClientAndroid::invalidateWindow(const IntRect&, bool)
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::invalidateContentsAndWindow(const IntRect& updateRect, bool /*immediate*/)
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
+{
+ notImplemented();
+}
+
+// new to change 38068 (Nov 6, 2008)
+void ChromeClientAndroid::scroll(const IntSize& scrollDelta,
+ const IntRect& rectToScroll, const IntRect& clipRect) {
+ notImplemented();
+}
+
+// new to change 38068 (Nov 6, 2008)
+IntPoint ChromeClientAndroid::screenToWindow(const IntPoint&) const {
+ notImplemented();
+ return IntPoint();
+}
+
+// new to change 38068 (Nov 6, 2008)
+IntRect ChromeClientAndroid::windowToScreen(const IntRect&) const {
+ notImplemented();
+ return IntRect();
+}
+
+PlatformPageClient ChromeClientAndroid::platformPageClient() const {
+ Page* page = m_webFrame->page();
+ Frame* mainFrame = page->mainFrame();
+ FrameView* view = mainFrame->view();
+ PlatformWidget viewBridge = view->platformWidget();
+ return viewBridge;
+}
+
+void ChromeClientAndroid::contentsSizeChanged(Frame*, const IntSize&) const
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::scrollRectIntoView(const IntRect&, const ScrollView*) const
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::formStateDidChange(const Node*)
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::scrollbarsModeDidChange() const
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {}
+void ChromeClientAndroid::setToolTip(const String&, TextDirection) {}
+void ChromeClientAndroid::print(Frame*) {}
+
+/*
+ * This function is called on the main (webcore) thread by SQLTransaction::deliverQuotaIncreaseCallback.
+ * The way that the callback mechanism is designed inside SQLTransaction means that there must be a new quota
+ * (which may be equal to the old quota if the user did not allow more quota) when this function returns. As
+ * we call into the browser thread to ask what to do with the quota, we block here and get woken up when the
+ * browser calls the native WebViewCore::SetDatabaseQuota method with the new quota value.
+ */
+#if ENABLE(DATABASE)
+void ChromeClientAndroid::exceededDatabaseQuota(Frame* frame, const String& name)
+{
+ SecurityOrigin* origin = frame->document()->securityOrigin();
+ DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
+
+ // We want to wait on a new quota from the UI thread. Reset the m_newQuota variable to represent we haven't received a new quota.
+ m_newQuota = -1;
+
+ // This origin is being tracked and has exceeded it's quota. Call into
+ // the Java side of things to inform the user.
+ unsigned long long currentQuota = 0;
+ if (tracker.hasEntryForOrigin(origin))
+ currentQuota = tracker.quotaForOrigin(origin);
+
+ unsigned long long estimatedSize = 0;
+
+ // Only update estimatedSize if we are trying to create a a new database, i.e. the usage for the database is 0.
+ if (tracker.usageForDatabase(name, origin) == 0)
+ estimatedSize = tracker.detailsForNameAndOrigin(name, origin).expectedUsage();
+
+ android::WebViewCore::getWebViewCore(frame->view())->exceededDatabaseQuota(frame->document()->documentURI(), name, currentQuota, estimatedSize);
+
+ // We've sent notification to the browser so now wait for it to come back.
+ m_quotaThreadLock.lock();
+ while (m_newQuota == -1) {
+ m_quotaThreadCondition.wait(m_quotaThreadLock);
+ }
+ m_quotaThreadLock.unlock();
+
+ // If new quota is unavailable, we may be able to resolve the situation by shrinking the quota of an origin that asked for a lot but is only using a little.
+ // If we find such a site, shrink it's quota and ask Java to try again.
+
+ if ((unsigned long long) m_newQuota == currentQuota && !m_triedToReclaimDBQuota) {
+ m_triedToReclaimDBQuota = true; // we should only try this once per quota overflow.
+ unsigned long long reclaimedQuotaBytes = tryToReclaimDatabaseQuota(origin);
+
+ // If we were able to free up enough space, try asking Java again.
+ // Otherwise, give up and deny the new database. :(
+ if (reclaimedQuotaBytes >= estimatedSize) {
+ exceededDatabaseQuota(frame, name);
+ return;
+ }
+ }
+
+ // Update the DatabaseTracker with the new quota value (if the user declined
+ // new quota, this may equal the old quota)
+ tracker.setQuota(origin, m_newQuota);
+ m_triedToReclaimDBQuota = false;
+}
+
+static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota) {
+ DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker();
+ Vector<RefPtr<SecurityOrigin> > origins;
+ tracker.origins(origins);
+ unsigned long long reclaimedQuotaBytes = 0;
+ for (unsigned i = 0; i < origins.size(); i++) {
+ SecurityOrigin* originToReclaimFrom = origins[i].get();
+
+ // Don't try to reclaim from the origin that has exceeded its quota.
+ if (originToReclaimFrom->equal(originNeedingQuota))
+ continue;
+
+ unsigned long long originUsage = tracker.usageForOrigin(originToReclaimFrom);
+ unsigned long long originQuota = tracker.quotaForOrigin(originToReclaimFrom);
+ // If the origin has a quota that is more than it's current usage +1MB, shrink it.
+ static const int ONE_MB = 1 * 1024 * 1024;
+ if (originUsage + ONE_MB < originQuota) {
+ unsigned long long newQuota = originUsage + ONE_MB;
+ tracker.setQuota(originToReclaimFrom, newQuota);
+ reclaimedQuotaBytes += originQuota - newQuota;
+ }
+ }
+ return reclaimedQuotaBytes;
+}
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void ChromeClientAndroid::reachedMaxAppCacheSize(int64_t spaceNeeded)
+{
+ // Set m_newQuota before calling into the Java side. If we do this after,
+ // we could overwrite the result passed from the Java side and deadlock in the
+ // wait call below.
+ m_newQuota = -1;
+ Page* page = m_webFrame->page();
+ Frame* mainFrame = page->mainFrame();
+ FrameView* view = mainFrame->view();
+ android::WebViewCore::getWebViewCore(view)->reachedMaxAppCacheSize(spaceNeeded);
+
+ // We've sent notification to the browser so now wait for it to come back.
+ m_quotaThreadLock.lock();
+ while (m_newQuota == -1) {
+ m_quotaThreadCondition.wait(m_quotaThreadLock);
+ }
+ m_quotaThreadLock.unlock();
+ if (m_newQuota > 0) {
+ WebCore::cacheStorage().setMaximumSize(m_newQuota);
+ // Now the app cache will retry the saving the previously failed cache.
+ }
+}
+#endif
+
+void ChromeClientAndroid::populateVisitedLinks()
+{
+ Page* page = m_webFrame->page();
+ Frame* mainFrame = page->mainFrame();
+ FrameView* view = mainFrame->view();
+ android::WebViewCore::getWebViewCore(view)->populateVisitedLinks(&page->group());
+}
+
+void ChromeClientAndroid::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
+{
+ ASSERT(geolocation);
+ if (!m_geolocationPermissions) {
+ m_geolocationPermissions = new GeolocationPermissions(android::WebViewCore::getWebViewCore(frame->view()),
+ m_webFrame->page()->mainFrame());
+ }
+ m_geolocationPermissions->queryPermissionState(frame);
+}
+
+void ChromeClientAndroid::cancelGeolocationPermissionRequestForFrame(Frame* frame, WebCore::Geolocation*)
+{
+ if (m_geolocationPermissions)
+ m_geolocationPermissions->cancelPermissionStateQuery(frame);
+}
+
+void ChromeClientAndroid::provideGeolocationPermissions(const String &origin, bool allow, bool remember)
+{
+ ASSERT(m_geolocationPermissions);
+ m_geolocationPermissions->providePermissionState(origin, allow, remember);
+}
+
+void ChromeClientAndroid::storeGeolocationPermissions()
+{
+ GeolocationPermissions::maybeStorePermanentPermissions();
+}
+
+void ChromeClientAndroid::onMainFrameLoadStarted()
+{
+ if (m_geolocationPermissions.get())
+ m_geolocationPermissions->resetTemporaryPermissionStates();
+}
+
+void ChromeClientAndroid::runOpenPanel(Frame* frame,
+ PassRefPtr<FileChooser> chooser)
+{
+ android::WebViewCore* core = android::WebViewCore::getWebViewCore(
+ frame->view());
+ core->openFileChooser(chooser);
+}
+
+void ChromeClientAndroid::chooseIconForFiles(const Vector<WTF::String>&, FileChooser*)
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::setCursor(const Cursor&)
+{
+ notImplemented();
+}
+
+void ChromeClientAndroid::wakeUpMainThreadWithNewQuota(long newQuota) {
+ MutexLocker locker(m_quotaThreadLock);
+ m_newQuota = newQuota;
+ m_quotaThreadCondition.signal();
+}
+
+#if ENABLE(TOUCH_EVENTS)
+void ChromeClientAndroid::needTouchEvents(bool needTouchEvents)
+{
+ FrameView* frameView = m_webFrame->page()->mainFrame()->view();
+ android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
+ if (core)
+ core->needTouchEvents(needTouchEvents);
+}
+#endif
+
+bool ChromeClientAndroid::selectItemWritingDirectionIsNatural()
+{
+ return false;
+}
+
+PassRefPtr<PopupMenu> ChromeClientAndroid::createPopupMenu(PopupMenuClient* client) const
+{
+ return adoptRef(new PopupMenuAndroid(static_cast<ListPopupMenuClient*>(client)));
+}
+
+PassRefPtr<SearchPopupMenu> ChromeClientAndroid::createSearchPopupMenu(PopupMenuClient*) const
+{
+ return adoptRef(new SearchPopupMenuAndroid);
+}
+
+void ChromeClientAndroid::reachedApplicationCacheOriginQuota(SecurityOrigin*)
+{
+ notImplemented();
+}
+
+#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS)
+void ChromeClientAndroid::webAppCanBeInstalled()
+{
+ FrameView* frameView = m_webFrame->page()->mainFrame()->view();
+ android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
+ if (core)
+ core->notifyWebAppCanBeInstalled();
+}
+#endif
+
+#if ENABLE(VIDEO)
+bool ChromeClientAndroid::supportsFullscreenForNode(const Node* node)
+{
+ return node->hasTagName(HTMLNames::videoTag);
+}
+
+void ChromeClientAndroid::enterFullscreenForNode(Node* node)
+{
+ if (!node->hasTagName(HTMLNames::videoTag))
+ return;
+
+ HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(node);
+ String url = videoElement->currentSrc();
+ LayerAndroid* layer = videoElement->platformLayer();
+ if (!layer)
+ return;
+
+ FrameView* frameView = m_webFrame->page()->mainFrame()->view();
+ android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView);
+ m_webFrame->page()->mainFrame()->document()->webkitWillEnterFullScreenForElement(videoElement);
+ if (core)
+ core->enterFullscreenForVideoLayer(layer->uniqueId(), url);
+}
+
+void ChromeClientAndroid::exitFullscreenForNode(Node* node)
+{
+}
+#endif
+
+#if ENABLE(FULLSCREEN_API)
+void ChromeClientAndroid::exitFullScreenForElement(Element* element)
+{
+ if (!element)
+ return;
+
+ HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(element);
+ videoElement->exitFullscreen();
+}
+#endif
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
new file mode 100644
index 0000000..d49cec8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
@@ -0,0 +1,216 @@
+/*
+ * 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 ChromeClientAndroid_h
+#define ChromeClientAndroid_h
+
+#include "ChromeClient.h"
+
+#include "GeolocationPermissions.h"
+#include "PopupMenu.h"
+#include "SearchPopupMenu.h"
+#include "Timer.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+class PopupMenuClient;
+class Geolocation;
+}
+
+using namespace WebCore;
+
+namespace android {
+ class WebFrame;
+
+ class ChromeClientAndroid : public ChromeClient {
+ public:
+ ChromeClientAndroid() : m_webFrame(0), m_geolocationPermissions(0)
+#if USE(ACCELERATED_COMPOSITING)
+ , m_rootGraphicsLayer(0)
+ , m_needsLayerSync(false)
+#endif
+ , m_triedToReclaimDBQuota(false)
+ { }
+ virtual void chromeDestroyed();
+
+ virtual void setWindowRect(const FloatRect&);
+ virtual FloatRect windowRect();
+
+ virtual FloatRect pageRect();
+
+ virtual float scaleFactor();
+
+ virtual void focus();
+ virtual void unfocus();
+ virtual void formDidBlur(const WebCore::Node*);
+ virtual bool canTakeFocus(FocusDirection);
+ virtual void takeFocus(FocusDirection);
+
+ virtual void focusedNodeChanged(Node*);
+ virtual void focusedFrameChanged(Frame*);
+
+ // The Frame pointer provides the ChromeClient with context about which
+ // Frame wants to create the new Page. Also, the newly created window
+ // should not be shown to the user until the ChromeClient of the newly
+ // created Page has its show method called.
+ virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&);
+ virtual void show();
+
+ virtual bool canRunModal();
+ virtual void runModal();
+
+ virtual void setToolbarsVisible(bool);
+ virtual bool toolbarsVisible();
+
+ virtual void setStatusbarVisible(bool);
+ virtual bool statusbarVisible();
+
+ virtual void setScrollbarsVisible(bool);
+ virtual bool scrollbarsVisible();
+
+ virtual void setMenubarVisible(bool);
+ virtual bool menubarVisible();
+
+ virtual void setResizable(bool);
+
+ virtual void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, unsigned int lineNumber, const String& sourceID);
+
+ virtual bool canRunBeforeUnloadConfirmPanel();
+ virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame);
+
+ virtual void closeWindowSoon();
+
+ virtual void runJavaScriptAlert(Frame*, const String&);
+ virtual bool runJavaScriptConfirm(Frame*, const String&);
+ virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result);
+ virtual void setStatusbarText(const String&);
+ virtual bool shouldInterruptJavaScript();
+ virtual bool tabsToLinks() const;
+
+ virtual IntRect windowResizerRect() const;
+
+ // Methods used by HostWindow.
+ virtual void invalidateWindow(const WebCore::IntRect&, bool);
+ virtual void invalidateContentsAndWindow(const WebCore::IntRect&, bool);
+ virtual void invalidateContentsForSlowScroll(const WebCore::IntRect&, bool);
+ virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect);
+ virtual IntPoint screenToWindow(const IntPoint&) const;
+ virtual IntRect windowToScreen(const IntRect&) const;
+ virtual PlatformPageClient platformPageClient() const;
+ virtual void contentsSizeChanged(Frame*, const IntSize&) const;
+ virtual void scrollRectIntoView(const IntRect&, const ScrollView*) const;
+ // End methods used by HostWindow.
+
+ virtual void scrollbarsModeDidChange() const;
+ virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned int);
+
+ virtual void setToolTip(const String&, TextDirection);
+
+ virtual void print(Frame*);
+#if ENABLE(DATABASE)
+ virtual void exceededDatabaseQuota(Frame*, const String&);
+#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ virtual void reachedMaxAppCacheSize(int64_t spaceNeeded);
+ virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*);
+#endif
+
+ virtual void populateVisitedLinks();
+
+#if ENABLE(TOUCH_EVENTS)
+ virtual void needTouchEvents(bool);
+#endif
+
+ // Methods used to request and provide Geolocation permissions.
+ virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*);
+ virtual void cancelGeolocationPermissionRequestForFrame(WebCore::Frame*, WebCore::Geolocation*);
+ // Android-specific
+ void provideGeolocationPermissions(const String &origin, bool allow, bool remember);
+ void storeGeolocationPermissions();
+ void onMainFrameLoadStarted();
+
+ virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
+ virtual void setCursor(const Cursor&);
+ virtual void chooseIconForFiles(const WTF::Vector<WTF::String>&, FileChooser*);
+
+ // Notification that the given form element has changed. This function
+ // will be called frequently, so handling should be very fast.
+ virtual void formStateDidChange(const Node*);
+
+ virtual PassOwnPtr<HTMLParserQuirks> createHTMLParserQuirks() { return 0; }
+
+ // Android-specific
+ void setWebFrame(android::WebFrame* webframe);
+ android::WebFrame* webFrame() { return m_webFrame; }
+ void wakeUpMainThreadWithNewQuota(long newQuota);
+
+#if USE(ACCELERATED_COMPOSITING)
+ virtual void attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* g);
+ virtual void setNeedsOneShotDrawingSynchronization();
+ virtual void scheduleCompositingLayerSync();
+ virtual bool allowsAcceleratedCompositing() const { return true; }
+ WebCore::GraphicsLayer* layersSync();
+#endif
+
+ virtual bool selectItemWritingDirectionIsNatural();
+ virtual PassRefPtr<WebCore::PopupMenu> createPopupMenu(WebCore::PopupMenuClient*) const;
+ virtual PassRefPtr<WebCore::SearchPopupMenu> createSearchPopupMenu(WebCore::PopupMenuClient*) const;
+
+#if ENABLE(CONTEXT_MENUS)
+ virtual void showContextMenu();
+#endif
+
+#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS)
+ virtual void webAppCanBeInstalled();
+#endif
+
+#if ENABLE(FULLSCREEN_API)
+ virtual void exitFullScreenForElement(Element*);
+#endif
+
+#if ENABLE(VIDEO)
+ virtual bool supportsFullscreenForNode(const WebCore::Node*);
+ virtual void enterFullscreenForNode(WebCore::Node*);
+ virtual void exitFullscreenForNode(WebCore::Node*);
+#endif
+
+ private:
+ android::WebFrame* m_webFrame;
+ // The Geolocation permissions manager.
+ OwnPtr<GeolocationPermissions> m_geolocationPermissions;
+#if USE(ACCELERATED_COMPOSITING)
+ WebCore::GraphicsLayer* m_rootGraphicsLayer;
+ bool m_needsLayerSync;
+#endif
+ WTF::ThreadCondition m_quotaThreadCondition;
+ WTF::Mutex m_quotaThreadLock;
+ long m_newQuota;
+ bool m_triedToReclaimDBQuota;
+ };
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h
new file mode 100644
index 0000000..8166eb7
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h
@@ -0,0 +1,104 @@
+/*
+ * 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 ChromiumIncludes_h
+#define ChromiumIncludes_h
+
+#include "config.h"
+
+// 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 LOG
+#undef LOG
+#endif
+
+#ifdef LOG_ASSERT
+#define LOG_ASSERT_WAS_DEFINED LOG_ASSERT
+#undef LOG_ASSERT
+#endif
+
+#include <android/net/android_network_library_impl.h>
+#include <base/callback.h>
+#include <base/condition_variable.h>
+#include <base/lock.h>
+#include <base/message_loop_proxy.h>
+#include <base/ref_counted.h>
+#include <base/string_util.h>
+#include <base/sys_string_conversions.h>
+#include <base/thread.h>
+#include <base/time.h>
+#include <base/tuple.h>
+#include <chrome/browser/net/sqlite_persistent_cookie_store.h>
+#include <net/base/auth.h>
+#include <net/base/cookie_monster.h>
+#include <net/base/cookie_policy.h>
+#include <net/base/data_url.h>
+#include <net/base/host_resolver.h>
+#include <net/base/io_buffer.h>
+#include <net/base/load_flags.h>
+#include <net/base/net_errors.h>
+#include <net/base/mime_util.h>
+#include <net/base/ssl_config_service.h>
+#include <net/disk_cache/disk_cache.h>
+#include <net/http/http_auth_handler_factory.h>
+#include <net/http/http_cache.h>
+#include <net/http/http_network_layer.h>
+#include <net/http/http_response_headers.h>
+#include <net/proxy/proxy_config_service_android.h>
+#include <net/proxy/proxy_service.h>
+#include <net/url_request/url_request.h>
+#include <net/url_request/url_request_context.h>
+
+#if ENABLE(WEB_AUTOFILL)
+#include <autofill/autofill_manager.h>
+#include <autofill/autofill_profile.h>
+#include <autofill/personal_data_manager.h>
+#include <base/logging.h>
+#include <base/scoped_vector.h>
+#include <base/string16.h>
+#include <base/utf_string_conversions.h>
+#include <chrome/browser/autofill/autofill_host.h>
+#include <chrome/browser/profile.h>
+#include <chrome/browser/tab_contents/tab_contents.h>
+#include <webkit/glue/form_data.h>
+#endif
+
+#undef LOG
+#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/ChromiumInit.cpp b/Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp
new file mode 100644
index 0000000..1872fb9
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "ChromiumInit.h"
+
+#include "ChromiumIncludes.h"
+
+#include <cutils/log.h>
+#include <string>
+
+namespace android {
+
+bool logMessageHandler(int severity, const char* file, int line, size_t message_start, const std::string& str) {
+ int androidSeverity = ANDROID_LOG_VERBOSE;
+ switch(severity) {
+ case logging::LOG_FATAL:
+ androidSeverity = ANDROID_LOG_FATAL;
+ break;
+ case logging::LOG_ERROR_REPORT:
+ case logging::LOG_ERROR:
+ androidSeverity = ANDROID_LOG_ERROR;
+ break;
+ case logging::LOG_WARNING:
+ androidSeverity = ANDROID_LOG_WARN;
+ break;
+ default:
+ androidSeverity = ANDROID_LOG_VERBOSE;
+ break;
+ }
+ android_printLog(androidSeverity, "chromium", "%s:%d: %s", file, line, str.c_str());
+ return false;
+}
+
+namespace {
+ scoped_ptr<net::NetworkChangeNotifier> networkChangeNotifier;
+}
+
+void initChromium()
+{
+ static Lock lock;
+ AutoLock aLock(lock);
+ static bool initCalled = false;
+ if (!initCalled) {
+ logging::SetLogMessageHandler(logMessageHandler);
+ networkChangeNotifier.reset(net::NetworkChangeNotifier::Create());
+ net::HttpNetworkLayer::EnableSpdy("npn");
+ initCalled = true;
+ }
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumInit.h b/Source/WebKit/android/WebCoreSupport/ChromiumInit.h
new file mode 100644
index 0000000..235c3dc
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ChromiumInit.h
@@ -0,0 +1,38 @@
+/*
+ * 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 ChromiumLogging_h
+#define ChromiumLogging_h
+
+namespace android {
+
+// Sends chromium logs to logcat
+//
+// This only calls into chromium once, but can be called multiple times.
+// It should be called before any other calls into external/chromium.
+void initChromium();
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp
new file mode 100644
index 0000000..3dc4b00
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "config.h"
+#include "ContextMenuClientAndroid.h"
+
+#include "NotImplemented.h"
+#include <wtf/Assertions.h>
+
+namespace WebCore {
+
+void ContextMenuClientAndroid::contextMenuDestroyed() { delete this; }
+
+PlatformMenuDescription ContextMenuClientAndroid::getCustomMenuFromDefaultItems(ContextMenu*) { notImplemented(); return 0; }
+void ContextMenuClientAndroid::contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { notImplemented(); }
+
+void ContextMenuClientAndroid::downloadURL(const KURL& url) { notImplemented(); }
+void ContextMenuClientAndroid::copyImageToClipboard(const HitTestResult&) { notImplemented(); }
+void ContextMenuClientAndroid::searchWithGoogle(const Frame*) { notImplemented(); }
+void ContextMenuClientAndroid::lookUpInDictionary(Frame*) { notImplemented(); }
+void ContextMenuClientAndroid::speak(const String&) { notImplemented(); }
+void ContextMenuClientAndroid::stopSpeaking() { notImplemented(); }
+bool ContextMenuClientAndroid::isSpeaking()
+{
+ notImplemented();
+ return false;
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h
new file mode 100644
index 0000000..4563829
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h
@@ -0,0 +1,51 @@
+/*
+ * 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 ContextMenuClientAndroid_h
+#define ContextMenuClientAndroid_h
+
+#include "ContextMenuClient.h"
+
+namespace WebCore {
+
+class ContextMenuClientAndroid : public ContextMenuClient {
+public:
+ virtual void contextMenuDestroyed();
+
+ virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*);
+ virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*);
+
+ virtual void downloadURL(const KURL& url);
+ virtual void copyImageToClipboard(const HitTestResult&);
+ virtual void searchWithGoogle(const Frame*);
+ virtual void lookUpInDictionary(Frame*);
+ virtual void speak(const String&);
+ virtual void stopSpeaking();
+ virtual bool isSpeaking();
+};
+
+} // namespace WebCore
+
+#endif // ContextMenuClientAndroid_h
diff --git a/Source/WebKit/android/WebCoreSupport/CookieClient.h b/Source/WebKit/android/WebCoreSupport/CookieClient.h
new file mode 100644
index 0000000..56d9382
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/CookieClient.h
@@ -0,0 +1,46 @@
+/*
+ * 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 COOKIE_CLIENT_H
+#define COOKIE_CLIENT_H
+
+#include <KURL.h>
+#include <PlatformString.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class CookieClient {
+
+public:
+ virtual ~CookieClient() {}
+ virtual void setCookies(const KURL& url, const String& value) = 0;
+ virtual String cookies(const KURL& url) = 0;
+ virtual bool cookiesEnabled() = 0;
+};
+
+}
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp
new file mode 100644
index 0000000..f8de733
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "DeviceMotionClientAndroid.h"
+
+#include "WebViewCore.h"
+
+using namespace WebCore;
+
+namespace android {
+
+DeviceMotionClientAndroid::DeviceMotionClientAndroid()
+ : m_client(0)
+{
+}
+
+void DeviceMotionClientAndroid::setWebViewCore(WebViewCore* webViewCore)
+{
+ m_webViewCore = webViewCore;
+ ASSERT(m_webViewCore);
+}
+
+void DeviceMotionClientAndroid::setController(DeviceMotionController* controller)
+{
+ // This will be called by the Page constructor before the WebViewCore
+ // has been configured regarding the mock. We cache the controller for
+ // later use.
+ m_controller = controller;
+ ASSERT(m_controller);
+}
+
+void DeviceMotionClientAndroid::startUpdating()
+{
+ client()->startUpdating();
+}
+
+void DeviceMotionClientAndroid::stopUpdating()
+{
+ client()->stopUpdating();
+}
+
+DeviceMotionData* DeviceMotionClientAndroid::currentDeviceMotion() const
+{
+ return client()->currentDeviceMotion();
+}
+
+void DeviceMotionClientAndroid::deviceMotionControllerDestroyed()
+{
+ delete this;
+}
+
+DeviceMotionClient* DeviceMotionClientAndroid::client() const
+{
+ if (!m_client) {
+ m_client = m_webViewCore->deviceMotionAndOrientationManager()->motionClient();
+ m_client->setController(m_controller);
+ }
+ return m_client;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h
new file mode 100644
index 0000000..98d4709
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h
@@ -0,0 +1,65 @@
+/*
+ * 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 DeviceMotionClientAndroid_h
+#define DeviceMotionClientAndroid_h
+
+#include <DeviceMotionClient.h>
+
+namespace WebCore {
+class DeviceMotionController;
+}
+
+namespace android {
+
+class WebViewCore;
+
+// The Android implementation of DeviceMotionClient. Acts as a proxy to
+// the real or mock impl, which is owned by the WebViewCore.
+class DeviceMotionClientAndroid : public WebCore::DeviceMotionClient {
+public:
+ DeviceMotionClientAndroid();
+
+ void setWebViewCore(WebViewCore*);
+
+ // DeviceMotionClient methods
+ virtual void setController(WebCore::DeviceMotionController*);
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual WebCore::DeviceMotionData* currentDeviceMotion() const;
+ virtual void deviceMotionControllerDestroyed();
+
+private:
+ WebCore::DeviceMotionClient* client() const;
+
+ WebViewCore* m_webViewCore;
+ WebCore::DeviceMotionController* m_controller;
+ // Lazily obtained cache of the client owned by the WebViewCore.
+ mutable WebCore::DeviceMotionClient* m_client;
+};
+
+} // namespace android
+
+#endif // DeviceMotionClientAndroid_h
diff --git a/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp
new file mode 100644
index 0000000..9d7145c
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "DeviceOrientationClientAndroid.h"
+
+#include "WebViewCore.h"
+
+using namespace WebCore;
+
+namespace android {
+
+DeviceOrientationClientAndroid::DeviceOrientationClientAndroid()
+ : m_client(0)
+{
+}
+
+void DeviceOrientationClientAndroid::setWebViewCore(WebViewCore* webViewCore)
+{
+ m_webViewCore = webViewCore;
+ ASSERT(m_webViewCore);
+}
+
+void DeviceOrientationClientAndroid::setController(DeviceOrientationController* controller)
+{
+ // This will be called by the Page constructor before the WebViewCore
+ // has been configured regarding the mock. We cache the controller for
+ // later use.
+ m_controller = controller;
+ ASSERT(m_controller);
+}
+
+void DeviceOrientationClientAndroid::startUpdating()
+{
+ client()->startUpdating();
+}
+
+void DeviceOrientationClientAndroid::stopUpdating()
+{
+ client()->stopUpdating();
+}
+
+DeviceOrientation* DeviceOrientationClientAndroid::lastOrientation() const
+{
+ return client()->lastOrientation();
+}
+
+void DeviceOrientationClientAndroid::deviceOrientationControllerDestroyed()
+{
+ delete this;
+}
+
+DeviceOrientationClient* DeviceOrientationClientAndroid::client() const
+{
+ if (!m_client) {
+ m_client = m_webViewCore->deviceMotionAndOrientationManager()->orientationClient();
+ m_client->setController(m_controller);
+ }
+ return m_client;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h
new file mode 100644
index 0000000..7842b95
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h
@@ -0,0 +1,65 @@
+/*
+ * 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 DeviceOrientationClientAndroid_h
+#define DeviceOrientationClientAndroid_h
+
+#include <DeviceOrientationClient.h>
+
+namespace WebCore {
+class DeviceOrientationController;
+}
+
+namespace android {
+
+class WebViewCore;
+
+// The Android implementation of DeviceOrientationClient. Acts as a proxy to
+// the real or mock impl, which is owned by the WebViewCore.
+class DeviceOrientationClientAndroid : public WebCore::DeviceOrientationClient {
+public:
+ DeviceOrientationClientAndroid();
+
+ void setWebViewCore(WebViewCore*);
+
+ // DeviceOrientationClient methods
+ virtual void setController(WebCore::DeviceOrientationController*);
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual WebCore::DeviceOrientation* lastOrientation() const;
+ virtual void deviceOrientationControllerDestroyed();
+
+private:
+ WebCore::DeviceOrientationClient* client() const;
+
+ WebViewCore* m_webViewCore;
+ WebCore::DeviceOrientationController* m_controller;
+ // Lazily obtained cache of the client owned by the WebViewCore.
+ mutable WebCore::DeviceOrientationClient* m_client;
+};
+
+} // namespace android
+
+#endif // DeviceOrientationClientAndroid_h
diff --git a/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp
new file mode 100644
index 0000000..f64b80c
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "DragClientAndroid.h"
+#include "NotImplemented.h"
+
+namespace android {
+
+void DragClientAndroid::dragControllerDestroyed() { notImplemented(); delete this; }
+
+void DragClientAndroid::willPerformDragDestinationAction(DragDestinationAction, DragData*) { notImplemented(); }
+
+DragDestinationAction DragClientAndroid::actionMaskForDrag(DragData*) { notImplemented(); return DragDestinationActionNone; }
+
+DragSourceAction DragClientAndroid::dragSourceActionMaskForPoint(const IntPoint&) { notImplemented(); return DragSourceActionNone; }
+
+void* DragClientAndroid::createDragImageForLink(KURL&, String const&, Frame*) { return NULL; }
+void DragClientAndroid::willPerformDragSourceAction(DragSourceAction, IntPoint const&, Clipboard*) {}
+void DragClientAndroid::startDrag(void*, IntPoint const&, IntPoint const&, Clipboard*, Frame*, bool) {}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h
new file mode 100644
index 0000000..020e1f1
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h
@@ -0,0 +1,51 @@
+/*
+ * 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 DragClientAndroid_h
+#define DragClientAndroid_h
+
+#include "DragClient.h"
+
+using namespace WebCore;
+
+namespace android {
+
+ class DragClientAndroid : public DragClient {
+ public:
+ virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*);
+ virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*);
+ virtual DragDestinationAction actionMaskForDrag(DragData*);
+ //We work in window rather than view coordinates here
+ virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&);
+
+ virtual void startDrag(DragImageRef dragImage, const IntPoint& dragImageOrigin, const IntPoint& eventPos, Clipboard*, Frame*, bool linkDrag = false);
+ virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*);
+
+ virtual void dragControllerDestroyed();
+ };
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
new file mode 100644
index 0000000..250fdbf
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "HTMLNames.h"
+#include "KeyboardEvent.h"
+#include "NotImplemented.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "WebViewCore.h"
+#include "WindowsKeyboardCodes.h"
+
+using namespace WebCore::HTMLNames;
+
+namespace android {
+
+void EditorClientAndroid::pageDestroyed() {
+ delete this;
+}
+
+bool EditorClientAndroid::shouldDeleteRange(Range*) { return true; }
+bool EditorClientAndroid::shouldShowDeleteInterface(HTMLElement*) { notImplemented(); return false; }
+bool EditorClientAndroid::smartInsertDeleteEnabled() { notImplemented(); return false; }
+bool EditorClientAndroid::isSelectTrailingWhitespaceEnabled(){ notImplemented(); return false; }
+bool EditorClientAndroid::isContinuousSpellCheckingEnabled() { notImplemented(); return false; }
+void EditorClientAndroid::toggleContinuousSpellChecking() { notImplemented(); }
+bool EditorClientAndroid::isGrammarCheckingEnabled() { notImplemented(); return false; }
+void EditorClientAndroid::toggleGrammarChecking() { notImplemented(); }
+int EditorClientAndroid::spellCheckerDocumentTag() { notImplemented(); return -1; }
+
+bool EditorClientAndroid::isEditable() { /* notImplemented(); */ return false; }
+
+// Following Qt's implementation. For shouldBeginEditing and shouldEndEditing.
+// Returning true for these fixes issue http://b/issue?id=735185
+bool EditorClientAndroid::shouldBeginEditing(Range*)
+{
+ return true;
+}
+
+bool EditorClientAndroid::shouldEndEditing(Range*)
+{
+ return true;
+}
+
+bool EditorClientAndroid::shouldInsertNode(Node*, Range*, EditorInsertAction) { notImplemented(); return true; }
+bool EditorClientAndroid::shouldInsertText(const String&, Range*, EditorInsertAction) { return true; }
+bool EditorClientAndroid::shouldApplyStyle(CSSStyleDeclaration*, Range*) { notImplemented(); return true; }
+
+void EditorClientAndroid::didBeginEditing() { notImplemented(); }
+
+// This function is called so that the platform can handle changes to content. It is called
+// after the contents have been edited or unedited (ie undo)
+void EditorClientAndroid::respondToChangedContents() { notImplemented(); }
+
+void EditorClientAndroid::didEndEditing() { notImplemented(); }
+void EditorClientAndroid::didWriteSelectionToPasteboard() { notImplemented(); }
+void EditorClientAndroid::didSetSelectionTypesForPasteboard() { notImplemented(); }
+
+// Copied from the Window's port of WebKit.
+static const unsigned AltKey = 1 << 0;
+static const unsigned ShiftKey = 1 << 1;
+
+struct KeyDownEntry {
+ unsigned virtualKey;
+ unsigned modifiers;
+ const char* name;
+};
+
+struct KeyPressEntry {
+ unsigned charCode;
+ unsigned modifiers;
+ const char* name;
+};
+
+static const KeyDownEntry keyDownEntries[] = {
+ { VK_LEFT, 0, "MoveLeft" },
+ { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" },
+ { VK_LEFT, AltKey, "MoveWordLeft" },
+ { VK_LEFT, AltKey | ShiftKey, "MoveWordLeftAndModifySelection" },
+ { VK_RIGHT, 0, "MoveRight" },
+ { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" },
+ { VK_RIGHT, AltKey, "MoveWordRight" },
+ { VK_RIGHT, AltKey | ShiftKey, "MoveWordRightAndModifySelection" },
+ { VK_UP, 0, "MoveUp" },
+ { VK_UP, ShiftKey, "MoveUpAndModifySelection" },
+ { VK_DOWN, 0, "MoveDown" },
+ { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" },
+
+ { VK_BACK, 0, "BackwardDelete" },
+ { VK_BACK, ShiftKey, "ForwardDelete" },
+ { VK_BACK, AltKey, "DeleteWordBackward" },
+ { VK_BACK, AltKey | ShiftKey, "DeleteWordForward" },
+
+ { VK_ESCAPE, 0, "Cancel" },
+ { VK_TAB, 0, "InsertTab" },
+ { VK_TAB, ShiftKey, "InsertBacktab" },
+ { VK_RETURN, 0, "InsertNewline" },
+ { VK_RETURN, AltKey, "InsertNewline" },
+ { VK_RETURN, AltKey | ShiftKey, "InsertNewline" }
+};
+
+static const KeyPressEntry keyPressEntries[] = {
+ { '\t', 0, "InsertTab" },
+ { '\t', ShiftKey, "InsertBackTab" },
+ { '\r', 0, "InsertNewline" },
+ { '\r', AltKey, "InsertNewline" },
+ { '\r', AltKey | ShiftKey, "InsertNewline" }
+};
+
+static const char* interpretKeyEvent(const KeyboardEvent* evt)
+{
+ const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
+
+ static HashMap<int, const char*>* keyDownCommandsMap = 0;
+ static HashMap<int, const char*>* keyPressCommandsMap = 0;
+
+ if (!keyDownCommandsMap) {
+ keyDownCommandsMap = new HashMap<int, const char*>;
+ keyPressCommandsMap = new HashMap<int, const char*>;
+
+ for (unsigned i = 0; i < sizeof(keyDownEntries)/sizeof(KeyDownEntry); i++)
+ keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
+
+ for (unsigned i = 0; i < sizeof(keyPressEntries)/sizeof(KeyPressEntry); i++)
+ keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
+ }
+
+ unsigned modifiers = 0;
+ if (keyEvent->shiftKey())
+ modifiers |= ShiftKey;
+ if (keyEvent->altKey())
+ modifiers |= AltKey;
+
+ if (evt->type() == eventNames().keydownEvent) {
+ int mapKey = modifiers << 16 | evt->keyCode();
+ return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
+ }
+
+ int mapKey = modifiers << 16 | evt->charCode();
+ return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
+}
+
+void EditorClientAndroid::handleKeyboardEvent(KeyboardEvent* event) {
+ ASSERT(m_page);
+ Frame* frame = m_page->focusController()->focusedOrMainFrame();
+ if (!frame)
+ return;
+
+ const PlatformKeyboardEvent* keyEvent = event->keyEvent();
+ // TODO: If the event is not coming from Android Java, e.g. from JavaScript,
+ // PlatformKeyboardEvent is null. We should support this later.
+ if (!keyEvent)
+ return;
+
+ Editor::Command command = frame->editor()->command(interpretKeyEvent(event));
+ if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) {
+ if (!command.isTextInsertion() && command.execute(event)) {
+ // This function mimics the Windows version. However, calling event->setDefaultHandled()
+ // prevents the javascript key events for the delete key from happening.
+ // Update: Safari doesn't send delete key events to javascript so
+ // we will mimic that behavior.
+ event->setDefaultHandled();
+ }
+ return;
+ }
+
+ if (command.execute(event)) {
+ event->setDefaultHandled();
+ return;
+ }
+
+ // Don't insert null or control characters as they can result in unexpected behaviour
+ if (event->charCode() < ' ')
+ return;
+
+ if (frame->editor()->insertText(keyEvent->text(), event))
+ event->setDefaultHandled();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// we just don't support Undo/Redo at the moment
+
+void EditorClientAndroid::registerCommandForUndo(PassRefPtr<EditCommand>) {}
+void EditorClientAndroid::registerCommandForRedo(PassRefPtr<EditCommand>) {}
+void EditorClientAndroid::clearUndoRedoOperations() {}
+bool EditorClientAndroid::canUndo() const { return false; }
+bool EditorClientAndroid::canRedo() const { return false; }
+void EditorClientAndroid::undo() {}
+void EditorClientAndroid::redo() {}
+
+// functions new to Jun-07 tip of tree merge:
+void EditorClientAndroid::showSpellingUI(bool) {}
+void EditorClientAndroid::getGuessesForWord(String const&, const String&, WTF::Vector<String>&) {}
+bool EditorClientAndroid::spellingUIIsShowing() { return false; }
+void EditorClientAndroid::checkGrammarOfString(unsigned short const*, int, WTF::Vector<GrammarDetail>&, int*, int*) {}
+void EditorClientAndroid::checkSpellingOfString(unsigned short const*, int, int*, int*) {}
+String EditorClientAndroid::getAutoCorrectSuggestionForMisspelledWord(const String&) { return String(); }
+void EditorClientAndroid::textFieldDidEndEditing(Element*) {}
+void EditorClientAndroid::textDidChangeInTextArea(Element*) {}
+void EditorClientAndroid::textDidChangeInTextField(Element*) {}
+void EditorClientAndroid::textFieldDidBeginEditing(Element*) {}
+void EditorClientAndroid::ignoreWordInSpellDocument(String const&) {}
+
+// We need to pass the selection up to the WebTextView
+void EditorClientAndroid::respondToChangedSelection() {
+ if (m_uiGeneratedSelectionChange)
+ return;
+ Frame* frame = m_page->focusController()->focusedOrMainFrame();
+ if (!frame || !frame->view())
+ return;
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(frame->view());
+ webViewCore->updateTextSelection();
+}
+
+bool EditorClientAndroid::shouldChangeSelectedRange(Range*, Range*, EAffinity,
+ bool) {
+ return m_shouldChangeSelectedRange;
+}
+
+bool EditorClientAndroid::doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; }
+void EditorClientAndroid::textWillBeDeletedInTextField(Element*) {}
+void EditorClientAndroid::updateSpellingUIWithGrammarString(String const&, GrammarDetail const&) {}
+void EditorClientAndroid::updateSpellingUIWithMisspelledWord(String const&) {}
+void EditorClientAndroid::learnWord(String const&) {}
+
+// functions new to the Nov-16-08 tip of tree merge:
+bool EditorClientAndroid::shouldMoveRangeAfterDelete(Range*, Range*) { return true; }
+void EditorClientAndroid::setInputMethodState(bool) {}
+
+// functions new to Feb-19 tip of tree merge:
+void EditorClientAndroid::handleInputMethodKeydown(KeyboardEvent*) {}
+
+void EditorClientAndroid::willSetInputMethodState()
+{
+ notImplemented();
+}
+
+void EditorClientAndroid::requestCheckingOfString(SpellChecker*, int, const String&) {}
+
+#if ENABLE(WEB_AUTOFILL)
+WebAutoFill* EditorClientAndroid::getAutoFill()
+{
+ if (!m_autoFill)
+ m_autoFill.set(new WebAutoFill());
+
+ return m_autoFill.get();
+}
+#endif
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h
new file mode 100644
index 0000000..94a6518
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h
@@ -0,0 +1,135 @@
+/*
+ * 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 EditorClientAndroid_h
+#define EditorClientAndroid_h
+
+#include "config.h"
+
+
+#include "EditorClient.h"
+#include "Page.h"
+#include "autofill/WebAutoFill.h"
+
+#include <wtf/OwnPtr.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class EditorClientAndroid : public EditorClient {
+public:
+ EditorClientAndroid() {
+ m_shouldChangeSelectedRange = true;
+ m_uiGeneratedSelectionChange = false;
+ }
+ virtual void pageDestroyed();
+
+ virtual bool shouldDeleteRange(Range*);
+ virtual bool shouldShowDeleteInterface(HTMLElement*);
+ virtual bool smartInsertDeleteEnabled();
+ virtual bool isSelectTrailingWhitespaceEnabled();
+ virtual bool isContinuousSpellCheckingEnabled();
+ virtual void toggleContinuousSpellChecking();
+ virtual bool isGrammarCheckingEnabled();
+ virtual void toggleGrammarChecking();
+ virtual int spellCheckerDocumentTag();
+
+ virtual bool isEditable();
+
+ virtual bool shouldBeginEditing(Range*);
+ virtual bool shouldEndEditing(Range*);
+ virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction);
+ virtual bool shouldInsertText(const String&, Range*, EditorInsertAction);
+ virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting);
+
+ virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*);
+// virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle);
+// virtual bool doCommandBySelector(SEL selector);
+ virtual bool shouldMoveRangeAfterDelete(Range*, Range*);
+
+ virtual void didBeginEditing();
+ virtual void respondToChangedContents();
+ virtual void respondToChangedSelection();
+ virtual void didEndEditing();
+ virtual void didWriteSelectionToPasteboard();
+ virtual void didSetSelectionTypesForPasteboard();
+// virtual void didChangeTypingStyle:(NSNotification *)notification;
+// virtual void didChangeSelection:(NSNotification *)notification;
+// virtual NSUndoManager* undoManager:(WebView *)webView;
+
+ virtual void registerCommandForUndo(PassRefPtr<EditCommand>);
+ virtual void registerCommandForRedo(PassRefPtr<EditCommand>);
+ virtual void clearUndoRedoOperations();
+
+ virtual bool canUndo() const;
+ virtual bool canRedo() const;
+
+ virtual void undo();
+ virtual void redo();
+
+ virtual void handleKeyboardEvent(KeyboardEvent*);
+ virtual void handleInputMethodKeydown(KeyboardEvent*);
+
+ virtual void textFieldDidBeginEditing(Element*);
+ virtual void textFieldDidEndEditing(Element*);
+ virtual void textDidChangeInTextField(Element*);
+ virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*);
+ virtual void textWillBeDeletedInTextField(Element*);
+ virtual void textDidChangeInTextArea(Element*);
+
+ virtual void ignoreWordInSpellDocument(const String&);
+ virtual void learnWord(const String&);
+ virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength);
+ virtual String getAutoCorrectSuggestionForMisspelledWord(const String& misspelledWorld);
+ virtual void checkGrammarOfString(const UChar*, int length, WTF::Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength);
+ virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail);
+ virtual void updateSpellingUIWithMisspelledWord(const String&);
+ virtual void showSpellingUI(bool show);
+ virtual bool spellingUIIsShowing();
+ virtual void getGuessesForWord(const String&, const String& context, WTF::Vector<String>& guesses);
+ virtual void willSetInputMethodState();
+ virtual void setInputMethodState(bool);
+ virtual void requestCheckingOfString(SpellChecker*, int, const String&);
+
+ // Android specific:
+ void setPage(Page* page) { m_page = page; }
+ void setShouldChangeSelectedRange(bool shouldChangeSelectedRange) { m_shouldChangeSelectedRange = shouldChangeSelectedRange; }
+ void setUiGeneratedSelectionChange(bool uiGenerated) { m_uiGeneratedSelectionChange = uiGenerated; }
+#if ENABLE(WEB_AUTOFILL)
+ WebAutoFill* getAutoFill();
+#endif
+private:
+ Page* m_page;
+ bool m_shouldChangeSelectedRange;
+ bool m_uiGeneratedSelectionChange;
+#if ENABLE(WEB_AUTOFILL)
+ OwnPtr<WebAutoFill> m_autoFill;
+#endif
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/FileSystemClient.h b/Source/WebKit/android/WebCoreSupport/FileSystemClient.h
new file mode 100644
index 0000000..5bde18a
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/FileSystemClient.h
@@ -0,0 +1,41 @@
+/*
+ * 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 FILESYSTEM_CLIENT_H
+#define FILESYSTEM_CLIENT_H
+
+#include "PlatformString.h"
+
+using namespace WebCore;
+
+namespace android {
+
+class FileSystemClient {
+public:
+ virtual ~FileSystemClient() { }
+ virtual String resolveFilePathForContentUri(const String&) = 0;
+};
+}
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
new file mode 100644
index 0000000..946a4a7
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
@@ -0,0 +1,1351 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "FrameLoaderClientAndroid.h"
+
+#include "BackForwardList.h"
+#include "CachedFrame.h"
+#include "CachedFramePlatformDataAndroid.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "DOMImplementation.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "EditorClientAndroid.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameNetworkingContextAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLPlugInElement.h"
+#include "HistoryItem.h"
+#include "IconDatabase.h"
+#include "MIMETypeRegistry.h"
+#include "NotImplemented.h"
+#include "PackageNotifier.h"
+#include "Page.h"
+#include "PlatformBridge.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "PluginDatabase.h"
+#include "PluginView.h"
+#include "PluginViewBase.h"
+#include "ProgressTracker.h"
+#include "RenderPart.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleInternal.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "TextEncoding.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebViewClientError.h"
+#include "WebViewCore.h"
+#include "autofill/WebAutoFill.h"
+#include "android_graphics.h"
+
+#include <utils/AssetManager.h>
+#include <wtf/text/CString.h>
+
+extern android::AssetManager* globalAssetManager();
+
+namespace android {
+
+static const int EXTRA_LAYOUT_DELAY = 1000;
+
+FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe)
+ : m_frame(NULL)
+ , m_webFrame(webframe)
+ , m_manualLoader(NULL)
+ , m_hasSentResponseToPlugin(false)
+ , m_onDemandPluginsEnabled(false) {
+ Retain(m_webFrame);
+}
+
+FrameLoaderClientAndroid* FrameLoaderClientAndroid::get(const WebCore::Frame* frame)
+{
+ return static_cast<FrameLoaderClientAndroid*> (frame->loader()->client());
+}
+
+void FrameLoaderClientAndroid::frameLoaderDestroyed() {
+ registerForIconNotification(false);
+ m_frame = 0;
+ Release(m_webFrame);
+ delete this;
+}
+
+bool FrameLoaderClientAndroid::hasWebView() const {
+ // FIXME,
+ // there is one web view per page, or top frame.
+ // as android's view is created from Java side, it is always there.
+ return true;
+}
+
+void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) {
+ m_onDemandPluginsEnabled = false;
+ // don't use representation
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::forceLayout() {
+ ASSERT(m_frame);
+ m_frame->view()->forceLayout();
+ // FIXME, should we adjust view size here?
+ m_frame->view()->adjustViewSize();
+}
+
+void FrameLoaderClientAndroid::forceLayoutForNonHTML() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::setCopiesOnScroll() {
+ // this is a hint about whether we need to force redraws, or can
+ // just copy the scrolled content. Since we always force a redraw
+ // anyways, we can ignore this call.
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::detachedFromParent2() {
+ // FIXME, ready to detach frame from view
+}
+
+void FrameLoaderClientAndroid::detachedFromParent3() {
+ // FIXME, ready to release view
+ notImplemented();
+}
+
+// This function is responsible for associating the "id" with a given
+// subresource load. The following functions that accept an "id" are
+// called for each subresource, so they should not be dispatched to the m_frame.
+void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id,
+ DocumentLoader*, const ResourceRequest&) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id,
+ ResourceRequest&, const ResourceResponse&) {
+ lowPriority_notImplemented();
+}
+
+bool FrameLoaderClientAndroid::shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier)
+{
+ notImplemented();
+ return false;
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*,
+ unsigned long id, const AuthenticationChallenge&) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*,
+ unsigned long id, const AuthenticationChallenge&) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*,
+ unsigned long id, const ResourceResponse&) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*,
+ unsigned long id, int lengthReceived) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*,
+ unsigned long id) {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader,
+ unsigned long id, const ResourceError&) {
+ lowPriority_notImplemented();
+}
+
+bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*,
+ const ResourceRequest&, const ResourceResponse&, int length) {
+ notImplemented();
+ return false;
+}
+
+void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() {
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() {
+ ASSERT(m_frame);
+ // Tell the load it was a redirect.
+ m_webFrame->loadStarted(m_frame);
+}
+
+void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&,
+ double interval, double fireDate) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidPushStateWithinPage()
+{
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReplaceStateWithinPage()
+{
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidPopStateWithinPage()
+{
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchWillClose() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveIcon() {
+ ASSERT(m_frame);
+ if (m_frame->tree() && m_frame->tree()->parent())
+ return;
+ WTF::String url(m_frame->loader()->url().string());
+ // Try to obtain the icon image.
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(
+ url, WebCore::IntSize(16, 16));
+ // If the request fails, try the original request url.
+ if (!icon) {
+ DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader();
+ KURL originalURL = docLoader->originalRequest().url();
+ icon = WebCore::iconDatabase()->iconForPageURL(
+ originalURL, WebCore::IntSize(16, 16));
+ }
+ // 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) {
+ LOGV("Received icon (%p) for %s", icon,
+ url.utf8().data());
+ m_webFrame->didReceiveIcon(icon);
+ } else {
+ LOGV("Icon data for %s unavailable, registering for notification...",
+ url.utf8().data());
+ registerForIconNotification();
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) {
+ ASSERT(m_frame);
+ // Do not report sub frame touch icons
+ if (m_frame->tree() && m_frame->tree()->parent())
+ return;
+ m_webFrame->didReceiveTouchIconURL(url, precomposed);
+}
+
+void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const String& title) {
+ ASSERT(m_frame);
+ // Used to check for FrameLoadTypeStandard but we only want to send the title for
+ // the top frame and not sub-frames.
+ if (!m_frame->tree() || !m_frame->tree()->parent()) {
+ m_webFrame->setTitle(title);
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchDidCommitLoad() {
+#if ENABLE(WEB_AUTOFILL)
+ if (m_frame == m_frame->page()->mainFrame()) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(m_frame->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->reset();
+ }
+#endif
+ verifiedOk();
+}
+
+static void loadDataIntoFrame(Frame* frame, KURL baseUrl, const String& url,
+ const String& data) {
+ if (baseUrl.isEmpty()) {
+ baseUrl = blankURL();
+ }
+ ResourceRequest request(baseUrl);
+ CString cstr = data.utf8();
+ RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length());
+ SubstituteData subData(buf, String("text/html"), String("utf-8"),
+ KURL(KURL(), url));
+ frame->loader()->load(request, subData, false);
+}
+
+void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) {
+ ASSERT(m_frame);
+ // Ignore ErrorInterrupted since it is due to a policy interruption. This
+ // is caused by a decision to download the main resource rather than
+ // display it.
+ if (error.errorCode() == InternalErrorInterrupted
+ || error.errorCode() == InternalErrorCancelled) {
+ // If we decided to download the main resource or if the user cancelled
+ // it, make sure we report that the load is done.
+ didFinishLoad();
+ return;
+ }
+
+ AssetManager* am = globalAssetManager();
+
+ // Check to see if the error code was not generated internally
+ WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain;
+ if ((error.errorCode() == ErrorFile ||
+ error.errorCode() == ErrorFileNotFound) &&
+ (!error.localizedDescription().isEmpty())) {
+ id = WebCore::PlatformBridge::LoadError;
+ }
+ String filename = m_webFrame->getRawResourceFilename(id);
+ if (filename.isEmpty())
+ return;
+
+ // Grab the error page from the asset manager
+ Asset* a = am->openNonAsset(
+ filename.utf8().data(), Asset::ACCESS_BUFFER);
+ if (!a)
+ return;
+
+ // Take the failing url and encode html entities so javascript urls are not
+ // executed.
+ CString failingUrl = error.failingURL().utf8();
+ WTF::Vector<char> url;
+ int len = failingUrl.length();
+ const char* data = failingUrl.data();
+ for (int i = 0; i < len; i++) {
+ char c = data[i];
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9'))
+ url.append(c);
+ else {
+ char buf[16];
+ int res = sprintf(buf, "&#%d;", c);
+ buf[res] = 0;
+ url.append(buf, res);
+ }
+ }
+
+ // Replace all occurances of %s with the failing url.
+ String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength());
+ s = s.replace("%s", String(url.data(), url.size()));
+
+ // Replace all occurances of %e with the error text
+ s = s.replace("%e", error.localizedDescription());
+
+ // Create the request and the substitute data and tell the FrameLoader to
+ // load with the replacement data.
+ // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for
+ // invalidate URL string.
+ loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s);
+
+ // Delete the asset.
+ delete a;
+
+ // Report that the load is finished, since it failed.
+ didFinishLoad();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) {
+ // called when page is completed with error
+ didFinishLoad();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() {
+ // called when finishedParsing
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFinishLoad() {
+ didFinishLoad();
+}
+
+void FrameLoaderClientAndroid::dispatchDidFirstLayout() {
+ ASSERT(m_frame);
+ // set EXTRA_LAYOUT_DELAY if the loader is not completed yet
+ if (!m_frame->loader()->isComplete())
+ m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY);
+ // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout
+ // so that about:blank will update the screen.
+ if (!m_frame->tree()->parent()) {
+ // Only need to notify Java side for the top frame
+ WebViewCore::getWebViewCore(m_frame->view())->didFirstLayout();
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchDidFirstVisuallyNonEmptyLayout()
+{
+ notImplemented();
+}
+
+Frame* FrameLoaderClientAndroid::dispatchCreatePage(const NavigationAction&) {
+ ASSERT(m_frame);
+#ifdef ANDROID_MULTIPLE_WINDOWS
+ if (m_frame->settings() && m_frame->settings()->supportMultipleWindows())
+ // Always a user gesture since window.open maps to
+ // ChromeClientAndroid::createWindow
+ return m_webFrame->createWindow(false, true);
+ else
+#endif
+ // If the client doesn't support multiple windows, just replace the
+ // current frame's contents.
+ return m_frame;
+}
+
+void FrameLoaderClientAndroid::dispatchShow() {
+ ASSERT(m_frame);
+ m_frame->view()->invalidate();
+}
+
+
+static bool TreatAsAttachment(const String& content_disposition) {
+ // Some broken sites just send
+ // Content-Disposition: ; filename="file"
+ // screen those out here.
+ if (content_disposition.startsWith(";"))
+ return false;
+
+ if (content_disposition.startsWith("inline", false))
+ return false;
+
+ // Some broken sites just send
+ // Content-Disposition: filename="file"
+ // without a disposition token... screen those out.
+ if (content_disposition.startsWith("filename", false))
+ return false;
+
+ // Also in use is Content-Disposition: name="file"
+ if (content_disposition.startsWith("name", false))
+ return false;
+
+ // We have a content-disposition of "attachment" or unknown.
+ // RFC 2183, section 2.8 says that an unknown disposition
+ // value should be treated as "attachment"
+ return true;
+}
+
+void FrameLoaderClientAndroid::dispatchDecidePolicyForMIMEType(FramePolicyFunction func,
+ const String& MIMEType, const ResourceRequest& request) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ if (!func)
+ return;
+
+ PolicyChecker* policy = m_frame->loader()->policyChecker();
+
+ if (request.isNull()) {
+ (policy->*func)(PolicyIgnore);
+ return;
+ }
+ // Default to Use (display internally).
+ PolicyAction action = PolicyUse;
+ // Check if we should Download instead.
+ const ResourceResponse& response = m_frame->loader()->activeDocumentLoader()->response();
+ const String& content_disposition = response.httpHeaderField("Content-Disposition");
+ if (!content_disposition.isEmpty() &&
+ TreatAsAttachment(content_disposition)) {
+ // Server wants to override our normal policy.
+ // Check to see if we are a sub frame (main frame has no owner element)
+ if (m_frame->ownerElement() != 0)
+ action = PolicyIgnore;
+ else
+ action = PolicyDownload;
+ (policy->*func)(action);
+ return;
+ }
+
+ // Ask if it can be handled internally.
+ if (!canShowMIMEType(MIMEType)) {
+ // Check to see if we are a sub frame (main frame has no owner element)
+ if (m_frame->ownerElement() != 0)
+ action = PolicyIgnore;
+ else
+ action = PolicyDownload;
+ (policy->*func)(action);
+ return;
+ }
+ // A status code of 204 indicates no content change. Ignore the result.
+ WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader();
+ if (docLoader->response().httpStatusCode() == 204)
+ action = PolicyIgnore;
+ (policy->*func)(action);
+}
+
+void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func,
+ const NavigationAction& action, const ResourceRequest& request,
+ PassRefPtr<FormState> formState, const String& frameName) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ if (!func)
+ return;
+
+ if (request.isNull()) {
+ (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
+ return;
+ }
+
+ if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted)
+ m_frame->loader()->resetMultipleFormSubmissionProtection();
+
+ // If we get to this point it means that a link has a target that was not
+ // found by the frame tree. Instead of creating a new frame, return the
+ // current frame in dispatchCreatePage.
+ if (canHandleRequest(request))
+ (m_frame->loader()->policyChecker()->*func)(PolicyUse);
+ else
+ (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
+}
+
+void FrameLoaderClientAndroid::cancelPolicyCheck() {
+ lowPriority_notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func,
+ const NavigationAction& action, const ResourceRequest& request,
+ PassRefPtr<FormState> formState) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ if (!func)
+ return;
+ if (request.isNull()) {
+ (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
+ return;
+ }
+
+ // Reset multiple form submission protection. If this is a resubmission, we check with the
+ // user and reset the protection if they choose to resubmit the form (see WebCoreFrameBridge.cpp)
+ if (action.type() == NavigationTypeFormSubmitted)
+ m_frame->loader()->resetMultipleFormSubmissionProtection();
+
+ if (action.type() == NavigationTypeFormResubmitted) {
+ m_webFrame->decidePolicyForFormResubmission(func);
+ return;
+ } else
+ (m_frame->loader()->policyChecker()->*func)(PolicyUse);
+}
+
+void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ (m_frame->loader()->policyChecker()->*func)(PolicyUse);
+}
+
+void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form)
+{
+ if (m_webFrame->shouldSaveFormData())
+ m_webFrame->saveFormData(form);
+}
+
+void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) {
+ ASSERT(m_frame);
+ if (m_manualLoader) {
+ m_manualLoader->didFail(error);
+ m_manualLoader = NULL;
+ m_hasSentResponseToPlugin = false;
+ } else {
+ if (!error.isNull() && error.errorCode() >= InternalErrorLast && error.errorCode() != ERROR_OK)
+ m_webFrame->reportError(error.errorCode(),
+ error.localizedDescription(), error.failingURL());
+ }
+}
+
+// This function is called right before the progress is updated.
+void FrameLoaderClientAndroid::willChangeEstimatedProgress() {
+ verifiedOk();
+}
+
+// This function is called after the progress has been updated. The bad part
+// about this is that when a page is completed, this function is called after
+// the progress has been reset to 0.
+void FrameLoaderClientAndroid::didChangeEstimatedProgress() {
+ verifiedOk();
+}
+
+// This will give us the initial estimate when the page first starts to load.
+void FrameLoaderClientAndroid::postProgressStartedNotification() {
+ ASSERT(m_frame);
+ if (m_frame->page())
+ m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress());
+}
+
+// This will give us any updated progress including the final progress.
+void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() {
+ ASSERT(m_frame);
+ if (m_frame->page())
+ m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress());
+}
+
+// This is just a notification that the progress has finished. Don't call
+// setProgress(1) because postProgressEstimateChangedNotification will do so.
+void FrameLoaderClientAndroid::postProgressFinishedNotification() {
+ WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view());
+ if (!m_frame->tree()->parent()) {
+ // only need to notify Java for the top frame
+ core->notifyProgressFinished();
+ }
+ // notify plugins that the frame has loaded
+ core->notifyPluginsOnFrameLoad(m_frame);
+}
+
+void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) {
+ // this is only interesting once we provide an external API for the DOM
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) {
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) {
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) {
+ // Telling the frame we received some data and passing 0 as the data is our
+ // way to get work done that is normally done when the first bit of data is
+ // received, even for the case of a document with no data (like about:blank)
+ if (!m_manualLoader) {
+ committedLoad(docLoader, 0, 0);
+ return;
+ }
+
+ m_manualLoader->didFinishLoading();
+ m_manualLoader = NULL;
+ m_hasSentResponseToPlugin = false;
+}
+
+void FrameLoaderClientAndroid::updateGlobalHistory() {
+ ASSERT(m_frame);
+
+ DocumentLoader* docLoader = m_frame->loader()->documentLoader();
+ ASSERT(docLoader);
+
+ // Code copied from FrameLoader.cpp:createHistoryItem
+ // Only add this URL to the database if it is a valid page
+ if (docLoader->unreachableURL().isEmpty()
+ && docLoader->response().httpStatusCode() < 400) {
+ m_webFrame->updateVisitedHistory(docLoader->urlForHistory(), false);
+ if (!docLoader->serverRedirectSourceForHistory().isNull())
+ m_webFrame->updateVisitedHistory(KURL(ParsedURLString, docLoader->serverRedirectDestinationForHistory()), false);
+ }
+}
+
+void FrameLoaderClientAndroid::updateGlobalHistoryRedirectLinks() {
+ // Note, do we need to do anything where there is no HistoryItem? If we call
+ // updateGlobalHistory(), we will add bunch of "data:xxx" urls for gmail.com
+ // which is not what we want. Opt to do nothing now.
+}
+
+bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const {
+ // hmmm, seems like we might do a more thoughtful check
+ ASSERT(m_frame);
+ return item != NULL;
+}
+
+void FrameLoaderClientAndroid::didDisplayInsecureContent()
+{
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::didRunInsecureContent(SecurityOrigin*)
+{
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) {
+ if (!m_manualLoader)
+ loader->commitData(data, length);
+
+ // commit data may have created a manual plugin loader
+ if (m_manualLoader) {
+ if (!m_hasSentResponseToPlugin) {
+ m_manualLoader->didReceiveResponse(loader->response());
+ // Failure could cause the main document to have an error causing
+ // the manual loader to be reset.
+ if (!m_manualLoader)
+ return;
+ m_hasSentResponseToPlugin = true;
+ }
+ m_manualLoader->didReceiveData(data, length);
+ }
+}
+
+ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorCancelled, request.url(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorCannotShowUrl, request.url(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorInterrupted, request.url(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorCannotShowMimeType, request.url(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorFileDoesNotExist, request.url(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorPluginWillHandleLoadError, request.url(), String());
+}
+
+bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) {
+ notImplemented();
+ return false;
+}
+
+bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const {
+ ASSERT(m_frame);
+ // Don't allow hijacking of intrapage navigation
+ if (WebCore::equalIgnoringFragmentIdentifier(request.url(), m_frame->loader()->url()))
+ return true;
+
+ // Don't allow hijacking of iframe urls that are http or https
+ if (request.url().protocol().startsWith("http", false) &&
+ m_frame->tree() && m_frame->tree()->parent())
+ return true;
+
+ return m_webFrame->canHandleRequest(request);
+}
+
+bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const {
+ // FIXME: This looks like it has to do with whether or not a type can be
+ // shown "internally" (i.e. inside the browser) regardless of whether
+ // or not the browser is doing the rendering, e.g. a full page plugin.
+ if (MIMETypeRegistry::isSupportedImageResourceMIMEType(mimeType) ||
+ MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) ||
+ MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) ||
+ (m_frame && m_frame->settings()
+ && m_frame->settings()->arePluginsEnabled()
+ && PluginDatabase::installedPlugins()->isMIMETypeRegistered(
+ mimeType)) ||
+ (DOMImplementation::isTextMIMEType(mimeType) &&
+ !mimeType.startsWith("text/vnd")) ||
+ DOMImplementation::isXMLMIMEType(mimeType))
+ return true;
+ return false;
+}
+
+bool FrameLoaderClientAndroid::canShowMIMETypeAsHTML(const String& mimeType) const {
+ return false;
+}
+
+bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const {
+ // don't use representation
+ verifiedOk();
+ return false;
+}
+
+String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const {
+ // FIXME, copy from Apple's port
+ String mimetype("x-apple-web-kit/");
+ mimetype.append(URLScheme.lower());
+ return mimetype;
+}
+
+void FrameLoaderClientAndroid::frameLoadCompleted() {
+ // copied from Apple port, without this back with sub-frame will trigger ASSERT
+ ASSERT(m_frame);
+}
+
+void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) {
+ ASSERT(m_frame);
+ ASSERT(item);
+ // store the current scale (only) for the top frame
+ if (!m_frame->tree()->parent()) {
+ // We should have added a bridge when the child item was added to its
+ // parent.
+ AndroidWebHistoryBridge* bridge = item->bridge();
+ ASSERT(bridge);
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
+ bridge->setScale(webViewCore->scale());
+ bridge->setTextWrapScale(webViewCore->textWrapScale());
+ }
+
+ WebCore::notifyHistoryItemChanged(item);
+}
+
+void FrameLoaderClientAndroid::restoreViewState() {
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
+ HistoryItem* item = m_frame->loader()->history()->currentItem();
+ AndroidWebHistoryBridge* bridge = item->bridge();
+ // restore the scale (only) for the top frame
+ if (!m_frame->tree()->parent()) {
+ webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale());
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchDidAddBackForwardItem(HistoryItem* item) const {
+ ASSERT(m_frame);
+ m_webFrame->addHistoryItem(item);
+}
+
+void FrameLoaderClientAndroid::dispatchDidRemoveBackForwardItem(HistoryItem* item) const {
+ ASSERT(m_frame);
+ m_webFrame->removeHistoryItem(0);
+}
+
+void FrameLoaderClientAndroid::dispatchDidChangeBackForwardIndex() const {
+ ASSERT(m_frame);
+ BackForwardList* list = m_frame->page()->backForwardList();
+ ASSERT(list);
+ m_webFrame->updateHistoryIndex(list->backListCount());
+}
+
+void FrameLoaderClientAndroid::provisionalLoadStarted() {
+ ASSERT(m_frame);
+ m_webFrame->loadStarted(m_frame);
+}
+
+void FrameLoaderClientAndroid::didFinishLoad() {
+ ASSERT(m_frame);
+ m_frame->document()->setExtraLayoutDelay(0);
+ m_webFrame->didFinishLoad(m_frame);
+}
+
+void FrameLoaderClientAndroid::prepareForDataSourceReplacement() {
+ verifiedOk();
+}
+
+PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader(
+ const ResourceRequest& request, const SubstituteData& data) {
+ RefPtr<DocumentLoader> loader = DocumentLoader::create(request, data);
+ return loader.release();
+}
+
+void FrameLoaderClientAndroid::setTitle(const String& title, const KURL& url) {
+ // Not needed. dispatchDidReceiveTitle is called immediately after this.
+ // url is used to update the Apple port history items.
+ verifiedOk();
+}
+
+String FrameLoaderClientAndroid::userAgent(const KURL& u) {
+ return m_webFrame->userAgentForURL(&u);
+}
+
+void FrameLoaderClientAndroid::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame) {
+ CachedFramePlatformDataAndroid* platformData = new CachedFramePlatformDataAndroid(m_frame->settings());
+ cachedFrame->setCachedFramePlatformData(platformData);
+}
+
+void FrameLoaderClientAndroid::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame) {
+ CachedFramePlatformDataAndroid* platformData = reinterpret_cast<CachedFramePlatformDataAndroid*>(cachedFrame->cachedFramePlatformData());
+#ifdef ANDROID_META_SUPPORT
+ platformData->restoreMetadata(m_frame->settings());
+#endif
+ m_webFrame->transitionToCommitted(m_frame);
+}
+
+void FrameLoaderClientAndroid::transitionToCommittedForNewPage() {
+ ASSERT(m_frame);
+
+#ifdef ANDROID_META_SUPPORT
+ // reset metadata settings for the main frame as they are not preserved cross page
+ if (m_frame == m_frame->page()->mainFrame() && m_frame->settings())
+ m_frame->settings()->resetMetadataSettings();
+#endif
+
+ // Save the old WebViewCore before creating a new FrameView. There is one
+ // WebViewCore per page. Each frame, including the main frame and sub frame,
+ // has a 1:1 FrameView and WebFrameView.
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
+ Retain(webViewCore);
+
+ // Save the old WebFrameView's bounds and apply them to the new WebFrameView
+ 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());
+ if (oldZoomFactor != 1.0f && oldZoomFactor != m_frame->textZoomFactor()) {
+ m_frame->setTextZoomFactor(oldZoomFactor);
+ }
+
+ // 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);
+ // WebFrameView Retains webViewCore, so call Release for webViewCore
+ Release(webViewCore);
+
+ m_webFrame->transitionToCommitted(m_frame);
+}
+
+void FrameLoaderClientAndroid::dispatchDidBecomeFrameset(bool)
+{
+}
+
+bool FrameLoaderClientAndroid::canCachePage() const {
+ return true;
+}
+
+void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&,
+ const ResourceRequest&, const ResourceResponse&) {
+ // Get the C++ side of the load listener and tell it to handle the download
+ handle->getInternal()->m_loader->downloadFile();
+}
+
+WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name,
+ HTMLFrameOwnerElement* ownerElement, const String& referrer,
+ bool allowsScrolling, int marginWidth, int marginHeight)
+{
+ Frame* parent = ownerElement->document()->frame();
+ FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(m_webFrame);
+ RefPtr<Frame> pFrame = Frame::create(parent->page(), ownerElement, loaderC);
+ Frame* newFrame = pFrame.get();
+ loaderC->setFrame(newFrame);
+ // Append the subframe to the parent and set the name of the subframe. The name must be set after
+ // appending the child so that the name becomes unique.
+ parent->tree()->appendChild(newFrame);
+ 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);
+ 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())
+ return 0;
+
+ parent->loader()->loadURLIntoChildFrame(url, referrer, pFrame.get());
+
+ // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame.
+ if (!pFrame->tree()->parent())
+ return NULL;
+
+ return pFrame.release();
+}
+
+// YouTube flash url path starts with /v/
+static const char slash_v_slash[] = { '/', 'v', '/' };
+static const char slash_e_slash[] = { '/', 'e', '/' };
+
+static bool isValidYouTubeVideo(const String& path)
+{
+ if (!charactersAreAllASCII(path.characters(), path.length()))
+ return false;
+ unsigned int len = path.length();
+ if (len <= sizeof(slash_v_slash)) // check for more than just /v/
+ return false;
+ CString str = path.lower().utf8();
+ const char* data = str.data();
+ // Youtube flash url can start with /v/ or /e/
+ if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0)
+ if (memcmp(data, slash_e_slash, sizeof(slash_e_slash)) != 0)
+ return false;
+ // Start after /v/
+ for (unsigned int i = sizeof(slash_v_slash); i < len; i++) {
+ char c = data[i];
+ // Check for alpha-numeric characters only.
+ if (WTF::isASCIIAlphanumeric(c) || c == '_' || c == '-')
+ continue;
+ // The url can have more parameters such as &hl=en after the video id.
+ // Once we start seeing extra parameters we can return true.
+ return c == '&' && i > sizeof(slash_v_slash);
+ }
+ return true;
+}
+
+static bool isYouTubeUrl(const KURL& url, const String& mimeType)
+{
+ String host = url.host();
+ bool youtube = host.endsWith("youtube.com")
+ || host.endsWith("youtube-nocookie.com");
+ return youtube && isValidYouTubeVideo(url.path())
+ && equalIgnoringCase(mimeType, "application/x-shockwave-flash");
+}
+
+static bool isYouTubeInstalled() {
+ return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube");
+}
+
+// Use PluginViewBase rather than an Android specific sub class as we do not require any
+// Android specific functionality; this just renders a placeholder which will later
+// activate the real plugin.
+class PluginToggleWidget : public PluginViewBase {
+public:
+ PluginToggleWidget(Frame* parent, const IntSize& size,
+ HTMLPlugInElement* elem, const KURL& url,
+ const WTF::Vector<String>& paramNames,
+ const WTF::Vector<String>& paramValues, const String& mimeType,
+ bool loadManually)
+ : PluginViewBase(0)
+ , m_parent(parent)
+ , m_size(size)
+ , m_element(elem)
+ , m_url(url)
+ , m_paramNames(paramNames)
+ , m_paramValues(paramValues)
+ , m_mimeType(mimeType)
+ , m_loadManually(loadManually)
+ {
+ resize(size);
+ }
+
+ virtual void paint(GraphicsContext* ctx, const IntRect& rect)
+ {
+ // Most of this code is copied from PluginView::paintMissingPluginIcon
+ // with slight modification.
+
+ static RefPtr<Image> image;
+ if (!image) {
+ image = Image::loadPlatformResource("togglePlugin");
+ }
+
+ IntRect imageRect(x(), y(), image->width(), image->height());
+
+ int xOffset = (width() - imageRect.width()) >> 1;
+ int yOffset = (height() - imageRect.height()) >> 1;
+
+ imageRect.move(xOffset, yOffset);
+
+ if (!rect.intersects(imageRect))
+ return;
+
+ // FIXME: We need to clip similarly to paintMissingPluginIcon but it is
+ // way screwed up right now. It has something to do with how we tell
+ // webkit the scroll position and it causes the placeholder to get
+ // clipped very badly. http://b/issue?id=2533303
+
+ ctx->save();
+ ctx->clip(frameRect());
+
+ ctx->setFillColor(Color::white, ColorSpaceDeviceRGB);
+ ctx->fillRect(frameRect());
+ if (frameRect().contains(imageRect)) {
+ // Leave a 2 pixel padding.
+ const int pixelWidth = 2;
+ IntRect innerRect = frameRect();
+ innerRect.inflate(-pixelWidth);
+ // Draw a 2 pixel light gray border.
+ ctx->setStrokeColor(Color::lightGray, ColorSpaceDeviceRGB);
+ ctx->strokeRect(innerRect, pixelWidth);
+ }
+
+ // Draw the image in the center
+ ctx->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect.location());
+ ctx->restore();
+ }
+
+ virtual void handleEvent(Event* event)
+ {
+ if (event->type() != eventNames().clickEvent)
+ return;
+
+ Frame* frame = m_parent->page()->mainFrame();
+ while (frame) {
+ RenderView* view = frame->contentRenderer();
+ const HashSet<RenderWidget*> widgets = view->widgets();
+ HashSet<RenderWidget*>::const_iterator it = widgets.begin();
+ HashSet<RenderWidget*>::const_iterator end = widgets.end();
+ for (; it != end; ++it) {
+ Widget* widget = (*it)->widget();
+ // PluginWidget is used only with PluginToggleWidget
+ if (widget && widget->isPluginViewBase()) {
+ PluginToggleWidget* ptw =
+ static_cast<PluginToggleWidget*>(widget);
+ ptw->swapPlugin(*it);
+ }
+ }
+ frame = frame->tree()->traverseNext();
+ }
+ }
+
+ void swapPlugin(RenderWidget* renderer) {
+ typedef FrameLoaderClientAndroid FLCA;
+ FLCA* client = static_cast<FLCA*>(m_parent->loader()->client());
+ client->enableOnDemandPlugins();
+ WTF::PassRefPtr<PluginView> prpWidget =
+ PluginView::create(m_parent.get(),
+ m_size,
+ m_element,
+ m_url,
+ m_paramNames,
+ m_paramValues,
+ m_mimeType,
+ m_loadManually);
+ RefPtr<Widget> myProtector(this);
+ prpWidget->focusPluginElement();
+ renderer->setWidget(prpWidget);
+ }
+
+private:
+ void invalidateRect(const IntRect& rect) { }
+
+ RefPtr<Frame> m_parent;
+ IntSize m_size;
+ HTMLPlugInElement* m_element;
+ KURL m_url;
+ WTF::Vector<String> m_paramNames;
+ WTF::Vector<String> m_paramValues;
+ String m_mimeType;
+ bool m_loadManually;
+};
+
+WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createPlugin(
+ const IntSize& size,
+ HTMLPlugInElement* element,
+ const KURL& url,
+ const WTF::Vector<String>& names,
+ const WTF::Vector<String>& values,
+ const String& mimeType,
+ bool loadManually) {
+ WTF::PassRefPtr<PluginView> prpWidget = 0;
+#ifdef ANDROID_PLUGINS
+ // This is copied from PluginView.cpp. We need to determine if a plugin
+ // will be found before doing some of the work in PluginView.
+ String mimeTypeCopy = mimeType;
+ PluginPackage* plugin =
+ PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy);
+ if (!plugin && PluginDatabase::installedPlugins()->refresh()) {
+ mimeTypeCopy = mimeType;
+ plugin = PluginDatabase::installedPlugins()->findPlugin(url,
+ mimeTypeCopy);
+ }
+ Settings* settings = m_frame->settings();
+ // Do the placeholder if plugins are on-demand and there is a plugin for the
+ // given mime type.
+ if (settings && settings->arePluginsOnDemand() && plugin &&
+ !m_onDemandPluginsEnabled) {
+ return adoptRef(new PluginToggleWidget(m_frame, size, element, url,
+ names, values, mimeType, loadManually));
+ }
+ prpWidget = PluginView::create(m_frame,
+ size,
+ element,
+ url,
+ names,
+ values,
+ mimeType,
+ loadManually);
+ // Return the plugin if it was loaded successfully. Otherwise, fallback to
+ // the youtube placeholder if possible. No need to check prpWidget as
+ // PluginView::create will create a PluginView for missing plugins.
+ // Note: this check really only checks if the plugin was found and not if
+ // the plugin was loaded.
+ if (prpWidget->status() == PluginStatusLoadedSuccessfully)
+ return prpWidget;
+#endif
+ // Create an iframe for youtube urls.
+ if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) {
+ WTF::RefPtr<Frame> frame = createFrame(blankURL(), String(), element,
+ String(), false, 0, 0);
+ if (frame) {
+ // grab everything after /v/
+ String videoId = url.path().substring(sizeof(slash_v_slash));
+ // Extract just the video id
+ unsigned videoIdEnd = 0;
+ for (; videoIdEnd < videoId.length(); videoIdEnd++) {
+ if (videoId[videoIdEnd] == '&') {
+ videoId = videoId.left(videoIdEnd);
+ break;
+ }
+ }
+ AssetManager* am = globalAssetManager();
+ Asset* a = am->open("webkit/youtube.html",
+ Asset::ACCESS_BUFFER);
+ if (!a)
+ return NULL;
+ String s = String((const char*)a->getBuffer(false), a->getLength());
+ s = s.replace("VIDEO_ID", videoId);
+ delete a;
+ loadDataIntoFrame(frame.get(),
+ KURL(ParsedURLString, "file:///android_asset/webkit/"), String(), s);
+ // Transfer ownership to a local refptr.
+ WTF::RefPtr<Widget> widget(frame->view());
+ return widget.release();
+ }
+ }
+ return prpWidget;
+}
+
+void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) {
+ // Do not redirect data if the Widget is our plugin placeholder.
+ if (pluginWidget->isPluginView()) {
+ m_manualLoader = static_cast<PluginView*>(pluginWidget);
+ }
+}
+
+WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, HTMLAppletElement*,
+ const KURL& baseURL, const WTF::Vector<String>& paramNames,
+ const WTF::Vector<String>& paramValues) {
+ // don't support widget yet
+ notImplemented();
+ return 0;
+}
+
+void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument(WebCore::Page*)
+{
+ ASSERT(m_frame);
+ // m_webFrame points to the WebFrame for the page that our frame previously
+ // belonged to. If the frame now belongs to a new page, we need to update
+ // m_webFrame to point to the WebFrame for the new page.
+ Page* newPage = m_frame->page();
+ if (newPage != m_webFrame->page()) {
+ ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client());
+ Release(m_webFrame);
+ m_webFrame = chromeClient->webFrame();
+ Retain(m_webFrame);
+ }
+}
+
+void FrameLoaderClientAndroid::transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*)
+{
+ notImplemented();
+}
+
+// This function is used by the <OBJECT> element to determine the type of
+// the contents and work out if it can render it.
+ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url,
+ const String& mimeType) {
+ return FrameLoader::defaultObjectContentType(url, mimeType);
+}
+
+// This function allows the application to set the correct CSS media
+// style. Android could use it to set the media style 'handheld'. Safari
+// may use it to set the media style to 'print' when the user wants to print
+// a particular web page.
+String FrameLoaderClientAndroid::overrideMediaType() const {
+ lowPriority_notImplemented();
+ return String();
+}
+
+// This function is used to re-attach Javascript<->native code classes.
+void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world)
+{
+ if (world != mainThreadNormalWorld())
+ return;
+
+ ASSERT(m_frame);
+ LOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n",
+ m_frame, m_frame->loader()->url().string().ascii().data());
+ m_webFrame->windowObjectCleared(m_frame);
+}
+
+void FrameLoaderClientAndroid::documentElementAvailable() {
+}
+
+// functions new to Jun-07 tip of tree merge:
+ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) {
+ return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String());
+}
+
+// functions new to Nov-07 tip of tree merge:
+void FrameLoaderClientAndroid::didPerformFirstNavigation() const {
+ // This seems to be just a notification that the UI can listen to, to
+ // know if the user has performed first navigation action.
+ // It is called from
+ // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
+ // "Navigation" here means a transition from one page to another that
+ // ends up in the back/forward list.
+}
+
+void FrameLoaderClientAndroid::registerForIconNotification(bool listen) {
+ if (listen)
+ WebIconDatabase::RegisterForIconNotification(this);
+ else
+ WebIconDatabase::UnregisterForIconNotification(this);
+}
+
+// This is the WebIconDatabaseClient method for receiving a notification when we
+// get the icon for the page.
+void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) {
+ // This call must happen before dispatchDidReceiveIcon since that method
+ // may register for icon notifications again since the icon data may have
+ // to be read from disk.
+ registerForIconNotification(false);
+ KURL u(ParsedURLString, pageUrl);
+ if (equalIgnoringFragmentIdentifier(u, m_frame->loader()->url())) {
+ dispatchDidReceiveIcon();
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchDidChangeIcons() {
+ notImplemented();
+}
+
+PassRefPtr<FrameNetworkingContext> FrameLoaderClientAndroid::createNetworkingContext()
+{
+ return FrameNetworkingContextAndroid::create(getFrame());
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
new file mode 100644
index 0000000..25561a8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
@@ -0,0 +1,271 @@
+/*
+ * 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 FrameLoaderClientAndroid_h
+#define FrameLoaderClientAndroid_h
+
+#include "CacheBuilder.h"
+#include "FrameLoaderClient.h"
+#include "ResourceResponse.h"
+#include "WebIconDatabase.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+class PluginManualLoader;
+}
+
+using namespace WebCore;
+
+namespace android {
+ class WebFrame;
+
+ class FrameLoaderClientAndroid : public FrameLoaderClient,
+ WebIconDatabaseClient {
+ public:
+ FrameLoaderClientAndroid(WebFrame* webframe);
+
+ Frame* getFrame() { return m_frame; }
+ static FrameLoaderClientAndroid* get(const Frame* frame);
+
+ void setFrame(Frame* frame) { m_frame = frame; }
+ WebFrame* webFrame() const { return m_webFrame; }
+
+ virtual void frameLoaderDestroyed();
+
+ virtual bool hasWebView() const; // mainly for assertions
+
+ virtual void makeRepresentation(DocumentLoader*);
+ virtual void forceLayout();
+ virtual void forceLayoutForNonHTML();
+
+ virtual void setCopiesOnScroll();
+
+ virtual void detachedFromParent2();
+ virtual void detachedFromParent3();
+
+ virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&);
+
+ virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier);
+ virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&);
+ virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&);
+ virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&);
+ virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived);
+ virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier);
+ virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&);
+ virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length);
+
+ virtual void dispatchDidHandleOnloadEvents();
+ virtual void dispatchDidReceiveServerRedirectForProvisionalLoad();
+ virtual void dispatchDidCancelClientRedirect();
+ virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate);
+ virtual void dispatchDidChangeLocationWithinPage();
+ virtual void dispatchDidPushStateWithinPage();
+ virtual void dispatchDidReplaceStateWithinPage();
+ virtual void dispatchDidPopStateWithinPage();
+ virtual void dispatchWillClose();
+ virtual void dispatchDidReceiveIcon();
+ virtual void dispatchDidStartProvisionalLoad();
+ virtual void dispatchDidReceiveTitle(const String& title);
+ virtual void dispatchDidCommitLoad();
+ virtual void dispatchDidFailProvisionalLoad(const ResourceError&);
+ virtual void dispatchDidFailLoad(const ResourceError&);
+ virtual void dispatchDidFinishDocumentLoad();
+ virtual void dispatchDidFinishLoad();
+ virtual void dispatchDidFirstLayout();
+ virtual void dispatchDidFirstVisuallyNonEmptyLayout();
+
+ virtual Frame* dispatchCreatePage(const NavigationAction&);
+ virtual void dispatchShow();
+
+ virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&);
+ virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName);
+ virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>);
+ virtual void cancelPolicyCheck();
+
+ virtual void dispatchUnableToImplementPolicy(const ResourceError&);
+
+ virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>);
+
+ virtual void dispatchDidLoadMainResource(DocumentLoader*);
+ virtual void revertToProvisionalState(DocumentLoader*);
+ virtual void setMainDocumentError(DocumentLoader*, const ResourceError&);
+
+ virtual void willChangeEstimatedProgress();
+ virtual void didChangeEstimatedProgress();
+ virtual void postProgressStartedNotification();
+ virtual void postProgressEstimateChangedNotification();
+ virtual void postProgressFinishedNotification();
+
+ virtual void setMainFrameDocumentReady(bool);
+
+ virtual void startDownload(const ResourceRequest&);
+
+ virtual void willChangeTitle(DocumentLoader*);
+ virtual void didChangeTitle(DocumentLoader*);
+
+ virtual void committedLoad(DocumentLoader*, const char*, int);
+ virtual void finishedLoading(DocumentLoader*);
+
+ virtual void updateGlobalHistory();
+ virtual void updateGlobalHistoryRedirectLinks();
+
+ virtual bool shouldGoToHistoryItem(HistoryItem*) const;
+
+ virtual void didDisplayInsecureContent();
+ virtual void didRunInsecureContent(SecurityOrigin*);
+
+ virtual void dispatchDidAddBackForwardItem(HistoryItem*) const;
+ virtual void dispatchDidRemoveBackForwardItem(HistoryItem*) const;
+ virtual void dispatchDidChangeBackForwardIndex() const;
+
+ virtual ResourceError cancelledError(const ResourceRequest&);
+ virtual ResourceError blockedError(const ResourceRequest&);
+ virtual ResourceError cannotShowURLError(const ResourceRequest&);
+ virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&);
+
+ virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&);
+ virtual ResourceError fileDoesNotExistError(const ResourceResponse&);
+ virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&);
+
+ virtual bool shouldFallBack(const ResourceError&);
+
+ virtual bool canHandleRequest(const ResourceRequest&) const;
+ virtual bool canShowMIMEType(const String& MIMEType) const;
+ virtual bool canShowMIMETypeAsHTML(const String& MIMEType) const;
+ virtual bool representationExistsForURLScheme(const String& URLScheme) const;
+ virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const;
+
+ virtual void frameLoadCompleted();
+ virtual void saveViewStateToItem(HistoryItem*);
+ virtual void restoreViewState();
+ virtual void provisionalLoadStarted();
+ virtual void didFinishLoad();
+ virtual void prepareForDataSourceReplacement();
+
+ virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&);
+ virtual void setTitle(const String& title, const KURL&);
+
+ // This provides the userAgent to WebCore. It is used by WebCore to
+ // populate navigator.userAgent and to set the HTTP header in
+ // ResourceRequest objects. We also set a userAgent on WebRequestContext
+ // for the Chromium HTTP stack, which overrides the value on the
+ // ResourceRequest.
+ virtual String userAgent(const KURL&);
+
+ virtual void savePlatformDataToCachedFrame(WebCore::CachedFrame*);
+ virtual void transitionToCommittedFromCachedFrame(WebCore::CachedFrame*);
+ virtual void transitionToCommittedForNewPage();
+
+ virtual void dispatchDidBecomeFrameset(bool isFrameSet);
+
+ virtual bool canCachePage() const;
+ virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&);
+
+ virtual WTF::PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight);
+ virtual void didTransferChildFrameToNewDocument(WebCore::Page*);
+ virtual void transferLoadingResourceFromPage(unsigned long identifier, DocumentLoader*, const ResourceRequest&, Page* oldPage);
+ virtual WTF::PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const WTF::Vector<String>&, const WTF::Vector<String>&, const String&, bool loadManually);
+ virtual void redirectDataToPlugin(Widget* pluginWidget);
+
+ virtual WTF::PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL& baseURL, const WTF::Vector<String>& paramNames, const WTF::Vector<String>& paramValues);
+
+ virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType);
+ virtual String overrideMediaType() const;
+
+ virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*);
+ virtual void documentElementAvailable();
+ virtual void didPerformFirstNavigation() const;
+
+#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; }
+#endif
+
+ virtual void registerForIconNotification(bool listen = true);
+
+ virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed);
+
+ virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext();
+
+ // 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();
+ void dispatchWillSendSubmitEvent(HTMLFormElement*);
+
+ virtual void didSaveToPageCache() { }
+ virtual void didRestoreFromPageCache() { }
+ private:
+ CacheBuilder m_cacheBuilder;
+ Frame* m_frame;
+ WebFrame* m_webFrame;
+ PluginManualLoader* m_manualLoader;
+ bool m_hasSentResponseToPlugin;
+ bool m_onDemandPluginsEnabled;
+
+ enum ResourceErrors {
+ InternalErrorCancelled = -99,
+ InternalErrorCannotShowUrl,
+ InternalErrorInterrupted,
+ InternalErrorCannotShowMimeType,
+ InternalErrorFileDoesNotExist,
+ InternalErrorPluginWillHandleLoadError,
+ InternalErrorLast
+ };
+
+ /* XXX: These must match android.net.http.EventHandler */
+ enum EventHandlerErrors {
+ Error = -1,
+ ErrorLookup = -2,
+ ErrorUnsupportedAuthScheme = -3,
+ ErrorAuth = -4,
+ ErrorProxyAuth = -5,
+ ErrorConnect = -6,
+ ErrorIO = -7,
+ ErrorTimeout = -8,
+ ErrorRedirectLoop = -9,
+ ErrorUnsupportedScheme = -10,
+ ErrorFailedSslHandshake = -11,
+ ErrorBadUrl = -12,
+ ErrorFile = -13,
+ ErrorFileNotFound = -14,
+ ErrorTooManyRequests = -15
+ };
+ friend class CacheBuilder;
+ };
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp
new file mode 100644
index 0000000..a5fe494
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "FrameNetworkingContextAndroid.h"
+
+#include "DocumentLoader.h"
+#include "MainResourceLoader.h"
+#include "Settings.h"
+
+using namespace WebCore;
+
+namespace android {
+
+FrameNetworkingContextAndroid::FrameNetworkingContextAndroid(WebCore::Frame* frame)
+ : WebCore::FrameNetworkingContext(frame)
+{
+}
+
+MainResourceLoader* FrameNetworkingContextAndroid::mainResourceLoader() const
+{
+ return frame()->loader()->activeDocumentLoader()->mainResourceLoader();
+}
+
+FrameLoaderClient* FrameNetworkingContextAndroid::frameLoaderClient() const
+{
+ return frame()->loader()->client();
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h
new file mode 100644
index 0000000..d0ff979
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h
@@ -0,0 +1,54 @@
+/*
+ * 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 FrameNetworkingContextAndroid_h
+#define FrameNetworkingContextAndroid_h
+
+#include "FrameNetworkingContext.h"
+
+namespace WebCore {
+class MainResourceLoader;
+class FrameLoaderClient;
+}
+
+namespace android {
+
+class FrameNetworkingContextAndroid : public WebCore::FrameNetworkingContext {
+public:
+ static PassRefPtr<FrameNetworkingContextAndroid> create(WebCore::Frame* frame)
+ {
+ return adoptRef(new FrameNetworkingContextAndroid(frame));
+ }
+
+private:
+ FrameNetworkingContextAndroid(WebCore::Frame*);
+
+ virtual WebCore::MainResourceLoader* mainResourceLoader() const;
+ virtual WebCore::FrameLoaderClient* frameLoaderClient() const;
+};
+
+} // namespace android
+
+#endif // FrameNetworkingContextAndroid_h
diff --git a/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp
new file mode 100755
index 0000000..36a9b61
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp
@@ -0,0 +1,419 @@
+/*
+ * 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 "config.h"
+#include "GeolocationPermissions.h"
+
+#include "DOMWindow.h"
+#include "Frame.h"
+#include "Geolocation.h"
+#include "Navigator.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteFileSystem.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include "WebViewCore.h"
+
+#include <text/CString.h>
+
+using namespace WebCore;
+
+namespace android {
+
+GeolocationPermissions::PermissionsMap GeolocationPermissions::s_permanentPermissions;
+GeolocationPermissions::GeolocationPermissionsVector GeolocationPermissions::s_instances;
+bool GeolocationPermissions::s_alwaysDeny = false;
+bool GeolocationPermissions::s_permanentPermissionsLoaded = false;
+bool GeolocationPermissions::s_permanentPermissionsModified = false;
+String GeolocationPermissions::s_databasePath;
+
+static const char* databaseName = "GeolocationPermissions.db";
+
+GeolocationPermissions::GeolocationPermissions(WebViewCore* webViewCore, Frame* mainFrame)
+ : m_webViewCore(webViewCore)
+ , m_mainFrame(mainFrame)
+ , m_timer(this, &GeolocationPermissions::timerFired)
+
+{
+ ASSERT(m_webViewCore);
+ maybeLoadPermanentPermissions();
+ s_instances.append(this);
+}
+
+GeolocationPermissions::~GeolocationPermissions()
+{
+ size_t index = s_instances.find(this);
+ s_instances.remove(index);
+}
+
+void GeolocationPermissions::queryPermissionState(Frame* frame)
+{
+ ASSERT(s_permanentPermissionsLoaded);
+
+ // We use SecurityOrigin::toString to key the map. Note that testing
+ // the SecurityOrigin pointer for equality is insufficient.
+ String originString = frame->document()->securityOrigin()->toString();
+
+ // If we've been told to always deny requests, do so.
+ if (s_alwaysDeny) {
+ makeAsynchronousCallbackToGeolocation(originString, false);
+ return;
+ }
+
+ // See if we have a record for this origin in the permanent permissions.
+ // These take precedence over temporary permissions so that changes made
+ // from the browser settings work as intended.
+ PermissionsMap::const_iterator iter = s_permanentPermissions.find(originString);
+ PermissionsMap::const_iterator end = s_permanentPermissions.end();
+ if (iter != end) {
+ bool allow = iter->second;
+ makeAsynchronousCallbackToGeolocation(originString, allow);
+ return;
+ }
+
+ // Check the temporary permisions.
+ iter = m_temporaryPermissions.find(originString);
+ end = m_temporaryPermissions.end();
+ if (iter != end) {
+ bool allow = iter->second;
+ makeAsynchronousCallbackToGeolocation(originString, allow);
+ return;
+ }
+
+ // If there's no pending request, prompt the user.
+ if (nextOriginInQueue().isEmpty()) {
+ // Although multiple tabs may request permissions for the same origin
+ // simultaneously, the routing in WebViewCore/CallbackProxy ensures that
+ // the result of the request will make it back to this object, so
+ // there's no need for a globally unique ID for the request.
+ m_webViewCore->geolocationPermissionsShowPrompt(originString);
+ }
+
+ // Add this request to the queue so we can track which frames requested it.
+ if (m_queuedOrigins.find(originString) == WTF::notFound) {
+ m_queuedOrigins.append(originString);
+ FrameSet frameSet;
+ frameSet.add(frame);
+ m_queuedOriginsToFramesMap.add(originString, frameSet);
+ } else {
+ ASSERT(m_queuedOriginsToFramesMap.contains(originString));
+ m_queuedOriginsToFramesMap.find(originString)->second.add(frame);
+ }
+}
+
+void GeolocationPermissions::cancelPermissionStateQuery(WebCore::Frame* frame)
+{
+ // We cancel any queued request for the given frame. There can be at most
+ // one of these, since each frame maps to a single origin. We only cancel
+ // the request if this frame is the only one reqesting permission for this
+ // origin.
+ //
+ // We can use the origin string to avoid searching the map.
+ String originString = frame->document()->securityOrigin()->toString();
+ size_t index = m_queuedOrigins.find(originString);
+ if (index == WTF::notFound)
+ return;
+
+ ASSERT(m_queuedOriginsToFramesMap.contains(originString));
+ OriginToFramesMap::iterator iter = m_queuedOriginsToFramesMap.find(originString);
+ ASSERT(iter->second.contains(frame));
+ iter->second.remove(frame);
+ if (!iter->second.isEmpty())
+ return;
+
+ m_queuedOrigins.remove(index);
+ m_queuedOriginsToFramesMap.remove(iter);
+
+ // If this is the origin currently being shown, cancel the prompt
+ // and show the next in the queue, if present.
+ if (index == 0) {
+ m_webViewCore->geolocationPermissionsHidePrompt();
+ if (!nextOriginInQueue().isEmpty())
+ m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue());
+ }
+}
+
+void GeolocationPermissions::makeAsynchronousCallbackToGeolocation(String origin, bool allow)
+{
+ m_callbackData.origin = origin;
+ m_callbackData.allow = allow;
+ m_timer.startOneShot(0);
+}
+
+void GeolocationPermissions::providePermissionState(String origin, bool allow, bool remember)
+{
+ ASSERT(s_permanentPermissionsLoaded);
+
+ // It's possible that this method is called with an origin that doesn't
+ // match m_originInProgress. This can occur if this object is reset
+ // while a permission result is in the process of being marshalled back to
+ // the WebCore thread from the browser. In this case, we simply ignore the
+ // call.
+ if (origin != nextOriginInQueue())
+ return;
+
+ maybeCallbackFrames(origin, allow);
+ recordPermissionState(origin, allow, remember);
+
+ // If the permissions are set to be remembered, cancel any queued requests
+ // for this domain in other tabs.
+ if (remember)
+ cancelPendingRequestsInOtherTabs(origin);
+
+ // Clear the origin from the queue.
+ ASSERT(!m_queuedOrigins.isEmpty());
+ m_queuedOrigins.remove(0);
+ ASSERT(m_queuedOriginsToFramesMap.contains(origin));
+ m_queuedOriginsToFramesMap.remove(origin);
+
+ // If there are other requests queued, start the next one.
+ if (!nextOriginInQueue().isEmpty())
+ m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue());
+}
+
+void GeolocationPermissions::recordPermissionState(String origin, bool allow, bool remember)
+{
+ if (remember) {
+ s_permanentPermissions.set(origin, allow);
+ s_permanentPermissionsModified = true;
+ } else {
+ // It's possible that another tab recorded a permanent permission for
+ // this origin while our request was in progress, but we record it
+ // anyway.
+ m_temporaryPermissions.set(origin, allow);
+ }
+}
+
+void GeolocationPermissions::cancelPendingRequestsInOtherTabs(String origin)
+{
+ for (GeolocationPermissionsVector::const_iterator iter = s_instances.begin();
+ iter != s_instances.end();
+ ++iter)
+ (*iter)->cancelPendingRequests(origin);
+}
+
+void GeolocationPermissions::cancelPendingRequests(String origin)
+{
+ size_t index = m_queuedOrigins.find(origin);
+
+ // Don't cancel the request if it's currently being shown, in which case
+ // it's at index 0.
+ if (index == WTF::notFound || !index)
+ return;
+
+ // Get the permission from the permanent list.
+ ASSERT(s_permanentPermissions.contains(origin));
+ PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin);
+ bool allow = iter->second;
+
+ maybeCallbackFrames(origin, allow);
+
+ m_queuedOrigins.remove(index);
+ ASSERT(m_queuedOriginsToFramesMap.contains(origin));
+ m_queuedOriginsToFramesMap.remove(origin);
+}
+
+void GeolocationPermissions::timerFired(Timer<GeolocationPermissions>* timer)
+{
+ ASSERT_UNUSED(timer, timer == &m_timer);
+ maybeCallbackFrames(m_callbackData.origin, m_callbackData.allow);
+}
+
+void GeolocationPermissions::resetTemporaryPermissionStates()
+{
+ ASSERT(s_permanentPermissionsLoaded);
+ m_queuedOrigins.clear();
+ m_queuedOriginsToFramesMap.clear();
+ m_temporaryPermissions.clear();
+ // If any permission results are being marshalled back to this thread, this
+ // will render them inefective.
+ m_timer.stop();
+
+ m_webViewCore->geolocationPermissionsHidePrompt();
+}
+
+const WTF::String& GeolocationPermissions::nextOriginInQueue()
+{
+ static const String emptyString = "";
+ return m_queuedOrigins.isEmpty() ? emptyString : m_queuedOrigins[0];
+}
+
+void GeolocationPermissions::maybeCallbackFrames(String origin, bool allow)
+{
+ // We can't track which frame issued the request, as frames can be deleted
+ // or have their contents replaced. Even uniqueChildName is not unique when
+ // frames are dynamically deleted and created. Instead, we simply call back
+ // to the Geolocation object in all frames from the correct origin.
+ for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ if (origin == frame->document()->securityOrigin()->toString()) {
+ // If the page has changed, it may no longer have a Geolocation
+ // object.
+ Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
+ if (geolocation)
+ geolocation->setIsAllowed(allow);
+ }
+ }
+}
+
+GeolocationPermissions::OriginSet GeolocationPermissions::getOrigins()
+{
+ maybeLoadPermanentPermissions();
+ OriginSet origins;
+ PermissionsMap::const_iterator end = s_permanentPermissions.end();
+ for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter)
+ origins.add(iter->first);
+ return origins;
+}
+
+bool GeolocationPermissions::getAllowed(String origin)
+{
+ maybeLoadPermanentPermissions();
+ bool allowed = false;
+ PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin);
+ PermissionsMap::const_iterator end = s_permanentPermissions.end();
+ if (iter != end)
+ allowed = iter->second;
+ return allowed;
+}
+
+void GeolocationPermissions::clear(String origin)
+{
+ maybeLoadPermanentPermissions();
+ PermissionsMap::iterator iter = s_permanentPermissions.find(origin);
+ if (iter != s_permanentPermissions.end()) {
+ s_permanentPermissions.remove(iter);
+ s_permanentPermissionsModified = true;
+ }
+}
+
+void GeolocationPermissions::allow(String origin)
+{
+ maybeLoadPermanentPermissions();
+ // We replace any existing permanent permission.
+ s_permanentPermissions.set(origin, true);
+ s_permanentPermissionsModified = true;
+}
+
+void GeolocationPermissions::clearAll()
+{
+ maybeLoadPermanentPermissions();
+ s_permanentPermissions.clear();
+ s_permanentPermissionsModified = true;
+}
+
+void GeolocationPermissions::maybeLoadPermanentPermissions()
+{
+ if (s_permanentPermissionsLoaded)
+ return;
+ s_permanentPermissionsLoaded = true;
+
+ SQLiteDatabase database;
+ if (!openDatabase(&database))
+ return;
+
+ // Create the table here, such that even if we've just created the DB, the
+ // commands below should succeed.
+ if (!database.executeCommand("CREATE TABLE IF NOT EXISTS Permissions (origin TEXT UNIQUE NOT NULL, allow INTEGER NOT NULL)")) {
+ database.close();
+ return;
+ }
+
+ SQLiteStatement statement(database, "SELECT * FROM Permissions");
+ if (statement.prepare() != SQLResultOk) {
+ database.close();
+ return;
+ }
+
+ ASSERT(s_permanentPermissions.size() == 0);
+ while (statement.step() == SQLResultRow)
+ s_permanentPermissions.set(statement.getColumnText(0), statement.getColumnInt64(1));
+
+ database.close();
+}
+
+void GeolocationPermissions::maybeStorePermanentPermissions()
+{
+ // If the permanent permissions haven't been modified, there's no need to
+ // save them to the DB. (If we haven't even loaded them, writing them now
+ // would overwrite the stored permissions with the empty set.)
+ if (!s_permanentPermissionsModified)
+ return;
+
+ SQLiteDatabase database;
+ if (!openDatabase(&database))
+ return;
+
+ SQLiteTransaction transaction(database);
+
+ // The number of entries should be small enough that it's not worth trying
+ // to perform a diff. Simply clear the table and repopulate it.
+ if (!database.executeCommand("DELETE FROM Permissions")) {
+ database.close();
+ return;
+ }
+
+ PermissionsMap::const_iterator end = s_permanentPermissions.end();
+ for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) {
+ SQLiteStatement statement(database, "INSERT INTO Permissions (origin, allow) VALUES (?, ?)");
+ if (statement.prepare() != SQLResultOk)
+ continue;
+ statement.bindText(1, iter->first);
+ statement.bindInt64(2, iter->second);
+ statement.executeCommand();
+ }
+
+ transaction.commit();
+ database.close();
+
+ s_permanentPermissionsModified = false;
+}
+
+void GeolocationPermissions::setDatabasePath(String path)
+{
+ // Take the first non-empty value.
+ if (s_databasePath.length() > 0)
+ return;
+ s_databasePath = path;
+}
+
+bool GeolocationPermissions::openDatabase(SQLiteDatabase* database)
+{
+ ASSERT(database);
+ String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(s_databasePath, databaseName);
+ if (!database->open(filename))
+ return false;
+ if (chmod(filename.utf8().data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) {
+ database->close();
+ return false;
+ }
+ return true;
+}
+
+void GeolocationPermissions::setAlwaysDeny(bool deny)
+{
+ s_alwaysDeny = deny;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h
new file mode 100644
index 0000000..fb31dfe
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h
@@ -0,0 +1,178 @@
+/*
+ * 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 GeolocationPermissions_h
+#define GeolocationPermissions_h
+
+#include "PlatformString.h"
+#include "Timer.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+ class Frame;
+ class Geolocation;
+ class SQLiteDatabase;
+}
+
+namespace android {
+
+ class WebViewCore;
+
+ // The GeolocationPermissions class manages Geolocation permissions for the
+ // browser. Permissions are managed on a per-origin basis, as required by
+ // the Geolocation spec - http://dev.w3.org/geo/api/spec-source.html. An
+ // origin specifies the scheme, host and port of particular frame. An
+ // origin is represented here as a string, using the output of
+ // WebCore::SecurityOrigin::toString.
+ //
+ // Each instance handles permissions for a given main frame. The class
+ // enforces the following policy.
+ // - Non-remembered permissions last for the dureation of the main frame.
+ // - Remembered permissions last indefinitely.
+ // - All permissions are shared between child frames of a main frame.
+ // - Only remembered permissions are shared between main frames.
+ // - Remembered permissions are made available for use in the browser
+ // settings menu.
+ class GeolocationPermissions : public RefCounted<GeolocationPermissions> {
+ public:
+ // Creates the GeolocationPermissions object to manage permissions for
+ // the specified main frame (i.e. tab). The WebViewCore is used to
+ // communicate with the browser to display UI.
+ GeolocationPermissions(WebViewCore* webViewCore, WebCore::Frame* mainFrame);
+ virtual ~GeolocationPermissions();
+
+ // Queries the permission state for the specified frame. If the
+ // permission state has not yet been set, prompts the user. Once the
+ // permission state has been determined, asynchronously calls back to
+ // the Geolocation objects in all frames in this WebView that are from
+ // the same origin as the requesting frame.
+ void queryPermissionState(WebCore::Frame* frame);
+ void cancelPermissionStateQuery(WebCore::Frame*);
+
+ // Provides this object with a permission state set by the user. The
+ // permission is specified by 'allow' and applied to 'origin'. If
+ // 'remember' is set, the permission state is remembered permanently.
+ // The new permission state is recorded and will trigger callbacks to
+ // geolocation objects as described above. If any other permission
+ // requests are queued, the next is started.
+ void providePermissionState(WTF::String origin, bool allow, bool remember);
+
+ // Clears the temporary permission state and any pending requests. Used
+ // when the main frame is refreshed or navigated to a new URL.
+ void resetTemporaryPermissionStates();
+
+ // Static methods for use from Java. These are used to interact with the
+ // browser settings menu and to update the permanent permissions when
+ // system settings are changed.
+ // Gets the list of all origins for which permanent permissions are
+ // recorded.
+ typedef HashSet<WTF::String> OriginSet;
+ static OriginSet getOrigins();
+ // Gets whether the specified origin is allowed.
+ static bool getAllowed(WTF::String origin);
+ // Clears the permission state for the specified origin.
+ static void clear(WTF::String origin);
+ // Sets the permission state for the specified origin to allowed.
+ static void allow(WTF::String origin);
+ // Clears the permission state for all origins.
+ static void clearAll();
+ // Sets whether the GeolocationPermissions object should always deny
+ // permission requests, irrespective of previously recorded permission
+ // states.
+ static void setAlwaysDeny(bool deny);
+
+ static void setDatabasePath(WTF::String path);
+ static bool openDatabase(WebCore::SQLiteDatabase*);
+
+ // Saves the permanent permissions to the DB if required.
+ static void maybeStorePermanentPermissions();
+
+ private:
+ // Records the permission state for the specified origin and whether
+ // this should be remembered.
+ void recordPermissionState(WTF::String origin, bool allow, bool remember);
+
+ // Used to make an asynchronous callback to the Geolocation objects.
+ void makeAsynchronousCallbackToGeolocation(WTF::String origin, bool allow);
+ void timerFired(WebCore::Timer<GeolocationPermissions>* timer);
+
+ // Calls back to the Geolocation objects in all frames from the
+ // specified origin. There may be no such objects, as the frames using
+ // Geolocation from the specified origin may no longer use Geolocation,
+ // or may have been navigated to a different origin..
+ void maybeCallbackFrames(WTF::String origin, bool allow);
+
+ // Cancels pending permission requests for the specified origin in
+ // other main frames (ie browser tabs). This is used when the user
+ // specifies permission to be remembered.
+ static void cancelPendingRequestsInOtherTabs(WTF::String origin);
+ void cancelPendingRequests(WTF::String origin);
+
+ static void maybeLoadPermanentPermissions();
+
+ const WTF::String& nextOriginInQueue();
+
+ WebViewCore* m_webViewCore;
+ WebCore::Frame* m_mainFrame;
+ // A vector of the origins queued to make a permission request.
+ // The first in the vector is the origin currently making the request.
+ typedef Vector<WTF::String> OriginVector;
+ OriginVector m_queuedOrigins;
+ // A map from a queued origin to the set of frames that have requested
+ // permission for that origin.
+ typedef HashSet<WebCore::Frame*> FrameSet;
+ typedef HashMap<WTF::String, FrameSet> OriginToFramesMap;
+ OriginToFramesMap m_queuedOriginsToFramesMap;
+
+ typedef WTF::HashMap<WTF::String, bool> PermissionsMap;
+ PermissionsMap m_temporaryPermissions;
+ static PermissionsMap s_permanentPermissions;
+
+ typedef WTF::Vector<GeolocationPermissions*> GeolocationPermissionsVector;
+ static GeolocationPermissionsVector s_instances;
+
+ WebCore::Timer<GeolocationPermissions> m_timer;
+
+ struct CallbackData {
+ WTF::String origin;
+ bool allow;
+ };
+ CallbackData m_callbackData;
+
+ static bool s_alwaysDeny;
+
+ static bool s_permanentPermissionsLoaded;
+ static bool s_permanentPermissionsModified;
+ static WTF::String s_databasePath;
+ };
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h b/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h
new file mode 100644
index 0000000..9d734e8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h
@@ -0,0 +1,54 @@
+/*
+ * 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 InspectorClientAndroid_h
+#define InspectorClientAndroid_h
+
+#include "InspectorClient.h"
+
+#include <wtf/Forward.h>
+
+namespace android {
+
+class InspectorClientAndroid : public InspectorClient {
+public:
+ virtual ~InspectorClientAndroid() { }
+
+ virtual void inspectorDestroyed() { delete this; }
+
+ virtual void openInspectorFrontend(WebCore::InspectorController*) {}
+
+ virtual void highlight(Node*) {}
+ virtual void hideHighlight() {}
+
+ virtual void populateSetting(const String& key, String* value) {}
+ virtual void storeSetting(const String& key, const String& value) {}
+
+ virtual bool sendMessageToFrontend(const WTF::String&) { return false; }
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h b/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h
new file mode 100644
index 0000000..1bcd8e8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h
@@ -0,0 +1,46 @@
+/*
+ * 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 KEY_GENERATOR_CLIENT_H
+#define KEY_GENERATOR_CLIENT_H
+
+#include "KURL.h"
+#include "PlatformString.h"
+
+#include <wtf/Vector.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class KeyGeneratorClient {
+public:
+ virtual ~KeyGeneratorClient() { }
+ virtual WTF::Vector<String> getSupportedKeyStrengthList() = 0;
+ virtual String getSignedPublicKeyAndChallengeString(unsigned index,
+ const String& challenge, const KURL& url) = 0;
+ };
+}
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
new file mode 100644
index 0000000..e6a2710
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
@@ -0,0 +1,654 @@
+/*
+ * 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 "config.h"
+#include "MediaPlayerPrivateAndroid.h"
+
+#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 "VideoLayerAndroid.h"
+#include "WebCoreJni.h"
+#include "WebViewCore.h"
+#include <GraphicsJNI.h>
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SkBitmap.h>
+#include <gui/SurfaceTexture.h>
+
+using namespace android;
+// Forward decl
+namespace android {
+sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
+};
+
+namespace WebCore {
+
+static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
+static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio";
+
+struct MediaPlayerPrivate::JavaGlue {
+ jobject m_javaProxy;
+ jmethodID m_play;
+ jmethodID m_teardown;
+ jmethodID m_seek;
+ jmethodID m_pause;
+ // Audio
+ jmethodID m_newInstance;
+ jmethodID m_setDataSource;
+ jmethodID m_getMaxTimeSeekable;
+ // Video
+ jmethodID m_getInstance;
+ jmethodID m_loadPoster;
+};
+
+MediaPlayerPrivate::~MediaPlayerPrivate()
+{
+ // m_videoLayer is reference counted, unref is enough here.
+ m_videoLayer->unref();
+ if (m_glue->m_javaProxy) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (env) {
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown);
+ env->DeleteGlobalRef(m_glue->m_javaProxy);
+ }
+ }
+ delete m_glue;
+}
+
+void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
+{
+ registrar(create, getSupportedTypes, supportsType);
+}
+
+MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
+{
+ if (WebViewCore::isSupportedMediaMimeType(type))
+ return MediaPlayer::MayBeSupported;
+ return MediaPlayer::IsNotSupported;
+}
+
+void MediaPlayerPrivate::pause()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_glue->m_javaProxy || !m_url.length())
+ return;
+
+ m_paused = true;
+ m_player->playbackStateChanged();
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_pause);
+ checkException(env);
+}
+
+void MediaPlayerPrivate::setVisible(bool visible)
+{
+ m_isVisible = visible;
+ if (m_isVisible)
+ createJavaPlayerIfNeeded();
+}
+
+void MediaPlayerPrivate::seek(float time)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ if (m_glue->m_javaProxy) {
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
+ m_currentTime = time;
+ }
+ checkException(env);
+}
+
+void MediaPlayerPrivate::prepareToPlay()
+{
+ // We are about to start playing. Since our Java VideoView cannot
+ // buffer any data, we just simply transition to the HaveEnoughData
+ // state in here. This will allow the MediaPlayer to transition to
+ // the "play" state, at which point our VideoView will start downloading
+ // the content and start the playback.
+ m_networkState = MediaPlayer::Loaded;
+ m_player->networkStateChanged();
+ m_readyState = MediaPlayer::HaveEnoughData;
+ m_player->readyStateChanged();
+}
+
+MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
+ : m_player(player),
+ m_glue(0),
+ m_duration(1), // keep this minimal to avoid initial seek problem
+ m_currentTime(0),
+ m_paused(true),
+ m_readyState(MediaPlayer::HaveNothing),
+ m_networkState(MediaPlayer::Empty),
+ m_poster(0),
+ m_naturalSize(100, 100),
+ m_naturalSizeUnknown(true),
+ m_isVisible(false),
+ m_videoLayer(new VideoLayerAndroid())
+{
+}
+
+void MediaPlayerPrivate::onEnded()
+{
+ m_currentTime = duration();
+ m_player->timeChanged();
+ m_paused = true;
+ m_player->playbackStateChanged();
+ m_networkState = MediaPlayer::Idle;
+}
+
+void MediaPlayerPrivate::onPaused()
+{
+ m_paused = true;
+ m_player->playbackStateChanged();
+ m_networkState = MediaPlayer::Idle;
+ m_player->playbackStateChanged();
+}
+
+void MediaPlayerPrivate::onTimeupdate(int position)
+{
+ m_currentTime = position / 1000.0f;
+ m_player->timeChanged();
+}
+
+void MediaPlayerPrivate::onStopFullscreen()
+{
+ if (m_player && m_player->mediaPlayerClient()
+ && m_player->mediaPlayerClient()->mediaPlayerOwningDocument()) {
+ m_player->mediaPlayerClient()->mediaPlayerOwningDocument()->webkitCancelFullScreen();
+ }
+}
+
+class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url)
+ {
+ m_url = url;
+ // Cheat a bit here to make sure Window.onLoad event can be triggered
+ // at the right time instead of real video play time, since only full
+ // screen video play is supported in Java's VideoView.
+ // See also comments in prepareToPlay function.
+ m_networkState = MediaPlayer::Loading;
+ m_player->networkStateChanged();
+ m_readyState = MediaPlayer::HaveCurrentData;
+ m_player->readyStateChanged();
+ }
+
+ void play()
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ 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();
+
+ if (m_currentTime == duration())
+ m_currentTime = 0;
+
+ jstring jUrl = wtfStringToJstring(env, m_url);
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl,
+ static_cast<jint>(m_currentTime * 1000.0f),
+ m_videoLayer->uniqueId());
+ env->DeleteLocalRef(jUrl);
+
+ checkException(env);
+ }
+ bool canLoadPoster() const { return true; }
+ void setPoster(const String& url)
+ {
+ if (m_posterUrl == url)
+ return;
+
+ m_posterUrl = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
+ return;
+ // Send the poster
+ jstring jUrl = wtfStringToJstring(env, m_posterUrl);
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ env->DeleteLocalRef(jUrl);
+ }
+ void paint(GraphicsContext* ctxt, const IntRect& r)
+ {
+ if (ctxt->paintingDisabled())
+ return;
+
+ if (!m_isVisible)
+ return;
+
+ if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
+ return;
+
+ SkCanvas* canvas = ctxt->platformContext()->mCanvas;
+ // We paint with the following rules in mind:
+ // - only downscale the poster, never upscale
+ // - maintain the natural aspect ratio of the poster
+ // - the poster should be centered in the target rect
+ float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
+ int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
+ int posterHeight = posterWidth / originalRatio;
+ int posterX = ((r.width() - posterWidth) / 2) + r.x();
+ int posterY = ((r.height() - posterHeight) / 2) + r.y();
+ IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
+ canvas->drawBitmapRect(*m_poster, 0, targetRect, 0);
+ }
+
+ void onPosterFetched(SkBitmap* poster)
+ {
+ m_poster = poster;
+ if (m_naturalSizeUnknown) {
+ // We had to fake the size at startup, or else our paint
+ // method would not be called. If we haven't yet received
+ // the onPrepared event, update the intrinsic size to the size
+ // of the poster. That will be overriden when onPrepare comes.
+ // In case of an error, we should report the poster size, rather
+ // than our initial fake value.
+ m_naturalSize = IntSize(poster->width(), poster->height());
+ m_player->sizeChanged();
+ }
+ }
+
+ void onPrepared(int duration, int width, int height)
+ {
+ m_duration = duration / 1000.0f;
+ m_naturalSize = IntSize(width, height);
+ m_naturalSizeUnknown = false;
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ }
+
+ virtual bool hasAudio() const { return false; } // do not display the audio UI
+ virtual bool hasVideo() const { return true; }
+ virtual bool supportsFullscreen() const { return true; }
+
+ MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
+ m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;II)V");
+
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
+ m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
+ m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
+ m_glue->m_javaProxy = 0;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded()
+ {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ jobject obj = 0;
+
+ FrameView* frameView = m_player->frameView();
+ if (!frameView)
+ return;
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView);
+ ASSERT(webViewCore);
+
+ // Get the HTML5VideoViewProxy instance
+ obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+ // Send the poster
+ jstring jUrl = 0;
+ if (m_posterUrl.length())
+ jUrl = wtfStringToJstring(env, m_posterUrl);
+ // Sending a NULL jUrl allows the Java side to try to load the default poster.
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ if (jUrl)
+ env->DeleteLocalRef(jUrl);
+
+ // Clean up.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+
+ float maxTimeSeekable() const
+ {
+ return m_duration;
+ }
+};
+
+class MediaPlayerAudioPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url)
+ {
+ m_url = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ jstring jUrl = wtfStringToJstring(env, m_url);
+ // start loading the data asynchronously
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl);
+ env->DeleteLocalRef(jUrl);
+ checkException(env);
+ }
+
+ void play()
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ m_paused = false;
+ m_player->playbackStateChanged();
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play);
+ checkException(env);
+ }
+
+ virtual bool hasAudio() const { return true; }
+ virtual bool hasVideo() const { return false; }
+ virtual bool supportsFullscreen() const { return false; }
+
+ float maxTimeSeekable() const
+ {
+ if (m_glue->m_javaProxy) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (env) {
+ float maxTime = env->CallFloatMethod(m_glue->m_javaProxy,
+ m_glue->m_getMaxTimeSeekable);
+ checkException(env);
+ return maxTime;
+ }
+ }
+ return 0;
+ }
+
+ MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V");
+ m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "()V");
+ m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F");
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
+ m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
+ m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
+ m_glue->m_javaProxy = 0;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded()
+ {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ jobject obj = 0;
+
+ // Get the HTML5Audio instance
+ obj = env->NewObject(clazz, m_glue->m_newInstance, this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+
+ // Clean up.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+
+ void onPrepared(int duration, int width, int height)
+ {
+ // Android media player gives us a duration of 0 for a live
+ // stream, so in that case set the real duration to infinity.
+ // We'll still be able to handle the case that we genuinely
+ // get an audio clip with a duration of 0s as we'll get the
+ // ended event when it stops playing.
+ if (duration > 0) {
+ m_duration = duration / 1000.0f;
+ } else {
+ m_duration = std::numeric_limits<float>::infinity();
+ }
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ m_player->prepareToPlay();
+ }
+};
+
+MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
+{
+ if (player->mediaElementType() == MediaPlayer::Video)
+ return new MediaPlayerVideoPrivate(player);
+ return new MediaPlayerAudioPrivate(player);
+}
+
+}
+
+namespace android {
+
+static void OnPrepared(JNIEnv* env, jobject obj, int duration, int width, int height, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onPrepared(duration, width, height);
+ }
+}
+
+static void OnEnded(JNIEnv* env, jobject obj, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onEnded();
+ }
+}
+
+static void OnPaused(JNIEnv* env, jobject obj, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onPaused();
+ }
+}
+
+static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer)
+{
+ if (!pointer || !poster)
+ return;
+
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster);
+ if (!posterNative)
+ return;
+ player->onPosterFetched(posterNative);
+}
+
+static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ // TODO: player->onBuffering(percent);
+ }
+}
+
+static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onTimeupdate(position);
+ }
+}
+
+// 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
+// video layers on the UI thread. Therefore, on the UI thread, we have to use
+// the videoLayerId from Java side to find the exact video layer in the tree
+// to set the surface texture.
+// Every time a play call into Java side, the videoLayerId will be sent and
+// saved in Java side. Then every time setBaseLayer call, the saved
+// videoLayerId will be passed to this function to find the Video Layer.
+// Return value: true when the video layer is found.
+static bool SendSurfaceTexture(JNIEnv* env, jobject obj, jobject surfTex,
+ int baseLayer, int videoLayerId,
+ int textureName, int playerState) {
+ if (!surfTex)
+ return false;
+
+ sp<SurfaceTexture> texture = android::SurfaceTexture_getSurfaceTexture(env, surfTex);
+ if (!texture.get())
+ return false;
+
+ 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*>(compositedRoot->findById(videoLayerId));
+ if (!videoLayer)
+ return false;
+
+ // Set the SurfaceTexture to the layer we found
+ videoLayer->setSurfaceTexture(texture, textureName, static_cast<PlayerState>(playerState));
+ return true;
+}
+
+static void OnStopFullscreen(JNIEnv* env, jobject obj, int pointer)
+{
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player =
+ reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ player->onStopFullscreen();
+ }
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod g_MediaPlayerMethods[] = {
+ { "nativeOnPrepared", "(IIII)V",
+ (void*) OnPrepared },
+ { "nativeOnEnded", "(I)V",
+ (void*) OnEnded },
+ { "nativeOnStopFullscreen", "(I)V",
+ (void*) OnStopFullscreen },
+ { "nativeOnPaused", "(I)V",
+ (void*) OnPaused },
+ { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
+ (void*) OnPosterFetched },
+ { "nativeSendSurfaceTexture", "(Landroid/graphics/SurfaceTexture;IIII)Z",
+ (void*) SendSurfaceTexture },
+ { "nativeOnTimeupdate", "(II)V",
+ (void*) OnTimeupdate },
+};
+
+static JNINativeMethod g_MediaAudioPlayerMethods[] = {
+ { "nativeOnBuffering", "(II)V",
+ (void*) OnBuffering },
+ { "nativeOnEnded", "(I)V",
+ (void*) OnEnded },
+ { "nativeOnPrepared", "(IIII)V",
+ (void*) OnPrepared },
+ { "nativeOnTimeupdate", "(II)V",
+ (void*) OnTimeupdate },
+};
+
+int registerMediaPlayerVideo(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, g_ProxyJavaClass,
+ g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
+}
+
+int registerMediaPlayerAudio(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio,
+ g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods));
+}
+
+}
+#endif // VIDEO
diff --git a/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp
new file mode 100644
index 0000000..32cdebf
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "MemoryUsage.h"
+
+#include <malloc.h>
+#include <wtf/CurrentTime.h>
+
+#if USE(V8)
+#include <v8.h>
+#endif // USE(V8)
+
+using namespace WTF;
+
+class MemoryUsageCache {
+public:
+ MemoryUsageCache()
+ : m_cachedMemoryUsage(0)
+ , m_cacheTime(0)
+ {
+ }
+
+ int getCachedMemoryUsage(bool forceFresh);
+
+private:
+ unsigned m_cachedMemoryUsage;
+ double m_cacheTime;
+ static const int CACHE_VALIDITY_MS = 2000;
+};
+
+int MemoryUsageCache::getCachedMemoryUsage(bool forceFresh)
+{
+ if (!forceFresh && currentTimeMS() <= m_cacheTime + CACHE_VALIDITY_MS)
+ return m_cachedMemoryUsage;
+
+ 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;
+}
+
+int MemoryUsage::memoryUsageMb(bool forceFresh)
+{
+ static MemoryUsageCache cache;
+ return cache.getCachedMemoryUsage(forceFresh);
+}
+
+int MemoryUsage::m_lowMemoryUsageMb = 0;
+int MemoryUsage::m_highMemoryUsageMb = 0;
+int MemoryUsage::m_highUsageDeltaMb = 0;
diff --git a/Source/WebKit/android/WebCoreSupport/MemoryUsage.h b/Source/WebKit/android/WebCoreSupport/MemoryUsage.h
new file mode 100644
index 0000000..2a3d7ed
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/MemoryUsage.h
@@ -0,0 +1,45 @@
+/*
+ * 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 MemoryUsage_h
+#define MemoryUsage_h
+
+class MemoryUsage {
+public:
+ static int memoryUsageMb(bool forceFresh);
+ static int lowMemoryUsageMb() { return m_lowMemoryUsageMb; }
+ static int highMemoryUsageMb() { return m_highMemoryUsageMb; }
+ static int highUsageDeltaMb() { return m_highUsageDeltaMb; }
+ static void setHighMemoryUsageMb(int highMemoryUsageMb) { m_highMemoryUsageMb = highMemoryUsageMb; }
+ static void setLowMemoryUsageMb(int lowMemoryUsageMb) { m_lowMemoryUsageMb = lowMemoryUsageMb; }
+ static void setHighUsageDeltaMb(int highUsageDeltaMb) { m_highUsageDeltaMb = highUsageDeltaMb; }
+
+private:
+ static int m_lowMemoryUsageMb;
+ static int m_highMemoryUsageMb;
+ static int m_highUsageDeltaMb;
+};
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp
new file mode 100644
index 0000000..8d8d809
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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 "config.h"
+#include <PlatformBridge.h>
+
+#include "CookieClient.h"
+#include "Document.h"
+#include "FileSystemClient.h"
+#include "FrameView.h"
+#include "JavaSharedClient.h"
+#include "KeyGeneratorClient.h"
+#include "MemoryUsage.h"
+#include "PluginView.h"
+#include "Settings.h"
+#include "WebCookieJar.h"
+#include "WebRequestContext.h"
+#include "WebViewCore.h"
+#include "npruntime.h"
+
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <ui/DisplayInfo.h>
+#include <ui/PixelFormat.h>
+#include <wtf/android/AndroidThreading.h>
+#include <wtf/MainThread.h>
+
+using namespace android;
+
+namespace WebCore {
+
+WTF::Vector<String> PlatformBridge::getSupportedKeyStrengthList()
+{
+ KeyGeneratorClient* client = JavaSharedClient::GetKeyGeneratorClient();
+ if (!client)
+ return WTF::Vector<String>();
+
+ return client->getSupportedKeyStrengthList();
+}
+
+String PlatformBridge::getSignedPublicKeyAndChallengeString(unsigned index, const String& challenge, const KURL& url)
+{
+ KeyGeneratorClient* client = JavaSharedClient::GetKeyGeneratorClient();
+ if (!client)
+ return String();
+
+ return client->getSignedPublicKeyAndChallengeString(index, challenge, url);
+}
+
+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::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();
+ 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)
+{
+ android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView);
+ return webViewCore->isPaused();
+}
+
+bool PlatformBridge::popupsAllowed(NPP)
+{
+ return false;
+}
+
+String PlatformBridge::resolveFilePathForContentUri(const String& contentUri)
+{
+ FileSystemClient* client = JavaSharedClient::GetFileSystemClient();
+ return client->resolveFilePathForContentUri(contentUri);
+}
+
+int PlatformBridge::PlatformBridge::screenDepth()
+{
+ android::DisplayInfo info;
+ android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info);
+ return info.pixelFormatInfo.bitsPerPixel;
+}
+
+FloatRect PlatformBridge::screenRect()
+{
+ android::DisplayInfo info;
+ android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info);
+ return FloatRect(0.0, 0.0, info.w, info.h);
+}
+
+// The visible size on screen in document coordinate
+int PlatformBridge::screenWidthInDocCoord(const WebCore::FrameView* frameView)
+{
+ android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView);
+ return webViewCore->screenWidth();
+}
+
+int PlatformBridge::screenHeightInDocCoord(const WebCore::FrameView* frameView)
+{
+ android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView);
+ return webViewCore->screenHeight();
+}
+
+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)
+{
+ android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView);
+ webViewCore->updateViewport();
+}
+
+void PlatformBridge::updateTextfield(FrameView* frameView, Node* nodePtr, bool changeToPassword, const WTF::String& text)
+{
+ android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView);
+ webViewCore->updateTextfield(nodePtr, changeToPassword, text);
+}
+
+void PlatformBridge::setScrollPosition(ScrollView* scrollView, int x, int y) {
+ // Check to make sure the view is the main FrameView.
+ android::WebViewCore *webViewCore = android::WebViewCore::getWebViewCore(scrollView);
+ if (webViewCore->mainFrame()->view() == scrollView)
+ webViewCore->scrollTo(x, y);
+}
+
+int PlatformBridge::lowMemoryUsageMB()
+{
+ return MemoryUsage::lowMemoryUsageMb();
+}
+
+int PlatformBridge::highMemoryUsageMB()
+{
+ return MemoryUsage::highMemoryUsageMb();
+}
+
+int PlatformBridge::highUsageDeltaMB()
+{
+ return MemoryUsage::highUsageDeltaMb();
+}
+
+int PlatformBridge::memoryUsageMB()
+{
+ return MemoryUsage::memoryUsageMb(false);
+}
+
+int PlatformBridge::actualMemoryUsageMB()
+{
+ return MemoryUsage::memoryUsageMb(true);
+}
+
+} // namespace WebCore
+
+
+// This is the implementation of AndroidThreading, which is declared in
+// JavaScriptCore/wtf/android/AndroidThreading.h. It is provided here, rather
+// than in its own source file, to avoid linker problems.
+//
+// By default, when building a shared library, the linker strips from static
+// libraries any compilation units which do not contain any code referenced from
+// that static library. Since
+// AndroidThreading::scheduleDispatchFunctionsOnMainThread is not referenced
+// from libwebcore.a, implementing it in its own compilation unit results in it
+// being stripped. This stripping can be avoided by using the linker option
+// --whole-archive for libwebcore.a, but this adds considerably to the size of
+// libwebcore.so.
+
+namespace WTF {
+
+// Callback in the main thread.
+static void timeoutFired(void*)
+{
+ dispatchFunctionsFromMainThread();
+}
+
+void AndroidThreading::scheduleDispatchFunctionsOnMainThread()
+{
+ JavaSharedClient::EnqueueFunctionPtr(timeoutFired, 0);
+}
+
+} // namespace WTF
diff --git a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp
new file mode 100644
index 0000000..7f54810
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <config.h>
+#include <ResourceLoaderAndroid.h>
+
+#include "Frame.h"
+#include "FrameLoaderClientAndroid.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
+#include "WebUrlLoader.h"
+#include "WebViewCore.h"
+
+using namespace android;
+
+namespace WebCore {
+
+PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(
+ ResourceHandle* handle, const ResourceRequest& request, FrameLoaderClient* client, bool isMainResource, bool isSync)
+{
+ // 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
+ // out the cache entry by the time we fetch it.
+ //
+ // So, we always say yes here, to prevent the FrameLoader from initiating a
+ // 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
new file mode 100644
index 0000000..3779ba8
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "UrlInterceptResponse"
+#include "config.h"
+
+#include "JNIUtility.h"
+#include "UrlInterceptResponse.h"
+#include "WebCoreJni.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+class JavaInputStreamWrapper {
+public:
+ JavaInputStreamWrapper(JNIEnv* env, jobject inputStream)
+ : m_inputStream(env->NewGlobalRef(inputStream))
+ , 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, "close", "()V");
+ LOG_ALWAYS_FATAL_IF(!m_close);
+ }
+
+ ~JavaInputStreamWrapper() {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_inputStream, m_close);
+ checkException(env);
+ env->DeleteGlobalRef(m_inputStream);
+ // In case we never call read().
+ if (m_buffer)
+ env->DeleteGlobalRef(m_buffer);
+ }
+
+ void read(std::vector<char>* out) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ // Initialize our read buffer to the capacity of out.
+ if (!m_buffer) {
+ m_buffer = env->NewByteArray(out->capacity());
+ m_buffer = (jbyteArray) env->NewGlobalRef(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.
+ out->resize(size);
+ env->GetByteArrayRegion(m_buffer, 0, size, (jbyte*)&out->front());
+ }
+
+private:
+ jobject m_inputStream;
+ jbyteArray m_buffer;
+ jmethodID m_read;
+ jmethodID m_close;
+};
+
+UrlInterceptResponse::UrlInterceptResponse(JNIEnv* env, jobject response) {
+ 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, "mEncoding",
+ "Ljava/lang/String;");
+ LOG_ALWAYS_FATAL_IF(!encoding);
+ jfieldID inputStream = env->GetFieldID(javaResponse, "mInputStream",
+ "Ljava/io/InputStream;");
+ LOG_ALWAYS_FATAL_IF(!inputStream);
+
+ jobject stream = env->GetObjectField(response, inputStream);
+ if (stream)
+ m_inputStream.set(new JavaInputStreamWrapper(env, stream));
+
+ jstring mimeStr = (jstring) env->GetObjectField(response, mimeType);
+ jstring encodingStr = (jstring) env->GetObjectField(response, encoding);
+
+ if (mimeStr) {
+ const char* s = env->GetStringUTFChars(mimeStr, NULL);
+ m_mimeType.assign(s, env->GetStringUTFLength(mimeStr));
+ env->ReleaseStringUTFChars(mimeStr, 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(mimeStr);
+ env->DeleteLocalRef(encodingStr);
+}
+
+UrlInterceptResponse::~UrlInterceptResponse() {
+ // Cannot be inlined because of JavaInputStreamWrapper visibility.
+}
+
+bool UrlInterceptResponse::readStream(std::vector<char>* out) const {
+ if (!m_inputStream)
+ return false;
+ m_inputStream->read(out);
+ return true;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h
new file mode 100644
index 0000000..64dad69
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h
@@ -0,0 +1,70 @@
+/*
+ * 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 UrlInterceptResponse_h
+#define UrlInterceptResponse_h
+
+#include "PlatformString.h"
+#include "wtf/Noncopyable.h"
+#include "wtf/OwnPtr.h"
+
+#include <jni.h>
+#include <string>
+#include <vector>
+
+namespace android {
+
+class JavaInputStreamWrapper;
+
+class UrlInterceptResponse : public Noncopyable {
+public:
+ UrlInterceptResponse(JNIEnv* env, jobject response);
+ ~UrlInterceptResponse();
+
+ const std::string& mimeType() const {
+ return m_mimeType;
+ }
+
+ const std::string& encoding() const {
+ return m_encoding;
+ }
+
+ int status() const {
+ return m_inputStream ? 200 : 404;
+ }
+
+ // Read from the input stream. Returns false if reading failed.
+ // A size of 0 indicates eof.
+ bool readStream(std::vector<char>* out) const;
+
+private:
+ std::string m_mimeType;
+ std::string m_encoding;
+ OwnPtr<JavaInputStreamWrapper> m_inputStream;
+};
+
+} // namespace android
+
+#endif
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
new file mode 100644
index 0000000..be9a700
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebCache.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 "WebCache.h"
+
+#include "JNIUtility.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "WebUrlLoaderClient.h"
+
+#include <wtf/text/CString.h>
+
+using namespace WTF;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+static WTF::Mutex instanceMutex;
+
+static const string& rootDirectory()
+{
+ // This method may be called on any thread, as the Java method is
+ // synchronized.
+ static WTF::Mutex mutex;
+ MutexLocker lock(mutex);
+ static string cacheDirectory;
+ if (cacheDirectory.empty()) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
+ jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;");
+ cacheDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
+ env->DeleteLocalRef(bridgeClass);
+ }
+ return cacheDirectory;
+}
+
+static string storageDirectory()
+{
+ // Private cache is currently in memory only
+ static const char* const kDirectory = "/webviewCacheChromium";
+ string storageDirectory = rootDirectory();
+ storageDirectory.append(kDirectory);
+ return storageDirectory;
+}
+
+static scoped_refptr<WebCache>* instance(bool isPrivateBrowsing)
+{
+ static scoped_refptr<WebCache> regularInstance;
+ static scoped_refptr<WebCache> privateInstance;
+ return isPrivateBrowsing ? &privateInstance : &regularInstance;
+}
+
+WebCache* WebCache::get(bool isPrivateBrowsing)
+{
+ MutexLocker lock(instanceMutex);
+ scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing);
+ if (!instancePtr->get())
+ *instancePtr = new WebCache(isPrivateBrowsing);
+ return instancePtr->get();
+}
+
+void WebCache::cleanup(bool isPrivateBrowsing)
+{
+ MutexLocker lock(instanceMutex);
+ scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing);
+ *instancePtr = 0;
+}
+
+WebCache::WebCache(bool isPrivateBrowsing)
+ : m_doomAllEntriesCallback(this, &WebCache::doomAllEntries)
+ , m_onClearDoneCallback(this, &WebCache::onClearDone)
+ , m_isClearInProgress(false)
+ , m_openEntryCallback(this, &WebCache::openEntry)
+ , m_onGetEntryDoneCallback(this, &WebCache::onGetEntryDone)
+ , m_isGetEntryInProgress(false)
+ , m_cacheBackend(0)
+{
+ base::Thread* ioThread = WebUrlLoaderClient::ioThread();
+ scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy();
+
+ static const int kMaximumCacheSizeBytes = 20 * 1024 * 1024;
+ m_hostResolver = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0);
+
+ m_proxyConfigService = new ProxyConfigServiceAndroid();
+ net::HttpCache::BackendFactory* backendFactory;
+ if (isPrivateBrowsing)
+ backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2);
+ else {
+ FilePath directoryPath(storageDirectory().c_str());
+ backendFactory = new net::HttpCache::DefaultBackend(net::DISK_CACHE, directoryPath, kMaximumCacheSizeBytes, cacheMessageLoopProxy);
+ }
+
+ m_cache = new net::HttpCache(m_hostResolver.get(),
+ 0, // dnsrr_resolver
+ 0, // dns_cert_checker
+ net::ProxyService::CreateWithoutProxyResolver(m_proxyConfigService, 0 /* net_log */),
+ net::SSLConfigService::CreateSystemSSLConfigService(),
+ net::HttpAuthHandlerFactory::CreateDefault(m_hostResolver.get()),
+ 0, // network_delegate
+ 0, // net_log
+ backendFactory);
+}
+
+void WebCache::clear()
+{
+ base::Thread* thread = WebUrlLoaderClient::ioThread();
+ if (thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::clearImpl));
+}
+
+void WebCache::clearImpl()
+{
+ if (m_isClearInProgress)
+ return;
+ m_isClearInProgress = true;
+
+ if (!m_cacheBackend) {
+ int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback);
+ // Code ERR_IO_PENDING indicates that the operation is still in progress and
+ // the supplied callback will be invoked when it completes.
+ if (code == ERR_IO_PENDING)
+ return;
+ else if (code != OK) {
+ onClearDone(0 /*unused*/);
+ return;
+ }
+ }
+ doomAllEntries(0 /*unused*/);
+}
+
+void WebCache::doomAllEntries(int)
+{
+ if (!m_cacheBackend) {
+ onClearDone(0 /*unused*/);
+ return;
+ }
+
+ // Code ERR_IO_PENDING indicates that the operation is still in progress and
+ // the supplied callback will be invoked when it completes.
+ if (m_cacheBackend->DoomAllEntries(&m_onClearDoneCallback) == ERR_IO_PENDING)
+ return;
+ onClearDone(0 /*unused*/);
+}
+
+void WebCache::onClearDone(int)
+{
+ m_isClearInProgress = false;
+}
+
+scoped_refptr<CacheResult> WebCache::getCacheResult(String url)
+{
+ // This is called on the UI thread.
+ MutexLocker lock(m_getEntryMutex);
+ if (m_isGetEntryInProgress)
+ return 0; // TODO: OK? Or can we queue 'em up?
+
+ // The Chromium methods are asynchronous, but we need this method to be
+ // synchronous. Do the work on the Chromium thread but block this thread
+ // here waiting for the work to complete.
+ base::Thread* thread = WebUrlLoaderClient::ioThread();
+ if (!thread)
+ return 0;
+
+ m_entry = 0;
+ m_isGetEntryInProgress = true;
+ m_entryUrl = url.threadsafeCopy();
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::getEntryImpl));
+
+ while (m_isGetEntryInProgress)
+ m_getEntryCondition.wait(m_getEntryMutex);
+
+ if (!m_entry)
+ return 0;
+
+ return new CacheResult(m_entry, url);
+}
+
+void WebCache::getEntryImpl()
+{
+ if (!m_cacheBackend) {
+ int code = m_cache->GetBackend(&m_cacheBackend, &m_openEntryCallback);
+ if (code == ERR_IO_PENDING)
+ return;
+ else if (code != OK) {
+ onGetEntryDone(0 /*unused*/);
+ return;
+ }
+ }
+ openEntry(0 /*unused*/);
+}
+
+void WebCache::openEntry(int)
+{
+ if (!m_cacheBackend) {
+ onGetEntryDone(0 /*unused*/);
+ return;
+ }
+
+ if (m_cacheBackend->OpenEntry(string(m_entryUrl.utf8().data()), &m_entry, &m_onGetEntryDoneCallback) == ERR_IO_PENDING)
+ return;
+ onGetEntryDone(0 /*unused*/);
+}
+
+void WebCache::onGetEntryDone(int)
+{
+ // Unblock the UI thread in getEntry();
+ MutexLocker lock(m_getEntryMutex);
+ m_isGetEntryInProgress = false;
+ m_getEntryCondition.signal();
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebCache.h b/Source/WebKit/android/WebCoreSupport/WebCache.h
new file mode 100644
index 0000000..7149fcc
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebCache.h
@@ -0,0 +1,88 @@
+/*
+ * 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 WebCache_h
+#define WebCache_h
+
+#include "CacheResult.h"
+#include "ChromiumIncludes.h"
+
+#include <OwnPtr.h>
+#include <platform/text/PlatformString.h>
+#include <wtf/ThreadingPrimitives.h>
+
+namespace android {
+
+// This class is not generally threadsafe. However, get() and cleanup() are
+// threadsafe.
+class WebCache : public base::RefCountedThreadSafe<WebCache> {
+public:
+ static WebCache* get(bool isPrivateBrowsing);
+ static void cleanup(bool isPrivateBrowsing);
+
+ void clear();
+ scoped_refptr<CacheResult> getCacheResult(WTF::String url);
+ net::HostResolver* hostResolver() { return m_hostResolver.get(); }
+ net::HttpCache* cache() { return m_cache.get(); }
+ net::ProxyConfigServiceAndroid* proxy() { return m_proxyConfigService; }
+
+private:
+ WebCache(bool isPrivateBrowsing);
+
+ // For clear()
+ void clearImpl();
+ void doomAllEntries(int);
+ void onClearDone(int);
+
+ // For getEntry()
+ void getEntryImpl();
+ void openEntry(int);
+ void onGetEntryDone(int);
+
+ OwnPtr<net::HostResolver> m_hostResolver;
+ OwnPtr<net::HttpCache> m_cache;
+ // This is owned by the ProxyService, which is owned by the HttpNetworkLayer,
+ // which is owned by the HttpCache, which is owned by this class.
+ net::ProxyConfigServiceAndroid* m_proxyConfigService;
+
+ // For clear()
+ net::CompletionCallbackImpl<WebCache> m_doomAllEntriesCallback;
+ net::CompletionCallbackImpl<WebCache> m_onClearDoneCallback;
+ bool m_isClearInProgress;
+ // For getEntry()
+ net::CompletionCallbackImpl<WebCache> m_openEntryCallback;
+ net::CompletionCallbackImpl<WebCache> m_onGetEntryDoneCallback;
+ bool m_isGetEntryInProgress;
+ String m_entryUrl;
+ disk_cache::Entry* m_entry;
+ WTF::Mutex m_getEntryMutex;
+ WTF::ThreadCondition m_getEntryCondition;
+
+ disk_cache::Backend* m_cacheBackend;
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
new file mode 100644
index 0000000..99de67e
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 "WebCookieJar.h"
+
+#include "JNIUtility.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "WebUrlLoaderClient.h"
+
+#include <cutils/log.h>
+#include <dirent.h>
+
+#undef ASSERT
+#define ASSERT(assertion, ...) do \
+ if (!(assertion)) { \
+ android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \
+ } \
+while (0)
+
+namespace android {
+
+static WTF::Mutex instanceMutex;
+static bool isFirstInstanceCreated = false;
+static bool fileSchemeCookiesEnabled = false;
+
+static const std::string& databaseDirectory()
+{
+ // This method may be called on any thread, as the Java method is
+ // synchronized.
+ static WTF::Mutex databaseDirectoryMutex;
+ MutexLocker lock(databaseDirectoryMutex);
+ static std::string databaseDirectory;
+ if (databaseDirectory.empty()) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass bridgeClass = env->FindClass("android/webkit/JniUtil");
+ jmethodID method = env->GetStaticMethodID(bridgeClass, "getDatabaseDirectory", "()Ljava/lang/String;");
+ databaseDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method)));
+ env->DeleteLocalRef(bridgeClass);
+ }
+ return databaseDirectory;
+}
+
+static void removeFileOrDirectory(const char* filename)
+{
+ struct stat filetype;
+ if (stat(filename, &filetype) != 0)
+ return;
+ if (S_ISDIR(filetype.st_mode)) {
+ DIR* directory = opendir(filename);
+ if (directory) {
+ while (struct dirent* entry = readdir(directory)) {
+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+ continue;
+ std::string entryName(filename);
+ entryName.append("/");
+ entryName.append(entry->d_name);
+ removeFileOrDirectory(entryName.c_str());
+ }
+ closedir(directory);
+ rmdir(filename);
+ }
+ return;
+ }
+ unlink(filename);
+}
+
+static std::string databaseDirectory(bool isPrivateBrowsing)
+{
+ static const char* const kDatabaseFilename = "/webviewCookiesChromium.db";
+ static const char* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db";
+
+ std::string databaseFilePath = databaseDirectory();
+ databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename);
+ return databaseFilePath;
+}
+
+scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing)
+{
+ static scoped_refptr<WebCookieJar> regularInstance;
+ static scoped_refptr<WebCookieJar> privateInstance;
+ return isPrivateBrowsing ? &privateInstance : &regularInstance;
+}
+
+WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing)
+{
+ MutexLocker lock(instanceMutex);
+ if (!isFirstInstanceCreated && fileSchemeCookiesEnabled)
+ net::CookieMonster::EnableFileScheme();
+ isFirstInstanceCreated = true;
+ scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
+ if (!instancePtr->get())
+ *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing));
+ return instancePtr->get();
+}
+
+void WebCookieJar::cleanup(bool isPrivateBrowsing)
+{
+ // This is called on the UI thread.
+ MutexLocker lock(instanceMutex);
+ scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing);
+ *instancePtr = 0;
+ removeFileOrDirectory(databaseDirectory(isPrivateBrowsing).c_str());
+}
+
+WebCookieJar::WebCookieJar(const std::string& databaseFilePath)
+ : m_allowCookies(true)
+{
+ // Setup the permissions for the file
+ 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);
+ else {
+ int fd = open(cDatabasePath, O_CREAT, mode);
+ if (fd >= 0)
+ close(fd);
+ }
+
+ FilePath cookiePath(databaseFilePath.c_str());
+ m_cookieDb = new SQLitePersistentCookieStore(cookiePath);
+ m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0);
+}
+
+bool WebCookieJar::allowCookies()
+{
+ MutexLocker lock(m_allowCookiesMutex);
+ return m_allowCookies;
+}
+
+void WebCookieJar::setAllowCookies(bool allow)
+{
+ MutexLocker lock(m_allowCookiesMutex);
+ 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&, net::CompletionCallback*)
+{
+ MutexLocker lock(m_allowCookiesMutex);
+ return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED;
+}
+
+// From CookiePolicy in chromium
+int WebCookieJar::CanSetCookie(const GURL&, const GURL&, const std::string&, net::CompletionCallback*)
+{
+ MutexLocker lock(m_allowCookiesMutex);
+ return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED;
+}
+
+class FlushSemaphore : public base::RefCounted<FlushSemaphore>
+{
+public:
+ FlushSemaphore()
+ : m_condition(&m_lock)
+ , m_count(0)
+ {}
+
+ void SendFlushRequest(net::CookieMonster* monster) {
+ // FlushStore() needs to run on a Chrome thread (because it will need
+ // to post the callback, and it may want to do so on its own thread.)
+ // We use the IO thread for this purpose.
+ //
+ // TODO(husky): Our threads are hidden away in various files. Clean this
+ // up and consider integrating with Chrome's browser_thread.h. Might be
+ // a better idea to use the DB thread here rather than the IO thread.
+
+ base::Thread* ioThread = WebUrlLoaderClient::ioThread();
+ if (ioThread) {
+ Task* callback = NewRunnableMethod(this, &FlushSemaphore::Callback);
+ ioThread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ monster, &net::CookieMonster::FlushStore, callback));
+ } else {
+ Callback();
+ }
+ }
+
+ // Block until the given number of callbacks has been made.
+ void Wait(int numCallbacks) {
+ AutoLock al(m_lock);
+ int lastCount = m_count;
+ while (m_count < numCallbacks) {
+ // TODO(husky): Maybe use TimedWait() here? But it's not obvious what
+ // to do if the flush fails. Might be okay just to let the OS kill us.
+ m_condition.Wait();
+ ASSERT(lastCount != m_count, "Wait finished without incrementing m_count %d %d", m_count, lastCount);
+ lastCount = m_count;
+ }
+ m_count -= numCallbacks;
+ }
+
+private:
+ friend class base::RefCounted<FlushSemaphore>;
+
+ void Callback() {
+ AutoLock al(m_lock);
+ m_count++;
+ m_condition.Broadcast();
+ }
+
+ Lock m_lock;
+ ConditionVariable m_condition;
+ volatile int m_count;
+};
+
+void WebCookieJar::flush()
+{
+ // Flush both cookie stores (private and non-private), wait for 2 callbacks.
+ static scoped_refptr<FlushSemaphore> semaphore(new FlushSemaphore());
+ semaphore->SendFlushRequest(get(false)->cookieStore()->GetCookieMonster());
+ semaphore->SendFlushRequest(get(true)->cookieStore()->GetCookieMonster());
+ semaphore->Wait(2);
+}
+
+bool WebCookieJar::acceptFileSchemeCookies()
+{
+ MutexLocker lock(instanceMutex);
+ return fileSchemeCookiesEnabled;
+}
+
+void WebCookieJar::setAcceptFileSchemeCookies(bool accept)
+{
+ // The Chromium HTTP stack only reflects changes to this flag when creating
+ // a new CookieMonster instance. While we could track whether any
+ // CookieMonster instances currently exist, this would be complicated and is
+ // not required, so we only allow this flag to be changed before the first
+ // instance is created.
+ MutexLocker lock(instanceMutex);
+ if (!isFirstInstanceCreated)
+ fileSchemeCookiesEnabled = accept;
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.h b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h
new file mode 100644
index 0000000..1f4266c
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h
@@ -0,0 +1,78 @@
+/*
+ * 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 WebCookieJar_h
+#define WebCookieJar_h
+
+#include "ChromiumIncludes.h"
+
+#include <wtf/ThreadingPrimitives.h>
+
+namespace android {
+
+// This class is threadsafe. It is used from the IO, WebCore and Chromium IO
+// threads.
+class WebCookieJar : public net::CookiePolicy, public base::RefCountedThreadSafe<WebCookieJar> {
+public:
+ static WebCookieJar* get(bool isPrivateBrowsing);
+ static void cleanup(bool isPrivateBrowsing);
+
+ // Flush all cookies to disk. Synchronous.
+ static void flush();
+
+ // CookiePolicy implementation from external/chromium
+ virtual int CanGetCookies(const GURL& url, const GURL& first_party_for_cookies, net::CompletionCallback*);
+ virtual int CanSetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie_line, net::CompletionCallback*);
+
+ bool allowCookies();
+ void setAllowCookies(bool allow);
+
+ // Getter and setter for whether we accept cookies for file scheme URLS.
+ // Defaults to false. Note that calls to the setter are ignored once the
+ // first instance of this class has been created.
+ static bool acceptFileSchemeCookies();
+ static void setAcceptFileSchemeCookies(bool);
+
+ // 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() { return m_cookieStore.get(); }
+ net::CookiePolicy* cookiePolicy() { return this; }
+
+ // Get the number of cookies that have actually been saved to flash.
+ // (This is used to implement CookieManager.hasCookies() in the Java framework.)
+ int getNumCookiesInDatabase();
+
+private:
+ WebCookieJar(const std::string& databaseFilePath);
+
+ scoped_refptr<SQLitePersistentCookieStore> m_cookieDb;
+ scoped_refptr<net::CookieStore> m_cookieStore;
+ bool m_allowCookies;
+ WTF::Mutex m_allowCookiesMutex;
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.cpp b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp
new file mode 100644
index 0000000..a7321da
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp
@@ -0,0 +1,531 @@
+/*
+ * 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 "WebRequest.h"
+
+#include "JNIUtility.h"
+#include "MainThread.h"
+#include "UrlInterceptResponse.h"
+#include "WebCoreFrameBridge.h"
+#include "WebRequestContext.h"
+#include "WebResourceRequest.h"
+#include "WebUrlLoaderClient.h"
+#include "jni.h"
+
+#include <cutils/log.h>
+#include <string>
+#include <utils/AssetManager.h>
+
+extern android::AssetManager* globalAssetManager();
+
+// TODO:
+// - Finish the file upload. Testcase is mobile buzz
+// - Add network throttle needed by Android plugins
+
+// TODO: Turn off asserts crashing before release
+// http://b/issue?id=2951985
+#undef ASSERT
+#define ASSERT(assertion, ...) do \
+ if (!(assertion)) { \
+ android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \
+ } \
+while (0)
+
+namespace android {
+
+namespace {
+ 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)
+ , m_authRequestCount(0)
+ , m_cacheMode(0)
+ , m_runnableFactory(this)
+ , m_wantToPause(false)
+ , m_isPaused(false)
+ , m_isSync(false)
+{
+ GURL gurl(m_url);
+
+ m_request = new URLRequest(gurl, this);
+
+ m_request->SetExtraRequestHeaders(webResourceRequest.requestHeaders());
+ m_request->set_referrer(webResourceRequest.referrer());
+ m_request->set_method(webResourceRequest.method());
+ m_request->set_load_flags(webResourceRequest.loadFlags());
+}
+
+// This is a special URL for Android. Query the Java InputStream
+// for data and send to WebCore
+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)
+ , m_authRequestCount(0)
+ , m_cacheMode(0)
+ , m_runnableFactory(this)
+ , m_wantToPause(false)
+ , m_isPaused(false)
+ , m_isSync(false)
+{
+}
+
+WebRequest::~WebRequest()
+{
+ ASSERT(m_loadState == Finished, "dtor called on a WebRequest in a different state than finished (%d)", m_loadState);
+
+ m_loadState = Deleted;
+}
+
+const std::string& WebRequest::getUrl() const
+{
+ return m_url;
+}
+
+const std::string& WebRequest::getUserAgent() const
+{
+ return m_userAgent;
+}
+
+#ifdef LOG_REQUESTS
+namespace {
+int remaining = 0;
+}
+#endif
+
+void WebRequest::finish(bool success)
+{
+ m_runnableFactory.RevokeAll();
+ ASSERT(m_loadState < Finished, "(%p) called finish on an already finished WebRequest (%d) (%s)", this, m_loadState, m_url.c_str());
+ if (m_loadState >= Finished)
+ return;
+#ifdef LOG_REQUESTS
+ time_t finish;
+ time(&finish);
+ finish = finish - m_startTime;
+ struct tm * timeinfo;
+ char buffer[80];
+ timeinfo = localtime(&finish);
+ strftime(buffer, 80, "Time: %M:%S",timeinfo);
+ android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) finish (%d) (%s) (%d) (%s)", this, --remaining, buffer, success, m_url.c_str());
+#endif
+
+ // Make sure WebUrlLoaderClient doesn't delete us in the middle of this method.
+ scoped_refptr<WebRequest> guard(this);
+
+ m_loadState = Finished;
+ if (success) {
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didFinishLoading));
+ } else {
+ if (m_interceptResponse == NULL) {
+ OwnPtr<WebResponse> webResponse(new WebResponse(m_request.get()));
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release()));
+ } else {
+ OwnPtr<WebResponse> webResponse(new WebResponse(m_url, m_interceptResponse->mimeType(), 0,
+ m_interceptResponse->encoding(), m_interceptResponse->status()));
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release()));
+ }
+ }
+ m_networkBuffer = 0;
+ m_request = 0;
+ m_urlLoader = 0;
+}
+
+void WebRequest::appendFileToUpload(const std::string& filename)
+{
+ // AppendFileToUpload is only valid before calling start
+ ASSERT(m_loadState == Created, "appendFileToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
+ FilePath filePath(filename);
+ m_request->AppendFileToUpload(filePath);
+}
+
+void WebRequest::appendBytesToUpload(WTF::Vector<char>* data)
+{
+ // AppendBytesToUpload is only valid before calling start
+ ASSERT(m_loadState == Created, "appendBytesToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
+ m_request->AppendBytesToUpload(data->data(), data->size());
+ delete data;
+}
+
+void WebRequest::setRequestContext(WebRequestContext* context)
+{
+ m_cacheMode = context->getCacheMode();
+ if (m_request)
+ m_request->set_context(context);
+}
+
+void WebRequest::updateLoadFlags(int& loadFlags)
+{
+ if (m_cacheMode == 1) { // LOAD_CACHE_ELSE_NETWORK
+ loadFlags |= net::LOAD_PREFERRING_CACHE;
+ loadFlags &= ~net::LOAD_VALIDATE_CACHE;
+ }
+ if (m_cacheMode == 2) // LOAD_NO_CACHE
+ loadFlags |= net::LOAD_BYPASS_CACHE;
+ if (m_cacheMode == 3) // LOAD_CACHE_ONLY
+ loadFlags |= net::LOAD_ONLY_FROM_CACHE;
+
+ if (m_isSync)
+ loadFlags |= net::LOAD_IGNORE_LIMITS;
+}
+
+void WebRequest::start()
+{
+ ASSERT(m_loadState == Created, "Start called on a WebRequest not in CREATED state: (%s)", m_url.c_str());
+#ifdef LOG_REQUESTS
+ android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) start (%d) (%s)", this, ++remaining, m_url.c_str());
+ time(&m_startTime);
+#endif
+
+ m_loadState = Started;
+
+ if (m_interceptResponse != NULL)
+ return handleInterceptedURL();
+
+ // Handle data urls before we send it off to the http stack
+ if (m_request->url().SchemeIs("data"))
+ return handleDataURL(m_request->url());
+
+ if (m_request->url().SchemeIs("browser"))
+ return handleBrowserURL(m_request->url());
+
+ // Update load flags with settings from WebSettings
+ int loadFlags = m_request->load_flags();
+ updateLoadFlags(loadFlags);
+ m_request->set_load_flags(loadFlags);
+
+ m_request->Start();
+}
+
+void WebRequest::cancel()
+{
+ ASSERT(m_loadState >= Started, "Cancel called on a not started WebRequest: (%s)", m_url.c_str());
+ ASSERT(m_loadState != Cancelled, "Cancel called on an already cancelled WebRequest: (%s)", m_url.c_str());
+
+ // There is a possible race condition between the IO thread finishing the request and
+ // the WebCore thread cancelling it. If the request has already finished, do
+ // nothing to avoid sending duplicate finish messages to WebCore.
+ if (m_loadState > Cancelled) {
+ return;
+ }
+ ASSERT(m_request, "Request set to 0 before it is finished");
+
+ m_loadState = Cancelled;
+
+ m_request->Cancel();
+ finish(true);
+}
+
+void WebRequest::pauseLoad(bool pause)
+{
+ ASSERT(m_loadState >= GotData, "PauseLoad in state other than RESPONSE and GOTDATA");
+ if (pause) {
+ if (!m_isPaused)
+ m_wantToPause = true;
+ } else {
+ m_wantToPause = false;
+ if (m_isPaused) {
+ m_isPaused = false;
+ MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading));
+ }
+ }
+}
+
+void WebRequest::handleInterceptedURL()
+{
+ m_loadState = Response;
+
+ const std::string& mime = m_interceptResponse->mimeType();
+ // Get the MIME type from the URL. "text/html" is a last resort, hopefully overridden.
+ std::string mimeType("text/html");
+ if (mime == "") {
+ // 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;
+ }
+
+
+ OwnPtr<WebResponse> webResponse(new WebResponse(m_url, mimeType, 0, m_interceptResponse->encoding(), m_interceptResponse->status()));
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
+
+ do {
+ // data is deleted in WebUrlLoaderClient::didReceiveAndroidFileData
+ // data is sent to the webcore thread
+ OwnPtr<std::vector<char> > data(new std::vector<char>);
+ data->reserve(kInitialReadBufSize);
+
+ // Read returns false on error and size of 0 on eof.
+ if (!m_interceptResponse->readStream(data.get()) || data->size() == 0)
+ break;
+
+ m_loadState = GotData;
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveAndroidFileData, data.release()));
+ } while (true);
+
+ finish(m_interceptResponse->status() == 200);
+}
+
+void WebRequest::handleDataURL(GURL url)
+{
+ OwnPtr<std::string> data(new std::string);
+ std::string mimeType;
+ std::string charset;
+
+ if (net::DataURL::Parse(url, &mimeType, &charset, data.get())) {
+ // PopulateURLResponse from chrome implementation
+ // weburlloader_impl.cc
+ m_loadState = Response;
+ OwnPtr<WebResponse> webResponse(new WebResponse(url.spec(), mimeType, data->size(), charset, 200));
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
+
+ if (!data->empty()) {
+ m_loadState = GotData;
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveDataUrl, data.release()));
+ }
+ } else {
+ // handle the failed case
+ }
+
+ finish(true);
+}
+
+void WebRequest::handleBrowserURL(GURL url)
+{
+ std::string data("data:text/html;charset=utf-8,");
+ if (url.spec() == "browser:incognito") {
+ AssetManager* assetManager = globalAssetManager();
+ Asset* asset = assetManager->open("webkit/incognito_mode_start_page.html", Asset::ACCESS_BUFFER);
+ if (asset) {
+ data.append((const char*)asset->getBuffer(false), asset->getLength());
+ delete asset;
+ }
+ }
+ GURL dataURL(data.c_str());
+ handleDataURL(dataURL);
+}
+
+// Called upon a server-initiated redirect. The delegate may call the
+// request's Cancel method to prevent the redirect from being followed.
+// Since there may be multiple chained redirects, there may also be more
+// than one redirect call.
+//
+// When this function is called, the request will still contain the
+// original URL, the destination of the redirect is provided in 'new_url'.
+// If the delegate does not cancel the request and |*defer_redirect| is
+// false, then the redirect will be followed, and the request's URL will be
+// changed to the new URL. Otherwise if the delegate does not cancel the
+// request and |*defer_redirect| is true, then the redirect will be
+// followed once FollowDeferredRedirect is called on the URLRequest.
+//
+// The caller must set |*defer_redirect| to false, so that delegates do not
+// need to set it if they are happy with the default behavior of not
+// deferring redirect.
+void WebRequest::OnReceivedRedirect(URLRequest* newRequest, const GURL& newUrl, bool* deferRedirect)
+{
+ ASSERT(m_loadState < Response, "Redirect after receiving response");
+ ASSERT(newRequest && newRequest->status().is_success(), "Invalid redirect");
+
+ OwnPtr<WebResponse> webResponse(new WebResponse(newRequest));
+ webResponse->setUrl(newUrl.spec());
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::willSendRequest, webResponse.release()));
+
+ // Defer the redirect until followDeferredRedirect() is called.
+ *deferRedirect = true;
+}
+
+// Called when we receive an authentication failure. The delegate should
+// call request->SetAuth() with the user's credentials once it obtains them,
+// or request->CancelAuth() to cancel the login and display the error page.
+// When it does so, the request will be reissued, restarting the sequence
+// of On* callbacks.
+void WebRequest::OnAuthRequired(URLRequest* request, net::AuthChallengeInfo* authInfo)
+{
+ ASSERT(m_loadState == Started, "OnAuthRequired called on a WebRequest not in STARTED state (state=%d)", m_loadState);
+
+ scoped_refptr<net::AuthChallengeInfo> authInfoPtr(authInfo);
+ bool firstTime = (m_authRequestCount == 0);
+ ++m_authRequestCount;
+
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr, firstTime));
+}
+
+// Called when we received an SSL certificate error. The delegate will provide
+// the user the options to proceed, cancel, or view certificates.
+void WebRequest::OnSSLCertificateError(URLRequest* request, int cert_error, net::X509Certificate* cert)
+{
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::reportSslCertError, cert_error, cert));
+}
+
+// After calling Start(), the delegate will receive an OnResponseStarted
+// callback when the request has completed. If an error occurred, the
+// request->status() will be set. On success, all redirects have been
+// followed and the final response is beginning to arrive. At this point,
+// meta data about the response is available, including for example HTTP
+// response headers if this is a request for a HTTP resource.
+void WebRequest::OnResponseStarted(URLRequest* request)
+{
+ ASSERT(m_loadState == Started, "Got response after receiving response");
+
+ m_loadState = Response;
+ if (request && request->status().is_success()) {
+ OwnPtr<WebResponse> webResponse(new WebResponse(request));
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release()));
+
+ // Start reading the response
+ startReading();
+ } else {
+ finish(false);
+ }
+}
+
+void WebRequest::setAuth(const string16& username, const string16& password)
+{
+ ASSERT(m_loadState == Started, "setAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState);
+
+ m_request->SetAuth(username, password);
+}
+
+void WebRequest::cancelAuth()
+{
+ ASSERT(m_loadState == Started, "cancelAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState);
+
+ m_request->CancelAuth();
+}
+
+void WebRequest::followDeferredRedirect()
+{
+ ASSERT(m_loadState < Response, "Redirect after receiving response");
+
+ m_request->FollowDeferredRedirect();
+}
+
+void WebRequest::proceedSslCertError()
+{
+ m_request->ContinueDespiteLastError();
+}
+
+void WebRequest::cancelSslCertError(int cert_error)
+{
+ m_request->SimulateError(cert_error);
+}
+
+void WebRequest::startReading()
+{
+ ASSERT(m_networkBuffer == 0, "startReading called with a nonzero buffer");
+ ASSERT(m_isPaused == 0, "startReading called in paused state");
+ ASSERT(m_loadState == Response || m_loadState == GotData, "StartReading in state other than RESPONSE and GOTDATA");
+ if (m_loadState > GotData) // We have been cancelled between reads
+ return;
+
+ if (m_wantToPause) {
+ m_isPaused = true;
+ return;
+ }
+
+ int bytesRead = 0;
+
+ if (!read(&bytesRead)) {
+ if (m_request && m_request->status().is_io_pending())
+ return; // Wait for OnReadCompleted()
+ return finish(false);
+ }
+
+ // bytesRead == 0 indicates finished
+ if (!bytesRead)
+ return finish(true);
+
+ m_loadState = GotData;
+ // Read ok, forward buffer to webcore
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead));
+ m_networkBuffer = 0;
+ MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading));
+}
+
+bool WebRequest::read(int* bytesRead)
+{
+ ASSERT(m_loadState == Response || m_loadState == GotData, "read in state other than RESPONSE and GOTDATA");
+ ASSERT(m_networkBuffer == 0, "Read called with a nonzero buffer");
+
+ // TODO: when asserts work, check that the buffer is 0 here
+ m_networkBuffer = new net::IOBuffer(kInitialReadBufSize);
+ return m_request->Read(m_networkBuffer, kInitialReadBufSize, bytesRead);
+}
+
+// This is called when there is data available
+
+// Called when the a Read of the response body is completed after an
+// IO_PENDING status from a Read() call.
+// The data read is filled into the buffer which the caller passed
+// to Read() previously.
+//
+// If an error occurred, request->status() will contain the error,
+// and bytes read will be -1.
+void WebRequest::OnReadCompleted(URLRequest* request, int bytesRead)
+{
+ ASSERT(m_loadState == Response || m_loadState == GotData, "OnReadCompleted in state other than RESPONSE and GOTDATA");
+
+ if (request->status().is_success()) {
+ m_loadState = GotData;
+ m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(
+ m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead));
+ m_networkBuffer = 0;
+
+ // Get the rest of the data
+ startReading();
+ } else {
+ finish(false);
+ }
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.h b/Source/WebKit/android/WebCoreSupport/WebRequest.h
new file mode 100644
index 0000000..252267b
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebRequest.h
@@ -0,0 +1,125 @@
+/*
+ * 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 WebRequest_h
+#define WebRequest_h
+
+#include "ChromiumIncludes.h"
+#include <wtf/Vector.h>
+
+class MessageLoop;
+
+namespace android {
+
+enum LoadState {
+ Created,
+ Started,
+ Response,
+ GotData,
+ Cancelled,
+ Finished,
+ Deleted
+};
+
+class UrlInterceptResponse;
+class WebFrame;
+class WebRequestContext;
+class WebResourceRequest;
+class WebUrlLoaderClient;
+
+// All methods in this class must be called on the io thread
+class WebRequest : public URLRequest::Delegate, public base::RefCountedThreadSafe<WebRequest> {
+public:
+ WebRequest(WebUrlLoaderClient*, const WebResourceRequest&);
+
+ // If this is an android specific url or the application wants to load
+ // custom data, we load the data through an input stream.
+ // Used for:
+ // - file:///android_asset
+ // - file:///android_res
+ // - content://
+ WebRequest(WebUrlLoaderClient*, const WebResourceRequest&, UrlInterceptResponse* intercept);
+
+ // Optional, but if used has to be called before start
+ void appendBytesToUpload(Vector<char>* data);
+ void appendFileToUpload(const std::string& filename);
+
+ void setRequestContext(WebRequestContext* context);
+ void start();
+ void cancel();
+ void pauseLoad(bool pause);
+
+ // From URLRequest::Delegate
+ virtual void OnReceivedRedirect(URLRequest*, const GURL&, bool* deferRedirect);
+ virtual void OnResponseStarted(URLRequest*);
+ virtual void OnReadCompleted(URLRequest*, int bytesRead);
+ virtual void OnAuthRequired(URLRequest*, net::AuthChallengeInfo*);
+ virtual void OnSSLCertificateError(URLRequest* request, int cert_error, net::X509Certificate* cert);
+
+ // Methods called during a request by the UI code (via WebUrlLoaderClient).
+ void setAuth(const string16& username, const string16& password);
+ void cancelAuth();
+ void followDeferredRedirect();
+ void proceedSslCertError();
+ void cancelSslCertError(int cert_error);
+
+ const std::string& getUrl() const;
+ const std::string& getUserAgent() const;
+
+ void setSync(bool sync) { m_isSync = sync; }
+private:
+ void startReading();
+ bool read(int* bytesRead);
+
+ friend class base::RefCountedThreadSafe<WebRequest>;
+ virtual ~WebRequest();
+ void handleDataURL(GURL);
+ void handleBrowserURL(GURL);
+ void handleInterceptedURL();
+ void finish(bool success);
+ void updateLoadFlags(int& loadFlags);
+
+ scoped_refptr<WebUrlLoaderClient> m_urlLoader;
+ OwnPtr<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;
+ int m_authRequestCount;
+ int m_cacheMode;
+ ScopedRunnableMethodFactory<WebRequest> m_runnableFactory;
+ bool m_wantToPause;
+ bool m_isPaused;
+ bool m_isSync;
+#ifdef LOG_REQUESTS
+ time_t m_startTime;
+#endif
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp b/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp
new file mode 100644
index 0000000..78c3501
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "WebRequestContext.h"
+
+#include "ChromiumIncludes.h"
+#include "ChromiumInit.h"
+#include "WebCache.h"
+#include "WebCookieJar.h"
+
+#include <wtf/text/CString.h>
+
+static std::string acceptLanguageStdString("");
+static WTF::String acceptLanguageWtfString("");
+static WTF::Mutex acceptLanguageMutex;
+
+static int numPrivateBrowsingInstances;
+
+extern void ANPSystemInterface_CleanupIncognito();
+
+using namespace WTF;
+
+namespace android {
+
+WebRequestContext::WebRequestContext(bool isPrivateBrowsing)
+ : m_isPrivateBrowsing(isPrivateBrowsing)
+{
+ // Initialize chromium logging, needs to be done before any chromium code is called.
+ initChromium();
+
+ if (m_isPrivateBrowsing) {
+ // Delete the old files if this is the first private browsing instance
+ // They are probably leftovers from a power cycle
+ // We do not need to clear the cache as it is in memory only for private browsing
+ if (!numPrivateBrowsingInstances)
+ WebCookieJar::cleanup(true);
+ numPrivateBrowsingInstances++;
+ }
+
+ WebCache* cache = WebCache::get(m_isPrivateBrowsing);
+ host_resolver_ = cache->hostResolver();
+ http_transaction_factory_ = cache->cache();
+
+ WebCookieJar* cookieJar = WebCookieJar::get(m_isPrivateBrowsing);
+ cookie_store_ = cookieJar->cookieStore();
+ cookie_policy_ = cookieJar;
+
+ // Also hardcoded in FrameLoader.java
+ accept_charset_ = "utf-8, iso-8859-1, utf-16, *;q=0.7";
+}
+
+WebRequestContext::~WebRequestContext()
+{
+ if (m_isPrivateBrowsing) {
+ numPrivateBrowsingInstances--;
+
+ // This is the last private browsing context, delete the cookies and cache
+ if (!numPrivateBrowsingInstances) {
+ WebCookieJar::cleanup(true);
+ WebCache::cleanup(true);
+ ANPSystemInterface_CleanupIncognito();
+ }
+ }
+}
+
+void WebRequestContext::setUserAgent(const String& string)
+{
+ MutexLocker lock(m_userAgentMutex);
+ m_userAgent = string.utf8().data();
+}
+
+void WebRequestContext::setCacheMode(int mode)
+{
+ m_cacheMode = mode;
+}
+
+int WebRequestContext::getCacheMode()
+{
+ return m_cacheMode;
+}
+
+const std::string& WebRequestContext::GetUserAgent(const GURL& url) const
+{
+ MutexLocker lock(m_userAgentMutex);
+ return m_userAgent;
+}
+
+void WebRequestContext::setAcceptLanguage(const String& string)
+{
+ MutexLocker lock(acceptLanguageMutex);
+ acceptLanguageStdString = string.utf8().data();
+ acceptLanguageWtfString = string;
+}
+
+const std::string& WebRequestContext::GetAcceptLanguage() const
+{
+ MutexLocker lock(acceptLanguageMutex);
+ return acceptLanguageStdString;
+}
+
+const String& WebRequestContext::acceptLanguage()
+{
+ MutexLocker lock(acceptLanguageMutex);
+ return acceptLanguageWtfString;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebRequestContext.h b/Source/WebKit/android/WebCoreSupport/WebRequestContext.h
new file mode 100644
index 0000000..51f0514
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebRequestContext.h
@@ -0,0 +1,64 @@
+/*
+ * 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 WebRequestContext_h
+#define WebRequestContext_h
+
+#include "ChromiumIncludes.h"
+#include "PlatformString.h"
+
+#include <wtf/ThreadingPrimitives.h>
+
+namespace android {
+
+// This class is generally not threadsafe.
+class WebRequestContext : public URLRequestContext {
+public:
+ // URLRequestContext overrides.
+ virtual const std::string& GetUserAgent(const GURL&) const;
+ virtual const std::string& GetAcceptLanguage() const;
+
+ WebRequestContext(bool isPrivateBrowsing);
+
+ // These methods are threadsafe.
+ void setUserAgent(const WTF::String&);
+ void setCacheMode(int);
+ int getCacheMode();
+ static void setAcceptLanguage(const WTF::String&);
+ static const WTF::String& acceptLanguage();
+
+private:
+ WebRequestContext();
+ ~WebRequestContext();
+
+ std::string m_userAgent;
+ int m_cacheMode;
+ mutable WTF::Mutex m_userAgentMutex;
+ bool m_isPrivateBrowsing;
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp
new file mode 100644
index 0000000..9b70fce
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "WebResourceRequest.h"
+
+#include "ResourceRequest.h"
+
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+
+namespace android {
+
+WebResourceRequest::WebResourceRequest(const WebCore::ResourceRequest& resourceRequest)
+{
+ // Set the load flags based on the WebCore request.
+ m_loadFlags = net::LOAD_NORMAL;
+ switch (resourceRequest.cachePolicy()) {
+ case ReloadIgnoringCacheData:
+ m_loadFlags |= net::LOAD_VALIDATE_CACHE;
+ break;
+ case ReturnCacheDataElseLoad:
+ m_loadFlags |= net::LOAD_PREFERRING_CACHE;
+ break;
+ case ReturnCacheDataDontLoad:
+ m_loadFlags |= net::LOAD_ONLY_FROM_CACHE;
+ break;
+ case UseProtocolCachePolicy:
+ break;
+ }
+
+ // TODO: We should consider setting these flags and net::LOAD_DO_NOT_SEND_AUTH_DATA
+ // when FrameLoaderClient::shouldUseCredentialStorage() is false. However,
+ // the required WebKit logic is not yet in place. See Chromium's
+ // FrameLoaderClientImpl::shouldUseCredentialStorage().
+ if (!resourceRequest.allowCookies()) {
+ m_loadFlags |= net::LOAD_DO_NOT_SAVE_COOKIES;
+ m_loadFlags |= net::LOAD_DO_NOT_SEND_COOKIES;
+ }
+
+
+ // Set the request headers
+ const HTTPHeaderMap& map = resourceRequest.httpHeaderFields();
+ for (HTTPHeaderMap::const_iterator it = map.begin(); it != map.end(); ++it) {
+ const std::string& nameUtf8 = it->first.string().utf8().data();
+ const std::string& valueUtf8 = it->second.utf8().data();
+
+ // Skip over referrer headers found in the header map because we already
+ // pulled it out as a separate parameter. We likewise prune the UA since
+ // that will be added back by the network layer.
+ if (LowerCaseEqualsASCII(nameUtf8, "referer") || LowerCaseEqualsASCII(nameUtf8, "user-agent"))
+ continue;
+
+ // Skip over "Cache-Control: max-age=0" header if the corresponding
+ // load flag is already specified. FrameLoader sets both the flag and
+ // the extra header -- the extra header is redundant since our network
+ // implementation will add the necessary headers based on load flags.
+ // See http://code.google.com/p/chromium/issues/detail?id=3434.
+ if ((m_loadFlags & net::LOAD_VALIDATE_CACHE) &&
+ LowerCaseEqualsASCII(nameUtf8, "cache-control") && LowerCaseEqualsASCII(valueUtf8, "max-age=0"))
+ continue;
+
+ m_requestHeaders.SetHeader(nameUtf8, valueUtf8);
+ }
+
+ m_method = resourceRequest.httpMethod().utf8().data();
+ m_referrer = resourceRequest.httpReferrer().utf8().data();
+ m_userAgent = resourceRequest.httpUserAgent().utf8().data();
+
+ m_url = resourceRequest.url().string().utf8().data();
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebResourceRequest.h b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.h
new file mode 100644
index 0000000..38f37b5
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.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 WebResourceRequest_h
+#define WebResourceRequest_h
+
+#include "ChromiumIncludes.h"
+
+#include <string>
+
+namespace WebCore {
+class ResourceRequest;
+}
+
+namespace android {
+
+class WebResourceRequest {
+
+public:
+ WebResourceRequest(const WebCore::ResourceRequest&);
+
+ const std::string& method() const
+ {
+ return m_method;
+ }
+
+ const std::string& referrer() const
+ {
+ return m_referrer;
+ }
+
+ const std::string& userAgent() const
+ {
+ return m_userAgent;
+ }
+
+ const net::HttpRequestHeaders& requestHeaders() const
+ {
+ return m_requestHeaders;
+ }
+
+ const std::string& url() const
+ {
+ return m_url;
+ }
+
+ int loadFlags() const
+ {
+ return m_loadFlags;
+ }
+
+private:
+ std::string m_method;
+ std::string m_referrer;
+ std::string m_userAgent;
+ net::HttpRequestHeaders m_requestHeaders;
+ std::string m_url;
+ int m_loadFlags;
+};
+
+} // namespace android
+
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebResponse.cpp b/Source/WebKit/android/WebCoreSupport/WebResponse.cpp
new file mode 100644
index 0000000..4d297d7
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebResponse.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "WebResponse.h"
+
+#include "MIMETypeRegistry.h"
+#include "PlatformString.h"
+#include "ResourceResponse.h"
+#include "ResourceError.h"
+
+#include <wtf/text/CString.h>
+
+using namespace std;
+
+namespace android {
+
+WebResponse::WebResponse(URLRequest* request)
+ : m_httpStatusCode(0)
+{
+ // The misleadingly-named os_error() is actually a net::Error enum constant.
+ m_error = net::Error(request->status().os_error());
+
+ m_url = request->url().spec();
+ m_host = request->url().HostNoBrackets();
+ request->GetMimeType(&m_mime);
+
+ request->GetCharset(&m_encoding);
+ m_expectedSize = request->GetExpectedContentSize();
+
+ m_sslInfo = request->ssl_info();
+
+ net::HttpResponseHeaders* responseHeaders = request->response_headers();
+ if (!responseHeaders)
+ return;
+
+ m_httpStatusCode = responseHeaders->response_code();
+ m_httpStatusText = responseHeaders->GetStatusText();
+
+ string value;
+ string name;
+ void* iter = 0;
+ while (responseHeaders->EnumerateHeaderLines(&iter, &name, &value))
+ m_headerFields[name] = value;
+}
+
+WebResponse::WebResponse(const string &url, const string &mimeType, long long expectedSize, const string &encoding, int httpStatusCode)
+ : m_error(net::OK)
+ , m_encoding(encoding)
+ , m_httpStatusCode(httpStatusCode)
+ , m_expectedSize(expectedSize)
+ , m_mime(mimeType)
+ , m_url(url)
+{
+}
+
+WebCore::ResourceResponse WebResponse::createResourceResponse()
+{
+ WebCore::ResourceResponse resourceResponse(createKurl(), getMimeType().c_str(), m_expectedSize, m_encoding.c_str(), "");
+ resourceResponse.setHTTPStatusCode(m_httpStatusCode);
+ resourceResponse.setHTTPStatusText(m_httpStatusText.c_str());
+
+ map<string, string>::const_iterator it;
+ for (it = m_headerFields.begin(); it != m_headerFields.end(); ++it)
+ resourceResponse.setHTTPHeaderField(it->first.c_str(), it->second.c_str());
+
+ return resourceResponse;
+}
+
+WebCore::ResourceError WebResponse::createResourceError()
+{
+ WebCore::ResourceError error(m_host.c_str(), ToWebViewClientError(m_error), m_url.c_str(), WTF::String());
+ return error;
+}
+
+
+WebCore::KURL WebResponse::createKurl()
+{
+ WebCore::KURL kurl(WebCore::ParsedURLString, m_url.c_str());
+ return kurl;
+}
+
+const string& WebResponse::getUrl() const
+{
+ return m_url;
+}
+
+void WebResponse::setUrl(const string& url)
+{
+ m_url = url;
+}
+
+// Calls WebCore APIs so should only be called from the WebCore thread.
+// TODO: can we return a WTF::String directly? Need to check all callsites.
+const string& WebResponse::getMimeType()
+{
+ if (!m_url.length())
+ return m_mime;
+
+ if (!m_mime.length() || !m_mime.compare("text/plain") || !m_mime.compare("application/octet-stream"))
+ m_mime = resolveMimeType(m_url, m_mime);
+
+ return m_mime;
+}
+
+const string WebResponse::resolveMimeType(const string& url, const string& old_mime)
+{
+ // Use "text/html" as a default (matching the behaviour of the Apache
+ // HTTP stack -- see guessMimeType() in LoadListener.java).
+ string mimeType = old_mime.length() ? old_mime : "text/html";
+ // Try to guess a better MIME type from the URL. We call
+ // getMIMETypeForExtension rather than getMIMETypeForPath because the
+ // latter defaults to "application/octet-stream" on failure.
+ WebCore::KURL kurl(WebCore::ParsedURLString, url.c_str());
+ WTF::String path = kurl.path();
+ size_t extensionPos = path.reverseFind('.');
+ if (extensionPos != WTF::notFound) {
+ // We found a file extension.
+ path.remove(0, extensionPos + 1);
+ // TODO: Should use content-disposition instead of url if it is there
+ WTF::String mime = WebCore::MIMETypeRegistry::getMIMETypeForExtension(path);
+ if (!mime.isEmpty()) {
+ // Great, we found a MIME type.
+ mimeType = std::string(mime.utf8().data(), mime.length());
+ }
+ }
+ return mimeType;
+}
+
+bool WebResponse::getHeader(const string& header, string* result) const
+{
+ map<string, string>::const_iterator iter = m_headerFields.find(header);
+ if (iter == m_headerFields.end())
+ return false;
+ if (result)
+ *result = iter->second;
+ return true;
+}
+
+long long WebResponse::getExpectedSize() const
+{
+ return m_expectedSize;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebResponse.h b/Source/WebKit/android/WebCoreSupport/WebResponse.h
new file mode 100644
index 0000000..88c8917
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebResponse.h
@@ -0,0 +1,91 @@
+/*
+ * 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 WebResponse_h
+#define WebResponse_h
+
+#include "ChromiumIncludes.h"
+#include "KURL.h"
+#include "WebViewClientError.h"
+
+#include <map>
+#include <string>
+
+namespace WebCore {
+class ResourceResponse;
+class ResourceError;
+}
+
+namespace android {
+
+class WebResponse {
+
+public:
+ WebResponse() {}
+ WebResponse(URLRequest*);
+ WebResponse(const std::string &url, const std::string &mimeType, long long expectedSize, const std::string &encoding, int httpStatusCode);
+
+ const std::string& getUrl() const;
+ void setUrl(const std::string&);
+
+ const std::string& getMimeType(); // Use only on WebCore thread.
+ bool getHeader(const std::string& header, std::string* result) const;
+ long long getExpectedSize() const;
+
+ const net::SSLInfo& getSslInfo() const { return m_sslInfo; }
+
+ // The create() methods create WebCore objects. They must only be called on the WebKit thread.
+ WebCore::KURL createKurl();
+ WebCore::ResourceResponse createResourceResponse();
+ WebCore::ResourceError createResourceError();
+
+ static const std::string resolveMimeType(const std::string& url, const std::string& old_mime);
+
+private:
+ net::Error m_error;
+ std::string m_encoding;
+ int m_httpStatusCode;
+ std::string m_host;
+ std::string m_httpStatusText;
+ long long m_expectedSize;
+ std::string m_mime;
+ std::string m_url;
+ net::SSLInfo m_sslInfo;
+
+ struct CaseInsensitiveLessThan {
+ bool operator()(const std::string& lhs, const std::string& rhs) const {
+ return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
+ }
+ };
+
+ // Header fields are case insensitive, so we use a case-insensitive map.
+ // See RFC 822, 3.4.7, "CASE INDEPENDENCE".
+ std::map<std::string, std::string, CaseInsensitiveLessThan> m_headerFields;
+
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp
new file mode 100644
index 0000000..0c90bc5
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp
@@ -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.
+ */
+
+#include "config.h"
+
+#include "WebUrlLoader.h"
+
+#include "FrameLoaderClientAndroid.h"
+#include "WebCoreFrameBridge.h"
+#include "WebUrlLoaderClient.h"
+
+namespace android {
+
+// on main thread
+WebUrlLoader::WebUrlLoader(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
+{
+ m_loaderClient = new WebUrlLoaderClient(webFrame, resourceHandle, resourceRequest);
+}
+
+// on main thread
+WebUrlLoader::~WebUrlLoader()
+{
+}
+
+PassRefPtr<WebUrlLoader> WebUrlLoader::start(FrameLoaderClient* client, WebCore::ResourceHandle* resourceHandle,
+ const WebCore::ResourceRequest& resourceRequest, bool isMainResource, bool isMainFrame, bool isSync, WebRequestContext* context)
+{
+ FrameLoaderClientAndroid* androidClient = static_cast<FrameLoaderClientAndroid*>(client);
+ WebFrame* webFrame = androidClient->webFrame();
+
+ if (webFrame->blockNetworkLoads() &&
+ (resourceRequest.url().protocolIs("http") ||
+ resourceRequest.url().protocolIs("https")))
+ return NULL;
+
+ webFrame->maybeSavePassword(androidClient->getFrame(), resourceRequest);
+
+ RefPtr<WebUrlLoader> loader = WebUrlLoader::create(webFrame, resourceHandle, resourceRequest);
+ loader->m_loaderClient->start(isMainResource, isMainFrame, isSync, context);
+
+ return loader.release();
+}
+
+PassRefPtr<WebUrlLoader> WebUrlLoader::create(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
+{
+ return adoptRef(new WebUrlLoader(webFrame, resourceHandle, resourceRequest));
+}
+
+// on main thread
+void WebUrlLoader::cancel()
+{
+ m_loaderClient->cancel();
+}
+
+void WebUrlLoader::downloadFile()
+{
+ m_loaderClient->downloadFile();
+}
+
+void WebUrlLoader::pauseLoad(bool pause)
+{
+ m_loaderClient->pauseLoad(pause);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h
new file mode 100644
index 0000000..dd88e73
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h
@@ -0,0 +1,57 @@
+/*
+ * 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 WebUrlLoader_h
+#define WebUrlLoader_h
+
+#include "ChromiumIncludes.h"
+#include "ResourceLoaderAndroid.h"
+
+using namespace WebCore;
+
+namespace android {
+class WebUrlLoaderClient;
+class WebFrame;
+class WebRequestContext;
+
+class WebUrlLoader : public ResourceLoaderAndroid {
+public:
+ virtual ~WebUrlLoader();
+ static PassRefPtr<WebUrlLoader> start(FrameLoaderClient* client, WebCore::ResourceHandle*, const WebCore::ResourceRequest&, bool isMainResource, bool isMainFrame, bool sync, WebRequestContext*);
+
+ virtual void cancel();
+ virtual void downloadFile();
+ virtual void pauseLoad(bool pause);
+
+private:
+ WebUrlLoader(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&);
+ static PassRefPtr<WebUrlLoader> create(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&);
+
+ scoped_refptr<WebUrlLoaderClient> m_loaderClient;
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
new file mode 100644
index 0000000..cf218e7
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "WebUrlLoaderClient"
+
+#include "config.h"
+#include "WebUrlLoaderClient.h"
+
+#include "ChromiumIncludes.h"
+#include "OwnPtr.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceResponse.h"
+#include "WebCoreFrameBridge.h"
+#include "WebRequest.h"
+#include "WebResourceRequest.h"
+
+#include <wtf/text/CString.h>
+
+namespace android {
+
+base::Thread* WebUrlLoaderClient::ioThread()
+{
+ static base::Thread* networkThread = 0;
+ static Lock networkThreadLock;
+
+ // Multiple threads appear to access the ioThread so we must ensure the
+ // critical section ordering.
+ AutoLock lock(networkThreadLock);
+
+ if (!networkThread)
+ networkThread = new base::Thread("network");
+
+ if (!networkThread)
+ return 0;
+
+ if (networkThread->IsRunning())
+ return networkThread;
+
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ if (!networkThread->StartWithOptions(options)) {
+ delete networkThread;
+ networkThread = 0;
+ }
+
+ return networkThread;
+}
+
+Lock* WebUrlLoaderClient::syncLock() {
+ static Lock s_syncLock;
+ return &s_syncLock;
+}
+
+ConditionVariable* WebUrlLoaderClient::syncCondition() {
+ static ConditionVariable s_syncCondition(syncLock());
+ return &s_syncCondition;
+}
+
+WebUrlLoaderClient::~WebUrlLoaderClient()
+{
+}
+
+bool WebUrlLoaderClient::isActive() const
+{
+ if (m_cancelling)
+ return false;
+ if (!m_resourceHandle)
+ return false;
+ if (!m_resourceHandle->client())
+ return false;
+ if (m_finished)
+ return false;
+
+ return true;
+}
+
+WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
+ : m_webFrame(webFrame)
+ , m_resourceHandle(resourceHandle)
+ , m_isMainResource(false)
+ , m_isMainFrame(false)
+ , m_isCertMimeType(false)
+ , m_cancelling(false)
+ , m_sync(false)
+ , m_finished(false)
+{
+ WebResourceRequest webResourceRequest(resourceRequest);
+ UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string());
+ if (intercept) {
+ m_request = new WebRequest(this, webResourceRequest, intercept);
+ return;
+ }
+
+ m_request = new WebRequest(this, webResourceRequest);
+
+ // Set uploads before start is called on the request
+ if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) {
+ Vector<FormDataElement>::iterator iter;
+ Vector<FormDataElement> elements = resourceRequest.httpBody()->elements();
+ for (iter = elements.begin(); iter != elements.end(); iter++) {
+ FormDataElement element = *iter;
+
+ switch (element.m_type) {
+ case FormDataElement::data:
+ if (!element.m_data.isEmpty()) {
+ // WebKit sometimes gives up empty data to append. These aren't
+ // necessary so we just optimize those out here.
+ base::Thread* thread = ioThread();
+ if (thread) {
+ Vector<char>* data = new Vector<char>(element.m_data);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data));
+ }
+ }
+ break;
+ case FormDataElement::encodedFile:
+ {
+ // Chromium check if it is a directory by checking
+ // element.m_fileLength, that doesn't work in Android
+ std::string filename = element.m_filename.utf8().data();
+ if (filename.size()) {
+ // Change from a url string to a filename
+ if (filename.find("file://") == 0) // Found at pos 0
+ filename.erase(0, 7);
+ base::Thread* thread = ioThread();
+ if (thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename));
+ }
+ }
+ break;
+#if ENABLE(BLOB)
+ case FormDataElement::encodedBlob:
+ LOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
+ break;
+#endif // ENABLE(BLOB)
+ default:
+ LOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
+ break;
+ }
+ }
+ }
+}
+
+bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
+{
+ base::Thread* thread = ioThread();
+ if (!thread) {
+ return false;
+ }
+
+ m_isMainResource = isMainResource;
+ m_isMainFrame = isMainFrame;
+ m_sync = sync;
+ if (m_sync) {
+ AutoLock autoLock(*syncLock());
+ m_request->setSync(sync);
+ m_request->setRequestContext(context);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
+
+ // Run callbacks until the queue is exhausted and m_finished is true.
+ // Sometimes, a sync load can wait forever and lock up the WebCore thread,
+ // here we use TimedWait() with multiple tries to avoid locking.
+ const int kMaxNumTimeout = 3;
+ const int kCallbackWaitingTime = 10;
+ int num_timeout = 0;
+ while(!m_finished) {
+ while (!m_queue.empty()) {
+ OwnPtr<Task> task(m_queue.front());
+ m_queue.pop_front();
+ task->Run();
+ }
+ if (m_finished) break;
+
+ syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime));
+ if (m_queue.empty()) {
+ 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) {
+ cancel();
+ m_resourceHandle = 0;
+ return false;
+ }
+ }
+ }
+
+ // This may be the last reference to us, so we may be deleted now.
+ // Don't access any more member variables after releasing this reference.
+ m_resourceHandle = 0;
+ } else {
+ // Asynchronous start.
+ // Important to set this before the thread starts so it has a reference and can't be deleted
+ // before the task starts running on the IO thread.
+ m_request->setRequestContext(context);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
+ }
+ return true;
+}
+
+namespace {
+// Check if the mime type is for certificate installation.
+// The items must be consistent with the sCertificateTypeMap
+// in frameworks/base/core/java/android/webkit/CertTool.java.
+bool isMimeTypeForCert(const std::string& mimeType)
+{
+ static std::hash_set<std::string> sCertificateTypeSet;
+ if (sCertificateTypeSet.empty()) {
+ sCertificateTypeSet.insert("application/x-x509-ca-cert");
+ sCertificateTypeSet.insert("application/x-x509-user-cert");
+ sCertificateTypeSet.insert("application/x-pkcs12");
+ }
+ return sCertificateTypeSet.find(mimeType) != sCertificateTypeSet.end();
+}
+}
+
+void WebUrlLoaderClient::downloadFile()
+{
+ if (m_response) {
+ std::string contentDisposition;
+ m_response->getHeader("content-disposition", &contentDisposition);
+ m_webFrame->downloadStart(m_response->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_response->getExpectedSize());
+
+ m_isCertMimeType = isMimeTypeForCert(m_response->getMimeType());
+ // Currently, only certificate mime type needs to receive the data.
+ // Other mime type, e.g. wav, will send the url to other application
+ // which will load the data by url.
+ if (!m_isCertMimeType)
+ cancel();
+ } else {
+ 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();
+ }
+}
+
+void WebUrlLoaderClient::cancel()
+{
+ if (!isActive())
+ return;
+
+ m_cancelling = true;
+
+ base::Thread* thread = ioThread();
+ if (thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel));
+}
+
+void WebUrlLoaderClient::pauseLoad(bool pause)
+{
+ if (!isActive())
+ return;
+
+ base::Thread* thread = ioThread();
+ if (thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::pauseLoad, pause));
+}
+
+void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password)
+{
+ if (!isActive())
+ return;
+
+ base::Thread* thread = ioThread();
+ if (!thread) {
+ return;
+ }
+ string16 username16 = ASCIIToUTF16(username);
+ string16 password16 = ASCIIToUTF16(password);
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16));
+}
+
+void WebUrlLoaderClient::cancelAuth()
+{
+ if (!isActive())
+ return;
+
+ base::Thread* thread = ioThread();
+ if (!thread) {
+ return;
+ }
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth));
+}
+
+void WebUrlLoaderClient::proceedSslCertError()
+{
+ base::Thread* thread = ioThread();
+ if (isActive() && thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError));
+ this->Release();
+}
+
+void WebUrlLoaderClient::cancelSslCertError(int cert_error)
+{
+ base::Thread* thread = ioThread();
+ if (isActive() && thread)
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error));
+ this->Release();
+}
+
+
+void WebUrlLoaderClient::finish()
+{
+ m_finished = true;
+ if (!m_sync) {
+ // This is the last reference to us, so we will be deleted now.
+ // We only release the reference here if start() was called asynchronously!
+ m_resourceHandle = 0;
+ }
+ m_request = 0;
+}
+
+namespace {
+// Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*.
+static void RunTask(void* v) {
+ OwnPtr<Task> task(static_cast<Task*>(v));
+ task->Run();
+}
+}
+
+// This is called from the IO thread, and dispatches the callback to the main thread.
+void WebUrlLoaderClient::maybeCallOnMainThread(Task* task)
+{
+ if (m_sync) {
+ AutoLock autoLock(*syncLock());
+ if (m_queue.empty()) {
+ syncCondition()->Broadcast();
+ }
+ m_queue.push_back(task);
+ } else {
+ // Let WebKit handle it.
+ callOnMainThread(RunTask, task);
+ }
+}
+
+// Response methods
+void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse)
+{
+ if (!isActive())
+ return;
+
+ m_response = webResponse;
+ m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse());
+
+ // Set the main page's certificate to WebView.
+ if (m_isMainResource && m_isMainFrame) {
+ const net::SSLInfo& ssl_info = m_response->getSslInfo();
+ if (ssl_info.is_valid()) {
+ std::vector<std::string> chain_bytes;
+ ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes);
+ m_webFrame->setCertificate(chain_bytes[0]);
+ }
+
+ // Look for X-Auto-Login on the main resource to log in the user.
+ std::string login;
+ if (m_response->getHeader("x-auto-login", &login))
+ m_webFrame->autoLogin(login);
+ }
+}
+
+void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)
+{
+ if (m_isMainResource && m_isCertMimeType) {
+ m_webFrame->didReceiveData(buf->data(), size);
+ }
+
+ if (!isActive() || !size)
+ return;
+
+ // didReceiveData will take a copy of the data
+ if (m_resourceHandle && m_resourceHandle->client())
+ m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size);
+}
+
+// For data url's
+void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str)
+{
+ if (!isActive() || !str->size())
+ return;
+
+ // didReceiveData will take a copy of the data
+ m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size());
+}
+
+// For special android files
+void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector)
+{
+ if (!isActive() || !vector->size())
+ return;
+
+ // didReceiveData will take a copy of the data
+ m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size());
+}
+
+void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse)
+{
+ if (isActive())
+ m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError());
+
+ // Always finish a request, if not it will leak
+ finish();
+}
+
+void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse)
+{
+ if (!isActive())
+ return;
+
+ KURL url = webResponse->createKurl();
+ OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url));
+ m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse());
+
+ // WebKit may have killed the request.
+ if (!isActive())
+ return;
+
+ // Like Chrome, we only follow the redirect if WebKit left the URL unmodified.
+ if (url == resourceRequest->url()) {
+ ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect));
+ } else {
+ cancel();
+ }
+}
+
+void WebUrlLoaderClient::didFinishLoading()
+{
+ if (isActive())
+ m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0);
+
+ if (m_isMainResource && m_isCertMimeType) {
+ m_webFrame->didFinishLoading();
+ }
+
+ // Always finish a request, if not it will leak
+ finish();
+}
+
+void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime)
+{
+ if (!isActive())
+ return;
+
+ std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port);
+ std::string realm = base::SysWideToUTF8(authChallengeInfo->realm);
+
+ m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime);
+}
+
+void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert)
+{
+ if (!isActive())
+ return;
+
+ std::vector<std::string> chain_bytes;
+ cert->GetChainDEREncodedBytes(&chain_bytes);
+ this->AddRef();
+ m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0]);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h
new file mode 100644
index 0000000..dc101db
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h
@@ -0,0 +1,130 @@
+/*
+ * 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 WebUrlLoaderClient_h
+#define WebUrlLoaderClient_h
+
+#include "ChromiumIncludes.h"
+#include "RefCounted.h"
+#include "WebResponse.h"
+#include "WebUrlLoader.h"
+
+#include <string>
+#include <deque>
+#include <string>
+#include <vector>
+
+class Lock;
+class ConditionVariable;
+
+namespace base {
+class Thread;
+}
+
+namespace net {
+class IOBuffer;
+class AuthChallengeInfo;
+}
+
+namespace android {
+
+class WebFrame;
+class WebRequest;
+class WebRequestContext;
+
+// This class handles communication between the IO thread where loading happens
+// and the webkit main thread.
+// TODO:
+// - Implement didFail
+// - Implement sync requests
+// - Implement downloadFile
+// - Implement pauseLoad
+class WebUrlLoaderClient : public base::RefCountedThreadSafe<WebUrlLoaderClient> {
+public:
+ WebUrlLoaderClient(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&);
+
+ // Called from WebCore, will be forwarded to the IO thread
+ bool start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext*);
+ void cancel();
+ void downloadFile();
+ void pauseLoad(bool pause);
+ void setAuth(const std::string& username, const std::string& password);
+ void cancelAuth();
+ void proceedSslCertError();
+ void cancelSslCertError(int cert_error);
+
+ typedef void CallbackFunction(void*);
+
+ // This is called from the IO thread, and dispatches the callback to the main thread.
+ // (For asynchronous calls, we just delegate to WebKit's callOnMainThread.)
+ void maybeCallOnMainThread(Task* task);
+
+ // Called by WebRequest (using maybeCallOnMainThread), should be forwarded to WebCore.
+ void didReceiveResponse(PassOwnPtr<WebResponse>);
+ void didReceiveData(scoped_refptr<net::IOBuffer>, int size);
+ void didReceiveDataUrl(PassOwnPtr<std::string>);
+ void didReceiveAndroidFileData(PassOwnPtr<std::vector<char> >);
+ void didFinishLoading();
+ void didFail(PassOwnPtr<WebResponse>);
+ void willSendRequest(PassOwnPtr<WebResponse>);
+ void authRequired(scoped_refptr<net::AuthChallengeInfo>, bool firstTime);
+ void reportSslCertError(int cert_error, net::X509Certificate* cert);
+
+ // Handle to the chrome IO thread
+ static base::Thread* ioThread();
+
+private:
+ friend class base::RefCountedThreadSafe<WebUrlLoaderClient>;
+ virtual ~WebUrlLoaderClient();
+
+ void finish();
+
+ WebFrame* m_webFrame;
+ RefPtr<WebCore::ResourceHandle> m_resourceHandle;
+ bool m_isMainResource;
+ bool m_isMainFrame;
+ bool m_isCertMimeType;
+ bool m_cancelling;
+ bool m_sync;
+ volatile bool m_finished;
+
+ scoped_refptr<WebRequest> m_request;
+ OwnPtr<WebResponse> m_response; // NULL until didReceiveResponse is called.
+
+ // Check if a request is active
+ bool isActive() const;
+
+ // Mutex and condition variable used for synchronous requests.
+ // Note that these are static. This works because there's only one main thread.
+ static Lock* syncLock();
+ static ConditionVariable* syncCondition();
+
+ // Queue of callbacks to be executed by the main thread. Must only be accessed inside mutex.
+ std::deque<Task*> m_queue;
+};
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp
new file mode 100644
index 0000000..59542da
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "WebViewClientError.h"
+
+using namespace net;
+
+namespace android {
+
+WebViewClientError ToWebViewClientError(net::Error error) {
+ // Note: many net::Error constants don't have an obvious mapping.
+ // These will be handled by the default case, ERROR_UNKNOWN.
+ switch(error) {
+ case ERR_UNSUPPORTED_AUTH_SCHEME:
+ return ERROR_UNSUPPORTED_AUTH_SCHEME;
+
+ case ERR_INVALID_AUTH_CREDENTIALS:
+ case ERR_MISSING_AUTH_CREDENTIALS:
+ case ERR_MISCONFIGURED_AUTH_ENVIRONMENT:
+ return ERROR_AUTHENTICATION;
+
+ case ERR_TOO_MANY_REDIRECTS:
+ return ERROR_REDIRECT_LOOP;
+
+ case ERR_UPLOAD_FILE_CHANGED:
+ return ERROR_FILE_NOT_FOUND;
+
+ case ERR_INVALID_URL:
+ return ERROR_BAD_URL;
+
+ case ERR_DISALLOWED_URL_SCHEME:
+ case ERR_UNKNOWN_URL_SCHEME:
+ return ERROR_UNSUPPORTED_SCHEME;
+
+ case ERR_IO_PENDING:
+ case ERR_NETWORK_IO_SUSPENDED:
+ return ERROR_IO;
+
+ case ERR_CONNECTION_TIMED_OUT:
+ case ERR_TIMED_OUT:
+ return ERROR_TIMEOUT;
+
+ case ERR_FILE_TOO_BIG:
+ return ERROR_FILE;
+
+ case ERR_HOST_RESOLVER_QUEUE_TOO_LARGE:
+ case ERR_INSUFFICIENT_RESOURCES:
+ case ERR_OUT_OF_MEMORY:
+ return ERROR_TOO_MANY_REQUESTS;
+
+ case ERR_CONNECTION_CLOSED:
+ case ERR_CONNECTION_RESET:
+ case ERR_CONNECTION_REFUSED:
+ case ERR_CONNECTION_ABORTED:
+ case ERR_CONNECTION_FAILED:
+ case ERR_SOCKET_NOT_CONNECTED:
+ return ERROR_CONNECT;
+
+ case ERR_ADDRESS_INVALID:
+ case ERR_ADDRESS_UNREACHABLE:
+ case ERR_NAME_NOT_RESOLVED:
+ case ERR_NAME_RESOLUTION_FAILED:
+ return ERROR_HOST_LOOKUP;
+
+ case ERR_SSL_PROTOCOL_ERROR:
+ case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
+ case ERR_TUNNEL_CONNECTION_FAILED:
+ case ERR_NO_SSL_VERSIONS_ENABLED:
+ case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
+ case ERR_SSL_RENEGOTIATION_REQUESTED:
+ case ERR_CERT_ERROR_IN_SSL_RENEGOTIATION:
+ case ERR_BAD_SSL_CLIENT_AUTH_CERT:
+ case ERR_SSL_NO_RENEGOTIATION:
+ case ERR_SSL_DECOMPRESSION_FAILURE_ALERT:
+ case ERR_SSL_BAD_RECORD_MAC_ALERT:
+ case ERR_SSL_UNSAFE_NEGOTIATION:
+ case ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY:
+ case ERR_SSL_SNAP_START_NPN_MISPREDICTION:
+ case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED:
+ case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY:
+ return ERROR_FAILED_SSL_HANDSHAKE;
+
+ case ERR_PROXY_AUTH_UNSUPPORTED:
+ case ERR_PROXY_AUTH_REQUESTED:
+ case ERR_PROXY_CONNECTION_FAILED:
+ case ERR_UNEXPECTED_PROXY_AUTH:
+ return ERROR_PROXY_AUTHENTICATION;
+
+ /* The certificate errors are handled by their own dialog
+ * and don't need to be reported to the framework again.
+ */
+ case ERR_CERT_COMMON_NAME_INVALID:
+ case ERR_CERT_DATE_INVALID:
+ case ERR_CERT_AUTHORITY_INVALID:
+ case ERR_CERT_CONTAINS_ERRORS:
+ case ERR_CERT_NO_REVOCATION_MECHANISM:
+ case ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
+ case ERR_CERT_REVOKED:
+ case ERR_CERT_INVALID:
+ case ERR_CERT_WEAK_SIGNATURE_ALGORITHM:
+ case ERR_CERT_NOT_IN_DNS:
+ return ERROR_OK;
+
+ default:
+ return ERROR_UNKNOWN;
+ }
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/WebViewClientError.h b/Source/WebKit/android/WebCoreSupport/WebViewClientError.h
new file mode 100644
index 0000000..d274dc7
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/WebViewClientError.h
@@ -0,0 +1,74 @@
+/*
+ * 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 WebViewClientError_h
+#define WebViewClientError_h
+
+#include "ChromiumIncludes.h"
+
+namespace android {
+
+// This enum must be kept in sync with WebViewClient.java
+enum WebViewClientError {
+ /** Success */
+ ERROR_OK = 0,
+ /** Generic error */
+ ERROR_UNKNOWN = -1,
+ /** Server or proxy hostname lookup failed */
+ ERROR_HOST_LOOKUP = -2,
+ /** Unsupported authentication scheme (not basic or digest) */
+ ERROR_UNSUPPORTED_AUTH_SCHEME = -3,
+ /** User authentication failed on server */
+ ERROR_AUTHENTICATION = -4,
+ /** User authentication failed on proxy */
+ ERROR_PROXY_AUTHENTICATION = -5,
+ /** Failed to connect to the server */
+ ERROR_CONNECT = -6,
+ /** Failed to read or write to the server */
+ ERROR_IO = -7,
+ /** Connection timed out */
+ ERROR_TIMEOUT = -8,
+ /** Too many redirects */
+ ERROR_REDIRECT_LOOP = -9,
+ /** Unsupported URI scheme */
+ ERROR_UNSUPPORTED_SCHEME = -10,
+ /** Failed to perform SSL handshake */
+ ERROR_FAILED_SSL_HANDSHAKE = -11,
+ /** Malformed URL */
+ ERROR_BAD_URL = -12,
+ /** Generic file error */
+ ERROR_FILE = -13,
+ /** File not found */
+ ERROR_FILE_NOT_FOUND = -14,
+ /** Too many requests during this load */
+ ERROR_TOO_MANY_REQUESTS = -15,
+};
+
+// Get the closest WebViewClient match to the given Chrome error code.
+WebViewClientError ToWebViewClientError(net::Error);
+
+} // namespace android
+
+#endif // WebViewClientError_h
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp
new file mode 100644
index 0000000..5bc4c92
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "AutoFillHostAndroid.h"
+
+#include "autofill/WebAutoFill.h"
+
+namespace android {
+
+AutoFillHostAndroid::AutoFillHostAndroid(WebAutoFill* autoFill)
+ : mAutoFill(autoFill)
+{
+}
+
+void AutoFillHostAndroid::AutoFillSuggestionsReturned(const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<string16>& icons, const std::vector<int>& uniqueIds)
+{
+ // TODO: what do we do with icons?
+ if (mAutoFill)
+ mAutoFill->querySuccessful(names[0], labels[0], uniqueIds[0]);
+}
+
+void AutoFillHostAndroid::AutoFillFormDataFilled(int queryId, const webkit_glue::FormData& form)
+{
+ if (mAutoFill)
+ mAutoFill->fillFormInPage(queryId, form);
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h
new file mode 100644
index 0000000..9677b46
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AutoFillHostAndroid_h
+#define AutoFillHostAndroid_h
+
+#include "ChromiumIncludes.h"
+
+#include <vector>
+
+namespace webkit_glue {
+class FormData;
+}
+
+namespace android {
+class WebAutoFill;
+
+// This class receives the callbacks from the AutoFillManager in the Chromium code.
+class AutoFillHostAndroid : public AutoFillHost {
+public:
+ AutoFillHostAndroid(WebAutoFill* autoFill);
+ virtual ~AutoFillHostAndroid() { }
+
+ virtual void AutoFillSuggestionsReturned(const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<string16>& icons, const std::vector<int>& uniqueIds);
+ virtual void AutoFillFormDataFilled(int queryId, const webkit_glue::FormData&);
+
+private:
+ WebAutoFill* mAutoFill;
+};
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp
new file mode 100644
index 0000000..6af0875
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2010 The Chromium Authors. All rights reserved.
+ * 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 "FormFieldAndroid.h"
+
+#include "ChromiumIncludes.h"
+#include "Element.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "StringUtils.h"
+#include <wtf/Vector.h>
+
+using WebCore::Element;
+using WebCore::HTMLFormControlElement;
+using WebCore::HTMLInputElement;
+using WebCore::HTMLOptionElement;
+using WebCore::HTMLSelectElement;
+
+using namespace WebCore::HTMLNames;
+
+// TODO: This file is taken from chromium/webkit/glue/form_field.cc and
+// customised to use WebCore types rather than WebKit API types. It would
+// be nice and would ease future merge pain if the two could be combined.
+
+namespace webkit_glue {
+
+FormField::FormField()
+ : max_length_(0),
+ is_autofilled_(false) {
+}
+
+// TODO: This constructor should probably be deprecated and the
+// functionality moved to FormManager.
+FormField::FormField(const HTMLFormControlElement& element)
+ : max_length_(0),
+ is_autofilled_(false) {
+ name_ = nameForAutoFill(element);
+
+ // TODO: Extract the field label. For now we just use the field
+ // name.
+ label_ = name_;
+
+ form_control_type_ = formControlType(element);
+ if (form_control_type_ == kText) {
+ const HTMLInputElement& input_element = static_cast<const HTMLInputElement&>(element);
+ value_ = WTFStringToString16(input_element.value());
+ max_length_ = input_element.size();
+ is_autofilled_ = input_element.isAutofilled();
+ } else if (form_control_type_ == kSelectOne) {
+ const HTMLSelectElement& const_select_element = static_cast<const HTMLSelectElement&>(element);
+ HTMLSelectElement& select_element = const_cast<HTMLSelectElement&>(const_select_element);
+ value_ = WTFStringToString16(select_element.value());
+
+ // For select-one elements copy option strings.
+ WTF::Vector<Element*> list_items = select_element.listItems();
+ option_strings_.reserve(list_items.size());
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (list_items[i]->hasTagName(optionTag))
+ option_strings_.push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()));
+ }
+ }
+
+ TrimWhitespace(value_, TRIM_LEADING, &value_);
+}
+
+FormField::FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int max_length, bool is_autofilled)
+ : label_(label),
+ name_(name),
+ value_(value),
+ form_control_type_(form_control_type),
+ max_length_(max_length),
+ is_autofilled_(is_autofilled) {
+}
+
+FormField::~FormField() {
+}
+
+bool FormField::operator==(const FormField& field) const {
+ // A FormField stores a value, but the value is not part of the identity of
+ // the field, so we don't want to compare the values.
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ form_control_type_ == field.form_control_type_ &&
+ max_length_ == field.max_length_);
+}
+
+bool FormField::operator!=(const FormField& field) const {
+ return !operator==(field);
+}
+
+bool FormField::StrictlyEqualsHack(const FormField& field) const {
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ value_ == field.value_ &&
+ form_control_type_ == field.form_control_type_ &&
+ max_length_ == field.max_length_);
+}
+
+std::ostream& operator<<(std::ostream& os, const FormField& field) {
+ return os
+ << UTF16ToUTF8(field.label())
+ << " "
+ << UTF16ToUTF8(field.name())
+ << " "
+ << UTF16ToUTF8(field.value())
+ << " "
+ << UTF16ToUTF8(field.form_control_type())
+ << " "
+ << field.max_length();
+}
+
+} // namespace webkit_glue
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h
new file mode 100644
index 0000000..c5e3ecc
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2010 The Chromium Authors. All rights reserved.
+ * 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 FormFieldAndroid_h
+#define FormFieldAndroid_h
+
+#include <base/string16.h>
+#include <vector>
+
+// TODO: This file is taken from chromium/webkit/glue/form_field.h and
+// customised to use WebCore types rather than WebKit API types. It would
+// be nice and would ease future merge pain if the two could be combined.
+
+namespace WebCore {
+class HTMLFormControlElement;
+}
+
+namespace webkit_glue {
+
+// Stores information about a field in a form.
+class FormField {
+public:
+ FormField();
+ explicit FormField(const WebCore::HTMLFormControlElement& element);
+ FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int max_length, bool is_autofilled);
+ virtual ~FormField();
+
+ const string16& label() const { return label_; }
+ const string16& name() const { return name_; }
+ const string16& value() const { return value_; }
+ const string16& form_control_type() const { return form_control_type_; }
+ int max_length() const { return max_length_; }
+ bool is_autofilled() const { return is_autofilled_; }
+
+ // Returns option string for elements for which they make sense (select-one,
+ // for example) for the rest of elements return an empty array.
+ const std::vector<string16>& option_strings() const { return option_strings_; }
+
+ void set_label(const string16& label) { label_ = label; }
+ void set_name(const string16& name) { name_ = name; }
+ void set_value(const string16& value) { value_ = value; }
+ void set_form_control_type(const string16& form_control_type) { form_control_type_ = form_control_type; }
+ void set_max_length(int max_length) { max_length_ = max_length; }
+ void set_autofilled(bool is_autofilled) { is_autofilled_ = is_autofilled; }
+ void set_option_strings(const std::vector<string16>& strings) { option_strings_ = strings; }
+
+ // Equality tests for identity which does not include |value_| or |size_|.
+ // Use |StrictlyEqualsHack| method to test all members.
+ // TODO: These operators need to be revised when we implement field
+ // ids.
+ bool operator==(const FormField& field) const;
+ bool operator!=(const FormField& field) const;
+
+ // Test equality of all data members.
+ // TODO: This will be removed when we implement field ids.
+ bool StrictlyEqualsHack(const FormField& field) const;
+
+private:
+ string16 label_;
+ string16 name_;
+ string16 value_;
+ string16 form_control_type_;
+ int max_length_;
+ bool is_autofilled_;
+ std::vector<string16> option_strings_;
+};
+
+// So we can compare FormFields with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const FormField& field);
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_FORM_FIELD_H_
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
new file mode 100644
index 0000000..9652794
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp
@@ -0,0 +1,871 @@
+/*
+ * Copyright (c) 2010 The Chromium Authors. All rights reserved.
+ * 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 "FormManagerAndroid.h"
+
+#include "DocumentLoader.h"
+#include "Element.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "HTMLCollection.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "Node.h"
+#include "NodeList.h"
+#include "HTMLCollection.h"
+#include "FormAssociatedElement.h"
+#include "FormFieldAndroid.h"
+#include "QualifiedName.h"
+#include "StringUtils.h"
+
+// TODO: This file is taken from chromium/chrome/renderer/form_manager.cc and
+// customised to use WebCore types rather than WebKit API types. It would be
+// nice and would ease future merge pain if the two could be combined.
+
+using webkit_glue::FormData;
+using webkit_glue::FormField;
+using WebCore::Element;
+using WebCore::FormAssociatedElement;
+using WebCore::HTMLCollection;
+using WebCore::HTMLElement;
+using WebCore::HTMLFormControlElement;
+using WebCore::HTMLFormElement;
+using WebCore::HTMLInputElement;
+using WebCore::HTMLLabelElement;
+using WebCore::HTMLOptionElement;
+using WebCore::HTMLSelectElement;
+using WebCore::Node;
+using WebCore::NodeList;
+
+using namespace WebCore::HTMLNames;
+
+namespace {
+
+// The number of fields required by AutoFill. Ideally we could send the forms
+// to AutoFill no matter how many fields are in the forms; however, finding the
+// label for each field is a costly operation and we can't spare the cycles if
+// it's not necessary.
+// Note the on ANDROID we reduce this from Chromium's 3 as it allows us to
+// autofill simple name/email forms for example. This improves the mobile
+// device experience where form filling can be time consuming and frustrating.
+const size_t kRequiredAutoFillFields = 2;
+
+// The maximum length allowed for form data.
+const size_t kMaxDataLength = 1024;
+
+// This is a helper function for the FindChildText() function.
+// Returns the aggregated values of the descendants or siblings of |node| that
+// are non-empty text nodes. This is a faster alternative to |innerText()| for
+// performance critical operations. It does a full depth-first search so
+// can be used when the structure is not directly known. The text is
+// accumulated after the whitespace has been stropped. Search depth is limited
+// with the |depth| parameter.
+string16 FindChildTextInner(Node* node, int depth) {
+ string16 element_text;
+ if (!node || depth <= 0)
+ return element_text;
+
+ string16 node_text = WTFStringToString16(node->nodeValue());
+ TrimWhitespace(node_text, TRIM_ALL, &node_text);
+ if (!node_text.empty())
+ element_text = node_text;
+
+ string16 child_text = FindChildTextInner(node->firstChild(), depth-1);
+ if (!child_text.empty())
+ element_text = element_text + child_text;
+
+ string16 sibling_text = FindChildTextInner(node->nextSibling(), depth-1);
+ if (!sibling_text.empty())
+ element_text = element_text + sibling_text;
+
+ return element_text;
+}
+
+// Returns the node value of the first decendant of |element| that is a
+// non-empty text node. "Non-empty" in this case means non-empty after the
+// whitespace has been stripped. Search is limited to withing 10 siblings and/or
+// descendants.
+string16 FindChildText(Element* element) {
+ Node* child = element->firstChild();
+
+ const int kChildSearchDepth = 10;
+ return FindChildTextInner(child, kChildSearchDepth);
+}
+
+string16 InferLabelFromPrevious(const HTMLFormControlElement& element) {
+ string16 inferred_label;
+ Node* previous = element.previousSibling();
+ if (!previous)
+ return string16();
+
+ if (previous->isTextNode()) {
+ inferred_label = WTFStringToString16(previous->nodeValue());
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ }
+
+ // If we didn't find text, check for previous paragraph.
+ // Eg. <p>Some Text</p><input ...>
+ // Note the lack of whitespace between <p> and <input> elements.
+ if (inferred_label.empty() && previous->isElementNode()) {
+ Element* element = static_cast<Element*>(previous);
+ if (element->hasTagName(pTag))
+ inferred_label = FindChildText(element);
+ }
+
+ // If we didn't find paragraph, check for previous paragraph to this.
+ // Eg. <p>Some Text</p> <input ...>
+ // Note the whitespace between <p> and <input> elements.
+ if (inferred_label.empty()) {
+ Node* sibling = previous->previousSibling();
+ if (sibling && sibling->isElementNode()) {
+ Element* element = static_cast<Element*>(sibling);
+ if (element->hasTagName(pTag))
+ inferred_label = FindChildText(element);
+ }
+ }
+
+ // Look for text node prior to <img> tag.
+ // Eg. Some Text<img/><input ...>
+ if (inferred_label.empty()) {
+ while (inferred_label.empty() && previous) {
+ if (previous->isTextNode()) {
+ inferred_label = WTFStringToString16(previous->nodeValue());
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ } else if (previous->isElementNode()) {
+ Element* element = static_cast<Element*>(previous);
+ if (!element->hasTagName(imgTag))
+ break;
+ } else
+ break;
+ previous = previous->previousSibling();
+ }
+ }
+
+ // Look for label node prior to <input> tag.
+ // Eg. <label>Some Text</label><input ...>
+ if (inferred_label.empty()) {
+ while (inferred_label.empty() && previous) {
+ if (previous->isTextNode()) {
+ inferred_label = WTFStringToString16(previous->nodeValue());
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ } else if (previous->isElementNode()) {
+ Element* element = static_cast<Element*>(previous);
+ if (element->hasTagName(labelTag)) {
+ inferred_label = FindChildText(element);
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+
+ previous = previous->previousSibling();
+ }
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// surrounding table structure.
+// Eg. <tr><td>Some Text</td><td><input ...></td></tr>
+// Eg. <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
+string16 InferLabelFromTable(const HTMLFormControlElement& element) {
+ string16 inferred_label;
+ Node* parent = element.parentNode();
+ while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(tdTag))
+ parent = parent->parentNode();
+
+ // Check all previous siblings, skipping non-element nodes, until we find a
+ // non-empty text block.
+ Node* previous = parent;
+ while(previous) {
+ if (previous->isElementNode()) {
+ Element* e = static_cast<Element*>(previous);
+ if (e->hasTagName(tdTag)) {
+ inferred_label = FindChildText(e);
+ if (!inferred_label.empty())
+ break;
+ }
+ }
+ previous = previous->previousSibling();
+ }
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a surrounding div table.
+// Eg. <div>Some Text<span><input ...></span></div>
+string16 InferLabelFromDivTable(const HTMLFormControlElement& element) {
+ Node* parent = element.parentNode();
+ while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(divTag))
+ parent = parent->parentNode();
+
+ if (!parent || !parent->isElementNode())
+ return string16();
+
+ Element* e = static_cast<Element*>(parent);
+ if (!e || !e->hasTagName(divTag))
+ return string16();
+
+ return FindChildText(e);
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a surrounding definition list.
+// Eg. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
+// Eg. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
+string16 InferLabelFromDefinitionList(const HTMLFormControlElement& element) {
+ string16 inferred_label;
+ Node* parent = element.parentNode();
+ while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(ddTag))
+ parent = parent->parentNode();
+
+ if (parent && parent->isElementNode()) {
+ Element* element = static_cast<Element*>(parent);
+ if (element->hasTagName(ddTag)) {
+ Node* previous = parent->previousSibling();
+
+ // Skip by any intervening text nodes.
+ while (previous && previous->isTextNode())
+ previous = previous->previousSibling();
+
+ if (previous && previous->isElementNode()) {
+ element = static_cast<Element*>(previous);
+ if (element->hasTagName(dtTag))
+ inferred_label = FindChildText(element);
+ }
+ }
+ }
+ return inferred_label;
+}
+
+void GetOptionStringsFromElement(HTMLFormControlElement* element, std::vector<string16>* option_strings) {
+ DCHECK(element);
+ DCHECK(option_strings);
+ option_strings->clear();
+ if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+
+ // For select-one elements copy option strings.
+ WTF::Vector<Element*> list_items = select_element->listItems();
+ option_strings->reserve(list_items.size());
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (list_items[i]->hasTagName(optionTag))
+ option_strings->push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()));
+ }
+ }
+}
+
+} // namespace
+
+namespace android {
+
+struct FormManager::FormElement {
+ RefPtr<HTMLFormElement> form_element;
+ std::vector<RefPtr<HTMLFormControlElement> > control_elements;
+ std::vector<string16> control_values;
+};
+
+FormManager::FormManager() {
+}
+
+FormManager::~FormManager() {
+ Reset();
+}
+
+// static
+void FormManager::HTMLFormControlElementToFormField(HTMLFormControlElement* element, ExtractMask extract_mask, FormField* field) {
+ DCHECK(field);
+
+ // The label is not officially part of a HTMLFormControlElement; however, the
+ // labels for all form control elements are scraped from the DOM and set in
+ // WebFormElementToFormData.
+ field->set_name(nameForAutoFill(*element));
+ field->set_form_control_type(formControlType(*element));
+
+ if (extract_mask & EXTRACT_OPTIONS) {
+ std::vector<string16> option_strings;
+ GetOptionStringsFromElement(element, &option_strings);
+ field->set_option_strings(option_strings);
+ }
+
+ if (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ field->set_max_length(input_element->maxLength());
+ field->set_autofilled(input_element->isAutofilled());
+ }
+
+ if (!(extract_mask & EXTRACT_VALUE))
+ return;
+
+ // TODO: In WebKit, move value() and setValue() to
+ // WebFormControlElement.
+ string16 value;
+ if (formControlType(*element) == kText ||
+ formControlType(*element) == kHidden) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ value = WTFStringToString16(input_element->value());
+ } else if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ value = WTFStringToString16(select_element->value());
+
+ // Convert the |select_element| value to text if requested.
+ if (extract_mask & EXTRACT_OPTION_TEXT) {
+ Vector<Element*> list_items = select_element->listItems();
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (list_items[i]->hasTagName(optionTag) &&
+ WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()) == value) {
+ value = WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->text());
+ break;
+ }
+ }
+ }
+ }
+
+ // TODO: This is a temporary stop-gap measure designed to prevent
+ // a malicious site from DOS'ing the browser with extremely large profile
+ // data. The correct solution is to parse this data asynchronously.
+ // See http://crbug.com/49332.
+ if (value.size() > kMaxDataLength)
+ value = value.substr(0, kMaxDataLength);
+
+ field->set_value(value);
+}
+
+// static
+string16 FormManager::LabelForElement(const HTMLFormControlElement& element) {
+ // Don't scrape labels for hidden elements.
+ if (formControlType(element) == kHidden)
+ return string16();
+
+ RefPtr<NodeList> labels = element.document()->getElementsByTagName("label");
+ for (unsigned i = 0; i < labels->length(); ++i) {
+ Node* e = labels->item(i);
+ if (e->hasTagName(labelTag)) {
+ HTMLLabelElement* label = static_cast<HTMLLabelElement*>(e);
+ if (label->control() == &element)
+ return FindChildText(label);
+ }
+ }
+
+ // Infer the label from context if not found in label element.
+ return FormManager::InferLabelForElement(element);
+}
+
+// static
+bool FormManager::HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, ExtractMask extract_mask, FormData* form) {
+ DCHECK(form);
+
+ Frame* frame = element->document()->frame();
+ if (!frame)
+ return false;
+
+ if (requirements & REQUIRE_AUTOCOMPLETE && !element->autoComplete())
+ return false;
+
+ form->name = WTFStringToString16(element->name());
+ form->method = WTFStringToString16(element->method());
+ form->origin = GURL(WTFStringToString16(frame->loader()->documentLoader()->url().string()));
+ form->action = GURL(WTFStringToString16(frame->document()->completeURL(element->action())));
+ form->user_submitted = element->wasUserSubmitted();
+
+ // If the completed URL is not valid, just use the action we get from
+ // WebKit.
+ if (!form->action.is_valid())
+ form->action = GURL(WTFStringToString16(element->action()));
+
+ // A map from a FormField's name to the FormField itself.
+ std::map<string16, FormField*> name_map;
+
+ // The extracted FormFields. We use pointers so we can store them in
+ // |name_map|.
+ ScopedVector<FormField> form_fields;
+
+ WTF::Vector<WebCore::FormAssociatedElement*> control_elements = element->associatedElements();
+
+ // A vector of bools that indicate whether each field in the form meets the
+ // requirements and thus will be in the resulting |form|.
+ std::vector<bool> fields_extracted(control_elements.size(), false);
+
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ if (!control_elements[i]->isFormControlElement())
+ continue;
+
+ HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]);
+ if (!(control_element->hasTagName(inputTag) || control_element->hasTagName(selectTag)))
+ continue;
+
+ if (requirements & REQUIRE_AUTOCOMPLETE &&
+ formControlType(*control_element) == kText) {
+ const WebCore::HTMLInputElement* input_element = static_cast<const WebCore::HTMLInputElement*>(control_element);
+ if (!input_element->autoComplete())
+ continue;
+ }
+
+ if (requirements & REQUIRE_ENABLED && !control_element->isEnabledFormControl())
+ continue;
+
+ // Create a new FormField, fill it out and map it to the field's name.
+ FormField* field = new FormField;
+ HTMLFormControlElementToFormField(control_element, extract_mask, field);
+ form_fields.push_back(field);
+ // TODO: A label element is mapped to a form control element's id.
+ // field->name() will contain the id only if the name does not exist. Add
+ // an id() method to HTMLFormControlElement and use that here.
+ name_map[field->name()] = field;
+ fields_extracted[i] = true;
+ }
+
+ // Don't extract field labels if we have no fields.
+ if (form_fields.empty())
+ return false;
+
+ // Loop through the label elements inside the form element. For each label
+ // element, get the corresponding form control element, use the form control
+ // element's name as a key into the <name, FormField> map to find the
+ // previously created FormField and set the FormField's label to the
+ // label.firstChild().nodeValue() of the label element.
+ RefPtr<WebCore::NodeList> labels = element->getElementsByTagName("label");
+ for (unsigned i = 0; i < labels->length(); ++i) {
+ HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(labels->item(i));
+ HTMLFormControlElement* field_element = label->control();
+ if (!field_element || field_element->type() == "hidden")
+ continue;
+
+ std::map<string16, FormField*>::iterator iter =
+ name_map.find(nameForAutoFill(*field_element));
+ if (iter != name_map.end())
+ iter->second->set_label(FindChildText(label));
+ }
+
+ // Loop through the form control elements, extracting the label text from the
+ // DOM. We use the |fields_extracted| vector to make sure we assign the
+ // extracted label to the correct field, as it's possible |form_fields| will
+ // not contain all of the elements in |control_elements|.
+ for (size_t i = 0, field_idx = 0; i < control_elements.size() && field_idx < form_fields.size(); ++i) {
+ // This field didn't meet the requirements, so don't try to find a label for
+ // it.
+ if (!fields_extracted[i])
+ continue;
+
+ if (!control_elements[i]->isFormControlElement())
+ continue;
+
+ const HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]);
+ if (form_fields[field_idx]->label().empty())
+ form_fields[field_idx]->set_label(FormManager::InferLabelForElement(*control_element));
+
+ ++field_idx;
+
+ }
+ // Copy the created FormFields into the resulting FormData object.
+ for (ScopedVector<FormField>::const_iterator iter = form_fields.begin(); iter != form_fields.end(); ++iter)
+ form->fields.push_back(**iter);
+
+ return true;
+}
+
+void FormManager::ExtractForms(Frame* frame) {
+
+ ResetFrame(frame);
+
+ WTF::PassRefPtr<HTMLCollection> web_forms = frame->document()->forms();
+
+ for (size_t i = 0; i < web_forms->length(); ++i) {
+ FormElement* form_element = new FormElement;
+ HTMLFormElement* html_form_element = static_cast<HTMLFormElement*>(web_forms->item(i));
+ form_element->form_element = html_form_element;
+
+ WTF::Vector<FormAssociatedElement*> control_elements = html_form_element->associatedElements();
+ for (size_t j = 0; j < control_elements.size(); ++j) {
+ if (!control_elements[j]->isFormControlElement())
+ continue;
+
+ HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(control_elements[j]);
+ form_element->control_elements.push_back(element);
+
+ // Save original values of "select-one" inputs so we can restore them
+ // when |ClearFormWithNode()| is invoked.
+ if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ string16 value = WTFStringToString16(select_element->value());
+ form_element->control_values.push_back(value);
+ } else
+ form_element->control_values.push_back(string16());
+ }
+
+ form_elements_.push_back(form_element);
+ }
+}
+
+void FormManager::GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<FormData>* forms) {
+ DCHECK(frame);
+ DCHECK(forms);
+
+ for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
+ FormElement* form_element = *form_iter;
+
+ if (form_element->form_element->document()->frame() != frame)
+ continue;
+
+ // We need at least |kRequiredAutoFillFields| fields before appending this
+ // form to |forms|.
+ if (form_element->control_elements.size() < kRequiredAutoFillFields)
+ continue;
+
+ if (requirements & REQUIRE_AUTOCOMPLETE && !form_element->form_element->autoComplete())
+ continue;
+
+ FormData form;
+ HTMLFormElementToFormData(form_element->form_element.get(), requirements, EXTRACT_VALUE, &form);
+ if (form.fields.size() >= kRequiredAutoFillFields)
+ forms->push_back(form);
+ }
+}
+
+bool FormManager::FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, FormData* form) {
+ DCHECK(form);
+
+ const Frame* frame = element->document()->frame();
+ if (!frame)
+ return false;
+
+ for (FormElementList::const_iterator iter = form_elements_.begin(); iter != form_elements_.end(); ++iter) {
+ const FormElement* form_element = *iter;
+
+ if (form_element->form_element->document()->frame() != frame)
+ continue;
+
+ for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) {
+ HTMLFormControlElement* candidate = iter->get();
+ if (nameForAutoFill(*candidate) == nameForAutoFill(*element)) {
+ ExtractMask extract_mask = static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
+ return HTMLFormElementToFormData(form_element->form_element.get(), requirements, extract_mask, form);
+ }
+ }
+ }
+ return false;
+}
+
+bool FormManager::FillForm(const FormData& form, Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElement(form, &form_element))
+ return false;
+
+ RequirementsMask requirements = static_cast<RequirementsMask>(REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY);
+ ForEachMatchingFormField(form_element, node, requirements, form, NewCallback(this, &FormManager::FillFormField));
+
+ return true;
+}
+
+bool FormManager::PreviewForm(const FormData& form, Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElement(form, &form_element))
+ return false;
+
+ RequirementsMask requirements = static_cast<RequirementsMask>(REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY);
+ ForEachMatchingFormField(form_element, node, requirements, form, NewCallback(this, &FormManager::PreviewFormField));
+
+ return true;
+}
+
+bool FormManager::ClearFormWithNode(Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
+ if (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+
+ // We don't modify the value of disabled fields.
+ if (!input_element->isEnabledFormControl())
+ continue;
+
+ input_element->setValue("");
+ input_element->setAutofilled(false);
+ // Clearing the value in the focused node (above) can cause selection
+ // to be lost. We force selection range to restore the text cursor.
+ if (node == input_element) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ } else if (formControlType(*element) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element);
+ select_element->setValue(form_element->control_values[i].c_str());
+ }
+ }
+
+ return true;
+}
+
+bool FormManager::ClearPreviewedFormWithNode(Node* node, bool was_autofilled) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
+
+ // Only input elements can be previewed.
+ if (formControlType(*element) != kText)
+ continue;
+
+ // If the input element has not been auto-filled, FormManager has not
+ // previewed this field, so we have nothing to reset.
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ if (!input_element->isAutofilled())
+ continue;
+
+ // There might be unrelated elements in this form which have already been
+ // auto-filled. For example, the user might have already filled the address
+ // part of a form and now be dealing with the credit card section. We only
+ // want to reset the auto-filled status for fields that were previewed.
+ if (input_element->suggestedValue().isEmpty())
+ continue;
+
+ // Clear the suggested value. For the initiating node, also restore the
+ // original value.
+ input_element->setSuggestedValue("");
+ bool is_initiating_node = (node == input_element);
+ if (is_initiating_node) {
+ // Call |setValue()| to force the renderer to update the field's displayed
+ // value.
+ input_element->setValue(input_element->value());
+ input_element->setAutofilled(was_autofilled);
+ } else {
+ input_element->setAutofilled(false);
+ }
+
+ // Clearing the suggested value in the focused node (above) can cause
+ // selection to be lost. We force selection range to restore the text
+ // cursor.
+ if (is_initiating_node) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ }
+
+ return true;
+}
+
+void FormManager::Reset() {
+ STLDeleteElements(&form_elements_);
+}
+
+void FormManager::ResetFrame(const Frame* frame) {
+ FormElementList::iterator iter = form_elements_.begin();
+ while (iter != form_elements_.end()) {
+ if ((*iter)->form_element->document()->frame() == frame) {
+ delete *iter;
+ iter = form_elements_.erase(iter);
+ } else
+ ++iter;
+ }
+}
+
+bool FormManager::FormWithNodeIsAutoFilled(Node* node) {
+ FormElement* form_element = NULL;
+ if (!FindCachedFormElementWithNode(node, &form_element))
+ return false;
+
+ for (size_t i = 0; i < form_element->control_elements.size(); ++i) {
+ HTMLFormControlElement* element = form_element->control_elements[i].get();
+ if (formControlType(*element) != kText)
+ continue;
+
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+ if (input_element->isAutofilled())
+ return true;
+ }
+
+ return false;
+}
+
+// static
+string16 FormManager::InferLabelForElement(const HTMLFormControlElement& element) {
+ // Don't scrape labels for hidden elements.
+ if (formControlType(element) == kHidden)
+ return string16();
+
+ string16 inferred_label = InferLabelFromPrevious(element);
+
+ // If we didn't find a label, check for table cell case.
+ if (inferred_label.empty())
+ inferred_label = InferLabelFromTable(element);
+
+ // If we didn't find a label, check for div table case.
+ if (inferred_label.empty())
+ inferred_label = InferLabelFromDivTable(element);
+
+ // If we didn't find a label, check for definition list case.
+ if (inferred_label.empty())
+ inferred_label = InferLabelFromDefinitionList(element);
+
+ return inferred_label;
+}
+
+bool FormManager::FindCachedFormElementWithNode(Node* node,
+ FormElement** form_element) {
+ for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
+ for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) {
+ if (iter->get() == node) {
+ *form_element = *form_iter;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool FormManager::FindCachedFormElement(const FormData& form, FormElement** form_element) {
+ for (FormElementList::iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) {
+ // TODO: matching on form name here which is not guaranteed to
+ // be unique for the page, nor is it guaranteed to be non-empty. Need to
+ // find a way to uniquely identify the form cross-process. For now we'll
+ // check form name and form action for identity.
+ // http://crbug.com/37990 test file sample8.html.
+ // Also note that WebString() == WebString(string16()) does not seem to
+ // evaluate to |true| for some reason TBD, so forcing to string16.
+ string16 element_name(WTFStringToString16((*form_iter)->form_element->name()));
+ GURL action(WTFStringToString16((*form_iter)->form_element->document()->completeURL((*form_iter)->form_element->action()).string()));
+ if (element_name == form.name && action == form.action) {
+ *form_element = *form_iter;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void FormManager::ForEachMatchingFormField(FormElement* form, Node* node, RequirementsMask requirements, const FormData& data, Callback* callback) {
+ // It's possible that the site has injected fields into the form after the
+ // page has loaded, so we can't assert that the size of the cached control
+ // elements is equal to the size of the fields in |form|. Fortunately, the
+ // one case in the wild where this happens, paypal.com signup form, the fields
+ // are appended to the end of the form and are not visible.
+ for (size_t i = 0, j = 0; i < form->control_elements.size() && j < data.fields.size(); ++i) {
+ HTMLFormControlElement* element = form->control_elements[i].get();
+ string16 element_name = nameForAutoFill(*element);
+
+ if (element_name.empty())
+ continue;
+
+ // Search forward in the |form| for a corresponding field.
+ size_t k = j;
+ while (k < data.fields.size() && element_name != data.fields[k].name())
+ k++;
+
+ if (k >= data.fields.size())
+ continue;
+
+ DCHECK_EQ(data.fields[k].name(), element_name);
+
+ bool is_initiating_node = false;
+
+ // More than likely |requirements| will contain REQUIRE_AUTOCOMPLETE and/or
+ // REQUIRE_EMPTY, which both require text form control elements, so special-
+ // case this type of element.
+ if (formControlType(*element) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element);
+
+ // TODO: WebKit currently doesn't handle the autocomplete
+ // attribute for select control elements, but it probably should.
+ if (requirements & REQUIRE_AUTOCOMPLETE && !input_element->autoComplete())
+ continue;
+
+ is_initiating_node = (input_element == node);
+ // Don't require the node that initiated the auto-fill process to be
+ // empty. The user is typing in this field and we should complete the
+ // value when the user selects a value to fill out.
+ if (requirements & REQUIRE_EMPTY && !is_initiating_node && !input_element->value().isEmpty())
+ continue;
+ }
+
+ if (requirements & REQUIRE_ENABLED && !element->isEnabledFormControl())
+ continue;
+
+ callback->Run(element, &data.fields[k], is_initiating_node);
+
+ // We found a matching form field so move on to the next.
+ ++j;
+ }
+
+ delete callback;
+}
+
+void FormManager::FillFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) {
+ // Nothing to fill.
+ if (data->value().empty())
+ return;
+
+ if (formControlType(*field) == kText) {
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field);
+
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ input_element->setValue(data->value().substr(0, input_element->maxLength()).c_str());
+ input_element->setAutofilled(true);
+ if (is_initiating_node) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ } else if (formControlType(*field) == kSelectOne) {
+ HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(field);
+ select_element->setValue(data->value().c_str());
+ }
+}
+
+void FormManager::PreviewFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) {
+ // Nothing to preview.
+ if (data->value().empty())
+ return;
+
+ // Only preview input fields.
+ if (formControlType(*field) != kText)
+ return;
+
+ HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field);
+
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ input_element->setSuggestedValue(data->value().substr(0, input_element->maxLength()).c_str());
+ input_element->setAutofilled(true);
+ if (is_initiating_node)
+ input_element->setSelectionRange(0, input_element->suggestedValue().length());
+}
+
+}
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h
new file mode 100644
index 0000000..e844981
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 The Chromium Authors. All rights reserved.
+ * 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 FormManagerAndroid_h
+#define FormManagerAndroid_h
+
+#include "ChromiumIncludes.h"
+
+#include <map>
+#include <vector>
+
+// TODO: This file is taken from chromium/chrome/renderer/form_manager.h and
+// customised to use WebCore types rather than WebKit API types. It would be
+// nice and would ease future merge pain if the two could be combined.
+
+namespace webkit_glue {
+struct FormData;
+class FormField;
+} // namespace webkit_glue
+
+namespace WebCore {
+class Frame;
+class HTMLFormControlElement;
+class HTMLFormElement;
+class Node;
+}
+
+using WebCore::Frame;
+using WebCore::HTMLFormControlElement;
+using WebCore::HTMLFormElement;
+using WebCore::Node;
+
+namespace android {
+
+// Manages the forms in a Document.
+class FormManager {
+public:
+ // A bit field mask for form requirements.
+ enum RequirementsMask {
+ REQUIRE_NONE = 0, // No requirements.
+ REQUIRE_AUTOCOMPLETE = 1 << 0, // Require that autocomplete != off.
+ REQUIRE_ENABLED = 1 << 1, // Require that disabled attribute is off.
+ REQUIRE_EMPTY = 1 << 2, // Require that the fields are empty.
+ };
+
+ // A bit field mask to extract data from HTMLFormControlElement.
+ enum ExtractMask {
+ EXTRACT_NONE = 0,
+ EXTRACT_VALUE = 1 << 0, // Extract value from HTMLFormControlElement.
+ EXTRACT_OPTION_TEXT = 1 << 1, // Extract option text from HTMLFormSelectElement. Only valid when |EXTRACT_VALUE| is set. This is used for form submission where humand readable value is captured.
+ EXTRACT_OPTIONS = 1 << 2, // Extract options from HTMLFormControlElement.
+ };
+
+ FormManager();
+ virtual ~FormManager();
+
+ // Fills out a FormField object from a given HTMLFormControlElement.
+ // |extract_mask|: See the enum ExtractMask above for details.
+ static void HTMLFormControlElementToFormField(HTMLFormControlElement* element, ExtractMask extract_mask, webkit_glue::FormField* field);
+
+ // Returns the corresponding label for |element|. WARNING: This method can
+ // potentially be very slow. Do not use during any code paths where the page
+ // is loading.
+ static string16 LabelForElement(const HTMLFormControlElement& element);
+
+ // Fills out a FormData object from a given WebFormElement. If |get_values|
+ // is true, the fields in |form| will have the values filled out. Returns
+ // true if |form| is filled out; it's possible that |element| won't meet the
+ // requirements in |requirements|. This also returns false if there are no
+ // fields in |form|.
+ // TODO: Remove the user of this in RenderView and move this to
+ // private.
+ static bool HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, ExtractMask extract_mask, webkit_glue::FormData* form);
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ void ExtractForms(Frame* frame);
+
+ // Returns a vector of forms in |frame| that match |requirements|.
+ void GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<webkit_glue::FormData>* forms);
+
+ // Finds the form that contains |element| and returns it in |form|. Returns
+ // false if the form is not found.
+ bool FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, webkit_glue::FormData* form);
+
+ // Fills the form represented by |form|. |form| should have the name set to
+ // the name of the form to fill out, and the number of elements and values
+ // must match the number of stored elements in the form. |node| is the form
+ // control element that initiated the auto-fill process.
+ // TODO: Is matching on name alone good enough? It's possible to
+ // store multiple forms with the same names from different frames.
+ bool FillForm(const webkit_glue::FormData& form, Node* node);
+
+ // Previews the form represented by |form|. |node| is the form control element
+ // that initiated the preview process. Same conditions as FillForm.
+ bool PreviewForm(const webkit_glue::FormData& form, Node* node);
+
+ // Clears the values of all input elements in the form that contains |node|.
+ // Returns false if the form is not found.
+ bool ClearFormWithNode(Node* node);
+
+ // Clears the placeholder values and the auto-filled background for any fields
+ // in the form containing |node| that have been previewed. Resets the
+ // autofilled state of |node| to |was_autofilled|. Returns false if the form
+ // is not found.
+ bool ClearPreviewedFormWithNode(Node* node, bool was_autofilled);
+
+ // Resets the stored set of forms.
+ void Reset();
+
+ // Resets the forms for the specified |frame|.
+ void ResetFrame(const Frame* frame);
+
+ // Returns true if |form| has any auto-filled fields.
+ bool FormWithNodeIsAutoFilled(Node* node);
+
+private:
+ // Stores the HTMLFormElement and the form control elements for a form.
+ // Original form values are stored so when we clear a form we can reset
+ // "select-one" values to their original state.
+ struct FormElement;
+
+ // Type for cache of FormElement objects.
+ typedef std::vector<FormElement*> FormElementList;
+
+ // The callback type used by ForEachMatchingFormField().
+ typedef Callback3<HTMLFormControlElement*, const webkit_glue::FormField*, bool>::Type Callback;
+
+ // Infers corresponding label for |element| from surrounding context in the
+ // DOM. Contents of preceeding <p> tag or preceeding text element found in
+ // the form.
+ static string16 InferLabelForElement(const HTMLFormControlElement& element);
+
+ // Finds the cached FormElement that contains |node|.
+ bool FindCachedFormElementWithNode(Node* node, FormElement** form_element);
+
+ // Uses the data in |form| to find the cached FormElement.
+ bool FindCachedFormElement(const webkit_glue::FormData& form, FormElement** form_element);
+
+ // For each field in |data| that matches the corresponding field in |form|
+ // and meets the |requirements|, |callback| is called with the actual
+ // WebFormControlElement and the FormField data from |form|. The field that
+ // matches |node| is not required to be empty if |requirements| includes
+ // REQUIRE_EMPTY. This method owns |callback|.
+ void ForEachMatchingFormField(FormElement* form, Node* node, RequirementsMask requirements, const webkit_glue::FormData& data, Callback* callback);
+
+ // A ForEachMatchingFormField() callback that sets |field|'s value using the
+ // value in |data|. This method also sets the autofill attribute, causing the
+ // background to be yellow.
+ void FillFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data, bool is_initiating_node);
+
+ // A ForEachMatchingFormField() callback that sets |field|'s placeholder value
+ // using the value in |data|, causing the test to be greyed-out. This method
+ // also sets the autofill attribute, causing the background to be yellow.
+ void PreviewFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data, bool is_initiaiting_node);
+
+ // The cached FormElement objects.
+ FormElementList form_elements_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormManager);
+};
+
+} // namespace android
+
+#endif // FormManagerAndroid_h
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp
new file mode 100644
index 0000000..598b9c4
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "MainThreadProxy.h"
+
+#include <wtf/MainThread.h>
+
+void MainThreadProxy::CallOnMainThread(CallOnMainThreadFunction f, void* c)
+{
+ callOnMainThread(f, c);
+}
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h
new file mode 100644
index 0000000..d9310bb
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h
@@ -0,0 +1,37 @@
+/*
+ * 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 MAIN_THREAD_PROXY_H
+#define MAIN_THREAD_PROXY_H
+
+typedef void CallOnMainThreadFunction(void*);
+
+class MainThreadProxy
+{
+public:
+ static void CallOnMainThread(CallOnMainThreadFunction, void*);
+};
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h b/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h
new file mode 100644
index 0000000..aa408a5
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010 The Android Open Source Project. 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.
+ *
+ * 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 AutoFillStringUtils_h_
+#define AutoFillStringUtils_h_
+
+#include "ChromiumIncludes.h"
+#include "HTMLFormControlElement.h"
+#include <wtf/text/WTFString.h>
+
+using WebCore::HTMLFormControlElement;
+
+const string16 kText = ASCIIToUTF16("text");
+const string16 kHidden = ASCIIToUTF16("hidden");
+const string16 kSelectOne = ASCIIToUTF16("select-one");
+
+inline string16 WTFStringToString16(const WTF::String& wtfString)
+{
+ WTF::String str = wtfString;
+
+ if (str.charactersWithNullTermination())
+ return string16(str.charactersWithNullTermination());
+ else
+ return string16();
+}
+
+inline string16 nameForAutoFill(const HTMLFormControlElement& element)
+{
+ // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported
+ // to use WebCore types for accessing element properties.
+ String name = element.name();
+ String trimmedName = name.stripWhiteSpace();
+ if (!trimmedName.isEmpty())
+ return WTFStringToString16(trimmedName);
+ name = element.getIdAttribute();
+ trimmedName = name.stripWhiteSpace();
+ if (!trimmedName.isEmpty())
+ return WTFStringToString16(trimmedName);
+ return string16();
+}
+
+inline string16 formControlType(const HTMLFormControlElement& element)
+{
+ // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported
+ // to use WebCore types for accessing element properties.
+ return WTFStringToString16(element.type());
+}
+
+#endif
+
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
new file mode 100644
index 0000000..a80636c
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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 "WebAutoFill.h"
+
+#if ENABLE(WEB_AUTOFILL)
+
+#include "AutoFillHostAndroid.h"
+#include "Frame.h"
+#include "FormData.h"
+#include "FormManagerAndroid.h"
+#include "FrameLoader.h"
+#include "HTMLFormControlElement.h"
+#include "MainThreadProxy.h"
+#include "Node.h"
+#include "Page.h"
+#include "Settings.h"
+#include "WebFrame.h"
+#include "WebRequestContext.h"
+#include "WebUrlLoaderClient.h"
+#include "WebViewCore.h"
+
+#define NO_PROFILE_SET 0
+#define FORM_NOT_AUTOFILLABLE -1
+
+namespace android
+{
+WebAutoFill::WebAutoFill()
+ : mQueryId(1)
+ , mWebViewCore(0)
+ , mLastSearchDomVersion(0)
+ , mParsingForms(false)
+{
+ mTabContents = new TabContents();
+ setEmptyProfile();
+}
+
+void WebAutoFill::init()
+{
+ if (mAutoFillManager)
+ return;
+
+ mFormManager = new FormManager();
+ // We use the WebView's WebRequestContext, which may be a private browsing context.
+ ASSERT(mWebViewCore);
+ mAutoFillManager = new AutoFillManager(mTabContents.get());
+ mAutoFillHost = new AutoFillHostAndroid(this);
+ mTabContents->SetProfileRequestContext(new AndroidURLRequestContextGetter(mWebViewCore->webRequestContext(), WebUrlLoaderClient::ioThread()));
+ mTabContents->SetAutoFillHost(mAutoFillHost.get());
+}
+
+WebAutoFill::~WebAutoFill()
+{
+ cleanUpQueryMap();
+ mUniqueIdMap.clear();
+}
+
+void WebAutoFill::cleanUpQueryMap()
+{
+ for (AutoFillQueryFormDataMap::iterator it = mQueryMap.begin(); it != mQueryMap.end(); it++)
+ delete it->second;
+ mQueryMap.clear();
+}
+
+void WebAutoFill::searchDocument(WebCore::Frame* frame)
+{
+ if (!enabled())
+ return;
+
+ MutexLocker lock(mFormsSeenMutex);
+
+ init();
+
+ cleanUpQueryMap();
+ mUniqueIdMap.clear();
+ mForms.clear();
+ mQueryId = 1;
+
+ ASSERT(mFormManager);
+ ASSERT(mAutoFillManager);
+
+ mAutoFillManager->Reset();
+ mFormManager->Reset();
+
+ mFormManager->ExtractForms(frame);
+ mFormManager->GetFormsInFrame(frame, FormManager::REQUIRE_AUTOCOMPLETE, &mForms);
+
+ // Needs to be done on a Chrome thread as it will make a URL request to the AutoFill server.
+ // TODO: Use our own Autofill thread instead of the IO thread.
+ // TODO: For now, block here. Would like to make this properly async.
+ base::Thread* thread = WebUrlLoaderClient::ioThread();
+ mParsingForms = true;
+ thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebAutoFill::formsSeenImpl));
+ while (mParsingForms)
+ mFormsSeenCondition.wait(mFormsSeenMutex);
+}
+
+// Called on the Chromium IO thread.
+void WebAutoFill::formsSeenImpl()
+{
+ MutexLocker lock(mFormsSeenMutex);
+ mAutoFillManager->FormsSeen(mForms);
+ mParsingForms = false;
+ mFormsSeenCondition.signal();
+}
+
+void WebAutoFill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldElement)
+{
+ ASSERT(formFieldElement);
+
+ Document* doc = formFieldElement->document();
+ Frame* frame = doc->frame();
+
+ // FIXME: AutoFill only works in main frame for now. Should consider
+ // child frames.
+ if (frame != frame->page()->mainFrame())
+ return;
+
+ unsigned domVersion = doc->domTreeVersion();
+ ASSERT(domVersion > 0);
+
+ if (mLastSearchDomVersion != domVersion) {
+ // Need to extract forms as DOM version has changed since the last time
+ // we searched.
+ searchDocument(formFieldElement->document()->frame());
+ mLastSearchDomVersion = domVersion;
+ }
+
+ if (!enabled()) {
+ // In case that we've just been disabled and the last time we got autofill
+ // suggestions and told Java about them, clear that bit Java side now
+ // we're disabled.
+ mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16());
+ return;
+ }
+
+ // Get the FormField from the Node.
+ webkit_glue::FormField* formField = new webkit_glue::FormField;
+ FormManager::HTMLFormControlElementToFormField(formFieldElement, FormManager::EXTRACT_NONE, formField);
+ formField->set_label(FormManager::LabelForElement(*formFieldElement));
+
+ webkit_glue::FormData* form = new webkit_glue::FormData;
+ mFormManager->FindFormWithFormControlElement(formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form);
+ mQueryMap[mQueryId] = new FormDataAndField(form, formField);
+
+ bool suggestions = mAutoFillManager->GetAutoFillSuggestions(*form, *formField);
+
+ mQueryId++;
+ if (!suggestions) {
+ ASSERT(mWebViewCore);
+ // Tell Java no autofill suggestions for this form.
+ mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16());
+ return;
+ }
+}
+
+void WebAutoFill::querySuccessful(const string16& value, const string16& label, int uniqueId)
+{
+ if (!enabled())
+ return;
+
+ // Store the unique ID for the query and inform java that autofill suggestions for this form are available.
+ // Pass java the queryId so that it can pass it back if the user decides to use autofill.
+ mUniqueIdMap[mQueryId] = uniqueId;
+
+ ASSERT(mWebViewCore);
+ mWebViewCore->setWebTextViewAutoFillable(mQueryId, mAutoFillProfile->Label());
+}
+
+void WebAutoFill::fillFormFields(int queryId)
+{
+ if (!enabled())
+ return;
+
+ webkit_glue::FormData* form = mQueryMap[queryId]->form();
+ webkit_glue::FormField* field = mQueryMap[queryId]->field();
+ ASSERT(form);
+ ASSERT(field);
+
+ AutoFillQueryToUniqueIdMap::iterator iter = mUniqueIdMap.find(queryId);
+ if (iter == mUniqueIdMap.end()) {
+ // The user has most likely tried to AutoFill the form again without
+ // refocussing the form field. The UI should protect against this
+ // but stop here to be certain.
+ return;
+ }
+ mAutoFillManager->FillAutoFillFormData(queryId, *form, *field, iter->second);
+ mUniqueIdMap.erase(iter);
+}
+
+void WebAutoFill::fillFormInPage(int queryId, const webkit_glue::FormData& form)
+{
+ if (!enabled())
+ return;
+
+ // FIXME: Pass a pointer to the Node that triggered the AutoFill flow here instead of 0.
+ // The consquence of passing 0 is that we should always fail the test in FormManader::ForEachMathcingFormField():169
+ // that says "only overwrite an elements current value if the user triggered autofill through that element"
+ // for elements that have a value already. But by a quirk of Android text views we are OK. We should still
+ // fix this though.
+ mFormManager->FillForm(form, 0);
+}
+
+bool WebAutoFill::enabled() const
+{
+ Page* page = mWebViewCore->mainFrame()->page();
+ return page ? page->settings()->autoFillEnabled() : false;
+}
+
+void WebAutoFill::setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName,
+ const string16& addressLine1, const string16& addressLine2, const string16& city,
+ const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber)
+{
+ if (!mAutoFillProfile)
+ mAutoFillProfile.set(new AutoFillProfile());
+
+ // Update the profile.
+ // Constants for AutoFill field types are found in external/chromium/chrome/browser/autofill/field_types.h.
+ mAutoFillProfile->SetInfo(AutoFillType(NAME_FULL), fullName);
+ mAutoFillProfile->SetInfo(AutoFillType(EMAIL_ADDRESS), emailAddress);
+ mAutoFillProfile->SetInfo(AutoFillType(COMPANY_NAME), companyName);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), addressLine1);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), addressLine2);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), city);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), state);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), zipCode);
+ mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), country);
+ mAutoFillProfile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), phoneNumber);
+
+ std::vector<AutoFillProfile> profiles;
+ profiles.push_back(*mAutoFillProfile);
+ updateProfileLabel();
+ mTabContents->profile()->GetPersonalDataManager()->SetProfiles(&profiles);
+}
+
+bool WebAutoFill::updateProfileLabel()
+{
+ std::vector<AutoFillProfile*> profiles;
+ profiles.push_back(mAutoFillProfile.get());
+ return AutoFillProfile::AdjustInferredLabels(&profiles);
+}
+
+void WebAutoFill::clearProfiles()
+{
+ if (!mAutoFillProfile)
+ return;
+ // For now Chromium only ever knows about one profile, so we can just
+ // remove it. If we support multiple profiles in the future
+ // we need to remove them all here.
+ std::string profileGuid = mAutoFillProfile->guid();
+ mTabContents->profile()->GetPersonalDataManager()->RemoveProfile(profileGuid);
+ setEmptyProfile();
+}
+
+void WebAutoFill::setEmptyProfile()
+{
+ // Set an empty profile. This will ensure that when autofill is enabled,
+ // we will still search the document for autofillable forms and inform
+ // java of their presence so we can invite the user to set up
+ // their own profile.
+
+ // Chromium code will strip the values sent into the profile so we need them to be
+ // at least one non-whitespace character long. We need to set all fields of the
+ // profile to a non-empty string so that any field type can trigger the autofill
+ // suggestion. AutoFill will not detect form fields if the profile value for that
+ // field is an empty string.
+ static const string16 empty = string16(ASCIIToUTF16("a"));
+ setProfile(empty, empty, empty, empty, empty, empty, empty, empty, empty, empty);
+}
+
+}
+
+#endif
diff --git a/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h
new file mode 100644
index 0000000..97e478e
--- /dev/null
+++ b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h
@@ -0,0 +1,127 @@
+/*
+ * 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 WebAutoFill_h
+#define WebAutoFill_h
+
+#if ENABLE(WEB_AUTOFILL)
+
+#include "ChromiumIncludes.h"
+
+#include <map>
+#include <vector>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/ThreadingPrimitives.h>
+
+class AutoFillManager;
+class AutoFillProfile;
+class AutoFillHost;
+
+namespace WebCore {
+class Frame;
+class HTMLFormControlElement;
+}
+
+namespace android
+{
+class FormManager;
+class WebViewCore;
+
+class FormDataAndField {
+public:
+ FormDataAndField(webkit_glue::FormData* form, webkit_glue::FormField* field)
+ : mForm(form)
+ , mField(field)
+ {
+ }
+
+ webkit_glue::FormData* form() { return mForm.get(); }
+ webkit_glue::FormField* field() { return mField.get(); }
+
+private:
+ OwnPtr<webkit_glue::FormData> mForm;
+ OwnPtr<webkit_glue::FormField> mField;
+};
+
+class WebAutoFill : public Noncopyable
+{
+public:
+ WebAutoFill();
+ virtual ~WebAutoFill();
+ void formFieldFocused(WebCore::HTMLFormControlElement*);
+ void fillFormFields(int queryId);
+ void querySuccessful(const string16& value, const string16& label, int uniqueId);
+ void fillFormInPage(int queryId, const webkit_glue::FormData& form);
+ void setWebViewCore(WebViewCore* webViewCore) { mWebViewCore = webViewCore; }
+ bool enabled() const;
+
+ void setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName,
+ const string16& addressLine1, const string16& addressLine2, const string16& city,
+ const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber);
+ void clearProfiles();
+
+ bool updateProfileLabel();
+
+ void reset() { mLastSearchDomVersion = 0; }
+
+private:
+ void init();
+ void searchDocument(WebCore::Frame*);
+ void setEmptyProfile();
+ void formsSeenImpl();
+ void cleanUpQueryMap();
+
+ OwnPtr<FormManager> mFormManager;
+ OwnPtr<AutoFillManager> mAutoFillManager;
+ OwnPtr<AutoFillHost> mAutoFillHost;
+ OwnPtr<TabContents> mTabContents;
+ OwnPtr<AutoFillProfile> mAutoFillProfile;
+
+ typedef std::vector<webkit_glue::FormData, std::allocator<webkit_glue::FormData> > FormList;
+ FormList mForms;
+
+ typedef std::map<int, FormDataAndField*> AutoFillQueryFormDataMap;
+ AutoFillQueryFormDataMap mQueryMap;
+
+ typedef std::map<int, int> AutoFillQueryToUniqueIdMap;
+ AutoFillQueryToUniqueIdMap mUniqueIdMap;
+ int mQueryId;
+
+ WebViewCore* mWebViewCore;
+
+ unsigned mLastSearchDomVersion;
+
+ WTF::Mutex mFormsSeenMutex; // Guards mFormsSeenCondition and mParsingForms.
+ WTF::ThreadCondition mFormsSeenCondition;
+ bool volatile mParsingForms;
+};
+
+}
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(android::WebAutoFill);
+
+#endif // ENABLE(WEB_AUTOFILL)
+#endif // WebAutoFill_h
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..edd5123
--- /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..36d478d
--- /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 MY_JAVA_VM_H
+#define MY_JAVA_VM_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/icu/unicode/ucnv.cpp b/Source/WebKit/android/icu/unicode/ucnv.cpp
new file mode 100644
index 0000000..1b40573
--- /dev/null
+++ b/Source/WebKit/android/icu/unicode/ucnv.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+// BEGIN android-added
+// Add config.h to avoid compiler error in uobject.h
+// ucnv.h includes uobject.h indirectly and uobjetcs.h defines new/delete.
+// new/delete are also defined in WebCorePrefix.h which auto included in Android make.
+//
+// config.h has to be on top of the include list.
+#include "config.h"
+// END android-added
+
+#include "EmojiFont.h"
+#include <icu4c/common/unicode/ucnv.h>
+
+namespace android {
+
+U_STABLE UConverter* U_EXPORT2
+ucnv_open_emoji(const char *converterName, UErrorCode *err) {
+ if (EmojiFont::IsAvailable()) {
+ if (strcmp(converterName, "Shift_JIS") == 0) {
+ converterName = EmojiFont::GetShiftJisConverterName();
+ }
+ }
+ return ucnv_open(converterName, err);
+}
+
+} // end namespace android
diff --git a/Source/WebKit/android/icu/unicode/ucnv.h b/Source/WebKit/android/icu/unicode/ucnv.h
new file mode 100644
index 0000000..5ddaedb
--- /dev/null
+++ b/Source/WebKit/android/icu/unicode/ucnv.h
@@ -0,0 +1,47 @@
+/*
+ * 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 ANDROID_UCNV_H
+#define ANDROID_UCNV_H
+
+// Include the real ucnv.h file from icu. Use a more exact reference so we do
+// not conflict with this file.
+#include <icu4c/common/unicode/ucnv.h>
+
+namespace android {
+
+U_STABLE UConverter* U_EXPORT2
+ucnv_open_emoji(const char *converterName, UErrorCode *err);
+
+}
+
+// Redefine ucnv_open to android::ucnv_open_emoji. This relies heavily on the
+// fact that this file will be included before any of the real icu headers.
+// This is done in Android.mk by including WebKit/android/icu before the
+// regular icu directory.
+#undef ucnv_open
+#define ucnv_open android::ucnv_open_emoji
+
+#endif
diff --git a/Source/WebKit/android/jni/CacheManager.cpp b/Source/WebKit/android/jni/CacheManager.cpp
new file mode 100644
index 0000000..144b62a
--- /dev/null
+++ b/Source/WebKit/android/jni/CacheManager.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011, 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"
+
+#if USE(CHROME_NETWORK_STACK)
+
+#include "ChromiumIncludes.h"
+#include "WebCache.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <platform/FileSystem.h>
+#include <platform/text/Base64.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebCore;
+using namespace base;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+// JNI for android.webkit.CacheManager
+static const char* javaCacheManagerClass = "android/webkit/CacheManager";
+
+static void setStringField(JNIEnv* env, const jobject& object, const jfieldID& field, const String& str)
+{
+ jstring jstr = wtfStringToJstring(env, str);
+ env->SetObjectField(object, field, jstr);
+ env->DeleteLocalRef(jstr);
+}
+
+static void setFieldFromHeaderIfPresent(CacheResult* result, const char* header, JNIEnv* env, const jobject& object, const jfieldID& field, bool allowEmptyString)
+{
+ String value;
+ if (result->firstResponseHeader(header, &value, allowEmptyString))
+ setStringField(env, object, field, value);
+}
+
+static String getCacheFileBaseDir(JNIEnv* env)
+{
+ static String baseDir;
+ if (baseDir.isEmpty()) {
+ jclass cacheManagerClass = env->FindClass("android/webkit/CacheManager");
+ jmethodID getCacheFileBaseDirMethod = env->GetStaticMethodID(cacheManagerClass, "getCacheFileBaseDir", "()Ljava/io/File;");
+ jclass fileClass = env->FindClass("java/io/File");
+ jmethodID getPathMethod = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
+ jobject fileObject = env->CallStaticObjectMethod(cacheManagerClass, getCacheFileBaseDirMethod);
+ baseDir = jstringToWtfString(env, static_cast<jstring>(env->CallObjectMethod(fileObject, getPathMethod)));
+ }
+ return baseDir;
+}
+
+static jobject getCacheResult(JNIEnv* env, jobject, jstring url)
+{
+ // This is called on the UI thread.
+ scoped_refptr<CacheResult> result = WebCache::get(false /*privateBrowsing*/)->getCacheResult(jstringToWtfString(env, url));
+ if (!result)
+ return 0;
+
+ // We create and populate a file with the cache entry. This allows us to
+ // replicate the behaviour of the Android HTTP stack in the Java
+ // CacheManager, which opens the cache file and provides an input stream to
+ // the file as part of the Java CacheResult object!
+ String urlWtfString = jstringToWtfString(env, url);
+ Vector<char> encodedUrl;
+ base64Encode(urlWtfString.utf8().data(), urlWtfString.length(), encodedUrl, false /*insertLFs*/);
+ String filePath = pathByAppendingComponent(getCacheFileBaseDir(env), encodedUrl.data());
+ if (!result->writeToFile(filePath))
+ return 0;
+
+ jclass cacheResultClass = env->FindClass("android/webkit/CacheManager$CacheResult");
+ jmethodID constructor = env->GetMethodID(cacheResultClass, "<init>", "()V");
+ // We only bother with the fields that are made accessible through the public API.
+ jfieldID contentdispositionField = env->GetFieldID(cacheResultClass, "contentdisposition", "Ljava/lang/String;");
+ jfieldID contentLengthField = env->GetFieldID(cacheResultClass, "contentLength", "J");
+ jfieldID etagField = env->GetFieldID(cacheResultClass, "etag", "Ljava/lang/String;");
+ jfieldID encodingField = env->GetFieldID(cacheResultClass, "encoding", "Ljava/lang/String;");
+ jfieldID expiresField = env->GetFieldID(cacheResultClass, "expires", "J");
+ jfieldID expiresStringField = env->GetFieldID(cacheResultClass, "expiresString", "Ljava/lang/String;");
+ jfieldID httpStatusCodeField = env->GetFieldID(cacheResultClass, "httpStatusCode", "I");
+ jfieldID lastModifiedField = env->GetFieldID(cacheResultClass, "lastModified", "Ljava/lang/String;");
+ jfieldID localPathField = env->GetFieldID(cacheResultClass, "localPath", "Ljava/lang/String;");
+ jfieldID locationField = env->GetFieldID(cacheResultClass, "location", "Ljava/lang/String;");
+ jfieldID mimeTypeField = env->GetFieldID(cacheResultClass, "mimeType", "Ljava/lang/String;");
+
+ jobject javaResult = env->NewObject(cacheResultClass, constructor);
+ setFieldFromHeaderIfPresent(result.get(), "content-disposition", env, javaResult, contentdispositionField, true);
+ env->SetLongField(javaResult, contentLengthField, result->contentSize());
+ setFieldFromHeaderIfPresent(result.get(), "etag", env, javaResult, etagField, false);
+ setStringField(env, javaResult, encodingField, "TODO"); // TODO: Where does the Android stack set this?
+ env->SetLongField(javaResult, expiresField, result->expires());
+ env->SetIntField(javaResult, httpStatusCodeField, result->responseCode());
+ setFieldFromHeaderIfPresent(result.get(), "last-modified", env, javaResult, lastModifiedField, false);
+ setStringField(env, javaResult, localPathField, encodedUrl.data());
+ setFieldFromHeaderIfPresent(result.get(), "location", env, javaResult, locationField, false);
+ setStringField(env, javaResult, mimeTypeField, result->mimeType());
+
+ return javaResult;
+}
+
+static JNINativeMethod gCacheManagerMethods[] = {
+ { "nativeGetCacheResult", "(Ljava/lang/String;)Landroid/webkit/CacheManager$CacheResult;", (void*) getCacheResult },
+};
+
+int registerCacheManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass cacheManager = env->FindClass(javaCacheManagerClass);
+ 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
new file mode 100644
index 0000000..0bdf303
--- /dev/null
+++ b/Source/WebKit/android/jni/CookieManager.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "WebCookieJar.h"
+#include "WebCoreJni.h"
+#include <JNIHelp.h>
+
+using namespace base;
+using namespace net;
+
+namespace android {
+
+// 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
+ // for such contexts are cleared up when we're done with them.
+ // TODO: Consider adding an optimisation to not create the context if it
+ // doesn't already exist.
+ WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->DeleteAll(true);
+
+ // 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();
+ CookieMonster::CookieList cookies = cookieMonster->GetAllCookies();
+ for (CookieMonster::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[] = {
+ { "nativeAcceptCookie", "()Z", (void*) acceptCookie },
+ { "nativeGetCookie", "(Ljava/lang/String;Z)Ljava/lang/String;", (void*) getCookie },
+ { "nativeHasCookies", "(Z)Z", (void*) hasCookies },
+ { "nativeRemoveAllCookie", "()V", (void*) removeAllCookie },
+ { "nativeRemoveExpiredCookie", "()V", (void*) removeExpiredCookie },
+ { "nativeRemoveSessionCookie", "()V", (void*) removeSessionCookie },
+ { "nativeSetAcceptCookie", "(Z)V", (void*) setAcceptCookie },
+ { "nativeSetCookie", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*) setCookie },
+ { "nativeFlushCookieStore", "()V", (void*) flushCookieStore },
+ { "nativeAcceptFileSchemeCookies", "()Z", (void*) acceptFileSchemeCookies },
+ { "nativeSetAcceptFileSchemeCookies", "(Z)V", (void*) setAcceptFileSchemeCookies },
+};
+
+int registerCookieManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass cookieManager = env->FindClass(javaCookieManagerClass);
+ LOG_ASSERT(cookieManager, "Unable to find class");
+ env->DeleteLocalRef(cookieManager);
+#endif
+ return jniRegisterNativeMethods(env, javaCookieManagerClass, gCookieManagerMethods, NELEM(gCookieManagerMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
new file mode 100644
index 0000000..8beb372
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DeviceMotionAndOrientationManager.h"
+
+#include "DeviceMotionClientImpl.h"
+#include "DeviceOrientationClientImpl.h"
+#include "DeviceOrientationController.h"
+#include "WebViewCore.h"
+#include "Frame.h"
+#include "Page.h"
+
+#include <DeviceOrientationClientMock.h>
+#include <JNIHelp.h>
+
+using namespace WebCore;
+
+namespace android {
+
+DeviceMotionAndOrientationManager::DeviceMotionAndOrientationManager(WebViewCore* webViewCore)
+ : m_useMock(false)
+ , m_webViewCore(webViewCore)
+{
+}
+
+void DeviceMotionAndOrientationManager::useMock()
+{
+ m_useMock = true;
+}
+
+void DeviceMotionAndOrientationManager::setMockMotion(PassRefPtr<DeviceMotionData> motion)
+{
+ // TODO: There is not yet a DeviceMotion mock.
+}
+
+void DeviceMotionAndOrientationManager::onMotionChange(PassRefPtr<DeviceMotionData> motion)
+{
+ ASSERT(!m_useMock);
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->onMotionChange(motion);
+}
+
+void DeviceMotionAndOrientationManager::setMockOrientation(PassRefPtr<DeviceOrientation> orientation)
+{
+ if (m_useMock)
+ static_cast<DeviceOrientationClientMock*>(orientationClient())->setOrientation(orientation);
+}
+
+void DeviceMotionAndOrientationManager::onOrientationChange(PassRefPtr<DeviceOrientation> orientation)
+{
+ ASSERT(!m_useMock);
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->onOrientationChange(orientation);
+}
+
+void DeviceMotionAndOrientationManager::maybeSuspendClients()
+{
+ if (!m_useMock) {
+ if (m_motionClient)
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->suspend();
+ if (m_orientationClient)
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->suspend();
+ }
+}
+
+void DeviceMotionAndOrientationManager::maybeResumeClients()
+{
+ if (!m_useMock) {
+ if (m_motionClient)
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->resume();
+ if (m_orientationClient)
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->resume();
+ }
+}
+
+DeviceMotionClient* DeviceMotionAndOrientationManager::motionClient()
+{
+ // TODO: There is not yet a DeviceMotion mock.
+ if (!m_motionClient)
+ m_motionClient.set(m_useMock ? 0
+ : static_cast<DeviceMotionClient*>(new DeviceMotionClientImpl(m_webViewCore)));
+ ASSERT(m_motionClient);
+ return m_motionClient.get();
+}
+
+DeviceOrientationClient* DeviceMotionAndOrientationManager::orientationClient()
+{
+ if (!m_orientationClient)
+ m_orientationClient.set(m_useMock ? new DeviceOrientationClientMock
+ : static_cast<DeviceOrientationClient*>(new DeviceOrientationClientImpl(m_webViewCore)));
+ ASSERT(m_orientationClient);
+ return m_orientationClient.get();
+}
+
+// JNI for android.webkit.DeviceMotionAndOrientationManager
+static const char* javaDeviceMotionAndOrientationManagerClass = "android/webkit/DeviceMotionAndOrientationManager";
+
+static WebViewCore* getWebViewCore(JNIEnv* env, jobject webViewCoreObject)
+{
+ jclass webViewCoreClass = env->FindClass("android/webkit/WebViewCore");
+ jfieldID nativeClassField = env->GetFieldID(webViewCoreClass, "mNativeClass", "I");
+ env->DeleteLocalRef(webViewCoreClass);
+ return reinterpret_cast<WebViewCore*>(env->GetIntField(webViewCoreObject, nativeClassField));
+}
+
+static void useMock(JNIEnv* env, jobject, jobject webViewCoreObject)
+{
+ 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)
+{
+ // We only provide accelerationIncludingGravity.
+ RefPtr<DeviceMotionData::Acceleration> accelerationIncludingGravity = DeviceMotionData::Acceleration::create(canProvideX, x, canProvideY, y, canProvideZ, z);
+ bool canProvideInterval = canProvideX || canProvideY || canProvideZ;
+ RefPtr<DeviceMotionData> motion = DeviceMotionData::create(0, accelerationIncludingGravity.release(), 0, canProvideInterval, interval);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->onMotionChange(motion.release());
+}
+
+static void setMockOrientation(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma)
+{
+ RefPtr<DeviceOrientation> orientation = DeviceOrientation::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->setMockOrientation(orientation.release());
+}
+
+static void onOrientationChange(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma)
+{
+ RefPtr<DeviceOrientation> orientation = DeviceOrientation::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->onOrientationChange(orientation.release());
+}
+
+static JNINativeMethod gDeviceMotionAndOrientationManagerMethods[] = {
+ { "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 }
+};
+
+int registerDeviceMotionAndOrientationManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass deviceMotionAndOrientationManager = env->FindClass(javaDeviceMotionAndOrientationManagerClass);
+ LOG_ASSERT(deviceMotionAndOrientationManager, "Unable to find class");
+ env->DeleteLocalRef(deviceMotionAndOrientationManager);
+#endif
+
+ return jniRegisterNativeMethods(env, javaDeviceMotionAndOrientationManagerClass, gDeviceMotionAndOrientationManagerMethods, NELEM(gDeviceMotionAndOrientationManagerMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
new file mode 100644
index 0000000..44463c1
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
@@ -0,0 +1,68 @@
+/*
+ * 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 DeviceMotionAndOrientationManager_h
+#define DeviceMotionAndOrientationManager_h
+
+#include <DeviceMotionData.h>
+#include <DeviceMotionClient.h>
+#include <DeviceOrientation.h>
+#include <DeviceOrientationClient.h>
+#include <OwnPtr.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+namespace android {
+
+class WebViewCore;
+
+// This class takes care of the fact that the clients used for DeviceMotion and
+// DeviceOrientation may be either the real implementations or mocks. It also
+// handles setting the data on both the real and mock clients. This class is
+// owned by WebViewCore and exists to keep cruft out of that class.
+class DeviceMotionAndOrientationManager {
+public:
+ DeviceMotionAndOrientationManager(WebViewCore*);
+
+ void useMock();
+ void setMockMotion(PassRefPtr<WebCore::DeviceMotionData>);
+ void onMotionChange(PassRefPtr<WebCore::DeviceMotionData>);
+ void setMockOrientation(PassRefPtr<WebCore::DeviceOrientation>);
+ void onOrientationChange(PassRefPtr<WebCore::DeviceOrientation>);
+ void maybeSuspendClients();
+ void maybeResumeClients();
+ WebCore::DeviceMotionClient* motionClient();
+ WebCore::DeviceOrientationClient* orientationClient();
+
+private:
+ bool m_useMock;
+ WebViewCore* m_webViewCore;
+ OwnPtr<WebCore::DeviceMotionClient> m_motionClient;
+ OwnPtr<WebCore::DeviceOrientationClient> m_orientationClient;
+};
+
+} // namespace android
+
+#endif // DeviceMotionAndOrientationManager_h
diff --git a/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp b/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp
new file mode 100644
index 0000000..82f3c35
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "DeviceMotionClientImpl.h"
+
+#include "WebViewCore.h"
+#include <DeviceMotionController.h>
+#include <Frame.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using JSC::Bindings::getJNIEnv;
+
+enum javaServiceClassMethods {
+ ServiceMethodStart = 0,
+ ServiceMethodStop,
+ ServiceMethodSuspend,
+ ServiceMethodResume,
+ ServiceMethodCount
+};
+static jmethodID javaServiceClassMethodIDs[ServiceMethodCount];
+
+DeviceMotionClientImpl::DeviceMotionClientImpl(WebViewCore* webViewCore)
+ : m_webViewCore(webViewCore)
+ , m_javaServiceObject(0)
+{
+ ASSERT(m_webViewCore);
+}
+
+DeviceMotionClientImpl::~DeviceMotionClientImpl()
+{
+ releaseJavaInstance();
+}
+
+jobject DeviceMotionClientImpl::getJavaInstance()
+{
+ // Lazily get the Java object. We can't do this until the WebViewCore is all
+ // set up.
+ if (m_javaServiceObject)
+ return m_javaServiceObject;
+
+ JNIEnv* env = getJNIEnv();
+
+ ASSERT(m_webViewCore);
+ jobject object = m_webViewCore->getDeviceMotionService();
+
+ // Get the Java DeviceMotionService class.
+ jclass javaServiceClass = env->GetObjectClass(object);
+ ASSERT(javaServiceClass);
+
+ // Set up the methods we wish to call on the Java DeviceMotionService
+ // class.
+ javaServiceClassMethodIDs[ServiceMethodStart] =
+ env->GetMethodID(javaServiceClass, "start", "()V");
+ javaServiceClassMethodIDs[ServiceMethodStop] =
+ env->GetMethodID(javaServiceClass, "stop", "()V");
+ javaServiceClassMethodIDs[ServiceMethodSuspend] =
+ env->GetMethodID(javaServiceClass, "suspend", "()V");
+ javaServiceClassMethodIDs[ServiceMethodResume] =
+ env->GetMethodID(javaServiceClass, "resume", "()V");
+ env->DeleteLocalRef(javaServiceClass);
+
+ m_javaServiceObject = getJNIEnv()->NewGlobalRef(object);
+ getJNIEnv()->DeleteLocalRef(object);
+
+ ASSERT(m_javaServiceObject);
+ return m_javaServiceObject;
+}
+
+void DeviceMotionClientImpl::releaseJavaInstance()
+{
+ ASSERT(m_javaServiceObject);
+ getJNIEnv()->DeleteGlobalRef(m_javaServiceObject);
+}
+
+void DeviceMotionClientImpl::startUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodStart]);
+}
+
+void DeviceMotionClientImpl::stopUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodStop]);
+}
+
+void DeviceMotionClientImpl::onMotionChange(PassRefPtr<DeviceMotionData> motion)
+{
+ m_lastMotion = motion;
+ m_controller->didChangeDeviceMotion(m_lastMotion.get());
+}
+
+void DeviceMotionClientImpl::suspend()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodSuspend]);
+}
+
+void DeviceMotionClientImpl::resume()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodResume]);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionClientImpl.h b/Source/WebKit/android/jni/DeviceMotionClientImpl.h
new file mode 100644
index 0000000..c979098
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionClientImpl.h
@@ -0,0 +1,70 @@
+/*
+ * 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 DeviceMotionClientImpl_h
+#define DeviceMotionClientImpl_h
+
+#include <DeviceMotionClient.h>
+#include <DeviceMotionData.h>
+#include <JNIUtility.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class DeviceMotionAndOrientationManager;
+class WebViewCore;
+
+class DeviceMotionClientImpl : public DeviceMotionClient {
+public:
+ DeviceMotionClientImpl(WebViewCore*);
+ virtual ~DeviceMotionClientImpl();
+
+ void onMotionChange(PassRefPtr<DeviceMotionData>);
+ void suspend();
+ void resume();
+
+ // DeviceMotionClient methods
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual DeviceMotionData* currentDeviceMotion() const { return m_lastMotion.get(); }
+ virtual void setController(DeviceMotionController* controller) { m_controller = controller; }
+ virtual void deviceMotionControllerDestroyed() { }
+
+private:
+ jobject getJavaInstance();
+ void releaseJavaInstance();
+
+ WebViewCore* m_webViewCore;
+ jobject m_javaServiceObject;
+ DeviceMotionController* m_controller;
+ RefPtr<DeviceMotionData> m_lastMotion;
+};
+
+} // namespace android
+
+#endif // DeviceMotionClientImpl_h
diff --git a/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp b/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp
new file mode 100644
index 0000000..bf3b3c3
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "DeviceOrientationClientImpl.h"
+
+#include "WebViewCore.h"
+#include <DeviceOrientationController.h>
+#include <Frame.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using JSC::Bindings::getJNIEnv;
+
+enum javaDeviceOrientationServiceClassMethods {
+ DeviceOrientationServiceMethodStart = 0,
+ DeviceOrientationServiceMethodStop,
+ DeviceOrientationServiceMethodSuspend,
+ DeviceOrientationServiceMethodResume,
+ DeviceOrientationServiceMethodCount
+};
+static jmethodID javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodCount];
+
+DeviceOrientationClientImpl::DeviceOrientationClientImpl(WebViewCore* webViewCore)
+ : m_webViewCore(webViewCore)
+ , m_javaDeviceOrientationServiceObject(0)
+{
+ ASSERT(m_webViewCore);
+}
+
+DeviceOrientationClientImpl::~DeviceOrientationClientImpl()
+{
+ releaseJavaInstance();
+}
+
+jobject DeviceOrientationClientImpl::getJavaInstance()
+{
+ // Lazily get the Java object. We can't do this until the WebViewCore is all
+ // set up.
+ if (m_javaDeviceOrientationServiceObject)
+ return m_javaDeviceOrientationServiceObject;
+
+ JNIEnv* env = getJNIEnv();
+
+ ASSERT(m_webViewCore);
+ jobject object = m_webViewCore->getDeviceOrientationService();
+
+ // Get the Java DeviceOrientationService class.
+ jclass javaDeviceOrientationServiceClass = env->GetObjectClass(object);
+ ASSERT(javaDeviceOrientationServiceClass);
+
+ // Set up the methods we wish to call on the Java DeviceOrientationService
+ // class.
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStart] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "start", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStop] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "stop", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodSuspend] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "suspend", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodResume] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "resume", "()V");
+ env->DeleteLocalRef(javaDeviceOrientationServiceClass);
+
+ m_javaDeviceOrientationServiceObject = getJNIEnv()->NewGlobalRef(object);
+ getJNIEnv()->DeleteLocalRef(object);
+
+ ASSERT(m_javaDeviceOrientationServiceObject);
+ return m_javaDeviceOrientationServiceObject;
+}
+
+void DeviceOrientationClientImpl::releaseJavaInstance()
+{
+ ASSERT(m_javaDeviceOrientationServiceObject);
+ getJNIEnv()->DeleteGlobalRef(m_javaDeviceOrientationServiceObject);
+}
+
+void DeviceOrientationClientImpl::startUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStart]);
+}
+
+void DeviceOrientationClientImpl::stopUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStop]);
+}
+
+void DeviceOrientationClientImpl::onOrientationChange(PassRefPtr<DeviceOrientation> orientation)
+{
+ m_lastOrientation = orientation;
+ m_controller->didChangeDeviceOrientation(m_lastOrientation.get());
+}
+
+void DeviceOrientationClientImpl::suspend()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodSuspend]);
+}
+
+void DeviceOrientationClientImpl::resume()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodResume]);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceOrientationClientImpl.h b/Source/WebKit/android/jni/DeviceOrientationClientImpl.h
new file mode 100644
index 0000000..0e3f6b3
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceOrientationClientImpl.h
@@ -0,0 +1,70 @@
+/*
+ * 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 DeviceOrientationClientImpl_h
+#define DeviceOrientationClientImpl_h
+
+#include <DeviceOrientation.h>
+#include <DeviceOrientationClient.h>
+#include <JNIUtility.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class DeviceMotionAndOrientationManager;
+class WebViewCore;
+
+class DeviceOrientationClientImpl : public DeviceOrientationClient {
+public:
+ DeviceOrientationClientImpl(WebViewCore*);
+ virtual ~DeviceOrientationClientImpl();
+
+ void onOrientationChange(PassRefPtr<DeviceOrientation>);
+ void suspend();
+ void resume();
+
+ // DeviceOrientationClient methods
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual DeviceOrientation* lastOrientation() const { return m_lastOrientation.get(); }
+ virtual void setController(DeviceOrientationController* controller) { m_controller = controller; }
+ virtual void deviceOrientationControllerDestroyed() { }
+
+private:
+ jobject getJavaInstance();
+ void releaseJavaInstance();
+
+ WebViewCore* m_webViewCore;
+ jobject m_javaDeviceOrientationServiceObject;
+ DeviceOrientationController* m_controller;
+ RefPtr<DeviceOrientation> m_lastOrientation;
+};
+
+} // namespace android
+
+#endif // DeviceOrientationClientImpl_h
diff --git a/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
new file mode 100755
index 0000000..a366601
--- /dev/null
+++ b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "config.h"
+
+#include <JNIHelp.h> // For jniRegisterNativeMethods
+#include "GeolocationPermissions.h"
+#include "WebCoreJni.h" // For jstringToWtfString
+
+
+/**
+ * This file provides a set of functions to bridge between the Java and C++
+ * GeolocationPermissions classes. The java GeolocationPermissions object calls
+ * the functions provided here, which in turn call static methods on the C++
+ * GeolocationPermissions class.
+ */
+
+namespace android {
+
+static jobject getOrigins(JNIEnv* env, jobject obj)
+{
+ GeolocationPermissions::OriginSet origins = GeolocationPermissions::getOrigins();
+
+ jclass setClass = env->FindClass("java/util/HashSet");
+ jmethodID constructor = env->GetMethodID(setClass, "<init>", "()V");
+ jmethodID addMethod = env->GetMethodID(setClass, "add", "(Ljava/lang/Object;)Z");
+ jobject set = env->NewObject(setClass, constructor);
+ env->DeleteLocalRef(setClass);
+
+ GeolocationPermissions::OriginSet::const_iterator end = origins.end();
+ for (GeolocationPermissions::OriginSet::const_iterator iter = origins.begin(); iter != end; ++iter) {
+ jstring originString = wtfStringToJstring(env, *iter);
+ env->CallBooleanMethod(set, addMethod, originString);
+ env->DeleteLocalRef(originString);
+ }
+ return set;
+}
+
+static bool getAllowed(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ return GeolocationPermissions::getAllowed(originString);
+}
+
+static void clear(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ GeolocationPermissions::clear(originString);
+}
+
+static void allow(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ GeolocationPermissions::allow(originString);
+}
+
+static void clearAll(JNIEnv* env, jobject obj)
+{
+ GeolocationPermissions::clearAll();
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gGeolocationPermissionsMethods[] = {
+ { "nativeGetOrigins", "()Ljava/util/Set;",
+ (void*) getOrigins },
+ { "nativeGetAllowed", "(Ljava/lang/String;)Z",
+ (void*) getAllowed },
+ { "nativeClear", "(Ljava/lang/String;)V",
+ (void*) clear },
+ { "nativeAllow", "(Ljava/lang/String;)V",
+ (void*) allow },
+ { "nativeClearAll", "()V",
+ (void*) clearAll }
+};
+
+int registerGeolocationPermissions(JNIEnv* env)
+{
+ const char* kGeolocationPermissionsClass = "android/webkit/GeolocationPermissions";
+#ifndef NDEBUG
+ jclass geolocationPermissions = env->FindClass(kGeolocationPermissionsClass);
+ LOG_ASSERT(geolocationPermissions, "Unable to find class");
+ env->DeleteLocalRef(geolocationPermissions);
+#endif
+
+ return jniRegisterNativeMethods(env, kGeolocationPermissionsClass,
+ gGeolocationPermissionsMethods, NELEM(gGeolocationPermissionsMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/JavaBridge.cpp b/Source/WebKit/android/jni/JavaBridge.cpp
new file mode 100644
index 0000000..2fa12fc
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaBridge.cpp
@@ -0,0 +1,514 @@
+/*
+ * 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 "MemoryCache.h"
+#include "Connection.h"
+#include "CookieClient.h"
+#include "FileSystemClient.h"
+#include "JavaSharedClient.h"
+#include "KeyGeneratorClient.h"
+#include "KURL.h"
+#include "NetworkStateNotifier.h"
+#include "PackageNotifier.h"
+#include "Page.h"
+#include "PluginClient.h"
+#include "PluginDatabase.h"
+#include "Timer.h"
+#include "TimerClient.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "WebCache.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SkUtils.h>
+#include <jni.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/AtomicString.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static jfieldID gJavaBridge_ObjectID;
+
+// ----------------------------------------------------------------------------
+
+class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient, public FileSystemClient
+{
+public:
+ JavaBridge(JNIEnv* env, jobject obj);
+ virtual ~JavaBridge();
+
+ /*
+ * WebCore -> Java API
+ */
+ virtual void setSharedTimer(long long timemillis);
+ virtual void stopSharedTimer();
+
+ virtual void setCookies(WebCore::KURL const& url, WTF::String const& value);
+ virtual WTF::String cookies(WebCore::KURL const& url);
+ virtual bool cookiesEnabled();
+
+ virtual WTF::Vector<WTF::String> getPluginDirectories();
+ virtual WTF::String getPluginSharedDataDirectory();
+
+ virtual WTF::Vector<String> getSupportedKeyStrengthList();
+ virtual WTF::String getSignedPublicKeyAndChallengeString(unsigned index,
+ const WTF::String& challenge, const WebCore::KURL& url);
+ virtual WTF::String resolveFilePathForContentUri(const WTF::String& uri);
+
+ ////////////////////////////////////////////
+
+ virtual void setSharedTimerCallback(void (*f)());
+
+ ////////////////////////////////////////////
+
+ virtual void signalServiceFuncPtrQueue();
+
+ // jni functions
+ static void Constructor(JNIEnv* env, jobject obj);
+ static void Finalize(JNIEnv* env, jobject obj);
+ static void SharedTimerFired(JNIEnv* env, jobject);
+ static void SetCacheSize(JNIEnv* env, jobject obj, jint bytes);
+ static void SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online);
+ static void SetNetworkType(JNIEnv* env, jobject obj, jstring type, jstring subtype);
+ static void SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer);
+ static void ServiceFuncPtrQueue(JNIEnv*);
+ static void UpdatePluginDirectories(JNIEnv* env, jobject obj, jobjectArray array, jboolean reload);
+ static void AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames);
+ static void AddPackageName(JNIEnv* env, jobject obj, jstring packageName);
+ static void RemovePackageName(JNIEnv* env, jobject obj, jstring packageName);
+ static void UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy);
+
+
+private:
+ jweak mJavaObject;
+ jmethodID mSetSharedTimer;
+ jmethodID mStopSharedTimer;
+ jmethodID mSetCookies;
+ jmethodID mCookies;
+ jmethodID mCookiesEnabled;
+ jmethodID mGetPluginDirectories;
+ jmethodID mGetPluginSharedDataDirectory;
+ jmethodID mSignalFuncPtrQueue;
+ jmethodID mGetKeyStrengthList;
+ jmethodID mGetSignedPublicKey;
+ jmethodID mResolveFilePathForContentUri;
+ AutoJObject javaObject(JNIEnv* env) { return getRealObject(env, mJavaObject); }
+};
+
+static void (*sSharedTimerFiredCallback)();
+
+JavaBridge::JavaBridge(JNIEnv* env, jobject obj)
+{
+ mJavaObject = env->NewWeakGlobalRef(obj);
+ jclass clazz = env->GetObjectClass(obj);
+
+ mSetSharedTimer = env->GetMethodID(clazz, "setSharedTimer", "(J)V");
+ mStopSharedTimer = env->GetMethodID(clazz, "stopSharedTimer", "()V");
+ mSetCookies = env->GetMethodID(clazz, "setCookies", "(Ljava/lang/String;Ljava/lang/String;)V");
+ mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;");
+ mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z");
+ mGetPluginDirectories = env->GetMethodID(clazz, "getPluginDirectories", "()[Ljava/lang/String;");
+ mGetPluginSharedDataDirectory = env->GetMethodID(clazz, "getPluginSharedDataDirectory", "()Ljava/lang/String;");
+ mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V");
+ mGetKeyStrengthList = env->GetMethodID(clazz, "getKeyStrengthList", "()[Ljava/lang/String;");
+ mGetSignedPublicKey = env->GetMethodID(clazz, "getSignedPublicKey", "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ mResolveFilePathForContentUri = env->GetMethodID(clazz, "resolveFilePathForContentUri", "(Ljava/lang/String;)Ljava/lang/String;");
+ env->DeleteLocalRef(clazz);
+
+ 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);
+ JavaSharedClient::SetPluginClient(this);
+ JavaSharedClient::SetKeyGeneratorClient(this);
+ JavaSharedClient::SetFileSystemClient(this);
+}
+
+JavaBridge::~JavaBridge()
+{
+ if (mJavaObject) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(mJavaObject);
+ mJavaObject = 0;
+ }
+
+ JavaSharedClient::SetTimerClient(NULL);
+ JavaSharedClient::SetCookieClient(NULL);
+ JavaSharedClient::SetPluginClient(NULL);
+ JavaSharedClient::SetKeyGeneratorClient(NULL);
+ JavaSharedClient::SetFileSystemClient(NULL);
+}
+
+void
+JavaBridge::setSharedTimer(long long timemillis)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis);
+}
+
+void
+JavaBridge::stopSharedTimer()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ env->CallVoidMethod(obj.get(), mStopSharedTimer);
+}
+
+void
+JavaBridge::setCookies(WebCore::KURL const& url, WTF::String const& value)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ const WTF::String& urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jstring jValueStr = wtfStringToJstring(env, value);
+
+ AutoJObject obj = javaObject(env);
+ env->CallVoidMethod(obj.get(), mSetCookies, jUrlStr, jValueStr);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jValueStr);
+}
+
+WTF::String
+JavaBridge::cookies(WebCore::KURL const& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ const WTF::String& urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+
+ AutoJObject obj = javaObject(env);
+ jstring string = (jstring)(env->CallObjectMethod(obj.get(), mCookies, jUrlStr));
+
+ WTF::String ret = jstringToWtfString(env, string);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(string);
+ return ret;
+}
+
+bool
+JavaBridge::cookiesEnabled()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled);
+ return (ret != 0);
+}
+
+WTF::Vector<WTF::String>
+JavaBridge::getPluginDirectories()
+{
+ WTF::Vector<WTF::String> directories;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ jobjectArray array = (jobjectArray)
+ env->CallObjectMethod(obj.get(), mGetPluginDirectories);
+ int count = env->GetArrayLength(array);
+ for (int i = 0; i < count; i++) {
+ jstring dir = (jstring) env->GetObjectArrayElement(array, i);
+ directories.append(jstringToWtfString(env, dir));
+ env->DeleteLocalRef(dir);
+ }
+ env->DeleteLocalRef(array);
+ checkException(env);
+ return directories;
+}
+
+WTF::String
+JavaBridge::getPluginSharedDataDirectory()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ jstring ret = (jstring)env->CallObjectMethod(obj.get(), mGetPluginSharedDataDirectory);
+ WTF::String path = jstringToWtfString(env, ret);
+ checkException(env);
+ return path;
+}
+
+void
+JavaBridge::setSharedTimerCallback(void (*f)())
+{
+ LOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f,
+ "Shared timer callback may already be set or null!");
+
+ sSharedTimerFiredCallback = f;
+}
+
+void JavaBridge::signalServiceFuncPtrQueue()
+{
+ // In order to signal the main thread we must go through JNI. This
+ // is the only usage on most threads, so we need to ensure a JNI
+ // environment is setup.
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue);
+}
+
+WTF::Vector<WTF::String>JavaBridge::getSupportedKeyStrengthList() {
+ WTF::Vector<WTF::String> list;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ jobjectArray array = (jobjectArray) env->CallObjectMethod(obj.get(),
+ mGetKeyStrengthList);
+ int count = env->GetArrayLength(array);
+ for (int i = 0; i < count; ++i) {
+ jstring keyStrength = (jstring) env->GetObjectArrayElement(array, i);
+ list.append(jstringToWtfString(env, keyStrength));
+ env->DeleteLocalRef(keyStrength);
+ }
+ env->DeleteLocalRef(array);
+ checkException(env);
+ return list;
+}
+
+WTF::String JavaBridge::getSignedPublicKeyAndChallengeString(unsigned index,
+ const WTF::String& challenge, const WebCore::KURL& url) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jChallenge = wtfStringToJstring(env, challenge);
+ const WTF::String& urlStr = url.string();
+ jstring jUrl = wtfStringToJstring(env, urlStr);
+ AutoJObject obj = javaObject(env);
+ jstring key = (jstring) env->CallObjectMethod(obj.get(),
+ mGetSignedPublicKey, index, jChallenge, jUrl);
+ WTF::String ret = jstringToWtfString(env, key);
+ env->DeleteLocalRef(jChallenge);
+ env->DeleteLocalRef(jUrl);
+ env->DeleteLocalRef(key);
+ return ret;
+}
+
+WTF::String JavaBridge::resolveFilePathForContentUri(const WTF::String& uri) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUri = wtfStringToJstring(env, uri);
+ AutoJObject obj = javaObject(env);
+ jstring path = static_cast<jstring>(env->CallObjectMethod(obj.get(), mResolveFilePathForContentUri, jUri));
+ WTF::String ret = jstringToWtfString(env, path);
+ env->DeleteLocalRef(jUri);
+ env->DeleteLocalRef(path);
+ return ret;
+}
+
+// ----------------------------------------------------------------------------
+
+void JavaBridge::Constructor(JNIEnv* env, jobject obj)
+{
+ JavaBridge* javaBridge = new JavaBridge(env, obj);
+ env->SetIntField(obj, gJavaBridge_ObjectID, (jint)javaBridge);
+}
+
+void JavaBridge::Finalize(JNIEnv* env, jobject obj)
+{
+ JavaBridge* javaBridge = (JavaBridge*)
+ (env->GetIntField(obj, gJavaBridge_ObjectID));
+ 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);
+}
+
+// we don't use the java bridge object, as we're just looking at a global
+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)
+{
+ WebCore::cache()->setCapacities(0, bytes/2, bytes);
+}
+
+void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online)
+{
+ WebCore::networkStateNotifier().networkStateChange(online);
+}
+
+void JavaBridge::SetNetworkType(JNIEnv* env, jobject obj, jstring javatype, jstring javasubtype)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, wifi, ("wifi"));
+ DEFINE_STATIC_LOCAL(AtomicString, mobile, ("mobile"));
+ DEFINE_STATIC_LOCAL(AtomicString, mobileSupl, ("mobile_supl"));
+ DEFINE_STATIC_LOCAL(AtomicString, gprs, ("gprs"));
+ DEFINE_STATIC_LOCAL(AtomicString, edge, ("edge"));
+ DEFINE_STATIC_LOCAL(AtomicString, umts, ("umts"));
+
+ String type = jstringToWtfString(env, javatype);
+ String subtype = jstringToWtfString(env, javasubtype);
+ Connection::ConnectionType connectionType = Connection::UNKNOWN;
+ if (type == wifi)
+ connectionType = Connection::WIFI;
+ else if (type == mobile || type == mobileSupl) {
+ if (subtype == edge || subtype == gprs)
+ connectionType = Connection::CELL_2G;
+ else if (subtype == umts)
+ connectionType = Connection::CELL_3G;
+ }
+ WebCore::networkStateNotifier().networkTypeChange(connectionType);
+}
+
+void JavaBridge::ServiceFuncPtrQueue(JNIEnv*)
+{
+ JavaSharedClient::ServiceFunctionPtrQueue();
+}
+
+void JavaBridge::UpdatePluginDirectories(JNIEnv* env, jobject obj,
+ jobjectArray array, jboolean reload) {
+ WTF::Vector<WTF::String> directories;
+ int count = env->GetArrayLength(array);
+ for (int i = 0; i < count; i++) {
+ jstring dir = (jstring) env->GetObjectArrayElement(array, i);
+ directories.append(jstringToWtfString(env, dir));
+ env->DeleteLocalRef(dir);
+ }
+ checkException(env);
+ WebCore::PluginDatabase *pluginDatabase =
+ WebCore::PluginDatabase::installedPlugins();
+ pluginDatabase->setPluginDirectories(directories);
+ // refreshPlugins() should refresh both PluginDatabase and Page's PluginData
+ WebCore::Page::refreshPlugins(reload);
+}
+
+void JavaBridge::AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames)
+{
+ if (!packageNames)
+ return;
+
+ // dalvikvm will raise exception if any of these fail
+ jclass setClass = env->FindClass("java/util/Set");
+ jmethodID iterator = env->GetMethodID(setClass, "iterator",
+ "()Ljava/util/Iterator;");
+ jobject iter = env->CallObjectMethod(packageNames, iterator);
+
+ jclass iteratorClass = env->FindClass("java/util/Iterator");
+ jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+ jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
+
+ HashSet<WTF::String> namesSet;
+ while (env->CallBooleanMethod(iter, hasNext)) {
+ jstring name = static_cast<jstring>(env->CallObjectMethod(iter, next));
+ namesSet.add(jstringToWtfString(env, name));
+ env->DeleteLocalRef(name);
+ }
+
+ packageNotifier().addPackageNames(namesSet);
+
+ env->DeleteLocalRef(iteratorClass);
+ env->DeleteLocalRef(iter);
+ env->DeleteLocalRef(setClass);
+}
+
+void JavaBridge::AddPackageName(JNIEnv* env, jobject obj, jstring packageName)
+{
+ packageNotifier().addPackageName(jstringToWtfString(env, packageName));
+}
+
+void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName)
+{
+ packageNotifier().removePackageName(jstringToWtfString(env, packageName));
+}
+
+void JavaBridge::UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy)
+{
+#if USE(CHROME_NETWORK_STACK)
+ std::string proxy = jstringToStdString(env, newProxy);
+ WebCache::get(false)->proxy()->UpdateProxySettings(proxy);
+ WebCache::get(true)->proxy()->UpdateProxySettings(proxy);
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gWebCoreJavaBridgeMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeConstructor", "()V",
+ (void*) JavaBridge::Constructor },
+ { "nativeFinalize", "()V",
+ (void*) JavaBridge::Finalize },
+ { "sharedTimerFired", "()V",
+ (void*) JavaBridge::SharedTimerFired },
+ { "setCacheSize", "(I)V",
+ (void*) JavaBridge::SetCacheSize },
+ { "setNetworkOnLine", "(Z)V",
+ (void*) JavaBridge::SetNetworkOnLine },
+ { "setNetworkType", "(Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) JavaBridge::SetNetworkType },
+ { "nativeServiceFuncPtrQueue", "()V",
+ (void*) JavaBridge::ServiceFuncPtrQueue },
+ { "nativeUpdatePluginDirectories", "([Ljava/lang/String;Z)V",
+ (void*) JavaBridge::UpdatePluginDirectories },
+ { "addPackageNames", "(Ljava/util/Set;)V",
+ (void*) JavaBridge::AddPackageNames },
+ { "addPackageName", "(Ljava/lang/String;)V",
+ (void*) JavaBridge::AddPackageName },
+ { "removePackageName", "(Ljava/lang/String;)V",
+ (void*) JavaBridge::RemovePackageName },
+ { "updateProxy", "(Ljava/lang/String;)V",
+ (void*) JavaBridge::UpdateProxy }
+};
+
+int registerJavaBridge(JNIEnv* env)
+{
+ jclass javaBridge = env->FindClass("android/webkit/JWebCoreJavaBridge");
+ LOG_FATAL_IF(javaBridge == NULL, "Unable to find class android/webkit/JWebCoreJavaBridge");
+ gJavaBridge_ObjectID = env->GetFieldID(javaBridge, "mNativeBridge", "I");
+ LOG_FATAL_IF(gJavaBridge_ObjectID == NULL, "Unable to find android/webkit/JWebCoreJavaBridge.mNativeBridge");
+ env->DeleteLocalRef(javaBridge);
+
+ return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge",
+ gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/JavaSharedClient.cpp b/Source/WebKit/android/jni/JavaSharedClient.cpp
new file mode 100644
index 0000000..e884c99
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaSharedClient.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "config.h"
+#include "FileSystemClient.h"
+#include "JavaSharedClient.h"
+#include "TimerClient.h"
+#include "SkDeque.h"
+#include "SkThread.h"
+
+namespace android {
+ TimerClient* JavaSharedClient::GetTimerClient()
+ {
+ return gTimerClient;
+ }
+
+ CookieClient* JavaSharedClient::GetCookieClient()
+ {
+ return gCookieClient;
+ }
+
+ PluginClient* JavaSharedClient::GetPluginClient()
+ {
+ return gPluginClient;
+ }
+
+ KeyGeneratorClient* JavaSharedClient::GetKeyGeneratorClient()
+ {
+ return gKeyGeneratorClient;
+ }
+
+ FileSystemClient* JavaSharedClient::GetFileSystemClient()
+ {
+ return gFileSystemClient;
+ }
+
+ void JavaSharedClient::SetTimerClient(TimerClient* client)
+ {
+ gTimerClient = client;
+ }
+
+ void JavaSharedClient::SetCookieClient(CookieClient* client)
+ {
+ gCookieClient = client;
+ }
+
+ void JavaSharedClient::SetPluginClient(PluginClient* client)
+ {
+ gPluginClient = client;
+ }
+
+ void JavaSharedClient::SetKeyGeneratorClient(KeyGeneratorClient* client)
+ {
+ gKeyGeneratorClient = client;
+ }
+
+ void JavaSharedClient::SetFileSystemClient(FileSystemClient* client)
+ {
+ gFileSystemClient = client;
+ }
+
+ TimerClient* JavaSharedClient::gTimerClient = NULL;
+ CookieClient* JavaSharedClient::gCookieClient = NULL;
+ PluginClient* JavaSharedClient::gPluginClient = NULL;
+ KeyGeneratorClient* JavaSharedClient::gKeyGeneratorClient = NULL;
+ FileSystemClient* JavaSharedClient::gFileSystemClient = NULL;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct FuncPtrRec {
+ void (*fProc)(void* payload);
+ void* fPayload;
+ };
+
+ static SkMutex gFuncPtrQMutex;
+ static SkDeque gFuncPtrQ(sizeof(FuncPtrRec));
+
+ void JavaSharedClient::EnqueueFunctionPtr(void (*proc)(void* payload),
+ void* payload)
+ {
+ gFuncPtrQMutex.acquire();
+
+ FuncPtrRec* rec = (FuncPtrRec*)gFuncPtrQ.push_back();
+ rec->fProc = proc;
+ rec->fPayload = payload;
+
+ gFuncPtrQMutex.release();
+
+ gTimerClient->signalServiceFuncPtrQueue();
+ }
+
+ void JavaSharedClient::ServiceFunctionPtrQueue()
+ {
+ 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);
+ }
+ }
+}
diff --git a/Source/WebKit/android/jni/JavaSharedClient.h b/Source/WebKit/android/jni/JavaSharedClient.h
new file mode 100644
index 0000000..9a09280
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaSharedClient.h
@@ -0,0 +1,65 @@
+/*
+ * 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 JAVA_SHARED_CLIENT_H
+#define JAVA_SHARED_CLIENT_H
+
+namespace android {
+
+ class TimerClient;
+ class CookieClient;
+ class PluginClient;
+ class KeyGeneratorClient;
+ class FileSystemClient;
+
+ class JavaSharedClient
+ {
+ public:
+ static TimerClient* GetTimerClient();
+ static CookieClient* GetCookieClient();
+ static PluginClient* GetPluginClient();
+ static KeyGeneratorClient* GetKeyGeneratorClient();
+ static FileSystemClient* GetFileSystemClient();
+
+ static void SetTimerClient(TimerClient* client);
+ static void SetCookieClient(CookieClient* client);
+ static void SetPluginClient(PluginClient* client);
+ static void SetKeyGeneratorClient(KeyGeneratorClient* client);
+ static void SetFileSystemClient(FileSystemClient* client);
+
+ // can be called from any thread, to be executed in webkit thread
+ static void EnqueueFunctionPtr(void (*proc)(void*), void* payload);
+ // only call this from webkit thread
+ static void ServiceFunctionPtrQueue();
+
+ private:
+ static TimerClient* gTimerClient;
+ static CookieClient* gCookieClient;
+ static PluginClient* gPluginClient;
+ static KeyGeneratorClient* gKeyGeneratorClient;
+ static FileSystemClient* gFileSystemClient;
+ };
+}
+#endif
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
new file mode 100644
index 0000000..40f8cef
--- /dev/null
+++ b/Source/WebKit/android/jni/MIMETypeRegistry.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "MIMETypeRegistry.h"
+
+#include "PlatformString.h"
+#include "WebCoreJni.h"
+
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+using namespace android;
+
+namespace WebCore {
+
+String MIMETypeRegistry::getMIMETypeForExtension(const String& ext)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass mimeClass = env->FindClass("android/webkit/MimeTypeMap");
+ LOG_ASSERT(mimeClass, "Could not find class MimeTypeMap");
+ jmethodID mimeTypeFromExtension = env->GetStaticMethodID(mimeClass,
+ "mimeTypeFromExtension",
+ "(Ljava/lang/String;)Ljava/lang/String;");
+ LOG_ASSERT(mimeTypeFromExtension,
+ "Could not find method mimeTypeFromExtension");
+ jstring extString = wtfStringToJstring(env, ext);
+ jobject mimeType = env->CallStaticObjectMethod(mimeClass,
+ mimeTypeFromExtension, extString);
+ String result = android::jstringToWtfString(env, (jstring) mimeType);
+ env->DeleteLocalRef(mimeClass);
+ env->DeleteLocalRef(extString);
+ env->DeleteLocalRef(mimeType);
+ return result;
+}
+
+bool MIMETypeRegistry::isApplicationPluginMIMEType(const String&)
+{
+ return false;
+}
+
+}
diff --git a/Source/WebKit/android/jni/MockGeolocation.cpp b/Source/WebKit/android/jni/MockGeolocation.cpp
new file mode 100755
index 0000000..1370715
--- /dev/null
+++ b/Source/WebKit/android/jni/MockGeolocation.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// The functions in this file are used to configure the mock GeolocationService
+// for the LayoutTests.
+
+#include "config.h"
+
+#include "Coordinates.h"
+#include "GeolocationServiceMock.h"
+#include "Geoposition.h"
+#include "JavaSharedClient.h"
+#include "PositionError.h"
+#include "WebCoreJni.h"
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <wtf/CurrentTime.h>
+
+using namespace WebCore;
+
+namespace android {
+
+static const char* javaMockGeolocationClass = "android/webkit/MockGeolocation";
+
+static void setPosition(JNIEnv* env, jobject, double latitude, double longitude, double accuracy)
+{
+ RefPtr<Coordinates> coordinates = Coordinates::create(latitude,
+ longitude,
+ false, 0.0, // altitude,
+ accuracy,
+ false, 0.0, // altitudeAccuracy,
+ false, 0.0, // heading
+ false, 0.0); // speed
+ RefPtr<Geoposition> position = Geoposition::create(coordinates.release(), WTF::currentTimeMS());
+ GeolocationServiceMock::setPosition(position.release());
+}
+
+static void setError(JNIEnv* env, jobject, int code, jstring message)
+{
+ PositionError::ErrorCode codeEnum = static_cast<PositionError::ErrorCode>(code);
+ String messageString = jstringToWtfString(env, message);
+ RefPtr<PositionError> error = PositionError::create(codeEnum, messageString);
+ GeolocationServiceMock::setError(error.release());
+}
+
+static JNINativeMethod gMockGeolocationMethods[] = {
+ { "nativeSetPosition", "(DDD)V", (void*) setPosition },
+ { "nativeSetError", "(ILjava/lang/String;)V", (void*) setError }
+};
+
+int registerMockGeolocation(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass mockGeolocation = env->FindClass(javaMockGeolocationClass);
+ LOG_ASSERT(mockGeolocation, "Unable to find class");
+ env->DeleteLocalRef(mockGeolocation);
+#endif
+
+ return jniRegisterNativeMethods(env, javaMockGeolocationClass, gMockGeolocationMethods, NELEM(gMockGeolocationMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/PictureSet.cpp b/Source/WebKit/android/jni/PictureSet.cpp
new file mode 100644
index 0000000..6dafd26
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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
+
+#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()
+{
+ mWidth = mHeight = 0;
+}
+
+PictureSet::~PictureSet()
+{
+ clear();
+}
+
+void PictureSet::add(const Pictures* temp)
+{
+ Pictures pictureAndBounds = *temp;
+ SkSafeRef(pictureAndBounds.mPicture);
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+}
+
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split, bool empty)
+{
+ DBG_SET_LOGD("%p area={%d,%d,r=%d,b=%d} pict=%p elapsed=%d split=%d", this,
+ area.getBounds().fLeft, area.getBounds().fTop,
+ area.getBounds().fRight, area.getBounds().fBottom, picture,
+ elapsed, split);
+ SkSafeRef(picture);
+ /* if nothing is drawn beneath part of the new picture, mark it as a base */
+ SkRegion diff = SkRegion(area);
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++)
+ diff.op(working->mArea, SkRegion::kDifference_Op);
+ Pictures pictureAndBounds = {area, picture, area.getBounds(),
+ elapsed, split, false, diff.isEmpty() == false, empty};
+ mPictures.append(pictureAndBounds);
+}
+
+/*
+Pictures are discarded when they are fully drawn over.
+When a picture is partially drawn over, it is discarded if it is not a base, and
+its rectangular bounds is reduced if it is a base.
+*/
+bool PictureSet::build()
+{
+ bool rebuild = false;
+ DBG_SET_LOGD("%p", this);
+ // walk pictures back to front, removing or trimming obscured ones
+ SkRegion drawn;
+ SkRegion inval;
+ Pictures* first = mPictures.begin();
+ Pictures* last = mPictures.end();
+ Pictures* working;
+ bool checkForNewBases = false;
+ for (working = last; working != first; ) {
+ --working;
+ SkRegion& area = working->mArea;
+ SkRegion visibleArea(area);
+ visibleArea.op(drawn, SkRegion::kDifference_Op);
+#if PICTURE_SET_DEBUG
+ const SkIRect& a = area.getBounds();
+ const SkIRect& d = drawn.getBounds();
+ const SkIRect& i = inval.getBounds();
+ const SkIRect& v = visibleArea.getBounds();
+ DBG_SET_LOGD("%p [%d] area={%d,%d,r=%d,b=%d} drawn={%d,%d,r=%d,b=%d}"
+ " inval={%d,%d,r=%d,b=%d} vis={%d,%d,r=%d,b=%d}",
+ this, working - first,
+ a.fLeft, a.fTop, a.fRight, a.fBottom,
+ d.fLeft, d.fTop, d.fRight, d.fBottom,
+ i.fLeft, i.fTop, i.fRight, i.fBottom,
+ v.fLeft, v.fTop, v.fRight, v.fBottom);
+#endif
+ bool tossPicture = false;
+ if (working->mBase == false) {
+ if (area != visibleArea) {
+ if (visibleArea.isEmpty() == false) {
+ DBG_SET_LOGD("[%d] partially overdrawn", working - first);
+ inval.op(visibleArea, SkRegion::kUnion_Op);
+ } else
+ DBG_SET_LOGD("[%d] fully hidden", working - first);
+ area.setEmpty();
+ tossPicture = true;
+ }
+ } else {
+ const SkIRect& visibleBounds = visibleArea.getBounds();
+ const SkIRect& areaBounds = area.getBounds();
+ if (visibleBounds != areaBounds) {
+ DBG_SET_LOGD("[%d] base to be reduced", working - first);
+ area.setRect(visibleBounds);
+ checkForNewBases = tossPicture = true;
+ }
+ if (area.intersects(inval)) {
+ DBG_SET_LOGD("[%d] base to be redrawn", working - first);
+ tossPicture = true;
+ }
+ }
+ if (tossPicture) {
+ SkSafeUnref(working->mPicture);
+ working->mPicture = NULL; // mark to redraw
+ }
+ if (working->mPicture == NULL) // may have been set to null elsewhere
+ rebuild = true;
+ drawn.op(area, SkRegion::kUnion_Op);
+ }
+ // collapse out empty regions
+ Pictures* writer = first;
+ for (working = first; working != last; working++) {
+ if (working->mArea.isEmpty())
+ continue;
+ *writer++ = *working;
+ }
+#if PICTURE_SET_DEBUG
+ if ((unsigned) (writer - first) != mPictures.size())
+ DBG_SET_LOGD("shrink=%d (was %d)", writer - first, mPictures.size());
+#endif
+ mPictures.shrink(writer - first);
+ /* When a base is discarded because it was entirely drawn over, all
+ remaining pictures are checked to see if one has become a base. */
+ if (checkForNewBases) {
+ drawn.setEmpty();
+ Pictures* last = mPictures.end();
+ for (working = mPictures.begin(); working != last; working++) {
+ SkRegion& area = working->mArea;
+ if (drawn.contains(working->mArea) == false) {
+ working->mBase = true;
+ DBG_SET_LOGD("[%d] new base", working - mPictures.begin());
+ }
+ drawn.op(working->mArea, SkRegion::kUnion_Op);
+ }
+ }
+ validate(__FUNCTION__);
+ return rebuild;
+}
+
+void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
+{
+ 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);
+ if (mWidth == width && height > mHeight) { // only grew vertically
+ SkIRect rect;
+ rect.set(0, mHeight, width, height - mHeight);
+ inval->op(rect, SkRegion::kUnion_Op);
+ } else {
+ clear(); // if both width/height changed, clear the old cache
+ inval->setRect(0, 0, width, height);
+ }
+ mWidth = width;
+ mHeight = height;
+}
+
+void PictureSet::clear()
+{
+ DBG_SET_LOG("");
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++) {
+ working->mArea.setEmpty();
+ SkSafeUnref(working->mPicture);
+ }
+ mPictures.clear();
+ mWidth = mHeight = 0;
+}
+
+bool PictureSet::draw(SkCanvas* canvas)
+{
+ 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;
+}
+
+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
+{
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ if (!working->mEmpty)
+ return false;
+ }
+ return true;
+}
+
+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::set(const PictureSet& src)
+{
+ DBG_SET_LOGD("start %p src=%p", this, &src);
+ clear();
+ mWidth = src.mWidth;
+ mHeight = src.mHeight;
+ 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");
+}
+
+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");
+}
+
+bool PictureSet::validate(const char* funct) const
+{
+ 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;
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/PictureSet.h b/Source/WebKit/android/jni/PictureSet.h
new file mode 100644
index 0000000..b177958
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.h
@@ -0,0 +1,104 @@
+/*
+ * 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>
+
+class SkCanvas;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ class PictureSet {
+ public:
+ PictureSet();
+ PictureSet(const PictureSet& src) { set(src); }
+ virtual ~PictureSet();
+ void add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split)
+ {
+ add(area, picture, elapsed, split, emptyPicture(picture));
+ }
+ 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 build();
+ // Update mWidth/mHeight, and adds any additional inval region
+ void checkDimensions(int width, int height, SkRegion* inval);
+ 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
+ bool reuseSubdivided(const SkRegion& );
+ void set(const PictureSet& );
+ void setDrawTimes(const PictureSet& );
+ void setPicture(size_t i, SkPicture* p);
+ size_t size() const { return mPictures.size(); }
+ void split(PictureSet* result) const;
+ bool upToDate(size_t i) const { return mPictures[i].mPicture != NULL; }
+ 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
+ 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;
+ int mHeight;
+ int mWidth;
+ };
+}
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
new file mode 100644
index 0000000..15b6d20
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -0,0 +1,2125 @@
+/*
+ * 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 "WebCoreFrameBridge.h"
+
+#include "Arena.h"
+#include "BackForwardList.h"
+#include "MemoryCache.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "ChromiumInit.h"
+#include "ContextMenuClientAndroid.h"
+#include "DeviceMotionClientAndroid.h"
+#include "DeviceOrientationClientAndroid.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "Element.h"
+#include "FocusController.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameLoadRequest.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "HTMLCollection.h"
+#include "HTMLElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "InspectorClientAndroid.h"
+#include "KURL.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "PlatformString.h"
+#include "RenderPart.h"
+#include "RenderSkinAndroid.h"
+#include "RenderTreeAsText.h"
+#include "RenderView.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleInternal.h"
+#include "ScriptController.h"
+#include "ScriptValue.h"
+#include "SecurityOrigin.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SubstituteData.h"
+#include "UrlInterceptResponse.h"
+#include "UserGestureIndicator.h"
+#include "WebCache.h"
+#include "WebCoreJni.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebUrlLoaderClient.h"
+#include "WebViewCore.h"
+#include "android_graphics.h"
+#include "jni.h"
+#include "wds/DebugServer.h"
+
+#include <JNIUtility.h>
+#include <JNIHelp.h>
+#include <SkGraphics.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <utils/misc.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 "JavaInstanceV8.h"
+#include "V8Counters.h"
+#endif // USE(JSC)
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#if ENABLE(ARCHIVE)
+#include "WebArchiveAndroid.h"
+#endif
+
+#if ENABLE(WEB_AUTOFILL)
+#include "autofill/WebAutoFill.h"
+#endif
+
+using namespace JSC::Bindings;
+
+static String* gUploadFileLabel;
+static String* gResetLabel;
+static String* gSubmitLabel;
+static String* gNoFileChosenLabel;
+
+String* WebCore::PlatformBridge::globalLocalizedName(
+ WebCore::PlatformBridge::rawResId resId)
+{
+ switch (resId) {
+ case WebCore::PlatformBridge::FileUploadLabel:
+ return gUploadFileLabel;
+ case WebCore::PlatformBridge::ResetLabel:
+ return gResetLabel;
+ case WebCore::PlatformBridge::SubmitLabel:
+ return gSubmitLabel;
+ case WebCore::PlatformBridge::FileUploadNoFileChosenLabel:
+ return gNoFileChosenLabel;
+
+ default:
+ return 0;
+ }
+}
+/**
+ * Instantiate the localized name desired.
+ */
+void initGlobalLocalizedName(WebCore::PlatformBridge::rawResId resId,
+ android::WebFrame* webFrame)
+{
+ String** pointer;
+ switch (resId) {
+ case WebCore::PlatformBridge::FileUploadLabel:
+ pointer = &gUploadFileLabel;
+ break;
+ case WebCore::PlatformBridge::ResetLabel:
+ pointer = &gResetLabel;
+ break;
+ case WebCore::PlatformBridge::SubmitLabel:
+ pointer = &gSubmitLabel;
+ break;
+ case WebCore::PlatformBridge::FileUploadNoFileChosenLabel:
+ pointer = &gNoFileChosenLabel;
+ break;
+ default:
+ return;
+ }
+ if (!(*pointer) && webFrame) {
+ (*pointer) = new String(webFrame->getRawResourceFilename(resId).impl());
+ }
+}
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024
+
+// ----------------------------------------------------------------------------
+
+struct WebFrame::JavaBrowserFrame
+{
+ jweak mObj;
+ jweak mHistoryList; // WebBackForwardList object
+ jmethodID mStartLoadingResource;
+ jmethodID mMaybeSavePassword;
+ jmethodID mShouldInterceptRequest;
+ jmethodID mLoadStarted;
+ jmethodID mTransitionToCommitted;
+ jmethodID mLoadFinished;
+ jmethodID mReportError;
+ jmethodID mSetTitle;
+ jmethodID mWindowObjectCleared;
+ jmethodID mSetProgress;
+ jmethodID mDidReceiveIcon;
+ jmethodID mDidReceiveTouchIconUrl;
+ jmethodID mUpdateVisitedHistory;
+ jmethodID mHandleUrl;
+ jmethodID mCreateWindow;
+ jmethodID mCloseWindow;
+ jmethodID mDecidePolicyForFormResubmission;
+ jmethodID mRequestFocus;
+ jmethodID mGetRawResFilename;
+ jmethodID mDensity;
+ jmethodID mGetFileSize;
+ jmethodID mGetFile;
+ jmethodID mDidReceiveAuthenticationChallenge;
+ jmethodID mReportSslCertError;
+ jmethodID mDownloadStart;
+ jmethodID mDidReceiveData;
+ jmethodID mDidFinishLoading;
+ jmethodID mSetCertificate;
+ jmethodID mShouldSaveFormData;
+ jmethodID mSaveFormData;
+ jmethodID mAutoLogin;
+ AutoJObject frame(JNIEnv* env) {
+ return getRealObject(env, mObj);
+ }
+ AutoJObject history(JNIEnv* env) {
+ return getRealObject(env, mHistoryList);
+ }
+};
+
+static jfieldID gFrameField;
+#define GET_NATIVE_FRAME(env, obj) ((WebCore::Frame*)env->GetIntField(obj, gFrameField))
+#define SET_NATIVE_FRAME(env, obj, frame) (env->SetIntField(obj, gFrameField, frame))
+
+// ----------------------------------------------------------------------------
+
+WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page)
+ : mPage(page)
+{
+ jclass clazz = env->GetObjectClass(obj);
+ 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 =
+ env->GetMethodID(clazz, "shouldInterceptRequest",
+ "(Ljava/lang/String;)Landroid/webkit/WebResourceResponse;");
+ mJavaFrame->mLoadStarted = env->GetMethodID(clazz, "loadStarted",
+ "(Ljava/lang/String;Landroid/graphics/Bitmap;IZ)V");
+ mJavaFrame->mTransitionToCommitted = env->GetMethodID(clazz, "transitionToCommitted",
+ "(IZ)V");
+ mJavaFrame->mLoadFinished = env->GetMethodID(clazz, "loadFinished",
+ "(Ljava/lang/String;IZ)V");
+ mJavaFrame->mReportError = env->GetMethodID(clazz, "reportError",
+ "(ILjava/lang/String;Ljava/lang/String;)V");
+ mJavaFrame->mSetTitle = env->GetMethodID(clazz, "setTitle",
+ "(Ljava/lang/String;)V");
+ mJavaFrame->mWindowObjectCleared = env->GetMethodID(clazz, "windowObjectCleared",
+ "(I)V");
+ mJavaFrame->mSetProgress = env->GetMethodID(clazz, "setProgress",
+ "(I)V");
+ mJavaFrame->mDidReceiveIcon = env->GetMethodID(clazz, "didReceiveIcon",
+ "(Landroid/graphics/Bitmap;)V");
+ mJavaFrame->mDidReceiveTouchIconUrl = env->GetMethodID(clazz, "didReceiveTouchIconUrl",
+ "(Ljava/lang/String;Z)V");
+ mJavaFrame->mUpdateVisitedHistory = env->GetMethodID(clazz, "updateVisitedHistory",
+ "(Ljava/lang/String;Z)V");
+ mJavaFrame->mHandleUrl = env->GetMethodID(clazz, "handleUrl",
+ "(Ljava/lang/String;)Z");
+ mJavaFrame->mCreateWindow = env->GetMethodID(clazz, "createWindow",
+ "(ZZ)Landroid/webkit/BrowserFrame;");
+ mJavaFrame->mCloseWindow = env->GetMethodID(clazz, "closeWindow",
+ "(Landroid/webkit/WebViewCore;)V");
+ mJavaFrame->mDecidePolicyForFormResubmission = env->GetMethodID(clazz,
+ "decidePolicyForFormResubmission", "(I)V");
+ mJavaFrame->mRequestFocus = env->GetMethodID(clazz, "requestFocus",
+ "()V");
+ mJavaFrame->mGetRawResFilename = env->GetMethodID(clazz, "getRawResFilename",
+ "(I)Ljava/lang/String;");
+ mJavaFrame->mDensity = env->GetMethodID(clazz, "density","()F");
+ mJavaFrame->mGetFileSize = env->GetMethodID(clazz, "getFileSize", "(Ljava/lang/String;)I");
+ mJavaFrame->mGetFile = env->GetMethodID(clazz, "getFile", "(Ljava/lang/String;[BII)I");
+ mJavaFrame->mDidReceiveAuthenticationChallenge = env->GetMethodID(clazz, "didReceiveAuthenticationChallenge",
+ "(ILjava/lang/String;Ljava/lang/String;Z)V");
+ mJavaFrame->mReportSslCertError = env->GetMethodID(clazz, "reportSslCertError", "(II[B)V");
+ mJavaFrame->mDownloadStart = env->GetMethodID(clazz, "downloadStart",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V");
+ mJavaFrame->mDidReceiveData = env->GetMethodID(clazz, "didReceiveData", "([BI)V");
+ mJavaFrame->mDidFinishLoading = env->GetMethodID(clazz, "didFinishLoading", "()V");
+ mJavaFrame->mSetCertificate = env->GetMethodID(clazz, "setCertificate", "([B)V");
+ mJavaFrame->mShouldSaveFormData = env->GetMethodID(clazz, "shouldSaveFormData", "()Z");
+ mJavaFrame->mSaveFormData = env->GetMethodID(clazz, "saveFormData", "(Ljava/util/HashMap;)V");
+ mJavaFrame->mAutoLogin = env->GetMethodID(clazz, "autoLogin",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ env->DeleteLocalRef(clazz);
+
+ 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->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;
+}
+
+WebFrame::~WebFrame()
+{
+ if (mJavaFrame->mObj) {
+ JNIEnv* env = getJNIEnv();
+ env->DeleteWeakGlobalRef(mJavaFrame->mObj);
+ env->DeleteWeakGlobalRef(mJavaFrame->mHistoryList);
+ mJavaFrame->mObj = 0;
+ }
+ delete mJavaFrame;
+ delete m_renderSkins;
+}
+
+WebFrame* WebFrame::getWebFrame(const WebCore::Frame* frame)
+{
+ FrameLoaderClientAndroid* client =
+ static_cast<FrameLoaderClientAndroid*> (frame->loader()->client());
+ return client->webFrame();
+}
+
+static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHeaderMap& map)
+{
+ jclass mapClass = env->FindClass("java/util/HashMap");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
+ jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
+ jobject hashMap = env->NewObject(mapClass, init, map.size());
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
+ jmethodID put = env->GetMethodID(mapClass, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ 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) {
+ if (i->first.length() == 0 || i->second.length() == 0)
+ continue;
+ jstring key = wtfStringToJstring(env, i->first);
+ jstring val = wtfStringToJstring(env, i->second);
+ if (key && val) {
+ env->CallObjectMethod(hashMap, put, key, val);
+ }
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+
+ env->DeleteLocalRef(mapClass);
+
+ return hashMap;
+}
+
+// This class stores the URI and the size of each file for upload. The URI is
+// stored so we do not have to create it again. The size is stored so we can
+// compare the actual size of the file with the stated size. If the actual size
+// is larger, we will not copy it, since we will not have enough space in our
+// buffer.
+class FileInfo {
+public:
+ FileInfo(JNIEnv* env, const WTF::String& name) {
+ m_uri = wtfStringToJstring(env, name);
+ checkException(env);
+ m_size = 0;
+ m_env = env;
+ }
+ ~FileInfo() {
+ m_env->DeleteLocalRef(m_uri);
+ }
+ int getSize() { return m_size; }
+ jstring getUri() { return m_uri; }
+ void setSize(int size) { m_size = size; }
+private:
+ // This is only a pointer to the JNIEnv* returned by
+ // JSC::Bindings::getJNIEnv(). Used to delete the jstring when finished.
+ JNIEnv* m_env;
+ jstring m_uri;
+ 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());
+
+ WTF::String method = request.httpMethod();
+ WebCore::HTTPHeaderMap headers = request.httpHeaderFields();
+
+ JNIEnv* env = getJNIEnv();
+ 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(mJavaFrame->frame(env).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 NULL;
+
+ PassRefPtr<WebCore::ResourceLoaderAndroid> h;
+ if (jLoadListener)
+ h = WebCoreResourceLoader::create(env, jLoadListener);
+ env->DeleteLocalRef(jLoadListener);
+ return h;
+}
+
+UrlInterceptResponse*
+WebFrame::shouldInterceptRequest(const WTF::String& url)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data());
+
+ JNIEnv* env = getJNIEnv();
+ jstring urlStr = wtfStringToJstring(env, url);
+ jobject response = env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mShouldInterceptRequest, urlStr);
+ env->DeleteLocalRef(urlStr);
+ if (response == 0)
+ return 0;
+ return new UrlInterceptResponse(env, response);
+}
+
+void
+WebFrame::reportError(int errorCode, const WTF::String& description,
+ const WTF::String& failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
+ JNIEnv* env = getJNIEnv();
+
+ jstring descStr = wtfStringToJstring(env, description);
+ jstring failUrl = wtfStringToJstring(env, failingUrl);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mReportError,
+ errorCode, descStr, failUrl);
+ env->DeleteLocalRef(descStr);
+ env->DeleteLocalRef(failUrl);
+}
+
+void
+WebFrame::loadStarted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ // activeDocumentLoader() can return null.
+ DocumentLoader* documentLoader = frame->loader()->activeDocumentLoader();
+ if (documentLoader == NULL)
+ return;
+
+ const WebCore::KURL& url = documentLoader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: loadStarted %s", url.string().ascii().data());
+
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ WebCore::FrameLoadType loadType = frame->loader()->loadType();
+
+ if (loadType == WebCore::FrameLoadTypeReplace ||
+ (loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList &&
+ !isMainFrame))
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ const WTF::String& urlString = url.string();
+ // If this is the main frame and we already have a favicon in the database,
+ // send it along with the page started notification.
+ jobject favicon = NULL;
+ if (isMainFrame) {
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlString, WebCore::IntSize(16, 16));
+ if (icon)
+ favicon = webcoreImageToJavaBitmap(env, icon);
+ LOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data());
+ }
+ jstring urlStr = wtfStringToJstring(env, urlString);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadStarted, urlStr, favicon,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+
+ // Inform the client that the main frame has started a new load.
+ if (isMainFrame && mPage) {
+ Chrome* chrome = mPage->chrome();
+ if (chrome) {
+ ChromeClientAndroid* client = static_cast<ChromeClientAndroid*>(chrome->client());
+ if (client)
+ client->onMainFrameLoadStarted();
+ }
+ }
+}
+
+void
+WebFrame::transitionToCommitted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ WebCore::FrameLoadType loadType = frame->loader()->loadType();
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mTransitionToCommitted,
+ (int)loadType, isMainFrame);
+ checkException(env);
+}
+
+void
+WebFrame::didFinishLoad(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ // activeDocumentLoader() can return null.
+ WebCore::FrameLoader* loader = frame->loader();
+ DocumentLoader* documentLoader = loader->activeDocumentLoader();
+ if (documentLoader == NULL)
+ return;
+
+ const WebCore::KURL& url = documentLoader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data());
+
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ WebCore::FrameLoadType loadType = loader->loadType();
+ const WTF::String& urlString = url.string();
+ jstring urlStr = wtfStringToJstring(env, urlString);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadFinished, urlStr,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+}
+
+void
+WebFrame::addHistoryItem(WebCore::HistoryItem* item)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: addHistoryItem");
+ JNIEnv* env = getJNIEnv();
+ WebHistory::AddItem(mJavaFrame->history(env), item);
+}
+
+void
+WebFrame::removeHistoryItem(int index)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: removeHistoryItem at %d", index);
+ JNIEnv* env = getJNIEnv();
+ WebHistory::RemoveItem(mJavaFrame->history(env), index);
+}
+
+void
+WebFrame::updateHistoryIndex(int newIndex)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
+ JNIEnv* env = getJNIEnv();
+ WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
+}
+
+void
+WebFrame::setTitle(const WTF::String& title)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+#ifndef NDEBUG
+ LOGV("setTitle(%s)", title.ascii().data());
+#endif
+ JNIEnv* env = getJNIEnv();
+ jstring jTitleStr = wtfStringToJstring(env, title);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetTitle, jTitleStr);
+ checkException(env);
+ env->DeleteLocalRef(jTitleStr);
+}
+
+void
+WebFrame::windowObjectCleared(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: windowObjectCleared");
+ JNIEnv* env = getJNIEnv();
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mWindowObjectCleared, (int)frame);
+ checkException(env);
+}
+
+void
+WebFrame::setProgress(float newProgress)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int progress = (int) (100 * newProgress);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetProgress, progress);
+ checkException(env);
+}
+
+const WTF::String
+WebFrame::userAgentForURL(const WebCore::KURL* url)
+{
+ return mUserAgent;
+}
+
+void
+WebFrame::didReceiveIcon(WebCore::Image* icon)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOG_ASSERT(icon, "DidReceiveIcon called without an image!");
+ JNIEnv* env = getJNIEnv();
+ jobject bitmap = webcoreImageToJavaBitmap(env, icon);
+ if (!bitmap)
+ return;
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidReceiveIcon, bitmap);
+ env->DeleteLocalRef(bitmap);
+ checkException(env);
+}
+
+void
+WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveTouchIconUrl, jUrlStr, precomposed);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+void
+WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ const WTF::String& urlStr = url.string();
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+bool
+WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ // always handle "POST" in place
+ if (equalIgnoringCase(request.httpMethod(), "POST"))
+ return true;
+ const WebCore::KURL& requestUrl = request.url();
+ bool isUserGesture = UserGestureIndicator::processingUserGesture();
+ if (!mUserInitiatedAction && !isUserGesture &&
+ (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") ||
+ requestUrl.protocolIs("file") || requestUrl.protocolIs("about") ||
+ WebCore::protocolIsJavaScript(requestUrl.string())))
+ return true;
+ const WTF::String& url = requestUrl.string();
+ // Empty urls should not be sent to java
+ if (url.isEmpty())
+ return true;
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+
+ // check to see whether browser app wants to hijack url loading.
+ // if browser app handles the url, we will return false to bail out WebCore loading
+ jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), mJavaFrame->mHandleUrl, jUrlStr);
+ checkException(env);
+ env->DeleteLocalRef(jUrlStr);
+ return (ret == 0);
+}
+
+bool
+WebFrame::shouldSaveFormData()
+{
+ JNIEnv* env = getJNIEnv();
+ jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mShouldSaveFormData);
+ checkException(env);
+ return ret;
+}
+
+WebCore::Frame*
+WebFrame::createWindow(bool dialog, bool userGesture)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ jobject obj = env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mCreateWindow, dialog, userGesture);
+ if (obj) {
+ WebCore::Frame* frame = GET_NATIVE_FRAME(env, obj);
+ return frame;
+ }
+ return NULL;
+}
+
+void
+WebFrame::requestFocus() const
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mRequestFocus);
+ checkException(env);
+}
+
+void
+WebFrame::closeWindow(WebViewCore* webViewCore)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ assert(webViewCore);
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mCloseWindow,
+ webViewCore->getJavaObject().get());
+}
+
+struct PolicyFunctionWrapper {
+ WebCore::FramePolicyFunction func;
+};
+
+void
+WebFrame::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ PolicyFunctionWrapper* p = new PolicyFunctionWrapper;
+ p->func = func;
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDecidePolicyForFormResubmission, p);
+}
+
+WTF::String
+WebFrame::getRawResourceFilename(WebCore::PlatformBridge::rawResId id) const
+{
+ JNIEnv* env = getJNIEnv();
+ jstring ret = (jstring) env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mGetRawResFilename, (int)id);
+
+ return jstringToWtfString(env, ret);
+}
+
+float
+WebFrame::density() const
+{
+ JNIEnv* env = getJNIEnv();
+ jfloat dpi = env->CallFloatMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDensity);
+ checkException(env);
+ return dpi;
+}
+
+#if USE(CHROME_NETWORK_STACK)
+void
+WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int jHandle = reinterpret_cast<int>(client);
+ jstring jHost = stdStringToJstring(env, host, true);
+ jstring jRealm = stdStringToJstring(env, realm, true);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveAuthenticationChallenge, jHandle, jHost, jRealm, useCachedCredentials);
+ env->DeleteLocalRef(jHost);
+ env->DeleteLocalRef(jRealm);
+ checkException(env);
+}
+#endif
+
+void
+WebFrame::reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int jHandle = reinterpret_cast<int>(client);
+
+ int len = cert.length();
+ jbyteArray jCert = env->NewByteArray(len);
+ jbyte* bytes = env->GetByteArrayElements(jCert, NULL);
+ cert.copy(reinterpret_cast<char*>(bytes), len);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mReportSslCertError, jHandle, cert_error, jCert);
+ env->DeleteLocalRef(jCert);
+ checkException(env);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+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();
+ jstring jUrl = stdStringToJstring(env, url, true);
+ jstring jUserAgent = stdStringToJstring(env, userAgent, true);
+ jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true);
+ jstring jMimetype = stdStringToJstring(env, mimetype, true);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDownloadStart, jUrl, jUserAgent, jContentDisposition, jMimetype, contentLength);
+
+ env->DeleteLocalRef(jUrl);
+ env->DeleteLocalRef(jUserAgent);
+ env->DeleteLocalRef(jContentDisposition);
+ env->DeleteLocalRef(jMimetype);
+ checkException(env);
+}
+
+void
+WebFrame::didReceiveData(const char* data, int size) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ jbyteArray jData = env->NewByteArray(size);
+ jbyte* bytes = env->GetByteArrayElements(jData, NULL);
+ memcpy(reinterpret_cast<char*>(bytes), data, size);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveData, jData, size);
+ env->DeleteLocalRef(jData);
+ checkException(env);
+}
+
+void
+WebFrame::didFinishLoading() {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidFinishLoading);
+ checkException(env);
+}
+
+#endif
+
+#if USE(CHROME_NETWORK_STACK)
+void WebFrame::setCertificate(const std::string& cert)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ int len = cert.length();
+ jbyteArray jCert = env->NewByteArray(len);
+ jbyte* bytes = env->GetByteArrayElements(jCert, NULL);
+ cert.copy(reinterpret_cast<char*>(bytes), len);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetCertificate, jCert);
+
+ env->DeleteLocalRef(jCert);
+ checkException(env);
+}
+#endif
+
+void WebFrame::autoLogin(const std::string& loginHeader)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimerCoutner::JavaCallbackTimeCounter);
+#endif
+ WTF::String header(loginHeader.c_str(), loginHeader.length());
+ WTF::Vector<WTF::String> split;
+ header.split('&', split);
+ if (!split.isEmpty()) {
+ WTF::String realm;
+ WTF::String account;
+ WTF::String args;
+ int len = split.size();
+ while (len--) {
+ WTF::String& str = split[len];
+ size_t equals = str.find('=');
+ if (equals == WTF::notFound)
+ continue;
+
+ WTF::String* result = 0;
+ if (str.startsWith("realm", false))
+ result = &realm;
+ else if (str.startsWith("account", false))
+ result = &account;
+ else if (str.startsWith("args", false))
+ result = &args;
+
+ if (result)
+ // Decode url escape sequences before sending to the app.
+ *result = WebCore::decodeURLEscapeSequences(str.substring(equals + 1));
+ }
+
+ // realm and args are required parameters.
+ if (realm.isEmpty() || args.isEmpty())
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ jstring jRealm = wtfStringToJstring(env, realm, true);
+ jstring jAccount = wtfStringToJstring(env, account);
+ jstring jArgs = wtfStringToJstring(env, args, true);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mAutoLogin, jRealm, jAccount, jArgs);
+ }
+}
+
+void WebFrame::maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request)
+{
+ if (request.httpMethod() != "POST")
+ return;
+
+ WTF::String username;
+ WTF::String password;
+ if (!getUsernamePasswordFromDom(frame, username, password))
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ jstring jUsername = wtfStringToJstring(env, username);
+ jstring jPassword = wtfStringToJstring(env, password);
+ jbyteArray jPostData = getPostData(request);
+ if (jPostData) {
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mMaybeSavePassword, jPostData, jUsername, jPassword);
+ }
+
+ env->DeleteLocalRef(jPostData);
+ env->DeleteLocalRef(jUsername);
+ env->DeleteLocalRef(jPassword);
+ checkException(env);
+}
+
+bool WebFrame::getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password)
+{
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = frame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->isPasswordField())
+ password = input->value();
+ else if (input->isTextField() || input->isEmailField())
+ username = input->value();
+ if (!username.isNull() && !password.isNull())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ return found;
+}
+
+jbyteArray WebFrame::getPostData(const WebCore::ResourceRequest& request)
+{
+ jbyteArray jPostDataStr = NULL;
+ WebCore::FormData* formdata = request.httpBody();
+ if (formdata) {
+ JNIEnv* env = getJNIEnv();
+ AutoJObject obj = mJavaFrame->frame(env);
+
+ // We can use the formdata->flatten() but it will result in two
+ // memcpys, first through loading up the vector with the form data
+ // then another to copy it out of the vector and into the java byte
+ // array. Instead, we copy the form data ourselves below saving a
+ // memcpy.
+ const WTF::Vector<WebCore::FormDataElement>& elements =
+ formdata->elements();
+
+ // Sizing pass
+ int size = 0;
+ size_t n = elements.size();
+ FileInfo** fileinfos = new FileInfo*[n];
+ for (size_t i = 0; i < n; ++i) {
+ fileinfos[i] = 0;
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ size += e.m_data.size();
+ } else if (e.m_type == WebCore::FormDataElement::encodedFile) {
+ fileinfos[i] = new FileInfo(env, e.m_filename);
+ int delta = env->CallIntMethod(obj.get(),
+ mJavaFrame->mGetFileSize, fileinfos[i]->getUri());
+ checkException(env);
+ fileinfos[i]->setSize(delta);
+ size += delta;
+ }
+ }
+
+ // Only create the byte array if there is POST data to pass up.
+ // The Java code is expecting null if there is no data.
+ if (size > 0) {
+ // Copy the actual form data.
+ jPostDataStr = env->NewByteArray(size);
+ if (jPostDataStr) {
+ // Write the form data to the java array.
+ jbyte* bytes = env->GetByteArrayElements(jPostDataStr, NULL);
+ int offset = 0;
+ for (size_t i = 0; i < n; ++i) {
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ int delta = e.m_data.size();
+ memcpy(bytes + offset, e.m_data.data(), delta);
+ offset += delta;
+ } else if (e.m_type
+ == WebCore::FormDataElement::encodedFile) {
+ int delta = env->CallIntMethod(obj.get(),
+ mJavaFrame->mGetFile, fileinfos[i]->getUri(),
+ jPostDataStr, offset, fileinfos[i]->getSize());
+ checkException(env);
+ offset += delta;
+ }
+ }
+ env->ReleaseByteArrayElements(jPostDataStr, bytes, 0);
+ }
+ }
+ delete[] fileinfos;
+ }
+ return jPostDataStr;
+}
+
+// ----------------------------------------------------------------------------
+
+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);
+ LOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!");
+ PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func;
+ 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)
+ pFrame->loader()->resetMultipleFormSubmissionProtection();
+
+ (pFrame->loader()->policyChecker()->*(pFunc->func))((WebCore::PolicyAction)decision);
+}
+
+static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAssetManager, jobject historyList)
+{
+ 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;
+ DeviceMotionClientAndroid* deviceMotionC = new DeviceMotionClientAndroid;
+ DeviceOrientationClientAndroid* deviceOrientationC = new DeviceOrientationClientAndroid;
+
+ WebCore::Page::PageClients pageClients;
+ pageClients.chromeClient = chromeC;
+ pageClients.contextMenuClient = new ContextMenuClientAndroid;
+ pageClients.editorClient = editorC;
+ pageClients.dragClient = new DragClientAndroid;
+ pageClients.inspectorClient = new InspectorClientAndroid;
+ pageClients.deviceMotionClient = deviceMotionC;
+ pageClients.deviceOrientationClient = deviceOrientationC;
+ WebCore::Page* page = new WebCore::Page(pageClients);
+
+ editorC->setPage(page);
+ page->setGroupName("android.webkit");
+
+ // Create a WebFrame to access the Java BrowserFrame associated with this page
+ WebFrame* webFrame = new WebFrame(env, obj, historyList, page);
+ // Attach webFrame to pageClients.chromeClient and release our ownership
+ chromeC->setWebFrame(webFrame);
+ Release(webFrame);
+
+ FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(webFrame);
+ // Create a Frame and the page holds its reference
+ WebCore::Frame* frame = WebCore::Frame::create(page, NULL, loaderC).get();
+ loaderC->setFrame(frame);
+#if ENABLE(WDS)
+ WDS::server()->addFrame(frame);
+#endif
+
+ // Create a WebViewCore to access the Java WebViewCore associated with this page
+ WebViewCore* webViewCore = new WebViewCore(env, javaview, frame);
+
+#if ENABLE(WEB_AUTOFILL)
+ editorC->getAutoFill()->setWebViewCore(webViewCore);
+#endif
+
+ // Create a FrameView
+ RefPtr<WebCore::FrameView> frameView = WebCore::FrameView::create(frame);
+ // Create a WebFrameView
+ WebFrameView* webFrameView = new WebFrameView(frameView.get(), webViewCore);
+ // As webFrameView Retains webViewCore, release our ownership
+ Release(webViewCore);
+ // As frameView Retains webFrameView, release our ownership
+ Release(webFrameView);
+ // Attach the frameView to the frame and release our ownership
+ frame->setView(frameView);
+ // Set the frame to active to turn on keyboard focus.
+ frame->init();
+ frame->selection()->setFocused(true);
+ frame->page()->focusController()->setFocused(true);
+ deviceMotionC->setWebViewCore(webViewCore);
+ deviceOrientationC->setWebViewCore(webViewCore);
+
+ // Allow local access to file:/// and substitute data
+ WebCore::SecurityOrigin::setLocalLoadPolicy(
+ WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData);
+
+ LOGV("::WebCore:: createFrame %p", frame);
+
+ // Set the mNativeFrame field in Frame
+ SET_NATIVE_FRAME(env, obj, (int)frame);
+
+ String directory = webFrame->getRawResourceFilename(
+ WebCore::PlatformBridge::DrawableDir);
+ if (directory.isEmpty())
+ LOGE("Can't find the drawable directory");
+ else {
+ // Setup the asset manager.
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ // Initialize our skinning classes
+ webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(am, directory));
+ }
+ for (int i = WebCore::PlatformBridge::FileUploadLabel;
+ i <= WebCore::PlatformBridge::FileUploadNoFileChosenLabel; i++)
+ initGlobalLocalizedName(
+ static_cast<WebCore::PlatformBridge::rawResId>(i), webFrame);
+}
+
+static void DestroyFrame(JNIEnv* env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!");
+
+ LOGV("::WebCore:: deleting frame %p", pFrame);
+
+ WebCore::FrameView* view = pFrame->view();
+ view->ref();
+ // detachFromParent will cause the page to be closed.
+ WebCore::FrameLoader* fl = pFrame->loader();
+ // retain a pointer because detachFromParent will set the page to null.
+ WebCore::Page* page = pFrame->page();
+ if (fl)
+ fl->detachFromParent();
+ delete page;
+ view->deref();
+
+ SET_NATIVE_FRAME(env, obj, 0);
+#if ENABLE(WDS)
+ WDS::server()->removeFrame(pFrame);
+#endif
+}
+
+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);
+ LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");
+
+ WTF::String webcoreUrl = jstringToWtfString(env, url);
+ WebCore::KURL kurl(WebCore::KURL(), webcoreUrl);
+ WebCore::ResourceRequest request(kurl);
+ if (headers) {
+ // dalvikvm will raise exception if any of these fail
+ jclass mapClass = env->FindClass("java/util/Map");
+ jmethodID entrySet = env->GetMethodID(mapClass, "entrySet",
+ "()Ljava/util/Set;");
+ jobject set = env->CallObjectMethod(headers, entrySet);
+
+ jclass setClass = env->FindClass("java/util/Set");
+ jmethodID iterator = env->GetMethodID(setClass, "iterator",
+ "()Ljava/util/Iterator;");
+ jobject iter = env->CallObjectMethod(set, iterator);
+
+ jclass iteratorClass = env->FindClass("java/util/Iterator");
+ jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+ jmethodID next = env->GetMethodID(iteratorClass, "next",
+ "()Ljava/lang/Object;");
+ jclass entryClass = env->FindClass("java/util/Map$Entry");
+ jmethodID getKey = env->GetMethodID(entryClass, "getKey",
+ "()Ljava/lang/Object;");
+ jmethodID getValue = env->GetMethodID(entryClass, "getValue",
+ "()Ljava/lang/Object;");
+
+ while (env->CallBooleanMethod(iter, hasNext)) {
+ jobject entry = env->CallObjectMethod(iter, next);
+ jstring key = (jstring) env->CallObjectMethod(entry, getKey);
+ jstring value = (jstring) env->CallObjectMethod(entry, getValue);
+ request.setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, value));
+ env->DeleteLocalRef(entry);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(value);
+ }
+
+ env->DeleteLocalRef(entryClass);
+ env->DeleteLocalRef(iteratorClass);
+ env->DeleteLocalRef(iter);
+ env->DeleteLocalRef(setClass);
+ env->DeleteLocalRef(set);
+ env->DeleteLocalRef(mapClass);
+ }
+ 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);
+ LOG_ASSERT(pFrame, "nativePostUrl must take a valid frame pointer!");
+
+ WebCore::KURL kurl(WebCore::KURL(), jstringToWtfString(env, url));
+ WebCore::ResourceRequest request(kurl);
+ request.setHTTPMethod("POST");
+ request.setHTTPContentType("application/x-www-form-urlencoded");
+
+ if (postData) {
+ jsize size = env->GetArrayLength(postData);
+ jbyte* bytes = env->GetByteArrayElements(postData, NULL);
+ RefPtr<FormData> formData = FormData::create((const void*)bytes, size);
+ // the identifier uses the same logic as generateFormDataIdentifier() in
+ // HTMLFormElement.cpp
+ formData->setIdentifier(static_cast<int64_t>(WTF::currentTime() * 1000000.0));
+ request.setHTTPBody(formData);
+ env->ReleaseByteArrayElements(postData, bytes, 0);
+ }
+
+ LOGV("PostUrl %s", kurl.string().latin1().data());
+ WebCore::FrameLoadRequest frameRequest(pFrame->document()->securityOrigin(), request);
+ pFrame->loader()->loadFrameRequest(frameRequest, false, false, 0, 0, WebCore::SendReferrer);
+}
+
+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);
+ LOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!");
+
+ // Setup the resource request
+ WebCore::ResourceRequest request(jstringToWtfString(env, baseUrl));
+
+ // Setup the substituteData
+ const char* dataStr = env->GetStringUTFChars(data, NULL);
+ WTF::PassRefPtr<WebCore::SharedBuffer> sharedBuffer =
+ WebCore::SharedBuffer::create();
+ 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),
+ WebCore::KURL(ParsedURLString, jstringToWtfString(env, failUrl)));
+
+ // Perform the load
+ pFrame->loader()->load(request, substituteData, false);
+}
+
+static void StopLoading(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ 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();
+}
+
+#if ENABLE(ARCHIVE)
+static String saveArchiveAutoname(String basename, String name, String extension) {
+ if (name.isNull() || name.isEmpty()) {
+ name = String("index");
+ }
+
+ String testname = basename;
+ testname.append(name);
+ testname.append(extension);
+
+ errno = 0;
+ struct stat permissions;
+ if (stat(testname.utf8().data(), &permissions) < 0) {
+ if (errno == ENOENT)
+ return testname;
+ return String();
+ }
+
+ const int maxAttempts = 100;
+ for (int i = 1; i < maxAttempts; i++) {
+ String testname = basename;
+ testname.append(name);
+ testname.append("-");
+ testname.append(String::number(i));
+ testname.append(extension);
+
+ errno = 0;
+ if (stat(testname.utf8().data(), &permissions) < 0) {
+ if (errno == ENOENT)
+ return testname;
+ return String();
+ }
+ }
+
+ return String();
+}
+#endif
+
+static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboolean autoname)
+{
+#if ENABLE(ARCHIVE)
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ 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;
+
+ const char* basenameNative = getCharactersFromJStringInEnv(env, basename);
+ String basenameString = String::fromUTF8(basenameNative);
+ String filename;
+
+ if (autoname) {
+ String name = pFrame->loader()->documentLoader()->originalURL().lastPathComponent();
+ String extension = String(".webarchivexml");
+ filename = saveArchiveAutoname(basenameString, name, extension);
+ } else {
+ filename = basenameString;
+ }
+
+ if (filename.isNull() || filename.isEmpty()) {
+ LOGD("saveWebArchive: Failed to select a filename to save.");
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ return NULL;
+ }
+
+ const int noCompression = 0;
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.utf8().data(), noCompression);
+ if (writer == NULL) {
+ LOGD("saveWebArchive: Failed to initialize xml writer.");
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ return NULL;
+ }
+
+ RefPtr<WebArchiveAndroid> archive = WebCore::WebArchiveAndroid::create(pFrame);
+
+ bool result = archive->saveWebArchive(writer);
+
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ xmlFreeTextWriter(writer);
+
+ if (result)
+ return wtfStringToJstring(env, filename);
+
+ return NULL;
+#endif
+}
+
+static jstring ExternalRepresentation(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ 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);
+ return wtfStringToJstring(env, renderDump);
+}
+
+static StringBuilder FrameAsText(WebCore::Frame *pFrame, jboolean dumpChildFrames) {
+ StringBuilder renderDump;
+ if (!pFrame)
+ return renderDump;
+ WebCore::Element *documentElement = pFrame->document()->documentElement();
+ if (!documentElement)
+ return renderDump;
+ if (pFrame->tree()->parent()) {
+ renderDump.append("\n--------\nFrame: '");
+ renderDump.append(pFrame->tree()->name());
+ renderDump.append("'\n--------\n");
+ }
+ renderDump.append(((WebCore::HTMLElement*)documentElement)->innerText());
+ renderDump.append("\n");
+ if (dumpChildFrames) {
+ for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) {
+ renderDump.append(FrameAsText(pFrame->tree()->child(i), dumpChildFrames).toString());
+ }
+ }
+ return renderDump;
+}
+
+static jstring DocumentAsText(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+
+ WTF::String renderDump = FrameAsText(pFrame, false /* dumpChildFrames */).toString();
+ return wtfStringToJstring(env, renderDump);
+}
+
+static jstring ChildFramesAsText(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+
+ StringBuilder renderDumpBuilder;
+ for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) {
+ renderDumpBuilder.append(FrameAsText(pFrame->tree()->child(i), true /* dumpChildFrames */).toString());
+ }
+ WTF::String renderDump = renderDumpBuilder.toString();
+ return wtfStringToJstring(env, renderDump);
+}
+
+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);
+ LOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!");
+
+ WebCore::FrameLoader* loader = pFrame->loader();
+ if (allowStale) {
+ // load the current page with FrameLoadTypeIndexedBackForward so that it
+ // will use cache when it is possible
+ WebCore::Page* page = pFrame->page();
+ WebCore::HistoryItem* item = page->backForwardList()->currentItem();
+ if (item)
+ page->goToItem(item, FrameLoadTypeIndexedBackForward);
+ } else
+ loader->reload(true);
+}
+
+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);
+ LOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!");
+
+ if (pos == 1)
+ pFrame->page()->goForward();
+ else if (pos == -1)
+ pFrame->page()->goBack();
+ else
+ pFrame->page()->goBackOrForward(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);
+ LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!");
+
+ WebCore::ScriptValue value =
+ pFrame->script()->executeScript(jstringToWtfString(env, script), true);
+ WTF::String result = WTF::String();
+ ScriptState* scriptState = mainWorldScriptState(pFrame);
+ if (!value.getString(scriptState, result))
+ return NULL;
+ return wtfStringToJstring(env, result);
+}
+
+// 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.
+class WeakJavaInstance : public JavaInstance {
+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)
+ : JavaInstance(instance)
+#endif
+ , m_beginEndDepth(0)
+ {
+ JNIEnv* env = getJNIEnv();
+ // JavaInstance creates a global ref to instance in its constructor.
+ env->DeleteGlobalRef(m_instance->instance());
+ // Set the object to a weak reference.
+ m_instance->setInstance(env->NewWeakGlobalRef(instance));
+ }
+ ~WeakJavaInstance()
+ {
+ JNIEnv* env = getJNIEnv();
+ // Store the weak reference so we can delete it later.
+ jweak weak = m_instance->instance();
+ // The JavaInstance destructor attempts to delete the global ref stored
+ // in m_instance. Since we replaced it in our constructor with a weak
+ // reference, restore the global ref here so the vm will not complain.
+ m_instance->setInstance(env->NewGlobalRef(
+ getRealObject(env, m_instance->instance()).get()));
+ // Delete the weak reference.
+ env->DeleteWeakGlobalRef(weak);
+ }
+
+ virtual void virtualBegin()
+ {
+ if (m_beginEndDepth++ > 0)
+ return;
+ m_weakRef = m_instance->instance();
+ JNIEnv* env = getJNIEnv();
+ // This is odd. getRealObject returns an AutoJObject which is used to
+ // cleanly create and delete a local reference. But, here we need to
+ // maintain the local reference across calls to virtualBegin() and
+ // virtualEnd(). So, release the local reference from the AutoJObject
+ // and delete the local reference in virtualEnd().
+ m_realObject = getRealObject(env, m_weakRef).release();
+ // Point to the real object
+ m_instance->setInstance(m_realObject);
+ // Call the base class method
+ INHERITED::virtualBegin();
+ }
+
+ virtual void virtualEnd()
+ {
+ if (--m_beginEndDepth > 0)
+ return;
+ // Call the base class method first to pop the local frame.
+ INHERITED::virtualEnd();
+ // Get rid of the local reference to the real object.
+ getJNIEnv()->DeleteLocalRef(m_realObject);
+ // Point back to the WeakReference.
+ m_instance->setInstance(m_weakRef);
+ }
+
+private:
+ typedef JavaInstance INHERITED;
+ jobject m_realObject;
+ jweak m_weakRef;
+ // The current depth of nested calls to virtualBegin and virtualEnd.
+ int m_beginEndDepth;
+};
+
+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;
+ LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
+
+ JavaVM* vm;
+ env->GetJavaVM(&vm);
+ 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);
+ // Pass ownership of the added object to bindToWindowObject.
+ NPObject* npObject = JavaInstanceToNPObject(addedObject.get());
+ pFrame->script()->bindToWindowObject(pFrame, name, npObject);
+ // bindToWindowObject calls NPN_RetainObject on the
+ // returned one (see createV8ObjectForNPObject in V8NPObject.cpp).
+ // bindToWindowObject also increases obj's ref count and decreases
+ // the ref count when the object is not reachable from JavaScript
+ // side. Code here must release the reference count increased by
+ // bindToWindowObject.
+
+ // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds
+ // we use WebCore/bindings/v8/npruntime.cpp (rather than
+ // WebCore/bridge/npruntime.cpp), so the function is implemented there.
+ // TODO: Combine the two versions of these NPAPI files.
+ 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::cache()->setDisabled(disabled);
+}
+
+static jboolean CacheDisabled(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ return WebCore::cache()->disabled();
+}
+
+static void ClearWebCoreCache()
+{
+ if (!WebCore::cache()->disabled()) {
+ // Disabling the cache will remove all resources from the cache. They may
+ // still live on if they are referenced by some Web page though.
+ WebCore::cache()->setDisabled(true);
+ WebCore::cache()->setDisabled(false);
+ }
+
+ // clear page cache
+ int pageCapacity = WebCore::pageCache()->capacity();
+ // Setting size to 0, makes all pages be released.
+ WebCore::pageCache()->setCapacity(0);
+ WebCore::pageCache()->releaseAutoreleasedPagesNow();
+ WebCore::pageCache()->setCapacity(pageCapacity);
+}
+
+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",
+ cache()->getLiveSize(), cache()->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);
+ 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);
+ LOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!");
+
+ bool found = false;
+ 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.
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ if (static_cast<WebCore::HTMLInputElement*>(e)->isPasswordField())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ return found;
+}
+
+static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!");
+ jobjectArray strArray = NULL;
+ WTF::String username;
+ WTF::String password;
+ if (WebFrame::getWebFrame(pFrame)->getUsernamePasswordFromDom(pFrame, username, password)) {
+ jclass stringClass = env->FindClass("java/lang/String");
+ strArray = env->NewObjectArray(2, stringClass, NULL);
+ env->DeleteLocalRef(stringClass);
+ env->SetObjectArrayElement(strArray, 0, wtfStringToJstring(env, username));
+ env->SetObjectArrayElement(strArray, 1, wtfStringToJstring(env, password));
+ }
+ return strArray;
+}
+
+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);
+ LOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!");
+
+ WebCore::HTMLInputElement* usernameEle = NULL;
+ WebCore::HTMLInputElement* passwordEle = NULL;
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->isPasswordField())
+ passwordEle = input;
+ else if (input->isTextField() || input->isEmailField())
+ usernameEle = input;
+ if (usernameEle != NULL && passwordEle != NULL)
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ usernameEle->setValue(jstringToWtfString(env, username));
+ passwordEle->setValue(jstringToWtfString(env, password));
+ }
+}
+
+void
+WebFrame::saveFormData(HTMLFormElement* form)
+{
+ if (form->autoComplete()) {
+ JNIEnv* env = getJNIEnv();
+ jclass mapClass = env->FindClass("java/util/HashMap");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
+ jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
+ jobject hashMap = env->NewObject(mapClass, init, 1);
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
+ jmethodID put = env->GetMethodID(mapClass, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ 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++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasTagName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(e);
+ if (input->isTextField() && !input->isPasswordField()
+ && input->autoComplete()) {
+ WTF::String value = input->value();
+ int len = value.length();
+ if (len) {
+ const WTF::AtomicString& name = input->name();
+ jstring key = wtfStringToJstring(env, name);
+ jstring val = wtfStringToJstring(env, value);
+ LOG_ASSERT(key && val, "name or value not set");
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ }
+ }
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSaveFormData, hashMap);
+ env->DeleteLocalRef(hashMap);
+ env->DeleteLocalRef(mapClass);
+ }
+}
+
+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);
+ LOGV("Sending orientation: %d", orientation);
+ pFrame->sendOrientationChangeEvent(orientation);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+
+static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ std::string username = jstringToStdString(env, jUsername);
+ std::string password = jstringToStdString(env, jPassword);
+ client->setAuth(username, password);
+}
+
+static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->cancelAuth();
+}
+
+static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->proceedSslCertError();
+}
+
+static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->cancelSslCertError(cert_error);
+}
+
+#else
+
+static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
+{
+ 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");
+}
+
+#endif // USE(CHROME_NETWORK_STACK)
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gBrowserFrameNativeMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeCallPolicyFunction", "(II)V",
+ (void*) CallPolicyFunction },
+ { "nativeCreateFrame", "(Landroid/webkit/WebViewCore;Landroid/content/res/AssetManager;Landroid/webkit/WebBackForwardList;)V",
+ (void*) CreateFrame },
+ { "nativeDestroyFrame", "()V",
+ (void*) DestroyFrame },
+ { "nativeStopLoading", "()V",
+ (void*) StopLoading },
+ { "nativeLoadUrl", "(Ljava/lang/String;Ljava/util/Map;)V",
+ (void*) LoadUrl },
+ { "nativePostUrl", "(Ljava/lang/String;[B)V",
+ (void*) PostUrl },
+ { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) LoadData },
+ { "nativeSaveWebArchive", "(Ljava/lang/String;Z)Ljava/lang/String;",
+ (void*) SaveWebArchive },
+ { "externalRepresentation", "()Ljava/lang/String;",
+ (void*) ExternalRepresentation },
+ { "documentAsText", "()Ljava/lang/String;",
+ (void*) DocumentAsText },
+ { "childFramesAsText", "()Ljava/lang/String;",
+ (void*) ChildFramesAsText },
+ { "reload", "(Z)V",
+ (void*) Reload },
+ { "nativeGoBackOrForward", "(I)V",
+ (void*) GoBackOrForward },
+ { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;)V",
+ (void*) AddJavascriptInterface },
+ { "stringByEvaluatingJavaScriptFromString",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) StringByEvaluatingJavaScriptFromString },
+ { "setCacheDisabled", "(Z)V",
+ (void*) SetCacheDisabled },
+ { "cacheDisabled", "()Z",
+ (void*) CacheDisabled },
+ { "clearCache", "()V",
+ (void*) ClearCache },
+ { "documentHasImages", "()Z",
+ (void*) DocumentHasImages },
+ { "hasPasswordField", "()Z",
+ (void*) HasPasswordField },
+ { "getUsernamePassword", "()[Ljava/lang/String;",
+ (void*) GetUsernamePassword },
+ { "setUsernamePassword", "(Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) SetUsernamePassword },
+ { "nativeOrientationChanged", "(I)V",
+ (void*) OrientationChanged },
+ { "nativeAuthenticationProceed", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) AuthenticationProceed },
+ { "nativeAuthenticationCancel", "(I)V",
+ (void*) AuthenticationCancel },
+ { "nativeSslCertErrorProceed", "(I)V",
+ (void*) SslCertErrorProceed },
+ { "nativeSslCertErrorCancel", "(II)V",
+ (void*) SslCertErrorCancel },
+};
+
+int registerWebFrame(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/BrowserFrame");
+ LOG_ASSERT(clazz, "Cannot find BrowserFrame");
+ gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
+ LOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame");
+ env->DeleteLocalRef(clazz);
+
+ return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
+ gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.h b/Source/WebKit/android/jni/WebCoreFrameBridge.h
new file mode 100644
index 0000000..6522a5f
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+// TODO: change name to WebFrame.h
+
+#ifndef WEBFRAME_H
+#define WEBFRAME_H
+
+#include "FrameLoaderClient.h"
+#include "PlatformBridge.h"
+#include "PlatformString.h"
+#include "WebCoreRefObject.h"
+#include <jni.h>
+#include <string>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+ class HTMLFormElement;
+ class Frame;
+ class HistoryItem;
+ class Image;
+ class Page;
+ class RenderPart;
+ class RenderSkinAndroid;
+ class ResourceHandle;
+ class ResourceLoaderAndroid;
+ class ResourceRequest;
+}
+
+namespace android {
+
+class WebViewCore;
+class WebUrlLoaderClient;
+class UrlInterceptResponse;
+
+// one instance of WebFrame per Page for calling into Java's BrowserFrame
+class WebFrame : public WebCoreRefObject {
+ public:
+ WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page);
+ ~WebFrame();
+
+ // 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,
+ const WTF::String& failingUrl);
+
+ void loadStarted(WebCore::Frame* frame);
+
+ void transitionToCommitted(WebCore::Frame* frame);
+
+ void didFinishLoad(WebCore::Frame* frame);
+
+ void addHistoryItem(WebCore::HistoryItem* item);
+
+ void removeHistoryItem(int index);
+
+ void updateHistoryIndex(int newIndex);
+
+ void setTitle(const WTF::String& title);
+
+ void windowObjectCleared(WebCore::Frame* frame);
+
+ void setProgress(float newProgress);
+
+ const WTF::String userAgentForURL(const WebCore::KURL* url);
+
+ void didReceiveIcon(WebCore::Image* icon);
+
+ void didReceiveTouchIconURL(const WTF::String& url, bool precomposed);
+
+ void updateVisitedHistory(const WebCore::KURL& url, bool reload);
+
+ virtual bool canHandleRequest(const WebCore::ResourceRequest& request);
+
+ WebCore::Frame* createWindow(bool dialog, bool userGesture);
+
+ void requestFocus() const;
+
+ void closeWindow(WebViewCore* webViewCore);
+
+ void decidePolicyForFormResubmission(WebCore::FramePolicyFunction func);
+
+ void setUserAgent(WTF::String userAgent) { mUserAgent = userAgent; }
+
+ WTF::String getRawResourceFilename(WebCore::PlatformBridge::rawResId) const;
+
+ float density() const;
+
+ void didReceiveAuthenticationChallenge(WebUrlLoaderClient*, const std::string& host, const std::string& realm, bool useCachedCredentials);
+
+ void reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert);
+
+ void downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, long long contentLength);
+
+ void didReceiveData(const char* data, int size);
+
+ void didFinishLoading();
+
+ void maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request);
+
+ void setCertificate(const std::string& cert);
+
+ // Parse the x-auto-login header and propagate the parameters to the
+ // 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
+ // FrameLoader.java to block java network loads.
+ void setBlockNetworkLoads(bool block) { mBlockNetworkLoads = block; }
+ bool blockNetworkLoads() const { return mBlockNetworkLoads; }
+
+ /**
+ * Helper methods. These are typically chunks of code that are called in
+ * slightly different ways by the Apache and Chrome HTTP stacks.
+ */
+ bool getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password);
+ jbyteArray getPostData(const WebCore::ResourceRequest& request);
+
+ bool shouldSaveFormData();
+ void saveFormData(WebCore::HTMLFormElement*);
+ const WebCore::RenderSkinAndroid* renderSkins() const { return m_renderSkins; }
+ void setRenderSkins(const WebCore::RenderSkinAndroid* skins) { m_renderSkins = skins; }
+private:
+ struct JavaBrowserFrame;
+ JavaBrowserFrame* mJavaFrame;
+ WebCore::Page* mPage;
+ WTF::String mUserAgent;
+ bool mBlockNetworkLoads;
+ bool mUserInitiatedAction;
+ const WebCore::RenderSkinAndroid* m_renderSkins;
+};
+
+} // namespace android
+
+#endif // WEBFRAME_H
diff --git a/Source/WebKit/android/jni/WebCoreJni.cpp b/Source/WebKit/android/jni/WebCoreJni.cpp
new file mode 100644
index 0000000..2a07999
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJni.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "webcoreglue"
+
+#include "config.h"
+#include "WebCoreJni.h"
+
+#include "NotImplemented.h"
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+namespace android {
+
+AutoJObject getRealObject(JNIEnv* env, jobject obj)
+{
+ jobject real = env->NewLocalRef(obj);
+ LOG_ASSERT(real, "The real object has been deleted!");
+ return AutoJObject(env, real);
+}
+
+/**
+ * Helper method for checking java exceptions
+ * @return true if an exception occurred.
+ */
+bool checkException(JNIEnv* env)
+{
+ if (env->ExceptionCheck() != 0)
+ {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
+ return true;
+ }
+ return false;
+}
+
+// This method is safe to call from the ui thread and the WebCore thread.
+WTF::String jstringToWtfString(JNIEnv* env, jstring str)
+{
+ if (!str || !env)
+ return WTF::String();
+ const jchar* s = env->GetStringChars(str, NULL);
+ if (!s)
+ return WTF::String();
+ WTF::String ret(s, env->GetStringLength(str));
+ env->ReleaseStringChars(str, s);
+ checkException(env);
+ return ret;
+}
+
+jstring wtfStringToJstring(JNIEnv* env, const WTF::String& str, bool validOnZeroLength)
+{
+ int length = str.length();
+ return length || validOnZeroLength ? env->NewString(str.characters(), length) : 0;
+}
+
+
+#if USE(CHROME_NETWORK_STACK)
+string16 jstringToString16(JNIEnv* env, jstring jstr)
+{
+ if (!jstr || !env)
+ return string16();
+
+ const char* s = env->GetStringUTFChars(jstr, 0);
+ if (!s)
+ return string16();
+ string16 str = UTF8ToUTF16(s);
+ env->ReleaseStringUTFChars(jstr, s);
+ checkException(env);
+ return str;
+}
+
+std::string jstringToStdString(JNIEnv* env, jstring jstr)
+{
+ if (!jstr || !env)
+ return std::string();
+
+ const char* s = env->GetStringUTFChars(jstr, 0);
+ if (!s)
+ return std::string();
+ std::string str(s);
+ env->ReleaseStringUTFChars(jstr, s);
+ checkException(env);
+ return str;
+}
+
+jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZeroLength)
+{
+ return !str.empty() || validOnZeroLength ? env->NewStringUTF(str.c_str()) : 0;
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/jni/WebCoreJni.h b/Source/WebKit/android/jni/WebCoreJni.h
new file mode 100644
index 0000000..ec25c8f
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJni.h
@@ -0,0 +1,96 @@
+/*
+ * 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 ANDROID_WEBKIT_WEBCOREJNI_H
+#define ANDROID_WEBKIT_WEBCOREJNI_H
+
+#include "ChromiumIncludes.h"
+#include "PlatformString.h"
+#include <jni.h>
+
+namespace android {
+
+// A helper class that automatically deletes the local reference to the jobject
+// returned from getRealObject.
+class AutoJObject {
+public:
+ AutoJObject(const AutoJObject& other)
+ : m_env(other.m_env)
+ , m_obj(other.m_obj ? other.m_env->NewLocalRef(other.m_obj) : NULL) {}
+ ~AutoJObject() {
+ if (m_obj)
+ m_env->DeleteLocalRef(m_obj);
+ }
+ jobject get() const {
+ return m_obj;
+ }
+ // Releases the local reference to the caller. The caller *must* delete the
+ // local reference when it is done with it.
+ jobject release() {
+ jobject obj = m_obj;
+ m_obj = 0;
+ return obj;
+ }
+ JNIEnv* env() const {
+ return m_env;
+ }
+private:
+ AutoJObject(); // Not permitted.
+ AutoJObject(JNIEnv* env, jobject obj)
+ : m_env(env)
+ , m_obj(obj) {}
+ JNIEnv* m_env;
+ jobject m_obj;
+ friend AutoJObject getRealObject(JNIEnv*, jobject);
+};
+
+// Get the real object stored in the weak reference returned as an
+// AutoJObject.
+AutoJObject getRealObject(JNIEnv*, jobject);
+
+// Helper method for check java exceptions. Returns true if an exception
+// occurred and logs the exception.
+bool checkException(JNIEnv* env);
+
+// Create a WTF::String object from a jstring object.
+WTF::String jstringToWtfString(JNIEnv*, jstring);
+// Returns a local reference to a new jstring. If validOnZeroLength is true then
+// passing in an empty WTF String will result in an empty jstring. Otherwise
+// 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);
+// Returns a local reference to a new jstring. If validOnZeroLength is true then
+// 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
+
+}
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
new file mode 100644
index 0000000..1f264a2
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
@@ -0,0 +1,319 @@
+/*
+ * 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 "webcoreglue"
+
+#include "config.h"
+
+#include "BackForwardList.h"
+#include "ChromeClientAndroid.h"
+#include "ContextMenuClientAndroid.h"
+#include "CookieClient.h"
+#include "DeviceMotionClientAndroid.h"
+#include "DeviceOrientationClientAndroid.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "InspectorClientAndroid.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "Page.h"
+#include "PlatformGraphicsContext.h"
+#include "ResourceRequest.h"
+#include "ScriptController.h"
+#include "SecurityOrigin.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageEncoder.h"
+#include "SubstituteData.h"
+#include "TimerClient.h"
+#include "TextEncoding.h"
+#include "WebCoreViewBridge.h"
+#include "WebFrameView.h"
+#include "WebViewCore.h"
+#include "benchmark/Intercept.h"
+#include "benchmark/MyJavaVM.h"
+
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+#define EXPORT __attribute__((visibility("default")))
+
+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*);
+extern int registerWebIconDatabase(JNIEnv*);
+extern int registerWebSettings(JNIEnv*);
+extern int registerWebView(JNIEnv*);
+#if ENABLE(DATABASE)
+extern int registerWebStorage(JNIEnv*);
+#endif
+extern int registerGeolocationPermissions(JNIEnv*);
+extern int registerMockGeolocation(JNIEnv*);
+#if ENABLE(VIDEO)
+extern int registerMediaPlayerAudio(JNIEnv*);
+extern int registerMediaPlayerVideo(JNIEnv*);
+#endif
+extern int registerDeviceMotionAndOrientationManager(JNIEnv*);
+extern int registerCookieManager(JNIEnv*);
+#if USE(CHROME_NETWORK_STACK)
+extern int registerCacheManager(JNIEnv*);
+#endif
+
+}
+
+struct RegistrationMethod {
+ const char* name;
+ int (*func)(JNIEnv*);
+};
+
+static RegistrationMethod gWebCoreRegMethods[] = {
+ { "JavaBridge", android::registerJavaBridge },
+ { "JniUtil", android::registerJniUtil },
+ { "WebFrame", android::registerWebFrame },
+ { "WebCoreResourceLoader", android::registerResourceLoader },
+ { "WebViewCore", android::registerWebViewCore },
+ { "WebHistory", android::registerWebHistory },
+ { "WebIconDatabase", android::registerWebIconDatabase },
+ { "WebSettings", android::registerWebSettings },
+#if ENABLE(DATABASE)
+ { "WebStorage", android::registerWebStorage },
+#endif
+ { "WebView", android::registerWebView },
+ { "GeolocationPermissions", android::registerGeolocationPermissions },
+ { "MockGeolocation", android::registerMockGeolocation },
+#if ENABLE(VIDEO)
+ { "HTML5Audio", android::registerMediaPlayerAudio },
+ { "HTML5VideoViewProxy", android::registerMediaPlayerVideo },
+#endif
+ { "DeviceMotionAndOrientationManager", android::registerDeviceMotionAndOrientationManager },
+ { "CookieManager", android::registerCookieManager },
+#if USE(CHROME_NETWORK_STACK)
+ { "CacheManager", android::registerCacheManager },
+#endif
+};
+
+EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ // Save the JavaVM pointer for use globally.
+ JSC::Bindings::setJavaVM(vm);
+
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("GetEnv failed!");
+ return result;
+ }
+ 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) {
+ LOGE("%s registration failed!", method->name);
+ return result;
+ }
+ method++;
+ }
+
+ // Initialize rand() function. The rand() function is used in
+ // FileSystemAndroid to create a random temporary filename.
+ srand(time(NULL));
+
+ 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, NULL);
+ 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/WebCoreRefObject.h b/Source/WebKit/android/jni/WebCoreRefObject.h
new file mode 100644
index 0000000..4228db6
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreRefObject.h
@@ -0,0 +1,46 @@
+/*
+ * 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 WEBCORE_FOUNDATION_h
+#define WEBCORE_FOUNDATION_h
+
+#include "SkRefCnt.h"
+
+typedef SkRefCnt WebCoreRefObject;
+
+static inline WebCoreRefObject* Retain(WebCoreRefObject* obj)
+{
+ if (obj)
+ obj->ref();
+ return obj;
+}
+
+static inline void Release(WebCoreRefObject* obj)
+{
+ if (obj)
+ obj->unref();
+}
+
+#endif // WEBCORE_FOUNDATION_h
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..c60b3f5
--- /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 ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+#define ANDROID_WEBKIT_RESOURCELOADLISTENER_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
new file mode 100644
index 0000000..59e1c9a
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreViewBridge.h
@@ -0,0 +1,106 @@
+/*
+ * 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 WEBCORE_VIEW_BRIDGE_H
+#define WEBCORE_VIEW_BRIDGE_H
+
+// TODO: move this outside of jni directory
+
+#include "IntRect.h"
+#include "WebCoreRefObject.h"
+
+namespace WebCore
+{
+ class GraphicsContext;
+}
+
+class WebCoreViewBridge : public WebCoreRefObject {
+public:
+ WebCoreViewBridge() { }
+ virtual ~WebCoreViewBridge() { }
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect) = 0;
+
+ const WebCore::IntRect& getBounds() const
+ {
+ return m_bounds;
+ }
+
+ const WebCore::IntRect& getVisibleBounds() const
+ {
+ return m_visibleBounds;
+ }
+
+ const WebCore::IntRect& getWindowBounds() const
+ {
+ return m_windowBounds;
+ }
+
+ void setSize(int w, int h)
+ {
+ m_bounds.setWidth(w);
+ m_bounds.setHeight(h);
+ }
+
+ void setVisibleSize(int w, int h)
+ {
+ m_visibleBounds.setWidth(w);
+ m_visibleBounds.setHeight(h);
+ }
+
+ void setLocation(int x, int y)
+ {
+ m_bounds.setX(x);
+ m_bounds.setY(y);
+ m_visibleBounds.setX(x);
+ m_visibleBounds.setY(y);
+ }
+
+ void setWindowBounds(int x, int y, int h, int v)
+ {
+ m_windowBounds = WebCore::IntRect(x, y, h, v);
+ }
+
+ int width() const { return m_bounds.width(); }
+ int height() const { return m_bounds.height(); }
+ int locX() const { return m_bounds.x(); }
+ int locY() const { return m_bounds.y(); }
+
+ int visibleWidth() const { return m_visibleBounds.width(); }
+ int visibleHeight() const { return m_visibleBounds.height(); }
+ int visibleX() const { return m_visibleBounds.x(); }
+ int visibleY() const { return m_visibleBounds.y(); }
+
+ virtual bool forFrameView() const { return false; }
+ virtual bool forPluginView() const { return false; }
+
+private:
+ WebCore::IntRect m_bounds;
+ WebCore::IntRect m_windowBounds;
+ WebCore::IntRect m_visibleBounds;
+};
+
+#endif // WEBCORE_VIEW_BRIDGE_H
diff --git a/Source/WebKit/android/jni/WebFrameView.cpp b/Source/WebKit/android/jni/WebFrameView.cpp
new file mode 100644
index 0000000..8e5eac4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebFrameView.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "webcoreglue"
+
+#include <config.h>
+#include "WebFrameView.h"
+
+#include "android_graphics.h"
+#include "GraphicsContext.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HostWindow.h"
+#include "PlatformGraphicsContext.h"
+#include "WebViewCore.h"
+
+#include <SkCanvas.h>
+
+namespace android {
+
+WebFrameView::WebFrameView(WebCore::FrameView* frameView, WebViewCore* webViewCore)
+ : WebCoreViewBridge()
+ , mFrameView(frameView)
+ , mWebViewCore(webViewCore) {
+ // attach itself to mFrameView
+ mFrameView->setPlatformWidget(this);
+ Retain(mWebViewCore);
+}
+
+WebFrameView::~WebFrameView() {
+ Release(mWebViewCore);
+}
+
+void WebFrameView::draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) {
+ WebCore::Frame* frame = mFrameView->frame();
+
+ 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
new file mode 100644
index 0000000..823f2b4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebFrameView.h
@@ -0,0 +1,65 @@
+/*
+ * 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 WEB_FRAMEVIEW_H
+#define WEB_FRAMEVIEW_H
+
+#include "WebCoreViewBridge.h"
+
+namespace WebCore {
+ class FrameView;
+}
+
+namespace android {
+ class WebViewCore;
+
+ class WebFrameView: public WebCoreViewBridge {
+ public:
+ WebFrameView(WebCore::FrameView* frameView, WebViewCore* webViewCore);
+ virtual ~WebFrameView();
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect);
+
+ WebViewCore* webViewCore() const {
+ return mWebViewCore;
+ }
+
+ void setView(WebCore::FrameView* frameView);
+
+ WebCore::FrameView* view() const {
+ return mFrameView;
+ }
+
+ virtual bool forFrameView() const { return true; }
+
+ private:
+ WebCore::FrameView* mFrameView;
+ WebViewCore* mWebViewCore;
+ };
+
+} // namespace android
+
+#endif // WEB_FRAMEVIEW_H
diff --git a/Source/WebKit/android/jni/WebHistory.cpp b/Source/WebKit/android/jni/WebHistory.cpp
new file mode 100644
index 0000000..97ce23b
--- /dev/null
+++ b/Source/WebKit/android/jni/WebHistory.cpp
@@ -0,0 +1,857 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "webhistory"
+
+#include "config.h"
+#include "WebHistory.h"
+
+#include "BackForwardList.h"
+#include "BackForwardListImpl.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "HistoryItem.h"
+#include "IconDatabase.h"
+#include "Page.h"
+#include "TextEncoding.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#include "WebIconDatabase.h"
+
+#include <JNIHelp.h>
+#include "JNIUtility.h"
+#include <SkUtils.h>
+#include <utils/misc.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+// Forward declarations
+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 {
+ jmethodID mAddHistoryItem;
+ jmethodID mRemoveHistoryItem;
+ jmethodID mSetCurrentIndex;
+} gWebBackForwardList;
+
+//--------------------------------------------------------------------------
+// WebBackForwardList native methods.
+//--------------------------------------------------------------------------
+
+static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
+{
+ 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());
+ RefPtr<WebCore::HistoryItem> current = list->currentItem();
+ // Remove each item instead of using close(). close() is intended to be used
+ // right before the list is deleted.
+ WebCore::HistoryItemVector& entries = list->entries();
+ int size = entries.size();
+ for (int i = size - 1; i >= 0; --i)
+ list->removeItem(entries[i].get());
+ // Add the current item back to the list.
+ if (current) {
+ current->setBridge(0);
+ // addItem will update the children to match the newly created bridge
+ list->addItem(current);
+
+ /*
+ * The Grand Prix site uses anchor navigations to change the display.
+ * WebKit tries to be smart and not load child frames that have the
+ * same history urls during an anchor navigation. This means that the
+ * current history item stored in the child frame's loader does not
+ * match the item found in the history tree. If we remove all the
+ * entries in the back/foward list, we have to restore the entire tree
+ * or else a HistoryItem might have a deleted parent.
+ *
+ * In order to restore the history tree correctly, we have to look up
+ * all the frames first and then look up the history item. We do this
+ * because the history item in the tree may be null at this point.
+ * Unfortunately, a HistoryItem can only search its immediately
+ * children so we do a breadth-first rebuild of the tree.
+ */
+
+ // Keep a small list of child frames to traverse.
+ WTF::Vector<WebCore::Frame*> frameQueue;
+ // Fix the top-level item.
+ pFrame->loader()->history()->setCurrentItem(current.get());
+ WebCore::Frame* child = pFrame->tree()->firstChild();
+ // Remember the parent history item so we can search for a child item.
+ RefPtr<WebCore::HistoryItem> parent = current;
+ while (child) {
+ // Use the old history item since the current one may have a
+ // deleted parent.
+ WebCore::HistoryItem* item = parent->childItemWithTarget(child->tree()->name());
+ child->loader()->history()->setCurrentItem(item);
+ // Append the first child to the queue if it exists. If there is no
+ // item, then we do not need to traverse the children since there
+ // will be no parent history item.
+ WebCore::Frame* firstChild;
+ if (item && (firstChild = child->tree()->firstChild()))
+ frameQueue.append(firstChild);
+ child = child->tree()->nextSibling();
+ // If we don't have a sibling for this frame and the queue isn't
+ // empty, use the next entry in the queue.
+ if (!child && !frameQueue.isEmpty()) {
+ child = frameQueue.at(0);
+ frameQueue.remove(0);
+ // Figure out the parent history item used when searching for
+ // the history item to use.
+ parent = child->tree()->parent()->loader()->history()->currentItem();
+ }
+ }
+ }
+}
+
+static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
+{
+ LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ WebCore::Page* page = pFrame->page();
+ WebCore::HistoryItem* currentItem =
+ static_cast<WebCore::BackForwardListImpl*>(page->backForwardList())->entries()[index].get();
+
+ // load the current page with FrameLoadTypeIndexedBackForward so that it
+ // will use cache when it is possible
+ page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
+}
+
+static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
+{
+ 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);
+ jsize size = env->GetArrayLength(data);
+
+ // Inflate the history tree into one HistoryItem or null if the inflation
+ // failed.
+ RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
+ 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
+ // incomplete HistoryItem but that is better than crashing due to a null
+ // item.
+ // We have a 2nd local variable since read_item_recursive may change the
+ // 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);
+ read_item_recursive(newItem.get(), &ptr, (int)size);
+ env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
+ bridge->setActive();
+
+ // Add the new item to the back/forward list.
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ pFrame->page()->backForwardList()->addItem(newItem);
+
+ // Update the item.
+ bridge->updateHistoryItem(newItem.get());
+}
+
+// 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)))
+
+jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
+{
+ if (!item)
+ return NULL;
+
+ // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
+ v.reserveCapacity(HISTORY_MIN_SIZE);
+
+ // Write the top-level history item and then write all the children
+ // recursively.
+ 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) {
+ // Do not want to update during inflation.
+ if (!m_active)
+ return;
+ WebHistoryItem* webItem = this;
+ // Now we need to update the top-most WebHistoryItem based on the top-most
+ // HistoryItem.
+ if (m_parent) {
+ webItem = m_parent.get();
+ if (webItem->hasOneRef()) {
+ // 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().
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ return;
+ }
+ while (webItem->parent())
+ webItem = webItem->parent();
+ item = webItem->historyItem();
+ if (!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.
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ return;
+ }
+ }
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ // Don't do anything if the item has been gc'd already
+ AutoJObject realItem = getRealObject(env, webItem->m_object);
+ if (!realItem.get())
+ return;
+
+ const WTF::String& urlString = item->urlString();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = wtfStringToJstring(env, urlString);
+ const WTF::String& originalUrlString = item->originalURLString();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull())
+ originalUrlStr = wtfStringToJstring(env, originalUrlString);
+ const WTF::String& titleString = item->title();
+ 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
+ // item, try to get the icon using the url without the ref.
+ jobject favicon = NULL;
+ WTF::String url = item->urlString();
+ if (item->url().hasFragmentIdentifier()) {
+ int refIndex = url.reverseFind('#');
+ url = url.substring(0, refIndex);
+ }
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(url,
+ WebCore::IntSize(16, 16));
+
+ 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) {
+ LOG_ASSERT(item, "historyItemChanged called with a null item");
+
+ if (item->bridge())
+ item->bridge()->updateHistoryItem(item);
+}
+
+void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
+{
+ 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();
+ // Allocate a blank WebHistoryItem
+ jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
+ 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);
+
+ // Add it to the list.
+ env->CallVoidMethod(list.get(), gWebBackForwardList.mAddHistoryItem, newItem);
+
+ // Delete our local reference.
+ env->DeleteLocalRef(newItem);
+}
+
+void WebHistory::RemoveItem(const AutoJObject& list, int index)
+{
+ if (list.get())
+ list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mRemoveHistoryItem, index);
+}
+
+void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
+{
+ if (list.get())
+ list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mSetCurrentIndex, newIndex);
+}
+
+static void write_string(WTF::Vector<char>& v, const WTF::String& str)
+{
+ unsigned strLen = str.length();
+ // Only do work if the string has data.
+ if (strLen) {
+ // 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 = 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.
+ v.grow(length);
+ // Grab the position to write to.
+ char* data = v.begin() + vectorLen;
+ // Write the actual string
+ int l = SkUTF16_ToUTF8(str.characters(), strLen, 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.
+ v.shrink(vectorLen + l);
+ } else
+ v.append((char*)&strLen, sizeof(unsigned));
+}
+
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item)
+{
+ // Original url
+ write_string(v, item->originalURLString());
+
+ // Url
+ write_string(v, item->urlString());
+
+ // Title
+ write_string(v, item->title());
+
+ // Form content type
+ write_string(v, item->formContentType());
+
+ // Form data
+ const WebCore::FormData* formData = item->formData();
+ if (formData) {
+ write_string(v, formData->flattenToString());
+ // save the identifier as it is not included in the flatten data
+ int64_t id = formData->identifier();
+ v.append((char*)&id, sizeof(int64_t));
+ } else
+ write_string(v, WTF::String()); // Empty constructor does not allocate a buffer.
+
+ // Target
+ write_string(v, item->target());
+
+ AndroidWebHistoryBridge* bridge = item->bridge();
+ LOG_ASSERT(bridge, "We should have a bridge here!");
+ // Screen scale
+ const float scale = bridge->scale();
+ LOGV("Writing scale %f", scale);
+ v.append((char*)&scale, sizeof(float));
+ const float textWrapScale = bridge->textWrapScale();
+ LOGV("Writing text wrap scale %f", textWrapScale);
+ v.append((char*)&textWrapScale, sizeof(float));
+
+ // Scroll position.
+ const int scrollX = item->scrollPoint().x();
+ v.append((char*)&scrollX, sizeof(int));
+ const int scrollY = item->scrollPoint().y();
+ 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();
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
+ for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
+ write_string(v, *i);
+ }
+
+ // Is target item
+ LOGV("Writing isTargetItem %d", item->isTargetItem());
+ v.append((char)item->isTargetItem());
+
+ // Children count
+ unsigned childCount = item->children().size();
+ LOGV("Writing childCount %d", childCount);
+ v.append((char*)&childCount, sizeof(unsigned));
+}
+
+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();
+ LOG_ASSERT(parent->bridge(),
+ "The parent item should have a bridge object!");
+ if (!item->bridge()) {
+ WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
+ item->setBridge(bridge);
+ bridge->setActive();
+ } else {
+ // The only time this item's parent may not be the same as the
+ // parent's bridge is during history close. In that case, the
+ // parent must not have a parent bridge.
+ WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
+ WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
+ LOG_ASSERT(parentBridge->parent() == 0 ||
+ bridge->parent() == parentBridge,
+ "Somehow this item has an incorrect parent");
+ bridge->setParent(parentBridge);
+ }
+ write_item(v, item);
+ write_children_recursive(v, item);
+ }
+}
+
+static bool read_item_recursive(WebCore::HistoryItem* newItem,
+ const char** pData, int length)
+{
+ if (!pData || length < HISTORY_MIN_SIZE)
+ return false;
+
+ const WebCore::TextEncoding& e = WebCore::UTF8Encoding();
+ const char* data = *pData;
+ const char* end = data + length;
+ int sizeofUnsigned = (int)sizeof(unsigned);
+
+ // Read the original url
+ // Read the expected length of the string.
+ int 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
+ 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
+ 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.
+ WTF::String formContentType;
+ WTF::PassRefPtr<WebCore::FormData> formData = NULL;
+
+ // 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
+ 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;
+ data += l;
+ // Read the identifier
+ {
+ 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) {
+ WebCore::ResourceRequest r;
+ r.setHTTPMethod("POST");
+ r.setHTTPContentType(formContentType);
+ r.setHTTPBody(formData);
+ newItem->setFormInfoFromRequest(r);
+ }
+
+ // Read the target
+ 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();
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
+ float fValue;
+ // 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);
+
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read scroll position.
+ int scrollX = 0;
+ memcpy(&scrollX, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ int scrollY = 0;
+ memcpy(&scrollY, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ newItem->setScrollPoint(IntPoint(scrollX, scrollY));
+
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // 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(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
+ // 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
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Child count %d", l);
+ data += sizeofUnsigned;
+ *pData = data;
+ 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::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 (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
+ return false;
+ }
+ child->bridge()->setActive();
+ newItem->addChildItem(child);
+ }
+ }
+ return true;
+}
+
+// On arm, this test will cause memory corruption since converting char* will
+// byte align the result and this test does not use memset (it probably
+// should).
+// On the simulator, using HistoryItem will invoke the IconDatabase which will
+// initialize the main thread. Since this is invoked by the Zygote process, the
+// 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 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));
+ LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!");
+ delete[] test1;
+ const char* test2 = new char[2];
+ LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!");
+ delete[] test2;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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) = '!';
+ 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;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ offset = 36;
+ // Test document state
+ 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;
+ 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));
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 2;
+ *(int*)(test3 + offset + 4) = 0;
+ *(int*)(test3 + offset + 8) = 20;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
+ delete[] test3;
+}
+#endif
+
+//---------------------------------------------------------
+// JNI registration
+//---------------------------------------------------------
+static JNINativeMethod gWebBackForwardListMethods[] = {
+ { "nativeClose", "(I)V",
+ (void*) WebHistoryClose },
+ { "restoreIndex", "(II)V",
+ (void*) WebHistoryRestoreIndex }
+};
+
+static JNINativeMethod gWebHistoryItemMethods[] = {
+ { "inflate", "(I[B)V",
+ (void*) WebHistoryInflate }
+};
+
+int registerWebHistory(JNIEnv* env)
+{
+ // Get notified of all changes to history items.
+ WebCore::notifyHistoryItemChanged = historyItemChanged;
+#ifdef UNIT_TEST
+ unit_test();
+#endif
+ // Find WebHistoryItem, its constructor, and the update method.
+ jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
+ 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");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
+ gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
+ "(Landroid/webkit/WebHistoryItem;)V");
+ LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
+ gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
+ "(I)V");
+ LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
+ gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
+ LOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
+ env->DeleteLocalRef(clazz);
+
+ int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",
+ gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods));
+ return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem",
+ gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebHistory.h b/Source/WebKit/android/jni/WebHistory.h
new file mode 100644
index 0000000..2d86aa4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebHistory.h
@@ -0,0 +1,68 @@
+/*
+ * 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 ANDROID_WEBKIT_WEBHISTORY_H
+#define ANDROID_WEBKIT_WEBHISTORY_H
+
+#include "AndroidWebHistoryBridge.h"
+
+#include <jni.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace android {
+
+class AutoJObject;
+
+class WebHistory {
+public:
+ 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);
+};
+
+// there are two scale factors saved with each history item. m_scale reflects the
+// viewport scale factor, default to 100 means 100%. m_textWrapScale records
+// the scale factor for wrapping the text paragraph.
+class WebHistoryItem : public WebCore::AndroidWebHistoryBridge {
+public:
+ WebHistoryItem(WebHistoryItem* parent)
+ : WebCore::AndroidWebHistoryBridge(0)
+ , m_parent(parent)
+ , 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(); }
+private:
+ RefPtr<WebHistoryItem> m_parent;
+ jweak m_object;
+};
+
+};
+
+#endif
diff --git a/Source/WebKit/android/jni/WebIconDatabase.cpp b/Source/WebKit/android/jni/WebIconDatabase.cpp
new file mode 100644
index 0000000..2a660d1
--- /dev/null
+++ b/Source/WebKit/android/jni/WebIconDatabase.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 "favicons"
+
+#include "config.h"
+#include "WebIconDatabase.h"
+
+#include "FileSystem.h"
+#include "GraphicsJNI.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SharedBuffer.h>
+#include <SkBitmap.h>
+#include <SkImageDecoder.h>
+#include <SkTemplates.h>
+#include <pthread.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon)
+{
+ if (!icon)
+ return NULL;
+ SkBitmap bm;
+ WebCore::SharedBuffer* buffer = icon->data();
+ if (!buffer || !SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(),
+ &bm, SkBitmap::kNo_Config,
+ SkImageDecoder::kDecodePixels_Mode))
+ return NULL;
+
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bm), false, NULL);
+}
+
+static WebIconDatabase* gIconDatabaseClient = new WebIconDatabase();
+
+// XXX: Called by the IconDatabase thread
+void WebIconDatabase::dispatchDidAddIconForPageURL(const WTF::String& pageURL)
+{
+ mNotificationsMutex.lock();
+ mNotifications.append(pageURL);
+ if (!mDeliveryRequested) {
+ mDeliveryRequested = true;
+ JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this);
+ }
+ mNotificationsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ // Do not add the same client twice.
+ if (db->mClients[i] == client)
+ return;
+ }
+ gIconDatabaseClient->mClients.append(client);
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ if (db->mClients[i] == client) {
+ db->mClients.remove(i);
+ break;
+ }
+ }
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::DeliverNotifications(void* v)
+{
+ ASSERT(v);
+ ((WebIconDatabase*)v)->deliverNotifications();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::deliverNotifications()
+{
+ ASSERT(mDeliveryRequested);
+
+ // Swap the notifications queue
+ Vector<WTF::String> queue;
+ mNotificationsMutex.lock();
+ queue.swap(mNotifications);
+ mDeliveryRequested = false;
+ mNotificationsMutex.unlock();
+
+ // Swap the clients queue
+ Vector<WebIconDatabaseClient*> clients;
+ clients.swap(mClients);
+
+ for (unsigned i = 0; i < queue.size(); ++i) {
+ for (unsigned j = 0; j < clients.size(); ++j) {
+ clients[j]->didAddIconForPageUrl(queue[i]);
+ }
+ }
+}
+
+static void Open(JNIEnv* env, jobject obj, jstring path)
+{
+ WebCore::IconDatabase* iconDb = WebCore::iconDatabase();
+ if (iconDb->isOpen())
+ return;
+ iconDb->setEnabled(true);
+ iconDb->setClient(gIconDatabaseClient);
+ LOG_ASSERT(path, "No path given to nativeOpen");
+ WTF::String pathStr = jstringToWtfString(env, path);
+ WTF::CString fullPath = WebCore::pathByAppendingComponent(pathStr,
+ WebCore::IconDatabase::defaultDatabaseFilename()).utf8();
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+ bool didSetPermissions = false;
+ if (access(fullPath.data(), F_OK) == 0) {
+ if (chmod(fullPath.data(), mode) == 0)
+ didSetPermissions = true;
+ } else {
+ int fd = open(fullPath.data(), O_CREAT, mode);
+ if (fd >= 0) {
+ close(fd);
+ didSetPermissions = true;
+ }
+ }
+ if (didSetPermissions) {
+ LOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
+ bool res = iconDb->open(pathStr);
+ if (!res)
+ LOGE("Open failed!");
+ } else
+ LOGE("Failed to set permissions on '%s'", fullPath.data());
+}
+
+static void Close(JNIEnv* env, jobject obj)
+{
+ WebCore::iconDatabase()->close();
+}
+
+static void RemoveAllIcons(JNIEnv* env, jobject obj)
+{
+ LOGV("Removing all icons");
+ WebCore::iconDatabase()->removeAllIcons();
+}
+
+static jobject IconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to iconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlStr,
+ WebCore::IntSize(16, 16));
+ LOGV("Retrieving icon for '%s' %p", urlStr.latin1().data(), icon);
+ return webcoreImageToJavaBitmap(env, icon);
+}
+
+static void RetainIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to retainIconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ LOGV("Retaining icon for '%s'", urlStr.latin1().data());
+ WebCore::iconDatabase()->retainIconForPageURL(urlStr);
+}
+
+static void ReleaseIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to releaseIconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ LOGV("Releasing icon for '%s'", urlStr.latin1().data());
+ WebCore::iconDatabase()->releaseIconForPageURL(urlStr);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gWebIconDatabaseMethods[] = {
+ { "nativeOpen", "(Ljava/lang/String;)V",
+ (void*) Open },
+ { "nativeClose", "()V",
+ (void*) Close },
+ { "nativeRemoveAllIcons", "()V",
+ (void*) RemoveAllIcons },
+ { "nativeIconForPageUrl", "(Ljava/lang/String;)Landroid/graphics/Bitmap;",
+ (void*) IconForPageUrl },
+ { "nativeRetainIconForPageUrl", "(Ljava/lang/String;)V",
+ (void*) RetainIconForPageUrl },
+ { "nativeReleaseIconForPageUrl", "(Ljava/lang/String;)V",
+ (void*) ReleaseIconForPageUrl }
+};
+
+int registerWebIconDatabase(JNIEnv* env)
+{
+#ifndef NDEBUG
+ 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/WebIconDatabase",
+ gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/WebIconDatabase.h b/Source/WebKit/android/jni/WebIconDatabase.h
new file mode 100644
index 0000000..b2169aa
--- /dev/null
+++ b/Source/WebKit/android/jni/WebIconDatabase.h
@@ -0,0 +1,75 @@
+/*
+ * 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 ANDROID_WEBKIT_WEBICONDATABASE_H
+#define ANDROID_WEBKIT_WEBICONDATABASE_H
+
+#include "IconDatabaseClient.h"
+#include "PlatformString.h"
+#include "utils/threads.h"
+#include <jni.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class Image;
+}
+
+namespace android {
+
+ class WebIconDatabaseClient {
+ public:
+ virtual ~WebIconDatabaseClient() {}
+ virtual void didAddIconForPageUrl(const WTF::String& pageUrl) = 0;
+ };
+
+ class WebIconDatabase : public WebCore::IconDatabaseClient {
+ public:
+ WebIconDatabase() : mDeliveryRequested(false) {}
+ // IconDatabaseClient method
+ virtual void dispatchDidAddIconForPageURL(const WTF::String& pageURL);
+
+ static void RegisterForIconNotification(WebIconDatabaseClient* client);
+ static void UnregisterForIconNotification(WebIconDatabaseClient* client);
+ static void DeliverNotifications(void*);
+
+ private:
+ // Deliver all the icon notifications
+ void deliverNotifications();
+
+ // List of clients.
+ Vector<WebIconDatabaseClient*> mClients;
+
+ // Queue of page urls that have received an icon.
+ Vector<WTF::String> mNotifications;
+ android::Mutex mNotificationsMutex;
+ // Flag to indicate that we have requested a delivery of notifications.
+ bool mDeliveryRequested;
+ };
+
+ jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon);
+
+};
+
+#endif
diff --git a/Source/WebKit/android/jni/WebSettings.cpp b/Source/WebKit/android/jni/WebSettings.cpp
new file mode 100644
index 0000000..468e7b0
--- /dev/null
+++ b/Source/WebKit/android/jni/WebSettings.cpp
@@ -0,0 +1,599 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "websettings"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "ApplicationCacheStorage.h"
+#include "BitmapAllocatorAndroid.h"
+#include "CachedResourceLoader.h"
+#include "ChromiumIncludes.h"
+#include "DatabaseTracker.h"
+#include "Database.h"
+#include "Document.h"
+#include "EditorClientAndroid.h"
+#include "FileSystem.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "GeolocationPermissions.h"
+#include "GeolocationPositionCache.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "RenderTable.h"
+#include "SQLiteFileSystem.h"
+#include "Settings.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#if USE(V8)
+#include "WorkerContextExecutionProxy.h"
+#endif
+#include "WebRequestContext.h"
+#include "WebViewCore.h"
+
+#include <JNIHelp.h>
+#include <utils/misc.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+static const int permissionFlags660 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+struct FieldIds {
+ FieldIds(JNIEnv* env, jclass clazz) {
+ mLayoutAlgorithm = env->GetFieldID(clazz, "mLayoutAlgorithm",
+ "Landroid/webkit/WebSettings$LayoutAlgorithm;");
+ mTextSize = env->GetFieldID(clazz, "mTextSize",
+ "Landroid/webkit/WebSettings$TextSize;");
+ mStandardFontFamily = env->GetFieldID(clazz, "mStandardFontFamily",
+ "Ljava/lang/String;");
+ mFixedFontFamily = env->GetFieldID(clazz, "mFixedFontFamily",
+ "Ljava/lang/String;");
+ mSansSerifFontFamily = env->GetFieldID(clazz, "mSansSerifFontFamily",
+ "Ljava/lang/String;");
+ mSerifFontFamily = env->GetFieldID(clazz, "mSerifFontFamily",
+ "Ljava/lang/String;");
+ mCursiveFontFamily = env->GetFieldID(clazz, "mCursiveFontFamily",
+ "Ljava/lang/String;");
+ mFantasyFontFamily = env->GetFieldID(clazz, "mFantasyFontFamily",
+ "Ljava/lang/String;");
+ mDefaultTextEncoding = env->GetFieldID(clazz, "mDefaultTextEncoding",
+ "Ljava/lang/String;");
+ mUserAgent = env->GetFieldID(clazz, "mUserAgent",
+ "Ljava/lang/String;");
+ mAcceptLanguage = env->GetFieldID(clazz, "mAcceptLanguage", "Ljava/lang/String;");
+ mMinimumFontSize = env->GetFieldID(clazz, "mMinimumFontSize", "I");
+ mMinimumLogicalFontSize = env->GetFieldID(clazz, "mMinimumLogicalFontSize", "I");
+ mDefaultFontSize = env->GetFieldID(clazz, "mDefaultFontSize", "I");
+ mDefaultFixedFontSize = env->GetFieldID(clazz, "mDefaultFixedFontSize", "I");
+ mLoadsImagesAutomatically = env->GetFieldID(clazz, "mLoadsImagesAutomatically", "Z");
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ mBlockNetworkImage = env->GetFieldID(clazz, "mBlockNetworkImage", "Z");
+#endif
+ mBlockNetworkLoads = env->GetFieldID(clazz, "mBlockNetworkLoads", "Z");
+ mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z");
+ mPluginState = env->GetFieldID(clazz, "mPluginState",
+ "Landroid/webkit/WebSettings$PluginState;");
+#if ENABLE(DATABASE)
+ mDatabaseEnabled = env->GetFieldID(clazz, "mDatabaseEnabled", "Z");
+#endif
+#if ENABLE(DOM_STORAGE)
+ mDomStorageEnabled = env->GetFieldID(clazz, "mDomStorageEnabled", "Z");
+#endif
+#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
+ // The databases saved to disk for both the SQL and DOM Storage APIs are stored
+ // in the same base directory.
+ mDatabasePath = env->GetFieldID(clazz, "mDatabasePath", "Ljava/lang/String;");
+ mDatabasePathHasBeenSet = env->GetFieldID(clazz, "mDatabasePathHasBeenSet", "Z");
+#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ mAppCacheEnabled = env->GetFieldID(clazz, "mAppCacheEnabled", "Z");
+ mAppCachePath = env->GetFieldID(clazz, "mAppCachePath", "Ljava/lang/String;");
+ mAppCacheMaxSize = env->GetFieldID(clazz, "mAppCacheMaxSize", "J");
+#endif
+#if ENABLE(WORKERS)
+ mWorkersEnabled = env->GetFieldID(clazz, "mWorkersEnabled", "Z");
+#endif
+ mGeolocationEnabled = env->GetFieldID(clazz, "mGeolocationEnabled", "Z");
+ mGeolocationDatabasePath = env->GetFieldID(clazz, "mGeolocationDatabasePath", "Ljava/lang/String;");
+ mXSSAuditorEnabled = env->GetFieldID(clazz, "mXSSAuditorEnabled", "Z");
+ mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
+ "mJavaScriptCanOpenWindowsAutomatically", "Z");
+ mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
+ mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z");
+ mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z");
+ mMaximumDecodedImageSize = env->GetFieldID(clazz, "mMaximumDecodedImageSize", "J");
+ mPrivateBrowsingEnabled = env->GetFieldID(clazz, "mPrivateBrowsingEnabled", "Z");
+ mSyntheticLinksEnabled = env->GetFieldID(clazz, "mSyntheticLinksEnabled", "Z");
+ mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z");
+ mPageCacheCapacity = env->GetFieldID(clazz, "mPageCacheCapacity", "I");
+#if ENABLE(WEB_AUTOFILL)
+ mAutoFillEnabled = env->GetFieldID(clazz, "mAutoFillEnabled", "Z");
+ 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;");
+ mAutoFillProfileAddressLine1 = env->GetFieldID(autoFillProfileClass, "mAddressLine1", "Ljava/lang/String;");
+ mAutoFillProfileAddressLine2 = env->GetFieldID(autoFillProfileClass, "mAddressLine2", "Ljava/lang/String;");
+ mAutoFillProfileCity = env->GetFieldID(autoFillProfileClass, "mCity", "Ljava/lang/String;");
+ mAutoFillProfileState = env->GetFieldID(autoFillProfileClass, "mState", "Ljava/lang/String;");
+ mAutoFillProfileZipCode = env->GetFieldID(autoFillProfileClass, "mZipCode", "Ljava/lang/String;");
+ mAutoFillProfileCountry = env->GetFieldID(autoFillProfileClass, "mCountry", "Ljava/lang/String;");
+ mAutoFillProfilePhoneNumber = env->GetFieldID(autoFillProfileClass, "mPhoneNumber", "Ljava/lang/String;");
+ env->DeleteLocalRef(autoFillProfileClass);
+#endif
+#if USE(CHROME_NETWORK_STACK)
+ mOverrideCacheMode = env->GetFieldID(clazz, "mOverrideCacheMode", "I");
+#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(mUserAgent, "Could not find field mUserAgent");
+ LOG_ASSERT(mAcceptLanguage, "Could not find field mAcceptLanguage");
+ 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
+ LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage");
+#endif
+ LOG_ASSERT(mBlockNetworkLoads, "Could not find field mBlockNetworkLoads");
+ LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
+ LOG_ASSERT(mPluginState, "Could not find field mPluginState");
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ 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)
+ LOG_ASSERT(mWorkersEnabled, "Could not find field mWorkersEnabled");
+#endif
+ LOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically,
+ "Could not find field mJavaScriptCanOpenWindowsAutomatically");
+ 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");
+ LOG_ASSERT(enumClass, "Could not find Enum class!");
+ mOrdinal = env->GetMethodID(enumClass, "ordinal", "()I");
+ LOG_ASSERT(mOrdinal, "Could not find method ordinal");
+ env->DeleteLocalRef(enumClass);
+
+ jclass textSizeClass = env->FindClass("android/webkit/WebSettings$TextSize");
+ LOG_ASSERT(textSizeClass, "Could not find TextSize enum");
+ mTextSizeValue = env->GetFieldID(textSizeClass, "value", "I");
+ env->DeleteLocalRef(textSizeClass);
+ }
+
+ // Field ids
+ jfieldID mLayoutAlgorithm;
+ jfieldID mTextSize;
+ jfieldID mStandardFontFamily;
+ jfieldID mFixedFontFamily;
+ jfieldID mSansSerifFontFamily;
+ jfieldID mSerifFontFamily;
+ jfieldID mCursiveFontFamily;
+ jfieldID mFantasyFontFamily;
+ jfieldID mDefaultTextEncoding;
+ jfieldID mUserAgent;
+ jfieldID mAcceptLanguage;
+ jfieldID mMinimumFontSize;
+ jfieldID mMinimumLogicalFontSize;
+ jfieldID mDefaultFontSize;
+ jfieldID mDefaultFixedFontSize;
+ jfieldID mLoadsImagesAutomatically;
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ jfieldID mBlockNetworkImage;
+#endif
+ jfieldID mBlockNetworkLoads;
+ jfieldID mJavaScriptEnabled;
+ jfieldID mPluginState;
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ jfieldID mAppCacheEnabled;
+ jfieldID mAppCachePath;
+ jfieldID mAppCacheMaxSize;
+#endif
+#if ENABLE(WORKERS)
+ jfieldID mWorkersEnabled;
+#endif
+ jfieldID mJavaScriptCanOpenWindowsAutomatically;
+ jfieldID mUseWideViewport;
+ jfieldID mSupportMultipleWindows;
+ jfieldID mShrinksStandaloneImagesToFit;
+ jfieldID mMaximumDecodedImageSize;
+ jfieldID mPrivateBrowsingEnabled;
+ jfieldID mSyntheticLinksEnabled;
+ jfieldID mUseDoubleTree;
+ jfieldID mPageCacheCapacity;
+ // Ordinal() method and value field for enums
+ jmethodID mOrdinal;
+ jfieldID mTextSizeValue;
+
+#if ENABLE(DATABASE)
+ jfieldID mDatabaseEnabled;
+#endif
+#if ENABLE(DOM_STORAGE)
+ jfieldID mDomStorageEnabled;
+#endif
+ jfieldID mGeolocationEnabled;
+ jfieldID mGeolocationDatabasePath;
+ jfieldID mXSSAuditorEnabled;
+#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
+ jfieldID mDatabasePath;
+ jfieldID mDatabasePathHasBeenSet;
+#endif
+#if ENABLE(WEB_AUTOFILL)
+ jfieldID mAutoFillEnabled;
+ jfieldID mAutoFillProfile;
+ jfieldID mAutoFillProfileFullName;
+ jfieldID mAutoFillProfileEmailAddress;
+ jfieldID mAutoFillProfileCompanyName;
+ jfieldID mAutoFillProfileAddressLine1;
+ jfieldID mAutoFillProfileAddressLine2;
+ jfieldID mAutoFillProfileCity;
+ jfieldID mAutoFillProfileState;
+ jfieldID mAutoFillProfileZipCode;
+ jfieldID mAutoFillProfileCountry;
+ jfieldID mAutoFillProfilePhoneNumber;
+#endif
+#if USE(CHROME_NETWORK_STACK)
+ jfieldID mOverrideCacheMode;
+#endif
+};
+
+static struct FieldIds* gFieldIds;
+
+// Note: This is moved from the old FrameAndroid.cpp
+static void recursiveCleanupForFullLayout(WebCore::RenderObject* obj)
+{
+ obj->setNeedsLayout(true, false);
+#ifdef ANDROID_LAYOUT
+ if (obj->isTable())
+ (static_cast<WebCore::RenderTable *>(obj))->clearSingleColumn();
+#endif
+ for (WebCore::RenderObject* n = obj->firstChild(); n; n = n->nextSibling())
+ recursiveCleanupForFullLayout(n);
+}
+
+#if ENABLE(WEB_AUTOFILL)
+inline string16 getStringFieldAsString16(JNIEnv* env, jobject autoFillProfile, jfieldID fieldId)
+{
+ jstring str = static_cast<jstring>(env->GetObjectField(autoFillProfile, fieldId));
+ return str ? jstringToString16(env, str) : string16();
+}
+
+void syncAutoFillProfile(JNIEnv* env, jobject autoFillProfile, WebAutoFill* webAutoFill)
+{
+ string16 fullName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileFullName);
+ string16 emailAddress = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileEmailAddress);
+ string16 companyName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCompanyName);
+ string16 addressLine1 = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileAddressLine1);
+ string16 addressLine2 = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileAddressLine2);
+ string16 city = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCity);
+ string16 state = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileState);
+ string16 zipCode = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileZipCode);
+ string16 country = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCountry);
+ string16 phoneNumber = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfilePhoneNumber);
+
+ webAutoFill->setProfile(fullName, emailAddress, companyName, addressLine1, addressLine2, city, state, zipCode, country, phoneNumber);
+}
+#endif
+
+class WebSettings {
+public:
+ static void Sync(JNIEnv* env, jobject obj, jint frame)
+ {
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ LOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__);
+ WebCore::Settings* s = pFrame->settings();
+ if (!s)
+ return;
+ WebCore::CachedResourceLoader* cachedResourceLoader = pFrame->document()->cachedResourceLoader();
+
+#ifdef ANDROID_LAYOUT
+ jobject layout = env->GetObjectField(obj, gFieldIds->mLayoutAlgorithm);
+ WebCore::Settings::LayoutAlgorithm l = (WebCore::Settings::LayoutAlgorithm)
+ env->CallIntMethod(layout, gFieldIds->mOrdinal);
+ if (s->layoutAlgorithm() != l) {
+ s->setLayoutAlgorithm(l);
+ if (pFrame->document()) {
+ pFrame->document()->styleSelectorChanged(WebCore::RecalcStyleImmediately);
+ if (pFrame->document()->renderer()) {
+ recursiveCleanupForFullLayout(pFrame->document()->renderer());
+ 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
+ // WebViewCore, on the UI side, so there needs to be a new way to do this.
+ //pFrame->makeFocusVisible();
+ }
+ }
+ }
+#endif
+ jobject textSize = env->GetObjectField(obj, gFieldIds->mTextSize);
+ float zoomFactor = env->GetIntField(textSize, gFieldIds->mTextSizeValue) / 100.0f;
+ if (pFrame->textZoomFactor() != zoomFactor)
+ pFrame->setTextZoomFactor(zoomFactor);
+
+ jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily);
+ s->setStandardFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily);
+ s->setFixedFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily);
+ s->setSansSerifFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily);
+ s->setSerifFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily);
+ s->setCursiveFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily);
+ s->setFantasyFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding);
+ s->setDefaultTextEncodingName(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent);
+ WebFrame::getWebFrame(pFrame)->setUserAgent(jstringToWtfString(env, str));
+#if USE(CHROME_NETWORK_STACK)
+ WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextUserAgent();
+
+ jint cacheMode = env->GetIntField(obj, gFieldIds->mOverrideCacheMode);
+ WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextCacheMode(cacheMode);
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mAcceptLanguage);
+ WebRequestContext::setAcceptLanguage(jstringToWtfString(env, str));
+#endif
+
+ jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize);
+ s->setMinimumFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mMinimumLogicalFontSize);
+ s->setMinimumLogicalFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mDefaultFontSize);
+ s->setDefaultFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mDefaultFixedFontSize);
+ s->setDefaultFixedFontSize(size);
+
+ jboolean flag = env->GetBooleanField(obj, gFieldIds->mLoadsImagesAutomatically);
+ s->setLoadsImagesAutomatically(flag);
+ if (flag)
+ cachedResourceLoader->setAutoLoadImages(true);
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage);
+ s->setBlockNetworkImage(flag);
+ if(!flag)
+ cachedResourceLoader->setBlockNetworkImage(false);
+#endif
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkLoads);
+ WebFrame* webFrame = WebFrame::getWebFrame(pFrame);
+ webFrame->setBlockNetworkLoads(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled);
+ s->setJavaScriptEnabled(flag);
+
+ // ON = 0
+ // ON_DEMAND = 1
+ // OFF = 2
+ jobject pluginState = env->GetObjectField(obj, gFieldIds->mPluginState);
+ int state = env->CallIntMethod(pluginState, gFieldIds->mOrdinal);
+ s->setPluginsEnabled(state < 2);
+#ifdef ANDROID_PLUGINS
+ s->setPluginsOnDemand(state == 1);
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ 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 | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+ }
+ jlong maxsize = env->GetLongField(obj, gFieldIds->mAppCacheMaxSize);
+ cacheStorage().setMaximumSize(maxsize);
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptCanOpenWindowsAutomatically);
+ s->setJavaScriptCanOpenWindowsAutomatically(flag);
+
+#ifdef ANDROID_LAYOUT
+ flag = env->GetBooleanField(obj, gFieldIds->mUseWideViewport);
+ s->setUseWideViewport(flag);
+#endif
+
+#ifdef ANDROID_MULTIPLE_WINDOWS
+ flag = env->GetBooleanField(obj, gFieldIds->mSupportMultipleWindows);
+ s->setSupportMultipleWindows(flag);
+#endif
+ flag = env->GetBooleanField(obj, gFieldIds->mShrinksStandaloneImagesToFit);
+ s->setShrinksStandaloneImagesToFit(flag);
+ jlong maxImage = env->GetLongField(obj, gFieldIds->mMaximumDecodedImageSize);
+ // Since in ImageSourceAndroid.cpp, the image will always not exceed
+ // MAX_SIZE_BEFORE_SUBSAMPLE, there's no need to pass the max value to
+ // WebCore, which checks (image_width * image_height * 4) as an
+ // estimation against the max value, which is done in CachedImage.cpp.
+ // And there're cases where the decoded image size will not
+ // exceed the max, but the WebCore estimation will. So the following
+ // code is commented out to fix those cases.
+ // if (maxImage == 0)
+ // maxImage = computeMaxBitmapSizeForCache();
+ s->setMaximumDecodedImageSize(maxImage);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mPrivateBrowsingEnabled);
+ s->setPrivateBrowsingEnabled(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mSyntheticLinksEnabled);
+ s->setDefaultFormatDetection(flag);
+ s->setFormatDetectionAddress(flag);
+ s->setFormatDetectionEmail(flag);
+ s->setFormatDetectionTelephone(flag);
+#if ENABLE(DATABASE)
+ flag = env->GetBooleanField(obj, gFieldIds->mDatabaseEnabled);
+ WebCore::Database::setIsAvailable(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mDatabasePathHasBeenSet);
+ if (flag) {
+ // If the user has set the database path, sync it to the DatabaseTracker.
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ DatabaseTracker::tracker().setDatabaseDirectoryPath(path);
+ // This database is created when the first HTML5 Database object is
+ // instantiated. If the file doesn't exist, we create it and set its
+ // permissions. The filename must match that in
+ // DatabaseTracker.cpp.
+ String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(path, "Databases.db");
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+ }
+#endif
+#if ENABLE(DOM_STORAGE)
+ flag = env->GetBooleanField(obj, gFieldIds->mDomStorageEnabled);
+ s->setLocalStorageEnabled(flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
+ if (str) {
+ WTF::String localStorageDatabasePath = jstringToWtfString(env,str);
+ if (localStorageDatabasePath.length()) {
+ localStorageDatabasePath = WebCore::pathByAppendingComponent(
+ localStorageDatabasePath, "localstorage");
+ // We need 770 for folders
+ mkdir(localStorageDatabasePath.utf8().data(),
+ permissionFlags660 | S_IXUSR | S_IXGRP);
+ s->setLocalStorageDatabasePath(localStorageDatabasePath);
+ }
+ }
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mGeolocationEnabled);
+ GeolocationPermissions::setAlwaysDeny(!flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mGeolocationDatabasePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ GeolocationPermissions::setDatabasePath(path);
+ GeolocationPositionCache::instance()->setDatabasePath(path);
+ // This database is created when the first Geolocation object is
+ // instantiated. If the file doesn't exist, we create it and set its
+ // permissions. The filename must match that in
+ // GeolocationPositionCache.cpp.
+ String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(path, "CachedGeoposition.db");
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+
+ flag = env->GetBooleanField(obj, gFieldIds->mXSSAuditorEnabled);
+ s->setXSSAuditorEnabled(flag);
+
+ size = env->GetIntField(obj, gFieldIds->mPageCacheCapacity);
+ if (size > 0) {
+ s->setUsesPageCache(true);
+ WebCore::pageCache()->setCapacity(size);
+ } else
+ s->setUsesPageCache(false);
+
+#if ENABLE(WEB_AUTOFILL)
+ flag = env->GetBooleanField(obj, gFieldIds->mAutoFillEnabled);
+ // TODO: This updates the Settings WebCore side with the user's
+ // preference for autofill and will stop WebCore making requests
+ // into the chromium autofill code. That code in Chromium also has
+ // a notion of being enabled/disabled that gets read from the users
+ // preferences. At the moment, it's hardcoded to true on Android
+ // (see chrome/browser/autofill/autofill_manager.cc:405). This
+ // setting should probably be synced into Chromium also.
+
+ s->setAutoFillEnabled(flag);
+
+ if (flag) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(pFrame->page()->editorClient());
+ WebAutoFill* webAutoFill = editorC->getAutoFill();
+ // Set the active AutoFillProfile data.
+ jobject autoFillProfile = env->GetObjectField(obj, gFieldIds->mAutoFillProfile);
+ if (autoFillProfile)
+ syncAutoFillProfile(env, autoFillProfile, webAutoFill);
+ else {
+ // The autofill profile is null. We need to tell Chromium about this because
+ // this may be because the user just deleted their profile but left the
+ // autofill feature setting enabled.
+ webAutoFill->clearProfiles();
+ }
+ }
+#endif
+ }
+};
+
+
+//-------------------------------------------------------------
+// JNI registration
+//-------------------------------------------------------------
+
+static JNINativeMethod gWebSettingsMethods[] = {
+ { "nativeSync", "(I)V",
+ (void*) WebSettings::Sync }
+};
+
+int registerWebSettings(JNIEnv* env)
+{
+ 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/WebSettings",
+ gWebSettingsMethods, NELEM(gWebSettingsMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/WebStorage.cpp b/Source/WebKit/android/jni/WebStorage.cpp
new file mode 100644
index 0000000..9ce207d
--- /dev/null
+++ b/Source/WebKit/android/jni/WebStorage.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 "config.h"
+
+#if ENABLE(DATABASE)
+
+#include "ApplicationCacheStorage.h"
+#include "DatabaseTracker.h"
+#include "JNIUtility.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "PageGroup.h"
+#include "SecurityOrigin.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+
+namespace android {
+
+static jobject GetOrigins(JNIEnv* env, jobject obj)
+{
+ Vector<RefPtr<WebCore::SecurityOrigin> > coreOrigins;
+ WebCore::DatabaseTracker::tracker().origins(coreOrigins);
+ Vector<WebCore::KURL> manifestUrls;
+ if (WebCore::cacheStorage().manifestURLs(&manifestUrls)) {
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ coreOrigins.append(manifestOrigin);
+ }
+ }
+
+ jclass setClass = env->FindClass("java/util/HashSet");
+ jmethodID cid = env->GetMethodID(setClass, "<init>", "()V");
+ jmethodID mid = env->GetMethodID(setClass, "add", "(Ljava/lang/Object;)Z");
+ jobject set = env->NewObject(setClass, cid);
+ env->DeleteLocalRef(setClass);
+
+ for (unsigned i = 0; i < coreOrigins.size(); ++i) {
+ WebCore::SecurityOrigin* origin = coreOrigins[i].get();
+ WTF::String url = origin->toString();
+ jstring jUrl = wtfStringToJstring(env, url);
+ env->CallBooleanMethod(set, mid, jUrl);
+ env->DeleteLocalRef(jUrl);
+ }
+
+ return set;
+}
+
+static unsigned long long GetQuotaForOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ unsigned long long quota = WebCore::DatabaseTracker::tracker().quotaForOrigin(securityOrigin.get());
+ return quota;
+}
+
+static unsigned long long GetUsageForOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ unsigned long long usage = WebCore::DatabaseTracker::tracker().usageForOrigin(securityOrigin.get());
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return usage;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ if (manifestOrigin->isSameSchemeHostPort(securityOrigin.get())) {
+ int64_t cacheSize = 0;
+ WebCore::cacheStorage().cacheGroupSize(manifestUrls[i].string(), &cacheSize);
+ usage += cacheSize;
+ }
+ }
+ return usage;
+}
+
+static void SetQuotaForOrigin(JNIEnv* env, jobject obj, jstring origin, unsigned long long quota)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ WebCore::DatabaseTracker::tracker().setQuota(securityOrigin.get(), quota);
+}
+
+static void DeleteOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ WebCore::DatabaseTracker::tracker().deleteOrigin(securityOrigin.get());
+
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ if (manifestOrigin->isSameSchemeHostPort(securityOrigin.get()))
+ WebCore::cacheStorage().deleteCacheGroup(manifestUrls[i]);
+ }
+}
+
+static void DeleteAllData(JNIEnv* env, jobject obj)
+{
+ WebCore::DatabaseTracker::tracker().deleteAllDatabases();
+
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i)
+ WebCore::cacheStorage().deleteCacheGroup(manifestUrls[i]);
+
+ // FIXME: this is a workaround for eliminating any DOM Storage data (both
+ // session and local storage) as there is no functionality inside WebKit at the
+ // moment to do it. That functionality is a WIP in https://bugs.webkit.org/show_bug.cgi?id=51878
+ // and when that patch lands and we merge it, we should move towards that approach instead.
+ WebCore::PageGroup::clearDomStorage();
+}
+
+static void SetAppCacheMaximumSize(JNIEnv* env, jobject obj, unsigned long long size)
+{
+ WebCore::cacheStorage().setMaximumSize(size);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gWebStorageMethods[] = {
+ { "nativeGetOrigins", "()Ljava/util/Set;",
+ (void*) GetOrigins },
+ { "nativeGetUsageForOrigin", "(Ljava/lang/String;)J",
+ (void*) GetUsageForOrigin },
+ { "nativeGetQuotaForOrigin", "(Ljava/lang/String;)J",
+ (void*) GetQuotaForOrigin },
+ { "nativeSetQuotaForOrigin", "(Ljava/lang/String;J)V",
+ (void*) SetQuotaForOrigin },
+ { "nativeDeleteOrigin", "(Ljava/lang/String;)V",
+ (void*) DeleteOrigin },
+ { "nativeDeleteAllData", "()V",
+ (void*) DeleteAllData },
+ { "nativeSetAppCacheMaximumSize", "(J)V",
+ (void*) SetAppCacheMaximumSize }
+};
+
+int registerWebStorage(JNIEnv* env)
+{
+#ifndef NDEBUG
+ 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/WebStorage",
+ gWebStorageMethods, NELEM(gWebStorageMethods));
+}
+
+}
+
+#endif //ENABLE(DATABASE)
diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..f2680b5
--- /dev/null
+++ b/Source/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,4598 @@
+/*
+ * 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 "WebViewCore.h"
+
+#include "AccessibilityObject.h"
+#include "Attribute.h"
+#include "BaseLayerAndroid.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "ChromiumIncludes.h"
+#include "ClientRect.h"
+#include "ClientRectList.h"
+#include "Color.h"
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "DOMWindow.h"
+#include "DOMSelection.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
+#include "FocusController.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "Geolocation.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "HistoryItem.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "InlineTextBox.h"
+#include "MemoryUsage.h"
+#include "NamedNodeMap.h"
+#include "Navigator.h"
+#include "Node.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginWidgetAndroid.h"
+#include "PluginView.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "Range.h"
+#include "RenderBox.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderPart.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SchemeRegistry.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkANP.h"
+#include "SkTemplates.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "WindowsKeyboardCodes.h"
+#include "android_graphics.h"
+#include "autofill/WebAutoFill.h"
+#include "htmlediting.h"
+#include "markup.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <ui/KeycodeLabels.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/AtomicString.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
+
+#if ENABLE(TOUCH_EVENTS) // Android
+#include "PlatformTouchEvent.h"
+#endif
+
+#ifdef ANDROID_DOM_LOGGING
+#include "AndroidLog.h"
+#include "RenderTreeAsText.h"
+#include <wtf/text/CString.h>
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "GraphicsLayerAndroid.h"
+#include "RenderLayerCompositor.h"
+#endif
+
+/* 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 {
+
+static SkTDArray<WebViewCore*> gInstanceList;
+
+void WebViewCore::addInstance(WebViewCore* inst) {
+ *gInstanceList.append() = inst;
+}
+
+void WebViewCore::removeInstance(WebViewCore* inst) {
+ int index = gInstanceList.find(inst);
+ LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
+ if (index >= 0) {
+ gInstanceList.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isInstance(WebViewCore* inst) {
+ return gInstanceList.find(inst) >= 0;
+}
+
+jobject WebViewCore::getApplicationContext() {
+
+ // check to see if there is a valid webviewcore object
+ if (gInstanceList.isEmpty())
+ return 0;
+
+ // get the context from the webview
+ jobject context = gInstanceList[0]->getContext();
+
+ if (!context)
+ return 0;
+
+ // get the application context using JNI
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass contextClass = env->GetObjectClass(context);
+ jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
+ env->DeleteLocalRef(contextClass);
+ jobject result = env->CallObjectMethod(context, appContextMethod);
+ checkException(env);
+ return result;
+}
+
+
+struct WebViewCoreStaticMethods {
+ jmethodID m_isSupportedMediaMimeType;
+} gWebViewCoreStaticMethods;
+
+// Check whether a media mimeType is supported in Android media framework.
+bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMimeType = wtfStringToJstring(env, mimeType);
+ jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
+ bool val = env->CallStaticBooleanMethod(webViewCore,
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType);
+ checkException(env);
+ env->DeleteLocalRef(webViewCore);
+ env->DeleteLocalRef(jMimeType);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+
+#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
+
+// Field ids for WebViewCore
+struct WebViewCoreFields {
+ jfieldID m_nativeClass;
+ jfieldID m_viewportWidth;
+ jfieldID m_viewportHeight;
+ jfieldID m_viewportInitialScale;
+ jfieldID m_viewportMinimumScale;
+ jfieldID m_viewportMaximumScale;
+ jfieldID m_viewportUserScalable;
+ jfieldID m_viewportDensityDpi;
+ jfieldID m_webView;
+ jfieldID m_drawIsPaused;
+ jfieldID m_lowMemoryUsageMb;
+ jfieldID m_highMemoryUsageMb;
+ jfieldID m_highUsageDeltaMb;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ jweak m_obj;
+ jmethodID m_scrollTo;
+ jmethodID m_contentDraw;
+ jmethodID m_layersDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_openFileChooser;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_jsInterrupt;
+ jmethodID m_didFirstLayout;
+ jmethodID m_updateViewport;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_updateTextSelection;
+ jmethodID m_clearTextEntry;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ jmethodID m_requestKeyboard;
+ jmethodID m_requestKeyboardWithSelection;
+ jmethodID m_exceededDatabaseQuota;
+ jmethodID m_reachedMaxAppCacheSize;
+ jmethodID m_populateVisitedLinks;
+ jmethodID m_geolocationPermissionsShowPrompt;
+ jmethodID m_geolocationPermissionsHidePrompt;
+ jmethodID m_getDeviceMotionService;
+ jmethodID m_getDeviceOrientationService;
+ jmethodID m_addMessageToConsole;
+ jmethodID m_formDidBlur;
+ jmethodID m_getPluginClass;
+ jmethodID m_showFullScreenPlugin;
+ jmethodID m_hideFullScreenPlugin;
+ jmethodID m_createSurface;
+ jmethodID m_addSurface;
+ jmethodID m_updateSurface;
+ 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_setWebTextViewAutoFillable;
+ jmethodID m_selectAt;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+};
+
+/*
+ * WebViewCore Implementation
+ */
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ LOG_ASSERT(m, "Could not find method %s", name);
+ return m;
+}
+
+Mutex WebViewCore::gFrameCacheMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::gCursorBoundsMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+ , m_deviceMotionAndOrientationManager(this)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_moveGeneration = 0;
+ m_lastGeneration = 0;
+ m_touchGeneration = 0;
+ m_blockTextfieldUpdates = false;
+ // just initial values. These should be set by client
+ m_maxXScroll = 320/4;
+ m_maxYScroll = 240/4;
+ m_textGeneration = 0;
+ m_screenWidth = 320;
+ m_textWrapWidth = 320;
+ m_scale = 1;
+#if ENABLE(TOUCH_EVENTS)
+ m_forwardingTouchEvents = false;
+#endif
+ m_isPaused = false;
+ m_screenOnCounter = 0;
+ m_shouldPaintCaret = true;
+
+ LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
+
+ jclass clazz = env->GetObjectClass(javaWebViewCore);
+ m_javaGlue = new JavaGlue;
+ 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;");
+ 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_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", "(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");
+ m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
+ m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
+ 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_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;I)V");
+ m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
+ m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
+ 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");
+#endif
+ m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
+ m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V");
+ env->DeleteLocalRef(clazz);
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ PageGroup::setShouldTrackVisitedLinks(true);
+
+ reset(true);
+
+ MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb));
+ MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb));
+ MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb));
+
+ WebViewCore::addInstance(this);
+
+#if USE(CHROME_NETWORK_STACK)
+ AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
+#endif
+}
+
+WebViewCore::~WebViewCore()
+{
+ WebViewCore::removeInstance(this);
+
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
+ m_javaGlue->m_obj = 0;
+ }
+ delete m_javaGlue;
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
+{
+ return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
+{
+ if (!view)
+ return 0;
+
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ if (!webFrameView)
+ return 0;
+ return webFrameView->webViewCore();
+}
+
+void WebViewCore::reset(bool fromConstructor)
+{
+ DBG_SET_LOG("");
+ if (fromConstructor) {
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ } else {
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ gFrameCacheMutex.unlock();
+ }
+
+ m_lastFocused = 0;
+ m_blurringNodePointer = 0;
+ m_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ m_focusBoundsChanged = false;
+ m_lastFocusedSelStart = 0;
+ m_lastFocusedSelEnd = 0;
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+ m_domtree_version = 0;
+ m_check_domtree_version = true;
+ m_progressDone = false;
+ m_hasCursorBounds = false;
+
+ m_scrollOffsetX = 0;
+ m_scrollOffsetY = 0;
+ m_screenWidth = 0;
+ m_screenHeight = 0;
+ m_groupForVisitedLinks = 0;
+ m_currentNodeDomNavigationAxis = 0;
+}
+
+static bool layoutIfNeededRecursive(WebCore::Frame* f)
+{
+ if (!f)
+ return true;
+
+ WebCore::FrameView* v = f->view();
+ if (!v)
+ return true;
+
+ 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();
+}
+
+CacheBuilder& WebViewCore::cacheBuilder()
+{
+ return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
+}
+
+WebCore::Node* WebViewCore::currentFocus()
+{
+ return cacheBuilder().currentFocus();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!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__);
+
+ // Copy m_buttons so we can pass it to our graphics context.
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+}
+
+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 there is a pending style recalculation, just return.
+ if (m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("recordPictureSet: pending style recalc, ignoring.");
+ 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();
+ int height = view->contentsHeight();
+
+ // Use the contents width and height as a starting point.
+ SkIRect contentRect;
+ contentRect.set(0, 0, width, height);
+ SkIRect total(contentRect);
+
+ // Traverse all the frames and add their sizes if they are in the visible
+ // rectangle.
+ for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
+ frame = frame->tree()->traverseNext()) {
+ // If the frame doesn't have an owner then it is the top frame and the
+ // view size is the frame size.
+ WebCore::RenderPart* owner = frame->ownerRenderer();
+ if (owner && owner->style()->visibility() == VISIBLE) {
+ int x = owner->x();
+ int y = owner->y();
+
+ // Traverse the tree up to the parent to find the absolute position
+ // of this frame.
+ WebCore::Frame* parent = frame->tree()->parent();
+ while (parent) {
+ WebCore::RenderPart* parentOwner = parent->ownerRenderer();
+ if (parentOwner) {
+ x += parentOwner->x();
+ y += parentOwner->y();
+ }
+ parent = parent->tree()->parent();
+ }
+ // Use the owner dimensions so that padding and border are
+ // included.
+ int right = x + owner->width();
+ int bottom = y + owner->height();
+ SkIRect frameRect = {x, y, right, bottom};
+ // Ignore a width or height that is smaller than 1. Some iframes
+ // have small dimensions in order to be hidden. The iframe
+ // expansion code does not expand in that case so we should ignore
+ // them here.
+ if (frameRect.width() > 1 && frameRect.height() > 1
+ && SkIRect::Intersects(total, frameRect))
+ total.join(x, y, right, bottom);
+ }
+ }
+
+ // If the new total is larger than the content, resize the view to include
+ // all the content.
+ if (!contentRect.contains(total)) {
+ // Resize the view to change the overflow clip.
+ view->resize(total.fRight, total.fBottom);
+
+ // We have to force a layout in order for the clip to change.
+ m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
+ view->forceLayout();
+
+ // Relayout similar to above
+ 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();
+ }
+
+ if (cacheBuilder().pictureSetDisabled())
+ content->clear();
+
+ content->checkDimensions(width, height, &m_addInval);
+
+ // The inval region may replace existing pictures. The existing pictures
+ // may have already been split into pieces. If reuseSubdivided() returns
+ // true, the split pieces are the last entries in the picture already. They
+ // are marked as invalid, and are rebuilt by rebuildPictureSet().
+
+ // If the new region doesn't match a set of split pieces, add it to the end.
+ if (!content->reuseSubdivided(m_addInval)) {
+ const SkIRect& inval = m_addInval.getBounds();
+ SkPicture* picture = rebuildPicture(inval);
+ DBG_SET_LOGD("{%d,%d,w=%d,h=%d}", inval.fLeft,
+ inval.fTop, inval.width(), inval.height());
+ content->add(m_addInval, picture, 0, false);
+ SkSafeUnref(picture);
+ }
+ // Remove any pictures already in the set that are obscured by the new one,
+ // and check to see if any already split pieces need to be redrawn.
+ if (content->build())
+ 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendFindAgain);
+ checkException(env);
+ }
+}
+
+void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons)
+{
+ // All the entries in buttons are either updates of previous entries in
+ // m_buttons or they need to be added to it.
+ Container* end = buttons->end();
+ for (Container* updatedContainer = buttons->begin();
+ updatedContainer != end; updatedContainer++) {
+ bool updated = false;
+ // Search for a previous entry that references the same node as our new
+ // data
+ Container* lastPossibleMatch = m_buttons.end();
+ for (Container* possibleMatch = m_buttons.begin();
+ possibleMatch != lastPossibleMatch; possibleMatch++) {
+ if (updatedContainer->matches(possibleMatch->node())) {
+ // Update our record, and skip to the next one.
+ possibleMatch->setRect(updatedContainer->rect());
+ updated = true;
+ break;
+ }
+ }
+ if (!updated) {
+ // This is a brand new button, so append it to m_buttons
+ m_buttons.append(*updatedContainer);
+ }
+ }
+ size_t i = 0;
+ // count will decrease each time one is removed, so check count each time.
+ while (i < m_buttons.size()) {
+ if (m_buttons[i].canBeRemoved()) {
+ m_buttons[i] = m_buttons.last();
+ m_buttons.removeLast();
+ } else {
+ i++;
+ }
+ }
+}
+
+// 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()
+{
+ DBG_SET_LOG("");
+ m_content.clear();
+ m_addInval.setEmpty();
+ m_rebuildInval.setEmpty();
+}
+
+bool WebViewCore::focusBoundsChanged()
+{
+ bool result = m_focusBoundsChanged;
+ m_focusBoundsChanged = false;
+ return result;
+}
+
+SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ 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();
+
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ recordingCanvas->translate(-inval.fLeft, -inval.fTop);
+ recordingCanvas->save();
+ view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft,
+ inval.fTop, inval.width(), inval.height()));
+ 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);
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+
+ return picture;
+}
+
+void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ 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__);
+}
+
+BaseLayerAndroid* WebViewCore::createBaseLayer()
+{
+ BaseLayerAndroid* base = new BaseLayerAndroid();
+ base->setContent(m_content);
+
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ // 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()) {
+ Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (color.isValid() && color.alpha() > 0)
+ base->setBackgroundColor(color);
+ }
+ }
+
+ // We update the layers
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
+ if (root) {
+ root->notifyClientAnimationStarted();
+ LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
+ base->addChild(copyLayer);
+ copyLayer->unref();
+ }
+#endif
+
+ return base;
+}
+
+BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+ DBG_SET_LOG("start");
+ 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();
+ region->op(m_rebuildInval, SkRegion::kUnion_Op);
+ 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 createBaseLayer();
+}
+
+void WebViewCore::splitContent(PictureSet* content)
+{
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+ content->split(&m_content);
+ rebuildPictureSet(&m_content);
+ content->set(m_content);
+}
+
+void WebViewCore::scrollTo(int x, int y, bool animate)
+{
+ 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollTo,
+ x, y, animate, false);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyProgressFinished()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
+ checkException(env);
+}
+
+void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendViewInvalidate,
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ checkException(env);
+}
+
+void WebViewCore::contentDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
+ checkException(env);
+}
+
+void WebViewCore::layersDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_layersDraw);
+ checkException(env);
+}
+
+void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
+{
+ 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();
+}
+
+void WebViewCore::contentInvalidateAll()
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ contentInvalidate(WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+}
+
+void WebViewCore::offInvalidate(const WebCore::IntRect &r)
+{
+ // FIXME: these invalidates are offscreen, and can be throttled or
+ // deferred until the area is visible. For now, treat them as
+ // regular invals so that drawing happens (inefficiently) for now.
+ 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()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ WebCore::FrameLoader* loader = m_mainFrame->loader();
+ const WebCore::KURL& url = loader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ WebCore::FrameLoadType loadType = loader->loadType();
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout,
+ loadType == WebCore::FrameLoadTypeStandard
+ // When redirect with locked history, we would like to reset the
+ // scale factor. This is important for www.yahoo.com as it is
+ // redirected to www.yahoo.com/?rs=1 on load.
+ || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ m_check_domtree_version = false;
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::updateViewport()
+{
+ 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateViewport);
+ checkException(env);
+}
+
+void WebViewCore::restoreScale(float scale, float textWrapScale)
+{
+ 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale);
+ checkException(env);
+}
+
+void WebViewCore::needTouchEvents(bool need)
+{
+ 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)
+ if (m_forwardingTouchEvents == need)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+
+ m_forwardingTouchEvents = 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboardWithSelection,
+ reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::requestKeyboard(bool showKeyboard)
+{
+ 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();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboard, showKeyboard);
+ checkException(env);
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ m_check_domtree_version = true;
+ sendNotifyProgressFinished();
+}
+
+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;
+ // The visible rect is located within our coordinate space so it
+ // contains the actual scroll position. Setting the location makes hit
+ // testing work correctly.
+ m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
+ m_scrollOffsetY);
+ if (sendScrollEvent) {
+ m_mainFrame->eventHandler()->sendScrollEvent();
+
+ // Only update history position if it's user scrolled.
+ // Update history item to reflect the new scroll position.
+ // This also helps save the history information when the browser goes to
+ // background, so scroll position will be restored if browser gets
+ // killed while in background.
+ WebCore::HistoryController* history = m_mainFrame->loader()->history();
+ // Because the history item saving could be heavy for large sites and
+ // scrolling can generate lots of small scroll offset, the following code
+ // reduces the saving frequency.
+ static const int MIN_SCROLL_DIFF = 32;
+ if (history->currentItem()) {
+ WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint();
+ if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF ||
+ std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) {
+ history->saveScrollPositionAndViewStateToItem(history->currentItem());
+ }
+ }
+ }
+
+ // 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);
+}
+
+void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
+ int textWrapWidth, float scale, int screenWidth, int screenHeight,
+ int anchorX, int anchorY, bool ignoreHeight)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->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;
+ if (scale >= 0) // negative means keep the current scale
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = m_maxXScroll * height / width;
+ // Don't reflow if the diff is small.
+ const bool reflow = otw && textWrapWidth &&
+ ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f;
+
+ // When the screen size change, fixed positioned element should be updated.
+ // This is supposed to be light weighted operation without a full layout.
+ if (osh != screenHeight || osw != screenWidth)
+ m_mainFrame->view()->updatePositionedObjects();
+
+ 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;
+ // If the text wrap changed, it is probably zoom change or
+ // orientation change. Try to keep the anchor at the same place.
+ if (otw && textWrapWidth && otw != textWrapWidth &&
+ (anchorX != 0 || anchorY != 0)) {
+ WebCore::HitTestResult 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.
+ if (bounds.width() == 0) {
+ 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());
+ }
+ }
+ }
+
+ // Set the size after finding the old anchor point as
+ // hitTestResultAtPoint causes a layout.
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->view()->forceLayout();
+
+ // scroll to restore current screen center
+ if (node) {
+ 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();
+ // force left align if width is not changed while height changed.
+ // the anchorPoint is probably at some white space in the node
+ // which is affected by text wrap around the screen width.
+ const bool leftAlign = (otw != textWrapWidth)
+ && (bounds.width() == newBounds.width())
+ && (bounds.height() != newBounds.height());
+ const float xPercentInDoc =
+ leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
+ const float xPercentInView =
+ leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw;
+ const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
+ const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
+ showRect(newBounds.x(), newBounds.y(), newBounds.width(),
+ newBounds.height(), view->contentsWidth(),
+ view->contentsHeight(),
+ xPercentInDoc, xPercentInView,
+ yPercentInDoc, yPercentInView);
+ }
+ }
+ }
+ } else {
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ m_mainFrame->view()->resize(width, height);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ }
+
+ // update the currently visible screen as perceived by the plugin
+ sendPluginVisibleScreen();
+}
+
+void WebViewCore::dumpDomTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ if (useFile)
+ gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
+ m_mainFrame->document()->showTreeForThis();
+ if (gDomTreeFile) {
+ fclose(gDomTreeFile);
+ gDomTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpRenderTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
+ const char* data = renderDump.data();
+ if (useFile) {
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ DUMP_RENDER_LOGD("%s", data);
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ } else {
+ // adb log can only output 1024 characters, so write out line by line.
+ // exclude '\n' as adb log adds it for each output.
+ int length = renderDump.length();
+ for (int i = 0, last = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last)
+ DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
+ last = i + 1;
+ }
+ }
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ cacheBuilder().mDebug.print();
+#endif
+}
+
+HTMLElement* WebViewCore::retrieveElement(int x, int y,
+ const QualifiedName& tagName)
+{
+ HitTestResult hitTestResult = m_mainFrame->eventHandler()
+ ->hitTestResultAtPoint(IntPoint(x, y), false, false,
+ DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
+ IntSize(1, 1));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return 0;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return 0;
+ }
+ Node* node = hitTestResult.innerNode();
+ Node* element = node;
+ while (element && (!element->isElementNode()
+ || !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);
+}
+
+HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y)
+{
+ return static_cast<HTMLAnchorElement*>
+ (retrieveElement(x, y, HTMLNames::aTag));
+}
+
+HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y)
+{
+ return static_cast<HTMLImageElement*>
+ (retrieveElement(x, y, HTMLNames::imgTag));
+}
+
+WTF::String WebViewCore::retrieveHref(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->href() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveAnchorText(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->text() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveImageSource(int x, int y)
+{
+ HTMLImageElement* image = retrieveImageElement(x, y);
+ return image ? image->src().string() : WTF::String();
+}
+
+WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
+ WebCore::Node* 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++) {
+ WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
+ list->item(i));
+ if (label->control() == node) {
+ Node* node = label;
+ String result;
+ while ((node = node->traverseNextNode(label))) {
+ if (node->isTextNode()) {
+ Text* textNode = static_cast<Text*>(node);
+ result += textNode->dataImpl();
+ }
+ }
+ return result;
+ }
+ }
+ }
+ return WTF::String();
+}
+
+static bool isContentEditable(const WebCore::Node* node)
+{
+ 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 (isContentEditable(node))
+ return true;
+ if (!node)
+ return false;
+ WebCore::RenderObject* renderer = node->renderer();
+ return renderer && (renderer->isTextField() || renderer->isTextArea());
+}
+
+void WebViewCore::revealSelection()
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ if (!isTextInput(focus))
+ return;
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ if (!focusedFrame->page()->focusController()->isActive())
+ return;
+ 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;
+#if DEBUG_NAV_UI
+ m_now = SkTime::GetMSecs();
+#endif
+ 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* mNode;
+ IntRect mBounds;
+};
+
+// get the bounding box of the Node
+static IntRect getAbsoluteBoundingBox(Node* node) {
+ IntRect rect;
+ RenderObject* render = node->renderer();
+ if (render->isRenderInline())
+ rect = toRenderInline(render)->linesVisualOverflowBoundingBox();
+ else if (render->isBox())
+ rect = toRenderBox(render)->visualOverflowRect();
+ else if (render->isText())
+ rect = toRenderText(render)->linesBoundingBox();
+ else
+ LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
+ FloatPoint absPos = render->localToAbsolute();
+ rect.move(absPos.x(), absPos.y());
+ return rect;
+}
+
+// get the highlight rectangles for the touch point (x, y) with the slop
+Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop)
+{
+ 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));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return rects;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return rects;
+ }
+ Frame* frame = hitTestResult.innerNode()->document()->frame();
+ Vector<TouchNodeData> nodeDataList;
+ 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?
+ // if the node is not in the same frame as the innerNode, skip it
+ if (it->get()->document()->frame() != frame)
+ continue;
+ // traverse up the tree to find the first node that needs highlight
+ bool found = false;
+ Node* eventNode = it->get();
+ while (eventNode) {
+ RenderObject* render = eventNode->renderer();
+ if (render->isBody() || render->isRenderView())
+ break;
+ if (eventNode->supportsFocus()
+ || eventNode->hasEventListeners(eventNames().clickEvent)
+ || eventNode->hasEventListeners(eventNames().mousedownEvent)
+ || eventNode->hasEventListeners(eventNames().mouseupEvent)) {
+ found = true;
+ break;
+ }
+ // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing.
+ // so do not search for the eventNode across explicit z-index border.
+ // TODO: this is a hard one to call. z-index is quite complicated as its value only
+ // matters when you compare two RenderLayer in the same hierarchy level. e.g. in
+ // the following example, "b" is on the top as its z level is the highest. even "c"
+ // has 100 as z-index, it is still below "d" as its parent has the same z-index as
+ // "d" and logically before "d". Of course "a" is the lowest in the z level.
+ //
+ // z-index:auto "a"
+ // z-index:2 "b"
+ // z-index:1
+ // z-index:100 "c"
+ // z-index:1 "d"
+ //
+ // If the fat point touches everyone, the order in the list should be "b", "d", "c"
+ // and "a". When we search for the event node for "b", we really don't want "a" as
+ // in the z-order it is behind everything else.
+ if (!render->style()->hasAutoZIndex())
+ break;
+ eventNode = eventNode->parentNode();
+ }
+ // didn't find any eventNode, skip it
+ if (!found)
+ continue;
+ // first quick check whether it is a duplicated node before computing bounding box
+ 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->mNode) {
+ found = false;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ // next check whether the node is fully covered by or fully covering another node.
+ found = false;
+ IntRect rect = getAbsoluteBoundingBox(eventNode);
+ if (rect.isEmpty()) {
+ // if the node's bounds is empty and it is not a ContainerNode, skip it.
+ if (!eventNode->isContainerNode())
+ continue;
+ // if the node's children are all positioned objects, its bounds can be empty.
+ // Walk through the children to find the bounding box.
+ Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild();
+ while (child) {
+ IntRect childrect;
+ if (child->renderer())
+ childrect = getAbsoluteBoundingBox(child);
+ if (!childrect.isEmpty()) {
+ rect.unite(childrect);
+ child = child->traverseNextSibling(eventNode);
+ } else
+ child = child->traverseNextNode(eventNode);
+ }
+ }
+ for (int i = nodeDataList.size() - 1; i >= 0; i--) {
+ TouchNodeData n = nodeDataList.at(i);
+ // the new node is enclosing an existing node, skip it
+ if (rect.contains(n.mBounds)) {
+ found = true;
+ break;
+ }
+ // the new node is fully inside an existing node, remove the existing node
+ if (n.mBounds.contains(rect))
+ nodeDataList.remove(i);
+ }
+ if (!found) {
+ TouchNodeData newNode;
+ newNode.mNode = eventNode;
+ newNode.mBounds = rect;
+ nodeDataList.append(newNode);
+ }
+ }
+ if (!nodeDataList.size())
+ return rects;
+ // finally select the node with the largest overlap with the fat point
+ TouchNodeData final;
+ 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;
+ Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
+ for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
+ IntRect rect = n->mBounds;
+ rect.intersect(testRect);
+ int a = rect.width() * rect.height();
+ if (a > area) {
+ final = *n;
+ area = a;
+ }
+ }
+ // now get the node's highlight rectangles in the page coordinate system
+ if (final.mNode) {
+ IntPoint frameAdjust;
+ if (frame != m_mainFrame) {
+ frameAdjust = frame->view()->contentsToWindow(IntPoint());
+ frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
+ }
+ 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].right()) {
+ 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].bottom()) {
+ if (y - rects[i].bottom() + 1 < distance) {
+ newx = x;
+ newy = rects[i].bottom() - 1;
+ distance = y - rects[i].bottom() + 1;
+ }
+ }
+ } else if (y >= rects[i].y() && y < rects[i].bottom()) {
+ 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].right()) {
+ if (x - rects[i].right() + 1 < distance) {
+ newx = rects[i].right() - 1;
+ newy = y;
+ distance = x - rects[i].right() + 1;
+ }
+ }
+ }
+ }
+ 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;
+ }
+ }
+ 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 rects;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- addPlugin %p", w);
+ /* The plugin must be appended to the end of the array. This ensures that if
+ the plugin is added while iterating through the array (e.g. sendEvent(...))
+ that the iteration process is not corrupted.
+ */
+ *m_plugins.append() = w;
+}
+
+void WebViewCore::removePlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- removePlugin %p", w);
+ int index = m_plugins.find(w);
+ if (index < 0) {
+ SkDebugf("--------------- pluginwindow not found! %p\n", w);
+ } else {
+ m_plugins.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
+{
+ return m_plugins.find(w) >= 0;
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 1.0 / 60;
+
+ if (!m_pluginInvalTimer.isActive()) {
+ m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
+ }
+}
+
+void WebViewCore::drawPlugins()
+{
+ SkRegion inval; // accumualte what needs to be redrawn
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+
+ for (; iter < stop; ++iter) {
+ PluginWidgetAndroid* w = *iter;
+ SkIRect dirty;
+ if (w->isDirty(&dirty)) {
+ w->draw();
+ inval.op(dirty, SkRegion::kUnion_Op);
+ }
+ }
+
+ if (!inval.isEmpty()) {
+ // inval.getBounds() is our rectangle
+ const SkIRect& bounds = inval.getBounds();
+ WebCore::IntRect r(bounds.fLeft, bounds.fTop,
+ bounds.width(), bounds.height());
+ this->viewInvalidate(r);
+ }
+}
+
+void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
+ // if frame is the parent then notify all plugins
+ if (!frame->tree()->parent()) {
+ // trigger an event notifying the plugins that the page has loaded
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ sendPluginEvent(event);
+ // trigger the on/off screen notification if the page was reloaded
+ sendPluginVisibleScreen();
+ }
+ // else if frame's parent has completed
+ else if (!frame->tree()->parent()->loader()->isLoading()) {
+ // send to all plugins who have this frame in their heirarchy
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ Frame* currentFrame = (*iter)->pluginView()->parentFrame();
+ while (currentFrame) {
+ if (frame == currentFrame) {
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ (*iter)->sendEvent(event);
+
+ // trigger the on/off screen notification if the page was reloaded
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+
+ break;
+ }
+ currentFrame = currentFrame->tree()->parent();
+ }
+ }
+ }
+}
+
+void WebViewCore::getVisibleScreen(ANPRectI& visibleRect)
+{
+ visibleRect.left = m_scrollOffsetX;
+ visibleRect.top = m_scrollOffsetY;
+ visibleRect.right = m_scrollOffsetX + m_screenWidth;
+ visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
+}
+
+void WebViewCore::sendPluginVisibleScreen()
+{
+ /* We may want to cache the previous values and only send the notification
+ to the plugin in the event that one of the values has changed.
+ */
+
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+ }
+}
+
+void WebViewCore::sendPluginEvent(const ANPEvent& evt)
+{
+ /* The list of plugins may be manipulated as we iterate through the list.
+ This implementation allows for the addition of new plugins during an
+ iteration, but may fail if a plugin is removed. Currently, there are not
+ any use cases where a plugin is deleted while processing this loop, but
+ if it does occur we will have to use an alternate data structure and/or
+ iteration mechanism.
+ */
+ for (int x = 0; x < m_plugins.count(); x++) {
+ m_plugins[x]->sendEvent(evt);
+ }
+}
+
+PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
+{
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ if ((*iter)->pluginView()->instance() == npp) {
+ return (*iter);
+ }
+ }
+ return 0;
+}
+
+static PluginView* nodeIsPlugin(Node* node) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->isWidget()) {
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (widget && widget->isPluginView())
+ return static_cast<PluginView*>(widget);
+ }
+ 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(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);
+ // 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());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ updateCacheOnNodeChange();
+}
+
+void WebViewCore::setSelection(int start, int end)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ 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);
+ setSelectionRange(focus, start, end);
+ client->setUiGeneratedSelectionChange(false);
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ bool isPasswordField = false;
+ if (focus->isElementNode()) {
+ WebCore::Element* element = static_cast<WebCore::Element*>(focus);
+ if (WebCore::InputElement* inputElement = WebCore::toInputElement(element))
+ isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField();
+ }
+ // For password fields, this is done in the UI side via
+ // bringPointIntoView, since the UI does the drawing.
+ if (renderer->isTextArea() || !isPasswordField)
+ revealSelection();
+}
+
+String WebViewCore::modifySelection(const int direction, const int axis)
+{
+ DOMSelection* selection = m_mainFrame->domWindow()->getSelection();
+ if (selection->rangeCount() > 1)
+ selection->removeAllRanges();
+ switch (axis) {
+ case AXIS_CHARACTER:
+ case AXIS_WORD:
+ case AXIS_SENTENCE:
+ return modifySelectionTextNavigationAxis(selection, direction, axis);
+ case AXIS_HEADING:
+ case AXIS_SIBLING:
+ case AXIS_PARENT_FIRST_CHILD:
+ case AXIS_DOCUMENT:
+ return modifySelectionDomNavigationAxis(selection, direction, axis);
+ default:
+ LOGE("Invalid navigation axis: %d", axis);
+ return String();
+ }
+}
+
+void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node)
+{
+ if (!frame || !node)
+ return;
+
+ Element* elementNode = 0;
+
+ // If not an Element, find a visible predecessor
+ // Element to scroll into view.
+ if (!node->isElementNode()) {
+ HTMLElement* body = frame->document()->body();
+ do {
+ if (!node || node == body)
+ return;
+ node = node->parentNode();
+ } while (!node->isElementNode() && !isVisible(node));
+ }
+
+ elementNode = static_cast<Element*>(node);
+ elementNode->scrollIntoViewIfNeeded(true);
+}
+
+String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ Node* body = m_mainFrame->document()->body();
+
+ ExceptionCode ec = 0;
+ String markup;
+
+ // initialize the selection if necessary
+ if (selection->rangeCount() == 0) {
+ if (m_currentNodeDomNavigationAxis
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_currentNodeDomNavigationAxis)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec);
+ m_currentNodeDomNavigationAxis = 0;
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else if (currentFocus()) {
+ selection->setPosition(currentFocus(), 0, ec);
+ } else if (m_cursorNode
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_cursorNode)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec);
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else {
+ selection->setPosition(body, 0, ec);
+ }
+ if (ec)
+ return String();
+ }
+
+ // collapse the selection
+ if (direction == DIRECTION_FORWARD)
+ selection->collapseToEnd(ec);
+ else
+ selection->collapseToStart(ec);
+ if (ec)
+ return String();
+
+ // Make sure the anchor node is a text node since we are generating
+ // the markup of the selection which includes the anchor, the focus,
+ // and any crossed nodes. Forcing the condition that the selection
+ // starts and ends on text nodes guarantees symmetric selection markup.
+ // Also this way the text content, rather its container, is highlighted.
+ Node* anchorNode = selection->anchorNode();
+ if (anchorNode->isElementNode()) {
+ // Collapsed selection while moving forward points to the
+ // next unvisited node and while moving backward to the
+ // last visited node.
+ if (direction == DIRECTION_FORWARD)
+ advanceAnchorNode(selection, direction, markup, false, ec);
+ else
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // If the selection is at the end of a non white space text move
+ // it to the next visible text node with non white space content.
+ // This is a workaround for the selection getting stuck.
+ anchorNode = selection->anchorNode();
+ if (anchorNode->isTextNode()) {
+ if (direction == DIRECTION_FORWARD) {
+ String suffix = anchorNode->textContent().substring(
+ selection->anchorOffset(), caretMaxOffset(anchorNode));
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (suffix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ } else {
+ String prefix = anchorNode->textContent().substring(0,
+ selection->anchorOffset());
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (prefix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ }
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // extend the selection
+ String directionStr;
+ if (direction == DIRECTION_FORWARD)
+ directionStr = "forward";
+ else
+ directionStr = "backward";
+
+ String axisStr;
+ if (axis == AXIS_CHARACTER)
+ axisStr = "character";
+ else if (axis == AXIS_WORD)
+ axisStr = "word";
+ else
+ axisStr = "sentence";
+
+ selection->modify("extend", directionStr, axisStr);
+
+ // Make sure the focus node is a text node in order to have the
+ // selection generate symmetric markup because the latter
+ // includes all nodes crossed by the selection. Also this way
+ // the text content, rather its container, is highlighted.
+ Node* focusNode = selection->focusNode();
+ if (focusNode->isElementNode()) {
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (!focusNode)
+ return String();
+ if (direction == DIRECTION_FORWARD) {
+ focusNode = focusNode->traversePreviousSiblingPostOrder(body);
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (focusNode && isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ if (ec)
+ return String();
+ }
+ } else {
+ focusNode = focusNode->traverseNextSibling();
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (anchorNode && isContentTextNode(anchorNode)) {
+ selection->extend(focusNode, 0, ec);
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // Enforce that the selection does not cross anchor boundaries. This is
+ // a workaround for the asymmetric behavior of WebKit while crossing
+ // anchors.
+ anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (anchorNode && focusNode && anchorNode != focusNode) {
+ Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode,
+ direction);
+ if (inputControl) {
+ if (direction == DIRECTION_FORWARD) {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traversePreviousSiblingPostOrder(
+ body);
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ } else {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traverseNextSibling();
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMinOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // make sure the selection is visible
+ if (direction == DIRECTION_FORWARD)
+ scrollNodeIntoView(m_mainFrame, selection->focusNode());
+ else
+ scrollNodeIntoView(m_mainFrame, selection->anchorNode());
+
+ // format markup for the visible content
+ PassRefPtr<Range> range = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ IntRect bounds = range->boundingBox();
+ selectAt(bounds.center().x(), bounds.center().y());
+ markup = formatMarkup(selection);
+ LOGV("Selection markup: %s", markup.utf8().data());
+
+ return markup;
+}
+
+Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction)
+{
+ if (node->offsetInCharacters())
+ return node;
+ if (!node->hasChildNodes())
+ return node;
+ if (offset < node->childNodeCount())
+ return node->childNode(offset);
+ else
+ if (direction == DIRECTION_FORWARD)
+ return node->traverseNextSibling();
+ else
+ return node->traversePreviousNodePostOrder(
+ node->document()->body());
+}
+
+Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction)
+{
+ Node* body = 0;
+ Node* currentNode = 0;
+ if (direction == DIRECTION_FORWARD) {
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traverseNextNode(body);
+ else
+ currentNode = anchorNode;
+ } else {
+ body = anchorNode->document()->body();
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traversePreviousSiblingPostOrder(body);
+ else
+ currentNode = anchorNode;
+ }
+ while (currentNode) {
+ if (isContentTextNode(currentNode)
+ || isContentInputElement(currentNode))
+ return currentNode;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode();
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(body);
+ }
+ return 0;
+}
+
+void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction,
+ String& markup, bool ignoreFirstNode, ExceptionCode& ec)
+{
+ Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ if (!anchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ // If the anchor offset is invalid i.e. the anchor node has no
+ // child with that index getImplicitAnchorNode returns the next
+ // logical node in the current direction. In such a case our
+ // position in the DOM tree was has already been advanced,
+ // therefore we there is no need to do that again.
+ if (selection->anchorNode()->isElementNode()) {
+ unsigned anchorOffset = selection->anchorOffset();
+ unsigned childNodeCount = selection->anchorNode()->childNodeCount();
+ if (anchorOffset >= childNodeCount)
+ ignoreFirstNode = false;
+ }
+ // Find the next anchor node given our position in the DOM and
+ // whether we want the current node to be considered as well.
+ Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode,
+ direction);
+ if (!nextAnchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ if (nextAnchorNode->isElementNode()) {
+ // If this is an input element tell the WebView thread
+ // to set the cursor to that control.
+ if (isContentInputElement(nextAnchorNode)) {
+ IntRect bounds = nextAnchorNode->getRect();
+ selectAt(bounds.center().x(), bounds.center().y());
+ }
+ Node* textNode = 0;
+ // Treat the text content of links as any other text but
+ // for the rest input elements select the control itself.
+ if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag))
+ textNode = traverseNextContentTextNode(nextAnchorNode,
+ nextAnchorNode, direction);
+ // We prefer to select the text content of the link if such,
+ // otherwise just select the element itself.
+ if (textNode) {
+ nextAnchorNode = textNode;
+ } else {
+ if (direction == DIRECTION_FORWARD) {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+ } else {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ }
+ if (!ec)
+ markup = formatMarkup(selection);
+ // make sure the selection is visible
+ scrollNodeIntoView(selection->frame(), nextAnchorNode);
+ return;
+ }
+ }
+ if (direction == DIRECTION_FORWARD)
+ selection->setPosition(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ else
+ selection->setPosition(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+}
+
+bool WebViewCore::isContentInputElement(Node* node)
+{
+ return (isVisible(node)
+ && (node->hasTagName(WebCore::HTMLNames::selectTag)
+ || node->hasTagName(WebCore::HTMLNames::aTag)
+ || node->hasTagName(WebCore::HTMLNames::inputTag)
+ || node->hasTagName(WebCore::HTMLNames::buttonTag)));
+}
+
+bool WebViewCore::isContentTextNode(Node* node)
+{
+ if (!node || !node->isTextNode())
+ return false;
+ Text* textNode = static_cast<Text*>(node);
+ return (isVisible(textNode) && textNode->length() > 0
+ && !textNode->containsOnlyWhitespace());
+}
+
+Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction)
+{
+ Node* currentNode = fromNode;
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(toNode);
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(toNode);
+ } while (currentNode && !isContentTextNode(currentNode));
+ return static_cast<Text*>(currentNode);
+}
+
+Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction)
+{
+ if (fromNode == toNode)
+ return 0;
+ if (direction == DIRECTION_FORWARD) {
+ Node* currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNodePostOrder();
+ }
+ currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNode();
+ }
+ } else {
+ Node* currentNode = fromNode->traversePreviousNode();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNode();
+ }
+ currentNode = fromNode->traversePreviousNodePostOrder();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNodePostOrder();
+ }
+ }
+ return 0;
+}
+
+bool WebViewCore::isDescendantOf(Node* parent, Node* node)
+{
+ Node* currentNode = node;
+ while (currentNode) {
+ if (currentNode == parent) {
+ return true;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return false;
+}
+
+String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ HTMLElement* body = m_mainFrame->document()->body();
+ if (!m_currentNodeDomNavigationAxis && selection->focusNode()) {
+ m_currentNodeDomNavigationAxis = selection->focusNode();
+ selection->empty();
+ if (m_currentNodeDomNavigationAxis->isTextNode())
+ m_currentNodeDomNavigationAxis =
+ m_currentNodeDomNavigationAxis->parentNode();
+ }
+ if (!m_currentNodeDomNavigationAxis)
+ m_currentNodeDomNavigationAxis = currentFocus();
+ if (!m_currentNodeDomNavigationAxis
+ || !CacheBuilder::validNode(m_mainFrame, m_mainFrame,
+ m_currentNodeDomNavigationAxis))
+ m_currentNodeDomNavigationAxis = body;
+ Node* currentNode = m_currentNodeDomNavigationAxis;
+ if (axis == AXIS_HEADING) {
+ if (currentNode == body && direction == DIRECTION_BACKWARD)
+ currentNode = currentNode->lastDescendant();
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(body);
+ else
+ currentNode = currentNode->traversePreviousNode(body);
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode) || !isHeading(currentNode)));
+ } else if (axis == AXIS_PARENT_FIRST_CHILD) {
+ if (direction == DIRECTION_FORWARD) {
+ currentNode = currentNode->firstChild();
+ while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)))
+ currentNode = currentNode->nextSibling();
+ } else {
+ do {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->parentNode();
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ }
+ } else if (axis == AXIS_SIBLING) {
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->nextSibling();
+ else {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->previousSibling();
+ }
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ } else if (axis == AXIS_DOCUMENT) {
+ currentNode = body;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->lastDescendant();
+ } else {
+ LOGE("Invalid axis: %d", axis);
+ return String();
+ }
+ if (currentNode) {
+ m_currentNodeDomNavigationAxis = currentNode;
+ scrollNodeIntoView(m_mainFrame, currentNode);
+ String selectionString = createMarkup(currentNode);
+ LOGV("Selection markup: %s", selectionString.utf8().data());
+ return selectionString;
+ }
+ return String();
+}
+
+bool WebViewCore::isHeading(Node* node)
+{
+ if (node->hasTagName(WebCore::HTMLNames::h1Tag)
+ || node->hasTagName(WebCore::HTMLNames::h2Tag)
+ || node->hasTagName(WebCore::HTMLNames::h3Tag)
+ || node->hasTagName(WebCore::HTMLNames::h4Tag)
+ || node->hasTagName(WebCore::HTMLNames::h5Tag)
+ || node->hasTagName(WebCore::HTMLNames::h6Tag)) {
+ return true;
+ }
+
+ if (node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ String roleAttribute =
+ element->getAttribute(WebCore::HTMLNames::roleAttr).string();
+ if (equalIgnoringCase(roleAttribute, "heading"))
+ return true;
+ }
+
+ return false;
+}
+
+bool WebViewCore::isVisible(Node* node)
+{
+ // start off an element
+ Element* element = 0;
+ if (node->isElementNode())
+ element = static_cast<Element*>(node);
+ else
+ element = node->parentElement();
+ // check renderer
+ if (!element->renderer()) {
+ return false;
+ }
+ // check size
+ if (element->offsetHeight() == 0 || element->offsetWidth() == 0) {
+ return false;
+ }
+ // check style
+ Node* body = m_mainFrame->document()->body();
+ Node* currentNode = element;
+ while (currentNode && currentNode != body) {
+ RenderStyle* style = currentNode->computedStyle();
+ if (style &&
+ (style->display() == NONE || style->visibility() == HIDDEN)) {
+ return false;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return true;
+}
+
+String WebViewCore::formatMarkup(DOMSelection* selection)
+{
+ ExceptionCode ec = 0;
+ String markup = String();
+ PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ if (!wholeRange->startContainer() || !wholeRange->startContainer())
+ return String();
+ // Since formatted markup contains invisible nodes it
+ // is created from the concatenation of the visible fragments.
+ Node* firstNode = wholeRange->firstNode();
+ Node* pastLastNode = wholeRange->pastLastNode();
+ Node* currentNode = firstNode;
+ PassRefPtr<Range> currentRange;
+
+ while (currentNode != pastLastNode) {
+ Node* nextNode = currentNode->traverseNextNode();
+ if (!isVisible(currentNode)) {
+ if (currentRange) {
+ markup = markup + currentRange->toHTML().utf8().data();
+ currentRange = 0;
+ }
+ } else {
+ if (!currentRange) {
+ currentRange = selection->frame()->document()->createRange();
+ if (ec)
+ break;
+ if (currentNode == firstNode) {
+ currentRange->setStart(wholeRange->startContainer(),
+ wholeRange->startOffset(), ec);
+ if (ec)
+ break;
+ } else {
+ currentRange->setStart(currentNode->parentNode(),
+ currentNode->nodeIndex(), ec);
+ if (ec)
+ break;
+ }
+ }
+ if (nextNode == pastLastNode) {
+ currentRange->setEnd(wholeRange->endContainer(),
+ wholeRange->endOffset(), ec);
+ if (ec)
+ break;
+ markup = markup + currentRange->toHTML().utf8().data();
+ } else {
+ if (currentNode->offsetInCharacters())
+ currentRange->setEnd(currentNode,
+ currentNode->maxCharacterOffset(), ec);
+ else
+ currentRange->setEnd(currentNode->parentNode(),
+ currentNode->nodeIndex() + 1, ec);
+ if (ec)
+ break;
+ }
+ }
+ currentNode = nextNode;
+ }
+ return markup.stripWhiteSpace();
+}
+
+void WebViewCore::selectAt(int x, int y)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_selectAt,
+ x, y);
+ checkException(env);
+}
+
+void WebViewCore::deleteSelection(int start, int end, int textGeneration)
+{
+ setSelection(start, end);
+ if (start == end)
+ return;
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
+ PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
+ key(down);
+ key(up);
+ client->setUiGeneratedSelectionChange(false);
+ m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::replaceTextfieldText(int oldStart,
+ int oldEnd, const WTF::String& replace, int start, int end,
+ int textGeneration)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ setSelection(oldStart, oldEnd);
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ 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,
+ const PlatformKeyboardEvent& event)
+{
+ 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;
+ }
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ // Also prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ key(event);
+ client->setUiGeneratedSelectionChange(false);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+ 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;
+}
+
+void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
+{
+ 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;
+ }
+ 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);
+}
+
+void WebViewCore::setFocusControllerActive(bool active)
+{
+ m_mainFrame->page()->focusController()->setActive(active);
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame)
+{
+ if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
+ WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
+
+ // item can be null when there is no offical URL for the current page. This happens
+ // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
+ // is no failing URL (common case is when content is loaded using data: scheme)
+ if (item) {
+ item->setDocumentState(frame->document()->formElementsState());
+ }
+}
+
+// Create an array of java Strings.
+static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
+{
+ jclass stringClass = env->FindClass("java/lang/String");
+ LOG_ASSERT(stringClass, "Could not find java/lang/String");
+ jobjectArray array = env->NewObjectArray(count, stringClass, 0);
+ 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]);
+ env->SetObjectArrayElement(array, i, newString);
+ env->DeleteLocalRef(newString);
+ checkException(env);
+ }
+ env->DeleteLocalRef(stringClass);
+ return array;
+}
+
+void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) {
+ if (!chooser)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ WTF::String acceptType = chooser->acceptTypes();
+ jstring jAcceptType = wtfStringToJstring(env, acceptType, true);
+ jstring jName = (jstring) env->CallObjectMethod(
+ m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType);
+ checkException(env);
+ env->DeleteLocalRef(jAcceptType);
+
+ const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL));
+
+ if (!string)
+ return;
+
+ WTF::String webcoreString = jstringToWtfString(env, jName);
+ env->ReleaseStringChars(jName, string);
+
+ if (webcoreString.length())
+ chooser->chooseFile(webcoreString);
+}
+
+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)
+{
+ // If m_popupReply is not null, then we already have a list showing.
+ if (m_popupReply != 0)
+ return;
+
+ LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
+
+ // Create an array of java Strings for the drop down.
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobjectArray labelArray = makeLabelArray(env, labels, count);
+
+ // Create an array determining whether each item is enabled.
+ jintArray enabledArray = env->NewIntArray(enabledCount);
+ checkException(env);
+ jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
+ checkException(env);
+
+ if (multiple) {
+ // Pass up an array representing which items are selected.
+ jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
+ checkException(env);
+ jint* selArray = env->GetIntArrayElements(selectedArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < selectedCountOrSelection; i++) {
+ selArray[i] = selected[i];
+ }
+ env->ReleaseIntArrayElements(selectedArray, selArray, 0);
+
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestListBox, labelArray, enabledArray,
+ selectedArray);
+ env->DeleteLocalRef(selectedArray);
+ } else {
+ // Pass up the single selection.
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
+ selectedCountOrSelection);
+ }
+
+ env->DeleteLocalRef(labelArray);
+ env->DeleteLocalRef(enabledArray);
+ checkException(env);
+
+ Retain(reply);
+ m_popupReply = reply;
+}
+
+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();
+ bool handled = eventHandler->keyEvent(event);
+ 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
+ // return false so that the key will be sent back to our navigation
+ // system.
+ handled |= frame->selection()->selection() != old;
+ }
+ return handled;
+ } else {
+ eventHandler = m_mainFrame->eventHandler();
+ }
+ return eventHandler->keyEvent(event);
+}
+
+// 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);
+ }
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
+{
+ RenderView* contentRenderer = m_mainFrame->contentRenderer();
+ if (!contentRenderer)
+ return 0;
+ return static_cast<GraphicsLayerAndroid*>(
+ contentRenderer->compositor()->rootPlatformLayer());
+}
+#endif
+
+bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
+{
+ bool preventDefault = false;
+
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
+ if (rootLayer)
+ rootLayer->pauseDisplay(true);
+#endif
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ #define MOTION_EVENT_ACTION_POINTER_DOWN 5
+ #define MOTION_EVENT_ACTION_POINTER_UP 6
+
+ WebCore::TouchEventType type = WebCore::TouchStart;
+ WebCore::PlatformTouchPoint::State defaultTouchState;
+ Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size());
+
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEnd;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchMove;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchCancel;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled;
+ break;
+ case 5: // MotionEvent.ACTION_POINTER_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
+ break;
+ case 6: // MotionEvent.ACTION_POINTER_UP
+ 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.
+ LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
+ return 0;
+ }
+
+ for (int c = 0; c < static_cast<int>(points.size()); c++) {
+ points[c].setX(points[c].x() - m_scrollOffsetX);
+ points[c].setY(points[c].y() - m_scrollOffsetY);
+
+ // Setting the touch state for each point.
+ // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP.
+ if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed;
+ } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased;
+ } else {
+ touchStates[c] = defaultTouchState;
+ };
+ }
+
+ WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (rootLayer)
+ rootLayer->pauseDisplay(false);
+#endif
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y)
+{
+ 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) {
+ LOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
+ const NamedNodeMap* attributes = node->attributes();
+ if (!attributes) return false;
+ size_t length = attributes->length();
+ for (size_t i = 0; i < length; i++) {
+ const Attribute* a = attributes->attributeItem(i);
+ if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden")
+ return true;
+ }
+ return false;
+}
+
+// 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;
+ }
+ }
+ 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);
+
+ // 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);
+ 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();
+ }
+ return handled;
+}
+
+void WebViewCore::popupReply(int index)
+{
+ if (m_popupReply) {
+ m_popupReply->replyInt(index);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::popupReply(const int* array, int count)
+{
+ if (m_popupReply) {
+ m_popupReply->replyIntArray(array, count);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::formDidBlur(const WebCore::Node* node)
+{
+ // 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::focusNodeChanged(const WebCore::Node* newFocus)
+{
+ if (isTextInput(newFocus))
+ m_shouldPaintCaret = true;
+ else if (m_blurringNodePointer) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_formDidBlur, m_blurringNodePointer);
+ checkException(env);
+ m_blurringNodePointer = 0;
+ }
+}
+
+void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMessageStr = wtfStringToJstring(env, message);
+ jstring jSourceIDStr = wtfStringToJstring(env, sourceID);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
+ jSourceIDStr, msgLevel);
+ env->DeleteLocalRef(jMessageStr);
+ env->DeleteLocalRef(jSourceIDStr);
+ checkException(env);
+}
+
+void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+void WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
+{
+#if ENABLE(DATABASE)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
+ jDatabaseIdentifierStr, currentQuota, estimatedSize);
+ env->DeleteLocalRef(jDatabaseIdentifierStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
+{
+ m_groupForVisitedLinks = group;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_populateVisitedLinks);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring originString = wtfStringToJstring(env, origin);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsShowPrompt,
+ originString);
+ env->DeleteLocalRef(originString);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsHidePrompt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsHidePrompt);
+ checkException(env);
+}
+
+jobject WebViewCore::getDeviceMotionService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceMotionService);
+ checkException(env);
+ return object;
+}
+
+jobject WebViewCore::getDeviceOrientationService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceOrientationService);
+ checkException(env);
+ return object;
+}
+
+bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jDefaultStr = wtfStringToJstring(env, defaultValue);
+ jstring returnVal = static_cast<jstring>(env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr));
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ checkException(env);
+
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = jstringToWtfString(env, returnVal);
+ env->DeleteLocalRef(returnVal);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, message);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsInterrupt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsInterrupt);
+ checkException(env);
+ return result;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return m_javaGlue->object(JSC::Bindings::getJNIEnv());
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextSelection() {
+ WebCore::Node* focusNode = currentFocus();
+ if (!focusNode)
+ return;
+ RenderObject* renderer = focusNode->renderer();
+ if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
+ return;
+ RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
+ rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WTF::String& text)
+{
+ if (m_blockTextfieldUpdates)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (changeToPassword) {
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, true, 0, m_textGeneration);
+ checkException(env);
+ return;
+ }
+ jstring string = wtfStringToJstring(env, text);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::clearTextEntry()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_clearTextEntry);
+}
+
+void WebViewCore::setBackgroundColor(SkColor c)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ if (!view)
+ return;
+
+ // need (int) cast to find the right constructor
+ WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
+ (int)SkColorGetB(c), (int)SkColorGetA(c));
+ view->setBaseBackgroundColor(bcolor);
+
+ // Background color of 0 indicates we want a transparent background
+ if (c == 0)
+ view->setTransparent(true);
+}
+
+jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ jstring libString = wtfStringToJstring(env, libName);
+ jstring classString = env->NewStringUTF(className);
+ jobject pluginClass = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getPluginClass,
+ libString, classString);
+ checkException(env);
+
+ // cleanup unneeded local JNI references
+ env->DeleteLocalRef(libString);
+ env->DeleteLocalRef(classString);
+
+ if (pluginClass != NULL) {
+ return static_cast<jclass>(pluginClass);
+ } else {
+ return NULL;
+ }
+}
+
+void WebViewCore::showFullScreenPlugin(jobject childView, NPP npp)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ env->CallVoidMethod(obj.get(),
+ m_javaGlue->m_showFullScreenPlugin, childView, (int)npp);
+ checkException(env);
+}
+
+void WebViewCore::hideFullScreenPlugin()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_hideFullScreenPlugin);
+ checkException(env);
+}
+
+jobject WebViewCore::createSurface(jobject view)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_createSurface, view);
+ checkException(env);
+ return result;
+}
+
+jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addSurface,
+ view, x, y, width, height);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateSurface, childView,
+ x, y, width, height);
+ checkException(env);
+}
+
+void WebViewCore::destroySurface(jobject childView)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_destroySurface, childView);
+ checkException(env);
+}
+
+jobject WebViewCore::getContext()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ jobject result = env->CallObjectMethod(obj.get(), m_javaGlue->m_getContext);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::keepScreenOn(bool screenOn) {
+ if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_keepScreenOn, screenOn);
+ checkException(env);
+ }
+
+ // update the counter
+ if (screenOn)
+ m_screenOnCounter++;
+ else if (m_screenOnCounter > 0)
+ 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)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_showRect,
+ left, top, width, height, contentWidth, contentHeight,
+ xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
+ checkException(env);
+}
+
+void WebViewCore::centerFitRect(int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_centerFitRect, x, y, width, height);
+ checkException(env);
+}
+
+
+void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setScrollbarModes,
+ horizontalMode, verticalMode);
+ checkException(env);
+}
+
+void WebViewCore::notifyWebAppCanBeInstalled()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp);
+ checkException(env);
+}
+
+#if ENABLE(VIDEO)
+void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr);
+ checkException(env);
+}
+#endif
+
+void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary)
+{
+#if ENABLE(WEB_AUTOFILL)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring preview = env->NewString(previewSummary.data(), previewSummary.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview);
+ env->DeleteLocalRef(preview);
+#endif
+}
+
+bool WebViewCore::drawIsPaused() const
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetBooleanField(m_javaGlue->object(env).get(),
+ gWebViewCoreFields.m_drawIsPaused);
+}
+
+#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
+ if (m_webRequestContext)
+ m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used
+}
+
+void WebViewCore::setWebRequestContextCacheMode(int cacheMode)
+{
+ m_cacheMode = cacheMode;
+ // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
+ if (!m_webRequestContext)
+ return;
+
+ m_webRequestContext->setCacheMode(cacheMode);
+}
+
+WebRequestContext* WebViewCore::webRequestContext()
+{
+ if (!m_webRequestContext) {
+ Settings* settings = mainFrame()->settings();
+ m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled());
+ setWebRequestContextUserAgent();
+ setWebRequestContextCacheMode(m_cacheMode);
+ }
+ return m_webRequestContext.get();
+}
+#endif
+
+void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* root = graphicsRootLayer();
+ if (!root)
+ return;
+
+ LayerAndroid* layerAndroid = root->platformLayer();
+ if (!layerAndroid)
+ return;
+
+ LayerAndroid* target = layerAndroid->findById(layer);
+ if (!target)
+ return;
+
+ RenderLayer* owner = target->owningLayer();
+ if (!owner)
+ return;
+
+ if (owner->stackingContext())
+ owner->scrollToOffset(rect.fLeft, rect.fTop, true, false);
+#endif
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static void RevealSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->revealSelection();
+}
+
+static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
+ int nodePointer)
+{
+ return wtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
+ (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
+}
+
+static void ClearContent(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->clearContent();
+}
+
+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)
+{
+#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 gen, jboolean sendScrollEvent, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(gen, sendScrollEvent, x, y);
+}
+
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
+{
+#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 keyCode, jint unichar,
+ jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
+ jboolean isDown)
+{
+#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 Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr, jboolean fake)
+{
+#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)
+{
+ GET_NATIVE_VIEW(env, obj)->contentInvalidateAll();
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
+ jint textGeneration)
+{
+#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 start, jint end)
+{
+#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 direction, jint granularity)
+{
+#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 oldStart, jint oldEnd, jstring replace, jint start, jint end,
+ jint textGeneration)
+{
+#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 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);
+ GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
+ PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
+}
+
+static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
+ jint y)
+{
+#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, jboolean active)
+{
+#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 frame)
+{
+#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)
+{
+ if (m_groupForVisitedLinks)
+ m_groupForVisitedLinks->addVisitedLink(string, length);
+}
+
+static jint UpdateLayers(JNIEnv *env, jobject obj, jobject region)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ BaseLayerAndroid* result = viewImpl->createBaseLayer();
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ if (result) {
+ SkIRect bounds;
+ LayerAndroid* root = static_cast<LayerAndroid*>(result->getChild(0));
+ if (root) {
+ root->bounds().roundOut(&bounds);
+ nativeRegion->setRect(bounds);
+ }
+ }
+ return reinterpret_cast<jint>(result);
+}
+
+static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
+{
+#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(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return reinterpret_cast<jint>(result);
+}
+
+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)
+{
+#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);
+}
+
+// Set aside a predetermined amount of space in which to place the listbox
+// choices, to avoid unnecessary allocations.
+// The size here is arbitrary. We want the size to be at least as great as the
+// number of items in the average multiple-select listbox.
+#define PREPARED_LISTBOX_STORAGE 10
+
+static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
+ jint size)
+{
+#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();
+ int count = 0;
+ for (int i = 0; i < size; i++) {
+ if (ptrArray[i]) {
+ array[count++] = i;
+ }
+ }
+ env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
+ viewImpl->popupReply(array, count);
+}
+
+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);
+ 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);
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jintArray idArray,
+ jintArray xArray, jintArray yArray,
+ jint count, jint actionIndex, jint metaState)
+{
+#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);
+ Vector<int> ids(count);
+ Vector<IntPoint> points(count);
+ for (int c = 0; c < count; c++) {
+ ids[c] = ptrIdArray[c];
+ points[c].setX(ptrXArray[c]);
+ points[c].setY(ptrYArray[c]);
+ }
+ env->ReleaseIntArrayElements(idArray, ptrIdArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT);
+
+ return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint frame, jint node, 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->touchUp(touchGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, 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__);
+ WTF::String result = viewImpl->retrieveHref(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, 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__);
+ WTF::String result = viewImpl->retrieveAnchorText(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveImageSource(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WTF::String result = GET_NATIVE_VIEW(env, obj)->retrieveImageSource(x, y);
+ return !result.isEmpty() ? wtfStringToJstring(env, result) : 0;
+}
+
+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)
+{
+#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 void MoveMouse(JNIEnv *env, jobject obj, 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->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) {
+ WebCore::Document* document = frame->document();
+ if (document) {
+ WebCore::RenderObject* renderer = document->renderer();
+ if (renderer && renderer->isRenderView()) {
+ return renderer->minPreferredLogicalWidth();
+ }
+ }
+ }
+ return 0;
+}
+
+static void SetViewportSettingsFromNative(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::Settings* s = viewImpl->mainFrame()->page()->settings();
+ if (!s)
+ return;
+
+#ifdef ANDROID_META_SUPPORT
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
+ env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
+#endif
+}
+
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
+{
+#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, jboolean useFile)
+{
+ 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, jboolean useFile)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpRenderTree(useFile);
+}
+
+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, jlong quota) {
+#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
+ 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
+ // up.
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
+ chromeC->wakeUpMainThreadWithNewQuota(quota);
+#endif
+}
+
+// Called from Java to provide a Geolocation permission state for the specified origin.
+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, jstring scheme) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
+}
+
+static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
+}
+
+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.
+ ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
+ ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
+ chromeClientAndroid->storeGeolocationPermissions();
+
+ 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();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kPause_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
+}
+
+static void Resume(JNIEnv* env, jobject obj)
+{
+ 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();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kResume_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
+}
+
+static void FreeMemory(JNIEnv* env, jobject obj)
+{
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+}
+
+static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ jobjectArray array = static_cast<jobjectArray>(hist);
+
+ jsize len = env->GetArrayLength(array);
+ for (jsize i = 0; i < len; i++) {
+ jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
+ const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, 0));
+ jsize len = env->GetStringLength(item);
+ viewImpl->addVisitedLink(str, len);
+ env->ReleaseStringChars(item, str);
+ env->DeleteLocalRef(item);
+ }
+}
+
+// Notification from the UI thread that the plugin's full-screen surface has been discarded
+static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
+ if (plugin)
+ plugin->exitFullScreen(false);
+}
+
+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 = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return 0;
+ 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].right(), rects[i].bottom());
+ 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 queryId)
+{
+#if ENABLE(WEB_AUTOFILL)
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+
+ WebCore::Frame* frame = viewImpl->mainFrame();
+ if (frame) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->fillFormFields(queryId);
+ }
+#endif
+}
+
+static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint layer, jobject jRect)
+{
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jRect, &rect);
+ GET_NATIVE_VIEW(env, obj)->scrollRenderLayer(layer, rect);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeFocusBoundsChanged", "()Z",
+ (void*) FocusBoundsChanged } ,
+ { "nativeKey", "(IIIZZZZ)Z",
+ (void*) Key },
+ { "nativeClick", "(IIZ)V",
+ (void*) Click },
+ { "nativeContentInvalidateAll", "()V",
+ (void*) ContentInvalidateAll },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIFIIIIZ)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(IZII)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(II)V",
+ (void*) SetSelection } ,
+ { "nativeModifySelection", "(II)Ljava/lang/String;",
+ (void*) ModifySelection },
+ { "nativeDeleteSelection", "(III)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
+ (void*) ReplaceTextfieldText } ,
+ { "nativeMoveFocus", "(II)V",
+ (void*) MoveFocus },
+ { "nativeMoveMouse", "(III)V",
+ (void*) MoveMouse },
+ { "nativeMoveMouseIfLatest", "(IIII)V",
+ (void*) MoveMouseIfLatest },
+ { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs },
+ { "nativeScrollFocusedTextInput", "(FI)V",
+ (void*) ScrollFocusedTextInput },
+ { "nativeSetFocusControllerActive", "(Z)V",
+ (void*) SetFocusControllerActive },
+ { "nativeSaveDocumentState", "(I)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(I[I[I[IIII)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIII)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
+ (void*) RetrieveAnchorText },
+ { "nativeRetrieveImageSource", "(II)Ljava/lang/String;",
+ (void*) RetrieveImageSource },
+ { "nativeStopPaintingCaret", "()V",
+ (void*) StopPaintingCaret },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeUpdateLayers", "(Landroid/graphics/Region;)I",
+ (void*) UpdateLayers },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSplitContent", "(I)V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree },
+ { "nativeDumpV8Counters", "()V",
+ (void*) DumpV8Counters },
+ { "nativeSetNewStorageLimit", "(J)V",
+ (void*) SetNewStorageLimit },
+ { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
+ (void*) GeolocationPermissionsProvide },
+ { "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", "()V", (void*) RevealSelection },
+ { "nativeUpdateFrameCacheIfLoading", "()V",
+ (void*) UpdateFrameCacheIfLoading },
+ { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
+ (void*) ProvideVisitedHistory },
+ { "nativeFullScreenPluginHidden", "(I)V",
+ (void*) FullScreenPluginHidden },
+ { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
+ (void*) ValidNodeAndBounds },
+ { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;",
+ (void*) GetTouchHighlightRects },
+ { "nativeAutoFillForm", "(I)V",
+ (void*) AutoFillForm },
+ { "nativeScrollLayer", "(ILandroid/graphics/Rect;)V",
+ (void*) ScrollRenderLayer },
+};
+
+int registerWebViewCore(JNIEnv* env)
+{
+ jclass widget = env->FindClass("android/webkit/WebViewCore");
+ LOG_ASSERT(widget,
+ "Unable to find class android/webkit/WebViewCore");
+ gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
+ "I");
+ LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
+ "Unable to find android/webkit/WebViewCore.mNativeClass");
+ gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
+ "mViewportWidth", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
+ "Unable to find android/webkit/WebViewCore.mViewportWidth");
+ gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
+ "mViewportHeight", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
+ "Unable to find android/webkit/WebViewCore.mViewportHeight");
+ gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
+ "mViewportInitialScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
+ "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
+ gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
+ "mViewportMinimumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
+ gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
+ "mViewportMaximumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
+ gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
+ "mViewportUserScalable", "Z");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
+ "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
+ gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
+ "mViewportDensityDpi", "I");
+ 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");
+ 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");
+ gWebViewCoreFields.m_highUsageDeltaMb = env->GetFieldID(widget, "mHighUsageDeltaMb", "I");
+
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType =
+ env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z");
+ LOG_FATAL_IF(!gWebViewCoreStaticMethods.m_isSupportedMediaMimeType,
+ "Could not find static method isSupportedMediaMimeType from WebViewCore");
+
+ env->DeleteLocalRef(widget);
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h
new file mode 100644
index 0000000..8d8f303
--- /dev/null
+++ b/Source/WebKit/android/jni/WebViewCore.h
@@ -0,0 +1,714 @@
+/*
+ * 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 WEBVIEWCORE_H
+#define WEBVIEWCORE_H
+
+#include "CacheBuilder.h"
+#include "CachedHistory.h"
+#include "DeviceMotionAndOrientationManager.h"
+#include "DOMSelection.h"
+#include "FileChooser.h"
+#include "PictureSet.h"
+#include "PlatformGraphicsContext.h"
+#include "SkColor.h"
+#include "SkTDArray.h"
+#include "SkRegion.h"
+#include "Timer.h"
+#include "WebCoreRefObject.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "android_npapi.h"
+
+#include <jni.h>
+#include <ui/KeycodeLabels.h>
+#include <ui/PixelFormat.h>
+
+namespace WebCore {
+ class Color;
+ class FrameView;
+ class HTMLAnchorElement;
+ class HTMLElement;
+ class HTMLImageElement;
+ class HTMLSelectElement;
+ class RenderPart;
+ class RenderText;
+ class Node;
+ class PlatformKeyboardEvent;
+ class QualifiedName;
+ class RenderTextControl;
+ class ScrollView;
+ class TimerBase;
+ class PageGroup;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+namespace WebCore {
+ class GraphicsLayerAndroid;
+}
+#endif
+
+namespace WebCore {
+ class BaseLayerAndroid;
+}
+
+struct PluginWidgetAndroid;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ enum Direction {
+ DIRECTION_BACKWARD = 0,
+ DIRECTION_FORWARD = 1
+ };
+
+ enum NavigationAxis {
+ AXIS_CHARACTER = 0,
+ AXIS_WORD = 1,
+ AXIS_SENTENCE = 2,
+ AXIS_HEADING = 3,
+ AXIS_SIBLING = 4,
+ AXIS_PARENT_FIRST_CHILD = 5,
+ AXIS_DOCUMENT = 6
+ };
+
+ class CachedFrame;
+ class CachedNode;
+ class CachedRoot;
+ class ListBoxReply;
+
+ class WebCoreReply : public WebCoreRefObject {
+ public:
+ virtual ~WebCoreReply() {}
+
+ virtual void replyInt(int value) {
+ SkDEBUGF(("WebCoreReply::replyInt(%d) not handled\n", value));
+ }
+
+ virtual void replyIntArray(const int* array, int count) {
+ SkDEBUGF(("WebCoreReply::replyIntArray() not handled\n"));
+ }
+ // add more replyFoo signatures as needed
+ };
+
+ // one instance of WebViewCore per page for calling into Java's WebViewCore
+ class WebViewCore : public WebCoreRefObject {
+ public:
+ /**
+ * Initialize the native WebViewCore with a JNI environment, a Java
+ * WebViewCore object and the main frame.
+ */
+ WebViewCore(JNIEnv* env, jobject javaView, WebCore::Frame* mainframe);
+ ~WebViewCore();
+
+ // helper function
+ static WebViewCore* getWebViewCore(const WebCore::FrameView* view);
+ static WebViewCore* getWebViewCore(const WebCore::ScrollView* view);
+
+ // Followings are called from native WebCore to Java
+
+ /**
+ * 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.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param animate If it is true, animate to the new scroll position
+ *
+ * This method calls Java to trigger a gradual scroll event.
+ */
+ void scrollTo(int x, int y, bool animate = false);
+
+ /**
+ * Record the invalid rectangle
+ */
+ void contentInvalidate(const WebCore::IntRect &rect);
+ void contentInvalidateAll();
+
+ /**
+ * Satisfy any outstanding invalidates, so that the current state
+ * of the DOM is drawn.
+ */
+ void contentDraw();
+
+ /**
+ * copy the layers to the UI side
+ */
+ void layersDraw();
+
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* graphicsRootLayer() const;
+#endif
+
+ /** Invalidate the view/screen, NOT the content/DOM, but expressed in
+ * content/DOM coordinates (i.e. they need to eventually be scaled,
+ * by webview into view.java coordinates
+ */
+ void viewInvalidate(const WebCore::IntRect& rect);
+
+ /**
+ * Invalidate part of the content that may be offscreen at the moment
+ */
+ void offInvalidate(const WebCore::IntRect &rect);
+
+ /**
+ * Called by webcore when the progress indicator is done
+ * used to rebuild and display any changes in focus
+ */
+ void notifyProgressFinished();
+
+ /**
+ * Notify the view that WebCore did its first layout.
+ */
+ void didFirstLayout();
+
+ /**
+ * Notify the view to update the viewport.
+ */
+ void updateViewport();
+
+ /**
+ * Notify the view to restore the screen width, which in turn restores
+ * the scale. Also restore the scale for the text wrap.
+ */
+ void restoreScale(float scale, float textWrapScale);
+
+ /**
+ * 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 String
+ * @param text If changeToPassword is false, this is the new text that
+ * should go into the textfield.
+ */
+ void updateTextfield(WebCore::Node* pointer,
+ bool changeToPassword, const WTF::String& text);
+
+ /**
+ * Tell the java side to update the current selection in the focused
+ * textfield to the WebTextView. This function finds the currently
+ * focused textinput, and passes its selection to java.
+ * If there is no focus, or it is not a text input, this does nothing.
+ */
+ void updateTextSelection();
+
+ void clearTextEntry();
+ // JavaScript support
+ void jsAlert(const WTF::String& url, const WTF::String& text);
+ bool jsConfirm(const WTF::String& url, const WTF::String& text);
+ bool jsPrompt(const WTF::String& url, const WTF::String& message,
+ const WTF::String& defaultValue, WTF::String& result);
+ bool jsUnload(const WTF::String& url, const WTF::String& message);
+ bool jsInterrupt();
+
+ /**
+ * Tell the Java side that the origin has exceeded its database quota.
+ * @param url The URL of the page that caused the quota overflow
+ * @param databaseIdentifier the id of the database that caused the
+ * quota overflow.
+ * @param currentQuota The current quota for the origin
+ * @param estimatedSize The estimated size of the database
+ */
+ void exceededDatabaseQuota(const WTF::String& url,
+ const WTF::String& databaseIdentifier,
+ const unsigned long long currentQuota,
+ const unsigned long long estimatedSize);
+
+ /**
+ * Tell the Java side that the appcache has exceeded its max size.
+ * @param spaceNeeded is the amount of disk space that would be needed
+ * in order for the last appcache operation to succeed.
+ */
+ void reachedMaxAppCacheSize(const unsigned long long spaceNeeded);
+
+ /**
+ * Set up the PageGroup's idea of which links have been visited,
+ * with the browser history.
+ * @param group the object to deliver the links to.
+ */
+ void populateVisitedLinks(WebCore::PageGroup*);
+
+ /**
+ * Instruct the browser to show a Geolocation permission prompt for the
+ * specified origin.
+ * @param origin The origin of the frame requesting Geolocation
+ * permissions.
+ */
+ void geolocationPermissionsShowPrompt(const WTF::String& origin);
+ /**
+ * Instruct the browser to hide the Geolocation permission prompt.
+ */
+ void geolocationPermissionsHidePrompt();
+
+ jobject getDeviceMotionService();
+ jobject getDeviceOrientationService();
+
+ void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID, int msgLevel);
+
+ /**
+ * Tell the Java side of the scrollbar mode
+ */
+ void setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode);
+
+ //
+ // Followings support calls from Java to native WebCore
+ //
+
+ WTF::String retrieveHref(int x, int y);
+ WTF::String retrieveAnchorText(int x, int y);
+ WTF::String retrieveImageSource(int x, int y);
+ WTF::String requestLabel(WebCore::Frame* , WebCore::Node* );
+
+ // 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 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(int moveGeneration, bool sendScrollEvent, int dx, int dy);
+
+ void setGlobalBounds(int x, int y, int h, int v);
+
+ void setSizeScreenWidthAndScale(int width, int height, int screenWidth,
+ float scale, int realScreenWidth, int screenHeight, int anchorX,
+ int anchorY, bool ignoreHeight);
+
+ /**
+ * Handle key events from Java.
+ * @return Whether keyCode was handled by this class.
+ */
+ bool key(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
+ */
+ bool handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState);
+
+ /**
+ * 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.
+ */
+ void touchUp(int touchGeneration, WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y);
+
+ /**
+ * Sets the index of the label from a popup
+ */
+ void popupReply(int index);
+ void popupReply(const int* array, int count);
+
+ /**
+ * Delete text from start to end in the focused textfield.
+ * If start == end, set the selection, but perform no deletion.
+ * If there is no focus, silently fail.
+ * If start and end are out of order, swap them.
+ */
+ void deleteSelection(int start, int end, int textGeneration);
+
+ /**
+ * Set the selection of the currently focused textfield to (start, end).
+ * If start and end are out of order, swap them.
+ */
+ void setSelection(int start, int end);
+
+ /**
+ * Modifies the current selection.
+ *
+ * Note: Accessibility support.
+ *
+ * direction - The direction in which to alter the selection.
+ * granularity - The granularity of the selection modification.
+ *
+ * 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.
+ */
+ String modifySelection(const int direction, const int granularity);
+
+ /**
+ * Moves the selection to the given node in a given frame i.e. selects that node.
+ *
+ * Note: Accessibility support.
+ *
+ * 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 string. This is not a well formed
+ * HTML, rather the selection annotated with the tags of all
+ * intermediary elements it crosses.
+ */
+ String moveSelection(WebCore::Frame* frame, WebCore::Node* node);
+
+ /**
+ * In the currently focused textfield, replace the characters from oldStart to oldEnd
+ * (if oldStart == oldEnd, this will be an insert at that position) with replace,
+ * and set the selection to (start, end).
+ */
+ void replaceTextfieldText(int oldStart,
+ int oldEnd, const WTF::String& replace, int start, int end,
+ int textGeneration);
+ void passToJs(int generation,
+ const WTF::String& , const WebCore::PlatformKeyboardEvent& );
+ /**
+ * Scroll the focused textfield to (x, y) in document space
+ */
+ void scrollFocusedTextInput(float x, int y);
+ /**
+ * Set the FocusController's active and focused states, so that
+ * the caret will draw (true) or not.
+ */
+ void setFocusControllerActive(bool active);
+
+ void saveDocumentState(WebCore::Frame* frame);
+
+ void addVisitedLink(const UChar*, int);
+
+ // TODO: I don't like this hack but I need to access the java object in
+ // order to send it as a parameter to java
+ AutoJObject getJavaObject();
+
+ // Return the parent WebView Java object associated with this
+ // WebViewCore.
+ 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
+ pageflipping bitmap.
+ */
+ void addPlugin(PluginWidgetAndroid*);
+ void removePlugin(PluginWidgetAndroid*);
+ // returns true if the pluginwidgit is in our active list
+ bool isPlugin(PluginWidgetAndroid*) const;
+ void invalPlugin(PluginWidgetAndroid*);
+ void drawPlugins();
+
+ // send the current screen size/zoom to all of the plugins in our list
+ void sendPluginVisibleScreen();
+
+ // send onLoad event to plugins who are descendents of the given frame
+ void notifyPluginsOnFrameLoad(const Frame*);
+
+ // gets a rect representing the current on-screen portion of the document
+ void getVisibleScreen(ANPRectI&);
+
+ // send this event to all of the plugins in our list
+ void sendPluginEvent(const ANPEvent&);
+
+ // 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);
+
+ void requestKeyboardWithSelection(const WebCore::Node*, int selStart, int selEnd);
+ // Notify the Java side that webkit is requesting a keyboard
+ void requestKeyboard(bool showKeyboard);
+
+ // Generates a class loader that contains classes from the plugin's apk
+ jclass getPluginClass(const WTF::String& libName, const char* className);
+
+ // Creates a full screen surface for a plugin
+ void showFullScreenPlugin(jobject webkitPlugin, NPP npp);
+
+ // Instructs the UI thread to discard the plugin's full-screen surface
+ void hideFullScreenPlugin();
+
+ // Creates a childView for the plugin but does not attach to the view hierarchy
+ jobject createSurface(jobject view);
+
+ // Adds the plugin's view (aka surface) to the view hierarchy
+ jobject addSurface(jobject view, int x, int y, int width, int height);
+
+ // Updates a Surface coordinates and dimensions for a plugin
+ void updateSurface(jobject childView, int x, int y, int width, int height);
+
+ // Destroys a SurfaceView for a plugin
+ void destroySurface(jobject childView);
+
+ // Returns the context (android.content.Context) of the WebView
+ jobject getContext();
+
+ // 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)
+ // pinned at the screen position (xPercentInView, yPercentInView).
+ void showRect(int left, int top, int width, int height, int contentWidth,
+ int contentHeight, float xPercentInDoc, float xPercentInView,
+ float yPercentInDoc, float yPercentInView);
+
+ // Scale the rect (x, y, width, height) to make it just fit and centered
+ // in the current view.
+ void centerFitRect(int x, int y, int width, int height);
+
+ // return a list of rects matching the touch point (x, y) with the slop
+ Vector<IntRect> getTouchHighlightRects(int x, int y, int slop);
+
+ // Open a file chooser for selecting a file to upload
+ void openFileChooser(PassRefPtr<WebCore::FileChooser> );
+
+ // reset the picture set to empty
+ void clearContent();
+
+ bool focusBoundsChanged();
+
+ // 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().
+ BaseLayerAndroid* createBaseLayer();
+
+ 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; }
+ void updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode);
+ void updateFrameCacheIfLoading();
+
+ // utility to split slow parts of the picture set
+ void splitContent(PictureSet*);
+
+ void notifyWebAppCanBeInstalled();
+
+#if ENABLE(VIDEO)
+ void enterFullscreenForVideoLayer(int layerId, const WTF::String& url);
+#endif
+
+ void setWebTextViewAutoFillable(int queryId, const string16& previewSummary);
+
+ DeviceMotionAndOrientationManager* deviceMotionAndOrientationManager() { return &m_deviceMotionAndOrientationManager; }
+
+ 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);
+ // call only from webkit thread (like add/remove), return true if inst
+ // is still alive
+ static bool isInstance(WebViewCore*);
+ // if there exists at least one WebViewCore instance then we return the
+ // application context, otherwise NULL is returned.
+ static jobject getApplicationContext();
+ // Check whether a media mimeType is supported in Android media framework.
+ static bool isSupportedMediaMimeType(const WTF::String& mimeType);
+
+ // 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;
+ // These two fields go together: we use the mutex to protect access to
+ // m_buttons, so that we, and webview.cpp can look/modify the m_buttons
+ // field safely from our respective threads
+ static Mutex gButtonMutex;
+ WTF::Vector<Container> m_buttons;
+ // end of shared members
+
+ // internal functions
+ private:
+ CacheBuilder& cacheBuilder();
+ WebCore::Node* currentFocus();
+ // Compare the new set of buttons to the old one. All of the new
+ // buttons either replace our old ones or should be added to our list.
+ // Then check the old buttons to see if any are no longer needed.
+ void updateButtonList(WTF::Vector<Container>* buttons);
+ void reset(bool fromConstructor);
+ // 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 recordPictureSet(PictureSet* master);
+
+ friend class ListBoxReply;
+ struct JavaGlue;
+ struct JavaGlue* m_javaGlue;
+ WebCore::Frame* m_mainFrame;
+ WebCoreReply* m_popupReply;
+ 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;
+ bool m_focusBoundsChanged;
+ bool m_skipContentDraw;
+ // 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
+ WebCore::IntPoint m_mousePos;
+ bool m_frameCacheOutOfDate;
+ bool m_progressDone;
+ int m_lastPassed;
+ int m_lastVelocity;
+ 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;
+ unsigned m_domtree_version;
+ bool m_check_domtree_version;
+ PageGroup* m_groupForVisitedLinks;
+ bool m_isPaused;
+ int m_cacheMode;
+ bool m_shouldPaintCaret;
+
+ SkTDArray<PluginWidgetAndroid*> m_plugins;
+ WebCore::Timer<WebViewCore> m_pluginInvalTimer;
+ void pluginInvalTimerFired(WebCore::Timer<WebViewCore>*) {
+ this->drawPlugins();
+ }
+ int m_screenOnCounter;
+
+ 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.
+ * @param frame Pointer to the Frame containing the node that was clicked on.
+ * @param node Pointer to the Node that was clicked on.
+ * @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
+ 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);
+ Node* m_currentNodeDomNavigationAxis;
+ 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);
+
+#if ENABLE(TOUCH_EVENTS)
+ bool m_forwardingTouchEvents;
+#endif
+#if DEBUG_NAV_UI
+ uint32_t m_now;
+#endif
+ DeviceMotionAndOrientationManager m_deviceMotionAndOrientationManager;
+#if USE(CHROME_NETWORK_STACK)
+ scoped_refptr<WebRequestContext> m_webRequestContext;
+#endif
+
+ // 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*);
+ };
+
+} // namespace android
+
+#endif // WEBVIEWCORE_H
diff --git a/Source/WebKit/android/nav/CacheBuilder.cpp b/Source/WebKit/android/nav/CacheBuilder.cpp
new file mode 100644
index 0000000..dc10f21
--- /dev/null
+++ b/Source/WebKit/android/nav/CacheBuilder.cpp
@@ -0,0 +1,3201 @@
+/*
+ * 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(), false, false);
+}
+
+#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 (node->hasTagName(HTMLNames::canvasTag))
+ mPictureSetDisabled = true;
+ if (checkForPluginViewThatWantsFocus(nodeRenderer)) {
+ bounds = absBounds;
+ isUnclipped = true;
+ takesFocus = true;
+ type = PLUGIN_CACHEDNODETYPE;
+ goto keepNode;
+ }
+ // Only use the root contentEditable element
+ if (node->isContentEditable() && !node->parentOrHostNode()->isContentEditable()) {
+ 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.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);
+ 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.setNavableRects();
+ 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();
+ lastCached->setNavableRects();
+ 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->getRect(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 (layer->width() == 0 || layer->height() == 0)
+ 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->width(), layer->height());
+ 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, false, false);
+#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.right() <= 0 || bounds.bottom() <= 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
+ int left = bounds.x() + ((RenderBox*)renderer)->paddingLeft()
+ + ((RenderBox*)renderer)->borderLeft();
+ int top = bounds.y() + ((RenderBox*)renderer)->paddingTop()
+ + ((RenderBox*)renderer)->borderTop();
+ int right = bounds.right() - ((RenderBox*)renderer)->paddingRight()
+ - ((RenderBox*)renderer)->borderRight();
+ int bottom = bounds.bottom() - ((RenderBox*)renderer)->paddingBottom()
+ - ((RenderBox*)renderer)->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..d48a045
--- /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..4b39810
--- /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..3d9e012
--- /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..b26e24b
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedFrame.cpp
@@ -0,0 +1,1494 @@
+/*
+ * 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.bottom() : prior.bottom();
+ 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.right();
+ int testRight = bestRect.right();
+ if (right < testRight)
+ right = testRight;
+ width = right - left;
+ } else {
+ left = direction == LEFT ? bestRect.right() : prior.right();
+ 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.bottom();
+ int testBottom = bestRect.bottom();
+ 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.right(), rect.bottom(),
+ testRect.x(), testRect.y(), testRect.right(), testRect.bottom());
+ 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.right(),
+ cursorRect.bottom());
+ 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->right() - testBounds.x(); break;
+ case RIGHT: distance = testBounds.right() - clip->x(); break;
+ case UP: distance = clip->bottom() - testBounds.y(); break;
+ case DOWN: distance = testBounds.bottom() - clip->y(); break;
+#endif
+ default:
+ distance = 0; ASSERT(0);
+ }
+ 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.bottom();
+ int testX = mNodeBounds.x();
+ int testRight = mNodeBounds.right();
+ setNavOverlap(navBounds.width(), navBounds.right() - 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.bottom() - mNodeBounds.bottom();
+ setNavInclusion(testRight - navBounds.right(), 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.right();
+ int testY = mNodeBounds.y();
+ int testBottom = mNodeBounds.bottom();
+ setNavOverlap(navBounds.height(), navBounds.bottom() - testY,
+ testBottom - navBounds.y());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavRight = navBounds.right() - mNodeBounds.right();
+ 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.bottom());
+ 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.right();
+ int testY = mNodeBounds.y();
+ int testBottom = mNodeBounds.bottom();
+ setNavOverlap(navBounds.height(), navBounds.bottom() - 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.right() - mNodeBounds.right();
+ setNavInclusion(testBottom - navBounds.bottom(), 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.bottom();
+ int testX = mNodeBounds.x();
+ int testRight = mNodeBounds.right();
+ setNavOverlap(navBounds.width(), navBounds.right() - testX,
+ testRight - navBounds.x());
+ if (canBeReachedByAnotherDirection()) {
+ mNode->setCondition(CachedNode::BEST_DIRECTION);
+ return REJECT_TEST;
+ }
+ int inNavBottom = navBounds.bottom() - mNodeBounds.bottom();
+ 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.right());
+ 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..039a0ee
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedFrame.h
@@ -0,0 +1,285 @@
+/*
+ * 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 CachedFrame_H
+#define CachedFrame_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().bottom(); }
+ 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().right(); }
+ 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
diff --git a/Source/WebKit/android/nav/CachedHistory.cpp b/Source/WebKit/android/nav/CachedHistory.cpp
new file mode 100644
index 0000000..9066412
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedHistory.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.bottom())
+ mMinWorkingHorizontal = viewBounds.y();
+ if (mMaxWorkingHorizontal > viewBounds.bottom() ||
+ mMaxWorkingHorizontal <= viewBounds.y())
+ mMaxWorkingHorizontal = viewBounds.bottom();
+ if (mMinWorkingVertical < viewBounds.x() ||
+ mMinWorkingVertical >= viewBounds.right())
+ mMinWorkingVertical = viewBounds.x();
+ if (mMaxWorkingVertical > viewBounds.right() ||
+ mMaxWorkingVertical <= viewBounds.x())
+ mMaxWorkingVertical = viewBounds.right();
+}
+
+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->bottom();
+ }
+ if (lastAxis != CachedFrame::UP && navBounds->width() > 0) {
+ mMinWorkingVertical = navBounds->x();
+ mMaxWorkingVertical = navBounds->right();
+ }
+ }
+ 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..e8c1ad9
--- /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..f7f9eea
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedInput.h
@@ -0,0 +1,112 @@
+/*
+ * 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 setTextSize(float textSize) { mTextSize = textSize; }
+ 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 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..299f2d1
--- /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 SkLayer* 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..3d963e0
--- /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..e3ba34d
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedNode.cpp
@@ -0,0 +1,432 @@
+/*
+ * 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 = mNavableRects;
+ ASSERT(partMax > 0);
+ WebCore::IntRect bounds = mCursorRing[0];
+ for (int partIndex = 1; 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 (mNavableRects <= 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->bottom();
+ int unitLeft = unitBoundsPtr->x();
+ int unitRight = unitBoundsPtr->right();
+ WebCore::IntRect* testBoundsPtr = mCursorRing.begin() - 1;
+ while (++testBoundsPtr < unitBoundsEnd) {
+ if (unitBoundsPtr == testBoundsPtr)
+ continue;
+ int testTop = testBoundsPtr->y();
+ int testBottom = testBoundsPtr->bottom();
+ int testLeft = testBoundsPtr->x();
+ int testRight = testBoundsPtr->right();
+ 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->bottom())
+ candidateTop = checkBoundsPtr->bottom();
+ else if (candidateTop < checkBoundsPtr->y() &&
+ candidateBottom <= checkBoundsPtr->bottom())
+ candidateBottom = checkBoundsPtr->y();
+ else
+ goto nextCheck;
+ } else {
+ if (candidateLeft >= checkBoundsPtr->x() &&
+ candidateRight > checkBoundsPtr->right())
+ candidateLeft = checkBoundsPtr->right();
+ else if (candidateLeft < checkBoundsPtr->x() &&
+ candidateRight <= checkBoundsPtr->right())
+ 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 = mNavableRects;
+ int innerMax = other->mNavableRects;
+ 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 mNavableRects=%d;\n", b->mNavableRects);
+ 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..f9bcbed
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedNode.h
@@ -0,0 +1,247 @@
+/*
+ * 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 mNavableRects; }
+ 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 setNavableRects() { mNavableRects = mCursorRing.size(); }
+ 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 mNavableRects; // FIXME: could be bitfield once I limit max number of rects
+ 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..8bc9328
--- /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..73a5c2c
--- /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..64bf19a
--- /dev/null
+++ b/Source/WebKit/android/nav/CachedRoot.cpp
@@ -0,0 +1,1813 @@
+/*
+ * 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->right(), r->bottom()};
+ 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.right(),
+ contentBounds.bottom(),
+ overBounds.x(), overBounds.y(), overBounds.right(), overBounds.bottom(),
+ mScrolledBounds.x(), mScrolledBounds.y(), mScrolledBounds.right(),
+ mScrolledBounds.bottom(),
+ mViewBounds.x(), mViewBounds.y(), mViewBounds.right(),
+ mViewBounds.bottom(),
+ bitBounds->x(), bitBounds->y(), bitBounds->right(),
+ bitBounds->bottom());
+}
+
+
+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.right(), bitBounds.bottom(),
+ nodeBounds.x(), nodeBounds.y(), nodeBounds.right(), nodeBounds.bottom(),
+ testBounds.x(), testBounds.y(), testBounds.right(), testBounds.bottom(),
+ 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 (mRootLayer) CachedLayer::Debug::printRootLayerAndroid(mRootLayer);
+#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.right());
+ setupScrolledBounds();
+ // (line up)
+ mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
+ int testTop = mScrolledBounds.y();
+ int viewBottom = mViewBounds.bottom();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.bottom() > viewBottom && viewBottom < mContents.height())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navTop = navBounds.y();
+ int scrollBottom;
+ if (testTop < navTop && navTop < (scrollBottom = mScrolledBounds.bottom())) {
+ 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.bottom());
+ setupScrolledBounds();
+ mScrolledBounds.setX(mScrolledBounds.x() - mMaxXScroll);
+ mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
+ int testRight = mScrolledBounds.right();
+ 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.right();
+ 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.right(),
+ 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.bottom(), 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.right() < newBounds.right())
+ offsetX = mViewBounds.right() - mHistory->mNavBounds.right();
+ if (mViewBounds.y() > newBounds.y())
+ offsetY = mViewBounds.y() - mHistory->mNavBounds.y();
+ else if (mViewBounds.bottom() < newBounds.bottom())
+ offsetY = mViewBounds.bottom() - mHistory->mNavBounds.bottom();
+ mHistory->mNavBounds.move(offsetX, offsetY);
+ }
+ mHistory->setDidFirstLayout(false);
+}
+
+bool CachedRoot::innerRight(const CachedNode* test, BestData* bestData) const
+{
+ ASSERT(minWorkingHorizontal() >= mViewBounds.y());
+ ASSERT(maxWorkingHorizontal() <= mViewBounds.bottom());
+ setupScrolledBounds();
+ // (align)
+ mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll);
+ int testLeft = mScrolledBounds.x();
+ int viewRight = mViewBounds.right();
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ if (navBounds.isEmpty() == false &&
+ navBounds.right() > viewRight && viewRight < mContents.width())
+ return false;
+ if (navBounds.isEmpty() == false) {
+ int navLeft = navBounds.x();
+ int scrollRight;
+ if (testLeft < navLeft && navLeft < (scrollRight = mScrolledBounds.right())) {
+ 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.right());
+ setupScrolledBounds();
+ mScrolledBounds.setY(mScrolledBounds.y() - mMaxYScroll);
+ mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll);
+ int testBottom = mScrolledBounds.bottom();
+ 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.bottom();
+ 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.right() <= mViewBounds.right();
+ case UP:
+ *delta = -mMaxYScroll;
+ return newOutset.y() >= mViewBounds.y();
+ case DOWN:
+ *delta = mMaxYScroll;
+ return newOutset.bottom() <= mViewBounds.bottom();
+ 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..1f8b851
--- /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; }
+ const 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.h b/Source/WebKit/android/nav/DrawExtra.h
new file mode 100644
index 0000000..6716a65
--- /dev/null
+++ b/Source/WebKit/android/nav/DrawExtra.h
@@ -0,0 +1,48 @@
+/*
+ * 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 DrawExtra_h
+#define DrawExtra_h
+
+class SkCanvas;
+
+namespace WebCore {
+ class IntRect;
+ class LayerAndroid;
+}
+
+using namespace WebCore;
+
+namespace android {
+
+class DrawExtra {
+public:
+ virtual ~DrawExtra() {}
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ) = 0;
+};
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/FindCanvas.cpp b/Source/WebKit/android/nav/FindCanvas.cpp
new file mode 100644
index 0000000..2d310b3
--- /dev/null
+++ b/Source/WebKit/android/nav/FindCanvas.cpp
@@ -0,0 +1,699 @@
+/*
+ * 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];
+ // FIXME: this should test if the match in the layer is visible
+ if (info.isInLayer())
+ return noBounds;
+ 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();
+}
+
+// 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..76ee1e2
--- /dev/null
+++ b/Source/WebKit/android/nav/FindCanvas.h
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ bool isInLayer() const { return m_layerId >= 0; }
+ int layerId() const { return m_layerId; }
+ void set(const SkRegion& region, SkPicture* pic, int 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() { delete m_matches; }
+ void clearCurrentLocation() { m_hasCurrentLocation = false; }
+ IntRect currentMatchBounds() const;
+ int currentMatchIndex() const { return m_findIndex; }
+ bool currentMatchIsInLayer() const;
+ virtual void draw(SkCanvas* , LayerAndroid* , IntRect* );
+ void findNext(bool forward);
+ bool isCurrentLocationValid() { return m_hasCurrentLocation; }
+ void setMatches(WTF::Vector<MatchInfo>* 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/ParseCanvas.h b/Source/WebKit/android/nav/ParseCanvas.h
new file mode 100644
index 0000000..9b7a889
--- /dev/null
+++ b/Source/WebKit/android/nav/ParseCanvas.h
@@ -0,0 +1,52 @@
+/*
+ * 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 PARSE_CANVAS_H
+#define PARSE_CANVAS_H
+
+#include "SkCanvas.h"
+
+namespace android {
+
+class ParseCanvas : public SkCanvas {
+protected:
+ virtual ~ParseCanvas() {
+ setBounder(0);
+ }
+
+ // Pictures parsed for content never want to create offscreen bitmaps.
+ // Instead, just save and restore the canvas state -- this also keeps
+ // the picture contents at their original coordinates.
+ virtual int saveLayer(const SkRect* , const SkPaint* , SaveFlags flags) {
+ return INHERITED::save(flags);
+ }
+private:
+ typedef SkCanvas INHERITED;
+};
+
+}
+
+#endif
+
diff --git a/Source/WebKit/android/nav/SelectText.cpp b/Source/WebKit/android/nav/SelectText.cpp
new file mode 100644
index 0000000..f8ea799
--- /dev/null
+++ b/Source/WebKit/android/nav/SelectText.cpp
@@ -0,0 +1,1983 @@
+/*
+ * 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 "CachedPrefix.h"
+#include "BidiResolver.h"
+#include "CachedRoot.h"
+#include "LayerAndroid.h"
+#include "ParseCanvas.h"
+#include "SelectText.h"
+#include "SkBitmap.h"
+#include "SkBounder.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
+#include <wtf/text/CString.h>
+#endif
+
+#define VERBOSE_LOGGING 0
+// #define EXTRA_NOISY_LOGGING 1
+
+// TextRunIterator has been copied verbatim from GraphicsContext.cpp
+namespace WebCore {
+
+class TextRunIterator {
+public:
+ TextRunIterator()
+ : m_textRun(0)
+ , m_offset(0)
+ {
+ }
+
+ TextRunIterator(const TextRun* textRun, unsigned offset)
+ : m_textRun(textRun)
+ , m_offset(offset)
+ {
+ }
+
+ TextRunIterator(const TextRunIterator& other)
+ : m_textRun(other.m_textRun)
+ , m_offset(other.m_offset)
+ {
+ }
+
+ unsigned offset() const { return m_offset; }
+ void increment() { m_offset++; }
+ bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
+ UChar current() const { return (*m_textRun)[m_offset]; }
+ WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
+
+ bool operator==(const TextRunIterator& other)
+ {
+ return m_offset == other.m_offset && m_textRun == other.m_textRun;
+ }
+
+ bool operator!=(const TextRunIterator& other) { return !operator==(other); }
+
+private:
+ const TextRun* m_textRun;
+ int m_offset;
+};
+
+// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText()
+void ReverseBidi(UChar* chars, int len) {
+ using namespace WTF::Unicode;
+ WTF::Vector<UChar> result;
+ result.reserveCapacity(len);
+ TextRun run(chars, len);
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
+ bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight,
+ BidiContext::create(0, LeftToRight, false)));
+ bidiResolver.setPosition(TextRunIterator(&run, 0));
+ bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len));
+ if (!bidiResolver.runCount())
+ return;
+ BidiCharacterRun* bidiRun = bidiResolver.firstRun();
+ while (bidiRun) {
+ int bidiStart = bidiRun->start();
+ int bidiStop = bidiRun->stop();
+ int size = result.size();
+ int bidiCount = bidiStop - bidiStart;
+ result.append(chars + bidiStart, bidiCount);
+ if (bidiRun->level() % 2) {
+ UChar* start = &result[size];
+ UChar* end = start + bidiCount;
+ // reverse the order of any RTL substrings
+ while (start < end) {
+ UChar temp = *start;
+ *start++ = *--end;
+ *end = temp;
+ }
+ start = &result[size];
+ end = start + bidiCount - 1;
+ // if the RTL substring had a surrogate pair, restore its order
+ while (start < end) {
+ UChar trail = *start++;
+ if (!U16_IS_SURROGATE(trail))
+ continue;
+ start[-1] = *start; // lead
+ *start++ = trail;
+ }
+ }
+ bidiRun = bidiRun->next();
+ }
+ bidiResolver.deleteRuns();
+ memcpy(chars, &result[0], len * sizeof(UChar));
+}
+
+}
+
+namespace android {
+
+#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
+#define CONTROL_HEIGHT 35
+#define CONTROL_WIDTH 21
+#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
+
+#define SLOP 35
+
+SelectText::SelectText()
+{
+ 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::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
+ canvas->save();
+ canvas->translate(m_selStart.fLeft, m_selStart.fBottom);
+ canvas->drawPicture(m_startControl);
+ canvas->restore();
+ canvas->save();
+ canvas->translate(m_selEnd.fRight, m_selEnd.fBottom);
+ canvas->drawPicture(m_endControl);
+ canvas->restore();
+ 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, cy);
+ test.inset(-SLOP, -SLOP);
+ return test.contains(x, y);
+}
+
+bool SelectText::hitSelection(int x, int y) const
+{
+ x -= m_startOffset.fX;
+ y -= m_startOffset.fY;
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(left, top, x, y))
+ return true;
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ if (hitCorner(right, bottom, x, y))
+ return true;
+ return m_selRegion.contains(x, y);
+}
+
+void SelectText::moveSelection(const 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;
+ }
+ int left = m_selStart.fLeft - CONTROL_WIDTH / 2;
+ int top = m_selStart.fBottom + CONTROL_HEIGHT / 2;
+ m_hitTopLeft = hitCorner(left, top, x, y);
+ int right = m_selEnd.fRight + CONTROL_WIDTH / 2;
+ int bottom = m_selEnd.fBottom + CONTROL_HEIGHT / 2;
+ bool hitBottomRight = hitCorner(right, bottom, x, y);
+ DBG_NAV_LOGD("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 && (!hitBottomRight || y - top < bottom - y)) {
+ 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;
+}
+
+/* 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
new file mode 100644
index 0000000..42239cf
--- /dev/null
+++ b/Source/WebKit/android/nav/SelectText.h
@@ -0,0 +1,116 @@
+/*
+ * 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 SELECT_TEXT_H
+#define SELECT_TEXT_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 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);
+public:
+ float m_inverseScale; // inverse scale, x, y used for drawing select path
+ int m_selectX;
+ int m_selectY;
+private:
+ 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;
+ 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;
+};
+
+}
+
+namespace WebCore {
+
+void ReverseBidi(UChar* chars, int len);
+
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp
new file mode 100644
index 0000000..ff5d73d
--- /dev/null
+++ b/Source/WebKit/android/nav/WebView.cpp
@@ -0,0 +1,2673 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "webviewglue"
+
+#include "config.h"
+
+#include "AndroidAnimation.h"
+#include "AndroidLog.h"
+#include "BaseLayerAndroid.h"
+#include "CachedFrame.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "DrawExtra.h"
+#include "FindCanvas.h"
+#include "Frame.h"
+#include "GraphicsJNI.h"
+#include "HTMLInputElement.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "LayerAndroid.h"
+#include "Node.h"
+#include "utils/Functor.h"
+#include "private/hwui/DrawGlInfo.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "ScrollableLayerAndroid.h"
+#include "SelectText.h"
+#include "SkCanvas.h"
+#include "SkDumpCanvas.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkTime.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "TilesManager.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "WebViewCore.h"
+#include "android_graphics.h"
+
+#ifdef GET_NATIVE_VIEW
+#undef GET_NATIVE_VIEW
+#endif
+
+#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField))
+
+#include <JNIUtility.h>
+#include <JNIHelp.h>
+#include <jni.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <ui/KeycodeLabels.h>
+#include <utils/AssetManager.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+static jfieldID gWebViewField;
+
+//-------------------------------------
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ LOG_ASSERT(m, "Could not find method %s", name);
+ return m;
+}
+
+//-------------------------------------
+// This class provides JNI for making calls into native code from the UI side
+// of the multi-threaded WebView.
+class WebView
+{
+public:
+enum FrameCachePermission {
+ DontAllowNewer,
+ AllowNewer,
+ AllowNewest
+};
+
+enum DrawExtras { // keep this in sync with WebView.java
+ DrawExtrasNone = 0,
+ DrawExtrasFind = 1,
+ DrawExtrasSelection = 2,
+ DrawExtrasCursorRing = 3
+};
+
+struct JavaGlue {
+ jweak m_obj;
+ jmethodID m_calcOurContentVisibleRectF;
+ 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_getVisibleRect;
+ jmethodID m_rebuildWebTextView;
+ jmethodID m_viewInvalidate;
+ jmethodID m_viewInvalidateRect;
+ jmethodID m_postInvalidateDelayed;
+ jmethodID m_inFullScreenMode;
+ jfieldID m_rectLeft;
+ jfieldID m_rectTop;
+ jmethodID m_rectWidth;
+ jmethodID m_rectHeight;
+ jfieldID m_rectFLeft;
+ jfieldID m_rectFTop;
+ jmethodID m_rectFWidth;
+ jmethodID m_rectFHeight;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+} m_javaGlue;
+
+WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, AssetManager* am) :
+ m_ring((WebViewCore*) viewImpl)
+{
+ 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_calcOurContentVisibleRectF = GetJMethod(env, clazz, "calcOurContentVisibleRectF", "(Landroid/graphics/RectF;)V");
+ 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_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_inFullScreenMode = GetJMethod(env, clazz, "inFullScreenMode", "()Z");
+ env->DeleteLocalRef(clazz);
+
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ 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 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;
+ if (drawableDir.isEmpty())
+ m_buttonSkin = 0;
+ else
+ m_buttonSkin = new RenderSkinButton(am, drawableDir);
+#if USE(ACCELERATED_COMPOSITING)
+ m_glWebViewState = 0;
+#endif
+}
+
+~WebView()
+{
+ if (m_javaGlue.m_obj)
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_javaGlue.m_obj);
+ m_javaGlue.m_obj = 0;
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ // We must remove the m_glWebViewState prior to deleting m_baseLayer. If we
+ // do not remove it here, we risk having BaseTiles trying to paint using a
+ // deallocated base layer.
+ stopGL();
+#endif
+ delete m_frameCacheUI;
+ delete m_navPictureUI;
+ SkSafeUnref(m_baseLayer);
+ delete m_glDrawFunctor;
+ delete m_buttonSkin;
+}
+
+void stopGL()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ delete m_glWebViewState;
+ m_glWebViewState = 0;
+#endif
+}
+
+WebViewCore* getWebViewCore() const {
+ return m_viewImpl;
+}
+
+// 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);
+}
+
+void hideCursor(CachedRoot* root)
+{
+ DBG_NAV_LOG("inner");
+ m_viewImpl->m_hasCursorBounds = false;
+ root->hideCursor();
+ viewInvalidate();
+}
+
+#if DUMP_NAV_CACHE
+void debugDump()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root)
+ root->mDebug.print();
+}
+#endif
+
+// Traverse our stored array of buttons that are in our picture, and update
+// their subpictures according to their current state.
+// Called from the UI thread. This is the one place in the UI thread where we
+// access the buttons stored in the WebCore thread.
+// hasFocus keeps track of whether the WebView has focus && windowFocus.
+// If not, we do not want to draw the button in a selected or pressed state
+void nativeRecordButtons(bool hasFocus, bool pressed, bool invalidate)
+{
+ bool cursorIsOnButton = false;
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedCursor = 0;
+ // Lock the mutex, since we now share with the WebCore thread.
+ m_viewImpl->gButtonMutex.lock();
+ if (m_viewImpl->m_buttons.size() && m_buttonSkin) {
+ // FIXME: In a future change, we should keep track of whether the selection
+ // has changed to short circuit (note that we would still need to update
+ // if we received new buttons from the WebCore thread).
+ WebCore::Node* cursor = 0;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ cachedCursor = root->currentCursor(&cachedFrame);
+ if (cachedCursor)
+ cursor = (WebCore::Node*) cachedCursor->nodePointer();
+ }
+
+ // Traverse the array, and update each button, depending on whether it
+ // is selected.
+ Container* end = m_viewImpl->m_buttons.end();
+ for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
+ RenderSkinAndroid::State state = RenderSkinAndroid::kNormal;
+ if (ptr->matches(cursor)) {
+ cursorIsOnButton = true;
+ // If the WebView is out of focus/window focus, set the state to
+ // normal, but still keep track of the fact that the selected is a
+ // button
+ if (hasFocus) {
+ if (pressed || m_ring.m_isPressed)
+ state = RenderSkinAndroid::kPressed;
+ else if (SkTime::GetMSecs() < m_ringAnimationEnd)
+ state = RenderSkinAndroid::kFocused;
+ }
+ }
+ ptr->updateFocusState(state, m_buttonSkin);
+ }
+ }
+ m_viewImpl->gButtonMutex.unlock();
+ if (invalidate && cachedCursor && cursorIsOnButton) {
+ const WebCore::IntRect& b = cachedCursor->bounds(cachedFrame);
+ viewInvalidateRect(b.x(), b.y(), b.right(), b.bottom());
+ }
+}
+
+// The caller has already determined that the desired document rect corresponds
+// to the main picture, and not a layer
+void scrollRectOnScreen(const IntRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ int dx = 0;
+ int left = rect.x();
+ int right = rect.right();
+ if (left < visible.fLeft) {
+ dx = left - visible.fLeft;
+ // Only scroll right if the entire width can fit on screen.
+ } else if (right > visible.fRight && right - left < visible.width()) {
+ dx = right - visible.fRight;
+ }
+ int dy = 0;
+ int top = rect.y();
+ int bottom = rect.bottom();
+ if (top < visible.fTop) {
+ dy = top - visible.fTop;
+ // Only scroll down if the entire height can fit on screen
+ } else if (bottom > visible.fBottom && bottom - top < visible.height()) {
+ dy = bottom - visible.fBottom;
+ }
+ if ((dx|dy) == 0 || !scrollBy(dx, dy))
+ return;
+ viewInvalidate();
+}
+
+void calcOurContentVisibleRect(SkRect* r)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass rectClass = env->FindClass("android/graphics/RectF");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(FFFF)V");
+ jobject jRect = env->NewObject(rectClass, init, 0, 0, 0, 0);
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_calcOurContentVisibleRectF, jRect);
+ r->fLeft = env->GetFloatField(jRect, m_javaGlue.m_rectFLeft);
+ r->fTop = env->GetFloatField(jRect, m_javaGlue.m_rectFTop);
+ r->fRight = r->fLeft + env->CallFloatMethod(jRect, m_javaGlue.m_rectFWidth);
+ r->fBottom = r->fTop + env->CallFloatMethod(jRect, m_javaGlue.m_rectFHeight);
+ env->DeleteLocalRef(rectClass);
+ env->DeleteLocalRef(jRect);
+ checkException(env);
+}
+
+void resetCursorRing()
+{
+ m_ringAnimationEnd = 0;
+ m_viewImpl->m_hasCursorBounds = false;
+}
+
+bool drawCursorPreamble(CachedRoot* root)
+{
+ 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 (node->isInLayer() && root->rootLayer()) {
+ LayerAndroid* layer = const_cast<LayerAndroid*>(root->rootLayer());
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ layer->updateFixedLayersPositions(visible);
+ 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;
+}
+
+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) {
+ m_glWebViewState = new GLWebViewState(&m_viewImpl->gButtonMutex);
+ 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, false);
+ }
+ }
+
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ return false;
+ }
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ if (!m_ring.m_isButton)
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+
+ unsigned int pic = m_glWebViewState->currentPictureCounter();
+
+ SkPicture picture;
+ IntRect rect(0, 0, 0, 0);
+ bool allowSame = false;
+ m_glWebViewState->resetRings();
+ if (extra) {
+ if (extra == &m_ring) {
+ m_glWebViewState->setRings(m_ring.rings(), m_ring.m_isPressed);
+ } else {
+ LayerAndroid mainPicture(m_navPictureUI);
+ PictureSet* content = m_baseLayer->content();
+ SkCanvas* canvas = picture.beginRecording(content->width(),
+ content->height());
+ extra->draw(canvas, &mainPicture, &rect);
+ picture.endRecording();
+ }
+ } else if (extras == DrawExtrasCursorRing && m_ring.m_isButton) {
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedCursor = root->currentCursor(&cachedFrame);
+ if (cachedCursor) {
+ rect = cachedCursor->bounds(cachedFrame);
+ allowSame = true;
+ }
+ }
+ m_glWebViewState->setExtra(m_baseLayer, picture, rect, allowSame);
+
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (compositeLayer)
+ compositeLayer->setExtra(extra);
+
+ SkRect visibleRect;
+ calcOurContentVisibleRect(&visibleRect);
+ bool ret = m_glWebViewState->drawGL(viewRect, visibleRect, invalRect,
+ webViewRect, titleBarHeight, clip, scale);
+ if (ret || m_glWebViewState->currentPictureCounter() != pic)
+ return true;
+#endif
+ return false;
+}
+
+PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
+{
+ PictureSet* ret = 0;
+ if (!m_baseLayer) {
+ canvas->drawColor(bgColor);
+ return ret;
+ }
+
+ // draw the content of the base layer first
+ PictureSet* content = m_baseLayer->content();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
+ content->height()), SkRegion::kDifference_Op);
+ canvas->drawColor(bgColor);
+ canvas->restoreToCount(sc);
+ if (content->draw(canvas))
+ ret = split ? new PictureSet(*content) : 0;
+
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ return ret;
+ }
+ LayerAndroid mainPicture(m_navPictureUI);
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ if (!m_ring.m_isButton)
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+ if (extra) {
+ IntRect dummy; // inval area, unused for now
+ extra->draw(canvas, &mainPicture, &dummy);
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (!compositeLayer)
+ return ret;
+ compositeLayer->setExtra(extra);
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ // call this to be sure we've adjusted for any scrolling or animations
+ // before we actually draw
+ compositeLayer->updateFixedLayersPositions(visible);
+ compositeLayer->updatePositions();
+ // We have to set the canvas' matrix on the base layer
+ // (to have fixed layers work as intended)
+ SkAutoCanvasRestore restore(canvas, true);
+ m_baseLayer->setMatrix(canvas->getTotalMatrix());
+ canvas->resetMatrix();
+ m_baseLayer->draw(canvas);
+#endif
+ return ret;
+}
+
+
+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.right() - newBounds.right()) > 4)
+ return;
+ if (abs(bounds.bottom() - newBounds.bottom()) > 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) {
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ LayerAndroid* layer = const_cast<LayerAndroid*>(
+ m_frameCacheUI->rootLayer());
+ if (layer) {
+ layer->updateFixedLayersPositions(visible);
+ 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();
+ env->CallVoidMethod(m_javaGlue.object(env).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()
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxXScroll);
+ checkException(env);
+ return result;
+}
+
+int getScaledMaxYScroll()
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxYScroll);
+ checkException(env);
+ return result;
+}
+
+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();
+ jobject jRect = env->CallObjectMethod(m_javaGlue.object(env).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.bottom() + dy > docHeight)
+ dy = docHeight - visibleRect.bottom();
+ else if (visibleRect.y() + dy < 0)
+ dy = -visibleRect.y();
+ if (visibleRect.right() + dx > docWidth)
+ dx = docWidth - visibleRect.right();
+ 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) {
+ 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);
+ if (result) {
+ foundBounds->offset(bounds.fLeft, bounds.fTop);
+ if (parent->masksToBounds()) {
+ if (bounds.width() < foundBounds->width())
+ foundBounds->fRight = foundBounds->fLeft + bounds.width();
+ if (bounds.height() < foundBounds->height())
+ foundBounds->fBottom = foundBounds->fTop + bounds.height();
+ }
+ return result;
+ }
+ }
+ if (parent->contentIsScrollable()) {
+ foundBounds->set(0, 0, bounds.width(), bounds.height());
+ return static_cast<const ScrollableLayerAndroid*>(parent);
+ }
+ return 0;
+}
+#endif
+
+int scrollableLayer(int x, int y, SkIRect* layerRect, SkIRect* bounds)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ const LayerAndroid* layerRoot = compositeRoot();
+ if (!layerRoot)
+ return 0;
+ const ScrollableLayerAndroid* result = findScrollableLayer(layerRoot, x, y,
+ bounds);
+ if (result) {
+ result->getScrollRect(layerRect);
+ return result->uniqueId();
+ }
+#endif
+ return 0;
+}
+
+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();
+ jstring jName = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue.object(env).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() + 500;
+ viewInvalidate();
+}
+
+void showCursorUntimed()
+{
+ DBG_NAV_LOG("");
+ m_ring.m_isPressed = false;
+ m_ringAnimationEnd = UINT_MAX;
+ viewInvalidate();
+}
+
+void setHeightCanMeasure(bool measure)
+{
+ m_heightCanMeasure = measure;
+}
+
+String getSelection()
+{
+ return m_selectText.getSelection();
+}
+
+void moveSelection(int x, int y)
+{
+ m_selectText.moveSelection(getVisibleRect(), x, y);
+}
+
+IntPoint selectableText()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return IntPoint(0, 0);
+ return m_selectText.selectableText(root);
+}
+
+void selectAll()
+{
+ m_selectText.selectAll();
+}
+
+int selectionX()
+{
+ return m_selectText.selectionX();
+}
+
+int selectionY()
+{
+ return m_selectText.selectionY();
+}
+
+void resetSelection()
+{
+ m_selectText.reset();
+}
+
+bool startSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ return m_selectText.startSelection(root, getVisibleRect(), x, y);
+}
+
+bool wordSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ 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)
+{
+ 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();
+ env->CallVoidMethod(m_javaGlue.object(env).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();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMoveMouse,
+ (jint) framePtr, (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();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_sendMoveMouseIfLatest, clearTextEntry, stopPaintingCaret);
+ checkException(env);
+}
+
+void sendMotionUp(
+ WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
+{
+ m_viewImpl->m_touchGeneration = ++m_generation;
+ 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();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMotionUp,
+ m_generation, (jint) framePtr, (jint) nodePtr, x, y);
+ checkException(env);
+}
+
+void findNext(bool forward)
+{
+ m_findOnPage.findNext(forward);
+ if (!m_findOnPage.currentMatchIsInLayer())
+ scrollRectOnScreen(m_findOnPage.currentMatchBounds());
+ 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;
+ if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) {
+ oldLocation = m_findOnPage.currentMatchBounds();
+ checkAgainstOldLocation = true;
+ } else
+ checkAgainstOldLocation = false;
+
+ m_findOnPage.setMatches(matches);
+
+ if (!checkAgainstOldLocation
+ || oldLocation != m_findOnPage.currentMatchBounds()) {
+ // FIXME: Need to scroll if the match is in a layer.
+ if (!m_findOnPage.currentMatchIsInLayer())
+ scrollRectOnScreen(m_findOnPage.currentMatchBounds());
+ }
+ viewInvalidate();
+}
+
+int currentMatchIndex()
+{
+ return m_findOnPage.currentMatchIndex();
+}
+
+bool scrollBy(int dx, int dy)
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ bool result = env->CallBooleanMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_scrollBy, dx, dy, true);
+ checkException(env);
+ return result;
+}
+
+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();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_rebuildWebTextView);
+ checkException(env);
+}
+
+void viewInvalidate()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidate);
+ checkException(env);
+}
+
+void viewInvalidateRect(int l, int t, int r, int b)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidateRect, l, r, t, b);
+ checkException(env);
+}
+
+void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_postInvalidateDelayed,
+ delay, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
+ checkException(env);
+}
+
+bool inFullScreenMode()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(m_javaGlue.object(env).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 copyScrollPositionRecursive(const LayerAndroid* from,
+ LayerAndroid* root)
+{
+ if (!from || !root)
+ return;
+ 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
+
+void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndicator,
+ bool isPictureAfterFirstLayout)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (m_glWebViewState)
+ m_glWebViewState->setBaseLayer(layer, inval, showVisualIndicator,
+ isPictureAfterFirstLayout);
+#endif
+
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ if (layer) {
+ LayerAndroid* newCompositeRoot = static_cast<LayerAndroid*>(layer->getChild(0));
+ copyScrollPositionRecursive(compositeRoot(), newCompositeRoot);
+ }
+#endif
+ SkSafeUnref(m_baseLayer);
+ m_baseLayer = layer;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ root->resetLayers();
+ root->setRootLayer(compositeRoot());
+}
+
+void replaceBaseContent(PictureSet* set)
+{
+ if (!m_baseLayer)
+ return;
+ m_baseLayer->setContent(*set);
+ delete set;
+}
+
+void copyBaseContentToPicture(SkPicture* picture)
+{
+ if (!m_baseLayer)
+ return;
+ PictureSet* content = m_baseLayer->content();
+ m_baseLayer->drawCanvas(picture->beginRecording(content->width(), content->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag));
+ picture->endRecording();
+}
+
+bool hasContent() {
+ if (!m_baseLayer)
+ return false;
+ return !m_baseLayer->content()->isEmpty();
+}
+
+void setFunctor(Functor* functor) {
+ delete m_glDrawFunctor;
+ m_glDrawFunctor = functor;
+}
+
+Functor* getFunctor() {
+ return m_glDrawFunctor;
+}
+
+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;
+ SelectText m_selectText;
+ FindOnPage m_findOnPage;
+ CursorRing m_ring;
+ BaseLayerAndroid* m_baseLayer;
+ Functor* m_glDrawFunctor;
+#if USE(ACCELERATED_COMPOSITING)
+ GLWebViewState* m_glWebViewState;
+#endif
+ const RenderSkinButton* m_buttonSkin;
+}; // end of WebView class
+
+
+/**
+ * This class holds a function pointer and parameters for calling drawGL into a specific
+ * viewport. The pointer to the Functor will be put on a framework display list to be called
+ * when the display list is replayed.
+ */
+class GLDrawFunctor : Functor {
+ public:
+ GLDrawFunctor(WebView* _wvInstance,
+ 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;
+ viewRect = _viewRect;
+ scale = _scale;
+ extras = _extras;
+ };
+ status_t operator()(int messageId, void* data) {
+ if (viewRect.isEmpty()) {
+ // NOOP operation if viewport is empty
+ return 0;
+ }
+
+ WebCore::IntRect inval;
+ int titlebarHeight = webViewRect.height() - viewRect.height();
+
+ uirenderer::DrawGlInfo* info = reinterpret_cast<uirenderer::DrawGlInfo*>(data);
+ 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);
+
+ bool retVal = (*wvInstance.*funcPtr)(localViewRect, &inval, webViewRect, titlebarHeight, clip, scale, extras);
+ if (retVal) {
+ IntRect finalInval;
+ 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());
+ }
+ info->dirtyLeft = finalInval.x();
+ info->dirtyTop = finalInval.y();
+ info->dirtyRight = finalInval.right();
+ info->dirtyBottom = finalInval.bottom();
+ }
+ // return 1 if invalidation needed, 0 otherwise
+ return retVal ? 1 : 0;
+ }
+ void updateRect(WebCore::IntRect& _viewRect) {
+ viewRect = _viewRect;
+ }
+ void updateViewRect(WebCore::IntRect& _viewRect) {
+ webViewRect = _viewRect;
+ }
+ private:
+ WebView* wvInstance;
+ bool (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*, WebCore::IntRect&, int, WebCore::IntRect&, float, int);
+ WebCore::IntRect viewRect;
+ WebCore::IntRect webViewRect;
+ jfloat scale;
+ jint extras;
+};
+
+/*
+ * 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();
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+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,
+ jobject jAssetManager)
+{
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ WTF::String dir = jstringToWtfString(env, drawableDir);
+ WebView* webview = new WebView(env, obj, viewImpl, dir, am);
+ // 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);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+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)
+{
+ 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 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, jint color,
+ jint extras, jboolean split) {
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split));
+}
+
+static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect, jobject jviewrect,
+ jfloat scale, jint extras) {
+ WebCore::IntRect viewRect;
+ if (jrect == NULL) {
+ viewRect = WebCore::IntRect();
+ } else {
+ viewRect = jrect_to_webrect(env, jrect);
+ }
+ WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
+ GLDrawFunctor* functor = new GLDrawFunctor(wvInstance, &android::WebView::drawGL,
+ viewRect, scale, extras);
+ wvInstance->setFunctor((Functor*) functor);
+
+ WebCore::IntRect webViewRect;
+ if (jviewrect == NULL) {
+ webViewRect = WebCore::IntRect();
+ } else {
+ webViewRect = jrect_to_webrect(env, jviewrect);
+ }
+ functor->updateViewRect(webViewRect);
+
+ return (jint)functor;
+}
+
+static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect, jobject jviewrect) {
+ WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
+ if (wvInstance != NULL) {
+ GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor();
+ if (functor != NULL) {
+ WebCore::IntRect viewRect;
+ if (jrect == NULL) {
+ viewRect = WebCore::IntRect();
+ } else {
+ viewRect = jrect_to_webrect(env, jrect);
+ }
+ functor->updateRect(viewRect);
+
+ WebCore::IntRect webViewRect;
+ if (jviewrect == NULL) {
+ webViewRect = WebCore::IntRect();
+ } else {
+ webViewRect = jrect_to_webrect(env, jviewrect);
+ }
+ functor->updateViewRect(webViewRect);
+ }
+ }
+}
+
+static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
+ if (root)
+ return root->evaluateAnimations();
+#endif
+ return false;
+}
+
+static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer, jobject inval,
+ jboolean showVisualIndicator,
+ jboolean isPictureAfterFirstLayout)
+{
+ BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
+ SkRegion invalRegion;
+ if (inval)
+ invalRegion = *GraphicsJNI::getNativeRegion(env, inval);
+ GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, invalRegion, showVisualIndicator,
+ isPictureAfterFirstLayout);
+}
+
+static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
+{
+ PictureSet* set = reinterpret_cast<PictureSet*>(content);
+ GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
+}
+
+static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
+}
+
+static bool nativeHasContent(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasContent();
+}
+
+static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ 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 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;
+}
+
+static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getFocusCandidate(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ return createJavaRect(env, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
+}
+
+static jobject nativeFocusCandidatePaddingRect(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return 0;
+ // Note that the Java Rect is being used to pass four integers, rather than
+ // being used as an actual rectangle.
+ return createJavaRect(env, input->paddingLeft(), input->paddingTop(),
+ input->paddingRight(), input->paddingBottom());
+}
+
+static jint nativeFocusCandidatePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ return reinterpret_cast<int>(node ? node->nodePointer() : 0);
+}
+
+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 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);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+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 nativeRecordButtons(JNIEnv* env, jobject obj, bool hasFocus,
+ bool pressed, bool invalidate)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->nativeRecordButtons(hasFocus, pressed, invalidate);
+}
+
+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);
+ LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
+ view->setHeightCanMeasure(measure);
+}
+
+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.right(), webRect.bottom());
+ 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 = 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)
+{
+ 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);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ String selection = view->getSelection();
+ return wtfStringToJstring(env, selection);
+}
+
+static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
+}
+
+static jint nativeSelectionX(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionX();
+}
+
+static jint nativeSelectionY(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionY();
+}
+
+static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
+ jfloat scale, jint x, jint y)
+{
+ GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
+}
+
+#ifdef ANDROID_DUMP_DISPLAY_TREE
+static void dumpToFile(const char text[], void* file) {
+ fwrite(text, 1, strlen(text), reinterpret_cast<FILE*>(file));
+ fwrite("\n", 1, 1, reinterpret_cast<FILE*>(file));
+}
+#endif
+
+static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
+{
+#ifdef ANDROID_DUMP_DISPLAY_TREE
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+
+ if (view && view->getWebViewCore()) {
+ FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w");
+ if (file) {
+ SkFormatDumper dumper(dumpToFile, file);
+ // dump the URL
+ if (jurl) {
+ const char* str = env->GetStringUTFChars(jurl, 0);
+ SkDebugf("Dumping %s to %s\n", str, DISPLAY_TREE_LOG_FILE);
+ dumpToFile(str, file);
+ env->ReleaseStringUTFChars(jurl, str);
+ }
+ // now dump the display tree
+ SkDumpCanvas canvas(&dumper);
+ // this will playback the picture into the canvas, which will
+ // spew its contents to the dumper
+ view->draw(&canvas, 0, 0, false);
+ // we're done with the file now
+ fwrite("\n", 1, 1, file);
+ fclose(file);
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ const LayerAndroid* rootLayer = view->compositeRoot();
+ if (rootLayer) {
+ FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
+ if (file) {
+ rootLayer->dumpLayers(file, 0);
+ fclose(file);
+ }
+ }
+#endif
+ }
+#endif
+}
+
+static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y,
+ jobject rect, jobject bounds)
+{
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ SkIRect nativeRect, nativeBounds;
+ int id = view->scrollableLayer(x, y, &nativeRect, &nativeBounds);
+ if (rect)
+ GraphicsJNI::irect_to_jrect(nativeRect, env, rect);
+ if (bounds)
+ GraphicsJNI::irect_to_jrect(nativeBounds, env, bounds);
+ return id;
+}
+
+static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint layerId, jint x,
+ jint y)
+{
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LayerAndroid* root = view->compositeRoot();
+ if (!root)
+ return false;
+ LayerAndroid* layer = root->findById(layerId);
+ if (!layer || !layer->contentIsScrollable())
+ return false;
+ return static_cast<ScrollableLayerAndroid*>(layer)->scrollTo(x, y);
+#endif
+ return false;
+}
+
+static void nativeSetExpandedTileBounds(JNIEnv*, jobject, jboolean enabled)
+{
+ TilesManager::instance()->setExpandedTileBounds(enabled);
+}
+
+/*
+ * 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;Landroid/content/res/AssetManager;)V",
+ (void*) nativeCreate },
+ { "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;IIZ)I",
+ (void*) nativeDraw },
+ { "nativeGetDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;FI)I",
+ (void*) nativeGetDrawGLFunction },
+ { "nativeUpdateDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;)V",
+ (void*) nativeUpdateDrawGLFunction },
+ { "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
+ (void*) nativeDumpDisplayTree },
+ { "nativeEvaluateLayersAnimations", "()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 },
+ { "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 },
+ { "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 },
+ { "nativeRecordButtons", "(ZZZ)V",
+ (void*) nativeRecordButtons },
+ { "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", "(ILandroid/graphics/Region;ZZ)V",
+ (void*) nativeSetBaseLayer },
+ { "nativeReplaceBaseContent", "(I)V",
+ (void*) nativeReplaceBaseContent },
+ { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) nativeCopyBaseContentToPicture },
+ { "nativeHasContent", "()Z",
+ (void*) nativeHasContent },
+ { "nativeSetSelectionPointer", "(ZFII)V",
+ (void*) nativeSetSelectionPointer },
+ { "nativeShowCursorTimed", "()V",
+ (void*) nativeShowCursorTimed },
+ { "nativeStartSelection", "(II)Z",
+ (void*) nativeStartSelection },
+ { "nativeStopGL", "()V",
+ (void*) nativeStopGL },
+ { "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", "(III)Z",
+ (void*) nativeScrollLayer },
+ { "nativeSetExpandedTileBounds", "(Z)V",
+ (void*) nativeSetExpandedTileBounds },
+};
+
+int registerWebView(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebView");
+ gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I");
+ LOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebView.mNativeClass");
+ env->DeleteLocalRef(clazz);
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/plugins/ANPBitmapInterface.cpp b/Source/WebKit/android/plugins/ANPBitmapInterface.cpp
new file mode 100644
index 0000000..4c6ad7c
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPBitmapInterface.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include "SkColorPriv.h"
+
+static bool anp_getPixelPacking(ANPBitmapFormat fmt, ANPPixelPacking* packing) {
+ switch (fmt) {
+ case kRGBA_8888_ANPBitmapFormat:
+ if (packing) {
+ packing->AShift = SK_A32_SHIFT;
+ packing->ABits = SK_A32_BITS;
+ packing->RShift = SK_R32_SHIFT;
+ packing->RBits = SK_R32_BITS;
+ packing->GShift = SK_G32_SHIFT;
+ packing->GBits = SK_G32_BITS;
+ packing->BShift = SK_B32_SHIFT;
+ packing->BBits = SK_B32_BITS;
+ }
+ return true;
+ case kRGB_565_ANPBitmapFormat:
+ if (packing) {
+ packing->AShift = 0;
+ packing->ABits = 0;
+ packing->RShift = SK_R16_SHIFT;
+ packing->RBits = SK_R16_BITS;
+ packing->GShift = SK_G16_SHIFT;
+ packing->GBits = SK_G16_BITS;
+ packing->BShift = SK_B16_SHIFT;
+ packing->BBits = SK_B16_BITS;
+ }
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPBitmapInterfaceV0_Init(ANPInterface* value) {
+ ANPBitmapInterfaceV0* i = reinterpret_cast<ANPBitmapInterfaceV0*>(value);
+
+ ASSIGN(i, getPixelPacking);
+}
diff --git a/Source/WebKit/android/plugins/ANPCanvasInterface.cpp b/Source/WebKit/android/plugins/ANPCanvasInterface.cpp
new file mode 100644
index 0000000..d6d89ff
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPCanvasInterface.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+
+static ANPCanvas* anp_newCanvas(const ANPBitmap* bitmap) {
+ SkBitmap bm;
+ return new ANPCanvas(*SkANP::SetBitmap(&bm, *bitmap));
+}
+
+static void anp_deleteCanvas(ANPCanvas* canvas) {
+ delete canvas;
+}
+
+static void anp_save(ANPCanvas* canvas) {
+ canvas->skcanvas->save();
+}
+
+static void anp_restore(ANPCanvas* canvas) {
+ canvas->skcanvas->restore();
+}
+
+static void anp_translate(ANPCanvas* canvas, float tx, float ty) {
+ canvas->skcanvas->translate(SkFloatToScalar(tx), SkFloatToScalar(ty));
+}
+
+static void anp_scale(ANPCanvas* canvas, float sx, float sy) {
+ canvas->skcanvas->scale(SkFloatToScalar(sx), SkFloatToScalar(sy));
+}
+
+static void anp_rotate(ANPCanvas* canvas, float degrees) {
+ canvas->skcanvas->rotate(SkFloatToScalar(degrees));
+}
+
+static void anp_skew(ANPCanvas* canvas, float kx, float ky) {
+ canvas->skcanvas->skew(SkFloatToScalar(kx), SkFloatToScalar(ky));
+}
+
+static void anp_clipRect(ANPCanvas* canvas, const ANPRectF* rect) {
+ SkRect r;
+ canvas->skcanvas->clipRect(*SkANP::SetRect(&r, *rect));
+}
+
+static void anp_clipPath(ANPCanvas* canvas, const ANPPath* path) {
+ canvas->skcanvas->clipPath(*path);
+}
+static void anp_concat(ANPCanvas* canvas, const ANPMatrix* matrix) {
+ canvas->skcanvas->concat(*matrix);
+}
+
+static void anp_getTotalMatrix(ANPCanvas* canvas, ANPMatrix* matrix) {
+ const SkMatrix& src = canvas->skcanvas->getTotalMatrix();
+ *matrix = *reinterpret_cast<const ANPMatrix*>(&src);
+}
+
+static bool anp_getLocalClipBounds(ANPCanvas* canvas, ANPRectF* r,
+ bool antialias) {
+ SkRect bounds;
+ if (canvas->skcanvas->getClipBounds(&bounds,
+ antialias ? SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
+ SkANP::SetRect(r, bounds);
+ return true;
+ }
+ return false;
+}
+
+static bool anp_getDeviceClipBounds(ANPCanvas* canvas, ANPRectI* r) {
+ const SkRegion& clip = canvas->skcanvas->getTotalClip();
+ if (!clip.isEmpty()) {
+ SkANP::SetRect(r, clip.getBounds());
+ return true;
+ }
+ return false;
+}
+
+static void anp_drawColor(ANPCanvas* canvas, ANPColor color) {
+ canvas->skcanvas->drawColor(color);
+}
+
+static void anp_drawPaint(ANPCanvas* canvas, const ANPPaint* paint) {
+ canvas->skcanvas->drawPaint(*paint);
+}
+
+static void anp_drawLine(ANPCanvas* canvas, float x0, float y0,
+ float x1, float y1, const ANPPaint* paint) {
+ canvas->skcanvas->drawLine(SkFloatToScalar(x0), SkFloatToScalar(y0),
+ SkFloatToScalar(x1), SkFloatToScalar(y1), *paint);
+}
+
+static void anp_drawRect(ANPCanvas* canvas, const ANPRectF* rect,
+ const ANPPaint* paint) {
+ SkRect r;
+ canvas->skcanvas->drawRect(*SkANP::SetRect(&r, *rect), *paint);
+}
+
+static void anp_drawOval(ANPCanvas* canvas, const ANPRectF* rect,
+ const ANPPaint* paint) {
+ SkRect r;
+ canvas->skcanvas->drawOval(*SkANP::SetRect(&r, *rect), *paint);
+}
+
+static void anp_drawPath(ANPCanvas* canvas, const ANPPath* path,
+ const ANPPaint* paint) {
+ canvas->skcanvas->drawPath(*path, *paint);
+}
+
+static void anp_drawText(ANPCanvas* canvas, const void* text, uint32_t length,
+ float x, float y, const ANPPaint* paint) {
+ canvas->skcanvas->drawText(text, length,
+ SkFloatToScalar(x), SkFloatToScalar(y),
+ *paint);
+}
+
+static void anp_drawPosText(ANPCanvas* canvas, const void* text,
+ uint32_t byteLength, const float xy[], const ANPPaint* paint) {
+ canvas->skcanvas->drawPosText(text, byteLength,
+ reinterpret_cast<const SkPoint*>(xy), *paint);
+}
+
+static void anp_drawBitmap(ANPCanvas* canvas, const ANPBitmap* bitmap,
+ float x, float y, const ANPPaint* paint) {
+ SkBitmap bm;
+ canvas->skcanvas->drawBitmap(*SkANP::SetBitmap(&bm, *bitmap),
+ SkFloatToScalar(x), SkFloatToScalar(y),
+ paint);
+}
+
+static void anp_drawBitmapRect(ANPCanvas* canvas, const ANPBitmap* bitmap,
+ const ANPRectI* src, const ANPRectF* dst,
+ const ANPPaint* paint) {
+ SkBitmap bm;
+ SkRect dstR;
+ SkIRect srcR, *srcPtr = NULL;
+
+ if (src) {
+ srcPtr = SkANP::SetRect(&srcR, *src);
+ }
+ canvas->skcanvas->drawBitmapRect(*SkANP::SetBitmap(&bm, *bitmap), srcPtr,
+ *SkANP::SetRect(&dstR, *dst), paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPCanvasInterfaceV0_Init(ANPInterface* value) {
+ ANPCanvasInterfaceV0* i = reinterpret_cast<ANPCanvasInterfaceV0*>(value);
+
+ ASSIGN(i, newCanvas);
+ ASSIGN(i, deleteCanvas);
+ ASSIGN(i, save);
+ ASSIGN(i, restore);
+ ASSIGN(i, translate);
+ ASSIGN(i, scale);
+ ASSIGN(i, rotate);
+ ASSIGN(i, skew);
+ ASSIGN(i, clipRect);
+ ASSIGN(i, clipPath);
+ ASSIGN(i, concat);
+ ASSIGN(i, getTotalMatrix);
+ ASSIGN(i, getLocalClipBounds);
+ ASSIGN(i, getDeviceClipBounds);
+ ASSIGN(i, drawColor);
+ ASSIGN(i, drawPaint);
+ ASSIGN(i, drawLine);
+ ASSIGN(i, drawRect);
+ ASSIGN(i, drawOval);
+ ASSIGN(i, drawPath);
+ ASSIGN(i, drawText);
+ ASSIGN(i, drawPosText);
+ ASSIGN(i, drawBitmap);
+ ASSIGN(i, drawBitmapRect);
+}
diff --git a/Source/WebKit/android/plugins/ANPEventInterface.cpp b/Source/WebKit/android/plugins/ANPEventInterface.cpp
new file mode 100644
index 0000000..2fdf159
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPEventInterface.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include "WebViewCore.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+
+#include "JavaSharedClient.h"
+
+using namespace android;
+
+struct WrappedANPEvent {
+ WebViewCore* fWVC;
+ PluginWidgetAndroid* fPWA;
+ ANPEvent fEvent;
+};
+
+/* Its possible we may be called after the plugin that initiated the event
+ has been torn-down. Thus we check that the assicated webviewcore and
+ pluginwidget are still active before dispatching the event.
+ */
+static void send_anpevent(void* data) {
+ WrappedANPEvent* wrapper = static_cast<WrappedANPEvent*>(data);
+ WebViewCore* core = wrapper->fWVC;
+ PluginWidgetAndroid* widget = wrapper->fPWA;
+
+ // be sure we're still alive before delivering the event
+ if (WebViewCore::isInstance(core) && core->isPlugin(widget)) {
+ widget->sendEvent(wrapper->fEvent);
+ }
+ delete wrapper;
+}
+
+static void anp_postEvent(NPP instance, const ANPEvent* event) {
+ if (instance && instance->ndata && event) {
+ PluginView* pluginView = static_cast<PluginView*>(instance->ndata);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ WebViewCore* wvc = pluginWidget->webViewCore();
+
+ WrappedANPEvent* wrapper = new WrappedANPEvent;
+ // recored these, and recheck that they are valid before delivery
+ // in send_anpevent
+ wrapper->fWVC = pluginWidget->webViewCore();
+ wrapper->fPWA = pluginWidget;
+ // make a copy of the event
+ wrapper->fEvent = *event;
+ JavaSharedClient::EnqueueFunctionPtr(send_anpevent, wrapper);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPEventInterfaceV0_Init(ANPInterface* value) {
+ ANPEventInterfaceV0* i = reinterpret_cast<ANPEventInterfaceV0*>(value);
+
+ ASSIGN(i, postEvent);
+}
diff --git a/Source/WebKit/android/plugins/ANPKeyCodes.h b/Source/WebKit/android/plugins/ANPKeyCodes.h
new file mode 100644
index 0000000..969679f
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPKeyCodes.h
@@ -0,0 +1,229 @@
+/*
+ * 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 ANPKeyCodes_DEFINED
+#define ANPKeyCodes_DEFINED
+
+/* List the key codes that are set to a plugin in the ANPKeyEvent.
+
+ These exactly match the values in android/view/KeyEvent.java and the
+ corresponding .h file android/keycodes.h.
+*/
+enum ANPKeyCodes {
+ kUnknown_ANPKeyCode = 0,
+
+ kSoftLeft_ANPKeyCode = 1,
+ kSoftRight_ANPKeyCode = 2,
+ kHome_ANPKeyCode = 3,
+ kBack_ANPKeyCode = 4,
+ kCall_ANPKeyCode = 5,
+ kEndCall_ANPKeyCode = 6,
+ k0_ANPKeyCode = 7,
+ k1_ANPKeyCode = 8,
+ k2_ANPKeyCode = 9,
+ k3_ANPKeyCode = 10,
+ k4_ANPKeyCode = 11,
+ k5_ANPKeyCode = 12,
+ k6_ANPKeyCode = 13,
+ k7_ANPKeyCode = 14,
+ k8_ANPKeyCode = 15,
+ k9_ANPKeyCode = 16,
+ kStar_ANPKeyCode = 17,
+ kPound_ANPKeyCode = 18,
+ kDpadUp_ANPKeyCode = 19,
+ kDpadDown_ANPKeyCode = 20,
+ kDpadLeft_ANPKeyCode = 21,
+ kDpadRight_ANPKeyCode = 22,
+ kDpadCenter_ANPKeyCode = 23,
+ kVolumeUp_ANPKeyCode = 24,
+ kVolumeDown_ANPKeyCode = 25,
+ kPower_ANPKeyCode = 26,
+ kCamera_ANPKeyCode = 27,
+ kClear_ANPKeyCode = 28,
+ kA_ANPKeyCode = 29,
+ kB_ANPKeyCode = 30,
+ kC_ANPKeyCode = 31,
+ kD_ANPKeyCode = 32,
+ kE_ANPKeyCode = 33,
+ kF_ANPKeyCode = 34,
+ kG_ANPKeyCode = 35,
+ kH_ANPKeyCode = 36,
+ kI_ANPKeyCode = 37,
+ kJ_ANPKeyCode = 38,
+ kK_ANPKeyCode = 39,
+ kL_ANPKeyCode = 40,
+ kM_ANPKeyCode = 41,
+ kN_ANPKeyCode = 42,
+ kO_ANPKeyCode = 43,
+ kP_ANPKeyCode = 44,
+ kQ_ANPKeyCode = 45,
+ kR_ANPKeyCode = 46,
+ kS_ANPKeyCode = 47,
+ kT_ANPKeyCode = 48,
+ kU_ANPKeyCode = 49,
+ kV_ANPKeyCode = 50,
+ kW_ANPKeyCode = 51,
+ kX_ANPKeyCode = 52,
+ kY_ANPKeyCode = 53,
+ kZ_ANPKeyCode = 54,
+ kComma_ANPKeyCode = 55,
+ kPeriod_ANPKeyCode = 56,
+ kAltLeft_ANPKeyCode = 57,
+ kAltRight_ANPKeyCode = 58,
+ kShiftLeft_ANPKeyCode = 59,
+ kShiftRight_ANPKeyCode = 60,
+ kTab_ANPKeyCode = 61,
+ kSpace_ANPKeyCode = 62,
+ kSym_ANPKeyCode = 63,
+ kExplorer_ANPKeyCode = 64,
+ kEnvelope_ANPKeyCode = 65,
+ kNewline_ANPKeyCode = 66,
+ kDel_ANPKeyCode = 67,
+ kGrave_ANPKeyCode = 68,
+ kMinus_ANPKeyCode = 69,
+ kEquals_ANPKeyCode = 70,
+ kLeftBracket_ANPKeyCode = 71,
+ kRightBracket_ANPKeyCode = 72,
+ kBackslash_ANPKeyCode = 73,
+ kSemicolon_ANPKeyCode = 74,
+ kApostrophe_ANPKeyCode = 75,
+ kSlash_ANPKeyCode = 76,
+ kAt_ANPKeyCode = 77,
+ kNum_ANPKeyCode = 78,
+ kHeadSetHook_ANPKeyCode = 79,
+ kFocus_ANPKeyCode = 80,
+ kPlus_ANPKeyCode = 81,
+ kMenu_ANPKeyCode = 82,
+ kNotification_ANPKeyCode = 83,
+ kSearch_ANPKeyCode = 84,
+ kMediaPlayPause_ANPKeyCode = 85,
+ kMediaStop_ANPKeyCode = 86,
+ kMediaNext_ANPKeyCode = 87,
+ kMediaPrevious_ANPKeyCode = 88,
+ kMediaRewind_ANPKeyCode = 89,
+ kMediaFastForward_ANPKeyCode = 90,
+ kMute_ANPKeyCode = 91,
+ kPageUp_ANPKeyCode = 92,
+ kPageDown_ANPKeyCode = 93,
+ kPictsymbols_ANPKeyCode = 94,
+ kSwitchCharset_ANPKeyCode = 95,
+ kButtonA_ANPKeyCode = 96,
+ kButtonB_ANPKeyCode = 97,
+ kButtonC_ANPKeyCode = 98,
+ kButtonX_ANPKeyCode = 99,
+ kButtonY_ANPKeyCode = 100,
+ kButtonZ_ANPKeyCode = 101,
+ kButtonL1_ANPKeyCode = 102,
+ kButtonR1_ANPKeyCode = 103,
+ kButtonL2_ANPKeyCode = 104,
+ kButtonR2_ANPKeyCode = 105,
+ kButtonThumbL_ANPKeyCode = 106,
+ kButtonThumbR_ANPKeyCode = 107,
+ kButtonStart_ANPKeyCode = 108,
+ kButtonSelect_ANPKeyCode = 109,
+ kButtonMode_ANPKeyCode = 110,
+ kEscape_ANPKeyCode = 111,
+ kForwardDel_ANPKeyCode = 112,
+ kCtrlLeft_ANPKeyCode = 113,
+ kCtrlRight_ANPKeyCode = 114,
+ kCapsLock_ANPKeyCode = 115,
+ kScrollLock_ANPKeyCode = 116,
+ kMetaLeft_ANPKeyCode = 117,
+ kMetaRight_ANPKeyCode = 118,
+ kFunction_ANPKeyCode = 119,
+ kSysRq_ANPKeyCode = 120,
+ kBreak_ANPKeyCode = 121,
+ kMoveHome_ANPKeyCode = 122,
+ kMoveEnd_ANPKeyCode = 123,
+ kInsert_ANPKeyCode = 124,
+ kForward_ANPKeyCode = 125,
+ kMediaPlay_ANPKeyCode = 126,
+ kMediaPause_ANPKeyCode = 127,
+ kMediaClose_ANPKeyCode = 128,
+ kMediaEject_ANPKeyCode = 129,
+ kMediaRecord_ANPKeyCode = 130,
+ kF1_ANPKeyCode = 131,
+ kF2_ANPKeyCode = 132,
+ kF3_ANPKeyCode = 133,
+ kF4_ANPKeyCode = 134,
+ kF5_ANPKeyCode = 135,
+ kF6_ANPKeyCode = 136,
+ kF7_ANPKeyCode = 137,
+ kF8_ANPKeyCode = 138,
+ kF9_ANPKeyCode = 139,
+ kF10_ANPKeyCode = 140,
+ kF11_ANPKeyCode = 141,
+ kF12_ANPKeyCode = 142,
+ kNumLock_ANPKeyCode = 143,
+ kNumPad0_ANPKeyCode = 144,
+ kNumPad1_ANPKeyCode = 145,
+ kNumPad2_ANPKeyCode = 146,
+ kNumPad3_ANPKeyCode = 147,
+ kNumPad4_ANPKeyCode = 148,
+ kNumPad5_ANPKeyCode = 149,
+ kNumPad6_ANPKeyCode = 150,
+ kNumPad7_ANPKeyCode = 151,
+ kNumPad8_ANPKeyCode = 152,
+ kNumPad9_ANPKeyCode = 153,
+ kNumPadDivide_ANPKeyCode = 154,
+ kNumPadMultiply_ANPKeyCode = 155,
+ kNumPadSubtract_ANPKeyCode = 156,
+ kNumPadAdd_ANPKeyCode = 157,
+ kNumPadDot_ANPKeyCode = 158,
+ kNumPadComma_ANPKeyCode = 159,
+ kNumPadEnter_ANPKeyCode = 160,
+ kNumPadEquals_ANPKeyCode = 161,
+ kNumPadLeftParen_ANPKeyCode = 162,
+ kNumPadRightParen_ANPKeyCode = 163,
+ kVolumeMute_ANPKeyCode = 164,
+ kInfo_ANPKeyCode = 165,
+ kChannelUp_ANPKeyCode = 166,
+ kChannelDown_ANPKeyCode = 167,
+ kZoomIn_ANPKeyCode = 168,
+ kZoomOut_ANPKeyCode = 169,
+ kTv_ANPKeyCode = 170,
+ kWindow_ANPKeyCode = 171,
+ kGuide_ANPKeyCode = 172,
+ kDvr_ANPKeyCode = 173,
+ kBookmark_ANPKeyCode = 174,
+ kCaptions_ANPKeyCode = 175,
+ kSettings_ANPKeyCode = 176,
+ kTvPower_ANPKeyCode = 177,
+ kTvInput_ANPKeyCode = 178,
+ kStbPower_ANPKeyCode = 179,
+ kStbInput_ANPKeyCode = 180,
+ kAvrPower_ANPKeyCode = 181,
+ kAvrInput_ANPKeyCode = 182,
+ kProgRed_ANPKeyCode = 183,
+ kProgGreen_ANPKeyCode = 184,
+ kProgYellow_ANPKeyCode = 185,
+ kProgBlue_ANPKeyCode = 186,
+ kAppSwitch_ANPKeyCode = 187,
+
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+};
+
+#endif
diff --git a/Source/WebKit/android/plugins/ANPLogInterface.cpp b/Source/WebKit/android/plugins/ANPLogInterface.cpp
new file mode 100644
index 0000000..23a4ed6
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPLogInterface.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "webkitPlugin"
+
+#include "utils/Log.h"
+#include "android_npapi.h"
+#include <stdarg.h>
+
+static void anp_log(ANPLogType logType, const char format[], ...) {
+ va_list args;
+ va_start(args, format);
+
+ android_LogPriority priority;
+ switch (logType) {
+ case kError_ANPLogType:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ case kWarning_ANPLogType:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case kDebug_ANPLogType:
+ priority = ANDROID_LOG_DEBUG;
+ break;
+ default:
+ priority = ANDROID_LOG_UNKNOWN;
+ break;
+ }
+ LOG_PRI_VA(priority, "plugin", format, args);
+
+ va_end(args);
+}
+
+void ANPLogInterfaceV0_Init(ANPInterface* value) {
+ ANPLogInterfaceV0* i = reinterpret_cast<ANPLogInterfaceV0*>(value);
+
+ i->log = anp_log;
+}
diff --git a/Source/WebKit/android/plugins/ANPMatrixInterface.cpp b/Source/WebKit/android/plugins/ANPMatrixInterface.cpp
new file mode 100644
index 0000000..f322315
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPMatrixInterface.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+
+#ifdef SK_SCALAR_IS_FIXED
+static void fromFloat(SkScalar dst[], const float src[], int n) {
+ for (int i = 0; i < n; i++) {
+ dst[i] = SkFloatToScalar(src[i]);
+ }
+}
+
+static void toFloat(float dst[], const SkScalar src[], int n) {
+ for (int i = 0; i < n; i++) {
+ dst[i] = SkScalarToFloat(src[i]);
+ }
+}
+#endif
+
+static ANPMatrix* anp_newMatrix() {
+ return new ANPMatrix;
+}
+
+static void anp_deleteMatrix(ANPMatrix* matrix) {
+ delete matrix;
+}
+
+static ANPMatrixFlag anp_getFlags(const ANPMatrix* matrix) {
+ return matrix->getType();
+}
+
+static void anp_copy(ANPMatrix* dst, const ANPMatrix* src) {
+ *dst = *src;
+}
+
+static void anp_get3x3(const ANPMatrix* matrix, float dst[9]) {
+ for (int i = 0; i < 9; i++) {
+ dst[i] = SkScalarToFloat(matrix->get(i));
+ }
+}
+
+static void anp_set3x3(ANPMatrix* matrix, const float src[9]) {
+ for (int i = 0; i < 9; i++) {
+ matrix->set(i, SkFloatToScalar(src[i]));
+ }
+}
+
+static void anp_setIdentity(ANPMatrix* matrix) {
+ matrix->reset();
+}
+
+static void anp_preTranslate(ANPMatrix* matrix, float tx, float ty) {
+ matrix->preTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
+}
+
+static void anp_postTranslate(ANPMatrix* matrix, float tx, float ty) {
+ matrix->postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
+}
+
+static void anp_preScale(ANPMatrix* matrix, float sx, float sy) {
+ matrix->preScale(SkFloatToScalar(sx), SkFloatToScalar(sy));
+}
+
+static void anp_postScale(ANPMatrix* matrix, float sx, float sy) {
+ matrix->postScale(SkFloatToScalar(sx), SkFloatToScalar(sy));
+}
+
+static void anp_preSkew(ANPMatrix* matrix, float kx, float ky) {
+ matrix->preSkew(SkFloatToScalar(kx), SkFloatToScalar(ky));
+}
+
+static void anp_postSkew(ANPMatrix* matrix, float kx, float ky) {
+ matrix->postSkew(SkFloatToScalar(kx), SkFloatToScalar(ky));
+}
+
+static void anp_preRotate(ANPMatrix* matrix, float degrees) {
+ matrix->preRotate(SkFloatToScalar(degrees));
+}
+
+static void anp_postRotate(ANPMatrix* matrix, float degrees) {
+ matrix->postRotate(SkFloatToScalar(degrees));
+}
+
+static void anp_preConcat(ANPMatrix* matrix, const ANPMatrix* other) {
+ matrix->preConcat(*other);
+}
+
+static void anp_postConcat(ANPMatrix* matrix, const ANPMatrix* other) {
+ matrix->postConcat(*other);
+}
+
+static bool anp_invert(ANPMatrix* dst, const ANPMatrix* src) {
+ return src->invert(dst);
+}
+
+static void anp_mapPoints(ANPMatrix* matrix, float dst[], const float src[],
+ int32_t count) {
+#ifdef SK_SCALAR_IS_FLOAT
+ matrix->mapPoints(reinterpret_cast<SkPoint*>(dst),
+ reinterpret_cast<const SkPoint*>(src), count);
+#else
+ const int N = 64;
+ SkPoint tmp[N];
+ do {
+ int n = count;
+ if (n > N) {
+ n = N;
+ }
+ fromFloat(&tmp[0].fX, src, n*2);
+ matrix->mapPoints(tmp, n);
+ toFloat(dst, &tmp[0].fX, n*2);
+ count -= n;
+ } while (count > 0);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPMatrixInterfaceV0_Init(ANPInterface* value) {
+ ANPMatrixInterfaceV0* i = reinterpret_cast<ANPMatrixInterfaceV0*>(value);
+
+ ASSIGN(i, newMatrix);
+ ASSIGN(i, deleteMatrix);
+ ASSIGN(i, getFlags);
+ ASSIGN(i, copy);
+ ASSIGN(i, get3x3);
+ ASSIGN(i, set3x3);
+ ASSIGN(i, setIdentity);
+ ASSIGN(i, preTranslate);
+ ASSIGN(i, postTranslate);
+ ASSIGN(i, preScale);
+ ASSIGN(i, postScale);
+ ASSIGN(i, preSkew);
+ ASSIGN(i, postSkew);
+ ASSIGN(i, preRotate);
+ ASSIGN(i, postRotate);
+ ASSIGN(i, preConcat);
+ ASSIGN(i, postConcat);
+ ASSIGN(i, invert);
+ ASSIGN(i, mapPoints);
+}
diff --git a/Source/WebKit/android/plugins/ANPOpenGLInterface.cpp b/Source/WebKit/android/plugins/ANPOpenGLInterface.cpp
new file mode 100644
index 0000000..839ec17
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPOpenGLInterface.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+
+#include "ANPOpenGL_npapi.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "MediaLayer.h"
+#include "WebViewCore.h"
+#include "Frame.h"
+#include "Page.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
+
+using namespace android;
+
+static WebCore::PluginView* pluginViewForInstance(NPP instance) {
+ if (instance && instance->ndata)
+ return static_cast<WebCore::PluginView*>(instance->ndata);
+ return WebCore::PluginView::currentPluginView();
+}
+
+static EGLContext anp_acquireContext(NPP instance) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ WebCore::MediaLayer* mediaLayer = pluginWidget->getLayer();
+
+ if (!mediaLayer)
+ return EGL_NO_CONTEXT;
+
+ return mediaLayer->getTexture()->producerAcquireContext();
+}
+
+static ANPTextureInfo anp_lockTexture(NPP instance) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ WebCore::MediaLayer* mediaLayer = pluginWidget->getLayer();
+ WebCore::DoubleBufferedTexture* texture = mediaLayer->getTexture();
+
+ // lock the texture and cache the internal info
+ WebCore::TextureInfo* info = texture->producerLock();
+ mediaLayer->setCurrentTextureInfo(info);
+
+ ANPTextureInfo anpInfo;
+ anpInfo.textureId = info->m_textureId;
+ anpInfo.width = (int32_t) info->m_width;
+ anpInfo.height = (int32_t) info->m_height;
+ anpInfo.internalFormat = info->m_internalFormat;
+ return anpInfo;
+}
+
+static void anp_releaseTexture(NPP instance, const ANPTextureInfo* textureInfo) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ WebCore::MediaLayer* mediaLayer = pluginWidget->getLayer();
+ WebCore::DoubleBufferedTexture* texture = mediaLayer->getTexture();
+
+ //copy the info into our internal structure
+ WebCore::TextureInfo* info = mediaLayer->getCurrentTextureInfo();
+ info->m_textureId = textureInfo->textureId;
+ info->m_width = textureInfo->width;
+ info->m_height = textureInfo->height;
+ info->m_internalFormat = textureInfo->internalFormat;
+
+ texture->producerReleaseAndSwap();
+
+ // invalidate the java view so that this content is drawn
+ pluginWidget->viewInvalidate();
+}
+
+static void anp_invertPluginContent(NPP instance, bool isContentInverted) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ WebCore::MediaLayer* mediaLayer = pluginWidget->getLayer();
+
+ mediaLayer->invertContents(isContentInverted);
+
+ //force the layer to sync to the UI thread
+ WebViewCore* wvc = pluginWidget->webViewCore();
+ if (wvc)
+ wvc->mainFrame()->page()->chrome()->client()->scheduleCompositingLayerSync();
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPOpenGLInterfaceV0_Init(ANPInterface* v) {
+ ANPOpenGLInterfaceV0* i = reinterpret_cast<ANPOpenGLInterfaceV0*>(v);
+
+ ASSIGN(i, acquireContext);
+ ASSIGN(i, lockTexture);
+ ASSIGN(i, releaseTexture);
+ ASSIGN(i, invertPluginContent);
+}
diff --git a/Source/WebKit/android/plugins/ANPOpenGL_npapi.h b/Source/WebKit/android/plugins/ANPOpenGL_npapi.h
new file mode 100644
index 0000000..5aabbc4
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPOpenGL_npapi.h
@@ -0,0 +1,63 @@
+/*
+ * 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 ANPOpenGL_npapi_H
+#define ANPOpenGL_npapi_H
+
+#include "android_npapi.h"
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+/**
+ * TODO should we not use EGL and GL data types for ABI safety?
+ */
+struct ANPTextureInfo {
+ GLuint textureId;
+ uint32_t width;
+ uint32_t height;
+ GLenum internalFormat;
+};
+
+struct ANPOpenGLInterfaceV0 : ANPInterface {
+ /**
+ */
+ EGLContext (*acquireContext)(NPP instance);
+
+ /**
+ */
+ ANPTextureInfo (*lockTexture)(NPP instance);
+
+ /**
+ */
+ void (*releaseTexture)(NPP instance, const ANPTextureInfo*);
+
+ /**
+ * Invert the contents of the plugin on the y-axis.
+ * default is to not be inverted (i.e. use OpenGL coordinates)
+ */
+ void (*invertPluginContent)(NPP instance, bool isContentInverted);
+};
+
+#endif //ANPOpenGL_npapi_H
diff --git a/Source/WebKit/android/plugins/ANPPaintInterface.cpp b/Source/WebKit/android/plugins/ANPPaintInterface.cpp
new file mode 100644
index 0000000..5c59df9
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPPaintInterface.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include "SkTypeface.h"
+
+static ANPPaint* anp_newPaint() {
+ return new ANPPaint;
+}
+
+static void anp_deletePaint(ANPPaint* paint) {
+ delete paint;
+}
+
+static ANPPaintFlags anp_getFlags(const ANPPaint* paint) {
+ return paint->getFlags();
+}
+
+static void anp_setFlags(ANPPaint* paint, ANPPaintFlags flags) {
+ paint->setFlags(flags);
+}
+
+static ANPColor anp_getColor(const ANPPaint* paint) {
+ return paint->getColor();
+}
+
+static void anp_setColor(ANPPaint* paint, ANPColor color) {
+ paint->setColor(color);
+}
+
+static ANPPaintStyle anp_getStyle(const ANPPaint* paint) {
+ return paint->getStyle();
+}
+
+static void anp_setStyle(ANPPaint* paint, ANPPaintStyle style) {
+ paint->setStyle(static_cast<SkPaint::Style>(style));
+}
+
+static float anp_getStrokeWidth(const ANPPaint* paint) {
+ return SkScalarToFloat(paint->getStrokeWidth());
+}
+
+static float anp_getStrokeMiter(const ANPPaint* paint) {
+ return SkScalarToFloat(paint->getStrokeMiter());
+}
+
+static ANPPaintCap anp_getStrokeCap(const ANPPaint* paint) {
+ return paint->getStrokeCap();
+}
+
+static ANPPaintJoin anp_getStrokeJoin(const ANPPaint* paint) {
+ return paint->getStrokeJoin();
+}
+
+static void anp_setStrokeWidth(ANPPaint* paint, float width) {
+ paint->setStrokeWidth(SkFloatToScalar(width));
+}
+
+static void anp_setStrokeMiter(ANPPaint* paint, float miter) {
+ paint->setStrokeMiter(SkFloatToScalar(miter));
+}
+
+static void anp_setStrokeCap(ANPPaint* paint, ANPPaintCap cap) {
+ paint->setStrokeCap(static_cast<SkPaint::Cap>(cap));
+}
+
+static void anp_setStrokeJoin(ANPPaint* paint, ANPPaintJoin join) {
+ paint->setStrokeJoin(static_cast<SkPaint::Join>(join));
+}
+
+static ANPTextEncoding anp_getTextEncoding(const ANPPaint* paint) {
+ return paint->getTextEncoding();
+}
+
+static ANPPaintAlign anp_getTextAlign(const ANPPaint* paint) {
+ return paint->getTextAlign();
+}
+
+static float anp_getTextSize(const ANPPaint* paint) {
+ return SkScalarToFloat(paint->getTextSize());
+}
+
+static float anp_getTextScaleX(const ANPPaint* paint) {
+ return SkScalarToFloat(paint->getTextScaleX());
+}
+
+static float anp_getTextSkewX(const ANPPaint* paint) {
+ return SkScalarToFloat(paint->getTextSkewX());
+}
+
+static ANPTypeface* anp_getTypeface(const ANPPaint* paint) {
+ return reinterpret_cast<ANPTypeface*>(paint->getTypeface());
+}
+
+static void anp_setTextEncoding(ANPPaint* paint, ANPTextEncoding encoding) {
+ paint->setTextEncoding(static_cast<SkPaint::TextEncoding>(encoding));
+}
+
+static void anp_setTextAlign(ANPPaint* paint, ANPPaintAlign align) {
+ paint->setTextAlign(static_cast<SkPaint::Align>(align));
+}
+
+static void anp_setTextSize(ANPPaint* paint, float textSize) {
+ paint->setTextSize(SkFloatToScalar(textSize));
+}
+
+static void anp_setTextScaleX(ANPPaint* paint, float scaleX) {
+ paint->setTextScaleX(SkFloatToScalar(scaleX));
+}
+
+static void anp_setTextSkewX(ANPPaint* paint, float skewX) {
+ paint->setTextSkewX(SkFloatToScalar(skewX));
+}
+
+static void anp_setTypeface(ANPPaint* paint, ANPTypeface* tf) {
+ paint->setTypeface(tf);
+}
+
+static float anp_measureText(ANPPaint* paint, const void* text,
+ uint32_t byteLength, ANPRectF* bounds) {
+ SkScalar w = paint->measureText(text, byteLength,
+ reinterpret_cast<SkRect*>(bounds));
+ return SkScalarToFloat(w);
+}
+
+/** Return the number of unichars specifed by the text.
+ If widths is not null, returns the array of advance widths for each
+ unichar.
+ If bounds is not null, returns the array of bounds for each unichar.
+ */
+static int anp_getTextWidths(ANPPaint* paint, const void* text,
+ uint32_t byteLength, float widths[], ANPRectF bounds[]) {
+ return paint->getTextWidths(text, byteLength, widths,
+ reinterpret_cast<SkRect*>(bounds));
+}
+
+static float anp_getFontMetrics(ANPPaint* paint, ANPFontMetrics* metrics) {
+ SkPaint::FontMetrics fm;
+ SkScalar spacing = paint->getFontMetrics(&fm);
+ if (metrics) {
+ metrics->fTop = SkScalarToFloat(fm.fTop);
+ metrics->fAscent = SkScalarToFloat(fm.fAscent);
+ metrics->fDescent = SkScalarToFloat(fm.fDescent);
+ metrics->fBottom = SkScalarToFloat(fm.fBottom);
+ metrics->fLeading = SkScalarToFloat(fm.fLeading);
+ }
+ return SkScalarToFloat(spacing);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPPaintInterfaceV0_Init(ANPInterface* value) {
+ ANPPaintInterfaceV0* i = reinterpret_cast<ANPPaintInterfaceV0*>(value);
+
+ ASSIGN(i, newPaint);
+ ASSIGN(i, deletePaint);
+ ASSIGN(i, getFlags);
+ ASSIGN(i, setFlags);
+ ASSIGN(i, getColor);
+ ASSIGN(i, setColor);
+ ASSIGN(i, getStyle);
+ ASSIGN(i, setStyle);
+ ASSIGN(i, getStrokeWidth);
+ ASSIGN(i, getStrokeMiter);
+ ASSIGN(i, getStrokeCap);
+ ASSIGN(i, getStrokeJoin);
+ ASSIGN(i, setStrokeWidth);
+ ASSIGN(i, setStrokeMiter);
+ ASSIGN(i, setStrokeCap);
+ ASSIGN(i, setStrokeJoin);
+ ASSIGN(i, getTextEncoding);
+ ASSIGN(i, getTextAlign);
+ ASSIGN(i, getTextSize);
+ ASSIGN(i, getTextScaleX);
+ ASSIGN(i, getTextSkewX);
+ ASSIGN(i, getTypeface);
+ ASSIGN(i, setTextEncoding);
+ ASSIGN(i, setTextAlign);
+ ASSIGN(i, setTextSize);
+ ASSIGN(i, setTextScaleX);
+ ASSIGN(i, setTextSkewX);
+ ASSIGN(i, setTypeface);
+ ASSIGN(i, measureText);
+ ASSIGN(i, getTextWidths);
+ ASSIGN(i, getFontMetrics);
+}
diff --git a/Source/WebKit/android/plugins/ANPPathInterface.cpp b/Source/WebKit/android/plugins/ANPPathInterface.cpp
new file mode 100644
index 0000000..69cabcf
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPPathInterface.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+
+static ANPPath* anp_newPath() {
+ return new ANPPath;
+}
+
+static void anp_deletePath(ANPPath* path) {
+ delete path;
+}
+
+static void anp_copy(ANPPath* dst, const ANPPath* src) {
+ *dst = *src;
+}
+
+static bool anp_equal(const ANPPath* p0, const ANPPath* p1) {
+ return *p0 == *p1;
+}
+
+static void anp_reset(ANPPath* path) {
+ path->reset();
+}
+
+static bool anp_isEmpty(const ANPPath* path) {
+ return path->isEmpty();
+}
+
+static void anp_getBounds(const ANPPath* path, ANPRectF* bounds) {
+ SkANP::SetRect(bounds, path->getBounds());
+}
+
+static void anp_moveTo(ANPPath* path, float x, float y) {
+ path->moveTo(SkFloatToScalar(x), SkFloatToScalar(y));
+}
+
+static void anp_lineTo(ANPPath* path, float x, float y) {
+ path->lineTo(SkFloatToScalar(x), SkFloatToScalar(y));
+}
+
+static void anp_quadTo(ANPPath* path, float x0, float y0, float x1, float y1) {
+ path->quadTo(SkFloatToScalar(x0), SkFloatToScalar(y0),
+ SkFloatToScalar(x1), SkFloatToScalar(y1));
+}
+
+static void anp_cubicTo(ANPPath* path, float x0, float y0,
+ float x1, float y1, float x2, float y2) {
+ path->cubicTo(SkFloatToScalar(x0), SkFloatToScalar(y0),
+ SkFloatToScalar(x1), SkFloatToScalar(y1),
+ SkFloatToScalar(x2), SkFloatToScalar(y2));
+}
+
+static void anp_close(ANPPath* path) {
+ path->close();
+}
+
+static void anp_offset(ANPPath* path, float dx, float dy, ANPPath* dst) {
+ path->offset(SkFloatToScalar(dx), SkFloatToScalar(dy), dst);
+}
+
+static void anp_transform(ANPPath* src, const ANPMatrix* matrix,
+ ANPPath* dst) {
+ src->transform(*matrix, dst);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPPathInterfaceV0_Init(ANPInterface* value) {
+ ANPPathInterfaceV0* i = reinterpret_cast<ANPPathInterfaceV0*>(value);
+
+ ASSIGN(i, newPath);
+ ASSIGN(i, deletePath);
+ ASSIGN(i, copy);
+ ASSIGN(i, equal);
+ ASSIGN(i, reset);
+ ASSIGN(i, isEmpty);
+ ASSIGN(i, getBounds);
+ ASSIGN(i, moveTo);
+ ASSIGN(i, lineTo);
+ ASSIGN(i, quadTo);
+ ASSIGN(i, cubicTo);
+ ASSIGN(i, close);
+ ASSIGN(i, offset);
+ ASSIGN(i, transform);
+}
diff --git a/Source/WebKit/android/plugins/ANPSoundInterface.cpp b/Source/WebKit/android/plugins/ANPSoundInterface.cpp
new file mode 100644
index 0000000..c238872
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPSoundInterface.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "android_npapi.h"
+
+#include "SkTypes.h"
+#include "media/AudioTrack.h"
+
+#include <system/audio.h>
+
+struct ANPAudioTrack {
+ void* mUser;
+ ANPAudioCallbackProc mProc;
+ android::AudioTrack* mTrack;
+};
+
+static ANPSampleFormat toANPFormat(int fm) {
+ switch (fm) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return kPCM16Bit_ANPSampleFormat;
+ case AUDIO_FORMAT_PCM_8_BIT:
+ return kPCM8Bit_ANPSampleFormat;
+ default:
+ return kUnknown_ANPSamleFormat;
+ }
+}
+
+static int fromANPFormat(ANPSampleFormat fm) {
+ switch (fm) {
+ case kPCM16Bit_ANPSampleFormat:
+ return AUDIO_FORMAT_PCM_16_BIT;
+ case kPCM8Bit_ANPSampleFormat:
+ return AUDIO_FORMAT_PCM_8_BIT;
+ default:
+ return AUDIO_FORMAT_INVALID;
+ }
+}
+
+static void callbackProc(int event, void* user, void* info) {
+ ANPAudioTrack* track = reinterpret_cast<ANPAudioTrack*>(user);
+
+ switch (event) {
+ case android::AudioTrack::EVENT_MORE_DATA: {
+ ANPAudioBuffer dst;
+ android::AudioTrack::Buffer* src;
+
+ src = reinterpret_cast<android::AudioTrack::Buffer*>(info);
+ dst.bufferData = src->raw;
+ dst.channelCount = src->channelCount;
+ dst.format = toANPFormat(src->format);
+ dst.size = src->size;
+ track->mProc(kMoreData_ANPAudioEvent, track->mUser, &dst);
+ // return the updated size field
+ src->size = dst.size;
+ break;
+ }
+ case android::AudioTrack::EVENT_UNDERRUN:
+ track->mProc(kUnderRun_ANPAudioEvent, track->mUser, NULL);
+ break;
+ default:
+ SkDebugf("------ unknown audio event for plugin %d\n", event);
+ break;
+ }
+}
+
+static ANPAudioTrack* ANPCreateTrack(uint32_t sampleRate,
+ ANPSampleFormat format,
+ int channelCount,
+ ANPAudioCallbackProc proc,
+ void* user) {
+
+ ANPAudioTrack* track = new ANPAudioTrack;
+
+ track->mUser = user;
+ track->mProc = proc;
+ track->mTrack = new android::AudioTrack(AUDIO_STREAM_MUSIC,
+ sampleRate,
+ fromANPFormat(format),
+ (channelCount > 1) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
+ 0, // frameCount
+ 0, // flags
+ callbackProc,
+ track,
+ 0);
+
+ if (track->mTrack->initCheck() != 0) { // failure
+ delete track->mTrack;
+ delete track;
+ track = NULL;
+ }
+ return track;
+}
+
+static void ANPDeleteTrack(ANPAudioTrack* track) {
+ if (track) {
+ delete track->mTrack;
+ delete track;
+ }
+}
+
+static void ANPTrackStart(ANPAudioTrack* track) {
+ track->mTrack->start();
+}
+
+static void ANPTrackPause(ANPAudioTrack* track) {
+ track->mTrack->pause();
+}
+
+static void ANPTrackStop(ANPAudioTrack* track) {
+ track->mTrack->stop();
+}
+
+static bool ANPTrackIsStopped(ANPAudioTrack* track) {
+ return track->mTrack->stopped();
+}
+
+static uint32_t ANPTrackLatency(ANPAudioTrack* track) {
+ return track->mTrack->latency();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ANPAudioTrackInterfaceV0_Init(ANPInterface* value) {
+ ANPAudioTrackInterfaceV0* si = reinterpret_cast<ANPAudioTrackInterfaceV0*>(value);
+ si->newTrack = ANPCreateTrack;
+ si->deleteTrack = ANPDeleteTrack;
+ si->start = ANPTrackStart;
+ si->pause = ANPTrackPause;
+ si->stop = ANPTrackStop;
+ si->isStopped = ANPTrackIsStopped;
+}
+
+void ANPAudioTrackInterfaceV1_Init(ANPInterface* value) {
+ // initialize the functions from the previous interface
+ ANPAudioTrackInterfaceV0_Init(value);
+ // add any new functions or override existing functions
+ ANPAudioTrackInterfaceV1* si = reinterpret_cast<ANPAudioTrackInterfaceV1*>(value);
+ si->trackLatency = ANPTrackLatency;
+}
diff --git a/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp
new file mode 100644
index 0000000..4b99b31
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "ANPSurface_npapi.h"
+
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "SkANP.h"
+#include "android_graphics.h"
+#include <JNIUtility.h>
+#include <surfaceflinger/Surface.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+// used to cache JNI method and field IDs for Surface Objects
+static struct ANPSurfaceInterfaceJavaGlue {
+ bool initialized;
+ jmethodID getSurfaceHolder;
+ jmethodID getSurface;
+ jfieldID surfacePointer;
+} gSurfaceJavaGlue;
+
+static inline sp<Surface> getSurface(JNIEnv* env, jobject view) {
+ if (!env || !view) {
+ return NULL;
+ }
+
+ if (!gSurfaceJavaGlue.initialized) {
+
+ jclass surfaceViewClass = env->FindClass("android/view/SurfaceView");
+ gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder",
+ "()Landroid/view/SurfaceHolder;");
+
+ jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder");
+ gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface",
+ "()Landroid/view/Surface;");
+
+ jclass surfaceClass = env->FindClass("android/view/Surface");
+ gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+ ANDROID_VIEW_SURFACE_JNI_ID, "I");
+
+ env->DeleteLocalRef(surfaceClass);
+ env->DeleteLocalRef(surfaceViewClass);
+ env->DeleteLocalRef(surfaceHolderClass);
+
+ gSurfaceJavaGlue.initialized = true;
+ }
+
+ jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder);
+ jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface);
+ jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer);
+
+ env->DeleteLocalRef(holder);
+ env->DeleteLocalRef(surface);
+
+ return sp<Surface>((Surface*) surfacePointer);
+}
+
+static inline ANPBitmapFormat convertPixelFormat(PixelFormat format) {
+ switch (format) {
+ case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat;
+ case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat;
+ default: return kUnknown_ANPBitmapFormat;
+ }
+}
+
+static bool anp_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
+ if (!bitmap || !surfaceView) {
+ return false;
+ }
+
+ sp<Surface> surface = getSurface(env, surfaceView);
+
+ if (!bitmap || !Surface::isValid(surface)) {
+ return false;
+ }
+
+ Region dirtyRegion;
+ if (dirtyRect) {
+ Rect rect(dirtyRect->left, dirtyRect->top, dirtyRect->right, dirtyRect->bottom);
+ if (!rect.isEmpty()) {
+ dirtyRegion.set(rect);
+ }
+ } else {
+ dirtyRegion.set(Rect(0x3FFF, 0x3FFF));
+ }
+
+ Surface::SurfaceInfo info;
+ status_t err = surface->lock(&info, &dirtyRegion);
+ if (err < 0) {
+ return false;
+ }
+
+ // the surface may have expanded the dirty region so we must to pass that
+ // information back to the plugin.
+ if (dirtyRect) {
+ Rect dirtyBounds = dirtyRegion.getBounds();
+ dirtyRect->left = dirtyBounds.left;
+ dirtyRect->right = dirtyBounds.right;
+ dirtyRect->top = dirtyBounds.top;
+ dirtyRect->bottom = dirtyBounds.bottom;
+ }
+
+ ssize_t bpr = info.s * bytesPerPixel(info.format);
+
+ bitmap->format = convertPixelFormat(info.format);
+ bitmap->width = info.w;
+ bitmap->height = info.h;
+ bitmap->rowBytes = bpr;
+
+ if (info.w > 0 && info.h > 0) {
+ bitmap->baseAddr = info.bits;
+ } else {
+ bitmap->baseAddr = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+static void anp_unlock(JNIEnv* env, jobject surfaceView) {
+ if (!surfaceView) {
+ return;
+ }
+
+ sp<Surface> surface = getSurface(env, surfaceView);
+
+ if (!Surface::isValid(surface)) {
+ return;
+ }
+
+ surface->unlockAndPost();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPSurfaceInterfaceV0_Init(ANPInterface* value) {
+ ANPSurfaceInterfaceV0* i = reinterpret_cast<ANPSurfaceInterfaceV0*>(value);
+
+ ASSIGN(i, lock);
+ ASSIGN(i, unlock);
+
+ // setup the java glue struct
+ gSurfaceJavaGlue.initialized = false;
+}
diff --git a/Source/WebKit/android/plugins/ANPSurface_npapi.h b/Source/WebKit/android/plugins/ANPSurface_npapi.h
new file mode 100644
index 0000000..910a948
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPSurface_npapi.h
@@ -0,0 +1,48 @@
+/*
+ * 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 ANPSurface_npapi_H
+#define ANPSurface_npapi_H
+
+#include "android_npapi.h"
+#include <jni.h>
+
+struct ANPSurfaceInterfaceV0 : ANPInterface {
+ /** Locks the surface from manipulation by other threads and provides a bitmap
+ to be written to. The dirtyRect param specifies which portion of the
+ bitmap will be written to. If the dirtyRect is NULL then the entire
+ surface will be considered dirty. If the lock was successful the function
+ will return true and the bitmap will be set to point to a valid bitmap.
+ If not the function will return false and the bitmap will be set to NULL.
+ */
+ bool (*lock)(JNIEnv* env, jobject surface, ANPBitmap* bitmap, ANPRectI* dirtyRect);
+ /** Given a locked surface handle (i.e. result of a successful call to lock)
+ the surface is unlocked and the contents of the bitmap, specifically
+ those inside the dirtyRect are written to the screen.
+ */
+ void (*unlock)(JNIEnv* env, jobject surface);
+};
+
+#endif //ANPSurface_npapi_H
diff --git a/Source/WebKit/android/plugins/ANPSystemInterface.cpp b/Source/WebKit/android/plugins/ANPSystemInterface.cpp
new file mode 100644
index 0000000..7199635
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPSystemInterface.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+
+#include "ANPSystem_npapi.h"
+#include "Frame.h"
+#include "JavaSharedClient.h"
+#include "PluginClient.h"
+#include "PluginPackage.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "Settings.h"
+#include "SkString.h"
+#include "WebViewCore.h"
+#include <wtf/text/CString.h>
+
+#include <dirent.h>
+
+//#define PLUGIN_DEBUG_LOCAL // controls the printing of log messages
+#include "PluginDebugAndroid.h"
+
+static const char* gApplicationDataDir = NULL;
+static const char* gApplicationDataDirIncognito = NULL;
+
+using namespace android;
+
+static WebCore::PluginView* pluginViewForInstance(NPP instance) {
+ if (instance && instance->ndata)
+ return static_cast<WebCore::PluginView*>(instance->ndata);
+ return WebCore::PluginView::currentPluginView();
+}
+
+static const char* anp_getApplicationDataDirectory() {
+ if (NULL == gApplicationDataDir) {
+ PluginClient* client = JavaSharedClient::GetPluginClient();
+ if (!client)
+ return NULL;
+
+ WTF::String path = client->getPluginSharedDataDirectory();
+ int length = path.length();
+ if (length == 0)
+ return NULL;
+
+ char* storage = (char*) malloc(length + 1);
+ if (NULL == storage)
+ return NULL;
+
+ memcpy(storage, path.utf8().data(), length);
+ storage[length] = '\0';
+
+ static const char incognitoPath[] = "/incognito_plugins";
+ char* incognitoStorage = (char*) malloc(length + strlen(incognitoPath) + 1);
+
+ strcpy(incognitoStorage, storage);
+ strcat(incognitoStorage, incognitoPath);
+
+ // save this assignment for last, so that if multiple threads call us
+ // (which should never happen), we never return an incomplete global.
+ // At worst, we would allocate storage for the path twice.
+ gApplicationDataDir = storage;
+ gApplicationDataDirIncognito = incognitoStorage;
+ }
+
+ return gApplicationDataDir;
+}
+
+static const char* anp_getApplicationDataDirectoryV2(NPP instance) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+
+ if (NULL == gApplicationDataDir) {
+ anp_getApplicationDataDirectory();
+ }
+
+ WebCore::Settings* settings = pluginWidget->webViewCore()->mainFrame()->settings();
+ if (settings && settings->privateBrowsingEnabled()) {
+ // if this is an incognito view then check the path to see if it exists
+ // and if it is a directory, otherwise if it does not exist create it.
+ struct stat st;
+ if (stat(gApplicationDataDirIncognito, &st) == 0) {
+ if (!S_ISDIR(st.st_mode)) {
+ return NULL;
+ }
+ } else {
+ if (mkdir(gApplicationDataDirIncognito, S_IRWXU) != 0) {
+ return NULL;
+ }
+ }
+
+ return gApplicationDataDirIncognito;
+ }
+
+ return gApplicationDataDir;
+}
+
+static jclass anp_loadJavaClass(NPP instance, const char* className) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+
+ jclass result;
+ result = pluginWidget->webViewCore()->getPluginClass(pluginView->plugin()->path(),
+ className);
+ return result;
+}
+
+static void anp_setPowerState(NPP instance, ANPPowerState powerState) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+
+ pluginWidget->setPowerState(powerState);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPSystemInterfaceV0_Init(ANPInterface* v) {
+ ANPSystemInterfaceV0* i = reinterpret_cast<ANPSystemInterfaceV0*>(v);
+
+ ASSIGN(i, getApplicationDataDirectory);
+ ASSIGN(i, loadJavaClass);
+}
+
+void ANPSystemInterfaceV1_Init(ANPInterface* v) {
+ // initialize the functions from the previous interface
+ ANPSystemInterfaceV0_Init(v);
+ // add any new functions or override existing functions
+ ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(v);
+ ASSIGN(i, setPowerState);
+}
+
+void ANPSystemInterfaceV2_Init(ANPInterface* v) {
+ // initialize the functions from the previous interface
+ ANPSystemInterfaceV1_Init(v);
+ // add any new functions or override existing functions
+ ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(v);
+ i->getApplicationDataDirectory = anp_getApplicationDataDirectoryV2;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool isDirectory(const char* path) {
+ struct stat st;
+ return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static void removeDirectory(const char* path) {
+ // create a pointer to a directory
+ DIR *dir = NULL;
+ dir = opendir(path);
+ if (!dir)
+ return;
+
+ struct dirent* entry = 0;
+ while ((entry = readdir(dir))) { // while there is still something in the directory to list
+ if (!entry)
+ return;
+
+ if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) {
+ PLUGIN_LOG(". file: %s", entry->d_name);
+ continue;
+ }
+
+ // concatenate the strings to get the complete path
+ static const char separator[] = "/";
+ char* file = (char*) malloc(strlen(path) + strlen(separator) + strlen(entry->d_name) + 1);
+ strcpy(file, path);
+ strcat(file, separator);
+ strcat(file, entry->d_name);
+
+ if (isDirectory(file) == true) {
+ PLUGIN_LOG("remove dir: %s", file);
+ removeDirectory(file);
+ } else { // it's a file, we can use remove
+ PLUGIN_LOG("remove file: %s", file);
+ remove(file);
+ }
+
+ free(file);
+ }
+
+ // clean up
+ closedir (dir); // close the directory
+ rmdir(path); // delete the directory
+}
+
+void ANPSystemInterface_CleanupIncognito() {
+ PLUGIN_LOG("cleanup incognito plugin directory");
+
+ if (gApplicationDataDirIncognito == NULL)
+ anp_getApplicationDataDirectory();
+ if (gApplicationDataDirIncognito == NULL)
+ return;
+
+ // check to see if the directory exists and if so delete it
+ if (isDirectory(gApplicationDataDirIncognito))
+ removeDirectory(gApplicationDataDirIncognito);
+}
diff --git a/Source/WebKit/android/plugins/ANPSystem_npapi.h b/Source/WebKit/android/plugins/ANPSystem_npapi.h
new file mode 100644
index 0000000..835bc7c
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPSystem_npapi.h
@@ -0,0 +1,72 @@
+/*
+ * 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 ANPSystem_npapi_H
+#define ANPSystem_npapi_H
+
+#include "android_npapi.h"
+#include <jni.h>
+
+struct ANPSystemInterfaceV0 : ANPInterface {
+ /** Return the path name for the current Application's plugin data directory,
+ or NULL if not supported
+ */
+ const char* (*getApplicationDataDirectory)();
+
+ /** A helper function to load java classes from the plugin's apk. The
+ function looks for a class given the fully qualified and null terminated
+ string representing the className. For example,
+
+ const char* className = "com.android.mypackage.MyClass";
+
+ If the class cannot be found or there is a problem loading the class
+ NULL will be returned.
+ */
+ jclass (*loadJavaClass)(NPP instance, const char* className);
+};
+
+enum ANPPowerStates {
+ kDefault_ANPPowerState = 0,
+ kScreenOn_ANPPowerState = 1
+};
+typedef int32_t ANPPowerState;
+
+struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 {
+ void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+struct ANPSystemInterfaceV2 : ANPInterface {
+ /** Return the path name for the current Application's plugin data directory,
+ or NULL if not supported. This directory will change depending on whether
+ or not the plugin is found within an incognito tab.
+ */
+ const char* (*getApplicationDataDirectory)(NPP instance);
+
+ // redeclaration of existing features
+ jclass (*loadJavaClass)(NPP instance, const char* className);
+ void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+#endif //ANPSystem_npapi_H
diff --git a/Source/WebKit/android/plugins/ANPTypefaceInterface.cpp b/Source/WebKit/android/plugins/ANPTypefaceInterface.cpp
new file mode 100644
index 0000000..99734a7
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPTypefaceInterface.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include "SkFontHost.h"
+
+static ANPTypeface* anp_createFromName(const char name[], ANPTypefaceStyle s) {
+ SkTypeface* tf = SkTypeface::CreateFromName(name,
+ static_cast<SkTypeface::Style>(s));
+ return reinterpret_cast<ANPTypeface*>(tf);
+}
+
+static ANPTypeface* anp_createFromTypeface(const ANPTypeface* family,
+ ANPTypefaceStyle s) {
+ SkTypeface* tf = SkTypeface::CreateFromTypeface(family,
+ static_cast<SkTypeface::Style>(s));
+ return reinterpret_cast<ANPTypeface*>(tf);
+}
+
+static int32_t anp_getRefCount(const ANPTypeface* tf) {
+ return tf ? tf->getRefCnt() : 0;
+}
+
+static void anp_ref(ANPTypeface* tf) {
+ SkSafeRef(tf);
+}
+
+static void anp_unref(ANPTypeface* tf) {
+ SkSafeUnref(tf);
+}
+
+static ANPTypefaceStyle anp_getStyle(const ANPTypeface* tf) {
+ SkTypeface::Style s = tf ? tf->style() : SkTypeface::kNormal;
+ return static_cast<ANPTypefaceStyle>(s);
+}
+
+static int32_t anp_getFontPath(const ANPTypeface* tf, char fileName[],
+ int32_t length, int32_t* index) {
+ size_t size = SkFontHost::GetFileName(SkTypeface::UniqueID(tf), fileName,
+ length, index);
+ return static_cast<int32_t>(size);
+}
+
+static const char* gFontDir;
+#define FONT_DIR_SUFFIX "/fonts/"
+
+static const char* anp_getFontDirectoryPath() {
+ if (NULL == gFontDir) {
+ const char* root = getenv("ANDROID_ROOT");
+ size_t len = strlen(root);
+ char* storage = (char*)malloc(len + sizeof(FONT_DIR_SUFFIX));
+ if (NULL == storage) {
+ return NULL;
+ }
+ memcpy(storage, root, len);
+ memcpy(storage + len, FONT_DIR_SUFFIX, sizeof(FONT_DIR_SUFFIX));
+ // save this assignment for last, so that if multiple threads call us
+ // (which should never happen), we never return an incomplete global.
+ // At worst, we would allocate storage for the path twice.
+ gFontDir = storage;
+ }
+ return gFontDir;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPTypefaceInterfaceV0_Init(ANPInterface* v) {
+ ANPTypefaceInterfaceV0* i = reinterpret_cast<ANPTypefaceInterfaceV0*>(v);
+
+ ASSIGN(i, createFromName);
+ ASSIGN(i, createFromTypeface);
+ ASSIGN(i, getRefCount);
+ ASSIGN(i, ref);
+ ASSIGN(i, unref);
+ ASSIGN(i, getStyle);
+ ASSIGN(i, getFontPath);
+ ASSIGN(i, getFontDirectoryPath);
+}
diff --git a/Source/WebKit/android/plugins/ANPVideoInterface.cpp b/Source/WebKit/android/plugins/ANPVideoInterface.cpp
new file mode 100644
index 0000000..8eb9846
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPVideoInterface.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011, 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "ANPVideo_npapi.h"
+#include "SkANP.h"
+
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "MediaLayer.h"
+
+static WebCore::PluginView* pluginViewForInstance(NPP instance) {
+ if (instance && instance->ndata)
+ return static_cast<WebCore::PluginView*>(instance->ndata);
+ return WebCore::PluginView::currentPluginView();
+}
+
+static WebCore::MediaLayer* mediaLayerForInstance(NPP instance) {
+ WebCore::PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ return pluginWidget->getLayer();
+}
+
+static ANativeWindow* anp_acquireNativeWindow(NPP instance) {
+ WebCore::MediaLayer* mediaLayer = mediaLayerForInstance(instance);
+
+ return mediaLayer->acquireNativeWindowForVideo();
+}
+
+static void anp_setWindowDimensions(NPP instance, const ANativeWindow* window,
+ const ANPRectF* dimensions) {
+
+ WebCore::MediaLayer* mediaLayer = mediaLayerForInstance(instance);
+ if (!mediaLayer)
+ return;
+
+ SkRect rect;
+ mediaLayer->setWindowDimensionsForVideo(window, *SkANP::SetRect(&rect, *dimensions));
+}
+
+
+static void anp_releaseNativeWindow(NPP instance, ANativeWindow* window) {
+ WebCore::MediaLayer* mediaLayer = mediaLayerForInstance(instance);
+ if (!mediaLayer)
+ return;
+
+ mediaLayer->releaseNativeWindowForVideo(window);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPVideoInterfaceV0_Init(ANPInterface* value) {
+ ANPVideoInterfaceV0* i = reinterpret_cast<ANPVideoInterfaceV0*>(value);
+
+ ASSIGN(i, acquireNativeWindow);
+ ASSIGN(i, setWindowDimensions);
+ ASSIGN(i, releaseNativeWindow);
+}
diff --git a/Source/WebKit/android/plugins/ANPVideo_npapi.h b/Source/WebKit/android/plugins/ANPVideo_npapi.h
new file mode 100644
index 0000000..18e0231
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPVideo_npapi.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011, 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 ANPVideo_npapi_H
+#define ANPVideo_npapi_H
+
+#include "android_npapi.h"
+#include <android/native_window.h>
+
+struct ANPVideoInterfaceV0 : ANPInterface {
+
+ /**
+ * Constructs a new native window to be used for rendering video content.
+ *
+ * Subsequent calls will produce new windows, but may also return NULL after
+ * n attempts if the browser has reached it's limit. Further, if the browser
+ * is unable to acquire the window quickly it may also return NULL in order
+ * to not prevent the plugin from executing. A subsequent call will then
+ * return the window if it is avaiable.
+ *
+ * NOTE: The hardware may fail if you try to decode more than the allowable
+ * number of videos supported on that device.
+ */
+ ANativeWindow* (*acquireNativeWindow)(NPP instance);
+
+ /**
+ * Sets the rectangle that specifies where the video content is to be drawn.
+ * The dimensions are in document space. Further, if the rect is NULL the
+ * browser will not attempt to draw the window, therefore do not set the
+ * dimensions until you queue the first buffer in the window.
+ */
+ void (*setWindowDimensions)(NPP instance, const ANativeWindow* window, const ANPRectF* dimensions);
+
+ /**
+ */
+ void (*releaseNativeWindow)(NPP instance, ANativeWindow* window);
+};
+
+#endif //ANPVideo_npapi_H
diff --git a/Source/WebKit/android/plugins/ANPWindowInterface.cpp b/Source/WebKit/android/plugins/ANPWindowInterface.cpp
new file mode 100644
index 0000000..a74616c
--- /dev/null
+++ b/Source/WebKit/android/plugins/ANPWindowInterface.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include "WebViewCore.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+
+static PluginView* pluginViewForInstance(NPP instance) {
+ if (instance && instance->ndata)
+ return static_cast<PluginView*>(instance->ndata);
+ return PluginView::currentPluginView();
+}
+
+static void anp_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ pluginWidget->setVisibleRects(rects, count);
+}
+
+static void anp_clearVisibleRects(NPP instance) {
+ anp_setVisibleRects(instance, NULL, 0);
+}
+
+static void anp_showKeyboard(NPP instance, bool value) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ if(pluginWidget->hasFocus())
+ pluginWidget->webViewCore()->requestKeyboard(value);
+}
+
+static void anp_requestFullScreen(NPP instance) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ // call focusPluginElement() so that the pluginView receives keyboard events
+ pluginView->focusPluginElement();
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ pluginWidget->requestFullScreen();
+}
+
+static void anp_exitFullScreen(NPP instance) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ pluginWidget->exitFullScreen(true);
+}
+
+static void anp_requestCenterFitZoom(NPP instance) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ pluginWidget->requestCenterFitZoom();
+}
+
+static ANPRectI anp_visibleRect(NPP instance) {
+ PluginView* pluginView = pluginViewForInstance(instance);
+ PluginWidgetAndroid* pluginWidget = pluginView->platformPluginWidget();
+ return pluginWidget->visibleRect();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPWindowInterfaceV0_Init(ANPInterface* value) {
+ ANPWindowInterfaceV0* i = reinterpret_cast<ANPWindowInterfaceV0*>(value);
+
+ ASSIGN(i, setVisibleRects);
+ ASSIGN(i, clearVisibleRects);
+ ASSIGN(i, showKeyboard);
+ ASSIGN(i, requestFullScreen);
+ ASSIGN(i, exitFullScreen);
+ ASSIGN(i, requestCenterFitZoom);
+}
+
+void ANPWindowInterfaceV1_Init(ANPInterface* value) {
+ // initialize the functions from the previous interface
+ ANPWindowInterfaceV0_Init(value);
+ // add any new functions or override existing functions
+ ANPWindowInterfaceV1* i = reinterpret_cast<ANPWindowInterfaceV1*>(value);
+ ASSIGN(i, visibleRect);
+}
diff --git a/Source/WebKit/android/plugins/PluginDebugAndroid.cpp b/Source/WebKit/android/plugins/PluginDebugAndroid.cpp
new file mode 100644
index 0000000..3958714
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginDebugAndroid.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "PluginDebugAndroid.h"
+#include "utils/Log.h"
+#include "utils/SystemClock.h"
+#include <stdarg.h>
+
+#define ARRAY_COUNT(array) static_cast<int32_t>(sizeof(array) / sizeof(array[0]))
+
+// used for key, mouse, and touch inputs
+static const char* const inputActions[] = {
+ "down",
+ "up",
+ "move", /* touch only */
+ "cancel", /* touch only */
+ "longPress", /* touch only */
+ "doubleTap" /* touch only */
+};
+
+static const char* const lifecycleActions[] = {
+ "kPause_ANPLifecycleAction",
+ "kResume_ANPLifecycleAction",
+ "kGainFocus_ANPLifecycleAction",
+ "kLoseFocus_ANPLifecycleAction",
+ "kFreeMemory_ANPLifecycleAction",
+ "kOnLoad_ANPLifecycleAction",
+ "kEnterFullScreen_ANPLifecycleAction",
+ "kExitFullScreen_ANPLifecycleAction",
+ "kOnScreen_ANPLifecycleAction",
+ "kOffScreen_ANPLifecycleAction"
+};
+
+void anp_logPlugin(const char format[], ...) {
+ va_list args;
+ va_start(args, format);
+ LOG_PRI_VA(ANDROID_LOG_DEBUG, "webkit_plugin", format, args);
+ va_end(args);
+}
+
+void anp_logPluginEvent(void* npp, const ANPEvent* evt, int16_t returnVal, int elapsedTime) {
+
+ switch(evt->eventType) {
+
+ case kNull_ANPEventType:
+ PLUGIN_LOG("%p EVENT::NULL", npp);
+ break;
+
+ case kKey_ANPEventType:
+ if(evt->data.key.action < ARRAY_COUNT(inputActions)) {
+ anp_logPlugin("%p EVENT::KEY[%d] time=%d action=%s code=%d vcode=%d unichar=%d repeat=%d mods=%x",
+ npp, returnVal, elapsedTime, inputActions[evt->data.key.action],
+ evt->data.key.nativeCode, evt->data.key.virtualCode,
+ evt->data.key.unichar, evt->data.key.repeatCount,
+ evt->data.key.modifiers);
+ } else {
+ PLUGIN_LOG("%p EVENT::KEY[%d] unknown action", npp, returnVal);
+ }
+ break;
+
+ case kMouse_ANPEventType:
+ if(evt->data.mouse.action < ARRAY_COUNT(inputActions)) {
+ anp_logPlugin("%p EVENT::MOUSE[%d] time=%d action=%s [%d %d]", npp,
+ returnVal, elapsedTime, inputActions[evt->data.mouse.action],
+ evt->data.touch.x, evt->data.touch.y);
+ } else {
+ anp_logPlugin("%p EVENT::MOUSE[%d] unknown action", npp, returnVal);
+ }
+ break;
+
+ case kTouch_ANPEventType:
+ if(evt->data.touch.action < ARRAY_COUNT(inputActions)) {
+
+ anp_logPlugin("%p EVENT::TOUCH[%d] time=%d action=%s [%d %d]",
+ npp, returnVal, elapsedTime,
+ inputActions[evt->data.touch.action], evt->data.touch.x,
+ evt->data.touch.y);
+ } else {
+ anp_logPlugin("%p EVENT::TOUCH[%d] unknown action", npp, returnVal);
+ }
+ break;
+
+ case kDraw_ANPEventType:
+ if (evt->data.draw.model == kBitmap_ANPDrawingModel) {
+ anp_logPlugin("%p EVENT::DRAW bitmap time=%d format=%d clip=[%d,%d,%d,%d]",
+ npp, elapsedTime, evt->data.draw.data.bitmap.format,
+ evt->data.draw.clip.left, evt->data.draw.clip.top,
+ evt->data.draw.clip.right, evt->data.draw.clip.bottom);
+ } else if (evt->data.draw.model == kOpenGL_ANPDrawingModel) {
+ anp_logPlugin("%p EVENT::DRAW openGL time=%d dimensions=[%d,%d]",
+ npp, elapsedTime, evt->data.draw.data.surface.width,
+ evt->data.draw.data.surface.height);
+ } else {
+ anp_logPlugin("%p EVENT::DRAW unknown drawing model", npp);
+ }
+ break;
+
+ case kLifecycle_ANPEventType:
+ if(evt->data.lifecycle.action < ARRAY_COUNT(lifecycleActions)) {
+ anp_logPlugin("%p EVENT::LIFECYCLE time=%d action=%s", npp, elapsedTime,
+ lifecycleActions[evt->data.lifecycle.action]);
+ } else {
+ anp_logPlugin("%p EVENT::LIFECYCLE unknown action", npp);
+ }
+ break;
+
+ case kCustom_ANPEventType:
+ anp_logPlugin("%p EVENT::CUSTOM time=%d", npp, elapsedTime);
+ break;
+
+ default:
+ anp_logPlugin("%p EVENT::UNKNOWN", npp);
+ break;
+ }
+}
diff --git a/Source/WebKit/android/plugins/PluginDebugAndroid.h b/Source/WebKit/android/plugins/PluginDebugAndroid.h
new file mode 100644
index 0000000..5002882
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginDebugAndroid.h
@@ -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.
+ */
+
+#ifndef PLUGIN_DEBUG_ANDROID_H__
+#define PLUGIN_DEBUG_ANDROID_H__
+
+#include "android_npapi.h"
+
+// Define PLUGIN_DEBUG_LOCAL in an individual C++ file to enable for
+// that file only.
+
+// Define PLUGIN_DEBUG_GLOBAL to 1 to turn plug-in debug for all
+// Android plug-in code in this directory.
+#define PLUGIN_DEBUG_GLOBAL 0
+
+#if PLUGIN_DEBUG_GLOBAL || defined(PLUGIN_DEBUG_LOCAL)
+# define PLUGIN_LOG(FORMAT, ARGS...) do { anp_logPlugin(FORMAT, ## ARGS); } while(0)
+# define PLUGIN_LOG_EVENT(NPP, EVT, RET, TIME) do { anp_logPluginEvent(NPP, EVT, RET, TIME); } while(0)
+
+/* Logs the given character array and optional arguments. All log entries use
+ the DEBUG priority and use the same "webkit_plugin" log tag.
+ */
+void anp_logPlugin(const char format[], ...);
+/* Logs a user readable description of a plugin event. The relevant contents of
+ each event are logged, as well as the value returned by the plugin instance
+ and how long the instance took to process the event (in milliseconds).
+ */
+void anp_logPluginEvent(void* npp, const ANPEvent* event, int16_t returnVal, int elapsedTime);
+
+#else
+# define PLUGIN_LOG(A, B...) do { } while(0)
+# define PLUGIN_LOG_EVENT(NPP, EVT, RET, TIME) do { } while(0)
+
+#endif
+
+#endif // defined(PLUGIN_DEBUG_ANDROID_H__)
diff --git a/Source/WebKit/android/plugins/PluginTimer.cpp b/Source/WebKit/android/plugins/PluginTimer.cpp
new file mode 100644
index 0000000..dfa7272
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginTimer.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2008 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.
+ *
+ * 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 "PluginTimer.h"
+#include "RefPtr.h"
+
+namespace WebCore {
+
+ static uint32_t gTimerID;
+
+ PluginTimer::PluginTimer(PluginTimer** list, NPP instance, bool repeat,
+ void (*timerFunc)(NPP npp, uint32_t timerID))
+ : m_list(list),
+ m_instance(instance),
+ m_timerFunc(timerFunc),
+ m_repeat(repeat),
+ m_unscheduled(false)
+ {
+ m_timerID = ++gTimerID;
+
+ m_next = *list;
+ if (m_next) {
+ m_next->m_prev = this;
+ }
+ m_prev = 0;
+ *list = this;
+ relaxAdoptionRequirement();
+ }
+
+ PluginTimer::~PluginTimer()
+ {
+ if (m_next) {
+ m_next->m_prev = m_prev;
+ }
+ if (m_prev) {
+ m_prev->m_next = m_next;
+ } else {
+ *m_list = m_next;
+ }
+ }
+
+ void PluginTimer::fired()
+ {
+ // ensure the timer cannot be deleted until this method completes
+ RefPtr<PluginTimer> protector(this);
+
+ if (!m_unscheduled)
+ m_timerFunc(m_instance, m_timerID);
+
+ // remove the timer if it is a one-shot timer (!m_repeat) or if is a
+ // repeating timer that has been unscheduled. In either case we must
+ // ensure that the refcount is 2 or greater since the PluginTimerList
+ // could have been deleted by the timerFunc and we must ensure that we
+ // do not double delete.
+ if ((!m_repeat || m_unscheduled) && refCount() > 1)
+ deref(); // mark the timer for deletion as it is no longer needed
+ }
+
+ // may return null if timerID is not found
+ PluginTimer* PluginTimer::Find(PluginTimer* list, uint32_t timerID)
+ {
+ PluginTimer* curr = list;
+ while (curr) {
+ if (curr->m_timerID == timerID) {
+ break;
+ }
+ curr = curr->m_next;
+ }
+ return curr;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ PluginTimerList::~PluginTimerList()
+ {
+ PluginTimer* curr = m_list;
+ PluginTimer* next;
+ while (curr) {
+ next = curr->next();
+ curr->deref();
+ curr = next;
+ }
+ }
+
+ uint32_t PluginTimerList::schedule(NPP instance, uint32_t interval, bool repeat,
+ void (*proc)(NPP npp, uint32_t timerID))
+ {
+ PluginTimer* timer = new PluginTimer(&m_list, instance, repeat, proc);
+
+ double dinterval = interval * 0.001; // milliseconds to seconds
+ if (repeat) {
+ timer->startRepeating(dinterval);
+ } else {
+ timer->startOneShot(dinterval);
+ }
+ return timer->timerID();
+ }
+
+ void PluginTimerList::unschedule(NPP instance, uint32_t timerID)
+ {
+ // Although it looks like simply deleting the timer would work here
+ // (stop() will be executed by the dtor), we cannot do this, as
+ // the plugin can call us while we are in the fired() method,
+ // (when we execute the timerFunc callback). Deleting the object
+ // we are in would then be a rather bad move...
+ PluginTimer* timer = PluginTimer::Find(m_list, timerID);
+ if (timer)
+ timer->unschedule();
+ }
+
+} // namespace WebCore
diff --git a/Source/WebKit/android/plugins/PluginTimer.h b/Source/WebKit/android/plugins/PluginTimer.h
new file mode 100644
index 0000000..20c0816
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginTimer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2008 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.
+ *
+ * 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 PluginTimer_H
+#define PluginTimer_H
+
+#include "RefCounted.h"
+#include "Timer.h"
+#include "npapi.h"
+
+namespace WebCore {
+
+ class PluginTimerList;
+
+ class PluginTimer : public TimerBase, public RefCounted<PluginTimer> {
+ public:
+ PluginTimer(PluginTimer** list, NPP instance, bool repeat,
+ void (*proc)(NPP npp, uint32_t timerID));
+ virtual ~PluginTimer();
+
+ uint32_t timerID() const { return m_timerID; }
+
+ void unschedule() { m_unscheduled = true; }
+
+ static PluginTimer* Find(PluginTimer* list, uint32_t timerID);
+
+ private:
+ // override from TimerBase
+ virtual void fired();
+
+ PluginTimer* next() const { return m_next; }
+ friend class PluginTimerList;
+
+ PluginTimer** m_list;
+ PluginTimer* m_prev;
+ PluginTimer* m_next;
+ NPP m_instance;
+ void (*m_timerFunc)(NPP, uint32_t);
+ uint32_t m_timerID;
+ bool m_repeat;
+ bool m_unscheduled;
+ };
+
+ class PluginTimerList {
+ public:
+ PluginTimerList() : m_list(0) {}
+ ~PluginTimerList();
+
+ uint32_t schedule(NPP instance, uint32_t interval, bool repeat,
+ void (*proc)(NPP npp, uint32_t timerID));
+ void unschedule(NPP instance, uint32_t timerID);
+
+ private:
+ PluginTimer* m_list;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebKit/android/plugins/PluginViewBridgeAndroid.cpp b/Source/WebKit/android/plugins/PluginViewBridgeAndroid.cpp
new file mode 100644
index 0000000..2be9dc3
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginViewBridgeAndroid.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "PluginViewBridgeAndroid.h"
+
+namespace WebCore {
+
+ void PluginViewBridgeAndroid::draw(GraphicsContext* gc,
+ const IntRect& rect) {}
+
+ bool PluginViewBridgeAndroid::forPluginView() const {
+ return true;
+ }
+
+}
diff --git a/Source/WebKit/android/plugins/PluginViewBridgeAndroid.h b/Source/WebKit/android/plugins/PluginViewBridgeAndroid.h
new file mode 100644
index 0000000..5d16f46
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginViewBridgeAndroid.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Collabora Ltd. 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.
+ *
+ * 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 PluginViewBridgeAndroid_H
+#define PluginViewBridgeAndroid_H
+
+#include "PluginView.h"
+#include "WebCoreViewBridge.h"
+
+namespace WebCore {
+
+ // (Dummy for now) WebCoreViewBridge associated with a PluginView Widget.
+ class PluginViewBridgeAndroid : public WebCoreViewBridge {
+ public:
+ PluginViewBridgeAndroid() {}
+
+ // overrides
+ virtual void draw(GraphicsContext* gc, const IntRect& rect);
+ virtual bool forPluginView() const;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp b/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp
new file mode 100644
index 0000000..b8a10cc
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginWidgetAndroid.cpp
@@ -0,0 +1,690 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "PluginWidgetAndroid.h"
+
+#if ENABLE(TOUCH_EVENTS)
+#include "ChromeClient.h"
+#endif
+#include "Document.h"
+#include "Element.h"
+#include "Frame.h"
+#include "Page.h"
+#include "PluginPackage.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "ScrollView.h"
+#include "SkANP.h"
+#include "SkFlipPixelRef.h"
+#include "SkString.h"
+#include "SkTime.h"
+#include "WebViewCore.h"
+#include "android_graphics.h"
+#include <JNIUtility.h>
+
+// #define PLUGIN_DEBUG_LOCAL // controls the printing of log messages
+#define DEBUG_EVENTS 0 // logs event contents, return value, and processing time
+#define DEBUG_VISIBLE_RECTS 0 // temporary debug printfs and fixes
+
+// this include statement must follow the declaration of PLUGIN_DEBUG_LOCAL
+#include "PluginDebugAndroid.h"
+
+PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
+ : m_pluginView(view) {
+ m_flipPixelRef = NULL;
+ m_core = NULL;
+ m_drawingModel = kBitmap_ANPDrawingModel;
+ m_eventFlags = 0;
+ m_pluginWindow = NULL;
+ m_requestedVisibleRectCount = 0;
+ m_requestedVisibleRect.setEmpty();
+ m_visibleDocRect.setEmpty();
+ m_pluginBounds.setEmpty();
+ m_hasFocus = false;
+ m_isFullScreen = false;
+ m_visible = false;
+ m_cachedZoomLevel = 0;
+ m_embeddedView = NULL;
+ m_embeddedViewAttached = false;
+ m_acceptEvents = false;
+ m_isSurfaceClippedOut = false;
+ m_layer = 0;
+ m_powerState = kDefault_ANPPowerState;
+}
+
+PluginWidgetAndroid::~PluginWidgetAndroid() {
+ PLUGIN_LOG("%p Deleting Plugin", m_pluginView->instance());
+ m_acceptEvents = false;
+ if (m_core) {
+ setPowerState(kDefault_ANPPowerState);
+ m_core->removePlugin(this);
+ if (m_isFullScreen) {
+ exitFullScreen(true);
+ }
+ if (m_embeddedView) {
+ m_core->destroySurface(m_embeddedView);
+ }
+ }
+
+ // cleanup any remaining JNI References
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (m_embeddedView) {
+ env->DeleteGlobalRef(m_embeddedView);
+ }
+
+ SkSafeUnref(m_flipPixelRef);
+
+ if (m_layer)
+ m_layer->unref();
+}
+
+void PluginWidgetAndroid::init(android::WebViewCore* core) {
+ m_core = core;
+ m_core->addPlugin(this);
+ m_acceptEvents = true;
+ PLUGIN_LOG("%p Initialized Plugin", m_pluginView->instance());
+}
+
+static SkBitmap::Config computeConfig(bool isTransparent) {
+ return isTransparent ? SkBitmap::kARGB_8888_Config
+ : SkBitmap::kRGB_565_Config;
+}
+
+void PluginWidgetAndroid::setWindow(NPWindow* window, bool isTransparent) {
+
+ // store the reference locally for easy lookup
+ m_pluginWindow = window;
+
+ // make a copy of the previous bounds
+ SkIRect oldPluginBounds = m_pluginBounds;
+
+ // keep a local copy of the plugin bounds because the m_pluginWindow pointer
+ // gets updated values prior to this method being called
+ m_pluginBounds.set(m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->x + m_pluginWindow->width,
+ m_pluginWindow->y + m_pluginWindow->height);
+
+ PLUGIN_LOG("%p PluginBounds (%d,%d,%d,%d)", m_pluginView->instance(),
+ m_pluginBounds.fLeft, m_pluginBounds.fTop,
+ m_pluginBounds.fRight, m_pluginBounds.fBottom);
+
+ const bool boundsChanged = m_pluginBounds != oldPluginBounds;
+
+ //TODO hack to ensure that we grab the most recent screen dimensions and scale
+ ANPRectI screenCoords;
+ m_core->getVisibleScreen(screenCoords);
+ float scale = m_core->scale();
+ bool scaleChanged = m_cachedZoomLevel != scale;
+ setVisibleScreen(screenCoords, scale);
+
+ // if the scale changed then setVisibleScreen will call this function and
+ // this call will potentially fire a duplicate draw event
+ if (!scaleChanged) {
+ sendSizeAndVisibilityEvents(boundsChanged);
+ }
+ layoutSurface(boundsChanged);
+
+ if (m_drawingModel != kSurface_ANPDrawingModel) {
+ SkSafeUnref(m_flipPixelRef);
+ m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
+ window->width, window->height);
+ }
+}
+
+bool PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
+
+ if (model == kOpenGL_ANPDrawingModel && m_layer == 0) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject webview = m_core->getWebViewJavaObject();
+ jobject weakWebViewRef = 0;
+ if (webview)
+ weakWebViewRef = env->NewWeakGlobalRef(webview);
+ m_layer = new WebCore::MediaLayer(weakWebViewRef);
+ }
+ else if (model != kOpenGL_ANPDrawingModel && m_layer != 0) {
+ m_layer->unref();
+ m_layer = 0;
+ }
+
+ if (m_drawingModel != model) {
+ // Trigger layer computation in RenderLayerCompositor
+ m_pluginView->getElement()->setNeedsStyleRecalc(SyntheticStyleChange);
+ }
+
+ m_drawingModel = model;
+ return true;
+}
+
+// returned rect is in the page coordinate
+bool PluginWidgetAndroid::isDirty(SkIRect* rect) const {
+ // nothing to report if we haven't had setWindow() called yet
+ if (NULL == m_flipPixelRef) {
+ return false;
+ }
+
+ const SkRegion& dirty = m_flipPixelRef->dirtyRgn();
+ if (dirty.isEmpty()) {
+ return false;
+ } else {
+ if (rect) {
+ *rect = dirty.getBounds();
+ rect->offset(m_pluginWindow->x, m_pluginWindow->y);
+ }
+ return true;
+ }
+}
+
+void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
+ bool signalRedraw) {
+ // nothing to do if we haven't had setWindow() called yet. m_flipPixelRef
+ // will also be null if this is a Surface model.
+ if (NULL == m_flipPixelRef) {
+ return;
+ }
+
+ m_flipPixelRef->inval(rect);
+
+ if (signalRedraw && m_flipPixelRef->isDirty()) {
+ m_core->invalPlugin(this);
+ }
+}
+
+void PluginWidgetAndroid::viewInvalidate() {
+ WebCore::IntRect rect(m_pluginBounds.fLeft, m_pluginBounds.fTop,
+ m_pluginBounds.width(), m_pluginBounds.height());
+ m_core->viewInvalidate(rect);
+}
+
+void PluginWidgetAndroid::draw(SkCanvas* canvas) {
+ if (NULL == m_flipPixelRef || !m_flipPixelRef->isDirty()) {
+ return;
+ }
+
+ SkAutoFlipUpdate update(m_flipPixelRef);
+ const SkBitmap& bitmap = update.bitmap();
+ const SkRegion& dirty = update.dirty();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kDraw_ANPEventType);
+
+ event.data.draw.model = m_drawingModel;
+ SkANP::SetRect(&event.data.draw.clip, dirty.getBounds());
+
+ switch (m_drawingModel) {
+ case kBitmap_ANPDrawingModel: {
+ WebCore::PluginPackage* pkg = m_pluginView->plugin();
+ NPP instance = m_pluginView->instance();
+
+ if (SkANP::SetBitmap(&event.data.draw.data.bitmap,
+ bitmap) &&
+ pkg->pluginFuncs()->event(instance, &event)) {
+
+ if (canvas && m_pluginWindow) {
+ SkBitmap bm(bitmap);
+ bm.setPixelRef(m_flipPixelRef);
+ canvas->drawBitmap(bm, 0, 0);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void PluginWidgetAndroid::setSurfaceClip(const SkIRect& clip) {
+
+ if (m_drawingModel != kSurface_ANPDrawingModel)
+ return;
+
+ /* don't display surfaces that are either entirely clipped or only 1x1 in
+ size. It appears that when an element is absolutely positioned and has
+ been completely clipped in CSS that webkit still sends a clip of 1x1.
+ */
+ bool clippedOut = (clip.width() <= 1 && clip.height() <= 1);
+ if(clippedOut != m_isSurfaceClippedOut) {
+ m_isSurfaceClippedOut = clippedOut;
+ layoutSurface();
+ }
+}
+
+void PluginWidgetAndroid::layoutSurface(bool pluginBoundsChanged) {
+
+ if (m_drawingModel != kSurface_ANPDrawingModel)
+ return;
+ if (!m_pluginWindow)
+ return;
+
+
+ bool displayPlugin = m_pluginView->isVisible() && !m_isSurfaceClippedOut;
+ PLUGIN_LOG("%p DisplayPlugin[%d] visible=[%d] clipped=[%d]",
+ m_pluginView->instance(), displayPlugin,
+ m_pluginView->isVisible(), m_isSurfaceClippedOut);
+
+ // if the surface does not exist then create a new surface
+ if (!m_embeddedView && displayPlugin) {
+
+ WebCore::PluginPackage* pkg = m_pluginView->plugin();
+ NPP instance = m_pluginView->instance();
+
+ jobject pluginSurface;
+ pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
+ static_cast<void*>(&pluginSurface));
+
+ jobject tempObj = m_core->addSurface(pluginSurface,
+ m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->width, m_pluginWindow->height);
+
+ if (tempObj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ m_embeddedView = env->NewGlobalRef(tempObj);
+ m_embeddedViewAttached = true;
+ }
+ // if the view is unattached but visible then attach it
+ } else if (m_embeddedView && !m_embeddedViewAttached && displayPlugin && !m_isFullScreen) {
+ m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->width, m_pluginWindow->height);
+ m_embeddedViewAttached = true;
+ // if the view is attached but invisible then remove it
+ } else if (m_embeddedView && m_embeddedViewAttached && !displayPlugin) {
+ m_core->destroySurface(m_embeddedView);
+ m_embeddedViewAttached = false;
+ // if the plugin's bounds have changed and it's visible then update it
+ } else if (pluginBoundsChanged && displayPlugin && !m_isFullScreen) {
+ m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->width, m_pluginWindow->height);
+
+ }
+}
+
+int16_t PluginWidgetAndroid::sendEvent(const ANPEvent& evt) {
+ if (!m_acceptEvents)
+ return 0;
+ WebCore::PluginPackage* pkg = m_pluginView->plugin();
+ NPP instance = m_pluginView->instance();
+ // "missing" plugins won't have these
+ if (pkg && instance) {
+
+ // if the plugin is gaining focus then update our state now to allow
+ // the plugin's event handler to perform actions that require focus
+ if (evt.eventType == kLifecycle_ANPEventType &&
+ evt.data.lifecycle.action == kGainFocus_ANPLifecycleAction) {
+ m_hasFocus = true;
+ }
+
+#if DEBUG_EVENTS
+ SkMSec startTime = SkTime::GetMSecs();
+#endif
+
+ // make a localCopy since the actual plugin may not respect its constness,
+ // and so we don't want our caller to have its param modified
+ ANPEvent localCopy = evt;
+ int16_t result = pkg->pluginFuncs()->event(instance, &localCopy);
+
+#if DEBUG_EVENTS
+ SkMSec endTime = SkTime::GetMSecs();
+ PLUGIN_LOG_EVENT(instance, &evt, result, endTime - startTime);
+#endif
+
+ // if the plugin is losing focus then delay the update of our state
+ // until after we notify the plugin and allow them to perform actions
+ // that may require focus
+ if (evt.eventType == kLifecycle_ANPEventType &&
+ evt.data.lifecycle.action == kLoseFocus_ANPLifecycleAction) {
+ m_hasFocus = false;
+ }
+
+ return result;
+ }
+ return 0;
+}
+
+void PluginWidgetAndroid::updateEventFlags(ANPEventFlags flags) {
+
+ // if there are no differences then immediately return
+ if (m_eventFlags == flags) {
+ return;
+ }
+
+ Document* doc = m_pluginView->parentFrame()->document();
+#if ENABLE(TOUCH_EVENTS)
+ if((m_eventFlags ^ flags) & kTouch_ANPEventFlag) {
+ if (flags & kTouch_ANPEventFlag)
+ doc->addListenerTypeIfNeeded(eventNames().touchstartEvent);
+ }
+#endif
+
+ m_eventFlags = flags;
+}
+
+bool PluginWidgetAndroid::isAcceptingEvent(ANPEventFlag flag) {
+ return m_eventFlags & flag;
+}
+
+void PluginWidgetAndroid::sendSizeAndVisibilityEvents(const bool updateDimensions) {
+ // TODO update the bitmap size based on the zoom? (for kBitmap_ANPDrawingModel)
+
+ const float zoomLevel = m_core->scale();
+
+ // notify the plugin of the new size
+ if (m_drawingModel == kOpenGL_ANPDrawingModel && updateDimensions && m_pluginWindow) {
+ PLUGIN_LOG("%s (%d,%d)[%f]", __FUNCTION__, m_pluginWindow->width,
+ m_pluginWindow->height, zoomLevel);
+ ANPEvent event;
+ SkANP::InitEvent(&event, kDraw_ANPEventType);
+ event.data.draw.model = kOpenGL_ANPDrawingModel;
+ event.data.draw.data.surface.width = m_pluginWindow->width * zoomLevel;
+ event.data.draw.data.surface.height = m_pluginWindow->height * zoomLevel;
+ sendEvent(event);
+ }
+
+ bool visible = SkIRect::Intersects(m_visibleDocRect, m_pluginBounds);
+ if(m_visible != visible) {
+
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%p changeVisiblity[%d] pluginBounds(%d,%d,%d,%d)",
+ m_pluginView->instance(), visible,
+ m_pluginBounds.fLeft, m_pluginBounds.fTop,
+ m_pluginBounds.fRight, m_pluginBounds.fBottom);
+#endif
+
+ // change the visibility
+ m_visible = visible;
+ // send the event
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = visible ? kOnScreen_ANPLifecycleAction
+ : kOffScreen_ANPLifecycleAction;
+ sendEvent(event);
+ }
+}
+
+void PluginWidgetAndroid::setVisibleScreen(const ANPRectI& visibleDocRect, float zoom) {
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%s (%d,%d,%d,%d)[%f]", __FUNCTION__, visibleDocRect.left,
+ visibleDocRect.top, visibleDocRect.right,
+ visibleDocRect.bottom, zoom);
+#endif
+ int oldScreenW = m_visibleDocRect.width();
+ int oldScreenH = m_visibleDocRect.height();
+
+ const bool zoomChanged = m_cachedZoomLevel != zoom;
+
+ // make local copies of the parameters
+ m_cachedZoomLevel = zoom;
+ m_visibleDocRect.set(visibleDocRect.left,
+ visibleDocRect.top,
+ visibleDocRect.right,
+ visibleDocRect.bottom);
+
+ int newScreenW = m_visibleDocRect.width();
+ int newScreenH = m_visibleDocRect.height();
+
+ // if the screen dimensions have changed by more than 5 pixels in either
+ // direction then recompute the plugin's visible rectangle
+ if (abs(oldScreenW - newScreenW) > 5 || abs(oldScreenH - newScreenH) > 5) {
+ PLUGIN_LOG("%s VisibleDoc old=[%d,%d] new=[%d,%d] ", __FUNCTION__,
+ oldScreenW, oldScreenH, newScreenW, newScreenH);
+ computeVisiblePluginRect();
+ }
+
+ sendSizeAndVisibilityEvents(zoomChanged);
+}
+
+ANPRectI PluginWidgetAndroid::visibleRect() {
+
+ SkIRect visibleRect;
+ visibleRect.setEmpty();
+
+ // compute the interesection of the visible screen and the plugin
+ bool visible = visibleRect.intersect(m_visibleDocRect, m_pluginBounds);
+ if (visible) {
+ // convert from absolute coordinates to the plugin's relative coordinates
+ visibleRect.offset(-m_pluginBounds.fLeft, -m_pluginBounds.fTop);
+ }
+
+ // convert from SkRect to ANPRect
+ ANPRectI result;
+ memcpy(&result, &visibleRect, sizeof(ANPRectI));
+ return result;
+}
+
+void PluginWidgetAndroid::setVisibleRects(const ANPRectI rects[], int32_t count) {
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%s count=%d", __FUNCTION__, count);
+#endif
+ // ensure the count does not exceed our allocated space
+ if (count > MAX_REQUESTED_RECTS)
+ count = MAX_REQUESTED_RECTS;
+
+ // store the values in member variables
+ m_requestedVisibleRectCount = count;
+ memcpy(m_requestedVisibleRects, rects, count * sizeof(rects[0]));
+
+#if DEBUG_VISIBLE_RECTS // FIXME: this fixes bad data from the plugin
+ // take it out once plugin supplies better data
+ for (int index = 0; index < count; index++) {
+ PLUGIN_LOG("%s [%d](%d,%d,%d,%d)", __FUNCTION__, index,
+ m_requestedVisibleRects[index].left,
+ m_requestedVisibleRects[index].top,
+ m_requestedVisibleRects[index].right,
+ m_requestedVisibleRects[index].bottom);
+ if (m_requestedVisibleRects[index].left ==
+ m_requestedVisibleRects[index].right) {
+ m_requestedVisibleRects[index].right += 1;
+ }
+ if (m_requestedVisibleRects[index].top ==
+ m_requestedVisibleRects[index].bottom) {
+ m_requestedVisibleRects[index].bottom += 1;
+ }
+ }
+#endif
+ computeVisiblePluginRect();
+}
+
+void PluginWidgetAndroid::computeVisiblePluginRect() {
+
+ // ensure the visibleDocRect has been set (i.e. not equal to zero)
+ if (m_visibleDocRect.isEmpty() || !m_pluginWindow || m_requestedVisibleRectCount < 1)
+ return;
+
+ // create a rect that will contain as many of the rects that will fit on screen
+ SkIRect visibleRect;
+ visibleRect.setEmpty();
+
+ for (int counter = 0; counter < m_requestedVisibleRectCount; counter++) {
+
+ ANPRectI* rect = &m_requestedVisibleRects[counter];
+
+ // create skia rect for easier manipulation and convert it to page coordinates
+ SkIRect pluginRect;
+ pluginRect.set(rect->left, rect->top, rect->right, rect->bottom);
+ pluginRect.offset(m_pluginWindow->x, m_pluginWindow->y);
+
+ // ensure the rect falls within the plugin's bounds
+ if (!m_pluginBounds.contains(pluginRect)) {
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%s (%d,%d,%d,%d) !contain (%d,%d,%d,%d)", __FUNCTION__,
+ m_pluginBounds.fLeft, m_pluginBounds.fTop,
+ m_pluginBounds.fRight, m_pluginBounds.fBottom,
+ pluginRect.fLeft, pluginRect.fTop,
+ pluginRect.fRight, pluginRect.fBottom);
+ // assume that the desired outcome is to clamp to the container
+ if (pluginRect.intersect(m_pluginBounds)) {
+ visibleRect = pluginRect;
+ }
+#endif
+ continue;
+ }
+
+ // combine this new rect with the higher priority rects
+ pluginRect.join(visibleRect);
+
+ // check to see if the new rect could be made to fit within the screen
+ // bounds. If this is the highest priority rect then attempt to center
+ // even if it doesn't fit on the screen.
+ if (counter > 0 && (m_visibleDocRect.width() < pluginRect.width() ||
+ m_visibleDocRect.height() < pluginRect.height()))
+ break;
+
+ // set the new visible rect
+ visibleRect = pluginRect;
+ }
+
+ m_requestedVisibleRect = visibleRect;
+ scrollToVisiblePluginRect();
+}
+
+void PluginWidgetAndroid::scrollToVisiblePluginRect() {
+
+ if (!m_hasFocus || m_requestedVisibleRect.isEmpty() || m_visibleDocRect.isEmpty()) {
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%s call m_hasFocus=%d m_requestedVisibleRect.isEmpty()=%d"
+ " m_visibleDocRect.isEmpty()=%d", __FUNCTION__, m_hasFocus,
+ m_requestedVisibleRect.isEmpty(), m_visibleDocRect.isEmpty());
+#endif
+ return;
+ }
+ // if the entire rect is already visible then we don't need to scroll
+ if (m_visibleDocRect.contains(m_requestedVisibleRect))
+ return;
+
+ // find the center of the visibleRect in document coordinates
+ int rectCenterX = m_requestedVisibleRect.fLeft + m_requestedVisibleRect.width()/2;
+ int rectCenterY = m_requestedVisibleRect.fTop + m_requestedVisibleRect.height()/2;
+
+ // find document coordinates for center of the visible screen
+ int visibleDocCenterX = m_visibleDocRect.fLeft + m_visibleDocRect.width()/2;
+ int visibleDocCenterY = m_visibleDocRect.fTop + m_visibleDocRect.height()/2;
+
+ //compute the delta of the two points and scale to screen coordinates
+ int deltaX = rectCenterX - visibleDocCenterX;
+ int deltaY = rectCenterY - visibleDocCenterY;
+
+ ScrollView* scrollView = m_pluginView->parent();
+ android::WebViewCore* core = android::WebViewCore::getWebViewCore(scrollView);
+#if DEBUG_VISIBLE_RECTS
+ PLUGIN_LOG("%s call scrollBy (%d,%d)", __FUNCTION__, deltaX, deltaY);
+#endif
+ core->scrollTo(rectCenterX, rectCenterY, true);
+}
+
+void PluginWidgetAndroid::requestFullScreen() {
+ if (m_isFullScreen) {
+ return;
+ }
+
+ if (!m_embeddedView && m_drawingModel == kOpenGL_ANPDrawingModel) {
+ WebCore::PluginPackage* pkg = m_pluginView->plugin();
+ NPP instance = m_pluginView->instance();
+
+ jobject pluginSurface;
+ pkg->pluginFuncs()->getvalue(instance, kJavaSurface_ANPGetValue,
+ static_cast<void*>(&pluginSurface));
+
+ // create the surface, but do not add it to the view hierarchy
+ jobject tempObj = m_core->createSurface(pluginSurface);
+
+ if (tempObj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ m_embeddedView = env->NewGlobalRef(tempObj);
+ m_embeddedViewAttached = false;
+ }
+ }
+
+ if (!m_embeddedView) {
+ return;
+ }
+
+ // send event to notify plugin of full screen change
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kEnterFullScreen_ANPLifecycleAction;
+ sendEvent(event);
+
+ // remove the embedded surface from the view hierarchy
+ if (m_drawingModel != kOpenGL_ANPDrawingModel)
+ m_core->destroySurface(m_embeddedView);
+
+ // add the full screen view
+ m_core->showFullScreenPlugin(m_embeddedView, m_pluginView->instance());
+ m_isFullScreen = true;
+}
+
+void PluginWidgetAndroid::exitFullScreen(bool pluginInitiated) {
+ if (!m_isFullScreen || !m_embeddedView) {
+ return;
+ }
+
+ // remove the full screen surface from the view hierarchy
+ if (pluginInitiated) {
+ m_core->hideFullScreenPlugin();
+ }
+
+ // add the embedded view back
+ if (m_drawingModel != kOpenGL_ANPDrawingModel)
+ m_core->updateSurface(m_embeddedView, m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->width, m_pluginWindow->height);
+
+ // send event to notify plugin of full screen change
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kExitFullScreen_ANPLifecycleAction;
+ sendEvent(event);
+
+ m_isFullScreen = false;
+}
+
+void PluginWidgetAndroid::requestCenterFitZoom() {
+ m_core->centerFitRect(m_pluginWindow->x, m_pluginWindow->y,
+ m_pluginWindow->width, m_pluginWindow->height);
+}
+
+void PluginWidgetAndroid::setPowerState(ANPPowerState powerState) {
+ if(m_powerState == powerState)
+ return;
+
+ // cleanup the old power state
+ switch (m_powerState) {
+ case kDefault_ANPPowerState:
+ break;
+ case kScreenOn_ANPPowerState:
+ m_core->keepScreenOn(false);
+ break;
+ }
+
+ // setup the new power state
+ switch (powerState) {
+ case kDefault_ANPPowerState:
+ break;
+ case kScreenOn_ANPPowerState:
+ m_core->keepScreenOn(true);
+ break;
+ }
+
+ m_powerState = powerState;
+}
+
diff --git a/Source/WebKit/android/plugins/PluginWidgetAndroid.h b/Source/WebKit/android/plugins/PluginWidgetAndroid.h
new file mode 100644
index 0000000..5d586b1
--- /dev/null
+++ b/Source/WebKit/android/plugins/PluginWidgetAndroid.h
@@ -0,0 +1,219 @@
+/*
+ * 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 PluginWidgetAndroid_H
+#define PluginWidgetAndroid_H
+
+#include "android_npapi.h"
+#include "ANPSystem_npapi.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "MediaLayer.h"
+#include "SkRect.h"
+#include <jni.h>
+
+namespace WebCore {
+ class PluginView;
+}
+
+namespace android {
+ class PluginSurface;
+ class WebViewCore;
+}
+
+class SkCanvas;
+class SkFlipPixelRef;
+
+/*
+ This is our extended state in a PluginView. This object is created and
+ kept insync with the PluginView, but is also available to WebViewCore
+ to allow its draw() method to be called from outside of the PluginView.
+ */
+struct PluginWidgetAndroid {
+ // initialize with our host pluginview. This will delete us when it is
+ // destroyed.
+ PluginWidgetAndroid(WebCore::PluginView* view);
+ ~PluginWidgetAndroid();
+
+ WebCore::PluginView* pluginView() const { return m_pluginView; }
+
+ // Needed by PluginSurface to manage the java SurfaceView.
+ android::WebViewCore* webViewCore() const { return m_core; }
+
+ /* Can't determine our core at construction time, so PluginView calls this
+ as soon as it has a parent.
+ */
+ void init(android::WebViewCore*);
+ /* Called each time the PluginView gets a new size or position.
+ */
+ void setWindow(NPWindow* window, bool isTransparent);
+
+ /* Called whenever the plugin itself requests a new drawing model. If the
+ hardware does not support the requested model then false is returned,
+ otherwise true is returned.
+ */
+ bool setDrawingModel(ANPDrawingModel);
+
+ /* Called to check if the plugin is running in "windowed" mode (i.e. surface
+ view).
+ */
+ bool isSurfaceDrawingModel() const { return kSurface_ANPDrawingModel == m_drawingModel; }
+
+ bool isOpenGLDrawingModel() const { return kOpenGL_ANPDrawingModel == m_drawingModel; }
+
+ /* Returns true (and optionally updates rect with the dirty bounds in the
+ page coordinate) if the plugin has invalidate us.
+ */
+ bool isDirty(SkIRect* dirtyBounds = NULL) const;
+ /* Called by PluginView to invalidate a portion of the plugin area (in
+ local plugin coordinates). If signalRedraw is true, this also triggers
+ a subsequent call to draw(NULL).
+ */
+ void inval(const WebCore::IntRect&, bool signalRedraw);
+
+ /* Called to draw into the plugin's bitmap. If canvas is non-null, the
+ bitmap itself is then drawn into the canvas.
+ */
+ void draw(SkCanvas* canvas = NULL);
+
+ /* Send this event to the plugin instance. A non-zero value will be
+ returned if the plugin handled the event.
+ */
+ int16_t sendEvent(const ANPEvent&);
+
+ /* Update the plugins event flags. If a flag is set to true then the plugin
+ wants to be notified of events of this type.
+ */
+ void updateEventFlags(ANPEventFlags);
+
+ /* Called to check if a plugin wants to accept a given event type. It
+ returns true if the plugin wants the events and false otherwise.
+ */
+ bool isAcceptingEvent(ANPEventFlag);
+
+ /* Notify the plugin of the currently visible screen coordinates (document
+ space) and the current zoom level.
+ */
+ void setVisibleScreen(const ANPRectI& visibleScreenRect, float zoom);
+
+ /** Returns a rectangle representing the visible area of the plugin on
+ screen. The coordinates are relative to the size of the plugin in the
+ document and will not be negative or exceed the plugin's size.
+ */
+ ANPRectI visibleRect();
+
+ /** Registers a set of rectangles that the plugin would like to keep on
+ screen. The rectangles are listed in order of priority with the highest
+ priority rectangle in location rects[0]. The browser will attempt to keep
+ as many of the rectangles on screen as possible and will scroll them into
+ view in response to the invocation of this method and other various events.
+ The count specifies how many rectangles are in the array. If the count is
+ zero it signals the plugin that any existing rectangles should be cleared
+ and no rectangles will be tracked.
+ */
+ void setVisibleRects(const ANPRectI rects[], int32_t count);
+
+ /** Called when a plugin wishes to enter into full screen mode. It invokes
+ the plugin's Java class (defined in the plugin's apk manifest), which is
+ called asynchronously and provides a View to be displayed full screen.
+ */
+ void requestFullScreen();
+
+ /** Called when a plugin wishes to exit from full screen mode. As a result,
+ the plugin's full-screen view is discarded by the view system. It is also
+ called in order to notify the native code that the browser has discarded
+ the view.
+ */
+ void exitFullScreen(bool pluginInitiated);
+
+ bool inFullScreen() { return m_isFullScreen; }
+
+ /** Called to check if a plugin currently has document focus, which is
+ required for certain operations (e.g. show/hide keyboard). It returns
+ true if the plugin currently has focus and false otherwise.
+ */
+ bool hasFocus() const { return m_hasFocus; }
+
+ /** Called to ensure the surface is being correctly displayed within the
+ view hierarchy. For instance, if the visibility of the plugin has
+ changed then we need to ensure the surface is added or removed from the
+ view system.
+ */
+ void layoutSurface(bool pluginBoundsChanged = false);
+
+ /** send the surface the currently visible portion of the plugin. This is not
+ the portion of the plugin visible on the screen but rather the portion of
+ the plugin that is not obscured by other HTML content.
+ */
+ void setSurfaceClip(const SkIRect& clip);
+
+ /** Called when a plugin wishes to be zoomed and centered in the current view.
+ */
+ void requestCenterFitZoom();
+
+ WebCore::MediaLayer* getLayer() const { return m_layer; }
+
+ void setPowerState(ANPPowerState powerState);
+
+ void viewInvalidate();
+
+private:
+ void computeVisiblePluginRect();
+ void scrollToVisiblePluginRect();
+ void sendSizeAndVisibilityEvents(const bool updateDimensions);
+
+ WebCore::MediaLayer* m_layer;
+
+ WebCore::PluginView* m_pluginView;
+ android::WebViewCore* m_core;
+ SkFlipPixelRef* m_flipPixelRef;
+ ANPDrawingModel m_drawingModel;
+ ANPEventFlags m_eventFlags;
+ NPWindow* m_pluginWindow;
+ SkIRect m_pluginBounds; // relative to the page
+ SkIRect m_visibleDocRect; // relative to the page
+ SkIRect m_requestedVisibleRect; // relative to the page
+ bool m_hasFocus;
+ bool m_isFullScreen;
+ bool m_visible;
+ float m_cachedZoomLevel; // used for comparison only
+ jobject m_embeddedView;
+ bool m_embeddedViewAttached;
+ bool m_acceptEvents;
+ bool m_isSurfaceClippedOut;
+ ANPPowerState m_powerState;
+
+ /* We limit the number of rectangles to minimize storage and ensure adequate
+ speed.
+ */
+ enum {
+ MAX_REQUESTED_RECTS = 5,
+ };
+
+ ANPRectI m_requestedVisibleRects[MAX_REQUESTED_RECTS];
+ int32_t m_requestedVisibleRectCount;
+};
+
+#endif
diff --git a/Source/WebKit/android/plugins/SkANP.cpp b/Source/WebKit/android/plugins/SkANP.cpp
new file mode 100644
index 0000000..720387d
--- /dev/null
+++ b/Source/WebKit/android/plugins/SkANP.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+#include <wtf/CurrentTime.h>
+
+SkRect* SkANP::SetRect(SkRect* dst, const ANPRectF& src) {
+ dst->set(SkFloatToScalar(src.left),
+ SkFloatToScalar(src.top),
+ SkFloatToScalar(src.right),
+ SkFloatToScalar(src.bottom));
+ return dst;
+}
+
+SkIRect* SkANP::SetRect(SkIRect* dst, const ANPRectI& src) {
+ dst->set(src.left, src.top, src.right, src.bottom);
+ return dst;
+}
+
+ANPRectI* SkANP::SetRect(ANPRectI* dst, const SkIRect& src) {
+ dst->left = src.fLeft;
+ dst->top = src.fTop;
+ dst->right = src.fRight;
+ dst->bottom = src.fBottom;
+ return dst;
+}
+
+ANPRectF* SkANP::SetRect(ANPRectF* dst, const SkRect& src) {
+ dst->left = SkScalarToFloat(src.fLeft);
+ dst->top = SkScalarToFloat(src.fTop);
+ dst->right = SkScalarToFloat(src.fRight);
+ dst->bottom = SkScalarToFloat(src.fBottom);
+ return dst;
+}
+
+SkBitmap* SkANP::SetBitmap(SkBitmap* dst, const ANPBitmap& src) {
+ SkBitmap::Config config = SkBitmap::kNo_Config;
+
+ switch (src.format) {
+ case kRGBA_8888_ANPBitmapFormat:
+ config = SkBitmap::kARGB_8888_Config;
+ break;
+ case kRGB_565_ANPBitmapFormat:
+ config = SkBitmap::kRGB_565_Config;
+ break;
+ default:
+ break;
+ }
+
+ dst->setConfig(config, src.width, src.height, src.rowBytes);
+ dst->setPixels(src.baseAddr);
+ return dst;
+}
+
+bool SkANP::SetBitmap(ANPBitmap* dst, const SkBitmap& src) {
+ if (!(dst->baseAddr = src.getPixels())) {
+ SkDebugf("SkANP::SetBitmap - getPixels() returned null\n");
+ return false;
+ }
+
+ switch (src.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ dst->format = kRGBA_8888_ANPBitmapFormat;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ dst->format = kRGB_565_ANPBitmapFormat;
+ break;
+ default:
+ SkDebugf("SkANP::SetBitmap - unsupported src.config %d\n", src.config());
+ return false;
+ }
+
+ dst->width = src.width();
+ dst->height = src.height();
+ dst->rowBytes = src.rowBytes();
+ return true;
+}
+
+void SkANP::InitEvent(ANPEvent* event, ANPEventType et) {
+ event->inSize = sizeof(ANPEvent);
+ event->eventType = et;
+}
diff --git a/Source/WebKit/android/plugins/SkANP.h b/Source/WebKit/android/plugins/SkANP.h
new file mode 100644
index 0000000..5c2a936
--- /dev/null
+++ b/Source/WebKit/android/plugins/SkANP.h
@@ -0,0 +1,79 @@
+/*
+ * 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 SkANP_DEFINED
+#define SkANP_DEFINED
+
+#include "android_npapi.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkTypeface.h"
+
+struct ANPMatrix : SkMatrix {
+};
+
+struct ANPPath : SkPath {
+};
+
+struct ANPPaint : SkPaint {
+};
+
+struct ANPTypeface : SkTypeface {
+};
+
+struct ANPCanvas {
+ SkCanvas* skcanvas;
+
+ // draw into the specified bitmap
+ explicit ANPCanvas(const SkBitmap& bm) {
+ skcanvas = new SkCanvas(bm);
+ }
+
+ // redirect all drawing to the specific SkCanvas
+ explicit ANPCanvas(SkCanvas* other) {
+ skcanvas = other;
+ skcanvas->ref();
+ }
+
+ ~ANPCanvas() {
+ skcanvas->unref();
+ }
+};
+
+class SkANP {
+public:
+ static SkRect* SetRect(SkRect* dst, const ANPRectF& src);
+ static SkIRect* SetRect(SkIRect* dst, const ANPRectI& src);
+ static ANPRectI* SetRect(ANPRectI* dst, const SkIRect& src);
+ static ANPRectF* SetRect(ANPRectF* dst, const SkRect& src);
+ static SkBitmap* SetBitmap(SkBitmap* dst, const ANPBitmap& src);
+ static bool SetBitmap(ANPBitmap* dst, const SkBitmap& src);
+
+ static void InitEvent(ANPEvent* event, ANPEventType et);
+};
+
+#endif
diff --git a/Source/WebKit/android/plugins/SurfaceCallback.h b/Source/WebKit/android/plugins/SurfaceCallback.h
new file mode 100644
index 0000000..945fc3f
--- /dev/null
+++ b/Source/WebKit/android/plugins/SurfaceCallback.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (C) 2008 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.
+ *
+ * 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 SurfaceCallback_H
+#define SurfaceCallback_H
+
+namespace android {
+
+ class SurfaceCallback {
+ public:
+ virtual ~SurfaceCallback() {}
+ virtual void surfaceCreated() = 0;
+ virtual void surfaceChanged(int format, int width, int height) = 0;
+ virtual void surfaceDestroyed() = 0;
+ };
+
+} // namespace android
+
+#endif
diff --git a/Source/WebKit/android/plugins/android_npapi.h b/Source/WebKit/android/plugins/android_npapi.h
new file mode 100644
index 0000000..b0c3765
--- /dev/null
+++ b/Source/WebKit/android/plugins/android_npapi.h
@@ -0,0 +1,987 @@
+/*
+ * 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.
+ */
+
+/* Defines the android-specific types and functions as part of npapi
+
+ In particular, defines the window and event types that are passed to
+ NPN_GetValue, NPP_SetWindow and NPP_HandleEvent.
+
+ To minimize what native libraries the plugin links against, some
+ functionality is provided via function-ptrs (e.g. time, sound)
+ */
+
+#ifndef android_npapi_H
+#define android_npapi_H
+
+#include <stdint.h>
+
+#include "npapi.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// General types
+
+enum ANPBitmapFormats {
+ kUnknown_ANPBitmapFormat = 0,
+ kRGBA_8888_ANPBitmapFormat = 1,
+ kRGB_565_ANPBitmapFormat = 2
+};
+typedef int32_t ANPBitmapFormat;
+
+struct ANPPixelPacking {
+ uint8_t AShift;
+ uint8_t ABits;
+ uint8_t RShift;
+ uint8_t RBits;
+ uint8_t GShift;
+ uint8_t GBits;
+ uint8_t BShift;
+ uint8_t BBits;
+};
+
+struct ANPBitmap {
+ void* baseAddr;
+ ANPBitmapFormat format;
+ int32_t width;
+ int32_t height;
+ int32_t rowBytes;
+};
+
+struct ANPRectF {
+ float left;
+ float top;
+ float right;
+ float bottom;
+};
+
+struct ANPRectI {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+};
+
+struct ANPCanvas;
+struct ANPMatrix;
+struct ANPPaint;
+struct ANPPath;
+struct ANPRegion;
+struct ANPTypeface;
+
+enum ANPMatrixFlags {
+ kIdentity_ANPMatrixFlag = 0,
+ kTranslate_ANPMatrixFlag = 0x01,
+ kScale_ANPMatrixFlag = 0x02,
+ kAffine_ANPMatrixFlag = 0x04,
+ kPerspective_ANPMatrixFlag = 0x08,
+};
+typedef uint32_t ANPMatrixFlag;
+
+///////////////////////////////////////////////////////////////////////////////
+// NPN_GetValue
+
+/** queries for a specific ANPInterface.
+
+ Maybe called with NULL for the NPP instance
+
+ NPN_GetValue(inst, interface_enum, ANPInterface*)
+ */
+#define kLogInterfaceV0_ANPGetValue ((NPNVariable)1000)
+#define kAudioTrackInterfaceV0_ANPGetValue ((NPNVariable)1001)
+#define kCanvasInterfaceV0_ANPGetValue ((NPNVariable)1002)
+#define kMatrixInterfaceV0_ANPGetValue ((NPNVariable)1003)
+#define kPaintInterfaceV0_ANPGetValue ((NPNVariable)1004)
+#define kPathInterfaceV0_ANPGetValue ((NPNVariable)1005)
+#define kTypefaceInterfaceV0_ANPGetValue ((NPNVariable)1006)
+#define kWindowInterfaceV0_ANPGetValue ((NPNVariable)1007)
+#define kBitmapInterfaceV0_ANPGetValue ((NPNVariable)1008)
+#define kSurfaceInterfaceV0_ANPGetValue ((NPNVariable)1009)
+#define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010)
+#define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011)
+
+#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012)
+#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013)
+#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014)
+#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015)
+#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016)
+
+#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017)
+
+/** queries for the drawing models supported on this device.
+
+ NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits)
+ */
+#define kSupportedDrawingModel_ANPGetValue ((NPNVariable)2000)
+
+/** queries for the context (android.content.Context) of the plugin. If no
+ instance is specified the application's context is returned. If the instance
+ is given then the context returned is identical to the context used to
+ create the webview in which that instance resides.
+
+ NOTE: Holding onto a non-application context after your instance has been
+ destroyed will cause a memory leak. Refer to the android documentation to
+ determine what context is best suited for your particular scenario.
+
+ NPN_GetValue(inst, kJavaContext_ANPGetValue, jobject context)
+ */
+#define kJavaContext_ANPGetValue ((NPNVariable)2001)
+
+///////////////////////////////////////////////////////////////////////////////
+// NPN_SetValue
+
+/** Request to set the drawing model. SetValue will return false if the drawing
+ model is not supported or has insufficient information for configuration.
+
+ NPN_SetValue(inst, kRequestDrawingModel_ANPSetValue, (void*)foo_ANPDrawingModel)
+ */
+#define kRequestDrawingModel_ANPSetValue ((NPPVariable)1000)
+
+/** These are used as bitfields in ANPSupportedDrawingModels_EnumValue,
+ and as-is in ANPRequestDrawingModel_EnumValue. The drawing model determines
+ how to interpret the ANPDrawingContext provided in the Draw event and how
+ to interpret the NPWindow->window field.
+ */
+enum ANPDrawingModels {
+ /** Draw into a bitmap from the browser thread in response to a Draw event.
+ NPWindow->window is reserved (ignore)
+ */
+ kBitmap_ANPDrawingModel = 1 << 0,
+ /** Draw into a surface (e.g. raster, openGL, etc.) using the Java surface
+ interface. When this model is used the browser will invoke the Java
+ class specified in the plugin's apk manifest. From that class the browser
+ will invoke the appropriate method to return an an instance of a android
+ Java View. The instance is then embedded in the html. The plugin can then
+ manipulate the view as it would any normal Java View in android.
+
+ Unlike the bitmap model, a surface model is opaque so no html content
+ behind the plugin will be visible. Unless the plugin needs to be
+ transparent the surface model should be chosen over the bitmap model as
+ it will have better performance.
+
+ Further, a plugin can manipulate some surfaces in native code using the
+ ANPSurfaceInterface. This interface can be used to manipulate Java
+ objects that extend Surface.class by allowing them to access the
+ surface's underlying bitmap in native code. For instance, if a raster
+ surface is used the plugin can lock, draw directly into the bitmap, and
+ unlock the surface in native code without making JNI calls to the Java
+ surface object.
+ */
+ kSurface_ANPDrawingModel = 1 << 1,
+ kOpenGL_ANPDrawingModel = 1 << 2,
+};
+typedef int32_t ANPDrawingModel;
+
+/** Request to receive/disable events. If the pointer is NULL then all flags will
+ be disabled. Otherwise, the event type will be enabled iff its corresponding
+ bit in the EventFlags bit field is set.
+
+ NPN_SetValue(inst, ANPAcceptEvents, (void*)EventFlags)
+ */
+#define kAcceptEvents_ANPSetValue ((NPPVariable)1001)
+
+/** The EventFlags are a set of bits used to determine which types of events the
+ plugin wishes to receive. For example, if the value is 0x03 then both key
+ and touch events will be provided to the plugin.
+ */
+enum ANPEventFlag {
+ kKey_ANPEventFlag = 0x01,
+ kTouch_ANPEventFlag = 0x02,
+};
+typedef uint32_t ANPEventFlags;
+
+///////////////////////////////////////////////////////////////////////////////
+// NPP_GetValue
+
+/** Requests that the plugin return a java surface to be displayed. This will
+ only be used if the plugin has choosen the kSurface_ANPDrawingModel.
+
+ NPP_GetValue(inst, kJavaSurface_ANPGetValue, jobject surface)
+ */
+#define kJavaSurface_ANPGetValue ((NPPVariable)2000)
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ANDROID INTERFACE DEFINITIONS
+
+/** Interfaces provide additional functionality to the plugin via function ptrs.
+ Once an interface is retrieved, it is valid for the lifetime of the plugin
+ (just like browserfuncs).
+
+ All ANPInterfaces begin with an inSize field, which must be set by the
+ caller (plugin) with the number of bytes allocated for the interface.
+ e.g. SomeInterface si; si.inSize = sizeof(si); browser->getvalue(..., &si);
+ */
+struct ANPInterface {
+ uint32_t inSize; // size (in bytes) of this struct
+};
+
+enum ANPLogTypes {
+ kError_ANPLogType = 0, // error
+ kWarning_ANPLogType = 1, // warning
+ kDebug_ANPLogType = 2 // debug only (informational)
+};
+typedef int32_t ANPLogType;
+
+struct ANPLogInterfaceV0 : ANPInterface {
+ /** dumps printf messages to the log file
+ e.g. interface->log(instance, kWarning_ANPLogType, "value is %d", value);
+ */
+ void (*log)(ANPLogType, const char format[], ...);
+};
+
+struct ANPBitmapInterfaceV0 : ANPInterface {
+ /** Returns true if the specified bitmap format is supported, and if packing
+ is non-null, sets it to the packing info for that format.
+ */
+ bool (*getPixelPacking)(ANPBitmapFormat, ANPPixelPacking* packing);
+};
+
+struct ANPMatrixInterfaceV0 : ANPInterface {
+ /** Return a new identity matrix
+ */
+ ANPMatrix* (*newMatrix)();
+ /** Delete a matrix previously allocated by newMatrix()
+ */
+ void (*deleteMatrix)(ANPMatrix*);
+
+ ANPMatrixFlag (*getFlags)(const ANPMatrix*);
+
+ void (*copy)(ANPMatrix* dst, const ANPMatrix* src);
+
+ /** Return the matrix values in a float array (allcoated by the caller),
+ where the values are treated as follows:
+ w = x * [6] + y * [7] + [8];
+ x' = (x * [0] + y * [1] + [2]) / w;
+ y' = (x * [3] + y * [4] + [5]) / w;
+ */
+ void (*get3x3)(const ANPMatrix*, float[9]);
+ /** Initialize the matrix from values in a float array,
+ where the values are treated as follows:
+ w = x * [6] + y * [7] + [8];
+ x' = (x * [0] + y * [1] + [2]) / w;
+ y' = (x * [3] + y * [4] + [5]) / w;
+ */
+ void (*set3x3)(ANPMatrix*, const float[9]);
+
+ void (*setIdentity)(ANPMatrix*);
+ void (*preTranslate)(ANPMatrix*, float tx, float ty);
+ void (*postTranslate)(ANPMatrix*, float tx, float ty);
+ void (*preScale)(ANPMatrix*, float sx, float sy);
+ void (*postScale)(ANPMatrix*, float sx, float sy);
+ void (*preSkew)(ANPMatrix*, float kx, float ky);
+ void (*postSkew)(ANPMatrix*, float kx, float ky);
+ void (*preRotate)(ANPMatrix*, float degrees);
+ void (*postRotate)(ANPMatrix*, float degrees);
+ void (*preConcat)(ANPMatrix*, const ANPMatrix*);
+ void (*postConcat)(ANPMatrix*, const ANPMatrix*);
+
+ /** Return true if src is invertible, and if so, return its inverse in dst.
+ If src is not invertible, return false and ignore dst.
+ */
+ bool (*invert)(ANPMatrix* dst, const ANPMatrix* src);
+
+ /** Transform the x,y pairs in src[] by this matrix, and store the results
+ in dst[]. The count parameter is treated as the number of pairs in the
+ array. It is legal for src and dst to point to the same memory, but
+ illegal for the two arrays to partially overlap.
+ */
+ void (*mapPoints)(ANPMatrix*, float dst[], const float src[],
+ int32_t count);
+};
+
+struct ANPPathInterfaceV0 : ANPInterface {
+ /** Return a new path */
+ ANPPath* (*newPath)();
+
+ /** Delete a path previously allocated by ANPPath() */
+ void (*deletePath)(ANPPath*);
+
+ /** Make a deep copy of the src path, into the dst path (already allocated
+ by the caller).
+ */
+ void (*copy)(ANPPath* dst, const ANPPath* src);
+
+ /** Returns true if the two paths are the same (i.e. have the same points)
+ */
+ bool (*equal)(const ANPPath* path0, const ANPPath* path1);
+
+ /** Remove any previous points, initializing the path back to empty. */
+ void (*reset)(ANPPath*);
+
+ /** Return true if the path is empty (has no lines, quads or cubics). */
+ bool (*isEmpty)(const ANPPath*);
+
+ /** Return the path's bounds in bounds. */
+ void (*getBounds)(const ANPPath*, ANPRectF* bounds);
+
+ void (*moveTo)(ANPPath*, float x, float y);
+ void (*lineTo)(ANPPath*, float x, float y);
+ void (*quadTo)(ANPPath*, float x0, float y0, float x1, float y1);
+ void (*cubicTo)(ANPPath*, float x0, float y0, float x1, float y1,
+ float x2, float y2);
+ void (*close)(ANPPath*);
+
+ /** Offset the src path by [dx, dy]. If dst is null, apply the
+ change directly to the src path. If dst is not null, write the
+ changed path into dst, and leave the src path unchanged. In that case
+ dst must have been previously allocated by the caller.
+ */
+ void (*offset)(ANPPath* src, float dx, float dy, ANPPath* dst);
+
+ /** Transform the path by the matrix. If dst is null, apply the
+ change directly to the src path. If dst is not null, write the
+ changed path into dst, and leave the src path unchanged. In that case
+ dst must have been previously allocated by the caller.
+ */
+ void (*transform)(ANPPath* src, const ANPMatrix*, ANPPath* dst);
+};
+
+/** ANPColor is always defined to have the same packing on all platforms, and
+ it is always unpremultiplied.
+
+ This is in contrast to 32bit format(s) in bitmaps, which are premultiplied,
+ and their packing may vary depending on the platform, hence the need for
+ ANPBitmapInterface::getPixelPacking()
+ */
+typedef uint32_t ANPColor;
+#define ANPColor_ASHIFT 24
+#define ANPColor_RSHIFT 16
+#define ANPColor_GSHIFT 8
+#define ANPColor_BSHIFT 0
+#define ANP_MAKE_COLOR(a, r, g, b) \
+ (((a) << ANPColor_ASHIFT) | \
+ ((r) << ANPColor_RSHIFT) | \
+ ((g) << ANPColor_GSHIFT) | \
+ ((b) << ANPColor_BSHIFT))
+
+enum ANPPaintFlag {
+ kAntiAlias_ANPPaintFlag = 1 << 0,
+ kFilterBitmap_ANPPaintFlag = 1 << 1,
+ kDither_ANPPaintFlag = 1 << 2,
+ kUnderlineText_ANPPaintFlag = 1 << 3,
+ kStrikeThruText_ANPPaintFlag = 1 << 4,
+ kFakeBoldText_ANPPaintFlag = 1 << 5,
+};
+typedef uint32_t ANPPaintFlags;
+
+enum ANPPaintStyles {
+ kFill_ANPPaintStyle = 0,
+ kStroke_ANPPaintStyle = 1,
+ kFillAndStroke_ANPPaintStyle = 2
+};
+typedef int32_t ANPPaintStyle;
+
+enum ANPPaintCaps {
+ kButt_ANPPaintCap = 0,
+ kRound_ANPPaintCap = 1,
+ kSquare_ANPPaintCap = 2
+};
+typedef int32_t ANPPaintCap;
+
+enum ANPPaintJoins {
+ kMiter_ANPPaintJoin = 0,
+ kRound_ANPPaintJoin = 1,
+ kBevel_ANPPaintJoin = 2
+};
+typedef int32_t ANPPaintJoin;
+
+enum ANPPaintAligns {
+ kLeft_ANPPaintAlign = 0,
+ kCenter_ANPPaintAlign = 1,
+ kRight_ANPPaintAlign = 2
+};
+typedef int32_t ANPPaintAlign;
+
+enum ANPTextEncodings {
+ kUTF8_ANPTextEncoding = 0,
+ kUTF16_ANPTextEncoding = 1,
+};
+typedef int32_t ANPTextEncoding;
+
+enum ANPTypefaceStyles {
+ kBold_ANPTypefaceStyle = 1 << 0,
+ kItalic_ANPTypefaceStyle = 1 << 1
+};
+typedef uint32_t ANPTypefaceStyle;
+
+typedef uint32_t ANPFontTableTag;
+
+struct ANPFontMetrics {
+ /** The greatest distance above the baseline for any glyph (will be <= 0) */
+ float fTop;
+ /** The recommended distance above the baseline (will be <= 0) */
+ float fAscent;
+ /** The recommended distance below the baseline (will be >= 0) */
+ float fDescent;
+ /** The greatest distance below the baseline for any glyph (will be >= 0) */
+ float fBottom;
+ /** The recommended distance to add between lines of text (will be >= 0) */
+ float fLeading;
+};
+
+struct ANPTypefaceInterfaceV0 : ANPInterface {
+ /** Return a new reference to the typeface that most closely matches the
+ requested name and style. Pass null as the name to return
+ the default font for the requested style. Will never return null
+
+ The 5 generic font names "serif", "sans-serif", "monospace", "cursive",
+ "fantasy" are recognized, and will be mapped to their logical font
+ automatically by this call.
+
+ @param name May be NULL. The name of the font family.
+ @param style The style (normal, bold, italic) of the typeface.
+ @return reference to the closest-matching typeface. Caller must call
+ unref() when they are done with the typeface.
+ */
+ ANPTypeface* (*createFromName)(const char name[], ANPTypefaceStyle);
+
+ /** Return a new reference to the typeface that most closely matches the
+ requested typeface and specified Style. Use this call if you want to
+ pick a new style from the same family of the existing typeface.
+ If family is NULL, this selects from the default font's family.
+
+ @param family May be NULL. The name of the existing type face.
+ @param s The style (normal, bold, italic) of the type face.
+ @return reference to the closest-matching typeface. Call must call
+ unref() when they are done.
+ */
+ ANPTypeface* (*createFromTypeface)(const ANPTypeface* family,
+ ANPTypefaceStyle);
+
+ /** Return the owner count of the typeface. A newly created typeface has an
+ owner count of 1. When the owner count is reaches 0, the typeface is
+ deleted.
+ */
+ int32_t (*getRefCount)(const ANPTypeface*);
+
+ /** Increment the owner count on the typeface
+ */
+ void (*ref)(ANPTypeface*);
+
+ /** Decrement the owner count on the typeface. When the count goes to 0,
+ the typeface is deleted.
+ */
+ void (*unref)(ANPTypeface*);
+
+ /** Return the style bits for the specified typeface
+ */
+ ANPTypefaceStyle (*getStyle)(const ANPTypeface*);
+
+ /** Some fonts are stored in files. If that is true for the fontID, then
+ this returns the byte length of the full file path. If path is not null,
+ then the full path is copied into path (allocated by the caller), up to
+ length bytes. If index is not null, then it is set to the truetype
+ collection index for this font, or 0 if the font is not in a collection.
+
+ Note: getFontPath does not assume that path is a null-terminated string,
+ so when it succeeds, it only copies the bytes of the file name and
+ nothing else (i.e. it copies exactly the number of bytes returned by the
+ function. If the caller wants to treat path[] as a C string, it must be
+ sure that it is allocated at least 1 byte larger than the returned size,
+ and it must copy in the terminating 0.
+
+ If the fontID does not correspond to a file, then the function returns
+ 0, and the path and index parameters are ignored.
+
+ @param fontID The font whose file name is being queried
+ @param path Either NULL, or storage for receiving up to length bytes
+ of the font's file name. Allocated by the caller.
+ @param length The maximum space allocated in path (by the caller).
+ Ignored if path is NULL.
+ @param index Either NULL, or receives the TTC index for this font.
+ If the font is not a TTC, then will be set to 0.
+ @return The byte length of th font's file name, or 0 if the font is not
+ baked by a file.
+ */
+ int32_t (*getFontPath)(const ANPTypeface*, char path[], int32_t length,
+ int32_t* index);
+
+ /** Return a UTF8 encoded path name for the font directory, or NULL if not
+ supported. If returned, this string address will be valid for the life
+ of the plugin instance. It will always end with a '/' character.
+ */
+ const char* (*getFontDirectoryPath)();
+};
+
+struct ANPPaintInterfaceV0 : ANPInterface {
+ /** Return a new paint object, which holds all of the color and style
+ attributes that affect how things (geometry, text, bitmaps) are drawn
+ in a ANPCanvas.
+
+ The paint that is returned is not tied to any particular plugin
+ instance, but it must only be accessed from one thread at a time.
+ */
+ ANPPaint* (*newPaint)();
+ void (*deletePaint)(ANPPaint*);
+
+ ANPPaintFlags (*getFlags)(const ANPPaint*);
+ void (*setFlags)(ANPPaint*, ANPPaintFlags);
+
+ ANPColor (*getColor)(const ANPPaint*);
+ void (*setColor)(ANPPaint*, ANPColor);
+
+ ANPPaintStyle (*getStyle)(const ANPPaint*);
+ void (*setStyle)(ANPPaint*, ANPPaintStyle);
+
+ float (*getStrokeWidth)(const ANPPaint*);
+ float (*getStrokeMiter)(const ANPPaint*);
+ ANPPaintCap (*getStrokeCap)(const ANPPaint*);
+ ANPPaintJoin (*getStrokeJoin)(const ANPPaint*);
+ void (*setStrokeWidth)(ANPPaint*, float);
+ void (*setStrokeMiter)(ANPPaint*, float);
+ void (*setStrokeCap)(ANPPaint*, ANPPaintCap);
+ void (*setStrokeJoin)(ANPPaint*, ANPPaintJoin);
+
+ ANPTextEncoding (*getTextEncoding)(const ANPPaint*);
+ ANPPaintAlign (*getTextAlign)(const ANPPaint*);
+ float (*getTextSize)(const ANPPaint*);
+ float (*getTextScaleX)(const ANPPaint*);
+ float (*getTextSkewX)(const ANPPaint*);
+ void (*setTextEncoding)(ANPPaint*, ANPTextEncoding);
+ void (*setTextAlign)(ANPPaint*, ANPPaintAlign);
+ void (*setTextSize)(ANPPaint*, float);
+ void (*setTextScaleX)(ANPPaint*, float);
+ void (*setTextSkewX)(ANPPaint*, float);
+
+ /** Return the typeface ine paint, or null if there is none. This does not
+ modify the owner count of the returned typeface.
+ */
+ ANPTypeface* (*getTypeface)(const ANPPaint*);
+
+ /** Set the paint's typeface. If the paint already had a non-null typeface,
+ its owner count is decremented. If the new typeface is non-null, its
+ owner count is incremented.
+ */
+ void (*setTypeface)(ANPPaint*, ANPTypeface*);
+
+ /** Return the width of the text. If bounds is not null, return the bounds
+ of the text in that rectangle.
+ */
+ float (*measureText)(ANPPaint*, const void* text, uint32_t byteLength,
+ ANPRectF* bounds);
+
+ /** Return the number of unichars specifed by the text.
+ If widths is not null, returns the array of advance widths for each
+ unichar.
+ If bounds is not null, returns the array of bounds for each unichar.
+ */
+ int (*getTextWidths)(ANPPaint*, const void* text, uint32_t byteLength,
+ float widths[], ANPRectF bounds[]);
+
+ /** Return in metrics the spacing values for text, respecting the paint's
+ typeface and pointsize, and return the spacing between lines
+ (descent - ascent + leading). If metrics is NULL, it will be ignored.
+ */
+ float (*getFontMetrics)(ANPPaint*, ANPFontMetrics* metrics);
+};
+
+struct ANPCanvasInterfaceV0 : ANPInterface {
+ /** Return a canvas that will draw into the specified bitmap. Note: the
+ canvas copies the fields of the bitmap, so it need not persist after
+ this call, but the canvas DOES point to the same pixel memory that the
+ bitmap did, so the canvas should not be used after that pixel memory
+ goes out of scope. In the case of creating a canvas to draw into the
+ pixels provided by kDraw_ANPEventType, those pixels are only while
+ handling that event.
+
+ The canvas that is returned is not tied to any particular plugin
+ instance, but it must only be accessed from one thread at a time.
+ */
+ ANPCanvas* (*newCanvas)(const ANPBitmap*);
+ void (*deleteCanvas)(ANPCanvas*);
+
+ void (*save)(ANPCanvas*);
+ void (*restore)(ANPCanvas*);
+ void (*translate)(ANPCanvas*, float tx, float ty);
+ void (*scale)(ANPCanvas*, float sx, float sy);
+ void (*rotate)(ANPCanvas*, float degrees);
+ void (*skew)(ANPCanvas*, float kx, float ky);
+ void (*concat)(ANPCanvas*, const ANPMatrix*);
+ void (*clipRect)(ANPCanvas*, const ANPRectF*);
+ void (*clipPath)(ANPCanvas*, const ANPPath*);
+
+ /** Return the current matrix on the canvas
+ */
+ void (*getTotalMatrix)(ANPCanvas*, ANPMatrix*);
+ /** Return the current clip bounds in local coordinates, expanding it to
+ account for antialiasing edge effects if aa is true. If the
+ current clip is empty, return false and ignore the bounds argument.
+ */
+ bool (*getLocalClipBounds)(ANPCanvas*, ANPRectF* bounds, bool aa);
+ /** Return the current clip bounds in device coordinates in bounds. If the
+ current clip is empty, return false and ignore the bounds argument.
+ */
+ bool (*getDeviceClipBounds)(ANPCanvas*, ANPRectI* bounds);
+
+ void (*drawColor)(ANPCanvas*, ANPColor);
+ void (*drawPaint)(ANPCanvas*, const ANPPaint*);
+ void (*drawLine)(ANPCanvas*, float x0, float y0, float x1, float y1,
+ const ANPPaint*);
+ void (*drawRect)(ANPCanvas*, const ANPRectF*, const ANPPaint*);
+ void (*drawOval)(ANPCanvas*, const ANPRectF*, const ANPPaint*);
+ void (*drawPath)(ANPCanvas*, const ANPPath*, const ANPPaint*);
+ void (*drawText)(ANPCanvas*, const void* text, uint32_t byteLength,
+ float x, float y, const ANPPaint*);
+ void (*drawPosText)(ANPCanvas*, const void* text, uint32_t byteLength,
+ const float xy[], const ANPPaint*);
+ void (*drawBitmap)(ANPCanvas*, const ANPBitmap*, float x, float y,
+ const ANPPaint*);
+ void (*drawBitmapRect)(ANPCanvas*, const ANPBitmap*,
+ const ANPRectI* src, const ANPRectF* dst,
+ const ANPPaint*);
+};
+
+struct ANPWindowInterfaceV0 : ANPInterface {
+ /** Registers a set of rectangles that the plugin would like to keep on
+ screen. The rectangles are listed in order of priority with the highest
+ priority rectangle in location rects[0]. The browser will attempt to keep
+ as many of the rectangles on screen as possible and will scroll them into
+ view in response to the invocation of this method and other various events.
+ The count specifies how many rectangles are in the array. If the count is
+ zero it signals the browser that any existing rectangles should be cleared
+ and no rectangles will be tracked.
+ */
+ void (*setVisibleRects)(NPP instance, const ANPRectI rects[], int32_t count);
+ /** Clears any rectangles that are being tracked as a result of a call to
+ setVisibleRects. This call is equivalent to setVisibleRect(inst, NULL, 0).
+ */
+ void (*clearVisibleRects)(NPP instance);
+ /** Given a boolean value of true the device will be requested to provide
+ a keyboard. A value of false will result in a request to hide the
+ keyboard. Further, the on-screen keyboard will not be displayed if a
+ physical keyboard is active.
+ */
+ void (*showKeyboard)(NPP instance, bool value);
+ /** Called when a plugin wishes to enter into full screen mode. The plugin's
+ Java class (defined in the plugin's apk manifest) will be called
+ asynchronously to provide a View object to be displayed full screen.
+ */
+ void (*requestFullScreen)(NPP instance);
+ /** Called when a plugin wishes to exit from full screen mode. As a result,
+ the plugin's full screen view will be discarded by the view system.
+ */
+ void (*exitFullScreen)(NPP instance);
+ /** Called when a plugin wishes to be zoomed and centered in the current view.
+ */
+ void (*requestCenterFitZoom)(NPP instance);
+};
+
+struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 {
+ /** Returns a rectangle representing the visible area of the plugin on
+ screen. The coordinates are relative to the size of the plugin in the
+ document and therefore will never be negative or exceed the plugin's size.
+ */
+ ANPRectI (*visibleRect)(NPP instance);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum ANPSampleFormats {
+ kUnknown_ANPSamleFormat = 0,
+ kPCM16Bit_ANPSampleFormat = 1,
+ kPCM8Bit_ANPSampleFormat = 2
+};
+typedef int32_t ANPSampleFormat;
+
+/** The audio buffer is passed to the callback proc to request more samples.
+ It is owned by the system, and the callback may read it, but should not
+ maintain a pointer to it outside of the scope of the callback proc.
+ */
+struct ANPAudioBuffer {
+ // RO - repeat what was specified in newTrack()
+ int32_t channelCount;
+ // RO - repeat what was specified in newTrack()
+ ANPSampleFormat format;
+ /** This buffer is owned by the caller. Inside the callback proc, up to
+ "size" bytes of sample data should be written into this buffer. The
+ address is only valid for the scope of a single invocation of the
+ callback proc.
+ */
+ void* bufferData;
+ /** On input, specifies the maximum number of bytes that can be written
+ to "bufferData". On output, specifies the actual number of bytes that
+ the callback proc wrote into "bufferData".
+ */
+ uint32_t size;
+};
+
+enum ANPAudioEvents {
+ /** This event is passed to the callback proc when the audio-track needs
+ more sample data written to the provided buffer parameter.
+ */
+ kMoreData_ANPAudioEvent = 0,
+ /** This event is passed to the callback proc if the audio system runs out
+ of sample data. In this event, no buffer parameter will be specified
+ (i.e. NULL will be passed to the 3rd parameter).
+ */
+ kUnderRun_ANPAudioEvent = 1
+};
+typedef int32_t ANPAudioEvent;
+
+/** Called to feed sample data to the track. This will be called in a separate
+ thread. However, you may call trackStop() from the callback (but you
+ cannot delete the track).
+
+ For example, when you have written the last chunk of sample data, you can
+ immediately call trackStop(). This will take effect after the current
+ buffer has been played.
+
+ The "user" parameter is the same value that was passed to newTrack()
+ */
+typedef void (*ANPAudioCallbackProc)(ANPAudioEvent event, void* user,
+ ANPAudioBuffer* buffer);
+
+struct ANPAudioTrack; // abstract type for audio tracks
+
+struct ANPAudioTrackInterfaceV0 : ANPInterface {
+ /** Create a new audio track, or NULL on failure. The track is initially in
+ the stopped state and therefore ANPAudioCallbackProc will not be called
+ until the track is started.
+ */
+ ANPAudioTrack* (*newTrack)(uint32_t sampleRate, // sampling rate in Hz
+ ANPSampleFormat,
+ int channelCount, // MONO=1, STEREO=2
+ ANPAudioCallbackProc,
+ void* user);
+ /** Deletes a track that was created using newTrack. The track can be
+ deleted in any state and it waits for the ANPAudioCallbackProc thread
+ to exit before returning.
+ */
+ void (*deleteTrack)(ANPAudioTrack*);
+
+ void (*start)(ANPAudioTrack*);
+ void (*pause)(ANPAudioTrack*);
+ void (*stop)(ANPAudioTrack*);
+ /** Returns true if the track is not playing (e.g. pause or stop was called,
+ or start was never called.
+ */
+ bool (*isStopped)(ANPAudioTrack*);
+};
+
+struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 {
+ /** Returns the track's latency in milliseconds. */
+ uint32_t (*trackLatency)(ANPAudioTrack*);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent
+
+enum ANPEventTypes {
+ kNull_ANPEventType = 0,
+ kKey_ANPEventType = 1,
+ /** Mouse events are triggered by either clicking with the navigational pad
+ or by tapping the touchscreen (if the kDown_ANPTouchAction is handled by
+ the plugin then no mouse event is generated). The kKey_ANPEventFlag has
+ to be set to true in order to receive these events.
+ */
+ kMouse_ANPEventType = 2,
+ /** Touch events are generated when the user touches on the screen. The
+ kTouch_ANPEventFlag has to be set to true in order to receive these
+ events.
+ */
+ kTouch_ANPEventType = 3,
+ /** Only triggered by a plugin using the kBitmap_ANPDrawingModel. This event
+ signals that the plugin needs to redraw itself into the provided bitmap.
+ */
+ kDraw_ANPEventType = 4,
+ kLifecycle_ANPEventType = 5,
+
+ /** This event type is completely defined by the plugin.
+ When creating an event, the caller must always set the first
+ two fields, the remaining data is optional.
+ ANPEvent evt;
+ evt.inSize = sizeof(ANPEvent);
+ evt.eventType = kCustom_ANPEventType
+ // other data slots are optional
+ evt.other[] = ...;
+ To post a copy of the event, call
+ eventInterface->postEvent(myNPPInstance, &evt);
+ That call makes a copy of the event struct, and post that on the event
+ queue for the plugin.
+ */
+ kCustom_ANPEventType = 6,
+ /** MultiTouch events are generated when the user touches on the screen. The
+ kTouch_ANPEventFlag has to be set to true in order to receive these
+ events. This type is a replacement for the older kTouch_ANPEventType.
+ */
+ kMultiTouch_ANPEventType = 7,
+};
+typedef int32_t ANPEventType;
+
+enum ANPKeyActions {
+ kDown_ANPKeyAction = 0,
+ kUp_ANPKeyAction = 1,
+};
+typedef int32_t ANPKeyAction;
+
+#include "ANPKeyCodes.h"
+typedef int32_t ANPKeyCode;
+
+enum ANPKeyModifiers {
+ kAlt_ANPKeyModifier = 1 << 0,
+ kShift_ANPKeyModifier = 1 << 1,
+};
+// bit-field containing some number of ANPKeyModifier bits
+typedef uint32_t ANPKeyModifier;
+
+enum ANPMouseActions {
+ kDown_ANPMouseAction = 0,
+ kUp_ANPMouseAction = 1,
+};
+typedef int32_t ANPMouseAction;
+
+enum ANPTouchActions {
+ /** This occurs when the user first touches on the screen. As such, this
+ action will always occur prior to any of the other touch actions. If
+ the plugin chooses to not handle this action then no other events
+ related to that particular touch gesture will be generated.
+ */
+ kDown_ANPTouchAction = 0,
+ kUp_ANPTouchAction = 1,
+ kMove_ANPTouchAction = 2,
+ kCancel_ANPTouchAction = 3,
+ // The web view will ignore the return value from the following actions
+ kLongPress_ANPTouchAction = 4,
+ kDoubleTap_ANPTouchAction = 5,
+};
+typedef int32_t ANPTouchAction;
+
+enum ANPLifecycleActions {
+ /** The web view containing this plugin has been paused. See documentation
+ on the android activity lifecycle for more information.
+ */
+ kPause_ANPLifecycleAction = 0,
+ /** The web view containing this plugin has been resumed. See documentation
+ on the android activity lifecycle for more information.
+ */
+ kResume_ANPLifecycleAction = 1,
+ /** The plugin has focus and is now the recipient of input events (e.g. key,
+ touch, etc.)
+ */
+ kGainFocus_ANPLifecycleAction = 2,
+ /** The plugin has lost focus and will not receive any input events until it
+ regains focus. This event is always preceded by a GainFocus action.
+ */
+ kLoseFocus_ANPLifecycleAction = 3,
+ /** The browser is running low on available memory and is requesting that
+ the plugin free any unused/inactive resources to prevent a performance
+ degradation.
+ */
+ kFreeMemory_ANPLifecycleAction = 4,
+ /** The page has finished loading. This happens when the page's top level
+ frame reports that it has completed loading.
+ */
+ kOnLoad_ANPLifecycleAction = 5,
+ /** The browser is honoring the plugin's request to go full screen. Upon
+ returning from this event the browser will resize the plugin's java
+ surface to full-screen coordinates.
+ */
+ kEnterFullScreen_ANPLifecycleAction = 6,
+ /** The browser has exited from full screen mode. Immediately prior to
+ sending this event the browser has resized the plugin's java surface to
+ its original coordinates.
+ */
+ kExitFullScreen_ANPLifecycleAction = 7,
+ /** The plugin is visible to the user on the screen. This event will always
+ occur after a kOffScreen_ANPLifecycleAction event.
+ */
+ kOnScreen_ANPLifecycleAction = 8,
+ /** The plugin is no longer visible to the user on the screen. This event
+ will always occur prior to an kOnScreen_ANPLifecycleAction event.
+ */
+ kOffScreen_ANPLifecycleAction = 9,
+};
+typedef uint32_t ANPLifecycleAction;
+
+struct TouchPoint {
+ int32_t id;
+ float x; // relative to your "window" (0...width)
+ float y; // relative to your "window" (0...height)
+ float pressure;
+ float size; // normalized to a value between 0...1
+};
+
+/* This is what is passed to NPP_HandleEvent() */
+struct ANPEvent {
+ uint32_t inSize; // size of this struct in bytes
+ ANPEventType eventType;
+ // use based on the value in eventType
+ union {
+ struct {
+ ANPKeyAction action;
+ ANPKeyCode nativeCode;
+ int32_t virtualCode; // windows virtual key code
+ ANPKeyModifier modifiers;
+ int32_t repeatCount; // 0 for initial down (or up)
+ int32_t unichar; // 0 if there is no value
+ } key;
+ struct {
+ ANPMouseAction action;
+ int32_t x; // relative to your "window" (0...width)
+ int32_t y; // relative to your "window" (0...height)
+ } mouse;
+ struct {
+ ANPTouchAction action;
+ ANPKeyModifier modifiers;
+ int32_t x; // relative to your "window" (0...width)
+ int32_t y; // relative to your "window" (0...height)
+ } touch;
+ struct {
+ ANPLifecycleAction action;
+ } lifecycle;
+ struct {
+ ANPDrawingModel model;
+ // relative to (0,0) in top-left of your plugin
+ ANPRectI clip;
+ // use based on the value in model
+ union {
+ ANPBitmap bitmap;
+ struct {
+ int32_t width;
+ int32_t height;
+ } surface;
+ } data;
+ } draw;
+ struct {
+ int64_t timestamp;
+ int32_t id;
+ ANPTouchAction action;
+ int32_t pointerCount;
+ TouchPoint* touchPoint;
+ } multiTouch;
+ int32_t other[8];
+ } data;
+};
+
+struct ANPEventInterfaceV0 : ANPInterface {
+ /** Post a copy of the specified event to the plugin. The event will be
+ delivered to the plugin in its main thread (the thread that receives
+ other ANPEvents). If, after posting before delivery, the NPP instance
+ is torn down, the event will be discarded.
+ */
+ void (*postEvent)(NPP inst, const ANPEvent* event);
+};
+
+
+#endif
diff --git a/Source/WebKit/android/smoke/MessageThread.cpp b/Source/WebKit/android/smoke/MessageThread.cpp
new file mode 100644
index 0000000..48f2222
--- /dev/null
+++ b/Source/WebKit/android/smoke/MessageThread.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "MessageThread"
+
+#include "config.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+#include "MessageThread.h"
+#include "ScriptController.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+static bool compareMessages(const Message& msg1,
+ const Message& msg2,
+ bool memberIsNull) {
+ return (msg1.object() == msg2.object() &&
+ (memberIsNull || msg1.member() == msg2.member()));
+}
+
+bool MessageQueue::hasMessages(const Message& message) {
+ AutoMutex lock(m_mutex);
+
+ static const Message::GenericMemberFunction nullMember = NULL;
+ const bool memberIsNull = message.member() == nullMember;
+
+ for (list<Message*>::iterator it = m_messages.begin();
+ it != m_messages.end(); ++it) {
+ Message* m = *it;
+ if (compareMessages(message, *m, memberIsNull))
+ return true;
+ }
+ return false;
+}
+
+void MessageQueue::remove(const Message& message) {
+ AutoMutex lock(m_mutex);
+
+ static const Message::GenericMemberFunction nullMember = NULL;
+ const bool memberIsNull = message.member() == nullMember;
+
+ for (list<Message*>::iterator it = m_messages.begin();
+ it != m_messages.end(); ++it) {
+ Message* m = *it;
+ if (compareMessages(message, *m, memberIsNull)) {
+ it = m_messages.erase(it);
+ delete m;
+ }
+ }
+}
+
+void MessageQueue::post(Message* message) {
+ AutoMutex lock(m_mutex);
+
+ double when = message->m_when;
+ LOG_ASSERT(when > 0, "Message time may not be 0");
+
+ list<Message*>::iterator it;
+ for (it = m_messages.begin(); it != m_messages.end(); ++it) {
+ Message* m = *it;
+ if (when < m->m_when) {
+ break;
+ }
+ }
+ m_messages.insert(it, message);
+ m_condition.signal();
+}
+
+void MessageQueue::postAtFront(Message* message) {
+ AutoMutex lock(m_mutex);
+ message->m_when = 0;
+ m_messages.push_front(message);
+}
+
+Message* MessageQueue::next() {
+ AutoMutex lock(m_mutex);
+ while (true) {
+ if (m_messages.empty()) {
+ // No messages, wait until another arrives
+ m_condition.wait(m_mutex);
+ }
+ Message* next = m_messages.front();
+ double now = WTF::currentTimeMS();
+ double diff = next->m_when - now;
+ if (diff > 0) {
+ // Not time for this message yet, wait the difference in nanos
+ m_condition.waitRelative(m_mutex,
+ static_cast<nsecs_t>(diff * 1000000) /* nanos */);
+ } else {
+ // Time for this message to run.
+ m_messages.pop_front();
+ return next;
+ }
+ }
+}
+
+bool MessageThread::threadLoop() {
+ WebCore::ScriptController::initializeThreading();
+
+ while (true) {
+ Message* message = m_queue.next();
+ if (message != NULL) {
+ message->run();
+ }
+ }
+ return false;
+}
+
+// Global thread object obtained by messageThread().
+static sp<MessageThread> gMessageThread;
+
+MessageThread* messageThread() {
+ if (gMessageThread == NULL) {
+ gMessageThread = new MessageThread();
+ gMessageThread->run("WebCoreThread");
+ }
+ return gMessageThread.get();
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/smoke/MessageThread.h b/Source/WebKit/android/smoke/MessageThread.h
new file mode 100644
index 0000000..ca0115b
--- /dev/null
+++ b/Source/WebKit/android/smoke/MessageThread.h
@@ -0,0 +1,108 @@
+/*
+ * 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 ANDROID_WEBKIT_MESSAGETHREAD_H
+#define ANDROID_WEBKIT_MESSAGETHREAD_H
+
+#include <list>
+
+#include "MessageTypes.h"
+
+#include <utils/threads.h>
+
+using std::list;
+
+namespace android {
+
+class MessageQueue {
+public:
+ MessageQueue() {}
+
+ // Return true if the queue has messages with the given object and member
+ // function. If member is null, return true if the message has the same
+ // object.
+ template <class T>
+ bool hasMessages(T* object, void (T::*member)(void));
+
+ // Remove all messages with the given object and member function. If
+ // member is null, remove all messages with the given object.
+ template <class T>
+ void remove(T* object, void (T::*member)(void));
+
+ // Post a new message to the queue.
+ void post(Message* closure);
+
+ // Post a new message at the front of the queue.
+ void postAtFront(Message* closure);
+
+ // Obtain the next message. Blocks until either a new message arrives or
+ // we reach the time of the next message.
+ Message* next();
+
+private:
+ bool hasMessages(const Message& message);
+ void remove(const Message& message);
+
+ list<Message*> m_messages;
+ Mutex m_mutex;
+ Condition m_condition;
+};
+
+template <class T>
+bool MessageQueue::hasMessages(T* object, void (T::*member)(void)) {
+ MemberFunctionMessage<T, void> message(object, member);
+ return hasMessages(message);
+}
+
+template <class T>
+void MessageQueue::remove(T* object, void (T::*member)(void)) {
+ MemberFunctionMessage<T, void> message(object, member);
+ remove(message);
+}
+
+class MessageThread : public Thread {
+public:
+ MessageQueue& queue() { return m_queue; }
+
+private:
+ MessageThread() : Thread(true /* canCallJava */) {}
+
+ virtual bool threadLoop();
+
+ MessageQueue m_queue;
+ // Used for thread initialization
+ Mutex m_mutex;
+ Condition m_condition;
+
+ friend MessageThread* messageThread();
+};
+
+// Get (possibly creating) the global MessageThread object used to pass
+// messages to WebCore.
+MessageThread* messageThread();
+
+} // namespace android
+
+#endif // ANDROID_WEBKIT_MESSAGETHREAD_H
diff --git a/Source/WebKit/android/smoke/MessageTypes.h b/Source/WebKit/android/smoke/MessageTypes.h
new file mode 100644
index 0000000..7da6cb8
--- /dev/null
+++ b/Source/WebKit/android/smoke/MessageTypes.h
@@ -0,0 +1,159 @@
+/*
+ * 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 ANDROID_WEBKIT_MESSAGETYPES_H_
+#define ANDROID_WEBKIT_MESSAGETYPES_H_
+
+#include <wtf/CurrentTime.h>
+
+// TODO(phanna): autogenerate these types!
+
+namespace android {
+
+// Forward declared for friendship!
+class MessageQueue;
+
+// Removes the reference from the typename so we store the actual value in the
+// closure.
+template <typename T> struct remove_reference { typedef T type; };
+template <typename T> struct remove_reference<T&> { typedef T type; };
+
+// Prevent the compiler from inferring the type.
+template <typename T> struct identity { typedef T type; };
+
+// Message base class. Defines the public run() method and contains generic
+// object and member function variables for use in MessageQueue.
+//
+// Note: The template subclass MemberFunctionMessage casts its object and
+// member function to the generic void* and Message::* types. During run(),
+// each template specialization downcasts to the original type and invokes the
+// correct function. This may seem dangerous but the compiler enforces
+// correctness in NewMessage and in the template constructor.
+class Message {
+public:
+ typedef void (Message::*GenericMemberFunction)(void);
+
+ virtual ~Message() {}
+ virtual void run() = 0;
+
+ // The wall time that the message is supposed to run.
+ double m_when;
+
+ void* object() const { return m_object; }
+ GenericMemberFunction member() const { return m_member; }
+
+protected:
+ Message(void* object, GenericMemberFunction member, long delay = 0)
+ : m_object(object)
+ , m_member(member) {
+ m_when = WTF::currentTimeMS() + delay;
+ }
+
+ // Downcast back to the original template params in run(). Also accessed
+ // by MessageQueue to compare messages.
+ void* m_object;
+ GenericMemberFunction m_member;
+
+private:
+ // Disallow copy
+ Message(const Message&);
+};
+
+// Forward declaration for partial specialization.
+template <class T, typename A1>
+class MemberFunctionMessage;
+
+template <class T>
+class MemberFunctionMessage<T, void> : public Message {
+private:
+ typedef void (T::*MemberSignature)();
+
+public:
+ inline MemberFunctionMessage(T* object,
+ MemberSignature member,
+ long delay = 0)
+ : Message(reinterpret_cast<void*>(object),
+ reinterpret_cast<GenericMemberFunction>(member),
+ delay) {}
+
+ virtual void run() {
+ MemberSignature member = reinterpret_cast<MemberSignature>(m_member);
+ (reinterpret_cast<T*>(m_object)->*member)();
+ delete this;
+ }
+};
+
+template <class T>
+inline Message* NewMessage(T* object, void (T::*member)()) {
+ return new MemberFunctionMessage<T, void>(object, member);
+}
+
+template <class T>
+inline Message* NewDelayedMessage(T* object, void (T::*member)(), long delay) {
+ return new MemberFunctionMessage<T, void>(object, member, delay);
+}
+
+template <class T, typename A1>
+class MemberFunctionMessage : public Message {
+private:
+ typedef void (T::*MemberSignature)(A1);
+
+public:
+ inline MemberFunctionMessage(T* object,
+ MemberSignature member,
+ A1 arg1,
+ long delay = 0)
+ : Message(reinterpret_cast<void*>(object),
+ reinterpret_cast<GenericMemberFunction>(member),
+ delay)
+ , m_arg1(arg1) {}
+
+ virtual void run() {
+ MemberSignature member = reinterpret_cast<MemberSignature>(m_member);
+ (reinterpret_cast<T*>(m_object)->*member)(m_arg1);
+ delete this;
+ }
+
+private:
+ typename remove_reference<A1>::type m_arg1;
+};
+
+template <class T, typename A1>
+inline Message* NewMessage(T* object, void (T::*member)(A1),
+ typename identity<A1>::type arg1) {
+ return new MemberFunctionMessage<T, A1>(
+ object, member, arg1);
+}
+
+template <class T, typename A1>
+inline Message* NewDelayedMessage(T* object, void (T::*member)(A1),
+ typename identity<A1>::type arg1, long delay) {
+ return new MemberFunctionMessage<T, A1>(object, member, arg1, delay);
+}
+
+} // namespace android
+
+
+#endif // ANDROID_WEBKIT_MESSAGETYPES_H_
diff --git a/Source/WebKit/android/wds/Command.cpp b/Source/WebKit/android/wds/Command.cpp
new file mode 100644
index 0000000..bd8536f
--- /dev/null
+++ b/Source/WebKit/android/wds/Command.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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 "wds"
+#include "config.h"
+
+#include "AndroidLog.h"
+#include "Command.h"
+#include "Connection.h"
+#include "DebugServer.h"
+#include "Frame.h"
+#include "RenderTreeAsText.h"
+#include "RenderView.h"
+#include "WebViewCore.h"
+#include <utils/Log.h>
+#include <wtf/text/CString.h>
+
+#if ENABLE(WDS)
+
+using namespace WebCore;
+
+namespace android {
+
+namespace WDS {
+
+//------------------------------------------------------------------------------
+// Actual commands -- XXX should be moved somewhere else
+//------------------------------------------------------------------------------
+static bool callDumpRenderTree(const Frame* frame, const Connection* conn) {
+ CString str = externalRepresentation(frame->contentRenderer()).latin1();
+ conn->write(str.data(), str.length());
+ return true;
+}
+
+static bool callDumpDomTree(const Frame* frame, const Connection* conn) {
+ WebViewCore::getWebViewCore(frame->view())->dumpDomTree(true);
+
+ FILE* f = fopen(DOM_TREE_LOG_FILE, "r");
+ if (!f) {
+ conn->write("Dom tree written to logcat\n");
+ } else {
+ char buf[512];
+ while (true) {
+ int nread = fread(buf, 1, sizeof(buf), f);
+ if (nread <= 0)
+ break;
+ conn->write(buf, nread);
+ }
+ fclose(f);
+ }
+ return true;
+}
+
+class WebCoreHandler : public Handler {
+public:
+ virtual void post(TargetThreadFunction func, void* v) const {
+ callOnMainThread(func, v);
+ }
+};
+static WebCoreHandler s_webcoreHandler;
+
+//------------------------------------------------------------------------------
+// End command section
+//------------------------------------------------------------------------------
+
+class InternalCommand : public Command {
+public:
+ InternalCommand(const Command* comm, const Frame* frame,
+ const Connection* connection)
+ : Command(*comm)
+ , m_frame(frame)
+ , m_connection(connection) {}
+ virtual ~InternalCommand() { delete m_connection; }
+
+ void doCommand() const {
+ 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);
+ }
+
+private:
+ const Frame* m_frame;
+ const Connection* m_connection;
+};
+
+static void commandDispatcher(void* v) {
+ InternalCommand* c = static_cast<InternalCommand*>(v);
+ c->doCommand();
+ delete c;
+}
+
+void Command::dispatch() {
+ m_handler.post(commandDispatcher, this);
+}
+
+Vector<const Command*>* Command::s_commands;
+
+void Command::Init() {
+ // Do not initialize twice.
+ if (s_commands)
+ return;
+ // XXX: Move this somewhere else.
+ s_commands = new Vector<const Command*>();
+ s_commands->append(new Command("DDOM", "Dump Dom Tree",
+ callDumpDomTree, s_webcoreHandler));
+ s_commands->append(new Command("DDRT", "Dump Render Tree",
+ callDumpRenderTree, s_webcoreHandler));
+}
+
+Command* Command::Find(const Connection* conn) {
+ char buf[COMMAND_LENGTH];
+ if (conn->read(buf, sizeof(buf)) != COMMAND_LENGTH)
+ return NULL;
+
+ // Linear search of commands. TODO: binary search when more commands are
+ // added.
+ Vector<const Command*>::const_iterator i = s_commands->begin();
+ Vector<const Command*>::const_iterator end = s_commands->end();
+ while (i != end) {
+ if (strncmp(buf, (*i)->name(), sizeof(buf)) == 0)
+ return new InternalCommand(*i, server()->getFrame(0), conn);
+ i++;
+ }
+ return NULL;
+}
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/Command.h b/Source/WebKit/android/wds/Command.h
new file mode 100644
index 0000000..90861d4
--- /dev/null
+++ b/Source/WebKit/android/wds/Command.h
@@ -0,0 +1,108 @@
+/*
+ * 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 WDS_COMMAND_H
+#define WDS_COMMAND_H
+
+#include "wtf/MainThread.h"
+#include "wtf/Vector.h"
+
+namespace WebCore {
+class Frame;
+}
+
+using namespace WTF;
+using namespace WebCore;
+
+namespace android {
+
+// WebCore Debug Server
+namespace WDS {
+
+class Connection;
+
+// Command identifier length
+#define COMMAND_LENGTH 4
+
+// The dispatcher function called with a Frame for context and the established
+// connection to the client. The connection can be used to read and write to the
+// client application. Return true on successful completion of the command,
+// return false to indicate failure.
+typedef bool (*DispatchFunction)(const Frame*, const Connection*);
+
+// Note: Although the type is named MainThreadFunction, it may not always be
+// the main thread. The type is generic enough to reuse here but named
+// something more appropriate.
+typedef MainThreadFunction TargetThreadFunction;
+
+// Helper class to dipatch functions on a particular thread.
+class Handler {
+public:
+ virtual ~Handler() {}
+ virtual void post(TargetThreadFunction, void*) const = 0;
+};
+
+// Class for containing information about particular commands.
+class Command {
+public:
+ Command(const char* name, const char* desc, const DispatchFunction func,
+ const Handler& handler)
+ : m_name(name)
+ , m_description(desc)
+ , m_dispatch(func)
+ , m_handler(handler) {}
+ Command(const Command& comm)
+ : m_name(comm.m_name)
+ , m_description(comm.m_description)
+ , m_dispatch(comm.m_dispatch)
+ , m_handler(comm.m_handler) {}
+ virtual ~Command() {}
+
+ // Initialize the debug server commands
+ static void Init();
+
+ // Find the command specified by the client request.
+ static Command* Find(const Connection* conn);
+
+ // Dispatch this command
+ void dispatch();
+
+ const char* name() const { return m_name; }
+
+protected:
+ const char* m_name;
+ const char* m_description;
+ const DispatchFunction m_dispatch;
+
+private:
+ const Handler& m_handler;
+ static Vector<const Command*>* s_commands;
+};
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/Connection.cpp b/Source/WebKit/android/wds/Connection.cpp
new file mode 100644
index 0000000..d7e55ac
--- /dev/null
+++ b/Source/WebKit/android/wds/Connection.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "wds"
+#include "config.h"
+
+#include "DebugServer.h" // used for ENABLE_WDS
+#include "Connection.h"
+#include <arpa/inet.h>
+#include <string.h>
+#include <utils/Log.h>
+
+#if ENABLE(WDS)
+
+#define MAX_CONNECTION_QUEUE 5
+#define log_errno(x) LOGE("%s: %d", x, strerror(errno))
+
+namespace android {
+
+namespace WDS {
+
+bool Socket::open() {
+ m_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (m_fd < 0) {
+ log_errno("Failed to create file descriptor");
+ return false;
+ }
+ return true;
+}
+
+bool ConnectionServer::connect(short port) {
+ if (!m_socket.open())
+ return false;
+ int fd = m_socket.fd();
+
+ // Build our sockaddr_in structure use to listen to incoming connections
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+
+ // Try to bind to the given port
+ if (bind(fd, (sockaddr*) &addr, sizeof(addr)) < 0) {
+ log_errno("Failed to bind to local host");
+ return false;
+ }
+
+ // Try to listen
+ if (listen(fd, MAX_CONNECTION_QUEUE) < 0) {
+ log_errno("Failed to listen");
+ return false;
+ }
+
+ return true;
+}
+
+Connection* ConnectionServer::accept() const {
+ int conn = ::accept(m_socket.fd(), NULL, NULL);
+ if (conn < 0) {
+ log_errno("Accept failed");
+ return NULL;
+ }
+ return new Connection(conn);
+}
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/Connection.h b/Source/WebKit/android/wds/Connection.h
new file mode 100644
index 0000000..d67179e
--- /dev/null
+++ b/Source/WebKit/android/wds/Connection.h
@@ -0,0 +1,86 @@
+/*
+ * 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 WDS_CONNECTION_H
+#define WDS_CONNECTION_H
+
+#include <unistd.h>
+#include <sys/socket.h>
+
+namespace android {
+
+namespace WDS {
+
+class Socket {
+public:
+ Socket(): m_fd(-1) {}
+ Socket(int fd): m_fd(fd) {}
+ ~Socket() {
+ if (m_fd != -1) {
+ shutdown(m_fd, SHUT_RDWR);
+ close(m_fd);
+ }
+ }
+ // Open a new socket using PF_INET and SOCK_STREAM
+ bool open();
+ int fd() const { return m_fd; }
+private:
+ int m_fd;
+};
+
+class Connection {
+public:
+ Connection(int conn): m_socket(conn) {}
+ int read(char buf[], size_t length) const {
+ return recv(m_socket.fd(), buf, length, 0);
+ }
+ int write(const char buf[], size_t length) const {
+ return send(m_socket.fd(), buf, length, 0);
+ }
+ int write(const char buf[]) const {
+ return write(buf, strlen(buf));
+ }
+private:
+ Socket m_socket;
+};
+
+class ConnectionServer {
+public:
+ ConnectionServer() {}
+
+ // Establish a connection to the local host on the given port.
+ bool connect(short port);
+
+ // Blocks on the established socket until a new connection arrives.
+ Connection* accept() const;
+private:
+ Socket m_socket;
+};
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/DebugServer.cpp b/Source/WebKit/android/wds/DebugServer.cpp
new file mode 100644
index 0000000..f33a65b
--- /dev/null
+++ b/Source/WebKit/android/wds/DebugServer.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "wds"
+#include "config.h"
+
+#include "Command.h"
+#include "Connection.h"
+#include "DebugServer.h"
+#include "wtf/MainThread.h"
+#include "wtf/Threading.h"
+#include <arpa/inet.h>
+#include <cutils/properties.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#if ENABLE(WDS)
+
+#define DEFAULT_PORT 9999
+#define log_errno(x) LOGE("%s: %d", x, strerror(errno))
+
+namespace android {
+
+namespace WDS {
+
+static DebugServer* s_server = NULL;
+
+// Main thread function for createThread
+static void* mainThread(void* v) {
+ DebugServer* server = static_cast<DebugServer*>(v);
+ server->start();
+ delete server;
+ s_server = NULL;
+ return NULL;
+}
+
+DebugServer* server() {
+ if (s_server == NULL)
+ s_server = new DebugServer();
+ return s_server;
+}
+
+DebugServer::DebugServer() {
+ // Read webcore.wds.enable to determine if the debug server should run
+ char buf[PROPERTY_VALUE_MAX];
+ int ret = property_get("webcore.wds.enable", buf, NULL);
+ if (ret != -1 && strcmp(buf, "1") == 0) {
+ LOGD("WDS Enabled");
+ m_threadId = createThread(mainThread, this, "WDS");
+ }
+ // Initialize the available commands.
+ Command::Init();
+}
+
+void DebugServer::start() {
+ LOGD("DebugServer thread started");
+
+ ConnectionServer cs;
+ if (!cs.connect(DEFAULT_PORT)) {
+ LOGE("Failed to start the server socket connection");
+ return;
+ }
+
+ while (true ) {
+ LOGD("Waiting for incoming connections...");
+ Connection* conn = cs.accept();
+ if (!conn) {
+ log_errno("Failed to accept new connections");
+ return;
+ }
+ LOGD("...Connection established");
+
+ Command* c = Command::Find(conn);
+ if (!c) {
+ LOGE("Could not find matching command");
+ delete conn;
+ } else {
+ // Dispatch the command, it will handle cleaning up the connection
+ // when finished.
+ c->dispatch();
+ }
+ }
+
+ LOGD("DebugServer thread finished");
+}
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/DebugServer.h b/Source/WebKit/android/wds/DebugServer.h
new file mode 100644
index 0000000..92edad9
--- /dev/null
+++ b/Source/WebKit/android/wds/DebugServer.h
@@ -0,0 +1,77 @@
+/*
+ * 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 DEBUGSERVER_H
+#define DEBUGSERVER_H
+
+// Turn on the wds feature in webkit
+#define ENABLE_WDS 0
+
+#include "wtf/Threading.h"
+#include "wtf/Vector.h"
+
+// Forward declarations.
+namespace WebCore {
+ class Frame;
+}
+
+using namespace WTF;
+using namespace WebCore;
+
+namespace android {
+
+// WebCore Debug Server
+namespace WDS {
+
+class DebugServer : WTFNoncopyable::Noncopyable {
+public:
+ void start();
+ void addFrame(Frame* frame) {
+ m_frames.append(frame);
+ }
+ void removeFrame(Frame* frame) {
+ size_t i = m_frames.find(frame);
+ if (i != notFound)
+ m_frames.remove(i);
+ }
+ Frame* getFrame(unsigned idx) {
+ if (idx < m_frames.size())
+ return m_frames.at(idx);
+ return NULL;
+ }
+private:
+ DebugServer();
+ WTF::Vector<Frame*> m_frames;
+ ThreadIdentifier m_threadId;
+ friend DebugServer* server();
+};
+
+DebugServer* server();
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/wds/client/AdbConnection.cpp b/Source/WebKit/android/wds/client/AdbConnection.cpp
new file mode 100644
index 0000000..465f9c3
--- /dev/null
+++ b/Source/WebKit/android/wds/client/AdbConnection.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 "wdsclient"
+
+#include "AdbConnection.h"
+#include "ClientUtils.h"
+#include "Device.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+void AdbConnection::close() {
+ if (m_fd != -1) {
+ shutdown(m_fd, SHUT_RDWR);
+ ::close(m_fd);
+ m_fd = -1;
+ }
+}
+
+// Default adb port
+#define ADB_PORT 5037
+
+bool AdbConnection::connect() {
+ // Some commands (host:devices for example) close the connection so we call
+ // connect after the response.
+ close();
+
+ m_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (m_fd < 0) {
+ log_errno("Failed to create socket for connecting to adb");
+ return false;
+ }
+
+ // Create the socket address struct
+ sockaddr_in adb;
+ createTcpSocket(adb, ADB_PORT);
+
+ // Connect to adb
+ if (::connect(m_fd, (sockaddr*) &adb, sizeof(adb)) < 0) {
+ log_errno("Failed to connect to adb");
+ return false;
+ }
+
+ // Connected
+ return true;
+}
+
+// Adb protocol stuff
+#define MAX_COMMAND_LENGTH 1024
+#define PAYLOAD_LENGTH 4
+#define PAYLOAD_FORMAT "%04X"
+
+bool AdbConnection::sendRequest(const char* fmt, ...) const {
+ if (m_fd == -1) {
+ LOGE("Connection is closed");
+ return false;
+ }
+
+ // Build the command (service)
+ char buf[MAX_COMMAND_LENGTH];
+ va_list args;
+ va_start(args, fmt);
+ int res = vsnprintf(buf, MAX_COMMAND_LENGTH, fmt, args);
+ va_end(args);
+
+ LOGV("Sending command: %04X%.*s", res, res, buf);
+
+ // Construct the payload length
+ char payloadLen[PAYLOAD_LENGTH + 1];
+ snprintf(payloadLen, sizeof(payloadLen), PAYLOAD_FORMAT, res);
+
+ // First, send the payload length
+ if (send(m_fd, payloadLen, PAYLOAD_LENGTH, 0) < 0) {
+ log_errno("Failure when sending payload");
+ return false;
+ }
+
+ // Send the actual command
+ if (send(m_fd, buf, res, 0) < 0) {
+ log_errno("Failure when sending command");
+ return false;
+ }
+
+ // Check for the OKAY from adb
+ return checkOkayResponse();
+}
+
+static void printFailureMessage(int fd) {
+ // Grab the payload length
+ char lenStr[PAYLOAD_LENGTH + 1];
+ int payloadLen = recv(fd, lenStr, sizeof(lenStr) - 1, 0);
+ LOG_ASSERT(payloadLen == PAYLOAD_LENGTH, "Incorrect payload size");
+ lenStr[PAYLOAD_LENGTH] = 0;
+
+ // Parse the hex payload
+ payloadLen = strtol(lenStr, NULL, 16);
+ if (payloadLen < 0)
+ return;
+
+ // Grab the message
+ char* msg = new char[payloadLen + 1]; // include null-terminator
+ int res = recv(fd, msg, payloadLen, 0);
+ if (res < 0) {
+ log_errno("Failure reading failure message from adb");
+ return;
+ } else if (res != payloadLen) {
+ LOGE("Incorrect payload length %d - expected %d", res, payloadLen);
+ return;
+ }
+ msg[res] = 0;
+
+ // Tell somebody about it
+ LOGE("Received failure from adb: %s", msg);
+
+ // Cleanup
+ delete[] msg;
+}
+
+#define ADB_RESPONSE_LENGTH 4
+
+bool AdbConnection::checkOkayResponse() const {
+ LOG_ASSERT(m_fd != -1, "Connection has been closed!");
+
+ char buf[ADB_RESPONSE_LENGTH];
+ int res = recv(m_fd, buf, sizeof(buf), 0);
+ if (res < 0) {
+ log_errno("Failure reading response from adb");
+ return false;
+ }
+
+ // Check for a response other than OKAY/FAIL
+ if ((res == ADB_RESPONSE_LENGTH) && (strncmp(buf, "OKAY", res) == 0)) {
+ 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;
+ }
+ LOGE("Incorrect response from adb - '%.*s'", res, buf);
+ return false;
+}
+
+void AdbConnection::clearDevices() {
+ for (unsigned i = 0; i < m_devices.size(); i++)
+ delete m_devices.editItemAt(i);
+ m_devices.clear();
+}
+
+const DeviceList& AdbConnection::getDeviceList() {
+ // Clear the current device list
+ clearDevices();
+
+ if (m_fd == -1) {
+ LOGE("Connection is closed");
+ return m_devices;
+ }
+
+ // Try to send the device list request
+ if (!sendRequest("host:devices")) {
+ LOGE("Failed to get device list from adb");
+ return m_devices;
+ }
+
+ // Get the payload length
+ char lenStr[PAYLOAD_LENGTH + 1];
+ int res = recv(m_fd, lenStr, sizeof(lenStr) - 1, 0);
+ if (res < 0) {
+ log_errno("Failure to read payload size of device list");
+ return m_devices;
+ }
+ lenStr[PAYLOAD_LENGTH] = 0;
+
+ // Parse the hex payload
+ int payloadLen = strtol(lenStr, NULL, 16);
+ if (payloadLen < 0)
+ return m_devices;
+
+ // Grab the list of devices. The format is as follows:
+ // <serialno><tab><state><newline>
+ char* msg = new char[payloadLen + 1];
+ res = recv(m_fd, msg, payloadLen, 0);
+ if (res < 0) {
+ log_errno("Failure reading the device list");
+ return m_devices;
+ } else if (res != payloadLen) {
+ LOGE("Incorrect payload length %d - expected %d", res, payloadLen);
+ return m_devices;
+ }
+ msg[res] = 0;
+
+ char serial[32];
+ char state[32];
+ int numRead;
+ char* ptr = msg;
+ while (sscanf(ptr, "%31s\t%31s\n%n", serial, state, &numRead) > 1) {
+ Device::DeviceType t = Device::DEVICE;
+ static const char emulator[] = "emulator-";
+ if (strncmp(serial, emulator, sizeof(emulator) - 1) == 0)
+ t = Device::EMULATOR;
+ LOGV("Adding device %s (%s)", serial, state);
+ m_devices.add(new Device(serial, t, this));
+
+ // Reset for the next line
+ ptr += numRead;
+ }
+ // Cleanup
+ delete[] msg;
+
+ return m_devices;
+}
diff --git a/Source/WebKit/android/wds/client/AdbConnection.h b/Source/WebKit/android/wds/client/AdbConnection.h
new file mode 100644
index 0000000..58bad67
--- /dev/null
+++ b/Source/WebKit/android/wds/client/AdbConnection.h
@@ -0,0 +1,47 @@
+/*
+ * 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 WDS_ADB_CONNECTION_H
+#define WDS_ADB_CONNECTION_H
+
+#include "DeviceList.h"
+
+class AdbConnection {
+public:
+ AdbConnection() : m_fd(-1) {}
+ ~AdbConnection() { clearDevices(); }
+ void close();
+ bool connect();
+ bool sendRequest(const char* fmt, ...) const;
+ const DeviceList& getDeviceList();
+
+private:
+ bool checkOkayResponse() const;
+ void clearDevices();
+ DeviceList m_devices;
+ int m_fd;
+};
+
+#endif
diff --git a/Source/WebKit/android/wds/client/Android.mk b/Source/WebKit/android/wds/client/Android.mk
new file mode 100644
index 0000000..db79dd4
--- /dev/null
+++ b/Source/WebKit/android/wds/client/Android.mk
@@ -0,0 +1,39 @@
+##
+## 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.
+##
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AdbConnection.cpp \
+ ClientUtils.cpp \
+ Device.cpp \
+ main.cpp
+
+LOCAL_STATIC_LIBRARIES := liblog libutils libcutils
+
+LOCAL_MODULE:= wdsclient
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/Source/WebKit/android/wds/client/ClientUtils.cpp b/Source/WebKit/android/wds/client/ClientUtils.cpp
new file mode 100644
index 0000000..f8ca889
--- /dev/null
+++ b/Source/WebKit/android/wds/client/ClientUtils.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "ClientUtils.h"
+#include <arpa/inet.h>
+#include <string.h>
+
+void createTcpSocket(sockaddr_in& addr, short port) {
+ memset(&addr, 0, sizeof(sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+}
diff --git a/Source/WebKit/android/wds/client/ClientUtils.h b/Source/WebKit/android/wds/client/ClientUtils.h
new file mode 100644
index 0000000..5da1624
--- /dev/null
+++ b/Source/WebKit/android/wds/client/ClientUtils.h
@@ -0,0 +1,46 @@
+/*
+ * 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 WDS_CLIENT_UTILS_H
+#define WDS_CLIENT_UTILS_H
+
+#include <arpa/inet.h>
+
+/*
+ * included for sockaddr_in structure, AF_INET definiton and etc.
+ */
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+
+// Callers need to include Log.h and errno.h to use this macro
+#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
+void createTcpSocket(sockaddr_in& addr, short port);
+
+#endif
diff --git a/Source/WebKit/android/wds/client/Device.cpp b/Source/WebKit/android/wds/client/Device.cpp
new file mode 100644
index 0000000..789a89d
--- /dev/null
+++ b/Source/WebKit/android/wds/client/Device.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "AdbConnection.h"
+#include "Device.h"
+
+bool Device::sendRequest(const char* req) const {
+ return m_connection->sendRequest("host-serial:%s:%s", m_name, req);
+}
diff --git a/Source/WebKit/android/wds/client/Device.h b/Source/WebKit/android/wds/client/Device.h
new file mode 100644
index 0000000..39d4b12
--- /dev/null
+++ b/Source/WebKit/android/wds/client/Device.h
@@ -0,0 +1,62 @@
+/*
+ * 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 WDS_DEVICE_H
+#define WDS_DEVICE_H
+
+#include <stdlib.h>
+
+class AdbConnection;
+
+class Device {
+public:
+ // Type of device.
+ // TODO: Add simulator support
+ enum DeviceType {
+ NONE = -1,
+ EMULATOR,
+ DEVICE
+ };
+
+ // Takes ownership of name
+ Device(char* name, DeviceType type, const AdbConnection* conn)
+ : m_connection(conn)
+ , m_name(strdup(name))
+ , m_type(type) {}
+ ~Device() { free(m_name); }
+
+ const char* name() const { return m_name; }
+ DeviceType type() const { return m_type; }
+
+ // Send a request to this device.
+ bool sendRequest(const char* req) const;
+
+private:
+ const AdbConnection* m_connection;
+ char* m_name;
+ DeviceType m_type;
+};
+
+#endif
diff --git a/Source/WebKit/android/wds/client/DeviceList.h b/Source/WebKit/android/wds/client/DeviceList.h
new file mode 100644
index 0000000..45bfb87
--- /dev/null
+++ b/Source/WebKit/android/wds/client/DeviceList.h
@@ -0,0 +1,35 @@
+/*
+ * 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 WDS_DEVICE_LIST_H
+#define WDS_DEVICE_LIST_H
+
+#include <utils/Vector.h>
+
+class Device;
+
+typedef android::Vector<Device*> DeviceList;
+
+#endif
diff --git a/Source/WebKit/android/wds/client/main.cpp b/Source/WebKit/android/wds/client/main.cpp
new file mode 100644
index 0000000..1c7d856
--- /dev/null
+++ b/Source/WebKit/android/wds/client/main.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 "wdsclient"
+
+#include "AdbConnection.h"
+#include "ClientUtils.h"
+#include "Device.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#define DEFAULT_WDS_PORT 9999
+#define STR(x) #x
+#define XSTR(x) STR(x)
+#define PORT_STR XSTR(DEFAULT_WDS_PORT)
+
+int wds_open() {
+ // Create the structure for connecting to the forwarded 9999 port
+ sockaddr_in addr;
+ createTcpSocket(addr, DEFAULT_WDS_PORT);
+
+ // Create our socket
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_errno("Failed to create file descriptor");
+ return -1;
+ }
+ // Connect to the remote wds server thread
+ if (connect(fd, (sockaddr*)&addr, sizeof(addr)) < 0) {
+ log_errno("Failed to connect to remote debug server");
+ return -1;
+ }
+ return fd;
+}
+
+// Clean up the file descriptor and connections
+void wds_close(int fd) {
+ if (fd != -1) {
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
+ }
+}
+
+int main(int argc, char** argv) {
+
+ Device::DeviceType type = Device::NONE;
+
+ if (argc <= 1) {
+ LOGE("wdsclient takes at least 1 argument");
+ return 1;
+ } else {
+ // Parse the options, look for -e or -d to choose a device.
+ while (true) {
+ int c = getopt(argc, argv, "ed");
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'e':
+ type = Device::EMULATOR;
+ break;
+ case 'd':
+ type = Device::DEVICE;
+ break;
+ default:
+ break;
+ }
+ }
+ if (optind == argc) {
+ LOGE("No command specified");
+ return 1;
+ }
+ }
+
+ // Do the initial connection.
+ AdbConnection conn;
+ conn.connect();
+
+ const DeviceList& devices = conn.getDeviceList();
+ // host:devices closes the connection, reconnect
+ conn.connect();
+
+ // No device specified and more than one connected, bail
+ if (type == Device::NONE && devices.size() > 1) {
+ LOGE("More than one device/emulator, please specify with -e or -d");
+ return 1;
+ } else if (devices.size() == 0) {
+ LOGE("No devices connected");
+ return 1;
+ }
+
+ // Find the correct device
+ const Device* device = NULL;
+ if (type == Device::NONE)
+ device = devices[0]; // grab the only one
+ else {
+ // Search for a matching device type
+ for (unsigned i = 0; i < devices.size(); i++) {
+ if (devices[i]->type() == type) {
+ device = devices[i];
+ break;
+ }
+ }
+ }
+
+ if (!device) {
+ LOGE("No device found!");
+ return 1;
+ }
+
+ // Forward tcp:9999
+ if (!device->sendRequest("forward:tcp:" PORT_STR ";tcp:" PORT_STR)) {
+ LOGE("Failed to send forwarding request");
+ return 1;
+ }
+
+ 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) {
+ LOGE("Commands must be 4 characters '%s'", command);
+ return 1;
+ }
+
+ // Open the wds connection
+ int wdsFd = wds_open();
+ if (wdsFd == -1)
+ return 1;
+
+ // Send the command specified
+ send(wdsFd, command, WDS_COMMAND_LENGTH, 0); // commands are 4 bytes
+
+ // Read and display the response
+ char response[256];
+ int res = 0;
+ while ((res = recv(wdsFd, response, sizeof(response), 0)) > 0)
+ printf("%.*s", res, response);
+ printf("\n\n");
+
+ // Shutdown
+ wds_close(wdsFd);
+
+ return 0;
+}