summaryrefslogtreecommitdiffstats
path: root/WebKit/android
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebKit/android
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebKit/android')
-rw-r--r--WebKit/android/AndroidLog.h47
-rw-r--r--WebKit/android/JavaVM/jni.h32
-rw-r--r--WebKit/android/RenderSkinAndroid.cpp71
-rw-r--r--WebKit/android/RenderSkinAndroid.h92
-rw-r--r--WebKit/android/RenderSkinButton.cpp107
-rw-r--r--WebKit/android/RenderSkinButton.h52
-rw-r--r--WebKit/android/RenderSkinCombo.cpp71
-rw-r--r--WebKit/android/RenderSkinCombo.h65
-rw-r--r--WebKit/android/RenderSkinRadio.cpp86
-rw-r--r--WebKit/android/RenderSkinRadio.h59
-rw-r--r--WebKit/android/TimeCounter.cpp156
-rw-r--r--WebKit/android/TimeCounter.h98
-rw-r--r--WebKit/android/TimerClient.h42
-rw-r--r--WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp257
-rw-r--r--WebKit/android/WebCoreSupport/ChromeClientAndroid.h120
-rw-r--r--WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp46
-rw-r--r--WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h50
-rw-r--r--WebKit/android/WebCoreSupport/DragClientAndroid.cpp45
-rw-r--r--WebKit/android/WebCoreSupport/DragClientAndroid.h51
-rw-r--r--WebKit/android/WebCoreSupport/EditorClientAndroid.cpp241
-rw-r--r--WebKit/android/WebCoreSupport/EditorClientAndroid.h113
-rw-r--r--WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp1106
-rw-r--r--WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h242
-rw-r--r--WebKit/android/WebCoreSupport/InspectorClientAndroid.h64
-rw-r--r--WebKit/android/jni/JavaBridge.cpp319
-rw-r--r--WebKit/android/jni/JavaSharedClient.cpp111
-rw-r--r--WebKit/android/jni/JavaSharedClient.h53
-rw-r--r--WebKit/android/jni/PictureSet.cpp665
-rw-r--r--WebKit/android/jni/PictureSet.h98
-rw-r--r--WebKit/android/jni/WebCoreFrameBridge.cpp1234
-rw-r--r--WebKit/android/jni/WebCoreFrameBridge.h134
-rw-r--r--WebKit/android/jni/WebCoreJni.cpp155
-rw-r--r--WebKit/android/jni/WebCoreJni.h74
-rw-r--r--WebKit/android/jni/WebCoreRefObject.h46
-rw-r--r--WebKit/android/jni/WebCoreResourceLoader.cpp333
-rw-r--r--WebKit/android/jni/WebCoreResourceLoader.h74
-rw-r--r--WebKit/android/jni/WebCoreViewBridge.h90
-rw-r--r--WebKit/android/jni/WebFrameView.cpp108
-rw-r--r--WebKit/android/jni/WebFrameView.h61
-rw-r--r--WebKit/android/jni/WebHistory.cpp882
-rw-r--r--WebKit/android/jni/WebHistory.h76
-rw-r--r--WebKit/android/jni/WebIconDatabase.cpp236
-rw-r--r--WebKit/android/jni/WebIconDatabase.h77
-rw-r--r--WebKit/android/jni/WebSettings.cpp356
-rw-r--r--WebKit/android/jni/WebViewCore.cpp2582
-rw-r--r--WebKit/android/jni/WebViewCore.h420
-rw-r--r--WebKit/android/nav/CacheBuilder.cpp3076
-rw-r--r--WebKit/android/nav/CacheBuilder.h272
-rw-r--r--WebKit/android/nav/CachedDebug.h74
-rw-r--r--WebKit/android/nav/CachedFrame.cpp1318
-rw-r--r--WebKit/android/nav/CachedFrame.h228
-rw-r--r--WebKit/android/nav/CachedHistory.cpp203
-rw-r--r--WebKit/android/nav/CachedHistory.h90
-rw-r--r--WebKit/android/nav/CachedNode.cpp346
-rw-r--r--WebKit/android/nav/CachedNode.h235
-rw-r--r--WebKit/android/nav/CachedNodeType.h41
-rw-r--r--WebKit/android/nav/CachedPrefix.h46
-rw-r--r--WebKit/android/nav/CachedRoot.cpp1087
-rw-r--r--WebKit/android/nav/CachedRoot.h115
-rw-r--r--WebKit/android/nav/FindCanvas.cpp467
-rw-r--r--WebKit/android/nav/FindCanvas.h207
-rw-r--r--WebKit/android/nav/SelectText.cpp263
-rw-r--r--WebKit/android/nav/SelectText.h42
-rw-r--r--WebKit/android/nav/WebView.cpp2322
-rw-r--r--WebKit/android/plugins/ANPCanvasInterface.cpp187
-rw-r--r--WebKit/android/plugins/ANPKeyCodes.h125
-rw-r--r--WebKit/android/plugins/ANPLogInterface.cpp61
-rw-r--r--WebKit/android/plugins/ANPMatrixInterface.cpp168
-rw-r--r--WebKit/android/plugins/ANPPaintInterface.cpp213
-rw-r--r--WebKit/android/plugins/ANPSoundInterface.cpp149
-rw-r--r--WebKit/android/plugins/ANPTypefaceInterface.cpp74
-rw-r--r--WebKit/android/plugins/ANPWindowInterface.cpp62
-rw-r--r--WebKit/android/plugins/PluginDebugAndroid.h46
-rw-r--r--WebKit/android/plugins/PluginTimer.cpp112
-rw-r--r--WebKit/android/plugins/PluginTimer.h78
-rw-r--r--WebKit/android/plugins/PluginViewBridgeAndroid.cpp38
-rw-r--r--WebKit/android/plugins/PluginViewBridgeAndroid.h48
-rw-r--r--WebKit/android/plugins/PluginWidgetAndroid.cpp146
-rw-r--r--WebKit/android/plugins/PluginWidgetAndroid.h94
-rw-r--r--WebKit/android/plugins/SkANP.cpp106
-rw-r--r--WebKit/android/plugins/SkANP.h80
-rw-r--r--WebKit/android/plugins/android_npapi.h620
-rw-r--r--WebKit/android/plugins/sample/Android.mk50
-rw-r--r--WebKit/android/plugins/sample/PluginObject.cpp178
-rw-r--r--WebKit/android/plugins/sample/PluginObject.h70
-rw-r--r--WebKit/android/plugins/sample/main.cpp435
-rw-r--r--WebKit/android/plugins/sample/main.h30
-rw-r--r--WebKit/android/plugins/sample/pluginGraphics.cpp181
-rw-r--r--WebKit/android/plugins/sample/pluginGraphics.h40
-rw-r--r--WebKit/android/sort.cpp56
-rw-r--r--WebKit/android/stl/algorithm274
-rw-r--r--WebKit/android/stl/concept_checks.h812
-rw-r--r--WebKit/android/stl/cstring128
-rw-r--r--WebKit/android/stl/heap.h48
-rw-r--r--WebKit/android/stl/limits31
-rw-r--r--WebKit/android/stl/memory132
-rw-r--r--WebKit/android/stl/new.h25
-rw-r--r--WebKit/android/stl/stl_config.h577
-rw-r--r--WebKit/android/stl/stl_heap.h298
-rw-r--r--WebKit/android/stl/stl_iterator_base.h274
-rw-r--r--WebKit/android/stl/strings.h25
-rw-r--r--WebKit/android/wds/Command.cpp154
-rw-r--r--WebKit/android/wds/Command.h108
-rw-r--r--WebKit/android/wds/Connection.cpp93
-rw-r--r--WebKit/android/wds/Connection.h85
-rw-r--r--WebKit/android/wds/DebugServer.cpp116
-rw-r--r--WebKit/android/wds/DebugServer.h77
-rw-r--r--WebKit/android/wds/client/AdbConnection.cpp237
-rw-r--r--WebKit/android/wds/client/AdbConnection.h47
-rw-r--r--WebKit/android/wds/client/Android.mk41
-rw-r--r--WebKit/android/wds/client/ClientUtils.cpp35
-rw-r--r--WebKit/android/wds/client/ClientUtils.h38
-rw-r--r--WebKit/android/wds/client/Device.cpp31
-rw-r--r--WebKit/android/wds/client/Device.h62
-rw-r--r--WebKit/android/wds/client/DeviceList.h35
-rw-r--r--WebKit/android/wds/client/main.cpp173
116 files changed, 29332 insertions, 0 deletions
diff --git a/WebKit/android/AndroidLog.h b/WebKit/android/AndroidLog.h
new file mode 100644
index 0000000..e62153b
--- /dev/null
+++ b/WebKit/android/AndroidLog.h
@@ -0,0 +1,47 @@
+/*
+ * 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 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 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 "/data/data/com.android.browser/displayTree.txt"
+
+#endif /* ANDROIDLOG_H_ */
diff --git a/WebKit/android/JavaVM/jni.h b/WebKit/android/JavaVM/jni.h
new file mode 100644
index 0000000..93c73f3
--- /dev/null
+++ b/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 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 _JNI_COVER_H_
+#define _JNI_COVER_H_
+
+#include "nativehelper/jni.h"
+#define AttachCurrentThread(a, b) AttachCurrentThread((JNIEnv**) a, b)
+
+#endif
diff --git a/WebKit/android/RenderSkinAndroid.cpp b/WebKit/android/RenderSkinAndroid.cpp
new file mode 100644
index 0000000..a261b27
--- /dev/null
+++ b/WebKit/android/RenderSkinAndroid.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 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"
+
+#include "config.h"
+#include "RenderSkinAndroid.h"
+#include "RenderSkinButton.h"
+#include "RenderSkinCombo.h"
+#include "RenderSkinRadio.h"
+#include "SkImageDecoder.h"
+
+#include "utils/AssetManager.h"
+#include "utils/Asset.h"
+
+namespace WebCore {
+
+RenderSkinAndroid::RenderSkinAndroid()
+ : m_height(0)
+ , m_width(0)
+{}
+
+void RenderSkinAndroid::Init(android::AssetManager* am)
+{
+ RenderSkinButton::Init(am);
+ RenderSkinCombo::Init(am);
+ RenderSkinRadio::Init(am);
+}
+
+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/WebKit/android/RenderSkinAndroid.h b/WebKit/android/RenderSkinAndroid.h
new file mode 100644
index 0000000..c693dd1
--- /dev/null
+++ b/WebKit/android/RenderSkinAndroid.h
@@ -0,0 +1,92 @@
+/*
+ * 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 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 RenderSkinAndroid_h
+#define RenderSkinAndroid_h
+
+namespace android {
+ class AssetManager;
+}
+
+class SkBitmap;
+
+namespace WebCore {
+class Node;
+class PlatformGraphicsContext;
+
+/* RenderSkinAndroid is the base class for all RenderSkins. Form elements each have a
+ * subclass for drawing themselves.
+ */
+class RenderSkinAndroid
+{
+public:
+ RenderSkinAndroid();
+ virtual ~RenderSkinAndroid() {}
+
+ enum State {
+ kDisabled,
+ kNormal,
+ kFocused,
+ kPressed,
+
+ kNumStates
+ };
+
+ /**
+ * Initialize the Android skinning system. The AssetManager may be used to find resources used
+ * in rendering.
+ */
+ static void Init(android::AssetManager*);
+
+ /* 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);
+
+ /* draw() tells the skin to draw itself, and returns true if the skin needs
+ * a redraw to animations, false otherwise
+ */
+ virtual bool draw(PlatformGraphicsContext*) { return false; }
+
+ /* notifyState() checks to see if the element is checked, focused, and enabled
+ * it must be implemented in the subclass
+ */
+ virtual void notifyState(Node* element) { }
+
+ /* setDim() tells the skin its width and height
+ */
+ virtual void setDim(int width, int height) { m_width = width; m_height = height; }
+
+protected:
+ int m_height;
+ int m_width;
+
+};
+
+} // WebCore
+
+#endif
+
diff --git a/WebKit/android/RenderSkinButton.cpp b/WebKit/android/RenderSkinButton.cpp
new file mode 100644
index 0000000..c878cb9
--- /dev/null
+++ b/WebKit/android/RenderSkinButton.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 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"
+
+#include "config.h"
+#include "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinButton.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include "SkRect.h"
+#include <utils/Debug.h>
+#include <utils/Log.h>
+
+struct PatchData {
+ const char* name;
+ int8_t outset, margin;
+};
+
+static const PatchData gFiles[] =
+ {
+ { "res/drawable/btn_default_normal_disable.9.png", 2, 7 },
+ { "res/drawable/btn_default_normal.9.png", 2, 7 },
+ { "res/drawable/btn_default_selected.9.png", 2, 7 },
+ { "res/drawable/btn_default_pressed.9.png", 2, 7 }
+ };
+
+static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])];
+static bool gDecoded;
+
+namespace WebCore {
+
+void RenderSkinButton::Init(android::AssetManager* am)
+{
+ static bool gInited;
+ if (gInited)
+ return;
+
+ gInited = true;
+ gDecoded = true;
+ for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) {
+ if (!RenderSkinAndroid::DecodeBitmap(am, gFiles[i].name, &gButton[i])) {
+ gDecoded = false;
+ LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ break;
+ }
+ }
+
+ // 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)
+{
+ // 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;
+ }
+
+ // Ensure that the state is within the valid range of our array.
+ SkASSERT(static_cast<unsigned>(newState) <
+ static_cast<unsigned>(RenderSkinAndroid::kNumStates));
+
+ // Set up the ninepatch information for drawing.
+ SkRect bounds;
+ android_setrect(&bounds, r);
+ const PatchData& pd = gFiles[newState];
+ int marginValue = pd.margin + pd.outset;
+
+ SkIRect margin;
+
+ margin.set(marginValue, marginValue, marginValue, marginValue);
+
+ // Draw to the canvas.
+ SkNinePatch::DrawNine(canvas, bounds, gButton[newState], margin);
+}
+
+} //WebCore
+
diff --git a/WebKit/android/RenderSkinButton.h b/WebKit/android/RenderSkinButton.h
new file mode 100644
index 0000000..55eb2da
--- /dev/null
+++ b/WebKit/android/RenderSkinButton.h
@@ -0,0 +1,52 @@
+/*
+ * 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 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 RenderSkinButton_h
+#define RenderSkinButton_h
+
+#include "RenderSkinAndroid.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.
+ */
+ static void Init(android::AssetManager*);
+ /**
+ * 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& , RenderSkinAndroid::State);
+};
+
+} // WebCore
+#endif
+
diff --git a/WebKit/android/RenderSkinCombo.cpp b/WebKit/android/RenderSkinCombo.cpp
new file mode 100644
index 0000000..902f2c0
--- /dev/null
+++ b/WebKit/android/RenderSkinCombo.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 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 "RenderSkinCombo.h"
+
+#include "Document.h"
+#include "Node.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+
+namespace WebCore {
+
+static SkBitmap s_bitmap[2]; // Collection of assets for a combo box
+static bool s_decoded; // True if all assets were decoded
+static const int s_margin = 2;
+static const SkIRect s_mar = { s_margin, s_margin,
+ RenderSkinCombo::extraWidth(), s_margin };
+
+RenderSkinCombo::RenderSkinCombo()
+{
+}
+
+void RenderSkinCombo::Init(android::AssetManager* am)
+{
+ if (s_decoded)
+ return;
+ // Maybe short circuiting is fine, since I don't even draw if one state is not decoded properly
+ // but is that necessary in the final version?
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-noHighlight.png", &s_bitmap[kNormal]);
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-disabled.png", &s_bitmap[kDisabled]) && s_decoded;
+}
+
+
+bool RenderSkinCombo::Draw(SkCanvas* canvas, Node* element, int x, int y, int width, int height)
+{
+ if (!s_decoded)
+ return true;
+ State state = element && element->isEnabled() ? kNormal : kDisabled;
+ if (height < (s_margin<<1) + 1) {
+ height = (s_margin<<1) + 1;
+ }
+ SkRect bounds;
+ bounds.set(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + width), SkIntToScalar(y + height));
+ SkNinePatch::DrawNine(canvas, bounds, s_bitmap[state], s_mar);
+ return false;
+}
+
+} //WebCore
diff --git a/WebKit/android/RenderSkinCombo.h b/WebKit/android/RenderSkinCombo.h
new file mode 100644
index 0000000..b5321ff
--- /dev/null
+++ b/WebKit/android/RenderSkinCombo.h
@@ -0,0 +1,65 @@
+/*
+ * 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 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 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:
+ RenderSkinCombo();
+ virtual ~RenderSkinCombo() {}
+
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*);
+
+ /**
+ * 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; }
+
+private:
+
+ static const int arrowMargin = 22;
+};
+
+} // WebCore
+
+#endif
diff --git a/WebKit/android/RenderSkinRadio.cpp b/WebKit/android/RenderSkinRadio.cpp
new file mode 100644
index 0000000..2fca175
--- /dev/null
+++ b/WebKit/android/RenderSkinRadio.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 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 "RenderSkinRadio.h"
+
+#include "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinAndroid.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+
+static const char* checks[] = { "res/drawable/checkbox_off_background.png", "res/drawable/checkbox_on_background.png",
+ "res/drawable/radiobutton_off_background.png", "res/drawable/radiobutton_on_background.png"};
+static const SkScalar SIZE = SkIntToScalar(19); // Default height and width - corresponds with the bitmap - perhaps we should query the bitmap.
+
+namespace WebCore {
+
+static SkBitmap s_bitmap[4];
+static bool s_decoded;
+
+void RenderSkinRadio::Init(android::AssetManager* am)
+{
+ if (s_decoded)
+ return;
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[0], &s_bitmap[0]);
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[1], &s_bitmap[1]) && s_decoded;
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[2], &s_bitmap[2]) && s_decoded;
+ s_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[3], &s_bitmap[3]) && s_decoded;
+}
+
+void RenderSkinRadio::Draw(SkCanvas* canvas, Node* element, const IntRect& ir,
+ bool isCheckBox)
+{
+ if (!s_decoded || !element) {
+ return;
+ }
+ SkRect r;
+ android_setrect(&r, ir);
+ int saveLayerCount = 0;
+ int saveScaleCount = 0;
+ if (!element->isEnabled()) {
+ saveLayerCount = canvas->saveLayerAlpha(&r, 0x80);
+ }
+ SkScalar width = r.width();
+ if (SIZE != width) {
+ SkScalar scale = SkScalarDiv(width, SIZE);
+ saveScaleCount = canvas->scale(scale, scale);
+ }
+ canvas->drawBitmap(s_bitmap[element->isChecked() + 2*(!isCheckBox)],
+ r.fLeft, r.fTop, NULL);
+ if (saveLayerCount != 0) {
+ canvas->restoreToCount(saveLayerCount);
+ } else if (saveScaleCount != 0) {
+ canvas->restoreToCount(saveScaleCount);
+ }
+ return;
+}
+
+} //WebCore
+
diff --git a/WebKit/android/RenderSkinRadio.h b/WebKit/android/RenderSkinRadio.h
new file mode 100644
index 0000000..f70098f
--- /dev/null
+++ b/WebKit/android/RenderSkinRadio.h
@@ -0,0 +1,59 @@
+/*
+ * 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 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 RenderSkinRadio_h
+#define RenderSkinRadio_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*);
+
+ /**
+ * 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/WebKit/android/TimeCounter.cpp b/WebKit/android/TimeCounter.cpp
new file mode 100644
index 0000000..0dc0f3e
--- /dev/null
+++ b/WebKit/android/TimeCounter.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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"
+
+#include "config.h"
+#include "TimeCounter.h"
+
+#include "CString.h"
+#include "Cache.h"
+#include "KURL.h"
+#include "SystemTime.h"
+#include <runtime/JSGlobalObject.h>
+#include <utils/Log.h>
+
+using namespace WebCore;
+
+namespace android {
+
+#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];
+
+static const char* timeCounterNames[] = {
+ "css parsing",
+ "javascript",
+ "calculate style",
+ "Java callback (frame bridge)",
+ "parsing (may include calcStyle or Java callback)",
+ "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 = get_thread_msec();
+ 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)
+{
+ String urlString = url;
+ int totalTime = static_cast<int>((currentTime() - sStartTotalTime) * 1000);
+ int threadTime = get_thread_msec() - 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);
+}
+
+void TimeCounter::reportNow()
+{
+ double current = currentTime();
+ uint32_t currentThread = get_thread_msec();
+ 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 = get_thread_msec();
+}
+
+void TimeCounter::start(enum Type type)
+{
+ uint32_t time = get_thread_msec();
+ if (sRecordWebCoreTime) {
+ sStartWebCoreThreadTime = time;
+ sRecordWebCoreTime = false;
+ }
+ sStartTime[type] = time;
+}
+
+#endif
+
+}
diff --git a/WebKit/android/TimeCounter.h b/WebKit/android/TimeCounter.h
new file mode 100644
index 0000000..d1b2dff
--- /dev/null
+++ b/WebKit/android/TimeCounter.h
@@ -0,0 +1,98 @@
+/*
+ * 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 TIME_COUNTER_H
+#define TIME_COUNTER_H
+
+#ifdef ANDROID_INSTRUMENT
+
+#include "SystemTime.h"
+
+namespace WebCore {
+
+class KURL;
+
+}
+
+namespace android {
+
+class TimeCounter {
+public:
+ enum Type {
+ // function base counters
+ CSSTimeCounter,
+ JavaScriptTimeCounter,
+ 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);
+ 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(WebCore::get_thread_msec()) {}
+ ~TimeCounterAuto() {
+ uint32_t time = WebCore::get_thread_msec();
+ TimeCounter::sEndWebCoreThreadTime = time;
+ TimeCounter::sTotalTimeUsed[m_type] += time - m_startTime;
+ TimeCounter::sCounter[m_type]++;
+ }
+private:
+ TimeCounter::Type m_type;
+ uint32_t m_startTime;
+};
+
+}
+#endif
+
+#endif
diff --git a/WebKit/android/TimerClient.h b/WebKit/android/TimerClient.h
new file mode 100644
index 0000000..09f9fc0
--- /dev/null
+++ b/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 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 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;
+ };
+
+}
+#endif
+
diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
new file mode 100644
index 0000000..abc6a32
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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 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"
+
+#include "config.h"
+
+#include "ChromeClientAndroid.h"
+#include "CString.h"
+#include "Document.h"
+#include "PlatformString.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "Page.h"
+#include "Screen.h"
+#include "ScriptController.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreViewBridge.h"
+#include "WebViewCore.h"
+#include "WindowFeatures.h"
+#include "Settings.h"
+
+namespace android {
+
+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()
+{
+ // only seems to be used for dashboard regions, so just return 1
+ return 1;
+}
+
+void ChromeClientAndroid::focus() {
+ ASSERT(m_webFrame);
+ // Ask the application to focus this WebView.
+ m_webFrame->requestFocus();
+}
+void ChromeClientAndroid::unfocus() { notImplemented(); }
+
+bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; }
+void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); }
+
+Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&,
+ const WindowFeatures& features)
+{
+ 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
+
+ WTF::PassRefPtr<WebCore::Screen> screen = WebCore::Screen::create(frame);
+ bool dialog = features.dialog || !features.resizable
+ || (features.heightSet && features.height < screen.get()->height()
+ && features.widthSet && features.width < screen.get()->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,
+ frame->script()->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(); }
+
+// This function is called by the JavaScript bindings to print usually an error to
+// a message console.
+void ChromeClientAndroid::addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) {
+ notImplemented();
+ LOGD("Console: %s line: %d source: %s\n", message.latin1().data(), lineNumber, sourceID.latin1().data());
+}
+
+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(false);
+ // 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()->removeAllEventListenersFromAllNodes();
+ // 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() { return true; }
+
+bool ChromeClientAndroid::tabsToLinks() const { return false; }
+
+IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); }
+
+// new to change 38068 (Nov 6, 2008)
+void ChromeClientAndroid::repaint(const IntRect& rect, bool contentChanged,
+ bool immediate, bool repaintContentOnly) {
+ notImplemented();
+// was in ScrollViewAndroid::update() : needs to be something like:
+// android::WebViewCore::getWebViewCore(this)->contentInvalidate(rect);
+}
+
+// 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();
+}
+
+// new to change 38068 (Nov 6, 2008)
+// in place of view()->containingWindow(), webkit now uses view()->hostWindow()->platformWindow()
+PlatformWidget ChromeClientAndroid::platformWindow() const {
+ Page* page = m_webFrame->page();
+ Frame* mainFrame = page->mainFrame();
+ FrameView* view = mainFrame->view();
+ PlatformWidget viewBridge = view->platformWidget();
+ return viewBridge;
+}
+
+void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {}
+void ChromeClientAndroid::setToolTip(const String&) {}
+void ChromeClientAndroid::print(Frame*) {}
+
+void ChromeClientAndroid::exceededDatabaseQuota(Frame*, const String&) {}
+
+// new to change 38068 (Nov 6, 2008)
+void ChromeClientAndroid::runOpenPanel(Frame*, PassRefPtr<FileChooser>) { notImplemented(); }
+
+}
diff --git a/WebKit/android/WebCoreSupport/ChromeClientAndroid.h b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
new file mode 100644
index 0000000..60b3a8f
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
@@ -0,0 +1,120 @@
+/*
+ * 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 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 ChromeClientAndroid_h
+#define ChromeClientAndroid_h
+
+#include "ChromeClient.h"
+
+using namespace WebCore;
+
+namespace android {
+ class WebFrame;
+
+ class ChromeClientAndroid : public ChromeClient {
+ public:
+ ChromeClientAndroid() : m_webFrame(NULL) {}
+ virtual void chromeDestroyed();
+
+ virtual void setWindowRect(const FloatRect&);
+ virtual FloatRect windowRect();
+
+ virtual FloatRect pageRect();
+
+ virtual float scaleFactor();
+
+ virtual void focus();
+ virtual void unfocus();
+
+ virtual bool canTakeFocus(FocusDirection);
+ virtual void takeFocus(FocusDirection);
+
+ // 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&);
+ 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(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 repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false);
+ 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 PlatformWidget platformWindow() const;
+ // End methods used by HostWindow.
+
+ virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned int);
+
+ virtual void setToolTip(const String&);
+
+ virtual void print(Frame*);
+
+ virtual void exceededDatabaseQuota(Frame*, const String&);
+
+ virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>);
+
+ // Android-specific
+ void setWebFrame(android::WebFrame* webframe);
+ private:
+ android::WebFrame* m_webFrame;
+ };
+
+}
+
+#endif
diff --git a/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp b/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp
new file mode 100644
index 0000000..7aabfc9
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.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 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 "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(); }
+
+}
diff --git a/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h b/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h
new file mode 100644
index 0000000..1860e4e
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h
@@ -0,0 +1,50 @@
+/*
+ * 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 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 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();
+};
+
+} // namespace WebCore
+
+#endif // ContextMenuClientAndroid_h
diff --git a/WebKit/android/WebCoreSupport/DragClientAndroid.cpp b/WebKit/android/WebCoreSupport/DragClientAndroid.cpp
new file mode 100644
index 0000000..64406e7
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/DragClientAndroid.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 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"
+
+#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/WebKit/android/WebCoreSupport/DragClientAndroid.h b/WebKit/android/WebCoreSupport/DragClientAndroid.h
new file mode 100644
index 0000000..5f0548f
--- /dev/null
+++ b/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 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 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/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
new file mode 100644
index 0000000..fb71fa2
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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 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"
+
+#include "config.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "KeyboardCodes.h"
+#include "KeyboardEvent.h"
+#include "NotImplemented.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+
+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::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&, Vector<String>&) {}
+bool EditorClientAndroid::spellingUIIsShowing() { return false; }
+void EditorClientAndroid::checkGrammarOfString(unsigned short const*, int, Vector<GrammarDetail>&, int*, int*) {}
+void EditorClientAndroid::checkSpellingOfString(unsigned short const*, int, int*, int*) {}
+void EditorClientAndroid::textFieldDidEndEditing(Element*) {}
+void EditorClientAndroid::textDidChangeInTextArea(Element*) {}
+void EditorClientAndroid::textDidChangeInTextField(Element*) {}
+void EditorClientAndroid::textFieldDidBeginEditing(Element*) {}
+void EditorClientAndroid::ignoreWordInSpellDocument(String const&) {}
+void EditorClientAndroid::respondToChangedSelection() {}
+bool EditorClientAndroid::shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) { return m_notFromClick; }
+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*) {}
+
+}
diff --git a/WebKit/android/WebCoreSupport/EditorClientAndroid.h b/WebKit/android/WebCoreSupport/EditorClientAndroid.h
new file mode 100644
index 0000000..fc35761
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/EditorClientAndroid.h
@@ -0,0 +1,113 @@
+/*
+ * 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 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 EditorClientAndroid_h
+#define EditorClientAndroid_h
+
+#include "EditorClient.h"
+#include "Page.h"
+
+using namespace WebCore;
+
+namespace android {
+
+class EditorClientAndroid : public EditorClient {
+public:
+ EditorClientAndroid() { m_notFromClick = true; }
+ virtual void pageDestroyed();
+
+ virtual bool shouldDeleteRange(Range*);
+ virtual bool shouldShowDeleteInterface(HTMLElement*);
+ virtual bool smartInsertDeleteEnabled();
+ 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 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 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 void checkGrammarOfString(const UChar*, int length, 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&, Vector<String>& guesses);
+ virtual bool shouldMoveRangeAfterDelete(Range*, Range*);
+ virtual void setInputMethodState(bool);
+
+ virtual void handleKeyboardEvent(KeyboardEvent*);
+ virtual void handleInputMethodKeydown(KeyboardEvent*);
+ // Android specific:
+ void setPage(Page* page) { m_page = page; }
+ void setFromClick(bool fromClick) { m_notFromClick = !fromClick; }
+private:
+ Page* m_page;
+ bool m_notFromClick;
+};
+
+}
+
+#endif
diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
new file mode 100644
index 0000000..8782132
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp
@@ -0,0 +1,1106 @@
+/*
+ * 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 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"
+
+#include "config.h"
+
+#include "android_graphics.h"
+#include "CString.h"
+#include "DocumentLoader.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "GraphicsContext.h"
+// HTMLFormElement needed for a bad include
+#include "HTMLFormElement.h"
+#include "HTMLFrameOwnerElement.h"
+#include "IconDatabase.h"
+#include "MIMETypeRegistry.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "PluginDatabase.h"
+#include "PluginView.h"
+#ifdef ANDROID_PLUGINS
+// Removed.
+#else
+#include "PluginViewBridgeAndroid.h"
+#endif
+#include "ProgressTracker.h"
+#include "RenderPart.h"
+#include "ResourceError.h"
+#include "SelectionController.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "TextEncoding.h"
+#include "Document.h"
+#include "FrameView.h"
+#include "HistoryItem.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleInternal.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebViewCore.h"
+#include "Settings.h"
+
+#include <utils/AssetManager.h>
+
+extern android::AssetManager* globalAssetManager();
+
+namespace android {
+
+static const int EXTRA_LAYOUT_DELAY = 1000;
+
+// FIXME: Need some data for how big this should be.
+#define MAX_SESSION_HISTORY 50
+static WTF::Vector<KURL> gSessionHistory;
+
+bool historyContains(const UChar* chars, unsigned len) {
+ const KURL url(String(chars, len));
+ WTF::Vector<KURL>::const_iterator end = gSessionHistory.end();
+ for (WTF::Vector<KURL>::const_iterator i = gSessionHistory.begin(); i != end; ++i) {
+ if (equalIgnoringRef(url, *i))
+ return true;
+ }
+ return false;
+}
+
+FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe)
+ : m_frame(NULL)
+ , m_webFrame(webframe) {
+ 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;
+}
+
+bool FrameLoaderClientAndroid::hasFrameView() const {
+ // FIXME,
+ // need to revisit for sub-frame case
+ return true;
+}
+
+bool FrameLoaderClientAndroid::privateBrowsingEnabled() const {
+ // FIXME, are we going to support private browsing?
+ notImplemented();
+ return false;
+}
+
+void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) {
+ // don't use representation
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::forceLayout() {
+ ASSERT(m_frame);
+ m_frame->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();
+}
+
+void FrameLoaderClientAndroid::detachedFromParent4() {
+ // FIXME, ready to release view
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::loadedFromPageCache() {
+ // don't support page cache
+ verifiedOk();
+}
+
+// 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();
+}
+
+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::dispatchWillClose() {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::dispatchDidReceiveIcon() {
+ ASSERT(m_frame);
+ if (m_frame->tree() && m_frame->tree()->parent())
+ return;
+ WebCore::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)
+ icon = WebCore::iconDatabase()->iconForPageURL(
+ m_frame->loader()->originalRequestURL().string(),
+ 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::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() {
+ ASSERT(m_frame);
+ WebViewCore::getWebViewCore(m_frame->view())->updateFrameGeneration(m_frame);
+}
+
+static void loadDataIntoFrame(Frame* frame, const String& url,
+ const String& data) {
+ ResourceRequest request(url);
+ CString cstr = data.utf8();
+ RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length());
+ SubstituteData subData(buf, String("text/html"), String("utf-8"),
+ request.url());
+ frame->loader()->load(request, subData);
+}
+
+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
+ WebFrame::RAW_RES_ID id = WebFrame::NODOMAIN;
+ if ((error.errorCode() == ErrorFile ||
+ error.errorCode() == ErrorFileNotFound) &&
+ (!error.localizedDescription().isEmpty())) {
+ id = WebFrame::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.
+ loadDataIntoFrame(m_frame, error.failingURL(), s);
+
+ // Delete the asset.
+ delete a;
+}
+
+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);
+ m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY);
+ // FIXME: Need to figure out if we need didLayout or didFirstLayout
+ // see WebViewCore::didLayout
+ if (!m_frame->tree()->parent()) {
+ // Only need to notify Java side for the top frame
+ WebViewCore::getWebViewCore(m_frame->view())->didFirstLayout();
+ }
+}
+
+Frame* FrameLoaderClientAndroid::dispatchCreatePage() {
+ 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&) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ // 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()) {
+ // Server wants to override our normal policy.
+ if (TreatAsAttachment(content_disposition)) {
+ // 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;
+ }
+ } else {
+ // 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;
+ }
+ }
+ // 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;
+ (m_frame->loader()->*func)(action);
+}
+
+void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func,
+ const NavigationAction&, const ResourceRequest& req,
+ PassRefPtr<FormState> formState, const String& frameName) {
+ ASSERT(m_frame);
+ // 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(req))
+ (m_frame->loader()->*func)(PolicyUse);
+ else
+ (m_frame->loader()->*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 (action.type() == NavigationTypeFormResubmitted) {
+ m_webFrame->decidePolicyForFormResubmission(func);
+ return;
+ } else {
+ (m_frame->loader()->*func)(PolicyUse);
+ }
+}
+
+void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) {
+ ASSERT(m_frame);
+ ASSERT(func);
+ (m_frame->loader()->*func)(PolicyUse);
+}
+
+void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) {
+ ASSERT(m_frame);
+ if (!error.isNull() && error.errorCode() >= InternalErrorLast)
+ m_webFrame->reportError(error.errorCode(),
+ error.localizedDescription(), error.failingURL());
+}
+
+void FrameLoaderClientAndroid::clearUnarchivingState(DocumentLoader*) {
+ notImplemented();
+}
+
+// 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() {
+ if (!m_frame->tree()->parent()) {
+ // only need to notify Java for the top frame
+ WebViewCore::getWebViewCore(m_frame->view())->notifyProgressFinished();
+ }
+}
+
+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)
+ committedLoad(docLoader, 0, 0);
+}
+
+void FrameLoaderClientAndroid::finalSetupForReplace(DocumentLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::updateGlobalHistoryForStandardLoad(const KURL& url) {
+ ASSERT(m_frame);
+ const String& str = url.string();
+ if (!historyContains(str.characters(), str.length())) {
+ if (gSessionHistory.size() == MAX_SESSION_HISTORY)
+ gSessionHistory.remove(0);
+ gSessionHistory.append(url);
+ }
+ m_webFrame->updateVisitedHistory(url, false);
+}
+
+void FrameLoaderClientAndroid::updateGlobalHistoryForReload(const KURL& url) {
+ ASSERT(m_frame);
+ m_webFrame->updateVisitedHistory(url, true);
+}
+
+bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const {
+ // hmmm, seems like we might do a more thoughtful check
+ ASSERT(m_frame);
+ return item != NULL;
+}
+
+void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) {
+ ASSERT(m_frame);
+ String encoding = loader->overrideEncoding();
+ bool userChosen = !encoding.isNull();
+ if (encoding.isNull())
+ encoding = loader->response().textEncodingName();
+ loader->frameLoader()->setEncoding(encoding, userChosen);
+ Document *doc = m_frame->document();
+ if (doc) {
+ loader->frameLoader()->addData(data, length);
+ }
+}
+
+ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorCancelled, String(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorCannotShowUrl, String(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) {
+ return ResourceError(String(), InternalErrorInterrupted, String(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorCannotShowMimeType, String(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String());
+}
+
+ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) {
+ return ResourceError(String(), InternalErrorPluginWillHandleLoadError, String(), String());
+}
+
+bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) {
+ notImplemented();
+ return false;
+}
+
+void FrameLoaderClientAndroid::setDefersLoading(bool) {
+ notImplemented();
+}
+
+bool FrameLoaderClientAndroid::willUseArchive(ResourceLoader*, const ResourceRequest&,
+ const KURL& originalURL) const {
+ lowPriority_notImplemented();
+ return false;
+}
+
+bool FrameLoaderClientAndroid::isArchiveLoadPending(ResourceLoader*) const {
+ lowPriority_notImplemented();
+ return false;
+}
+
+void FrameLoaderClientAndroid::cancelPendingArchiveLoad(ResourceLoader*) {
+ notImplemented();
+}
+
+void FrameLoaderClientAndroid::clearArchivedResources() {
+ notImplemented();
+}
+
+bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const {
+ ASSERT(m_frame);
+ // Don't allow hijacking of intrapage navigation
+ if (WebCore::equalIgnoringRef(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;
+
+ if (m_webFrame->canHandleRequest(request)) {
+#ifdef ANDROID_META_SUPPORT
+ // reset metadata settings for the top frame as they are not preserved cross page
+ if (!m_frame->tree()->parent() && m_frame->settings())
+ m_frame->settings()->resetMetadataSettings();
+#endif
+ return true;
+ }
+ return false;
+}
+
+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) ||
+ PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType))
+ return true;
+ 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);
+ m_frame->loader()->setPreviousHistoryItem(0);
+}
+
+void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) {
+#ifdef ANDROID_HISTORY_CLIENT
+ ASSERT(m_frame);
+ ASSERT(item);
+ // We should have added a bridge when the child item was added to its
+ // parent.
+ WebHistoryItem* bridge = item->bridge();
+ ASSERT(bridge);
+ // store the current scale (only) for the top frame
+ if (!m_frame->tree()->parent()) {
+ bridge->setScale(WebViewCore::getWebViewCore(m_frame->view())->scale());
+ }
+
+ WebCore::notifyHistoryItemChanged(item);
+#endif
+}
+
+void FrameLoaderClientAndroid::restoreViewState() {
+#ifdef ANDROID_HISTORY_CLIENT
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
+ HistoryItem* item = m_frame->loader()->currentHistoryItem();
+ // restore the scale (only) for the top frame
+ if (!m_frame->tree()->parent()) {
+ webViewCore->restoreScale(item->bridge()->scale());
+ }
+#endif
+}
+
+#ifdef ANDROID_HISTORY_CLIENT
+void FrameLoaderClientAndroid::dispatchDidAddHistoryItem(HistoryItem* item) const {
+ ASSERT(m_frame);
+ m_webFrame->addHistoryItem(item);
+}
+
+void FrameLoaderClientAndroid::dispatchDidRemoveHistoryItem(HistoryItem* item, int index) const {
+ ASSERT(m_frame);
+ m_webFrame->removeHistoryItem(index);
+}
+
+void FrameLoaderClientAndroid::dispatchDidChangeHistoryIndex(
+ BackForwardList* list) const {
+ ASSERT(m_frame);
+ m_webFrame->updateHistoryIndex(list->backListCount());
+}
+#endif
+
+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() {
+ ASSERT(m_frame);
+ m_frame->loader()->detachChildren();
+}
+
+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);
+}
+
+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
+ WebCoreResourceLoader* loader = handle->getInternal()->m_loader;
+ 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.
+ FrameView* frameView = new WebCore::FrameView(newFrame);
+ WebFrameView* webFrameView = new WebFrameView(frameView,
+ WebViewCore::getWebViewCore(parent->view()));
+ // frameView Retains webFrameView, so call Release for webFrameView
+ Release(webFrameView);
+ // Attach the frameView to the newFrame.
+ newFrame->setView(frameView);
+ // setView() refs the frameView so call deref on the frameView
+ frameView->deref();
+ 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 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();
+ if (memcmp(data, slash_v_slash, sizeof(slash_v_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))
+ 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)
+{
+ return url.host().endsWith("youtube.com") && isValidYouTubeVideo(url.path())
+ && equalIgnoringCase(mimeType, "application/x-shockwave-flash");
+}
+
+Widget* FrameLoaderClientAndroid::createPlugin(
+ const IntSize& size,
+ Element* element,
+ const KURL& url,
+ const WTF::Vector<String, 0u>& names,
+ const WTF::Vector<String, 0u>& values,
+ const String& mimeType,
+ bool loadManually) {
+ // Create an iframe for youtube urls.
+ if (isYouTubeUrl(url, mimeType)) {
+ RefPtr<Frame> frame = createFrame(KURL(), String(),
+ static_cast<HTMLFrameOwnerElement*>(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(),
+ "file:///android_asset/webkit/", s);
+ return frame->view();
+ }
+ return NULL;
+ }
+#ifdef ANDROID_PLUGINS
+ return PluginView::create(m_frame,
+ size,
+ element,
+ url,
+ names,
+ values,
+ mimeType,
+ loadManually);
+#else
+ return NULL;
+#endif
+}
+
+void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) {
+ // don't support plugin yet
+ notImplemented();
+}
+
+Widget* FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, Element*,
+ const KURL& baseURL, const WTF::Vector<String>& paramNames,
+ const WTF::Vector<String>& paramValues) {
+ // don't support widget yet
+ notImplemented();
+ return 0;
+}
+
+// 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) {
+ if (mimeType.length() == 0)
+ {
+ // Guess the mimeType from the extension
+ if (url.hasPath())
+ {
+ String path = url.path();
+ int lastIndex = path.reverseFind('.');
+ static const String image("image/");
+ if (lastIndex >= 0)
+ {
+ String mime(path.substring(lastIndex + 1));
+ mime.insert(image, 0);
+ if (Image::supportsType(mime))
+ return ObjectContentImage;
+ }
+ }
+ return ObjectContentFrame;
+ }
+ if (equalIgnoringCase(mimeType, "text/html") ||
+ equalIgnoringCase(mimeType, "text/xml") ||
+ equalIgnoringCase(mimeType, "text/") ||
+ equalIgnoringCase(mimeType, "application/xml") ||
+ equalIgnoringCase(mimeType, "application/xhtml+xml") ||
+ equalIgnoringCase(mimeType, "application/x-javascript"))
+ return ObjectContentFrame;
+ if (Image::supportsType(mimeType))
+ return ObjectContentImage;
+ // Use OtherPlugin so embed and object tags draw the null plugin view
+ return ObjectContentOtherPlugin;
+}
+
+// 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::windowObjectCleared() {
+ 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);
+}
+
+// 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(pageUrl);
+ if (equalIgnoringRef(u, m_frame->loader()->url())) {
+ dispatchDidReceiveIcon();
+ }
+}
+
+// functions new to Feb-19 tip of tree merge:
+// According to the changelog:
+// The very Mac-centric "makeDocumentView", "setDocumentViewFromCachedPage",
+// and "saveDocumentViewToCachedPage" become "transitionToCommittedForNewPage",
+// "transitionToCommittedFromCachedPage", and "savePlatformDataToCachedPage"
+// accordingly
+void FrameLoaderClientAndroid::savePlatformDataToCachedPage(CachedPage*) {
+ // don't support page cache
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::transitionToCommittedFromCachedPage(CachedPage*) {
+ // don't support page cache
+ verifiedOk();
+}
+
+void FrameLoaderClientAndroid::transitionToCommittedForNewPage() {
+ ASSERT(m_frame);
+ if (m_frame->settings() && !m_frame->settings()->usesPageCache()) {
+ m_webFrame->transitionToCommitted(m_frame);
+ return;
+ }
+
+ // Remember the old WebFrameView
+ WebFrameView* webFrameView = static_cast<WebFrameView*> (
+ m_frame->view()->platformWidget());
+ Retain(webFrameView);
+
+ // Remove the old FrameView
+ m_frame->setView(NULL);
+
+ // Create a new FrameView and associate it with the saved webFrameView
+ FrameView* view = new FrameView(m_frame);
+ webFrameView->setView(view);
+
+ Release(webFrameView);
+
+ // Give the new FrameView to the Frame
+ m_frame->setView(view);
+
+ // Deref since FrameViews are created with a ref of 1
+ view->deref();
+
+ if (m_frame->ownerRenderer())
+ m_frame->ownerRenderer()->setWidget(view);
+
+ m_frame->view()->initScrollbars();
+
+ m_webFrame->transitionToCommitted(m_frame);
+}
+
+// new as of webkit 34152
+void FrameLoaderClientAndroid::updateGlobalHistory(const KURL& url) {
+ m_webFrame->updateVisitedHistory(url, false);
+}
+
+}
diff --git a/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
new file mode 100644
index 0000000..58b296e
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h
@@ -0,0 +1,242 @@
+/*
+ * 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 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 FrameLoaderClientAndroid_h
+#define FrameLoaderClientAndroid_h
+
+#include "CacheBuilder.h"
+#include "FrameLoaderClient.h"
+#include "ResourceResponse.h"
+#include "WebIconDatabase.h"
+
+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 bool hasFrameView() const; // ditto
+
+ virtual bool privateBrowsingEnabled() const;
+
+ virtual void makeRepresentation(DocumentLoader*);
+ virtual void forceLayout();
+ virtual void forceLayoutForNonHTML();
+
+ virtual void setCopiesOnScroll();
+
+ virtual void detachedFromParent2();
+ virtual void detachedFromParent3();
+ virtual void detachedFromParent4();
+
+ virtual void loadedFromPageCache();
+
+ virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&);
+
+ virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse);
+ 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 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 Frame* dispatchCreatePage();
+ 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 clearUnarchivingState(DocumentLoader*);
+
+ 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 finalSetupForReplace(DocumentLoader*);
+
+ virtual void updateGlobalHistory(const KURL&);
+ virtual void updateGlobalHistoryForStandardLoad(const KURL&);
+ virtual void updateGlobalHistoryForReload(const KURL&);
+ virtual bool shouldGoToHistoryItem(HistoryItem*) const;
+#ifdef ANDROID_HISTORY_CLIENT
+ virtual void dispatchDidAddHistoryItem(HistoryItem*) const;
+ virtual void dispatchDidRemoveHistoryItem(HistoryItem*, int) const;
+ virtual void dispatchDidChangeHistoryIndex(BackForwardList*) const;
+#endif
+
+ 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 void setDefersLoading(bool);
+
+ virtual bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const;
+ virtual bool isArchiveLoadPending(ResourceLoader*) const;
+ virtual void cancelPendingArchiveLoad(ResourceLoader*);
+ virtual void clearArchivedResources();
+
+ virtual bool canHandleRequest(const ResourceRequest&) const;
+ virtual bool canShowMIMEType(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&);
+
+ virtual String userAgent(const KURL&);
+
+ 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 Widget* createPlugin(const IntSize&, Element*, const KURL&,
+ const WTF::Vector<WebCore::String, 0u>&, const WTF::Vector<String, 0u>&,
+ const String&, bool);
+ virtual void redirectDataToPlugin(Widget* pluginWidget);
+
+ virtual Widget* createJavaAppletWidget(const IntSize&, Element*, const KURL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues);
+
+ virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType);
+ virtual String overrideMediaType() const;
+
+ virtual void windowObjectCleared();
+
+ virtual void didPerformFirstNavigation() const;
+ virtual void registerForIconNotification(bool listen = true);
+
+ virtual void savePlatformDataToCachedPage(CachedPage*);
+ virtual void transitionToCommittedFromCachedPage(CachedPage*);
+ virtual void transitionToCommittedForNewPage();
+
+ // 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; }
+ private:
+ CacheBuilder m_cacheBuilder;
+ Frame* m_frame;
+ WebFrame* m_webFrame;
+
+ 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/WebKit/android/WebCoreSupport/InspectorClientAndroid.h b/WebKit/android/WebCoreSupport/InspectorClientAndroid.h
new file mode 100644
index 0000000..9eb85e5
--- /dev/null
+++ b/WebKit/android/WebCoreSupport/InspectorClientAndroid.h
@@ -0,0 +1,64 @@
+/*
+ * 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 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 InspectorClientAndroid_h
+#define InspectorClientAndroid_h
+
+#include "InspectorClient.h"
+
+namespace android {
+
+class InspectorClientAndroid : public InspectorClient {
+public:
+ virtual ~InspectorClientAndroid() { }
+
+ virtual void inspectorDestroyed() { delete this; }
+
+ virtual Page* createPage() { return NULL; }
+
+ virtual String localizedStringsURL() { return String(); }
+
+ virtual void showWindow() {}
+ virtual void closeWindow() {}
+
+ virtual void attachWindow() {}
+ virtual void detachWindow() {}
+
+ virtual void setAttachedWindowHeight(unsigned height) {}
+
+ virtual void highlight(Node*) {}
+ virtual void hideHighlight() {}
+
+ virtual void inspectedURLChanged(const String& newURL) {}
+
+ // new as of 38068
+ virtual void populateSetting(const String&, InspectorController::Setting&) {}
+ virtual void storeSetting(const String&, const InspectorController::Setting&) {}
+ virtual void removeSetting(const String&) {}
+};
+
+}
+
+#endif
diff --git a/WebKit/android/jni/JavaBridge.cpp b/WebKit/android/jni/JavaBridge.cpp
new file mode 100644
index 0000000..eefeea5
--- /dev/null
+++ b/WebKit/android/jni/JavaBridge.cpp
@@ -0,0 +1,319 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "Cache.h"
+#include "CookieClient.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "NetworkStateNotifier.h"
+#include "Timer.h"
+#include "TimerClient.h"
+#include "jni_utility.h"
+#include "WebCoreJni.h"
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <SkImageRef_GlobalPool.h>
+#include <SkUtils.h>
+#include <utils/misc.h>
+
+// maximum bytes used to cache decoded images
+// (not including big images using ashmem)
+#define IMAGE_POOL_BUDGET (512 * 1024)
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static jfieldID gJavaBridge_ObjectID;
+
+// ----------------------------------------------------------------------------
+
+class JavaBridge : public TimerClient, public CookieClient
+{
+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, WebCore::KURL const& docURL, WebCore::String const& value);
+ virtual WebCore::String cookies(WebCore::KURL const& url);
+ virtual bool cookiesEnabled();
+
+ ////////////////////////////////////////////
+
+ virtual void setSharedTimerCallback(void (*f)());
+
+ ////////////////////////////////////////////
+
+ 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 SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer);
+ static void ServiceFuncPtrQueue(JNIEnv*);
+
+private:
+ jobject mJavaObject;
+ jmethodID mSetSharedTimer;
+ jmethodID mStopSharedTimer;
+ jmethodID mSetCookies;
+ jmethodID mCookies;
+ jmethodID mCookiesEnabled;
+ jmethodID mSignalFuncPtrQueue;
+};
+
+static void (*sSharedTimerFiredCallback)();
+static JavaBridge* gJavaBridge;
+
+JavaBridge::JavaBridge(JNIEnv* env, jobject obj)
+{
+ mJavaObject = adoptGlobalRef(env, 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;Ljava/lang/String;)V");
+ mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;");
+ mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z");
+ mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V");
+
+ 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");
+
+ JavaSharedClient::SetTimerClient(this);
+ JavaSharedClient::SetCookieClient(this);
+ gJavaBridge = this;
+}
+
+JavaBridge::~JavaBridge()
+{
+ if (mJavaObject) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteGlobalRef(mJavaObject);
+ mJavaObject = 0;
+ }
+
+ JavaSharedClient::SetTimerClient(NULL);
+ JavaSharedClient::SetCookieClient(NULL);
+}
+
+void
+JavaBridge::setSharedTimer(long long timemillis)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis);
+}
+
+void
+JavaBridge::stopSharedTimer()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mStopSharedTimer);
+}
+
+void
+JavaBridge::setCookies(WebCore::KURL const& url, WebCore::KURL const& docUrl, WebCore::String const& value)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ const WebCore::String& urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ const WebCore::String& docUrlStr = docUrl.string();
+ jstring jDocUrlStr = env->NewString(docUrlStr.characters(), docUrlStr.length());
+ jstring jValueStr = env->NewString(value.characters(), value.length());
+
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSetCookies, jUrlStr, jDocUrlStr, jValueStr);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jDocUrlStr);
+ env->DeleteLocalRef(jValueStr);
+}
+
+WebCore::String
+JavaBridge::cookies(WebCore::KURL const& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ const WebCore::String& urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ jstring string = (jstring)(env->CallObjectMethod(obj.get(), mCookies, jUrlStr));
+
+ WebCore::String ret = to_string(env, string);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(string);
+ return ret;
+}
+
+bool
+JavaBridge::cookiesEnabled()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled);
+ return (ret != 0);
+}
+
+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 = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue);
+}
+
+// ----------------------------------------------------------------------------
+
+// visible to Shared
+void AndroidSignalServiceFuncPtrQueue()
+{
+ gJavaBridge->signalServiceFuncPtrQueue();
+}
+
+// ----------------------------------------------------------------------------
+
+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);
+ SkImageRef_GlobalPool::SetRAMBudget(IMAGE_POOL_BUDGET);
+ LOGV("--- set ImageRef budget %d\n", SkImageRef_GlobalPool::GetRAMBudget());
+}
+
+void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online)
+{
+ WebCore::networkStateNotifier().networkStateChange(online);
+}
+
+void JavaBridge::SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer)
+{
+ WebCore::setDeferringTimers(defer);
+}
+
+void JavaBridge::ServiceFuncPtrQueue(JNIEnv*)
+{
+ JavaSharedClient::ServiceFunctionPtrQueue();
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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 },
+ { "setDeferringTimers", "(Z)V",
+ (void*) JavaBridge::SetDeferringTimers },
+ { "nativeServiceFuncPtrQueue", "()V",
+ (void*) JavaBridge::ServiceFuncPtrQueue },
+};
+
+int register_javabridge(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");
+
+ return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge",
+ gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/JavaSharedClient.cpp b/WebKit/android/jni/JavaSharedClient.cpp
new file mode 100644
index 0000000..f115f62
--- /dev/null
+++ b/WebKit/android/jni/JavaSharedClient.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 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 "JavaSharedClient.h"
+#define LOG_TAG "JavaSharedClient"
+#include "utils/Log.h"
+#include "SkDeque.h"
+#include "SkThread.h"
+
+namespace android {
+ void AndroidSignalServiceFuncPtrQueue();
+
+ TimerClient* JavaSharedClient::GetTimerClient()
+ {
+ //LOG_ASSERT(gTimerClient != NULL, "gTimerClient not initialized!!!");
+ return gTimerClient;
+ }
+
+ CookieClient* JavaSharedClient::GetCookieClient()
+ {
+ //LOG_ASSERT(gCookieClient != NULL, "gCookieClient not initialized!!!");
+ return gCookieClient;
+ }
+
+ void JavaSharedClient::SetTimerClient(TimerClient* client)
+ {
+ //LOG_ASSERT(gTimerClient == NULL || client == NULL, "gTimerClient already set, aborting...");
+ gTimerClient = client;
+ }
+
+ void JavaSharedClient::SetCookieClient(CookieClient* client)
+ {
+ //LOG_ASSERT(gCookieClient == NULL || client == NULL, "gCookieClient already set, aborting...");
+ gCookieClient = client;
+ }
+
+ TimerClient* JavaSharedClient::gTimerClient = NULL;
+ CookieClient* JavaSharedClient::gCookieClient = 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();
+
+ android::AndroidSignalServiceFuncPtrQueue();
+ }
+
+ void JavaSharedClient::ServiceFunctionPtrQueue()
+ {
+ for (;;) {
+ void (*proc)(void*);
+ void* payload;
+ 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 (NULL != rec) {
+ proc = rec->fProc;
+ payload = rec->fPayload;
+ gFuncPtrQ.pop_front();
+ }
+ gFuncPtrQMutex.release();
+
+ if (NULL == rec) {
+ break;
+ }
+ proc(payload);
+ }
+ }
+}
diff --git a/WebKit/android/jni/JavaSharedClient.h b/WebKit/android/jni/JavaSharedClient.h
new file mode 100644
index 0000000..05788e1
--- /dev/null
+++ b/WebKit/android/jni/JavaSharedClient.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 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 JAVA_SHARED_CLIENT_H
+#define JAVA_SHARED_CLIENT_H
+
+namespace android {
+
+ class TimerClient;
+ class CookieClient;
+
+ class JavaSharedClient
+ {
+ public:
+ static TimerClient* GetTimerClient();
+ static CookieClient* GetCookieClient();
+
+ static void SetTimerClient(TimerClient* client);
+ static void SetCookieClient(CookieClient* 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;
+ };
+}
+#endif
diff --git a/WebKit/android/jni/PictureSet.cpp b/WebKit/android/jni/PictureSet.cpp
new file mode 100644
index 0000000..65aac8f
--- /dev/null
+++ b/WebKit/android/jni/PictureSet.cpp
@@ -0,0 +1,665 @@
+/*
+ * 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 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_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 "SystemTime.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;
+ pictureAndBounds.mPicture->safeRef();
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+}
+
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split)
+{
+ 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);
+ picture->safeRef();
+ /* 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};
+ 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) {
+ working->mPicture->safeUnref();
+ 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();
+ working->mPicture->safeUnref();
+ }
+ 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);
+ pathClip.computeBounds(&pathBounds, SkPath::kFast_BoundsType);
+ } else {
+ pathBounds.set(area.getBounds());
+ canvas->clipRect(pathBounds);
+ }
+ canvas->translate(pathBounds.fLeft, pathBounds.fTop);
+ canvas->save();
+ uint32_t startTime = WebCore::get_thread_msec();
+ canvas->drawPicture(*working->mPicture);
+ size_t elapsed = working->mElapsed = WebCore::get_thread_msec() - 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 void commonDrawBitmap(const SkBitmap& bitmap,
+ 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
+ }
+
+ 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 (emptyPicture(working->mPicture) == false)
+ 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;
+ working->mPicture->safeUnref();
+ 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)
+{
+ mPictures[i].mPicture->safeUnref();
+ mPictures[i].mPicture = 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);
+ } 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);
+ 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);
+ 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/WebKit/android/jni/PictureSet.h b/WebKit/android/jni/PictureSet.h
new file mode 100644
index 0000000..ae250b0
--- /dev/null
+++ b/WebKit/android/jni/PictureSet.h
@@ -0,0 +1,98 @@
+/*
+ * 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 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 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);
+ 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
+ };
+ void add(const Pictures* temp);
+ WTF::Vector<Pictures> mPictures;
+ int mHeight;
+ int mWidth;
+ };
+}
+
+#endif
diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp
new file mode 100644
index 0000000..e14a534
--- /dev/null
+++ b/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -0,0 +1,1234 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "android_graphics.h"
+#include "Arena.h"
+#include "AtomicString.h"
+#include "Cache.h"
+#include "ChromeClientAndroid.h"
+#include "ContextMenuClientAndroid.h"
+#include "CString.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "Element.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GCController.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "HTMLElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "InspectorClientAndroid.h"
+#include "JSDOMWindow.h"
+#include <runtime/InitializeThreading.h>
+#include <runtime/JSLock.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 "ScriptController.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SubstituteData.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebViewCore.h"
+#include "wds/DebugServer.h"
+
+#include <runtime_root.h>
+#include <runtime_object.h>
+#include <jni_utility.h>
+#include "jni.h"
+#include "jni_instance.h"
+
+#include <JNIHelp.h>
+#include <SkGraphics.h>
+#include <SkImageRef_GlobalPool.h>
+#include <utils/misc.h>
+#include <utils/AssetManager.h>
+#include <android_runtime/android_util_AssetManager.h>
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024
+
+// ----------------------------------------------------------------------------
+
+struct WebFrame::JavaBrowserFrame
+{
+ jobject mObj;
+ jobject mHistoryList; // WebBackForwardList object
+ jmethodID mStartLoadingResource;
+ jmethodID mLoadStarted;
+ jmethodID mTransitionToCommitted;
+ jmethodID mLoadFinished;
+ jmethodID mReportError;
+ jmethodID mSetTitle;
+ jmethodID mWindowObjectCleared;
+ jmethodID mSetProgress;
+ jmethodID mDidReceiveIcon;
+ jmethodID mUpdateVisitedHistory;
+ jmethodID mHandleUrl;
+ jmethodID mCreateWindow;
+ jmethodID mCloseWindow;
+ jmethodID mDecidePolicyForFormResubmission;
+ jmethodID mRequestFocus;
+ jmethodID mGetRawResFilename;
+ 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 = adoptGlobalRef(env, obj);
+ mJavaFrame->mHistoryList = adoptGlobalRef(env, historyList);
+ mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BIZZ)Landroid/webkit/LoadListener;");
+ 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->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;");
+
+ LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource");
+ 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->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");
+
+ mUserAgent = WebCore::String();
+ mUserInitiatedClick = false;
+}
+
+WebFrame::~WebFrame()
+{
+ if (mJavaFrame->mObj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteGlobalRef(mJavaFrame->mObj);
+ env->DeleteGlobalRef(mJavaFrame->mHistoryList);
+ mJavaFrame->mObj = 0;
+ }
+ delete mJavaFrame;
+}
+
+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 = env->NewString((unsigned short *)i->first.characters(), i->first.length());
+ jstring val = env->NewString((unsigned short *)i->second.characters(), i->second.length());
+ if (key && val) {
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ env->DeleteLocalRef(mapClass);
+
+ return hashMap;
+}
+
+WebCoreResourceLoader*
+WebFrame::startLoadingResource(WebCore::ResourceHandle* loader,
+ const WebCore::ResourceRequest& request,
+ bool isHighPriority, bool synchronous)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: startLoadingResource(%p, %s)",
+ loader, request.url().string().ascii().data());
+
+ WebCore::String method = request.httpMethod();
+ WebCore::HTTPHeaderMap headers = request.httpHeaderFields();
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WebCore::String urlStr = request.url().string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ jstring jMethodStr = NULL;
+ if (!method.isEmpty())
+ jMethodStr = env->NewString(method.characters(), method.length());
+ jbyteArray jPostDataStr = NULL;
+ WebCore::FormData* formdata = request.httpBody();
+ if (formdata) {
+ // 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();
+ for (size_t i = 0; i < n; ++i) {
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ size += e.m_data.size();
+ }
+ }
+
+ // 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;
+ }
+ }
+ env->ReleaseByteArrayElements(jPostDataStr, bytes, 0);
+ }
+ }
+ }
+
+ jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers);
+
+ // Convert the WebCore Cache Policy to a WebView Cache Policy.
+ int cacheMode = 0; // WebView.LOAD_NORMAL
+ switch (request.cachePolicy()) {
+ case WebCore::ReloadIgnoringCacheData:
+ cacheMode = 2; // WebView.LOAD_NO_CACHE
+ break;
+ case WebCore::ReturnCacheDataDontLoad:
+ cacheMode = 3; // WebView.LOAD_CACHE_ONLY
+ break;
+ case WebCore::ReturnCacheDataElseLoad:
+ cacheMode = 1; // WebView.LOAD_CACHE_ELSE_NETWORK
+ break;
+ case WebCore::UseProtocolCachePolicy:
+ default:
+ break;
+ }
+
+ LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode);
+
+
+ jobject jLoadListener =
+ env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mStartLoadingResource,
+ (int)loader, jUrlStr, jMethodStr, jHeaderMap,
+ jPostDataStr, cacheMode, isHighPriority, synchronous);
+
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jMethodStr);
+ env->DeleteLocalRef(jPostDataStr);
+ env->DeleteLocalRef(jHeaderMap);
+ if (checkException(env))
+ return NULL;
+
+ WebCoreResourceLoader* h = NULL;
+ if (jLoadListener)
+ h = new WebCoreResourceLoader(env, jLoadListener);
+ env->DeleteLocalRef(jLoadListener);
+ return h;
+}
+
+void
+WebFrame::reportError(int errorCode, const WebCore::String& description,
+ const WebCore::String& failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ jstring descStr = env->NewString((unsigned short*)description.characters(), description.length());
+ jstring failUrl = env->NewString((unsigned short*)failingUrl.characters(), failingUrl.length());
+ 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
+ const WebCore::KURL& url = frame->loader()->activeDocumentLoader()->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::FrameLoadTypeSame ||
+ (loadType == WebCore::FrameLoadTypeRedirectWithLockedHistory &&
+ !isMainFrame))
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WebCore::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 = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadStarted, urlStr, favicon,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+}
+
+void
+WebFrame::transitionToCommitted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = JSC::Bindings::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 = JSC::Bindings::getJNIEnv();
+ WebCore::FrameLoader* loader = frame->loader();
+ const WebCore::KURL& url = loader->activeDocumentLoader()->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();
+ WebCore::String urlString(url.string());
+ jstring urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+ 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 = JSC::Bindings::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 = JSC::Bindings::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 = JSC::Bindings::getJNIEnv();
+ WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
+}
+
+void
+WebFrame::setTitle(const WebCore::String& title)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+#ifndef NDEBUG
+ LOGV("setTitle(%s)", title.ascii().data());
+#endif
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jTitleStr = env->NewString((unsigned short *)title.characters(), title.length());
+
+ 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 = JSC::Bindings::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 = JSC::Bindings::getJNIEnv();
+ int progress = (int) (100 * newProgress);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetProgress, progress);
+ checkException(env);
+}
+
+const WebCore::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 = JSC::Bindings::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::updateVisitedHistory(const WebCore::KURL& url, bool reload)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ WebCore::String urlStr(url.string());
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = env->NewString((unsigned short*)urlStr.characters(), urlStr.length());
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload);
+ checkException(env);
+}
+
+bool
+WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ // Internal loads are ok but any request that is due to a user hitting a key
+ // should be checked.
+ bool userGesture = false;
+#ifdef ANDROID_USER_GESTURE
+ userGesture = request.userGesture();
+#endif
+ // always handle "POST" in place
+ if (equalIgnoringCase(request.httpMethod(), "POST"))
+ return true;
+ WebCore::KURL requestUrl = request.url();
+ if (!mUserInitiatedClick && !userGesture &&
+ (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") ||
+ requestUrl.protocolIs("file") || requestUrl.protocolIs("about") ||
+ requestUrl.protocolIs("javascript")))
+ return true;
+ WebCore::String url(request.url().string());
+ // Empty urls should not be sent to java
+ if (url.isEmpty())
+ return true;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+
+ // 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);
+ return (ret == 0);
+}
+
+WebCore::Frame*
+WebFrame::createWindow(bool dialog, bool userGesture)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = JSC::Bindings::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 = JSC::Bindings::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 = JSC::Bindings::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 = JSC::Bindings::getJNIEnv();
+ PolicyFunctionWrapper* p = new PolicyFunctionWrapper;
+ p->func = func;
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDecidePolicyForFormResubmission, p);
+}
+
+WebCore::String
+WebFrame::getRawResourceFilename(RAW_RES_ID id) const
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring ret = (jstring) env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mGetRawResFilename, (int)id);
+
+ return to_string(env, ret);
+}
+
+// ----------------------------------------------------------------------------
+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!");
+
+ (pFrame->loader()->*(pFunc->func))((WebCore::PolicyAction)decision);
+}
+
+static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAssetManager, jobject historyList)
+{
+ JSC::initializeThreading();
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ ChromeClientAndroid* chromeC = new ChromeClientAndroid;
+ EditorClientAndroid* editorC = new EditorClientAndroid;
+ WebCore::ContextMenuClient* contextMenuC = new ContextMenuClientAndroid;
+ WebCore::DragClient* dragC = new DragClientAndroid;
+ InspectorClientAndroid* inspectorC = new InspectorClientAndroid;
+ // Create a new page
+ WebCore::Page* page = new WebCore::Page(chromeC, contextMenuC, editorC, dragC, inspectorC);
+ // css files without explicit MIMETYPE is treated as generic text files in
+ // the Java side. So we can't enforce CSS MIMETYPE.
+ page->settings()->setEnforceCSSMIMETypeInStrictMode(false);
+ /* TODO: Don't turn on PageCache until we can restore the ScrollView State.
+ * This caused bug http://b/issue?id=1202983
+ page->settings()->setUsesPageCache(true);
+ // 10 is a random number chosen because it is small enough to give the user
+ // a good back/forward page cache without allowing the page cache to get too
+ // big.
+ WebCore::pageCache()->setCapacity(10);
+ */
+ 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 chromeC 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);
+
+ // Create a FrameView
+ WebCore::FrameView* frameView = new WebCore::FrameView(frame);
+ // Create a WebFrameView
+ WebFrameView* webFrameView = new WebFrameView(frameView, 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);
+ frameView->deref();
+
+ // Set the frame to active to turn on keyboard focus.
+ frame->init();
+ frame->selection()->setFocused(true);
+
+ // Allow local access to file:/// and substitute data
+ WebCore::FrameLoader::setLocalLoadPolicy(
+ WebCore::FrameLoader::AllowLocalLoadsForLocalAndSubstituteData);
+
+ LOGV("::WebCore:: createFrame %p", frame);
+
+ // Set the mNativeFrame field in Frame
+ SET_NATIVE_FRAME(env, obj, (int)frame);
+
+ // Setup the asset manager.
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ // Initialize our skinning classes
+ WebCore::RenderSkinAndroid::Init(am);
+}
+
+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)
+{
+#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!");
+
+ WebCore::String webcoreUrl = to_string(env, url);
+ WebCore::ResourceRequest request(webcoreUrl);
+ LOGV("LoadUrl %s", webcoreUrl.latin1().data());
+ pFrame->loader()->load(request);
+}
+
+
+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(to_string(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,
+ to_string(env, mimeType), to_string(env, encoding),
+ WebCore::KURL(to_string(env, failUrl)));
+
+ // Perform the load
+ pFrame->loader()->load(request, substituteData);
+}
+
+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();
+}
+
+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
+ WebCore::String renderDump = WebCore::externalRepresentation(pFrame->contentRenderer());
+ unsigned len = renderDump.length();
+ if (!len)
+ return NULL;
+ return env->NewString(renderDump.characters(), len);
+}
+
+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!");
+
+ WebCore::Element *documentElement = pFrame->document()->documentElement();
+ if (!documentElement)
+ return NULL;
+ WebCore::String renderDump = ((WebCore::HTMLElement*)documentElement)->innerText();
+ renderDump.append("\n");
+ unsigned len = renderDump.length();
+ if (!len)
+ return NULL;
+ return env->NewString((unsigned short*)renderDump.characters(), len);
+}
+
+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)
+ loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding());
+ else
+ loader->reload();
+}
+
+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->loader()->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!");
+
+ JSC::JSValue* r =
+ pFrame->loader()->executeScript(to_string(env, script), true);
+ WebCore::String result = WebCore::String();
+ if (r) {
+ // note: r->getString() returns a UString.
+ result = WebCore::String(r->isString() ? r->getString() :
+ r->toString(pFrame->script()->globalObject()->globalExec()));
+ }
+
+ unsigned len = result.length();
+ if (len == 0)
+ return NULL;
+ return env->NewString((unsigned short*)result.characters(), len);
+}
+
+static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
+ jobject javascriptObj, jstring interfaceName)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = (WebCore::Frame*)nativeFramePointer;
+ LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
+
+ JavaVM* vm;
+ env->GetJavaVM(&vm);
+ LOGV("::WebCore:: addJSInterface: %p", pFrame);
+
+ // Copied from qwebframe.cpp
+ JSC::JSLock lock(false);
+ WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame);
+ JSC::Bindings::RootObject *root = pFrame->script()->bindingRootObject();
+ if (window) {
+ JSC::Bindings::setJavaVM(vm);
+ // Add the binding to JS environment
+ JSC::ExecState* exec = window->globalExec();
+ JSC::JSObject *addedObject = JSC::Bindings::Instance::createRuntimeObject(
+ exec, JSC::Bindings::JavaInstance::create(javascriptObj, root));
+ // Add the binding name to the window's table of child objects.
+ JSC::PutPropertySlot slot;
+ window->put(exec,
+ JSC::Identifier(exec, to_string(env, interfaceName)),
+ addedObject, slot);
+ }
+}
+
+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 ClearCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ 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);
+ }
+ // force JavaScript to GC when clear cache
+ WebCore::gcController().garbageCollectSoon();
+ // clear image cache
+ SkImageRef_GlobalPool::SetRAMUsed(0);
+}
+
+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();
+ while (node && !found) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ if (((WebCore::HTMLInputElement*)e)->inputType() ==
+ WebCore::HTMLInputElement::PASSWORD)
+ 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;
+
+ WebCore::String username, password;
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->inputType() == WebCore::HTMLInputElement::PASSWORD)
+ password = input->value();
+ else if (input->inputType() == WebCore::HTMLInputElement::TEXT)
+ username = input->value();
+ if (!username.isNull() && !password.isNull())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ jclass stringClass = env->FindClass("java/lang/String");
+ strArray = env->NewObjectArray(2, stringClass, NULL);
+ env->SetObjectArrayElement(strArray, 0, env->NewString((unsigned short *)
+ username.characters(), username.length()));
+ env->SetObjectArrayElement(strArray, 1, env->NewString((unsigned short *)
+ password.characters(), password.length()));
+ }
+ 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) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->inputType() == WebCore::HTMLInputElement::PASSWORD)
+ passwordEle = input;
+ else if (input->inputType() == WebCore::HTMLInputElement::TEXT)
+ usernameEle = input;
+ if (usernameEle != NULL && passwordEle != NULL)
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ usernameEle->setValue(to_string(env, username));
+ passwordEle->setValue(to_string(env, password));
+ }
+}
+
+static jobject GetFormTextData(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "GetFormTextData must take a valid frame pointer!");
+ jobject hashMap = NULL;
+
+ WTF::PassRefPtr<WebCore::HTMLCollection> collection = pFrame->document()->forms();
+ if (collection->length() > 0) {
+ 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");
+ 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");
+
+ static const WebCore::AtomicString text("text");
+ static const WebCore::AtomicString off("off");
+
+ WebCore::HTMLFormElement* form;
+ WebCore::HTMLInputElement* input;
+ for (WebCore::Node* node = collection->firstItem(); node; node = collection->nextItem()) {
+ form = static_cast<WebCore::HTMLFormElement*>(node);
+ if (form->autoComplete()) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i < size; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->type() == text) {
+ if (e->hasAttribute(WebCore::HTMLNames::autocompleteAttr)) {
+ const WebCore::AtomicString& attr = e->getAttribute(WebCore::HTMLNames::autocompleteAttr);
+ if (attr == off)
+ continue;
+ }
+ input = (WebCore::HTMLInputElement*) e;
+ WebCore::String value = input->value();
+ int len = value.length();
+ if (len) {
+ const WebCore::AtomicString& name = input->name();
+ jstring key = env->NewString((jchar *)name.characters(), name.length());
+ jstring val = env->NewString((jchar *)value.characters(), len);
+ LOG_ASSERT(key && val, "name or value not set");
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ }
+ }
+ }
+ env->DeleteLocalRef(mapClass);
+
+ }
+ return hashMap;
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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 },
+ { "stopLoading", "()V",
+ (void*) StopLoading },
+ { "nativeLoadUrl", "(Ljava/lang/String;)V",
+ (void*) LoadUrl },
+ { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) LoadData },
+ { "externalRepresentation", "()Ljava/lang/String;",
+ (void*) ExternalRepresentation },
+ { "documentAsText", "()Ljava/lang/String;",
+ (void*) DocumentAsText },
+ { "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 },
+ { "getFormTextData", "()Ljava/util/HashMap;",
+ (void*) GetFormTextData }
+};
+
+int register_webframe(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");
+
+ return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
+ gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
+}
+
+} /* namespace android */
+
diff --git a/WebKit/android/jni/WebCoreFrameBridge.h b/WebKit/android/jni/WebCoreFrameBridge.h
new file mode 100644
index 0000000..f554117
--- /dev/null
+++ b/WebKit/android/jni/WebCoreFrameBridge.h
@@ -0,0 +1,134 @@
+/*
+ * 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 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.
+ */
+
+// TODO: change name to WebFrame.h
+
+#ifndef WEBFRAME_H
+#define WEBFRAME_H
+
+#include "FrameLoaderClient.h"
+#include "PlatformString.h"
+#include "WebCoreRefObject.h"
+#include <jni.h>
+
+namespace WebCore {
+ class HistoryItem;
+ class Image;
+ class Page;
+ class RenderPart;
+ class ResourceHandle;
+ class ResourceRequest;
+}
+
+namespace android {
+
+class WebCoreResourceLoader;
+class WebViewCore;
+
+// one instance of WebFrame per Page for calling into Java's BrowserFrame
+class WebFrame : public WebCoreRefObject {
+ public:
+ // these ids need to be in sync with the constants in BrowserFrame.java
+ enum RAW_RES_ID {
+ NODOMAIN = 1,
+ LOADERROR,
+ };
+ WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page);
+ ~WebFrame();
+
+ // helper function
+ static WebFrame* getWebFrame(const WebCore::Frame* frame);
+
+ virtual WebCoreResourceLoader* startLoadingResource(WebCore::ResourceHandle*,
+ const WebCore::ResourceRequest& request,
+ bool isHighPriority,
+ bool synchronous);
+
+ void reportError(int errorCode, const WebCore::String& description,
+ const WebCore::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 WebCore::String& title);
+
+ void windowObjectCleared(WebCore::Frame* frame);
+
+ void setProgress(float newProgress);
+
+ const WebCore::String userAgentForURL(const WebCore::KURL* url);
+
+ void didReceiveIcon(WebCore::Image* icon);
+
+ 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(WebCore::String userAgent) { mUserAgent = userAgent; }
+
+ WebCore::String getRawResourceFilename(RAW_RES_ID) const;
+
+ /**
+ * When the user initiates a click (via trackball, enter-press, or touch),
+ * we set mUserInitiatedClick 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.
+ * We also check it to determine whether or not to allow webkit to request
+ * a scroll. If it was user initated, the scroll is allowed.
+ */
+ void setUserInitiatedClick(bool userInitiatedClick) { mUserInitiatedClick = userInitiatedClick; }
+
+ bool userInitiatedClick() { return mUserInitiatedClick; }
+
+ WebCore::Page* page() const { return mPage; }
+
+private:
+ struct JavaBrowserFrame;
+ JavaBrowserFrame* mJavaFrame;
+ WebCore::Page* mPage;
+ WebCore::String mUserAgent;
+ bool mUserInitiatedClick;
+};
+
+} // namespace android
+
+#endif // WEBFRAME_H
diff --git a/WebKit/android/jni/WebCoreJni.cpp b/WebKit/android/jni/WebCoreJni.cpp
new file mode 100644
index 0000000..f9d9cc9
--- /dev/null
+++ b/WebKit/android/jni/WebCoreJni.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include "config.h"
+
+#include "WebCoreJni.h"
+#include "jni_utility.h"
+#include <jni.h>
+#include <utils/Log.h>
+
+namespace android {
+
+extern int register_webframe(JNIEnv*);
+extern int register_javabridge(JNIEnv*);
+extern int register_resource_loader(JNIEnv*);
+extern int register_webviewcore(JNIEnv*);
+extern int register_webhistory(JNIEnv*);
+extern int register_webicondatabase(JNIEnv*);
+extern int register_websettings(JNIEnv*);
+extern int register_webview(JNIEnv*);
+
+// Class, constructor, and get method on WeakReference
+jclass gWeakRefClass;
+jmethodID gWeakRefInit;
+jmethodID gWeakRefGet;
+
+jobject adoptGlobalRef(JNIEnv* env, jobject obj)
+{
+ // Create a WeakReference object
+ jobject ref = env->NewObject(gWeakRefClass, gWeakRefInit, obj);
+ // Increment the ref count of the WeakReference
+ ref = env->NewGlobalRef(ref);
+ return ref;
+}
+
+AutoJObject getRealObject(JNIEnv* env, jobject obj)
+{
+ jobject real = env->CallObjectMethod(obj, gWeakRefGet);
+ if (!real)
+ LOGE("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.
+WebCore::String to_string(JNIEnv* env, jstring str)
+{
+ if (!str || !env)
+ return WebCore::String();
+ const jchar* s = env->GetStringChars(str, NULL);
+ if (!s)
+ return WebCore::String();
+ WebCore::String ret(s, env->GetStringLength(str));
+ env->ReleaseStringChars(str, s);
+ checkException(env);
+ return ret;
+}
+
+}
+
+struct RegistrationMethod {
+ const char* name;
+ int (*func)(JNIEnv*);
+};
+
+static RegistrationMethod gWebCoreRegMethods[] = {
+ { "JavaBridge", android::register_javabridge },
+ { "WebFrame", android::register_webframe },
+ { "WebCoreResourceLoader", android::register_resource_loader },
+ { "WebViewCore", android::register_webviewcore },
+ { "WebHistory", android::register_webhistory },
+ { "WebIconDatabase", android::register_webicondatabase },
+ { "WebSettings", android::register_websettings },
+ { "WebView", android::register_webview }
+};
+
+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!");
+
+ // Instantiate the WeakReference fields.
+ android::gWeakRefClass = env->FindClass("java/lang/ref/WeakReference");
+ LOG_ASSERT(android::gWeakRefClass, "Could not find WeakReference");
+ android::gWeakRefInit = env->GetMethodID(android::gWeakRefClass,
+ "<init>", "(Ljava/lang/Object;)V");
+ LOG_ASSERT(android::gWeakRefInit,
+ "Could not find constructor for WeakReference");
+ android::gWeakRefGet = env->GetMethodID(android::gWeakRefClass, "get",
+ "()Ljava/lang/Object;");
+ LOG_ASSERT(android::gWeakRefInit,
+ "Could not find get method for WeakReference");
+
+ 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;
+}
diff --git a/WebKit/android/jni/WebCoreJni.h b/WebKit/android/jni/WebCoreJni.h
new file mode 100644
index 0000000..d6e48c7
--- /dev/null
+++ b/WebKit/android/jni/WebCoreJni.h
@@ -0,0 +1,74 @@
+/*
+ * 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 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 ANDROID_WEBKIT_WEBCOREJNI_H
+#define ANDROID_WEBKIT_WEBCOREJNI_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() {
+ if (m_obj)
+ m_env->DeleteLocalRef(m_obj);
+ }
+ jobject get() const {
+ return m_obj;
+ }
+ JNIEnv* env() const {
+ return m_env;
+ }
+private:
+ 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 WeakReference returned as an
+// AutoJObject.
+AutoJObject getRealObject(JNIEnv*, jobject);
+
+// Convert the given jobject to a WeakReference and create a new global
+// reference to that WeakReference.
+jobject adoptGlobalRef(JNIEnv*, jobject);
+
+// Helper method for check java exceptions. Returns true if an exception
+// occurred and logs the exception.
+bool checkException(JNIEnv* env);
+
+// Create a WebCore::String object from a jstring object.
+WebCore::String to_string(JNIEnv* env, jstring str);
+
+}
+
+#endif
diff --git a/WebKit/android/jni/WebCoreRefObject.h b/WebKit/android/jni/WebCoreRefObject.h
new file mode 100644
index 0000000..3e9f840
--- /dev/null
+++ b/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 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 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/WebKit/android/jni/WebCoreResourceLoader.cpp b/WebKit/android/jni/WebCoreResourceLoader.cpp
new file mode 100644
index 0000000..d4eda81
--- /dev/null
+++ b/WebKit/android/jni/WebCoreResourceLoader.cpp
@@ -0,0 +1,333 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "jni_utility.h"
+#include "WebCoreResourceLoader.h"
+#include "SkUtils.h"
+
+#include "CString.h"
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceHandleInternal.h"
+#include "ResourceResponse.h"
+#include "WebCoreJni.h"
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#include <utils/misc.h>
+#include <JNIHelp.h>
+#include <SkTypes.h>
+#include <stdlib.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct resourceloader_t {
+ jfieldID mObject;
+ jmethodID mCancelMethodID;
+ jmethodID mDownloadFileMethodID;
+ jmethodID mWillLoadFromCacheMethodID;
+} 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
+
+WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener)
+{
+ 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);
+}
+
+/*
+* 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)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WebCore::String urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ bool val = env->CallStaticBooleanMethod(resourceLoader,
+ gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr);
+ checkException(env);
+ 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) {
+ WebCore::String valStr = to_string(env, val);
+ if (!valStr.isEmpty())
+ response->setHTTPHeaderField(to_string(env, key), valStr);
+ }
+}
+
+jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
+ jstring statusText, jstring mimeType, jlong expectedLength,
+ jstring encoding, jlong expireTime)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOG_ASSERT(url, "Must have a url in the response!");
+ WebCore::KURL kurl(to_string(env, url));
+ WebCore::String encodingStr;
+ WebCore::String mimeTypeStr;
+ if (mimeType) {
+ mimeTypeStr = to_string(env, mimeType);
+ LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
+ }
+ if (encoding) {
+ encodingStr = to_string(env, encoding);
+ LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
+ }
+ WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
+ kurl, mimeTypeStr, (long long)expectedLength,
+ encodingStr, WebCore::String());
+ response->setHTTPStatusCode(statusCode);
+ if (statusText) {
+ WebCore::String status = to_string(env, statusText);
+ response->setHTTPStatusText(status);
+ LOGV("Response setStatusText: %s", status.latin1().data());
+ }
+ // FIXME: This assumes that time_t is a long and that long is the same size as int.
+ if ((unsigned long)expireTime > INT_MAX)
+ expireTime = INT_MAX;
+ response->setExpirationDate((time_t)expireTime);
+ 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);
+}
+
+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->request();
+ WebCore::KURL url(WebCore::KURL(to_string(env, baseUrl)),
+ to_string(env, redirectTo));
+ r.setURL(url);
+ if (r.httpMethod() == "POST") {
+ r.setHTTPMethod("GET");
+ r.clearHTTPReferrer();
+ r.setHTTPBody(0);
+ r.setHTTPContentType("");
+ }
+ 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;
+ }
+ handle->client()->willSendRequest(handle, r, *response);
+ delete response;
+ WebCore::String s = url.string();
+ return env->NewString((unsigned short*)s.characters(), s.length());
+}
+
+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,
+ to_string(env, failingUrl), to_string(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;J)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 register_resource_loader(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.mWillLoadFromCacheMethodID =
+ env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;)Z");
+ LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
+ "Could not find static method willLoadFromCache on LoadListener");
+
+ return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
+ gResourceloaderMethods, NELEM(gResourceloaderMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/WebCoreResourceLoader.h b/WebKit/android/jni/WebCoreResourceLoader.h
new file mode 100644
index 0000000..5dd5abe
--- /dev/null
+++ b/WebKit/android/jni/WebCoreResourceLoader.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 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 ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+#define ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+
+#include "KURL.h"
+
+#include "WebCoreRefObject.h"
+#include <jni.h>
+
+namespace android {
+
+class WebCoreResourceLoader : public WebCoreRefObject
+{
+public:
+ WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener);
+ virtual ~WebCoreResourceLoader();
+
+ /**
+ * Call to java to cancel the current load.
+ */
+ void cancel();
+
+ /**
+ * Call to java to download the current load rather than feed it
+ * back to WebCore
+ */
+ void downloadFile();
+
+ /**
+ * Call to java to find out if this URL is in the cache
+ */
+ static bool willLoadFromCache(const WebCore::KURL& url);
+
+ // Native jni functions
+ static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring);
+ static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring,
+ jstring, jlong, jstring, jlong);
+ 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);
+
+private:
+ jobject mJLoader;
+};
+
+} // end namespace android
+
+#endif
diff --git a/WebKit/android/jni/WebCoreViewBridge.h b/WebKit/android/jni/WebCoreViewBridge.h
new file mode 100644
index 0000000..db79a37
--- /dev/null
+++ b/WebKit/android/jni/WebCoreViewBridge.h
@@ -0,0 +1,90 @@
+/*
+ * 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 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 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() :
+ mBounds(0,0,0,0),
+ m_windowBounds(0,0,0,0)
+ {}
+ virtual ~WebCoreViewBridge() {}
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect) = 0;
+
+ const WebCore::IntRect& getBounds() const
+ {
+ return mBounds;
+ }
+
+ const WebCore::IntRect& getWindowBounds() const
+ {
+ return m_windowBounds;
+ }
+
+ void setSize(int w, int h)
+ {
+ mBounds.setWidth(w);
+ mBounds.setHeight(h);
+ }
+
+ void setLocation(int x, int y)
+ {
+ mBounds.setX(x);
+ mBounds.setY(y);
+ }
+
+ void setWindowBounds(int x, int y, int h, int v)
+ {
+ m_windowBounds = WebCore::IntRect(x, y, h, v);
+ }
+
+ int width() const { return mBounds.width(); }
+ int height() const { return mBounds.height(); }
+ int locX() const { return mBounds.x(); }
+ int locY() const { return mBounds.y(); }
+
+ virtual bool forFrameView() const { return false; }
+ virtual bool forPluginView() const { return false; }
+
+private:
+ WebCore::IntRect mBounds;
+ WebCore::IntRect m_windowBounds;
+};
+
+#endif // WEBCORE_VIEW_BRIDGE_H
diff --git a/WebKit/android/jni/WebFrameView.cpp b/WebKit/android/jni/WebFrameView.cpp
new file mode 100644
index 0000000..f9a9a5b
--- /dev/null
+++ b/WebKit/android/jni/WebFrameView.cpp
@@ -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 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 "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.
+ SkRect r;
+ android_setrect(&r, transRect);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+ canvas->clipRect(r);
+ }
+ 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/WebKit/android/jni/WebFrameView.h b/WebKit/android/jni/WebFrameView.h
new file mode 100644
index 0000000..2347071
--- /dev/null
+++ b/WebKit/android/jni/WebFrameView.h
@@ -0,0 +1,61 @@
+/*
+ * 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 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 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);
+
+ virtual bool forFrameView() const { return true; }
+
+ private:
+ WebCore::FrameView* mFrameView;
+ WebViewCore* mWebViewCore;
+ };
+
+} // namespace android
+
+#endif // WEB_FRAMEVIEW_H
diff --git a/WebKit/android/jni/WebHistory.cpp b/WebKit/android/jni/WebHistory.cpp
new file mode 100644
index 0000000..8a75230
--- /dev/null
+++ b/WebKit/android/jni/WebHistory.cpp
@@ -0,0 +1,882 @@
+/*
+ * 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 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 "webhistory"
+
+#include <config.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Platform.h>
+
+#include "WebHistory.h"
+
+#include "BackForwardList.h"
+#include "CString.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "HistoryItem.h"
+#include "Page.h"
+#include "TextEncoding.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#include "jni_utility.h"
+
+#include <JNIHelp.h>
+#include <SkUtils.h>
+#include <utils/misc.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;
+ jfieldID mCurrentIndex;
+} 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::BackForwardList* list = 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(NULL);
+ // 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()->setCurrentHistoryItem(current);
+ 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->childItemWithName(child->tree()->name());
+ child->loader()->setCurrentHistoryItem(item);
+ // Append the first child to the queue if it exists.
+ if (WebCore::Frame* f = child->tree()->firstChild())
+ frameQueue.append(f);
+ 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()->currentHistoryItem();
+ }
+ }
+ }
+}
+
+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;
+
+ // Set the current index in the list.
+ WebCore::BackForwardList* list = pFrame->page()->backForwardList();
+ WebCore::HistoryItem* currentItem = list->entries()[index].get();
+ list->goToItem(currentItem);
+
+ // Update the current and previous history item.
+ WebCore::FrameLoader* loader = pFrame->loader();
+ loader->setCurrentHistoryItem(currentItem);
+ loader->setPreviousHistoryItem(list->backItem());
+
+ // Update the request with the current item's info.
+ WebCore::ResourceRequest& request = loader->documentLoader()->request();
+ request.setURL(currentItem->url());
+ request.setMainDocumentURL(currentItem->url());
+ // Reload the current page
+ loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding());
+}
+
+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();
+#ifdef ANDROID_HISTORY_CLIENT
+ RefPtr<WebHistoryItem> bridge = new WebHistoryItem(env, obj, newItem.get());
+ newItem->setBridge(bridge.get());
+#endif
+ // 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);
+#ifdef ANDROID_HISTORY_CLIENT
+ bridge->setActive();
+#endif
+
+ // Add the new item to the back/forward list.
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ pFrame->page()->backForwardList()->addItem(newItem);
+
+#ifdef ANDROID_HISTORY_CLIENT
+ // Update the item.
+ bridge->updateHistoryItem(newItem.get());
+#endif
+}
+
+// 7 empty strings + no document state + children count = 9 unsigned values
+// 1 char for isTargetItem
+// ANDROID_HISTORY_CLIENT adds 1 int for scale.
+#ifdef ANDROID_HISTORY_CLIENT
+#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
+#else
+#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 9 + sizeof(char)))
+#endif
+
+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.
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
+#endif
+ 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) {
+ mObject = adoptGlobalRef(env, obj);
+ mScale = 100;
+ mActive = false;
+ mParent = NULL;
+ mHistoryItem = item;
+}
+
+WebHistoryItem::~WebHistoryItem() {
+ if (mObject) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+ env->DeleteGlobalRef(mObject);
+ }
+}
+
+void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
+#ifdef ANDROID_HISTORY_CLIENT
+ // Do not want to update during inflation.
+ if (!mActive)
+ return;
+ WebHistoryItem* webItem = this;
+ // Now we need to update the top-most WebHistoryItem based on the top-most
+ // HistoryItem.
+ if (mParent) {
+ webItem = mParent.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();
+ }
+ 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->mObject);
+ if (!realItem.get())
+ return;
+
+ const WebCore::String& urlString = item->urlString();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+ const WebCore::String& originalUrlString = item->originalURLString();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull()) {
+ originalUrlStr = env->NewString(
+ (unsigned short*) originalUrlString.characters(),
+ originalUrlString.length());
+ }
+ const WebCore::String& titleString = item->title();
+ jstring titleStr = NULL;
+ if (!titleString.isNull())
+ titleStr = env->NewString((unsigned short*)titleString.characters(), titleString.length());
+
+ // 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;
+ WebCore::String url = item->urlString();
+ if (item->url().hasRef()) {
+ 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);
+#endif
+}
+
+static void historyItemChanged(WebCore::HistoryItem* item) {
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(item,
+ "historyItemChanged called with a null item");
+ if (item->bridge())
+ item->bridge()->updateHistoryItem(item);
+#endif
+}
+
+void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
+{
+#ifdef ANDROID_HISTORY_CLIENT
+ 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);
+
+ // 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);
+#endif
+}
+
+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()->SetIntField(list.get(), gWebBackForwardList.mCurrentIndex, newIndex);
+}
+
+static void write_string(WTF::Vector<char>& v, const WebCore::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 referrer
+ write_string(v, item->formReferrer());
+
+ // Form data
+ const WebCore::FormData* formData = item->formData();
+ if (formData)
+ write_string(v, formData->flattenToString());
+ else
+ write_string(v, WebCore::String()); // Empty constructor does not allocate a buffer.
+
+ // Target
+ write_string(v, item->target());
+
+#ifdef ANDROID_HISTORY_CLIENT
+ WebHistoryItem* bridge = item->bridge();
+ LOG_ASSERT(bridge, "We should have a bridge here!");
+ // Screen scale
+ int scale = bridge->scale();
+ LOGV("Writing scale %d", scale);
+ v.append((char*)&scale, sizeof(int));
+#endif
+
+ // Document state
+ const WTF::Vector<WebCore::String>& docState = item->documentState();
+ WTF::Vector<WebCore::String>::const_iterator end = docState.end();
+ unsigned stateSize = docState.size();
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
+ for (WTF::Vector<WebCore::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();
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(parent->bridge(),
+ "The parent item should have a bridge object!");
+ if (!item->bridge()) {
+ WebHistoryItem* bridge = new 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.
+ LOG_ASSERT(parent->bridge()->parent() == NULL ||
+ item->bridge()->parent() == parent->bridge(),
+ "Somehow this item has an incorrect parent");
+ item->bridge()->setParent(parent->bridge());
+ }
+#endif
+ 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.
+ WebCore::String formContentType;
+ WebCore::String formReferrer;
+ 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 referrer
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Referrer %d %.*s", l, l, data);
+ if (data + l < end)
+ formReferrer = 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;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Set up the form info
+ if (formData != NULL) {
+ WebCore::ResourceRequest r;
+ r.setHTTPMethod("POST");
+ r.setHTTPContentType(formContentType);
+ r.setHTTPReferrer(formReferrer);
+ 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;
+
+#ifdef ANDROID_HISTORY_CLIENT
+ WebHistoryItem* bridge = newItem->bridge();
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
+ // Read the screen scale
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Screen scale %d", l);
+ bridge->setScale(l);
+ data += sizeofUnsigned;
+ if (end - data < sizeofUnsigned)
+ return false;
+#endif
+
+ // 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<WebCore::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();
+#ifdef ANDROID_HISTORY_CLIENT
+ // Set a bridge that will not call into java.
+ child->setBridge(new WebHistoryItem(bridge));
+#endif
+ // Read the child item.
+ if (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
+ return false;
+ }
+#ifdef ANDROID_HISTORY_CLIENT
+ child->bridge()->setActive();
+#endif
+ 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();
+#ifdef ANDROID_HISTORY_CLIENT
+ testItem->setBridge(new WebHistoryItem(NULL));
+#endif
+ 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 referrer
+ 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 referrer 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!");
+#ifdef ANDROID_HISTORY_CLIENT
+ offset += 4; // Scale
+#endif
+ // 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!");
+
+#ifdef ANDROID_HISTORY_CLIENT
+ offset = 36;
+#else
+ offset = 28;
+#endif
+ // 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 register_webhistory(JNIEnv* env)
+{
+#ifdef ANDROID_HISTORY_CLIENT
+ // Get notified of all changes to history items.
+ WebCore::notifyHistoryItemChanged = historyItemChanged;
+#endif
+#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");
+
+ // Find the WebBackForwardList object, the addHistoryItem and
+ // removeHistoryItem methods and the mCurrentIndex field.
+ 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.mCurrentIndex = env->GetFieldID(clazz, "mCurrentIndex", "I");
+ LOG_ASSERT(gWebBackForwardList.mCurrentIndex, "Could not find field mCurrentIndex");
+
+ 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/WebKit/android/jni/WebHistory.h b/WebKit/android/jni/WebHistory.h
new file mode 100644
index 0000000..40dc8f8
--- /dev/null
+++ b/WebKit/android/jni/WebHistory.h
@@ -0,0 +1,76 @@
+/*
+ * 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 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 ANDROID_WEBKIT_WEBHISTORY_H
+#define ANDROID_WEBKIT_WEBHISTORY_H
+
+#include <jni.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class HistoryItem;
+}
+
+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);
+};
+
+class WebHistoryItem : public WTF::RefCounted<WebHistoryItem> {
+public:
+ WebHistoryItem(WebHistoryItem* parent)
+ : mParent(parent)
+ , mObject(NULL)
+ , mScale(100)
+ , mActive(false)
+ , mHistoryItem(NULL) {}
+ WebHistoryItem(JNIEnv*, jobject, WebCore::HistoryItem*);
+ ~WebHistoryItem();
+ void updateHistoryItem(WebCore::HistoryItem* item);
+ void setScale(int s) { mScale = s; }
+ void setActive() { mActive = true; }
+ void setParent(WebHistoryItem* parent) { mParent = parent; }
+ WebHistoryItem* parent() { return mParent.get(); }
+ int scale() { return mScale; }
+ WebCore::HistoryItem* historyItem() { return mHistoryItem; }
+private:
+ RefPtr<WebHistoryItem> mParent;
+ jobject mObject;
+ int mScale;
+ bool mActive;
+ WebCore::HistoryItem* mHistoryItem;
+};
+
+};
+
+#endif
diff --git a/WebKit/android/jni/WebIconDatabase.cpp b/WebKit/android/jni/WebIconDatabase.cpp
new file mode 100644
index 0000000..c982c78
--- /dev/null
+++ b/WebKit/android/jni/WebIconDatabase.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 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 "favicons"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "WebIconDatabase.h"
+
+#include "IconDatabase.h"
+#include "Image.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "jni_utility.h"
+#include "KURL.h"
+#include "WebCoreJni.h"
+
+#include <pthread.h>
+#include "GraphicsJNI.h"
+#include <SkBitmap.h>
+#include <SkImageDecoder.h>
+#include <SkTemplates.h>
+#include <utils/misc.h>
+#include <JNIHelp.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 WebCore::String& pageURL)
+{
+ // If there are no clients currently, drop this message.
+ if (mClients.size() == 0)
+ return;
+
+ // Attempt to attach to the current vm.
+ JavaVM* vm = JSC::Bindings::getJavaVM();
+ JavaVMAttachArgs args;
+
+ args.version = JNI_VERSION_1_4;
+ args.name = "IconDatabase";
+ args.group = NULL;
+
+ JNIEnv* env;
+ bool threadIsAttached = true;
+ if (vm->AttachCurrentThread(&env, (void*) &args) != JNI_OK) {
+ LOGE("Could not attach IconDatabase thread to the VM");
+ threadIsAttached = false;
+ }
+
+ mNotificationsMutex.lock();
+ mNotifications.append(pageURL);
+ if (!mDeliveryRequested) {
+ if (threadIsAttached) {
+ mDeliveryRequested = true;
+ JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this);
+ }
+ }
+ mNotificationsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client)
+{
+ gIconDatabaseClient->mClientsMutex.lock();
+ gIconDatabaseClient->mClients.append(client);
+ gIconDatabaseClient->mClientsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ db->mClientsMutex.lock();
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ if (db->mClients[i] == client) {
+ db->mClients.remove(i);
+ break;
+ }
+ }
+ db->mClientsMutex.unlock();
+}
+
+// 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<WebCore::String> queue;
+ mNotificationsMutex.lock();
+ queue.swap(mNotifications);
+ mDeliveryRequested = false;
+ mNotificationsMutex.unlock();
+
+ // Swap the clients queue
+ Vector<WebIconDatabaseClient*> clients;
+ mClientsMutex.lock();
+ clients.swap(mClients);
+ mClientsMutex.unlock();
+
+ 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");
+ WebCore::String pathStr = to_string(env, path);
+ LOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
+ bool res = iconDb->open(pathStr);
+ if (!res)
+ LOGE("Open failed!");
+}
+
+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");
+ WebCore::String urlStr = to_string(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");
+ WebCore::String urlStr = to_string(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");
+ WebCore::String urlStr = to_string(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 register_webicondatabase(JNIEnv* env)
+{
+ jclass webIconDB = env->FindClass("android/webkit/WebIconDatabase");
+ LOG_ASSERT(webIconDB, "Unable to find class android.webkit.WebIconDatabase");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabase",
+ gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods));
+}
+
+}
diff --git a/WebKit/android/jni/WebIconDatabase.h b/WebKit/android/jni/WebIconDatabase.h
new file mode 100644
index 0000000..c88842c
--- /dev/null
+++ b/WebKit/android/jni/WebIconDatabase.h
@@ -0,0 +1,77 @@
+/*
+ * 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 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 ANDROID_WEBKIT_WEBICONDATABASE_H
+#define ANDROID_WEBKIT_WEBICONDATABASE_H
+
+#include "IconDatabaseClient.h"
+#include "utils/threads.h"
+#include "wtf/Vector.h"
+
+#include <jni.h>
+
+namespace WebCore {
+ class Image;
+ class String;
+}
+
+namespace android {
+
+ class WebIconDatabaseClient {
+ public:
+ virtual ~WebIconDatabaseClient() {}
+ virtual void didAddIconForPageUrl(const WebCore::String& pageUrl) = 0;
+ };
+
+ class WebIconDatabase : public WebCore::IconDatabaseClient {
+ public:
+ WebIconDatabase() : mDeliveryRequested(false) {}
+ // IconDatabaseClient method
+ virtual void dispatchDidAddIconForPageURL(const WebCore::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 and a mutex to protect it.
+ Vector<WebIconDatabaseClient*> mClients;
+ android::Mutex mClientsMutex;
+
+ // Queue of page urls that have received an icon.
+ Vector<WebCore::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/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp
new file mode 100644
index 0000000..407544a
--- /dev/null
+++ b/WebKit/android/jni/WebSettings.cpp
@@ -0,0 +1,356 @@
+/*
+ * 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 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 "websettings"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "DocLoader.h"
+#include "Page.h"
+#include "RenderTable.h"
+#ifdef ANDROID_PLUGINS
+#include "PlatformString.h"
+#include "PluginDatabase.h"
+#endif
+#include "Settings.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <utils/misc.h>
+
+namespace WebCore {
+// Defined in FileSystemAndroid.cpp
+extern String sPluginPath;
+}
+
+namespace android {
+
+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;");
+ 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
+ mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z");
+ mPluginsEnabled = env->GetFieldID(clazz, "mPluginsEnabled", "Z");
+#ifdef ANDROID_PLUGINS
+ mPluginsPath = env->GetFieldID(clazz, "mPluginsPath", "Ljava/lang/String;");
+#endif
+ mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
+ "mJavaScriptCanOpenWindowsAutomatically", "Z");
+ mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
+ mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z");
+ mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z");
+ mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z");
+
+ 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(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(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
+ LOG_ASSERT(mPluginsEnabled, "Could not find field mPluginsEnabled");
+#ifdef ANDROID_PLUGINS
+ LOG_ASSERT(mPluginsPath, "Could not find field mPluginsPath");
+#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(mUseDoubleTree, "Could not find field mUseDoubleTree");
+
+ jclass c = env->FindClass("java/lang/Enum");
+ LOG_ASSERT(c, "Could not find Enum class!");
+ mOrdinal = env->GetMethodID(c, "ordinal", "()I");
+ LOG_ASSERT(mOrdinal, "Could not find method ordinal");
+ c = env->FindClass("android/webkit/WebSettings$TextSize");
+ LOG_ASSERT(c, "Could not find TextSize enum");
+ mTextSizeValue = env->GetFieldID(c, "value", "I");
+ }
+
+ // Field ids
+ jfieldID mLayoutAlgorithm;
+ jfieldID mTextSize;
+ jfieldID mStandardFontFamily;
+ jfieldID mFixedFontFamily;
+ jfieldID mSansSerifFontFamily;
+ jfieldID mSerifFontFamily;
+ jfieldID mCursiveFontFamily;
+ jfieldID mFantasyFontFamily;
+ jfieldID mDefaultTextEncoding;
+ jfieldID mUserAgent;
+ jfieldID mMinimumFontSize;
+ jfieldID mMinimumLogicalFontSize;
+ jfieldID mDefaultFontSize;
+ jfieldID mDefaultFixedFontSize;
+ jfieldID mLoadsImagesAutomatically;
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ jfieldID mBlockNetworkImage;
+#endif
+ jfieldID mJavaScriptEnabled;
+ jfieldID mPluginsEnabled;
+#ifdef ANDROID_PLUGINS
+ jfieldID mPluginsPath;
+#endif
+ jfieldID mJavaScriptCanOpenWindowsAutomatically;
+ jfieldID mUseWideViewport;
+ jfieldID mSupportMultipleWindows;
+ jfieldID mShrinksStandaloneImagesToFit;
+ jfieldID mUseDoubleTree;
+
+ // Ordinal() method and value field for enums
+ jmethodID mOrdinal;
+ jfieldID mTextSizeValue;
+};
+
+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);
+}
+
+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::DocLoader* docLoader = pFrame->document()->docLoader();
+
+#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()->updateStyleSelector();
+ 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->zoomFactor() != zoomFactor)
+ pFrame->setZoomFactor(zoomFactor, /*isTextOnly*/true);
+
+ jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily);
+ s->setStandardFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily);
+ s->setFixedFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily);
+ s->setSansSerifFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily);
+ s->setSerifFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily);
+ s->setCursiveFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily);
+ s->setFantasyFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding);
+ s->setDefaultTextEncodingName(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent);
+ WebFrame::getWebFrame(pFrame)->setUserAgent(to_string(env, str));
+
+ 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)
+ docLoader->setAutoLoadImages(true);
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage);
+ s->setBlockNetworkImage(flag);
+ if(!flag)
+ docLoader->setBlockNetworkImage(false);
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled);
+ s->setJavaScriptEnabled(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mPluginsEnabled);
+ s->setPluginsEnabled(flag);
+
+#ifdef ANDROID_PLUGINS
+ ::WebCore::PluginDatabase *pluginDatabase =
+ ::WebCore::PluginDatabase::installedPlugins();
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mPluginsPath);
+ if (str) {
+ WebCore::String pluginsPath = to_string(env, str);
+ // When a new browser Tab is created, the corresponding
+ // Java WebViewCore object will sync (with the native
+ // side) its associated WebSettings at initialization
+ // time. However, at that point, the WebSettings object's
+ // mPluginsPaths member is set to the empty string. The
+ // real plugin path will be set later by the tab and the
+ // WebSettings will be synced again.
+ //
+ // There is no point in instructing WebCore's
+ // PluginDatabase instance to set the plugin path to the
+ // empty string. Furthermore, if the PluginDatabase
+ // instance is already initialized, setting the path to
+ // the empty string will cause the PluginDatabase to
+ // forget about the plugin files it has already
+ // inspected. When the path is subsequently set to the
+ // correct value, the PluginDatabase will attempt to load
+ // and initialize plugins that are already loaded and
+ // initialized.
+ if (pluginsPath.length()) {
+ s->setPluginsPath(pluginsPath);
+ // Set the plugin directories to this single entry.
+ Vector< ::WebCore::String > paths(1);
+ paths[0] = pluginsPath;
+ pluginDatabase->setPluginDirectories(paths);
+ // Set the home directory for plugin temporary files
+ WebCore::sPluginPath = paths[0];
+ // Reload plugins. We call Page::refreshPlugins() instead
+ // of pluginDatabase->refresh(), as we need to ensure that
+ // the list of mimetypes exposed by the browser are also
+ // updated.
+ WebCore::Page::refreshPlugins(false);
+ }
+ }
+#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);
+#if USE(LOW_BANDWIDTH_DISPLAY)
+ flag = env->GetBooleanField(obj, gFieldIds->mUseDoubleTree);
+ pFrame->loader()->setUseLowBandwidthDisplay(flag);
+#endif
+ }
+};
+
+//-------------------------------------------------------------
+// JNI registration
+//-------------------------------------------------------------
+
+static JNINativeMethod gWebSettingsMethods[] = {
+ { "nativeSync", "(I)V",
+ (void*) WebSettings::Sync }
+};
+
+int register_websettings(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/WebSettings");
+ LOG_ASSERT(clazz, "Unable to find class WebSettings!");
+ gFieldIds = new FieldIds(env, clazz);
+ return jniRegisterNativeMethods(env, "android/webkit/WebSettings",
+ gWebSettingsMethods, NELEM(gWebSettingsMethods));
+}
+
+}
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..d9f9cec
--- /dev/null
+++ b/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,2582 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include <config.h>
+#include "WebViewCore.h"
+
+#include "AtomicString.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Color.h"
+#include "Document.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Font.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HitTestResult.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "InlineTextBox.h"
+#include <JNIHelp.h>
+#include "KeyboardCodes.h"
+#include "Node.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginInfoStore.h"
+#include "PluginWidgetAndroid.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "RenderLayer.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "StringImpl.h"
+#include "SystemTime.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "HistoryItem.h"
+#include "android_graphics.h"
+#include <ui/KeycodeLabels.h>
+#include "jni_utility.h"
+
+#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 "CString.h"
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.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 {
+
+// ----------------------------------------------------------------------------
+
+#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_webView;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ jobject m_obj;
+ jmethodID m_spawnScrollTo;
+ jmethodID m_scrollTo;
+ jmethodID m_scrollBy;
+ jmethodID m_contentDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_didFirstLayout;
+ jmethodID m_sendMarkNodeInvalid;
+ jmethodID m_sendNotifyFocusSet;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendRecomputeFocus;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ 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::gFrameGenerationMutex;
+Mutex WebViewCore::gRecomputeFocusMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::m_contentMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_buildGeneration = 0;
+ m_moveGeneration = 0;
+ m_generation = 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_scale = 1;
+
+ 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 = adoptGlobalRef(env, javaWebViewCore);
+ m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V");
+ m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V");
+ m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V");
+ m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[Z[I)V");
+ m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[ZI)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_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "()V");
+ m_javaGlue->m_sendMarkNodeInvalid = GetJMethod(env, clazz, "sendMarkNodeInvalid", "(I)V");
+ m_javaGlue->m_sendNotifyFocusSet = GetJMethod(env, clazz, "sendNotifyFocusSet", "()V");
+ m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
+ m_javaGlue->m_sendRecomputeFocus = GetJMethod(env, clazz, "sendRecomputeFocus", "()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_restoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V");
+ m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ reset(true);
+}
+
+WebViewCore::~WebViewCore()
+{
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteGlobalRef(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)
+{
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ 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_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_blockFocusChange = false;
+ m_snapAnchorNode = 0;
+ m_useReplay = false;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+ m_domtree_version = 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();
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document())
+ return;
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame))
+ return;
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ 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, INT_MAX, INT_MAX));
+
+ 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 (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;
+ // 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();
+ 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);
+ picture->safeUnref();
+ }
+ // 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);
+ m_frameCacheOutOfDate = true;
+}
+
+void WebViewCore::checkNavCache()
+{
+ CacheBuilder& builder = FrameLoaderClientAndroid::get(m_mainFrame)
+ ->getCacheBuilder();
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ WebCore::IntRect oldBounds = oldFocusNode ?
+ oldFocusNode->getRect() : WebCore::IntRect(0,0,0,0);
+ DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
+ m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height());
+ unsigned latestVersion = m_mainFrame->document()->domTreeVersion();
+ if (m_lastFocused != oldFocusNode || m_lastFocusedBounds != oldBounds
+ || m_findIsUp || latestVersion != m_domtree_version) {
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ DBG_NAV_LOGD("call updateFrameCache m_domtree_version=%d latest=%d",
+ m_domtree_version, latestVersion);
+ m_domtree_version = latestVersion;
+ updateFrameCache();
+ }
+}
+
+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++;
+ }
+ }
+}
+
+void WebViewCore::clearContent()
+{
+ DBG_SET_LOG("");
+ m_contentMutex.lock();
+ m_content.clear();
+ m_contentMutex.unlock();
+ m_addInval.setEmpty();
+}
+
+void WebViewCore::copyContentToPicture(SkPicture* picture)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+
+ int w = copyContent.width();
+ int h = copyContent.height();
+ copyContent.draw(picture->beginRecording(w, h, PICT_RECORD_FLAGS));
+ picture->endRecording();
+ DBG_SET_LOG("end");
+}
+
+bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewUIDrawTimeCounter);
+#endif
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ SkRect clip;
+ clip.set(0, 0, copyContent.width(), copyContent.height());
+ canvas->clipRect(clip, SkRegion::kDifference_Op);
+ canvas->drawColor(color);
+ canvas->restoreToCount(sc);
+ bool tookTooLong = copyContent.draw(canvas);
+ m_contentMutex.lock();
+ m_content.setDrawTimes(copyContent);
+ m_contentMutex.unlock();
+ DBG_SET_LOG("end");
+ return tookTooLong;
+}
+
+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);
+ 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()));
+
+ 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__);
+}
+
+bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
+#endif
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet contentCopy(m_content);
+ m_contentMutex.unlock();
+ recordPictureSet(&contentCopy);
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ if (progress > 0.0f && progress < 1.0f && contentCopy.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return false;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
+ m_contentMutex.lock();
+ contentCopy.setDrawTimes(m_content);
+ m_content.set(contentCopy);
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ m_contentMutex.unlock();
+ 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 true;
+}
+
+void WebViewCore::splitContent()
+{
+ bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
+ PictureSet tempPictureSet;
+ m_contentMutex.lock();
+ m_content.split(&tempPictureSet);
+ m_contentMutex.unlock();
+ rebuildPictureSet(&tempPictureSet);
+ m_contentMutex.lock();
+ m_content.set(tempPictureSet);
+ m_contentMutex.unlock();
+}
+
+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(), animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo, x, y);
+ checkException(env);
+}
+
+void WebViewCore::sendMarkNodeInvalid(WebCore::Node* node)
+{
+ 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_sendMarkNodeInvalid, (int) node);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyFocusSet()
+{
+ 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_sendNotifyFocusSet);
+ 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::sendRecomputeFocus()
+{
+ 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_sendRecomputeFocus);
+ 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::scrollBy(int dx, int dy, bool animate)
+{
+ if (!(dx | dy))
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy,
+ dx, dy, animate);
+ 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::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, max;
+ android_setrect(&rect, r);
+ max.set(0, 0, INT_MAX, INT_MAX);
+ if (!rect.intersect(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::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!");
+
+ const WebCore::KURL& url = m_mainFrame->loader()->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::restoreScale(int scale)
+{
+ 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);
+ 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) // Android
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::notifyFocusSet()
+{
+ sendNotifyFocusSet();
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ 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");
+ }
+ this->scrollBy(dx, dy, true);
+}
+
+void WebViewCore::setScrollOffset(int dx, int dy)
+{
+ DBG_NAV_LOGD("{%d,%d}", dx, dy);
+ 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);
+ m_mainFrame->sendScrollEvent();
+ }
+}
+
+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 screenWidth, int scale, int realScreenWidth, int screenHeight)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->height();
+ window->setSize(width, height);
+ int osw = m_screenWidth;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%d) new:(w=%d,h=%d,sw=%d,scale=%d)",
+ ow, oh, osw, m_scale, width, height, screenWidth, scale);
+ m_screenWidth = screenWidth;
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = (screenWidth * height / width) >> 2;
+ if (ow != width || oh != height || osw != screenWidth) {
+ WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
+ realScreenWidth, screenHeight);
+ if (r) {
+ // get current screen center position
+ WebCore::IntPoint screenCenter = WebCore::IntPoint(
+ m_scrollOffsetX + (realScreenWidth >> 1),
+ m_scrollOffsetY + (screenHeight >> 1));
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(screenCenter, false);
+ WebCore::Node* node = hitTestResult.innerNode();
+ WebCore::IntRect bounds;
+ WebCore::IntPoint offset;
+ 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());
+ offset = WebCore::IntPoint(screenCenter.x() - bounds.x(),
+ screenCenter.y() - bounds.y());
+ if (offset.x() < 0 || offset.x() > realScreenWidth ||
+ offset.y() < 0 || offset.y() > screenHeight)
+ {
+ DBG_NAV_LOGD("offset out of bounds:(x=%d,y=%d)",
+ offset.x(), offset.y());
+ node = 0;
+ }
+ }
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->forceLayout(true);
+ // scroll to restore current screen center
+ if (!node)
+ return;
+ const WebCore::IntRect& newBounds = node->getRect();
+ DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
+ "h=%d,ns=%d)", newBounds.x(), newBounds.y(),
+ newBounds.width(), newBounds.height());
+ scrollBy(newBounds.x() - bounds.x(), newBounds.y() - bounds.y(),
+ false);
+ }
+ }
+}
+
+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
+ if (useFile)
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame->contentRenderer()).utf8();
+ const char* data = renderDump.data();
+ int length = renderDump.length();
+ int last = 0;
+ for (int i = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last) {
+ char* chunk = new char[i - last + 1];
+ strncpy(chunk, (data + last), i - last);
+ chunk[i - last] = '\0';
+ DUMP_RENDER_LOGD("%s", chunk);
+ }
+ last = i + 1;
+ }
+ }
+ if (gRenderTreeFile) {
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().mDebug.print();
+#endif
+}
+
+WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
+{
+ CacheBuilder& builder = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
+ if (!builder.validNode(frame, node))
+ return WebCore::String();
+ if (!node->hasTagName(WebCore::HTMLNames::aTag))
+ return WebCore::String();
+ WebCore::HTMLAnchorElement* anchor = static_cast<WebCore::HTMLAnchorElement*>(node);
+ return anchor->href();
+}
+
+bool WebViewCore::prepareFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return false;
+ }
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
+#endif
+ m_frameCacheOutOfDate = false;
+#if DEBUG_NAV_UI
+ DBG_NAV_LOG("m_frameCacheOutOfDate was true");
+ m_now = SkTime::GetMSecs();
+#endif
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+ m_temp->setGeneration(++m_buildGeneration);
+ CacheBuilder& builder = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
+ 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);
+ return true;
+}
+
+void WebViewCore::releaseFrameCache(bool newCache)
+{
+ if (!newCache) {
+ DBG_NAV_LOG("!newCache");
+ return;
+ }
+ 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();
+ notifyFocusSet();
+ // it's tempting to send an invalidate here, but it's a bad idea
+ // the cache is now up to date, but the focus is not -- the event
+ // may need to be recomputed from the prior history. An invalidate
+ // will draw the stale location causing the ring to flash at the wrong place.
+}
+
+void WebViewCore::updateFrameCache()
+{
+ m_useReplay = false;
+ releaseFrameCache(prepareFrameCache());
+}
+
+void WebViewCore::removeFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ int last = m_frameGenerations.size() - 1;
+ for (int index = 0; index <= last; index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOGD("index=%d last=%d", index, last);
+ if (index != last)
+ m_frameGenerations[index] = m_frameGenerations[last];
+ m_frameGenerations.removeLast();
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+}
+
+void WebViewCore::updateFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ ++m_buildGeneration;
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOG("replace");
+ m_frameGenerations[index].m_generation = m_buildGeneration;
+ goto done;
+ }
+ }
+ {
+ FrameGen frameGen = {frame, m_buildGeneration};
+ m_frameGenerations.append(frameGen);
+ DBG_NAV_LOG("append");
+ }
+done:
+ gFrameGenerationMutex.unlock();
+}
+
+int WebViewCore::retrieveFrameGeneration(WebCore::Frame* frame)
+{
+ int result = INT_MAX;
+ gFrameGenerationMutex.lock();
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ result = m_frameGenerations[index].m_generation;
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+ DBG_NAV_LOGD("frame=%p m_generation=%d result=%d", frame, m_generation, result);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+ SkDebugf("----------- addPlugin %p", w);
+ *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);
+ }
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 0; // should this be non-zero?
+
+ 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();
+ w->localToPageCoords(&dirty);
+ 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::setFinalFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool block)
+{
+ DBG_NAV_LOGD("frame=%p node=%p x=%d y=%d", frame, node, x, y);
+ bool result = finalKitFocus(frame, node, x, y, false);
+ if (block) {
+ m_blockFocusChange = true;
+ if (!result && node)
+ touchUp(m_touchGeneration, 0, 0, 0, x, y, 0, true, true);
+ }
+}
+
+void WebViewCore::setKitFocus(int moveGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " buildGeneration=%d frame=%p node=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, buildGeneration, frame, node, x, y);
+ if (m_blockFocusChange) {
+ DBG_NAV_LOG("m_blockFocusChange");
+ return;
+ }
+ 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
+ }
+ if (!commonKitFocus(moveGeneration, buildGeneration, frame, node, x, y,
+ ignoreNullFocus))
+ return;
+ m_lastGeneration = moveGeneration;
+}
+
+bool WebViewCore::commonKitFocus(int generation, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("generation=%d buildGeneration=%d frame=%p"
+ " node=%p x=%d y=%d", generation, buildGeneration, frame, node, x, y);
+ m_useReplay = true;
+ bool newCache = prepareFrameCache(); // must wait for possible recompute before using
+ if (m_moveGeneration > generation) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > generation=%d",
+ m_moveGeneration, generation);
+ releaseFrameCache(newCache);
+ return false; // short-circuit if a newer move has already been generated
+ }
+ // if the nav cache has been rebuilt since this focus request was generated,
+ // send a request back to the UI side to recompute the kit-side focus
+ if (m_buildGeneration > buildGeneration || (node && !FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().validNode(frame, node))) {
+ DBG_NAV_LOGD("m_buildGeneration=%d > buildGeneration=%d",
+ m_buildGeneration, buildGeneration);
+ gRecomputeFocusMutex.lock();
+ bool first = !m_recomputeEvents.size();
+ m_recomputeEvents.append(generation);
+ gRecomputeFocusMutex.unlock();
+ releaseFrameCache(newCache);
+ if (first)
+ sendRecomputeFocus();
+ return false;
+ }
+ releaseFrameCache(newCache);
+ if (!node && ignoreNullFocus)
+ return true;
+ finalKitFocus(frame, node, x, y, false);
+ return true;
+}
+
+// Update mouse position and may change focused node.
+// If donotChangeDOMFocus is true, the function does not changed focused node
+// in the DOM tree. Changing the focus in DOM may trigger onblur event
+// handler on the current focused node before firing mouse up and down events.
+bool WebViewCore::finalKitFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool donotChangeDOMFocus)
+{
+ CacheBuilder& builder = FrameLoaderClientAndroid::
+ get(m_mainFrame)->getCacheBuilder();
+ if (!frame || builder.validNode(frame, NULL) == false)
+ frame = m_mainFrame;
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ // 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, WebCore::currentTime());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ bool valid = builder.validNode(frame, node);
+ if (!donotChangeDOMFocus) {
+ WebCore::Document* oldDoc = oldFocusNode ? oldFocusNode->document() : 0;
+ if (!node) {
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ } else if (!valid) {
+ DBG_NAV_LOGD("sendMarkNodeInvalid node=%p", node);
+ sendMarkNodeInvalid(node);
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ }
+ // If we jump frames (docs), kill the focus on the old doc
+ if (oldFocusNode && node->document() != oldDoc) {
+ oldDoc->setFocusedNode(0);
+ }
+ if (!node->isTextNode())
+ static_cast<WebCore::Element*>(node)->focus(false);
+ if (node->document()->focusedNode() != node) {
+ // This happens when Element::focus() fails as we may try to set the
+ // focus to a node which WebCore doesn't recognize as a focusable node.
+ // So we need to do some extra work, as it does in Element::focus(),
+ // besides calling Document::setFocusedNode.
+ if (oldFocusNode) {
+ // copied from clearSelectionIfNeeded in FocusController.cpp
+ WebCore::SelectionController* s = oldDoc->frame()->selection();
+ if (!s->isNone())
+ s->clear();
+ }
+ //setFocus on things that WebCore doesn't recognize as supporting focus
+ //for instance, if there is an onclick element that does not support focus
+ node->document()->setFocusedNode(node);
+ }
+ } else { // !donotChangeDOMFocus
+ if (!node || !valid)
+ return false;
+ }
+
+ DBG_NAV_LOGD("setFocusedNode node=%p", node);
+ builder.setLastFocus(node);
+ m_lastFocused = node;
+ m_lastFocusedBounds = node->getRect();
+ return true;
+}
+
+// helper function to find the frame that has focus
+static WebCore::Frame* FocusedFrame(WebCore::Frame* frame)
+{
+ if (!frame)
+ return 0;
+ WebCore::Node* focusNode = FrameLoaderClientAndroid::get(frame)->getCacheBuilder().currentFocus();
+ if (!focusNode)
+ return 0;
+ WebCore::Document* doc = focusNode->document();
+ if (!doc)
+ return 0;
+ return doc->frame();
+}
+
+static WebCore::RenderTextControl* FocusedTextControl(WebCore::Frame* frame)
+{
+ WebCore::Node* focusNode = FrameLoaderClientAndroid::get(frame)->getCacheBuilder().currentFocus();
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ return static_cast<WebCore::RenderTextControl*>(renderer);
+ }
+ return 0;
+}
+
+WebCore::Frame* WebViewCore::changedKitFocus(WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y)
+{
+ if (!frame || !node)
+ return m_mainFrame;
+ WebCore::Node* current = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().currentFocus();
+ if (current == node)
+ return frame;
+ return finalKitFocus(frame, node, x, y, false) ? frame : m_mainFrame;
+}
+
+static int findTextBoxIndex(WebCore::Node* node, const WebCore::IntPoint& pt)
+{
+ if (!node->isTextNode()) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) isText=false", node, pt.x(), pt.y());
+ return -2; // error
+ }
+ WebCore::RenderText* renderText = (WebCore::RenderText*) node->renderer();
+ if (!renderText) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) renderText=0", node, pt.x(), pt.y());
+ return -3; // error
+ }
+ int renderX, renderY;
+ renderText->absolutePosition(renderX, renderY);
+ WebCore::InlineTextBox *textBox = renderText->firstTextBox();
+ int globalX, globalY;
+ CacheBuilder::GetGlobalOffset(node, &globalX, &globalY);
+ int x = pt.x() - globalX;
+ int y = pt.y() - globalY;
+ do {
+ int textBoxStart = textBox->start();
+ int textBoxEnd = textBoxStart + textBox->len();
+ if (textBoxEnd <= textBoxStart)
+ continue;
+ WebCore::IntRect bounds = textBox->selectionRect(renderX, renderY,
+ textBoxStart, textBoxEnd);
+ if (!bounds.contains(x, y))
+ continue;
+ int offset = textBox->offsetForPosition(x - renderX);
+#if DEBUG_NAV_UI
+ int prior = offset > 0 ? textBox->positionForOffset(offset - 1) : -1;
+ int current = textBox->positionForOffset(offset);
+ int next = textBox->positionForOffset(offset + 1);
+ DBG_NAV_LOGD(
+ "offset=%d pt.x=%d globalX=%d renderX=%d x=%d "
+ "textBox->x()=%d textBox->start()=%d prior=%d current=%d next=%d",
+ offset, pt.x(), globalX, renderX, x,
+ textBox->xPos(), textBox->start(), prior, current, next
+ );
+#endif
+ return textBox->start() + offset;
+ } while ((textBox = textBox->nextTextBox()));
+ return -1; // couldn't find point, may have walked off end
+}
+
+static inline bool isPunctuation(UChar c)
+{
+ return WTF::Unicode::category(c) & (0
+ | WTF::Unicode::Punctuation_Dash
+ | WTF::Unicode::Punctuation_Open
+ | WTF::Unicode::Punctuation_Close
+ | WTF::Unicode::Punctuation_Connector
+ | WTF::Unicode::Punctuation_Other
+ | WTF::Unicode::Punctuation_InitialQuote
+ | WTF::Unicode::Punctuation_FinalQuote
+ );
+}
+
+static int centerX(const SkIRect& rect)
+{
+ return (rect.fLeft + rect.fRight) >> 1;
+}
+
+static int centerY(const SkIRect& rect)
+{
+ return (rect.fTop + rect.fBottom) >> 1;
+}
+
+WebCore::String WebViewCore::getSelection(SkRegion* selRgn)
+{
+ SkRegion::Iterator iter(*selRgn);
+ // FIXME: switch this to use StringBuilder instead
+ WebCore::String result;
+ WebCore::Node* lastStartNode = 0;
+ int lastStartEnd = -1;
+ UChar lastChar = 0xffff;
+ for (; !iter.done(); iter.next()) {
+ const SkIRect& rect = iter.rect();
+ DBG_NAV_LOGD("rect=(%d, %d, %d, %d)", rect.fLeft, rect.fTop,
+ rect.fRight, rect.fBottom);
+ int cy = centerY(rect);
+ WebCore::IntPoint startPt = WebCore::IntPoint(rect.fLeft + 1, cy);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(startPt, false);
+ WebCore::Node* node = hitTestResult.innerNode();
+ if (!node) {
+ DBG_NAV_LOG("!node");
+ return result;
+ }
+ WebCore::IntPoint endPt = WebCore::IntPoint(rect.fRight - 2, cy);
+ hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(endPt, false);
+ WebCore::Node* endNode = hitTestResult.innerNode();
+ if (!endNode) {
+ DBG_NAV_LOG("!endNode");
+ return result;
+ }
+ int start = findTextBoxIndex(node, startPt);
+ if (start < 0)
+ continue;
+ int end = findTextBoxIndex(endNode, endPt);
+ if (end < -1) // use node if endNode is not valid
+ endNode = node;
+ if (end <= 0)
+ end = static_cast<WebCore::Text*>(endNode)->string()->length();
+ DBG_NAV_LOGD("node=%p start=%d endNode=%p end=%d", node, start, endNode, end);
+ WebCore::Node* startNode = node;
+ do {
+ if (!node->isTextNode())
+ continue;
+ if (node->getRect().isEmpty())
+ continue;
+ WebCore::Text* textNode = static_cast<WebCore::Text*>(node);
+ WebCore::StringImpl* string = textNode->string();
+ if (!string->length())
+ continue;
+ const UChar* chars = string->characters();
+ int newLength = node == endNode ? end : string->length();
+ if (node == startNode) {
+ #if DEBUG_NAV_UI
+ if (node == lastStartNode)
+ DBG_NAV_LOGD("start=%d last=%d", start, lastStartEnd);
+ #endif
+ if (node == lastStartNode && start < lastStartEnd)
+ break; // skip rect if text overlaps already written text
+ lastStartNode = node;
+ lastStartEnd = newLength - start;
+ }
+ if (newLength < start) {
+ DBG_NAV_LOGD("newLen=%d < start=%d", newLength, start);
+ break;
+ }
+ if (!isPunctuation(chars[start]))
+ result.append(' ');
+ result.append(chars + start, newLength - start);
+ start = 0;
+ } while (node != endNode && (node = node->traverseNextNode()));
+ }
+ result = result.simplifyWhiteSpace().stripWhiteSpace();
+#if DUMP_NAV_CACHE
+ {
+ char buffer[256];
+ CacheBuilder::Debug debug;
+ debug.init(buffer, sizeof(buffer));
+ debug.print("copy: ");
+ debug.wideString(result);
+ DUMP_NAV_LOGD("%s", buffer);
+ }
+#endif
+ return result;
+}
+
+static void selectInFrame(WebCore::Frame* frame, int start, int end)
+{
+ WebCore::Document* doc = frame->document();
+ if (!doc)
+ return;
+
+ WebCore::Node* focus = doc->focusedNode();
+ if (!focus)
+ return;
+
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* rtc = static_cast<WebCore::RenderTextControl*>(renderer);
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+ rtc->setSelectionRange(start, end);
+ frame->revealSelection();
+ }
+}
+
+WebCore::Frame* WebViewCore::setSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ // FIXME: Consider using a generation number to avoid doing this many more times than necessary.
+ frame = changedKitFocus(frame, node, x, y);
+ if (!frame)
+ return 0;
+ selectInFrame(frame, start, end);
+ return frame;
+}
+
+// Shortcut for no modifier keys
+#define NO_MODIFIER_KEYS (static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(0))
+
+WebCore::Frame* WebViewCore::deleteSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ frame = setSelection(frame, node, x, y, start, end);
+ if (start != end) {
+ WebCore::PlatformKeyboardEvent downEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyDown, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(downEvent);
+ WebCore::PlatformKeyboardEvent upEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyUp, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(upEvent);
+ }
+ return frame;
+}
+
+void WebViewCore::replaceTextfieldText(WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int oldStart, int oldEnd, jstring replace, int start, int end)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ WebCore::String webcoreString = to_string(env, replace);
+ frame = setSelection(frame, node, x, y, oldStart, oldEnd);
+ WebCore::TypingCommand::insertText(frame->document(), webcoreString, false);
+ selectInFrame(frame, start, end);
+}
+
+void WebViewCore::passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation,
+ jstring currentText, int keyCode, int keyValue, bool down, bool cap, bool fn, bool sym)
+{
+ frame = changedKitFocus(frame, node, x, y);
+ // Construct the ModifierKey value
+ int mods = 0;
+ if (cap) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (fn) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+ if (sym) {
+ mods |= WebCore::PlatformKeyboardEvent::CtrlKey;
+ }
+ WebCore::PlatformKeyboardEvent event(keyCode, keyValue,
+ down ? WebCore::PlatformKeyboardEvent::KeyDown :
+ WebCore::PlatformKeyboardEvent::KeyUp,
+ 0, static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ frame->eventHandler()->keyEvent(event);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+
+ WebCore::Node* currentFocus = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().currentFocus();
+ // Make sure we have the same focus and it is a text field.
+ if (node == currentFocus && currentFocus) {
+ WebCore::RenderObject* renderer = currentFocus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* renderText = static_cast<WebCore::RenderTextControl*>(renderer);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WebCore::String current = to_string(env, currentText);
+ WebCore::String test = renderText->text();
+ // If the text changed during the key event, update the UI text field.
+ if (test != current)
+ updateTextfield(currentFocus, false, test);
+ }
+ }
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame)
+{
+ if (!FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder()
+ .validNode(frame, 0))
+ frame = m_mainFrame;
+ WebCore::HistoryItem *item = frame->loader()->currentHistoryItem();
+
+ // 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());
+ }
+}
+
+// Convert a WebCore::String into an array of characters where the first
+// character represents the length, for easy conversion to java.
+static uint16_t* stringConverter(const WebCore::String& text)
+{
+ size_t length = text.length();
+ uint16_t* itemName = new uint16_t[length+1];
+ itemName[0] = (uint16_t)length;
+ uint16_t* firstChar = &(itemName[1]);
+ memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
+ return itemName;
+}
+
+// Response to dropdown created for a listbox.
+class ListBoxReply : public WebCoreReply {
+public:
+ ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view)
+ : m_select(select)
+ , m_frame(frame)
+ , m_viewImpl(view)
+ {}
+
+ // Response used if the listbox only allows single selection.
+ // index is listIndex of the selected item, or -1 if nothing is selected.
+ virtual void replyInt(int index)
+ {
+ if (-2 == index) {
+ // Special value for cancel. Do nothing.
+ return;
+ }
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !FrameLoaderClientAndroid::get(m_viewImpl->m_mainFrame)->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+ if (-1 == index) {
+ if (m_select->selectedIndex() != -1) {
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ return;
+ }
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(m_select->listToOptionIndex(index)));
+ if (!option->selected()) {
+ option->setSelected(true);
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ }
+
+ // Response if the listbox allows multiple selection. array stores the listIndices
+ // of selected positions.
+ virtual void replyIntArray(const int* array, int count)
+ {
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !FrameLoaderClientAndroid::get(m_viewImpl->m_mainFrame)->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ WebCore::HTMLOptionElement* option;
+ for (int i = 0; i < count; i++) {
+ option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(m_select->listToOptionIndex(array[i])));
+ option->setSelected(true);
+ }
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+private:
+ // The select element associated with this listbox.
+ WebCore::HTMLSelectElement* m_select;
+ // The frame of this select element, to verify that it is valid.
+ WebCore::Frame* m_frame;
+ // For calling invalidate and checking the select element's validity
+ WebViewCore* m_viewImpl;
+};
+
+// 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::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.
+ jbooleanArray enabledArray = env->NewBooleanArray(enabledCount);
+ checkException(env);
+ jboolean* ptrArray = env->GetBooleanArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseBooleanArrayElements(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(int keyCode, UChar32 unichar, int repeatCount, bool isShift, bool isAlt, bool isDown)
+{
+ DBG_NAV_LOGD("key: keyCode=%d", keyCode);
+
+ WebCore::EventHandler* eventHandler = m_mainFrame->eventHandler();
+ WebCore::Node* focusNode = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ eventHandler = focusNode->document()->frame()->eventHandler();
+ }
+
+ int mods = 0; // PlatformKeyboardEvent::ModifierKey
+ if (isShift) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (isAlt) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+ WebCore::PlatformKeyboardEvent evt(keyCode, unichar,
+ isDown ? WebCore::PlatformKeyboardEvent::KeyDown : WebCore::PlatformKeyboardEvent::KeyUp,
+ repeatCount, static_cast<WebCore::PlatformKeyboardEvent::ModifierKey> (mods));
+ return eventHandler->keyEvent(evt);
+}
+
+bool WebViewCore::click() {
+ bool keyHandled = false;
+ WebCore::Node* focusNode = FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ WebFrame::getWebFrame(m_mainFrame)->setUserInitiatedClick(true);
+ keyHandled = handleMouseClick(focusNode->document()->frame(), focusNode);
+ WebFrame::getWebFrame(m_mainFrame)->setUserInitiatedClick(false);
+ }
+ // match in setFinalFocus()
+ m_blockFocusChange = false;
+ return keyHandled;
+}
+
+bool WebViewCore::handleTouchEvent(int action, int x, int y)
+{
+ bool preventDefault = false;
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ WebCore::TouchEventType type = WebCore::TouchEventCancel;
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchEventStart;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEventEnd;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchEventMove;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchEventCancel;
+ break;
+ }
+ WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ WebCore::PlatformTouchEvent te(pt, pt, type);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y, int size,
+ bool isClick, bool retry)
+{
+ 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
+ }
+ if (retry || isClick)
+ finalKitFocus(frame, node, x, y, true); // don't change DOM focus
+ else if (!commonKitFocus(touchGeneration, buildGeneration,
+ frame, node, x, y, false)) {
+ return;
+ }
+ m_lastGeneration = touchGeneration;
+ // If this is just a touch and not a click, we have already done the change in focus,
+ // so just leave the function now.
+ if (!isClick)
+ return;
+ if (frame) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(m_mainFrame->editor()->client());
+ client->setFromClick(true);
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node);
+ client->setFromClick(false);
+}
+
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
+{
+ bool valid = framePtr == NULL || FrameLoaderClientAndroid::get(
+ m_mainFrame)->getCacheBuilder().validNode(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->setUserInitiatedClick(true);
+ WebCore::EventTargetNodeCast(nodePtr)->dispatchSimulatedClick(0,
+ true, true);
+ webFrame->setUserInitiatedClick(false);
+ return true;
+ }
+ WebCore::RenderObject* renderer = nodePtr->renderer();
+ if (renderer && renderer->isMenuList()) {
+ WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr);
+ const WTF::Vector<WebCore::HTMLElement*>& listItems = select->listItems();
+ SkTDArray<const uint16_t*> names;
+ SkTDArray<int> enabledArray;
+ SkTDArray<int> selectedArray;
+ int size = listItems.size();
+ bool multiple = select->multiple();
+ for (int i = 0; i < size; i++) {
+ if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) {
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]);
+ *names.append() = stringConverter(option->optionText());
+ *enabledArray.append() = option->disabled() ? 0 : 1;
+ if (multiple && option->selected())
+ *selectedArray.append() = i;
+ } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) {
+ WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]);
+ *names.append() = stringConverter(optGroup->groupLabelText());
+ *enabledArray.append() = 0;
+ if (multiple)
+ *selectedArray.append() = 0;
+ }
+ }
+ WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this);
+ listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(),
+ multiple, selectedArray.begin(), multiple ? selectedArray.count() :
+ select->optionToListIndex(select->selectedIndex()));
+ return true;
+ }
+ }
+ if (!valid || !framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedClick(true);
+ DBG_NAV_LOGD("m_mousePos={%d,%d}", m_mousePos.x(), m_mousePos.y());
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WebCore::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,
+ WebCore::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedClick(false);
+ 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 = NULL;
+ }
+}
+
+void WebViewCore::jsAlert(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+bool WebViewCore::jsConfirm(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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 WebCore::String& url, const WebCore::String& text, const WebCore::String& defaultValue, WebCore::String& result)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jDefaultStr = env->NewString((unsigned short *)defaultValue.characters(), defaultValue.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ jstring returnVal = (jstring) env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr);
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = to_string(env, returnVal);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WebCore::String& url, const WebCore::String& message)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = env->NewString((unsigned short *)message.characters(), message.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return getRealObject(JSC::Bindings::getJNIEnv(), m_javaGlue->m_obj);
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WebCore::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;
+ }
+ int length = text.length();
+ jstring string = env->NewString((unsigned short *) text.characters(), length);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::setSnapAnchor(int x, int y)
+{
+ m_snapAnchorNode = 0;
+ if (!x && !y) {
+ return;
+ }
+
+ WebCore::IntPoint point = WebCore::IntPoint(x, y);
+ WebCore::Node* node = m_mainFrame->eventHandler()->hitTestResultAtPoint(point, false).innerNode();
+ if (node) {
+// LOGD("found focus node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ while (node) {
+ if (node->hasTagName(WebCore::HTMLNames::divTag) ||
+ node->hasTagName(WebCore::HTMLNames::tableTag)) {
+ m_snapAnchorNode = node;
+ return;
+ }
+// LOGD("parent node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ node = node->parentNode();
+ }
+ }
+}
+
+void WebViewCore::snapToAnchor()
+{
+ if (m_snapAnchorNode) {
+ if (m_snapAnchorNode->inDocument()) {
+ int rx, ry;
+ m_snapAnchorNode->renderer()->absolutePosition(rx, ry);
+ scrollTo(rx, ry);
+ } else {
+ m_snapAnchorNode = 0;
+ }
+ }
+}
+
+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);
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string)
+{
+ int length = string.length();
+ if (!length)
+ return 0;
+ jstring ret = env->NewString((jchar *)string.characters(), length);
+ env->DeleteLocalRef(ret);
+ return ret;
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint screenWidth, jfloat scale, jint realScreenWidth, jint screenHeight)
+{
+#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");
+ // convert the scale to an int
+ int s = (int) (scale * 100);
+ // a negative value indicates that we should not change the scale
+ if (scale < 0)
+ s = viewImpl->scale();
+
+ viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, s,
+ realScreenWidth, screenHeight);
+}
+
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint dx, jint dy)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(dx, dy);
+}
+
+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 isDown)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Key");
+
+ return viewImpl->key(keyCode, unichar, repeatCount, isShift, isAlt, isDown);
+}
+
+static jboolean Click(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 Click");
+
+ return viewImpl->click();
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeDeleteSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->deleteSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+static void SetSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSetSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->setSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
+ jint framePtr, jint nodePtr, jint x, jint y, jint oldStart, jint oldEnd,
+ jstring replace, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeReplaceTextfieldText()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeReplaceTextfieldText");
+ viewImpl->replaceTextfieldText((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr, x, y, oldStart,
+ oldEnd, replace, start, end);
+}
+
+static void PassToJs(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jint generation, jstring currentText, jint keyCode,
+ jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativePassToJs()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativePassToJs");
+ viewImpl->passToJs((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, generation, currentText, keyCode, keyValue, down, cap, fn, sym);
+}
+
+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);
+}
+
+static bool 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;
+ bool result = viewImpl->recordContent(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return result;
+}
+
+static void SplitContent(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent();
+}
+
+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)
+{
+#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) == CacheBuilder::FOUND_COMPLETE;
+ jstring ret = 0;
+ if (success) {
+ ret = env->NewString((jchar*) addrChars + start, end - start);
+ env->DeleteLocalRef(ret);
+ }
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, 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__);
+ return viewImpl->handleTouchEvent(action, x, y);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y, jint size,
+ jboolean isClick, jboolean retry)
+{
+#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, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y, size, isClick, retry);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint frame,
+ jint node)
+{
+#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::String result = viewImpl->retrieveHref((WebCore::Frame*) frame,
+ (WebCore::Node*) node);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+static void SetFinalFocus(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jboolean block)
+{
+#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->setFinalFocus((WebCore::Frame*) frame, (WebCore::Node*) node, x,
+ y, block);
+}
+
+static void SetKitFocus(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y,
+ jboolean ignoreNullFocus)
+{
+#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->setKitFocus(moveGeneration, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y,
+ ignoreNullFocus);
+}
+
+static void UnblockFocus(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->unblockFocus();
+}
+
+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->minPrefWidth();
+ }
+ }
+ }
+ 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());
+#endif
+}
+
+static void SetSnapAnchor(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__);
+
+ viewImpl->setSnapAnchor(x, y);
+}
+
+static void SnapToAnchor(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->snapToAnchor();
+}
+
+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 jstring GetSelection(JNIEnv *env, jobject obj, jobject selRgn)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ SkRegion* selectionRegion = GraphicsJNI::getNativeRegion(env, selRgn);
+ WebCore::String result = viewImpl->getSelection(selectionRegion);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+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 RefreshPlugins(JNIEnv *env,
+ jobject obj,
+ jboolean reloadOpenPages)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ // Refresh the list of plugins, optionally reloading all open
+ // pages.
+ WebCore::refreshPlugins(reloadOpenPages);
+}
+
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebCore::FrameLoader::registerURLSchemeAsLocal(to_string(env, scheme));
+}
+
+static void CheckNavCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->checkNavCache();
+}
+
+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 CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ viewImpl->copyContentToPicture(picture);
+}
+
+static bool DrawContent(JNIEnv *env, jobject obj, jobject canv, jint color)
+{
+ // Note: this is called from UI thread, don't count it for WebViewCoreTimeCounter
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ return viewImpl->drawContent(canvas, color);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeCheckNavCache", "()V",
+ (void*) CheckNavCache },
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) CopyContentToPicture },
+ { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z",
+ (void*) DrawContent } ,
+ { "nativeKey", "(IIIZZZ)Z",
+ (void*) Key },
+ { "nativeClick", "()Z",
+ (void*) Click },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIFII)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(II)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(IIIIII)V",
+ (void*) SetSelection } ,
+ { "nativeDeleteSelection", "(IIIIII)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IIIIIILjava/lang/String;II)V",
+ (void*) ReplaceTextfieldText } ,
+ { "passToJs", "(IIIIILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs } ,
+ { "nativeSaveDocumentState", "(I)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(III)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIIIIIZZ)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeSetFinalFocus", "(IIIIZ)V",
+ (void*) SetFinalFocus },
+ { "nativeSetKitFocus", "(IIIIIIZ)V",
+ (void*) SetKitFocus },
+ { "nativeUnblockFocus", "()V",
+ (void*) UnblockFocus },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSetSnapAnchor", "(II)V",
+ (void*) SetSnapAnchor },
+ { "nativeSnapToAnchor", "()V",
+ (void*) SnapToAnchor },
+ { "nativeSplitContent", "()V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeGetSelection", "(Landroid/graphics/Region;)Ljava/lang/String;",
+ (void*) GetSelection },
+ { "nativeRefreshPlugins", "(Z)V",
+ (void*) RefreshPlugins },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree }
+};
+
+int register_webviewcore(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_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h
new file mode 100644
index 0000000..3743206
--- /dev/null
+++ b/WebKit/android/jni/WebViewCore.h
@@ -0,0 +1,420 @@
+/*
+ * 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 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 WEBVIEWCORE_H
+#define WEBVIEWCORE_H
+
+#include "CacheBuilder.h"
+#include "CachedHistory.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 <jni.h>
+#include <ui/KeycodeLabels.h>
+
+namespace WebCore {
+ class AtomicString;
+ class Color;
+ class FrameView;
+ class HTMLSelectElement;
+ class RenderPart;
+ class RenderText;
+ class Node;
+ class RenderTextControl;
+ class ScrollView;
+ class TimerBase;
+}
+
+struct PluginWidgetAndroid;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ 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
+
+ /**
+ * 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);
+
+ /**
+ * Scroll to the point x,y relative to the current position.
+ * @param x The relative x position.
+ * @param y The relative y position.
+ * @param animate If it is true, animate to the new scroll position
+ */
+ void scrollBy(int x, int y, bool animate);
+
+ /**
+ * Record the invalid rectangle
+ */
+ void contentInvalidate(const WebCore::IntRect &rect);
+
+ /**
+ * Satisfy any outstanding invalidates, so that the current state
+ * of the DOM is drawn.
+ */
+ void contentDraw();
+
+ /** 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 focus was set after returning to prior page
+ * used to rebuild and display any changes in focus
+ */
+ void notifyFocusSet();
+
+ /**
+ * 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 restore the screen width, which in turn restores
+ * the scale.
+ */
+ void restoreScale(int);
+
+ /**
+ * 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 WebCore::String& text);
+
+ // JavaScript support
+ void jsAlert(const WebCore::String& url, const WebCore::String& text);
+ bool jsConfirm(const WebCore::String& url, const WebCore::String& text);
+ bool jsPrompt(const WebCore::String& url, const WebCore::String& message,
+ const WebCore::String& defaultValue, WebCore::String& result);
+ bool jsUnload(const WebCore::String& url, const WebCore::String& message);
+
+ //
+ // Followings support calls from Java to native WebCore
+ //
+
+ WebCore::String retrieveHref(WebCore::Frame* frame, WebCore::Node* node);
+
+ WebCore::String getSelection(SkRegion* );
+
+ // Create a single picture to represent the drawn DOM (used by navcache)
+ void recordPicture(SkPicture* picture);
+ // Rebuild the nav cache if the dom changed
+ void checkNavCache();
+ // 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);
+ void setFinalFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool block);
+ void setKitFocus(int moveGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus);
+
+ // set the scroll amount that webview.java is currently showing
+ void setScrollOffset(int dx, int dy);
+
+ void setGlobalBounds(int x, int y, int h, int v);
+
+ void setSizeScreenWidthAndScale(int width, int height, int screenWidth,
+ int scale, int realScreenWidth, int screenHeight);
+
+ /**
+ * Handle key events from Java.
+ * @return Whether keyCode was handled by this class.
+ */
+ bool key(int keyCode, UChar32 unichar, int repeatCount, bool isShift, bool isAlt, bool isDown);
+
+ /**
+ * Handle (mouse) click event from Java
+ */
+ bool click();
+
+ /**
+ * Handle touch event
+ */
+ bool handleTouchEvent(int action, int x, int y);
+
+ /**
+ * Handle motionUp event from the UI thread (called touchUp in the
+ * WebCore thread).
+ */
+ void touchUp(int touchGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int size, bool isClick, bool retry);
+
+ /**
+ * 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 there is no
+ * focus, or if start == end, silently fail, but set selection to that value.
+ * If start and end are out of order, swap them.
+ * Use the frame, node, x, and y to ensure that the correct node is focused.
+ * Return a frame. Convenience so replaceTextfieldText can use this function.
+ */
+ WebCore::Frame* deleteSelection(WebCore::Frame* frame, WebCore::Node* node, int x,
+ int y,int start, int end);
+
+ /**
+ * Set the selection of the currently focused textfield to (start, end).
+ * If start and end are out of order, swap them.
+ * Use the frame, node, x, and y to ensure that the correct node is focused.
+ * Return a frame. Convenience so deleteSelection can use this function.
+ */
+ WebCore::Frame* setSelection(WebCore::Frame* frame, WebCore::Node* node, int x,
+ int y,int start, int end);
+ /**
+ * In the currenlty focused textfield, represented by frame, node, x, and y (which
+ * are used to ensure it has focus), 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(WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int oldStart, int oldEnd, jstring replace, int start, int end);
+ void passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation,
+ jstring currentText, int jKeyCode, int keyVal, bool down, bool cap, bool fn, bool sym);
+
+ void saveDocumentState(WebCore::Frame* frame);
+
+ // 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 setSnapAnchor(int x, int y);
+ void snapToAnchor();
+ void unblockFocus() { m_blockFocusChange = false; }
+ void updateFrameCache();
+ 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*);
+ void invalPlugin(PluginWidgetAndroid*);
+ void drawPlugins();
+
+ // Notify the Java side whether it needs to pass down the touch events
+ void needTouchEvents(bool);
+
+ // other public functions
+ public:
+ void removeFrameGeneration(WebCore::Frame* );
+ void updateFrameGeneration(WebCore::Frame* );
+
+ // reset the picture set to empty
+ void clearContent();
+
+ // flatten the picture set to a picture
+ void copyContentToPicture(SkPicture* );
+
+ // draw the picture set with the specified background color
+ bool drawContent(SkCanvas* , SkColor );
+
+ // record the inval area, and the picture size
+ bool recordContent(SkRegion* , SkIPoint* );
+ int screenWidth() const { return m_screenWidth; }
+ int scale() const { return m_scale; }
+ WebCore::Frame* mainFrame() const { return m_mainFrame; }
+
+ // utility to split slow parts of the picture set
+ void splitContent();
+
+ // these members are shared with webview.cpp
+ int retrieveFrameGeneration(WebCore::Frame* );
+ static Mutex gFrameCacheMutex;
+ CachedRoot* m_frameCacheKit; // nav data being built by webcore
+ SkPicture* m_navPictureKit;
+ int m_generation; // copy of the number bumped by WebViewNative
+ 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_useReplay;
+ bool m_findIsUp;
+ static Mutex gRecomputeFocusMutex;
+ WTF::Vector<int> m_recomputeEvents;
+ // 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:
+ // 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);
+
+ 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);
+
+ friend class ListBoxReply;
+ struct FrameGen {
+ const WebCore::Frame* m_frame;
+ int m_generation;
+ };
+ WTF::Vector<FrameGen> m_frameGenerations;
+ static Mutex gFrameGenerationMutex;
+ struct JavaGlue;
+ struct JavaGlue* m_javaGlue;
+ WebCore::Frame* m_mainFrame;
+ WebCoreReply* m_popupReply;
+ WebCore::Node* m_lastFocused;
+ WebCore::IntRect m_lastFocusedBounds;
+ static Mutex m_contentMutex; // protects ui/core thread pictureset access
+ PictureSet m_content; // the set of pictures to draw (accessed by UI too)
+ SkRegion m_addInval; // the accumulated inval region (not yet drawn)
+ // Used in passToJS to avoid updating the UI text field until after the
+ // key event has been processed.
+ bool m_blockTextfieldUpdates;
+ 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_buildGeneration;
+ 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_blockFocusChange;
+ int m_lastPassed;
+ int m_lastVelocity;
+ CachedHistory m_history;
+ WebCore::Node* m_snapAnchorNode;
+ int m_screenWidth;
+ int m_scale;
+ unsigned m_domtree_version;
+
+ SkTDArray<PluginWidgetAndroid*> m_plugins;
+ WebCore::Timer<WebViewCore> m_pluginInvalTimer;
+ void pluginInvalTimerFired(WebCore::Timer<WebViewCore>*) {
+ this->drawPlugins();
+ }
+
+ WebCore::Frame* changedKitFocus(WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y);
+ bool commonKitFocus(int generation, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus);
+ bool finalKitFocus(WebCore::Frame* frame, WebCore::Node* node, int x, int y, bool donotChangeDOMFocus);
+ void doMaxScroll(CacheBuilder::Direction dir);
+ SkPicture* rebuildPicture(const SkIRect& inval);
+ void rebuildPictureSet(PictureSet* );
+ void sendMarkNodeInvalid(WebCore::Node* );
+ void sendNotifyFocusSet();
+ void sendNotifyProgressFinished();
+ void sendRecomputeFocus();
+ bool handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr);
+ bool prepareFrameCache();
+ void releaseFrameCache(bool newCache);
+#if DEBUG_NAV_UI
+ uint32_t m_now;
+#endif
+ };
+
+} // namespace android
+
+#endif // WEBVIEWCORE_H
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp
new file mode 100644
index 0000000..9150830
--- /dev/null
+++ b/WebKit/android/nav/CacheBuilder.cpp
@@ -0,0 +1,3076 @@
+/*
+ * 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 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 "CachedPrefix.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Document.h"
+#include "EventNames.h"
+#include "EventTargetNode.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 "InlineTextBox.h"
+#include "KURL.h"
+#include "PluginView.h"
+#include "RenderImage.h"
+#include "RenderInline.h"
+#include "RenderListBox.h"
+#include "RenderSkinCombo.h"
+#include "RenderTextControl.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();
+}
+
+#if DUMP_NAV_CACHE
+
+#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(", ");
+}
+
+int CacheBuilder::Debug::flowBoxes(RenderFlow* flow, int ifIndex, int indent) {
+ char scratch[256];
+ const InlineFlowBox* box = flow->firstLineBox();
+ if (box == NULL)
+ return ifIndex;
+ do {
+ newLine();
+ int i = snprintf(scratch, sizeof(scratch), "// render flow:%p"
+ " box:%p%.*s", flow, box, indent, " ");
+ for (; box; box = box->nextFlowBox()) {
+ i += snprintf(&scratch[i], sizeof(scratch) - i,
+ " [%d]:{%d, %d, %d, %d}", ++ifIndex,
+ box->xPos(), box->yPos(), box->width(), box->height());
+ if (ifIndex % 4 == 0)
+ break;
+ }
+ print(scratch);
+ } while (box);
+ RenderObject const * const end = flow->lastChild();
+ if (end == NULL)
+ return ifIndex;
+ indent += 2;
+ if (indent > 8)
+ indent = 8;
+ for (const RenderObject* renderer = flow->firstChild(); renderer != end;
+ renderer = renderer->nextSibling())
+ {
+ if (renderer->isInlineFlow())
+ ifIndex = flowBoxes((RenderFlow*) renderer, ifIndex, indent);
+ }
+ return ifIndex;
+}
+
+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 (mBuffer[len] == '\\' || 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("#define TEST%s_RECTS NULL\n", name);
+ DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = 0; // 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;
+ EventTargetNode* elementTarget = node->isEventTargetNode() ?
+ (EventTargetNode*) node : NULL;
+ if (elementTarget) {
+ if (elementTarget->getEventListener(eventNames().clickEvent))
+ properties.append("ONCLICK | ");
+ if (elementTarget->getEventListener(eventNames().mousedownEvent))
+ properties.append("MOUSEDOWN | ");
+ if (elementTarget->getEventListener(eventNames().mouseupEvent))
+ properties.append("MOUSEUP | ");
+ if (elementTarget->getEventListener(eventNames().mouseoverEvent))
+ properties.append("MOUSEOVER | ");
+ if (elementTarget->getEventListener(eventNames().mouseoutEvent))
+ properties.append("MOUSEOUT | ");
+ if (elementTarget->getEventListener(eventNames().keydownEvent))
+ properties.append("KEYDOWN | ");
+ if (elementTarget->getEventListener(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 = Builder(frame)->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;
+ if (renderer) {
+ const IntRect& absB = renderer->absoluteBoundingBoxRect();
+ snprintf(scratch, sizeof(scratch), ", {%d, %d, %d, %d}, %s"
+ ", %d},",absB.x(), absB.y(), absB.width(), absB.height(),
+ renderer->hasOverflowClip() ? "true" : "false", tabindex);
+ 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);
+ NamedAttrMap* 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)
+ renderTree(renderer, 0, node, count);
+ 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;
+ 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) == 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) {
+ int renderX, renderY;
+ renderText->absolutePosition(renderX, renderY);
+ IntRect rect = textBox->selectionRect(renderX, renderY, 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(), textBox->selectionHeight(), textBox->selectionTop());
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d",
+ textBox->spaceAdd(), textBox->start(), textBox->textPos());
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d",
+ textBox->xPos(), textBox->yPos(), textBox->width(), textBox->height());
+ mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d }, // %d ",
+ textBox->baseline(), ++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", 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 (node->isEventTargetNode())
+ 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::renderTree(RenderObject* renderer, int indent,
+ Node* child, int count)
+{
+ char scratch[256];
+ Node* node = renderer->node();
+ if (node != child) {
+ count = ParentIndex(child, count, node);
+ if (renderer->isRenderBlock() == false)
+ goto tryParent;
+ RenderBlock* renderBlock = (RenderBlock*) renderer;
+ if (renderBlock->hasColumns() == false)
+ goto tryParent;
+ Vector<IntRect>* rects = renderBlock->columnRects();
+ newLine(indent);
+ snprintf(scratch, sizeof(scratch), "// render parent=%d", count);
+ print(scratch);
+ for (size_t x = 0; x < rects->size(); x++) {
+ const IntRect& rect = rects->at(x);
+ snprintf(scratch, sizeof(scratch), "(%d,%d,%d,%d) ", rect.x(),
+ rect.y(), rect.width(), rect.height());
+ print(scratch);
+ }
+ }
+ {
+ newLine(indent);
+ RenderStyle* style = renderer->style();
+ EVisibility vis = style->visibility();
+ ASSERT(vis == VISIBLE || vis == HIDDEN || vis == COLLAPSE);
+ snprintf(scratch, sizeof(scratch),
+ "// render style visible:%s opacity:%g width:%d height:%d"
+ " hasBackground:%s isInlineFlow:%s isBlockFlow:%s"
+ " textOverflow:%s",
+ vis == VISIBLE ? "visible" : vis == HIDDEN ? "hidden" : "collapse",
+ style->opacity(), renderer->width(), renderer->height(),
+ style->hasBackground() ? "true" : "false",
+ renderer->isInlineFlow() ? "true" : "false",
+ renderer->isBlockFlow() ? "true" : "false",
+ style->textOverflow() ? "true" : "false"
+ );
+ print(scratch);
+ newLine(indent);
+ const IntRect& oRect = renderer->overflowRect(true);
+ const IntRect& cRect = renderer->getOverflowClipRect(0,0);
+ snprintf(scratch, sizeof(scratch),
+ "// render xPos:%d yPos:%d overflowRect:{%d, %d, %d, %d} "
+ " getOverflowClipRect:{%d, %d, %d, %d} ",
+ renderer->xPos(), renderer->yPos(),
+ oRect.x(), oRect.y(), oRect.width(), oRect.height(),
+ cRect.x(), cRect.y(), cRect.width(), cRect.height()
+ );
+ print(scratch);
+ if (renderer->isInlineFlow()) {
+ RenderFlow* renderFlow = (RenderFlow*) renderer;
+ int ifIndex = 0;
+ flowBoxes(renderFlow, ifIndex, 0);
+ }
+ }
+tryParent:
+ RenderObject* parent = renderer->parent();
+ if (parent)
+ renderTree(parent, indent + 2, node, count);
+}
+
+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()
+{
+ mLastKnownFocus = NULL;
+ mAllowableTypes = ALL_CACHEDNODETYPES;
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ gNavCacheLogFile = NULL;
+#endif
+}
+
+void CacheBuilder::adjustForColumns(const ClipColumnTracker& track,
+ CachedNode* node, IntRect* bounds)
+{
+ int x = 0;
+ int y = 0;
+ int tx = track.mBounds.x();
+ int ty = track.mBounds.y();
+ int columnGap = track.mColumnGap;
+ size_t limit = track.mColumns->size();
+ for (size_t index = 0; index < limit; index++) {
+ IntRect column = track.mColumns->at(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();
+ }
+}
+
+bool CacheBuilder::AnyChildIsClick(Node* node)
+{
+ Node* child = node->firstChild();
+ while (child != NULL) {
+ if (child->isEventTargetNode()) {
+ EventTargetNode* target = (EventTargetNode*) child;
+ if (target->isFocusable() ||
+ target->getEventListener(eventNames().clickEvent) ||
+ target->getEventListener(eventNames().mousedownEvent) ||
+ target->getEventListener(eventNames().mouseupEvent) ||
+ target->getEventListener(eventNames().keydownEvent) ||
+ target->getEventListener(eventNames().keyupEvent))
+ 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);
+ EventTargetNode* target = (EventTargetNode*) node;
+ if (target->getEventListener(eventNames().mouseoverEvent) == NULL &&
+ target->getEventListener(eventNames().mouseoutEvent) == NULL &&
+ target->getEventListener(eventNames().keydownEvent) == NULL &&
+ target->getEventListener(eventNames().keyupEvent) == NULL)
+ return false;
+ if (target->getEventListener(eventNames().clickEvent))
+ return false;
+ if (target->getEventListener(eventNames().mousedownEvent))
+ return false;
+ if (target->getEventListener(eventNames().mouseupEvent))
+ return false;
+ return AnyChildIsClick(node);
+}
+
+void CacheBuilder::buildCache(CachedRoot* root)
+{
+ Frame* frame = FrameAnd(this);
+ mLastKnownFocus = NULL;
+ m_areaBoundsMap.clear();
+ BuildFrame(frame, frame, root, (CachedFrame*) root);
+ root->finishInit(); // set up frame parent pointers, child pointers
+ setData((CachedFrame*) root);
+}
+
+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()) {
+ PluginView* pv = static_cast<PluginView*>(widget);
+ // check if this plugin really wants key events (TODO)
+ return true;
+ }
+ }
+ return false;
+}
+
+// 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<Tracker> tracker(1);
+ {
+ Tracker* baseTracker = tracker.data(); // sentinel
+ bzero(baseTracker, sizeof(Tracker));
+ baseTracker->mCachedNodeIndex = -1;
+ }
+ WTF::Vector<ClipColumnTracker> clipTracker(1);
+ {
+ ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel
+ bzero(baseTracker, sizeof(ClipColumnTracker));
+ }
+ WTF::Vector<TabIndexTracker> tabIndexTracker(1);
+ {
+ TabIndexTracker* baseTracker = tabIndexTracker.data(); // sentinel
+ bzero(baseTracker, sizeof(TabIndexTracker));
+ }
+#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(cachedParentNode);
+ Node* node = parent;
+ int cacheIndex = 1;
+ Node* focused = doc->focusedNode();
+ if (focused) {
+ setLastFocus(focused);
+ cachedRoot->setFocusBounds(mLastKnownFocusBounds);
+ }
+ int globalOffsetX, globalOffsetY;
+ GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY);
+ while (walk.mMore || (node = node->traverseNextNode()) != NULL) {
+#if DUMP_NAV_CACHE
+ nodeIndex++;
+#endif
+ Tracker* last = &tracker.last();
+ int lastChildIndex = cachedFrame->size() - 1;
+ while (node == last->mLastChild) {
+ if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ cacheIndex--;
+ tracker.removeLast();
+ lastChildIndex = last->mCachedNodeIndex;
+ last = &tracker.last();
+ }
+ if (node == last->mParentLastChild)
+ last->mParentLastChild = NULL;
+ do {
+ const ClipColumnTracker* lastClip = &clipTracker.last();
+ if (node != lastClip->mLastChild)
+ break;
+ clipTracker.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.setChildFrameIndex(childFrameIndex);
+#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 hasFocusRing = true;
+ if (nodeRenderer != NULL) {
+ RenderStyle* style = nodeRenderer->style();
+ if (style->visibility() == HIDDEN)
+ continue;
+ if (nodeRenderer->isImage()) { // set all the area elements to have a link to their images
+ RenderImage* image = static_cast<RenderImage*>(nodeRenderer);
+ HTMLMapElement* map = image->imageMap();
+ if (map) {
+ Node* node;
+ for (node = map->firstChild(); node;
+ node = node->traverseNextNode(map)) {
+ if (!node->hasTagName(HTMLNames::areaTag))
+ continue;
+ HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
+ m_areaBoundsMap.set(area, image);
+ }
+ }
+ }
+ isTransparent = style->hasBackground() == false;
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ hasFocusRing = style->tapHighlightColor().alpha() > 0;
+#endif
+ }
+ bool more = walk.mMore;
+ walk.reset();
+ // GetGlobalBounds(node, &bounds, false);
+ bool computeFocusRings = false;
+ bool hasClip = false;
+ bool hasMouseOver = false;
+ bool isAnchor = false;
+ bool isArea = node->hasTagName(HTMLNames::areaTag);
+ bool isInput = false;
+ bool isPassword = false;
+ bool isTextArea = false;
+ bool isTextField = false;
+ bool isRtlText = false;
+ bool isUnclipped = false;
+ bool isFocus = node == focused;
+ bool takesFocus = false;
+ bool wantsKeyEvents = false;
+ int maxLength = -1;
+ int textSize = 12;
+ int columnGap = 0;
+ TextDirection direction = LTR;
+ String name;
+ String exported;
+ CachedNodeType type = NORMAL_CACHEDNODETYPE;
+ IntRect bounds;
+ IntRect absBounds;
+ WTF::Vector<IntRect>* columns = NULL;
+ int minimumFocusableWidth = MINIMUM_FOCUSABLE_WIDTH;
+ int minimumFocusableHeight = MINIMUM_FOCUSABLE_HEIGHT;
+ if (isArea) {
+ HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
+ bounds = getAreaRect(area);
+ 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();
+ absBounds.move(globalOffsetX, globalOffsetY);
+ hasClip = nodeRenderer->hasOverflowClip();
+
+ if (checkForPluginViewThatWantsFocus(nodeRenderer)) {
+ bounds = absBounds;
+ isUnclipped = true;
+ takesFocus = true;
+ wantsKeyEvents = true;
+ goto keepNode;
+ }
+ if (nodeRenderer->isRenderBlock()) {
+ RenderBlock* renderBlock = (RenderBlock*) nodeRenderer;
+ if (renderBlock->hasColumns()) {
+ columns = renderBlock->columnRects();
+#ifdef ANDROID_EXPOSE_COLUMN_GAP
+ columnGap = renderBlock->columnGap();
+#endif
+ direction = renderBlock->style()->direction();
+ }
+ }
+ if ((hasClip != false || columns != NULL) && lastChild) {
+ clipTracker.grow(clipTracker.size() + 1);
+ ClipColumnTracker& clip = clipTracker.last();
+ clip.mBounds = absBounds;
+ clip.mLastChild = OneAfter(lastChild);
+ clip.mNode = node;
+ clip.mColumns = columns;
+ clip.mColumnGap = columnGap;
+ clip.mHasClip = hasClip;
+ clip.mDirection = direction;
+ if (columns != NULL) {
+ const IntRect& oRect = nodeRenderer->overflowRect(true);
+ clip.mBounds.move(oRect.x(), oRect.y());
+ }
+ }
+ if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODETYPE) {
+ 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 = (CachedNodeType) 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.focusRings()) == false)
+ continue;
+ absBounds = bounds;
+ cachedNode.setBounds(bounds);
+ if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH)
+ continue;
+ if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT)
+ continue;
+ computeFocusRings = 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 = (HTMLInputElement*) node;
+ if (input->inputType() == HTMLInputElement::FILE)
+ continue;
+ isInput = true;
+ isTextField = input->isTextField();
+ isPassword = input->inputType() == HTMLInputElement::PASSWORD;
+ maxLength = input->maxLength();
+ name = String(input->name().string());
+ isUnclipped = isTransparent; // can't detect if this is drawn on top (example: deviant.com login parts)
+ } else if (node->hasTagName(HTMLNames::textareaTag))
+ isTextArea = true;
+ else if (node->hasTagName(HTMLNames::aTag)) {
+ const HTMLAnchorElement* anchorNode =
+ (const HTMLAnchorElement*) node;
+ if (!anchorNode->isFocusable() && !HasTriggerEvent(node))
+ continue;
+ EventTargetNode* target = (EventTargetNode*) node;
+ if (target->disabled())
+ continue;
+ hasMouseOver = target->getEventListener(eventNames().mouseoverEvent);
+ isAnchor = true;
+ KURL href = anchorNode->href();
+ if (!href.isEmpty() && !href.protocolIs("javascript"))
+ // Set the exported string for all non-javascript anchors.
+ exported = href.string();
+ }
+ if (isTextField || isTextArea) {
+ RenderTextControl* renderText =
+ static_cast<RenderTextControl*>(nodeRenderer);
+ if (isFocus)
+ cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd());
+ exported = String(renderText->text());
+ // FIXME: Would it be better to use (float) size()?
+ // 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();
+ textSize = style->fontSize();
+ isRtlText = style->direction() == RTL ||
+ style->textAlign() == WebCore::RIGHT ||
+ style->textAlign() == WebCore::WEBKIT_RIGHT;
+ }
+ minimumFocusableWidth += 4;
+ minimumFocusableHeight += 4;
+ }
+ takesFocus = true;
+ if (isAnchor) {
+ bounds = absBounds;
+ } else {
+ bool isFocusable = node->isKeyboardFocusable(NULL) ||
+ node->isMouseFocusable() || node->isFocusable();
+ if (isFocusable == false) {
+ if (node->isEventTargetNode() == false)
+ continue;
+ EventTargetNode* eventTargetNode = (EventTargetNode*) node;
+ if (eventTargetNode->disabled())
+ continue;
+ bool overOrOut = HasOverOrOut(node);
+ bool hasTrigger = HasTriggerEvent(node);
+ if (overOrOut == false && hasTrigger == false)
+ continue;
+ takesFocus = hasTrigger;
+ }
+ bounds = node->getRect();
+ // For Bank of America site
+ if (isTextField && nodeRenderer->paddingLeft() > 100) {
+ int paddingLeft = nodeRenderer->paddingLeft();
+ int paddingTop = nodeRenderer->paddingTop();
+ int x = bounds.x() + paddingLeft;
+ int y = bounds.y() + paddingTop;
+ int width = bounds.width() - paddingLeft - nodeRenderer->paddingRight();
+ int height = bounds.height() - paddingTop - nodeRenderer->paddingBottom();
+ bounds.setLocation(IntPoint(x, y));
+ bounds.setSize(IntSize(width, height));
+ }
+ if (bounds.width() < minimumFocusableWidth)
+ continue;
+ if (bounds.height() < minimumFocusableHeight)
+ continue;
+ bounds.move(globalOffsetX, globalOffsetY);
+ }
+ computeFocusRings = true;
+ keepNode:
+ cachedNode.init(node);
+ if (computeFocusRings == false) {
+ cachedNode.setBounds(bounds);
+ cachedNode.focusRings().append(bounds);
+ } else if (ConstructPartRects(node, bounds, cachedNode.boundsPtr(),
+ globalOffsetX, globalOffsetY, &cachedNode.focusRings()) == false)
+ continue;
+ keepTextNode:
+ 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);
+ continue;
+ }
+ const IntRect& parentClip = clipTrack.mBounds;
+ if (hasClip == false && isAnchor)
+ clip = parentClip;
+ else
+ clip.intersect(parentClip);
+ hasClip = true;
+ }
+ if (hasClip && cachedNode.clip(clip) == false) {
+ cachedNode.setBounds(clip);
+ cachedNode.focusRings().append(clip);
+ isUnclipped = true;
+ }
+ cachedNode.setNavableRects();
+ cachedNode.setChildFrameIndex(-1);
+ cachedNode.setExport(exported);
+ cachedNode.setHasFocusRing(hasFocusRing);
+ cachedNode.setHasMouseOver(hasMouseOver);
+ cachedNode.setHitBounds(absBounds);
+ cachedNode.setIndex(cacheIndex);
+ cachedNode.setIsAnchor(isAnchor);
+ cachedNode.setIsArea(isArea);
+ cachedNode.setIsFocus(isFocus);
+ cachedNode.setIsInput(isInput);
+ cachedNode.setIsPassword(isPassword);
+ cachedNode.setIsRtlText(isRtlText);
+ cachedNode.setIsTextArea(isTextArea);
+ cachedNode.setIsTextField(isTextField);
+ cachedNode.setIsTransparent(isTransparent);
+ cachedNode.setIsUnclipped(isUnclipped);
+ cachedNode.setMaxLength(maxLength);
+ cachedNode.setName(name);
+ cachedNode.setParentIndex(last->mCachedNodeIndex);
+ if (last->mParentLastChild == NULL)
+ last->mParentLastChild = OneAfter(node->parentNode()->lastChild());
+ cachedNode.setParentGroup(last->mParentLastChild);
+ cachedNode.setTabIndex(tabIndex);
+ cachedNode.setTextSize(textSize);
+ cachedNode.setType(type);
+ cachedNode.setWantsKeyEvents(wantsKeyEvents);
+#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);
+ Tracker& working = tracker.last();
+ working.mCachedNodeIndex = lastIndex;
+ working.mLastChild = OneAfter(lastChild);
+ working.mParentLastChild = OneAfter(node->parentNode()->lastChild());
+ last = &tracker.at(tracker.size() - 2);
+ working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus;
+ }
+ }
+ cacheIndex++;
+ }
+ while (tracker.size() > 1) {
+ Tracker* last = &tracker.last();
+ int lastChildIndex = cachedFrame->size() - 1;
+ if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex))
+ cacheIndex--;
+ tracker.removeLast();
+ }
+}
+
+bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame,
+ const Tracker* 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->focusRings().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 &&
+ lastNode->isEventTargetNode() == true &&
+ HasOverOrOut(lastNode) == true &&
+ HasTriggerEvent(lastNode) == false;
+ if (cachedFrame->focusIndex() == lastChildIndex)
+ cachedFrame->setFocusIndex(last->mCachedNodeIndex);
+ if (onlyChildCached->parent() == lastCached)
+ onlyChildCached->setParentIndex(lastCached->parentIndex());
+ if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL))
+ *lastCached = *onlyChildCached;
+ cachedFrame->removeLast();
+ 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)
+{
+ FindState addressState;
+ FindReset(&addressState);
+ addressState.mWords[0] = addressState.mStarts[0] = chars;
+ 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 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->mBases[s->mWordCount] = baseChars;
+ s->mWords[s->mWordCount] = chars - s->mNumberCount;
+ s->mStarts[s->mWordCount] = s->mCurrentStart;
+ 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) {
+ 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;
+ memmove(s->mBases, &s->mBases[shift], (sizeof(s->mBases) / sizeof(s->mBases[0]) - shift) * sizeof(s->mBases[0]));
+ memmove(s->mWords, &s->mWords[shift], (sizeof(s->mWords) / sizeof(s->mWords[0]) - shift) * sizeof(s->mWords[0]));
+ memmove(s->mStarts, &s->mStarts[shift], (sizeof(s->mStarts) / sizeof(s->mStarts[0]) - shift) * sizeof(s->mStarts[0]));
+ 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->mBases[s->mWordCount] = baseChars;
+ s->mWords[s->mWordCount] = chars;
+ s->mStarts[s->mWordCount] = s->mCurrentStart;
+ 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->mBases[s->mWordCount] = baseChars;
+ s->mWords[s->mWordCount] = chars;
+ s->mStarts[s->mWordCount] = s->mCurrentStart;
+ 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;
+ 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;
+ 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: // minus two below skips city before state
+ for (int wordsIndex = s->mStateWord - 2; 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];
+ 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)
+ return FOUND_NONE;
+ s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction];
+ }
+ }
+ return FOUND_COMPLETE;
+ }
+ nextTest:
+ names += offset;
+ }
+ }
+ 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;
+ memmove(s->mBases, &s->mBases[shift], (sizeof(s->mBases) / sizeof(s->mBases[0]) - shift) * sizeof(s->mBases[0]));
+ memmove(s->mWords, &s->mWords[shift], (sizeof(s->mWords) / sizeof(s->mWords[0]) - shift) * sizeof(s->mWords[0]));
+ memmove(s->mStarts, &s->mStarts[shift], (sizeof(s->mStarts) / sizeof(s->mStarts[0]) - shift) * sizeof(s->mStarts[0]));
+ 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) const
+{
+ RenderImage* map = m_areaBoundsMap.get(area);
+ if (!map)
+ return IntRect();
+ if (area->isDefault())
+ return map->absoluteBoundingBoxRect();
+ return area->getRect(map);
+}
+
+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)
+{
+ EventTargetNode* target = (EventTargetNode*) node;
+ return target->getEventListener(eventNames().mouseoverEvent) ||
+ target->getEventListener(eventNames().mouseoutEvent);
+
+}
+
+bool CacheBuilder::HasTriggerEvent(Node* node)
+{
+ EventTargetNode* target = (EventTargetNode*) node;
+ return target->getEventListener(eventNames().clickEvent) ||
+ target->getEventListener(eventNames().mousedownEvent) ||
+ target->getEventListener(eventNames().mouseupEvent) ||
+ target->getEventListener(eventNames().keydownEvent) ||
+ target->getEventListener(eventNames().keyupEvent);
+}
+
+// #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;
+}
+
+// does not find text to keep it fast
+// (this assume text nodes are more rarely moved than other nodes)
+Node* CacheBuilder::findByCenter(int x, int y) const
+{
+ DBG_NAV_LOGD("x=%d y=%d\n", x, y);
+ Frame* frame = FrameAnd(this);
+ Node* node = frame->document();
+ ASSERT(node != NULL);
+ int globalOffsetX, globalOffsetY;
+ GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY);
+ while ((node = node->traverseNextNode()) != NULL) {
+ Frame* child = HasFrame(node);
+ if (child != NULL) {
+ if (child->document() == NULL)
+ continue;
+ CacheBuilder* cacheBuilder = Builder(child);
+ // if (cacheBuilder->mViewBounds.isEmpty())
+ // continue;
+ Node* result = cacheBuilder->findByCenter(x, y);
+ if (result != NULL)
+ return result;
+ }
+ if (node->isTextNode())
+ continue;
+ IntRect bounds;
+ if (node->hasTagName(HTMLNames::areaTag)) {
+ HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
+ bounds = getAreaRect(area);
+ bounds.move(globalOffsetX, globalOffsetY);
+ } else
+ bounds = node->getRect();
+ if (bounds.isEmpty())
+ continue;
+ bounds.move(globalOffsetX, globalOffsetY);
+ if (x != bounds.x() + (bounds.width() >> 1))
+ continue;
+ if (y != bounds.y() + (bounds.height() >> 1))
+ continue;
+ if (node->isKeyboardFocusable(NULL))
+ return node;
+ if (node->isMouseFocusable())
+ return node;
+ if (node->isFocusable())
+ return node;
+ if (node->isEventTargetNode() == false)
+ continue;
+ if (AnyIsClick(node))
+ continue;
+ if (HasTriggerEvent(node) == false)
+ continue;
+ return node;
+ }
+ return NULL;
+}
+
+bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node,
+ CachedNodeType* type, String* exported) const
+{
+ Text* textNode = static_cast<Text*>(node);
+ StringImpl* string = textNode->string();
+ 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 = (CachedNodeType) (checkType << 1))
+ {
+ if ((checkType & 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->string();
+ 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 {
+ node = node->traverseNextNode();
+ if (node == NULL || node->hasTagName(HTMLNames::aTag)) {
+ 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->string();
+ baseChars = string->characters();
+ inlineTextBox = baseInline;
+ start = inlineTextBox->start();
+ finalNode:
+ findState.mEndResult = 0;
+ } while (true);
+tryNextCheckType:
+ node = textNode;
+ baseInline = saveInline;
+ string = textNode->string();
+ 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:
+ exported->insert(WebCore::String("mailto:"), 0);
+ break;
+ case PHONE_CACHEDNODETYPE:
+ exported->insert(WebCore::String("tel:"), 0);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+noTextMatch:
+ walk->reset();
+ return false;
+}
+
+bool CacheBuilder::IsMailboxChar(UChar ch)
+{
+ static const unsigned body[] = {0x03ff6000, 0x87fffffe, 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::outOfDate()
+{
+ Node* kitFocusNode = currentFocus();
+ if (mLastKnownFocus != kitFocusNode) {
+ DBG_NAV_LOGD("%s\n", "mLastKnownFocus != kitFocusNode");
+ return true;
+ }
+ if (kitFocusNode == NULL)
+ return false;
+ IntRect kitBounds = kitFocusNode->getRect();
+ bool result = kitBounds != mLastKnownFocusBounds;
+ if (result == true)
+ DBG_NAV_LOGD("%s\n", "kitBounds != mLastKnownFocusBounds");
+ return result;
+}
+
+void CacheBuilder::setLastFocus(Node* node)
+{
+ ASSERT(node);
+ mLastKnownFocus = node;
+ mLastKnownFocusBounds = node->getRect();
+}
+
+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;
+}
+
+bool CacheBuilder::validNode(void* matchFrame, void* matchNode) const
+{
+ Frame* frame = FrameAnd(this);
+ if (matchFrame == frame) {
+ if (matchNode == NULL)
+ return true;
+ Node* node = frame->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 = frame->tree()->firstChild();
+ while (child) {
+ bool result = Builder(child)->validNode(matchFrame, matchNode);
+ if (result)
+ return result;
+ child = child->tree()->nextSibling();
+ }
+#if DEBUG_NAV_UI
+ if (frame->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)
+{
+ 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;
+ if (test->isTextNode()) {
+ RenderText* renderText = (RenderText*) renderer;
+ InlineTextBox *textBox = renderText->firstTextBox();
+ if (textBox == NULL)
+ 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 (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();
+ if (AddPartRect(bounds, x, y, result, focusBounds) == false)
+ return false;
+ continue;
+ }
+ if (renderer->hasOverflowClip() == false) {
+ if (nodeIsAnchor && test->hasTagName(HTMLNames::divTag)) {
+ IntRect bounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by AddPartRect
+ int left = bounds.x() + renderer->paddingLeft()
+ + renderer->borderLeft();
+ int top = bounds.y() + renderer->paddingTop()
+ + renderer->borderTop();
+ int right = bounds.right() - renderer->paddingRight()
+ - renderer->borderRight();
+ int bottom = bounds.bottom() - renderer->paddingBottom()
+ - 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->string();
+ const UChar* chars = string->characters();
+ int renderX, renderY;
+ renderText->absolutePosition(renderX, renderY);
+ do {
+ int textBoxStart = textBox->start();
+ int textBoxEnd = textBoxStart + textBox->len();
+ if (textBoxEnd <= start)
+ continue;
+ if (textBoxEnd > relEnd)
+ textBoxEnd = relEnd;
+ IntRect bounds = textBox->selectionRect(renderX, renderY,
+ 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 (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/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h
new file mode 100644
index 0000000..07040e2
--- /dev/null
+++ b/WebKit/android/nav/CacheBuilder.h
@@ -0,0 +1,272 @@
+/*
+ * 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 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 CacheBuilder_H
+#define CacheBuilder_H
+
+#include "CachedDebug.h"
+#include "CachedNodeType.h"
+#include "IntRect.h"
+#include "PlatformString.h"
+#include "TextDirection.h"
+#include "wtf/HashMap.h"
+#include "wtf/Vector.h"
+
+#define NAVIGATION_MAX_PHONE_LENGTH 14
+
+using namespace WebCore;
+
+namespace WebCore {
+
+class Document;
+class Frame;
+class HTMLAreaElement;
+class InlineTextBox;
+class Node;
+class PlatformGraphicsContext;
+class RenderFlow;
+class RenderImage;
+class RenderObject;
+class RenderLayer;
+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_CACHEDNODETYPES; }
+ void buildCache(CachedRoot* root);
+ static bool ConstructPartRects(Node* node, const IntRect& bounds,
+ IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result);
+ Node* currentFocus() const;
+ void disallowAddressDetection() { mAllowableTypes = (CachedNodeType) (
+ mAllowableTypes & ~ADDRESS_CACHEDNODETYPE); }
+ void disallowEmailDetection() { mAllowableTypes = (CachedNodeType) (
+ mAllowableTypes & ~EMAIL_CACHEDNODETYPE); }
+ void disallowPhoneDetection() { mAllowableTypes = (CachedNodeType) (
+ mAllowableTypes & ~PHONE_CACHEDNODETYPE); }
+ static FoundState FindAddress(const UChar* , unsigned length, int* start, int* end);
+ Node* findByCenter(int x, int y) const;
+ static void GetGlobalOffset(Frame* , int* x, int * y);
+ static void GetGlobalOffset(Node* , int* x, int * y);
+ bool outOfDate();
+ void setLastFocus(Node* );
+ bool validNode(void* framePtr, void* nodePtr) const;
+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];
+ CachedNodeType mStoreType;
+ 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* 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;
+ };
+ struct ClipColumnTracker {
+ IntRect mBounds;
+ Node* mLastChild;
+ Node* mNode;
+ WTF::Vector<IntRect>* mColumns;
+ int mColumnGap;
+ TextDirection mDirection;
+ bool mHasClip;
+ };
+ struct TabIndexTracker {
+ int mTabIndex;
+ Node* mLastChild;
+ };
+ struct Tracker {
+ int mCachedNodeIndex;
+ int mTabIndex;
+ Node* mLastChild;
+ Node* mParentLastChild;
+ bool mSomeParentTakesFocus;
+ };
+ void adjustForColumns(const ClipColumnTracker& track,
+ CachedNode* node, IntRect* bounds);
+ 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);
+ void BuildFrame(Frame* root, Frame* frame,
+ CachedRoot* cachedRoot, CachedFrame* cachedFrame);
+ bool CleanUpContainedNodes(CachedFrame* cachedFrame,
+ const Tracker* 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* );
+ IntRect getAreaRect(const HTMLAreaElement* area) const;
+ 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* );
+ Node* tryFocus(Direction direction);
+ Node* trySegment(Direction direction, int mainStart, int mainEnd);
+ Node* mLastKnownFocus;
+ IntRect mLastKnownFocusBounds;
+ CachedNodeType mAllowableTypes;
+ WTF::HashMap<const HTMLAreaElement* , RenderImage* > m_areaBoundsMap;
+#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);
+ int flowBoxes(RenderFlow* flow, int ifIndex, int indent);
+ 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 renderTree(RenderObject* , int indent, Node* , int count);
+ 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/WebKit/android/nav/CachedDebug.h b/WebKit/android/nav/CachedDebug.h
new file mode 100644
index 0000000..3127112
--- /dev/null
+++ b/WebKit/android/nav/CachedDebug.h
@@ -0,0 +1,74 @@
+/*
+ * 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 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 CachedDebug_H
+#define CachedDebug_H
+
+#ifndef DUMP_NAV_CACHE
+#ifdef NDEBUG
+#define DUMP_NAV_CACHE 0
+#else
+#define DUMP_NAV_CACHE 1
+#endif
+#endif
+
+#ifndef DEBUG_NAV_UI
+#ifdef NDEBUG
+#define DEBUG_NAV_UI 0
+#else
+#define DEBUG_NAV_UI 1
+#endif
+#endif
+
+#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 DBG_NAV_LOGD_THROTTLE(format, ...) ((void)0)
+#define DEBUG_NAV_UI_LOGD(...) ((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)
+#else
+#define DUMP_NAV_LOGD(...) LOGD(__VA_ARGS__)
+#endif
+#else
+#define DUMP_NAV_LOGD(...) ((void)0)
+#endif
+
+#endif
diff --git a/WebKit/android/nav/CachedFrame.cpp b/WebKit/android/nav/CachedFrame.cpp
new file mode 100644
index 0000000..4db9e40
--- /dev/null
+++ b/WebKit/android/nav/CachedFrame.cpp
@@ -0,0 +1,1318 @@
+/*
+ * 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 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 "CachedPrefix.h"
+#include "CachedHistory.h"
+#include "CachedNode.h"
+#include "CachedRoot.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 {
+
+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::checkVisited(const CachedNode* node, Direction direction) const
+{
+ return history()->checkVisited(node, direction);
+}
+
+void CachedFrame::clearFocus()
+{
+ if (mFocus < 0)
+ return;
+ CachedNode& focus = mCachedNodes[mFocus];
+ focus.clearFocus(this);
+ mFocus = -1;
+}
+
+// the thing that sucks is that when you're on a link, you want to navigate next door to a link just like this one, but can't make it
+// so with all my other sucky compares, maybe there needs to be one that prefers links that are aligned with the current focus...
+
+// 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 CachedNode* focus) const
+{
+ if (testData.mNode->tabIndex() != bestData.mNode->tabIndex()) {
+ if (testData.mNode->tabIndex() < bestData.mNode->tabIndex()
+ || (focus && focus->tabIndex() < bestData.mNode->tabIndex())) {
+ testData.mNode->setCondition(CachedNode::HIGHER_TAB_INDEX);
+ return REJECT_TEST;
+ }
+ return TEST_IS_BEST;
+ }
+// start here;
+ // if the test minor axis line intersects the line segment between focus 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_FOCUS);
+ return REJECT_TEST;
+ }
+ return TEST_IS_BEST;
+ }
+ if (testData.mInNav) {
+ if (bestData.mMajorDelta < testData.mMajorDelta) {
+ testData.mNode->setCondition(CachedNode::CLOSER_IN_FOCUS);
+ 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;
+// SkFixed focusMultiplier = SK_Fixed1;
+// if (focus != NULL) {
+// if (testData.mMajorDelta < bestData.mMajorDelta) {
+// // use bestData.mMajorDelta,
+// } else if (bestData.mMajorDelta < testData.mMajorDelta) {
+//
+// }
+ 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.mFocusChild != bestData.mFocusChild) {
+ if (bestData.mFocusChild) {
+ testData.mNode->setCondition(CachedNode::IN_FOCUS_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::currentFocus(const CachedFrame** framePtr) const
+{
+ if (framePtr)
+ *framePtr = this;
+ if (mFocus < 0)
+ return NULL;
+ const CachedNode* result = &mCachedNodes[mFocus];
+ const CachedFrame* frame = hasFrame(result);
+ if (frame != NULL)
+ return frame->currentFocus(framePtr);
+ (const_cast<CachedNode*>(result))->fixUpFocusRects();
+ 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, const CachedNode** directHit, const CachedFrame** framePtr, int* x, int* y) 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.mMouseBounds = testData.mNodeBounds = test->getBounds();
+ bool checkForHidden = true;
+ for (size_t part = 0; part < parts; part++) {
+ if (test->focusRings().at(part).intersects(rect)) {
+ if (checkForHidden && mRoot->maskIfHidden(&testData) == true)
+ break;
+ checkForHidden = false;
+ WebCore::IntRect testRect = test->focusRings().at(part);
+ testRect.intersect(testData.mMouseBounds);
+ if (testRect.contains(center)) {
+ // We have a direct hit.
+ if (*directHit == NULL) {
+ *directHit = test;
+ *framePtr = this;
+ *x = center.x();
+ *y = center.y();
+ } else {
+ // We have hit another one before
+ const CachedNode* d = *directHit;
+ if (d->getBounds().contains(testRect)) {
+ // This rectangle is inside the other one, so it is
+ // the best one.
+ *directHit = test;
+ *framePtr = this;
+ }
+ }
+ }
+ if (NULL != *directHit) {
+ // If we have a direct hit already, there is no need to
+ // calculate the distances, or check the other focusring parts
+ break;
+ }
+ 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;
+ 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 (*best > distance) {
+ *best = distance;
+ 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, directHit,
+ framePtr, x, y);
+ if (NULL != frameResult)
+ result = frameResult;
+ }
+ if (NULL != *directHit) {
+ result = *directHit;
+ }
+ 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,
+ int* best, const CachedFrame** framePtr, int* x, int* y) 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;
+ const WebCore::IntRect& testRect = test->hitBounds();
+ if (testRect.intersects(rect) == false)
+ continue;
+ BestData testData;
+ testData.mNode = test;
+ testData.mMouseBounds = testData.mNodeBounds = testRect;
+ if (mRoot->maskIfHidden(&testData) == true)
+ continue;
+ const WebCore::IntRect& bounds = testData.mMouseBounds;
+ WebCore::IntPoint testCenter = WebCore::IntPoint(bounds.x() +
+ (bounds.width() >> 1), bounds.y() + (bounds.height() >> 1));
+ int dx = testCenter.x() - center.x();
+ int dy = testCenter.y() - center.y();
+ int distance = dx * dx + dy * dy;
+ if (*best <= distance)
+ continue;
+ *best = distance;
+ result = test;
+ *framePtr = this;
+ const WebCore::IntRect& focusRect = test->focusRings().at(0);
+ *x = focusRect.x() + (focusRect.width() >> 1);
+ *y = focusRect.y() + (focusRect.height() >> 1);
+ }
+ for (const CachedFrame* frame = mCachedFrames.begin();
+ frame != mCachedFrames.end(); frame++) {
+ const CachedNode* frameResult = frame->findBestHitAt(rect, best,
+ framePtr, x, y);
+ if (NULL != frameResult)
+ result = frameResult;
+ }
+ return result;
+}
+
+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->isFocusable(*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->focusRings().at(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;
+ bestData->mMouseBounds = bestData->mNodeBounds =
+ test->focusRings().at(part);
+ 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);
+ }
+ }
+ }
+ }
+}
+
+bool CachedFrame::finishInit()
+{
+ CachedNode* lastCached = lastNode();
+ lastCached->setLast();
+ CachedFrame* child = mCachedFrames.begin();
+ while (child != mCachedFrames.end()) {
+ child->mParent = this;
+ if (child->finishInit())
+ setFocusIndex(child->indexInParent());
+ child++;
+ }
+ return focusIndex() > 0;
+}
+
+const CachedNode* CachedFrame::frameDown(const CachedNode* test, const CachedNode* limit, BestData* bestData,
+ const CachedNode* focus) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameDown, test, bestData, focus))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, DOWN) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.mNodeBounds = test->focusRings().at(part);
+ if (testData.setDownDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData, focus);
+ 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, focus);
+ if (checkVisited(innerData.mNode, DOWN)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, DOWN))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit);
+ ASSERT(focus == NULL || bestData->mNode != focus);
+ // does the best contain something (or, is it contained by an area which is not the focus?)
+ // 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 CachedNode* focus) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameLeft, test, bestData, focus))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, LEFT) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.mNodeBounds = test->focusRings().at(part);
+ if (testData.setLeftDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData, focus);
+ 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, focus);
+ 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(focus == NULL || bestData->mNode != focus);
+ return bestData->mNode;
+}
+
+int CachedFrame::frameNodeCommon(BestData& testData, const CachedNode* test, BestData* bestData, BestData* originalData,
+ const CachedNode* focus) const
+{
+ testData.mFrame = this;
+ testData.mNode = test;
+ test->clearCondition();
+ if (test->disabled()) {
+ testData.mNode->setCondition(CachedNode::DISABLED);
+ return REJECT_TEST;
+ }
+ if (mRoot->scrolledBounds().intersects(test->bounds()) == false) {
+ testData.mNode->setCondition(CachedNode::FOCUSABLE);
+ return REJECT_TEST;
+ }
+// if (isFocusable(test, &testData.mNodeBounds, walk) == false) {
+// testData.mNode->setCondition(CachedNode::FOCUSABLE);
+// return REJECT_TEST;
+// }
+//
+ if (test == focus) {
+ testData.mNode->setCondition(CachedNode::NOT_FOCUS_NODE);
+ return REJECT_TEST;
+ }
+// if (test->bounds().contains(mRoot->focusBounds())) {
+// testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+// return REJECT_TEST;
+// }
+ void* par = focus ? focus->parentGroup() : NULL;
+ testData.mFocusChild = test->parentGroup() == par;
+#if 0 // not debugged
+ if (focus && focus->hasMouseOver() && test->hasMouseOver() == false &&
+ focus->bounds().contains(test->bounds()))
+ return REJECT_TEST;
+#endif
+ if (bestData->mNode == NULL)
+ return TEST_IS_BEST;
+#if 0 // not debugged
+ if (focus && focus->hasMouseOver() && test->hasMouseOver() == false &&
+ focus->bounds().contains(test->bounds()))
+ return REJECT_TEST;
+ if (test->hasMouseOver() != bestData->mNode->hasMouseOver()) {
+ if (test->hasMouseOver()) {
+ if (test->bounds().contains(bestData->mNode->bounds())) {
+ const_cast<CachedNode*>(bestData->mNode)->setDisabled(true);
+ bestData->mNode = NULL; // force part tests to be ignored, yet still set up remaining test data for later comparison
+ return TEST_IS_BEST;
+ }
+ } else {
+ if (bestData->mNode->bounds().contains(test->bounds())) {
+ test->setCondition(CachedNode::ANCHOR_IN_ANCHOR);
+ return REJECT_TEST;
+ }
+ }
+ }
+#endif
+ if (focus && testData.mNode->parentIndex() != bestData->mNode->parentIndex()) {
+ int focusParentIndex = focus->parentIndex();
+ if (focusParentIndex >= 0) {
+ if (bestData->mNode->parentIndex() == focusParentIndex)
+ return REJECT_TEST;
+ if (testData.mNode->parentIndex() == focusParentIndex)
+ 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 CachedNode* focus) const
+{
+ if (testData.mNodeBounds.contains(mRoot->focusBounds())) {
+ testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+ return REJECT_TEST;
+ }
+ testData.setDistances();
+ if (bestData->mNode != NULL) {
+ int compared = compare(testData, *bestData, focus);
+ 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 CachedNode* focus) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameRight, test, bestData, focus))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, RIGHT) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.mNodeBounds = test->focusRings().at(part);
+ if (testData.setRightDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData, focus);
+ 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, focus);
+ if (checkVisited(innerData.mNode, RIGHT)) {
+ *bestData = innerData;
+ continue;
+ }
+ }
+ if (checkVisited(test, RIGHT))
+ *bestData = testData;
+ }
+ } while ((test = test->traverseNextNode()) != limit);
+ ASSERT(focus == NULL || bestData->mNode != focus);
+ return bestData->mNode;
+}
+
+const CachedNode* CachedFrame::frameUp(const CachedNode* test, const CachedNode* limit, BestData* bestData,
+ const CachedNode* focus) const
+{
+ BestData originalData = *bestData;
+ do {
+ if (moveInFrame(&CachedFrame::frameUp, test, bestData, focus))
+ continue;
+ BestData testData;
+ if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST)
+ continue;
+ if (checkVisited(test, UP) == false)
+ continue;
+ size_t parts = test->navableRects();
+ for (size_t part = 0; part < parts; part++) {
+ testData.mNodeBounds = test->focusRings().at(part);
+ if (testData.setUpDirection(history()))
+ continue;
+ int result = framePartCommon(testData, test, bestData, focus);
+ 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, focus);
+ 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(focus == NULL || bestData->mNode != focus);
+ return bestData->mNode;
+}
+
+const CachedFrame* CachedFrame::hasFrame(const CachedNode* node) const
+{
+ return node->isFrame() ? &mCachedFrames[node->childFrameIndex()] : NULL;
+}
+
+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;
+ mFocus = -1;
+ mFrame = frame;
+ mParent = NULL; // set up parents after stretchy arrays are set up
+ mIndex = childFrameIndex;
+}
+
+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();
+}
+
+bool CachedFrame::moveInFrame(MoveInDirection moveInDirection,
+ const CachedNode* test, BestData* bestData,
+ const CachedNode* focus) 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, focus);
+ return true;
+}
+
+const WebCore::IntRect& CachedFrame::_navBounds() const
+{
+ return history()->navBounds();
+}
+
+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();
+ }
+}
+
+bool CachedFrame::sameFrame(const CachedFrame* test) const
+{
+ ASSERT(test);
+ if (mIndex != test->mIndex)
+ return false;
+ if (mIndex == -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::setFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y)
+{
+ if (NULL == node) {
+ const_cast<CachedRoot*>(mRoot)->setCachedFocus(NULL, NULL);
+ return true;
+ }
+ if (mFrame != frame) {
+ for (CachedFrame* testF = mCachedFrames.begin(); testF != mCachedFrames.end();
+ testF++) {
+ if (testF->setFocus(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();
+ WTF::Vector<WebCore::IntRect>& focusRings = test->focusRings();
+ for (size_t part = 0; part < partMax; part++) {
+ const WebCore::IntRect& testBounds = focusRings.at(part);
+ if (testBounds.contains(x, y) == false)
+ continue;
+ if (test->isFocus()) {
+ DBG_NAV_LOGD("already set? test=%d frame=%p node=%p x=%d y=%d",
+ test->index(), frame, node, x, y);
+ return true;
+ }
+ const_cast<CachedRoot*>(mRoot)->setCachedFocus(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->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+ 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->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+ 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->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+ 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->setCondition(CachedNode::NOT_ENCLOSING_FOCUS);
+ 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 focus
+ // prefer leftmost center
+ // if left and right > 0, test node subsumes focus
+ mNavDelta = left;
+ mNavDelta2 = right;
+}
+
+void CachedFrame::BestData::setNavOverlap(int span, int left, int right)
+{
+ mNavOutside = left < MIN_OVERLAP || right < MIN_OVERLAP; // if left or right < 0, test node is not in umbra of focus
+ 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)
+{
+ mWorkingOutside = left < MIN_OVERLAP || right < MIN_OVERLAP; // if left or right < 0, test node is not in umbra of focus
+ 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();
+ DUMP_NAV_LOGD("// }; // end of nodes\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 mIndex=%d;\n", b->mIndex);
+ DUMP_NAV_LOGD("// const CachedRoot* mRoot=%p;\n", b->mRoot);
+ DUMP_NAV_LOGD("// int mFocus=%d;\n", b->mFocus);
+}
+
+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/WebKit/android/nav/CachedFrame.h b/WebKit/android/nav/CachedFrame.h
new file mode 100644
index 0000000..8e77470
--- /dev/null
+++ b/WebKit/android/nav/CachedFrame.h
@@ -0,0 +1,228 @@
+/*
+ * 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 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 CachedFrame_H
+#define CachedFrame_H
+
+#include "CachedNode.h"
+#include "IntRect.h"
+#include "SkFixed.h"
+#include "wtf/Vector.h"
+
+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
+ };
+ CachedFrame() {}
+ void add(CachedNode& node) { mCachedNodes.append(node); }
+ void addFrame(CachedFrame& child) { mCachedFrames.append(child); }
+ bool checkVisited(const CachedNode* , CachedFrame::Direction ) const;
+ size_t childCount() { return mCachedFrames.size(); }
+ void clearFocus();
+ 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,
+ const CachedNode** , const CachedFrame** , int* x, int* y) const;
+ const CachedFrame* findBestFrameAt(int x, int y) const;
+ const CachedNode* findBestHitAt(const WebCore::IntRect& ,
+ int* best, const CachedFrame** , int* x, int* y) const;
+ bool finishInit();
+ CachedFrame* firstChild() { return mCachedFrames.begin(); }
+ const CachedFrame* firstChild() const { return mCachedFrames.begin(); }
+ int focusIndex() const { return mFocus; }
+ void* framePointer() const { return mFrame; }
+ CachedNode* getIndex(int index) { return index >= 0 ?
+ &mCachedNodes[index] : NULL; }
+ const CachedFrame* hasFrame(const CachedNode* node) const;
+ int indexInParent() const { return mIndex; }
+ void init(const CachedRoot* root, int index, WebCore::Frame* frame);
+ const CachedFrame* lastChild() const { return &mCachedFrames.last(); }
+ CachedNode* lastNode() { return &mCachedNodes.last(); }
+ CachedFrame* lastChild() { return &mCachedFrames.last(); }
+ const CachedFrame* parent() const { return mParent; }
+ CachedFrame* parent() { return mParent; }
+ bool sameFrame(const CachedFrame* ) const;
+ void removeLast() { mCachedNodes.removeLast(); }
+ void resetClippedOut();
+ void setContentsSize(int width, int height) { mContents.setWidth(width);
+ mContents.setHeight(height); }
+ void setData();
+ bool setFocus(WebCore::Frame* , WebCore::Node* , int x, int y);
+ void setFocusIndex(int focusIndex) const { mFocus = focusIndex; }
+ void setLocalViewBounds(const WebCore::IntRect& bounds) { mLocalViewBounds = bounds; }
+ int size() { return mCachedNodes.size(); }
+ const CachedNode* validDocument() const;
+protected:
+ struct BestData {
+ WebCore::IntRect mNodeBounds;
+ WebCore::IntRect mMouseBounds;
+ 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 mFocusChild;
+ 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* );
+ static SkFixed Overlap(int span, int left, int right);
+ void reset() { mNode = NULL; }
+ int right() const { return bounds().right(); }
+ 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(); }
+ };
+ typedef const CachedNode* (CachedFrame::*MoveInDirection)(
+ const CachedNode* test, const CachedNode* limit, BestData* bestData,
+ const CachedNode* focus) 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
+ CachedNode* focus) const;
+ void findClosest(BestData* , Direction original, Direction test,
+ WebCore::IntRect* clip) const;
+ int frameNodeCommon(BestData& testData, const CachedNode* test,
+ BestData* bestData, BestData* originalData,
+ const CachedNode* focus) const;
+ int framePartCommon(BestData& testData, const CachedNode* test,
+ BestData* bestData, const CachedNode* focus) const;
+ const CachedNode* frameDown(const CachedNode* test, const CachedNode* limit,
+ BestData* , const CachedNode* focus) const;
+ const CachedNode* frameLeft(const CachedNode* test, const CachedNode* limit,
+ BestData* , const CachedNode* focus) const;
+ const CachedNode* frameRight(const CachedNode* test, const CachedNode* limit,
+ BestData* , const CachedNode* focus) const;
+ const CachedNode* frameUp(const CachedNode* test, const CachedNode* limit,
+ BestData* , const CachedNode* focus) const;
+ int minWorkingHorizontal() const;
+ int minWorkingVertical() const;
+ int maxWorkingHorizontal() const;
+ int maxWorkingVertical() const;
+ bool moveInFrame(MoveInDirection , const CachedNode* test, BestData* best,
+ const CachedNode* focus) const;
+ const WebCore::IntRect& _navBounds() const;
+ WebCore::IntRect mContents;
+ WebCore::IntRect mLocalViewBounds;
+ WebCore::IntRect mViewBounds;
+ WTF::Vector<CachedNode> mCachedNodes;
+ WTF::Vector<CachedFrame> mCachedFrames;
+ void* mFrame; // WebCore::Frame*, used only to compare pointers
+ CachedFrame* mParent;
+ int mIndex; // index within parent's array of children, or -1 if root
+ const CachedRoot* mRoot;
+ mutable int mFocus;
+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/WebKit/android/nav/CachedHistory.cpp b/WebKit/android/nav/CachedHistory.cpp
new file mode 100644
index 0000000..f75f237
--- /dev/null
+++ b/WebKit/android/nav/CachedHistory.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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 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 "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;
+ mFocusIsInput = false;
+ mPriorIsInput = false;
+ mDidFirstLayout = false;
+ mPriorMove = mLastMove = CachedFrame::UNINITIALIZED;
+ mMinWorkingHorizontal = mMinWorkingVertical = INT_MIN;
+ mMaxWorkingHorizontal = mMaxWorkingVertical = INT_MAX;
+}
+
+void CachedHistory::setWorking(CachedFrame::Direction newMove,
+ const CachedNode* focus, 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 (focus != NULL || mLastMove != CachedFrame::UNINITIALIZED) {
+ mPriorMove = mLastMove;
+ mLastMove = newMove;
+ }
+ const WebCore::IntRect* navBounds = &mNavBounds;
+ if (focus != NULL) {
+ WebCore::IntRect focusBounds;
+ focus->getBounds(&focusBounds);
+ if (focusBounds.isEmpty() == false)
+ mNavBounds = focusBounds;
+ mPriorIsInput = mFocusIsInput;
+ mFocusIsInput = focus->isInput(); // focus->localName() == "input";
+ }
+ 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();
+ }
+ } else if (mPriorIsInput) {
+ if (newAxis == CachedFrame::UP_DOWN) {
+ if (mPriorBounds.x() == mMinWorkingVertical && mPriorBounds.right() == mMaxWorkingVertical) {
+ mMinWorkingVertical = navBounds->x();
+ mMaxWorkingVertical = navBounds->right();
+ }
+ } else {
+ if (mPriorBounds.y() == mMinWorkingHorizontal && mPriorBounds.bottom() == mMaxWorkingHorizontal) {
+ mMinWorkingHorizontal = navBounds->y();
+ mMaxWorkingHorizontal = navBounds->bottom();
+ }
+ }
+ }
+ 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(mNavBounds);
+ DEBUG_PRINT_RECT(mPriorBounds);
+ DEBUG_PRINT_BOOL(mDirectionChange);
+ DEBUG_PRINT_BOOL(mFocusIsInput);
+ DEBUG_PRINT_BOOL(mPriorIsInput);
+ 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/WebKit/android/nav/CachedHistory.h b/WebKit/android/nav/CachedHistory.h
new file mode 100644
index 0000000..e48d44b
--- /dev/null
+++ b/WebKit/android/nav/CachedHistory.h
@@ -0,0 +1,90 @@
+/*
+ * 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 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 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 setNavBounds(const WebCore::IntRect& loc) { mNavBounds = loc; }
+ void setWorking(CachedFrame::Direction , const CachedNode* focus,
+ 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 focus ring is partially visible
+ WebCore::IntRect mNavBounds; // focus ring bounds plus optional keystroke movement
+ WebCore::IntRect mPriorBounds; // prior chosen focus ring (for reversing narrowing)
+ bool mDirectionChange;
+ bool mFocusIsInput; // defer max/min to non-focus node if focus is too broad
+ bool mPriorIsInput; // defer max/min to non-focus node if focus is too broad
+ 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/WebKit/android/nav/CachedNode.cpp b/WebKit/android/nav/CachedNode.cpp
new file mode 100644
index 0000000..b786677
--- /dev/null
+++ b/WebKit/android/nav/CachedNode.cpp
@@ -0,0 +1,346 @@
+/*
+ * 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 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 "CachedPrefix.h"
+#include "CachedFrame.h"
+#include "CachedHistory.h"
+#include "Node.h"
+#include "PlatformString.h"
+
+#include "android_graphics.h"
+#include "CachedNode.h"
+
+namespace android {
+
+void CachedNode::clearFocus(CachedFrame* parent)
+{
+ if (isFrame()) {
+ CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this));
+ child->clearFocus();
+ }
+ mIsFocus = 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, &mFocusRing);
+}
+
+#define OVERLAP 3
+
+void CachedNode::fixUpFocusRects()
+{
+ if (mFixedUpFocusRects)
+ return;
+ mFixedUpFocusRects = true;
+ if (mNavableRects <= 1)
+ return;
+#if DEBUG_NAV_UI
+ {
+ WebCore::IntRect* boundsPtr = mFocusRing.begin() - 1;
+ const WebCore::IntRect* const boundsEnd = mFocusRing.begin() + mFocusRing.size();
+ while (++boundsPtr < boundsEnd)
+ LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, boundsPtr - mFocusRing.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 = mFocusRing.size();
+ WebCore::IntRect* unitBoundsPtr = mFocusRing.begin() - 1;
+ const WebCore::IntRect* const unitBoundsEnd = mFocusRing.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 = mFocusRing.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 = mFocusRing.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__, mFocusRing.size(),
+ candidate.x(), candidate.y(), candidate.width(), candidate.height());
+#endif
+ mFocusRing.append(candidate);
+ again = true;
+ goto tryAgain;
+ nextCheck:
+ continue;
+ }
+ }
+tryAgain:
+ ;
+ } while (again);
+}
+
+
+void CachedNode::focusRingBounds(WebCore::IntRect* bounds) const
+{
+ int partMax = mNavableRects;
+ ASSERT(partMax > 0);
+ *bounds = mFocusRing[0];
+ for (int partIndex = 1; partIndex < partMax; partIndex++)
+ bounds->unite(mFocusRing[partIndex]);
+ bounds->inflate(FOCUS_RING_HIT_TEST_RADIUS);
+}
+
+void CachedNode::init(WebCore::Node* node)
+{
+ bzero(this, sizeof(CachedNode));
+ mExport = WebCore::String();
+ mName = WebCore::String();
+ mNode = node;
+ mParentIndex = mChildFrameIndex = -1;
+ mType = android::NORMAL_CACHEDNODETYPE;
+}
+
+void CachedNode::move(int x, int y)
+{
+ mBounds.move(x, y);
+ // mHitTestBounds will be moved by caller
+ WebCore::IntRect* first = mFocusRing.begin();
+ WebCore::IntRect* last = first + mFocusRing.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 = mFocusRing[outerIndex];
+ int innerIndex = 0;
+ do {
+ const WebCore::IntRect& innerBounds = other->mFocusRing[innerIndex];
+ if (innerBounds.contains(outerBounds))
+ return true;
+ } while (++innerIndex < innerMax);
+ } while (++outerIndex < outerMax);
+ return false;
+}
+
+#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_FOCUS: return "CLOSER_IN_FOCUS"; break;
+ case CLOSER_OVERLAP: return "CLOSER_OVERLAP"; break;
+ case CLOSER_TOP: return "CLOSER_TOP"; break;
+ case FOCUSABLE: return "FOCUSABLE"; 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_FOCUS: return "IN_FOCUS"; break;
+ case IN_FOCUS_CHILDREN: return "IN_FOCUS_CHILDREN"; break;
+ case NOT_ENCLOSING_FOCUS: return "NOT_ENCLOSING_FOCUS"; break;
+ // case NOT_FOCUS_CHILD: return "NOT_FOCUS_CHILD"; break;
+ case NOT_FOCUS_NODE: return "NOT_FOCUS_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;
+ 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))
+ scratch[index++] = *ch++;
+ DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
+ index = snprintf(scratch, sizeof(scratch), "// char* mName=\"");
+ ch = b->mName.characters();
+ while (ch && *ch && index < sizeof(scratch))
+ scratch[index++] = *ch++;
+ DUMP_NAV_LOGD("%.*s\"\n", index, scratch);
+ DEBUG_PRINT_RECT(mBounds);
+ DEBUG_PRINT_RECT(mHitBounds);
+ const WTF::Vector<WebCore::IntRect>& rects = b->focusRings();
+ size_t size = rects.size();
+ DUMP_NAV_LOGD("// IntRect focusRings={ // size=%d\n", size);
+ for (size_t i = 0; i < size; i++)
+ DUMP_NAV_LOGD(" // {%d, %d, %d, %d}, // %d\n", rects[i].x(), rects[i].y(),
+ rects[i].width(), rects[i].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 mChildFrameIndex=%d;\n", b->mChildFrameIndex);
+ DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex);
+ DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength);
+ DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects);
+ DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex);
+ DUMP_NAV_LOGD("// int mTextSize=%d;\n", b->mTextSize);
+ DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex);
+ 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(mFixedUpFocusRects);
+ DEBUG_PRINT_BOOL(mHasFocusRing);
+ DEBUG_PRINT_BOOL(mHasMouseOver);
+ DEBUG_PRINT_BOOL(mIsAnchor);
+ DEBUG_PRINT_BOOL(mIsArea);
+ DEBUG_PRINT_BOOL(mIsFocus);
+ DEBUG_PRINT_BOOL(mIsInput);
+ DEBUG_PRINT_BOOL(mIsParentAnchor);
+ DEBUG_PRINT_BOOL(mIsPassword);
+ DEBUG_PRINT_BOOL(mIsRtlText);
+ DEBUG_PRINT_BOOL(mIsTextArea);
+ DEBUG_PRINT_BOOL(mIsTextField);
+ DEBUG_PRINT_BOOL(mIsTransparent);
+ DEBUG_PRINT_BOOL(mIsUnclipped);
+ DEBUG_PRINT_BOOL(mLast);
+ DEBUG_PRINT_BOOL(mWantsKeyEvents);
+ DUMP_NAV_LOGD("\n");
+}
+
+#endif
+
+}
diff --git a/WebKit/android/nav/CachedNode.h b/WebKit/android/nav/CachedNode.h
new file mode 100644
index 0000000..aa64982
--- /dev/null
+++ b/WebKit/android/nav/CachedNode.h
@@ -0,0 +1,235 @@
+/*
+ * 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 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 CachedNode_H
+#define CachedNode_H
+
+#include "AtomicString.h"
+#include "CachedDebug.h"
+#include "CachedNodeType.h"
+#include "IntRect.h"
+#include "PlatformString.h"
+#include "wtf/Vector.h"
+
+namespace WebCore {
+ class Node;
+}
+
+namespace android {
+
+class CachedFrame;
+
+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_FOCUS,
+ CLOSER_OVERLAP,
+ CLOSER_TOP,
+ FOCUSABLE,
+ FURTHER,
+ IN_UMBRA,
+ IN_WORKING,
+ LEFTMOST,
+ 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_FOCUS,
+ IN_FOCUS_CHILDREN,
+ NOT_ENCLOSING_FOCUS,
+ // NOT_FOCUS_CHILD,
+ NOT_FOCUS_NODE,
+ OUTSIDE_OF_BEST, // containership
+ OUTSIDE_OF_ORIGINAL, // containership
+ 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
+ }
+
+ const WebCore::IntRect& bounds() const { return mBounds; }
+ WebCore::IntRect* boundsPtr() { return &mBounds; }
+ int childFrameIndex() const { return mChildFrameIndex; }
+ void clearCondition() const { mCondition = NOT_REJECTED; }
+ void clearFocus(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; }
+ bool disabled() const { return mDisabled; }
+ const CachedNode* document() const { return &this[-mIndex]; }
+ void fixUpFocusRects();
+ void focusRingBounds(WebCore::IntRect* ) const;
+ WTF::Vector<WebCore::IntRect>& focusRings() { return mFocusRing; }
+ const WTF::Vector<WebCore::IntRect>& focusRings() const { return mFocusRing; }
+ const WebCore::IntRect& getBounds() const { return mBounds; }
+ void getBounds(WebCore::IntRect* bounds) const { *bounds = mBounds; }
+ const WebCore::String& getExport() const { return mExport; }
+ bool hasFocusRing() const { return mHasFocusRing; }
+ bool hasMouseOver() const { return mHasMouseOver; }
+ const WebCore::IntRect& hitBounds() const { return mHitBounds; }
+ int index() const { return mIndex; }
+ void init(WebCore::Node* node);
+ bool isAnchor() const { return mIsAnchor; }
+ bool isArea() const { return mIsArea; }
+ bool isFocus() const { return mIsFocus; }
+ bool isFocusable(const WebCore::IntRect& clip) const {
+ return clip.intersects(mBounds);
+ }
+ bool isFrame() const { return mChildFrameIndex >= 0 ; }
+ bool isInput() const { return mIsInput; }
+ bool isPassword() const { return mIsPassword; }
+ bool isRtlText() const { return mIsRtlText; }
+ bool isTextArea() const { return mIsTextArea; }
+ bool isTextField() const { return mIsTextField; }
+ bool isTransparent() const { return mIsTransparent; }
+ bool isUnclipped() const { return mIsUnclipped; }
+ bool isWantsKeyEvents() const { return mWantsKeyEvents; }
+
+ int maxLength() const { return mMaxLength; };
+ void move(int x, int y);
+ const WebCore::String& name() const { return mName; }
+ int navableRects() const { return mNavableRects; }
+ void* nodePointer() const { return mNode; }
+ bool noSecondChance() const { return mCondition > SECOND_CHANCE_END; }
+ const CachedNode* parent() const { return document() + mParentIndex; }
+ void* parentGroup() const { return mParentGroup; }
+ int parentIndex() const { return mParentIndex; }
+ bool partRectsContains(const CachedNode* other) const;
+ void reset();
+ void setBounds(const WebCore::IntRect& bounds) { mBounds = bounds; }
+ void setChildFrameIndex(int index) { mChildFrameIndex = index; }
+ void setClippedOut(bool clipped) { mClippedOut = clipped; }
+ void setCondition(Condition condition) const { mCondition = condition; }
+ void setDisabled(bool disabled) { mDisabled = disabled; }
+ void setExport(const WebCore::String& exported) { mExport = exported; }
+ void setHasFocusRing(bool hasFocusRing) { mHasFocusRing = hasFocusRing; }
+ void setHasMouseOver(bool hasMouseOver) { mHasMouseOver = hasMouseOver; }
+ void setHitBounds(const WebCore::IntRect& bounds) { mHitBounds = bounds; }
+ void setIndex(int index) { mIndex = index; }
+ void setIsAnchor(bool isAnchor) { mIsAnchor = isAnchor; }
+ void setIsArea(bool isArea) { mIsArea = isArea; }
+ void setIsFocus(bool isFocus) { mIsFocus = isFocus; }
+ void setIsInput(bool isInput) { mIsInput = isInput; }
+ void setIsParentAnchor(bool isAnchor) { mIsParentAnchor = isAnchor; }
+ void setIsPassword(bool isPassword) { mIsPassword = isPassword; }
+ void setIsRtlText(bool isRtlText) { mIsRtlText = isRtlText; }
+ void setIsTextArea(bool isTextArea) { mIsTextArea = isTextArea; }
+ void setIsTextField(bool isTextField) { mIsTextField = isTextField; }
+ void setIsTransparent(bool isTransparent) { mIsTransparent = isTransparent; }
+ void setIsUnclipped(bool unclipped) { mIsUnclipped = unclipped; }
+ void setLast() { mLast = true; }
+ void setMaxLength(int maxLength) { mMaxLength = maxLength; }
+ void setName(const WebCore::String& name) { mName = name; }
+ void setNavableRects() { mNavableRects = mFocusRing.size(); }
+ void setParentGroup(void* group) { mParentGroup = group; }
+ void setParentIndex(int parent) { mParentIndex = parent; }
+ void setTabIndex(int index) { mTabIndex = index; }
+ void setTextSize(int textSize) { mTextSize = textSize; }
+ void setType(CachedNodeType type) { mType = type; }
+ void setWantsKeyEvents(bool wantsKeys) { mWantsKeyEvents = wantsKeys; }
+ int tabIndex() const { return mTabIndex; }
+ const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; }
+ int textSize() const { return mTextSize; }
+ CachedNodeType type() const { return mType; }
+private:
+ WebCore::String mExport;
+ WebCore::String mName;
+ WebCore::IntRect mBounds;
+ WebCore::IntRect mHitBounds;
+ WTF::Vector<WebCore::IntRect> mFocusRing;
+ void* mNode; // WebCore::Node*, only used to match pointers
+ void* mParentGroup; // WebCore::Node*, only used to match pointers
+ int mChildFrameIndex; // set to -1 if node is not a frame
+ int mIndex; // index of itself, to find first in array (document)
+ int mMaxLength;
+ int mNavableRects; // FIXME: could be bitfield once I limit max number of rects
+ int mParentIndex;
+ int mTextSize;
+ int mTabIndex;
+ mutable Condition mCondition : 5; // why the node was not chosen on the first pass
+ CachedNodeType mType : 3;
+ bool mClippedOut : 1;
+ bool mDisabled : 1;
+ bool mFixedUpFocusRects : 1;
+ bool mHasFocusRing : 1;
+ bool mHasMouseOver : 1;
+ bool mIsAnchor : 1;
+ bool mIsArea : 1;
+ bool mIsFocus : 1;
+ bool mIsInput : 1;
+ bool mIsParentAnchor : 1;
+ bool mIsPassword : 1;
+ bool mIsRtlText : 1;
+ bool mIsTextArea : 1;
+ bool mIsTextField : 1;
+ bool mIsTransparent : 1;
+ bool mIsUnclipped : 1;
+ bool mLast : 1; // true if this is the last node in a group
+ bool mWantsKeyEvents : 1; // true for nodes like plugins
+#ifdef BROWSER_DEBUG
+public:
+ WebCore::Node* webCoreNode() const { return (WebCore::Node*) mNode; }
+ bool mDisplayMeasure;
+ mutable bool mInCompare;
+ // mutable int mCondition;
+ 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/WebKit/android/nav/CachedNodeType.h b/WebKit/android/nav/CachedNodeType.h
new file mode 100644
index 0000000..07346ac
--- /dev/null
+++ b/WebKit/android/nav/CachedNodeType.h
@@ -0,0 +1,41 @@
+/*
+ * 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 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 CachedNodeType_H
+#define CachedNodeType_H
+
+namespace android {
+
+enum CachedNodeType {
+ NORMAL_CACHEDNODETYPE = 0,
+ ADDRESS_CACHEDNODETYPE = 1,
+ EMAIL_CACHEDNODETYPE = 2,
+ PHONE_CACHEDNODETYPE = 4,
+ ALL_CACHEDNODETYPES = 7
+};
+
+}
+
+#endif
diff --git a/WebKit/android/nav/CachedPrefix.h b/WebKit/android/nav/CachedPrefix.h
new file mode 100644
index 0000000..b466771
--- /dev/null
+++ b/WebKit/android/nav/CachedPrefix.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 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 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
+
+#endif
diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp
new file mode 100644
index 0000000..4a50c80
--- /dev/null
+++ b/WebKit/android/nav/CachedRoot.cpp
@@ -0,0 +1,1087 @@
+/*
+ * 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 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 "CachedPrefix.h"
+#include "CachedHistory.h"
+#include "CachedNode.h"
+#include "SkBitmap.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkPixelRef.h"
+#include "SkRegion.h"
+
+#include "CachedRoot.h"
+
+#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
+ };
+
+ 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 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 && !defined BROWSER_DEBUG
+ 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"
+ };
+#endif
+
+#define kMargin 16
+#define kSlop 2
+
+class BoundsCheck : public CommonCheck {
+public:
+ BoundsCheck() {
+ mAllDrawnIn.setEmpty();
+ mLastAll.setEmpty();
+ mLastOver.setEmpty();
+ }
+
+ static int Area(SkIRect test) {
+ return test.width() * test.height();
+ }
+
+ void checkLast() {
+ if (mAllDrawnIn.isEmpty())
+ return;
+ if (mLastAll.isEmpty() || Area(mLastAll) < Area(mAllDrawnIn)) {
+ mLastAll = mAllDrawnIn;
+ mDrawnOver.setEmpty();
+ }
+ mAllDrawnIn.setEmpty();
+ }
+
+ bool hidden() {
+ return (mLastAll.isEmpty() && mLastOver.isEmpty()) ||
+ mDrawnOver.contains(mBounds);
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ if (joinGlyphs(rect))
+ return false;
+ bool interestingType = mType == kDrawBitmap_Type ||
+ mType == kDrawRect_Type || isTextType(mType);
+ if (SkIRect::Intersects(mBounds, rect) == false) {
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ LOGD("%s (no intersect) rect={%d,%d,%d,%d} mType=%s\n", __FUNCTION__,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ TypeNames[mType]);
+#endif
+ if (interestingType)
+ checkLast();
+ return false;
+ }
+ if (interestingType == false)
+ return false;
+ if (mBoundsSlop.contains(rect) ||
+ (mBounds.fLeft == rect.fLeft && mBounds.fRight == rect.fRight &&
+ mBounds.fTop >= rect.fTop && mBounds.fBottom <= rect.fBottom) ||
+ (mBounds.fTop == rect.fTop && mBounds.fBottom == rect.fBottom &&
+ mBounds.fLeft >= rect.fLeft && mBounds.fRight <= rect.fRight)) {
+ mDrawnOver.setEmpty();
+ mAllDrawnIn.join(rect);
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ LOGD("%s (contains) rect={%d,%d,%d,%d}"
+ " mAllDrawnIn={%d,%d,%d,%d} mType=%s\n", __FUNCTION__,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ mAllDrawnIn.fLeft, mAllDrawnIn.fTop, mAllDrawnIn.fRight, mAllDrawnIn.fBottom,
+ TypeNames[mType]);
+#endif
+ } else {
+ checkLast();
+ if (!isTextType(mType)) {
+ if (
+#if 0
+// should the opaqueness of the bitmap disallow its ability to draw over?
+// not sure that this test is needed
+ (mType != kDrawBitmap_Type ||
+ (mIsOpaque && mAllOpaque)) &&
+#endif
+ mLastAll.isEmpty() == false)
+ mDrawnOver.op(rect, SkRegion::kUnion_Op);
+ } else {
+// FIXME
+// sometimes the text is not drawn entirely inside the focus area, even though
+// it is the correct text. Until I figure out why, I allow text drawn at the
+// end that is not covered up by something else to represent the focusable link
+// example that triggers this that should be figured out:
+// http://cdn.labpixies.com/campaigns/blackjack/blackjack.html?lang=en&country=US&libs=assets/feature/core
+// ( http://tinyurl.com/ywsyzb )
+ mLastOver = rect;
+ }
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ const SkIRect& drawnOver = mDrawnOver.getBounds();
+ LOGD("%s (overlaps) rect={%d,%d,%d,%d}"
+ " mDrawnOver={%d,%d,%d,%d} mType=%s mIsOpaque=%s mAllOpaque=%s\n", __FUNCTION__,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ drawnOver.fLeft, drawnOver.fTop, drawnOver.fRight, drawnOver.fBottom,
+ TypeNames[mType], mIsOpaque ? "true" : "false", mAllOpaque ? "true" : "false");
+#endif
+ }
+ return false;
+ }
+
+ SkIRect mBounds;
+ SkIRect mBoundsSlop;
+ SkRegion mDrawnOver;
+ SkIRect mLastOver;
+ SkIRect mAllDrawnIn;
+ SkIRect mLastAll;
+};
+
+class BoundsCanvas : public SkCanvas {
+public:
+
+ BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) {
+ mTransparentLayer = 0;
+ setBounder(bounder);
+ }
+
+ virtual ~BoundsCanvas() {
+ setBounder(NULL);
+ }
+
+ virtual void drawPaint(const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPaint_Type);
+ SkCanvas::drawPaint(paint);
+ }
+
+ virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPoints_Type);
+ SkCanvas::drawPoints(mode, count, pts, paint);
+ }
+
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawRect_Type);
+ SkCanvas::drawRect(rect, paint);
+ }
+
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawPath_Type);
+ SkCanvas::drawPath(path, paint);
+ }
+
+ virtual void commonDrawBitmap(const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ mBounder.setType(CommonCheck::kDrawBitmap_Type);
+ mBounder.setIsOpaque(bitmap.isOpaque());
+ SkCanvas::commonDrawBitmap(bitmap, matrix, paint);
+ }
+
+ virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint = NULL) {
+ mBounder.setType(CommonCheck::kDrawSprite_Type);
+ mBounder.setIsOpaque(bitmap.isOpaque());
+ SkCanvas::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);
+ SkCanvas::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);
+ SkCanvas::drawPosText(text, byteLength, pos, paint);
+ 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);
+ SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
+ if (mBounder.mUnion.isEmpty())
+ 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);
+ SkCanvas::drawTextOnPath(text, byteLength, path, matrix, paint);
+ mBounder.doRect(CommonCheck::kDrawTextOnPath_Type);
+ }
+
+ virtual void drawPicture(SkPicture& picture) {
+ mBounder.setType(CommonCheck::kDrawPicture_Type);
+ SkCanvas::drawPicture(picture);
+ }
+
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ int depth = SkCanvas::saveLayer(bounds, paint, flags);
+ if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) {
+ mTransparentLayer = depth;
+ mBounder.setAllOpaque(false);
+ }
+ return depth;
+ }
+
+ virtual void restore() {
+ int depth = getSaveCount();
+ if (depth == mTransparentLayer) {
+ mTransparentLayer = 0;
+ mBounder.setAllOpaque(true);
+ }
+ SkCanvas::restore();
+ }
+
+ int mTransparentLayer;
+ CommonCheck& mBounder;
+};
+
+/*
+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 SkCanvas {
+public:
+ ImageCanvas(SkBounder* bounder) : mURI(NULL) {
+ setBounder(bounder);
+ }
+
+// 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 SkMatrix& , const SkPaint& ) {
+ SkPixelRef* pixelRef = bitmap.pixelRef();
+ if (pixelRef != NULL) {
+ mURI = pixelRef->getURI();
+ }
+ }
+
+ 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;
+};
+
+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;
+ }
+ newNode->focusRingBounds(&newOutset);
+ }
+ 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;
+}
+
+
+int CachedRoot::checkForCenter(int x, int y) const
+{
+ int width = mViewBounds.width();
+ 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(*mPicture);
+ 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);
+ checker.translate(SkIntToScalar(-mViewBounds.x() -
+ (xDelta < 0 ? xDelta : 0)), SkIntToScalar(-mViewBounds.y()));
+ checker.drawPicture(*mPicture);
+ *xDeltaPtr = jiggleCheck.jiggle();
+}
+
+const CachedNode* CachedRoot::findAt(const WebCore::IntRect& rect,
+ const CachedFrame** framePtr, int* x, int* y) const
+{
+ int best = INT_MAX;
+ (const_cast<CachedRoot*>(this))->resetClippedOut();
+ const CachedNode* directHit = NULL;
+ const CachedNode* node = findBestAt(rect, &best, &directHit, framePtr, x, y);
+ DBG_NAV_LOGD("node=%d (%p)", node == NULL ? 0 : node->index(),
+ node == NULL ? NULL : node->nodePointer());
+ if (node == NULL) {
+ node = findBestHitAt(rect, &best, 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::focusLocation() const
+{
+ const WebCore::IntRect& bounds = mHistory->mNavBounds;
+ return WebCore::IntPoint(bounds.x() + (bounds.width() >> 1),
+ bounds.y() + (bounds.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;
+}
+
+void CachedRoot::getSimulatedMousePosition(WebCore::IntPoint* point)
+{
+#ifndef NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+ const WebCore::IntRect& mouseBounds = mHistory->mMouseBounds;
+ point->setX(mouseBounds.x() + (mouseBounds.width() >> 1));
+ point->setY(mouseBounds.y() + (mouseBounds.height() >> 1));
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ const WebCore::IntRect& navBounds = mHistory->mNavBounds;
+ LOGD("%s mHistory->mNavBounds={%d,%d,%d,%d} "
+ "mHistory->mMouseBounds={%d,%d,%d,%d} point={%d,%d}\n", __FUNCTION__,
+ 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();
+ if (mFocusBounds.isEmpty() == false &&
+ mFocusBounds.bottom() > viewBottom && viewBottom < mContents.height())
+ return false;
+ if (mHistory->mNavBounds.isEmpty() == false) {
+ int navTop = mHistory->mNavBounds.y();
+ int scrollBottom;
+ if (testTop < navTop && navTop < (scrollBottom = mScrolledBounds.bottom())) {
+ mScrolledBounds.setHeight(scrollBottom - navTop);
+ mScrolledBounds.setY(navTop);
+ }
+ }
+ frameDown(test, NULL, bestData, currentFocus());
+ 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();
+ if (mFocusBounds.isEmpty() == false &&
+ mFocusBounds.x() < viewLeft && viewLeft > mContents.x())
+ return false;
+ if (mHistory->mNavBounds.isEmpty() == false) {
+ int navRight = mHistory->mNavBounds.right();
+ int scrollLeft;
+ if (testRight > navRight && navRight > (scrollLeft = mScrolledBounds.x()))
+ mScrolledBounds.setWidth(navRight - scrollLeft);
+ }
+ frameLeft(test, NULL, bestData, currentFocus());
+ return true;
+}
+
+
+void CachedRoot::innerMove(const CachedNode* node, BestData* bestData,
+ Direction direction, WebCore::IntPoint* scroll, bool firstCall)
+{
+ bestData->reset();
+ mFocusChild = false;
+ bool outOfFocus = mFocus < 0;
+ bool firstTime = mHistory->didFirstLayout() && outOfFocus;
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ LOGD("%s mHistory->didFirstLayout()=%s && mFocus=%d\n", __FUNCTION__,
+ mHistory->didFirstLayout() ? "true" : "false", mFocus);
+#endif
+ if (firstTime)
+ mHistory->reset();
+ const CachedNode* focus = currentFocus();
+ mHistory->setWorking(direction, focus, mViewBounds);
+ mFocusBounds = WebCore::IntRect(0, 0, 0, 0);
+ if (focus != NULL)
+ focus->getBounds(&mFocusBounds);
+ bool findClosest = false;
+ if (mScrollOnly == false) {
+ switch (direction) {
+ case LEFT:
+ if (outOfFocus)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.right(),
+ mViewBounds.y(), 1, mViewBounds.height());
+ findClosest = innerLeft(node, bestData);
+ break;
+ case RIGHT:
+ if (outOfFocus)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x() - 1,
+ mViewBounds.y(), 1, mViewBounds.height());
+ findClosest = innerRight(node, bestData);
+ break;
+ case UP:
+ if (outOfFocus)
+ mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(),
+ mViewBounds.bottom(), mViewBounds.width(), 1);
+ findClosest = innerUp(node, bestData);
+ break;
+ case DOWN:
+ if (outOfFocus)
+ 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->mMouseBounds = bestData->mNodeBounds;
+ if (adjustForScroll(bestData, direction, scroll, findClosest))
+ return;
+ if (bestData->mNode != NULL) {
+ mHistory->addToVisited(bestData->mNode, direction);
+ mHistory->mNavBounds = mFocusBounds = bestData->mNodeBounds;
+ mHistory->mMouseBounds = bestData->mMouseBounds;
+ } 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();
+ if (mFocusBounds.isEmpty() == false &&
+ mFocusBounds.right() > viewRight && viewRight < mContents.width())
+ return false;
+ if (mHistory->mNavBounds.isEmpty() == false) {
+ int navLeft = mHistory->mNavBounds.x();
+ int scrollRight;
+ if (testLeft < navLeft && navLeft < (scrollRight = mScrolledBounds.right())) {
+ mScrolledBounds.setWidth(scrollRight - navLeft);
+ mScrolledBounds.setX(navLeft);
+ }
+ }
+ frameRight(test, NULL, bestData, currentFocus());
+ 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();
+ if (mFocusBounds.isEmpty() == false &&
+ mFocusBounds.y() < viewTop && viewTop > mContents.y())
+ return false;
+ if (mHistory->mNavBounds.isEmpty() == false) {
+ int navBottom = mHistory->mNavBounds.bottom();
+ int scrollTop;
+ if (testBottom > navBottom && navBottom > (scrollTop = mScrolledBounds.y()))
+ mScrolledBounds.setHeight(navBottom - scrollTop);
+ }
+ frameUp(test, NULL, bestData, currentFocus());
+ return true;
+}
+
+WebCore::String CachedRoot::imageURI(int x, int y) const
+{
+ ImageCheck imageCheck;
+ ImageCanvas checker(&imageCheck);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ checker.setBitmapDevice(bitmap);
+ checker.translate(SkIntToScalar(-x), SkIntToScalar(-y));
+ checker.drawPicture(*mPicture);
+ return WebCore::String(checker.mURI);
+}
+
+bool CachedRoot::maskIfHidden(BestData* best) const
+{
+ if (mPicture == NULL) {
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ LOGD("%s missing picture\n", __FUNCTION__);
+#endif
+ return false;
+ }
+ const CachedNode* bestNode = best->mNode;
+ if (bestNode->isUnclipped())
+ return false;
+ // given the picture matching this nav cache
+ // create an SkBitmap with dimensions of the focus intersected w/ extended view
+ const WebCore::IntRect& nodeBounds = bestNode->getBounds();
+ WebCore::IntRect bounds = nodeBounds;
+ bounds.intersect(mScrolledBounds);
+ int leftMargin = bounds.x() == nodeBounds.x() ? kMargin : 0;
+ int topMargin = bounds.y() == nodeBounds.y() ? kMargin : 0;
+ int rightMargin = bounds.right() == nodeBounds.right() ? kMargin : 0;
+ int bottomMargin = bounds.bottom() == nodeBounds.bottom() ? kMargin : 0;
+ bool unclipped = (leftMargin & topMargin & rightMargin & bottomMargin) != 0;
+ WebCore::IntRect marginBounds = nodeBounds;
+ marginBounds.inflate(kMargin);
+ marginBounds.intersect(mScrolledBounds);
+ BoundsCheck boundsCheck;
+ BoundsCanvas checker(&boundsCheck);
+ boundsCheck.mBounds.set(leftMargin, topMargin,
+ leftMargin + bounds.width(), topMargin + bounds.height());
+ boundsCheck.mBoundsSlop = boundsCheck.mBounds;
+ boundsCheck.mBoundsSlop.inset(-kSlop, -kSlop);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, marginBounds.width(),
+ marginBounds.height());
+ checker.setBitmapDevice(bitmap);
+ // insert probes to be called when the data corresponding to this focus ring is drawn
+ // need to know if focus ring was generated by text, image, or parent (like div)
+ // ? need to know (like imdb menu bar) to give up sometimes (when?)
+ checker.translate(SkIntToScalar(leftMargin - bounds.x()),
+ SkIntToScalar(topMargin - bounds.y()));
+ checker.drawPicture(*mPicture);
+ boundsCheck.checkLast();
+ // was it not drawn or clipped out?
+ if (boundsCheck.hidden()) { // if hidden, return false so that nav can try again
+ CachedNode* node = const_cast<CachedNode*>(best->mNode);
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ const SkIRect& m = boundsCheck.mBounds;
+ const SkIRect& s = boundsCheck.mBoundsSlop;
+ LOGD("%s hidden node:%p (%d) mBounds={%d,%d,%d,%d} mBoundsSlop="
+ "{%d,%d,%d,%d}\n", __FUNCTION__, node, node->index(),
+ m.fLeft, m.fTop, m.fRight, m.fBottom,
+ s.fLeft, s.fTop, s.fRight, s.fBottom);
+ const SkIRect& o = boundsCheck.mDrawnOver.getBounds();
+ const SkIRect& l = boundsCheck.mLastAll;
+ const SkIRect& u = boundsCheck.mUnion;
+ LOGD("%s hidden mDrawnOver={%d,%d,%d,%d} mLastAll={%d,%d,%d,%d}"
+ " mUnion={%d,%d,%d,%d}\n", __FUNCTION__,
+ o.fLeft, o.fTop, o.fRight, o.fBottom,
+ l.fLeft, l.fTop, l.fRight, l.fBottom,
+ u.fLeft, u.fTop, u.fRight, u.fBottom);
+ const SkIRect& a = boundsCheck.mAllDrawnIn;
+ const WebCore::IntRect& c = mScrolledBounds;
+ const WebCore::IntRect& b = nodeBounds;
+ LOGD("%s hidden mAllDrawnIn={%d,%d,%d,%d} mScrolledBounds={%d,%d,%d,%d}"
+ " nodeBounds={%d,%d,%d,%d}\n", __FUNCTION__,
+ a.fLeft, a.fTop, a.fRight, a.fBottom,
+ c.x(), c.y(), c.right(), c.bottom(),
+ b.x(), b.y(), b.right(), b.bottom());
+ LOGD("%s bits.mWidth=%d bits.mHeight=%d transX=%d transY=%d\n", __FUNCTION__,
+ marginBounds.width(),marginBounds.height(),
+ kMargin - bounds.x(), kMargin - bounds.y());
+#endif
+ node->setDisabled(true);
+ node->setClippedOut(unclipped == false);
+ 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
+ const SkIRect& over = boundsCheck.mDrawnOver.getBounds();
+ if (over.isEmpty() == false) {
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ SkIRect orig = boundsCheck.mBounds;
+#endif
+ SkIRect& base = boundsCheck.mBounds;
+ if (base.fLeft < over.fRight && base.fRight > over.fRight)
+ base.fLeft = over.fRight;
+ else if (base.fRight > over.fLeft && base.fLeft < over.fLeft)
+ base.fRight = over.fLeft;
+ if (base.fTop < over.fBottom && base.fBottom > over.fBottom)
+ base.fTop = over.fBottom;
+ else if (base.fBottom > over.fTop && base.fTop < over.fTop)
+ base.fBottom = over.fTop;
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ const SkIRect& modded = boundsCheck.mBounds;
+ LOGD("%s partially occluded node:%p (%d) old:{%d,%d,%d,%d} new:{%d,%d,%d,%d}\n",
+ __FUNCTION__, best->mNode, best->mNode->index(),
+ orig.fLeft, orig.fTop, orig.fRight, orig.fBottom,
+ base.fLeft, base.fTop, base.fRight, base.fBottom);
+#endif
+ best->mMouseBounds = WebCore::IntRect(bounds.x() + base.fLeft - kMargin,
+ bounds.y() + base.fTop - kMargin, base.width(), base.height());
+ }
+ return false;
+}
+
+const CachedNode* CachedRoot::moveFocus(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);
+ *framePtr = bestData.mFrame;
+ return const_cast<CachedNode*>(bestData.mNode);
+}
+
+void CachedRoot::reset()
+{
+#ifndef NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+ mContents = mViewBounds = WebCore::IntRect(0, 0, 0, 0);
+ mMaxXScroll = mMaxYScroll = 0;
+ mSelectionStart = mSelectionEnd = -1;
+ mScrollOnly = false;
+ mFocusBounds = WebCore::IntRect(0, 0, 0, 0);
+}
+
+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)
+{
+#if !defined NDEBUG
+ ASSERT(CachedFrame::mDebug.mInUse);
+#endif
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ const CachedNode* focus = currentFocus();
+ WebCore::IntRect bounds;
+ if (focus)
+ bounds = focus->bounds();
+ LOGD("%s old focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}\n", __FUNCTION__,
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+#endif
+ clearFocus();
+ if (node == NULL)
+ return;
+ node->setIsFocus(true);
+ ASSERT(node->isFrame() == false);
+ frame->setFocusIndex(node - frame->document());
+ ASSERT(frame->focusIndex() > 0 && frame->focusIndex() < (int) frame->size());
+ CachedFrame* parent;
+ while ((parent = frame->parent()) != NULL) {
+ parent->setFocusIndex(frame->indexInParent());
+ frame = parent;
+ }
+#if DEBUG_NAV_UI && !defined BROWSER_DEBUG
+ focus = currentFocus();
+ bounds = WebCore::IntRect(0, 0, 0, 0);
+ if (focus)
+ bounds = focus->bounds();
+ LOGD("%s new focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}\n", __FUNCTION__,
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+#endif
+}
+
+void CachedRoot::setupScrolledBounds() const
+{
+ mScrolledBounds = mViewBounds;
+}
+
+#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);
+ DEBUG_PRINT_RECT(mFocusBounds);
+ DUMP_NAV_LOGD("// int mMaxXScroll=%d, mMaxYScroll=%d;\n",
+ b->mMaxXScroll, b->mMaxYScroll);
+ DEBUG_PRINT_BOOL(mFocusChild);
+#ifdef DUMP_NAV_CACHE_USING_PRINTF
+ if (gNavCacheLogFile)
+ fclose(gNavCacheLogFile);
+ gNavCacheLogFile = NULL;
+ gWriteLogMutex.unlock();
+#endif
+}
+
+#endif
+
+}
diff --git a/WebKit/android/nav/CachedRoot.h b/WebKit/android/nav/CachedRoot.h
new file mode 100644
index 0000000..ab1b823
--- /dev/null
+++ b/WebKit/android/nav/CachedRoot.h
@@ -0,0 +1,115 @@
+/*
+ * 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 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 CachedRoot_H
+#define CachedRoot_H
+
+#include "CachedFrame.h"
+#include "IntPoint.h"
+#include "SkPicture.h"
+
+class SkRect;
+
+namespace android {
+
+class CachedHistory;
+class CachedNode;
+
+class CachedRoot : public CachedFrame {
+public:
+ bool adjustForScroll(BestData* , Direction , WebCore::IntPoint* scrollPtr,
+ bool findClosest);
+ int checkForCenter(int x, int y) const;
+ void checkForJiggle(int* ) const;
+ int documentHeight() { return mContents.height(); }
+ int documentWidth() { return mContents.width(); }
+ const CachedNode* findAt(const WebCore::IntRect& , const CachedFrame** ,
+ int* x, int* y) const;
+ const WebCore::IntRect& focusBounds() const { return mFocusBounds; }
+ bool focusChild() const { return mFocusChild; }
+ WebCore::IntPoint focusLocation() const;
+ int generation() const { return mGeneration; }
+ SkPicture* getPicture() { return mPicture; }
+ int getAndResetSelectionEnd();
+ int getAndResetSelectionStart();
+// const WebCore::IntRect& navClipBounds() const { return mClippedBounds; }
+ void getSimulatedMousePosition(WebCore::IntPoint* );
+// bool hasNavClipBounds() { return mClippedBounds.isEmpty() == false; }
+ 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;
+ WebCore::String imageURI(int x, int y) const;
+ bool maskIfHidden(BestData* ) const;
+ const CachedNode* moveFocus(Direction , const CachedFrame** , WebCore::IntPoint* scroll);
+ void reset();
+// void resetNavClipBounds() { mClippedBounds = WebCore::IntRect(-1, -1, 0, 0); }
+ CachedHistory* rootHistory() const { return mHistory; }
+ bool scrollDelta(WebCore::IntRect& focusRingBounds, Direction , int* delta);
+ const WebCore::IntRect& scrolledBounds() const { return mScrolledBounds; }
+ void setCachedFocus(CachedFrame* , CachedNode* );
+ void setFocusBounds(const WebCore::IntRect& r) { mFocusBounds = r; }
+ void setGeneration(int generation) { mGeneration = generation; }
+ void setTextGeneration(int textGeneration) { mTextGeneration = textGeneration; }
+ void setFocusChild(bool state) const { mFocusChild = state; }
+ void setMaxScroll(int x, int y) { mMaxXScroll = x; mMaxYScroll = y; }
+// void setNavClipBounds(const WebCore::IntRect& r) { mClippedBounds = r; }
+ void setPicture(SkPicture* picture) { mPicture = picture; }
+ void setScrollOnly(bool state) { mScrollOnly = state; }
+ void setSelection(int start, int end) { mSelectionStart = start; mSelectionEnd = end; }
+ void setupScrolledBounds() const;
+ void setVisibleRect(const WebCore::IntRect& r) { mViewBounds = r; }
+ int textGeneration() const { return mTextGeneration; }
+ int width() const { return mPicture ? mPicture->width() : 0; }
+private:
+ CachedHistory* mHistory;
+ SkPicture* mPicture;
+ WebCore::IntRect mFocusBounds; // chosen focus ring
+ mutable WebCore::IntRect mScrolledBounds; // view bounds + amount visible as result of scroll
+ int mGeneration;
+ 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;
+ mutable bool mFocusChild; // temporary state set if walked nodes are children of focus
+ bool mScrollOnly;
+#if DUMP_NAV_CACHE
+public:
+ class Debug {
+public:
+ CachedRoot* base() const;
+ void print() const;
+ } mDebug;
+#endif
+};
+
+}
+
+#endif
diff --git a/WebKit/android/nav/FindCanvas.cpp b/WebKit/android/nav/FindCanvas.cpp
new file mode 100644
index 0000000..24b0129
--- /dev/null
+++ b/WebKit/android/nav/FindCanvas.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 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 "FindCanvas.h"
+
+#include "SkRect.h"
+
+// MatchInfo methods
+////////////////////////////////////////////////////////////////////////////////
+
+MatchInfo::MatchInfo() {
+ m_picture = 0;
+}
+
+MatchInfo::~MatchInfo() {
+ m_picture->safeUnref();
+}
+
+MatchInfo::MatchInfo(const MatchInfo& src) {
+ m_location = src.m_location;
+ m_picture = src.m_picture;
+ m_picture->safeRef();
+}
+
+void MatchInfo::set(const SkRegion& region, SkPicture* pic) {
+ m_picture->safeUnref();
+ 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& 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) {
+
+ setBounder(&mBounder);
+ mOutset = -SkIntToScalar(2);
+ mMatches = new WTF::Vector<MatchInfo>();
+ mWorkingIndex = 0;
+ mWorkingCanvas = 0;
+ mWorkingPicture = 0;
+}
+
+FindCanvas::~FindCanvas() {
+ setBounder(NULL);
+ /* Just in case getAndClear was not called. */
+ delete mMatches;
+ mWorkingPicture->safeUnref();
+}
+
+// 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::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);
+ rect.inset(mOutset, mOutset);
+ // We need an SkIRect for SkRegion operations.
+ SkIRect iRect;
+ rect.roundOut(&iRect);
+ // If the rectangle is partially clipped, assume that the text is
+ // not visible, so skip this match.
+ if (getTotalClip().contains(iRect)) {
+ 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
+ } else {
+ // This match was clipped out, so begin looking at the next
+ // character from our hidden match
+ index = matchIndex;
+ }
+ // 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);
+ // Only save a partial if it is in the current clip.
+ if (getTotalClip().contains(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);
+ mMatches->last().set(region, mWorkingPicture);
+}
+
+void FindCanvas::resetWorkingCanvas() {
+ mWorkingPicture->unref();
+ mWorkingPicture = 0;
+ // Do not need to reset mWorkingCanvas itself because we only access it via
+ // getWorkingCanvas.
+}
diff --git a/WebKit/android/nav/FindCanvas.h b/WebKit/android/nav/FindCanvas.h
new file mode 100644
index 0000000..5d79b4c
--- /dev/null
+++ b/WebKit/android/nav/FindCanvas.h
@@ -0,0 +1,207 @@
+/*
+ * 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 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 Find_Canvas_h
+#define Find_Canvas_h
+
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkRegion.h"
+#include "SkTDArray.h"
+#include "icu/unicode/umachine.h"
+#include "wtf/Vector.h"
+
+class SkRect;
+class SkTypeface;
+
+// Stores both region information and an SkPicture of the match, so that the
+// region can be drawn, followed by drawing the matching text on top of it.
+// This class owns its SkPicture
+class MatchInfo {
+public:
+ MatchInfo();
+ ~MatchInfo();
+ MatchInfo(const MatchInfo& src);
+ const SkRegion& getLocation() const { return m_location; }
+ // Return a pointer to our picture, representing the matching text. Does
+ // not transfer ownership of the picture.
+ SkPicture* getPicture() const { return m_picture; }
+ // This will make a copy of the region, and increase the ref count on the
+ // SkPicture. If this MatchInfo already had one, unref it.
+ void set(const SkRegion& region, SkPicture* pic);
+private:
+ MatchInfo& operator=(MatchInfo& src);
+ SkRegion m_location;
+ SkPicture* m_picture;
+};
+
+// 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) {
+ }
+
+ int found() const { return mNumFound; }
+
+ // 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;
+ size_t mLength;
+ FindBounder mBounder;
+ int mNumFound;
+ SkScalar mOutset;
+ SkTDArray<GlyphSet> mGlyphSets;
+
+ SkPicture* mWorkingPicture;
+ SkCanvas* mWorkingCanvas;
+ SkRegion mWorkingRegion;
+ int mWorkingIndex;
+};
+
+#endif // Find_Canvas_h
+
diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp
new file mode 100644
index 0000000..7bdbf14
--- /dev/null
+++ b/WebKit/android/nav/SelectText.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 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 "webcoreglue"
+
+#include "CachedPrefix.h"
+#include "SelectText.h"
+#include "SkBitmap.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkPicture.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+
+class CommonCheck : public SkBounder {
+public:
+ CommonCheck() : mMatrix(NULL), mPaint(NULL) {}
+
+ virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y) {
+ mMatrix = &matrix;
+ mPaint = &paint;
+ mY = y;
+ mBase = mBottom = mTop = INT_MAX;
+ }
+
+ int base() {
+ if (mBase == INT_MAX) {
+ SkPoint result;
+ mMatrix->mapXY(0, mY, &result);
+ mBase = SkScalarFloor(result.fY);
+ }
+ return mBase;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+protected:
+ const SkMatrix* mMatrix;
+ const SkPaint* mPaint;
+ int mBase;
+ int mBottom;
+ int mTop;
+ SkScalar mY;
+};
+
+class FirstCheck : public CommonCheck {
+public:
+ FirstCheck(int x, int y)
+ : mDistance(INT_MAX), mFocusX(x), mFocusY(y) {
+ mBestBounds.setEmpty();
+ }
+
+ const SkIRect& bestBounds() {
+ DBG_NAV_LOGD("mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d",
+ mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight,
+ mBestBounds.fBottom, mTop, mBottom);
+ return mBestBounds;
+ }
+
+ void offsetBounds(int dx, int dy) {
+ mBestBounds.offset(dx, dy);
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ int dx = ((rect.fLeft + rect.fRight) >> 1) - mFocusX;
+ int dy = ((top() + bottom()) >> 1) - mFocusY;
+ int distance = dx * dx + dy * dy;
+#ifdef EXTRA_NOISY_LOGGING
+ if (distance < 500 || abs(distance - mDistance) < 500)
+ DBG_NAV_LOGD("distance=%d mDistance=%d", distance, mDistance);
+#endif
+ if (mDistance > distance) {
+ mDistance = distance;
+ mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom());
+#ifdef EXTRA_NOISY_LOGGING
+ DBG_NAV_LOGD("mBestBounds={%d,%d,r=%d,b=%d}",
+ mBestBounds.fLeft, mBestBounds.fTop,
+ mBestBounds.fRight, mBestBounds.fBottom);
+#endif
+ }
+ return false;
+ }
+protected:
+ SkIRect mBestBounds;
+ int mDistance;
+ int mFocusX;
+ int mFocusY;
+};
+
+class MultilineBuilder : public CommonCheck {
+public:
+ MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy,
+ SkRegion* region)
+ : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) {
+ mLast.setEmpty();
+ mLastBase = INT_MAX;
+ mStart.offset(-dx, -dy);
+ mEnd.offset(-dx, -dy);
+ }
+
+ virtual bool onIRect(const SkIRect& rect) {
+ bool captureLast = false;
+ if ((rect.fLeft == mStart.fLeft && rect.fRight == mStart.fRight &&
+ top() == mStart.fTop && bottom() == mStart.fBottom) ||
+ (rect.fLeft == mEnd.fLeft && rect.fRight == mEnd.fRight &&
+ top() == mEnd.fTop && bottom() == mEnd.fBottom)) {
+ captureLast = mCapture;
+ mCapture ^= true;
+ }
+ if (mCapture || captureLast) {
+ SkIRect full;
+ full.set(rect.fLeft, top(), rect.fRight, bottom());
+ if ((mLast.fTop < base() && mLast.fBottom >= base())
+ || (mLastBase <= full.fBottom && mLastBase > full.fTop)) {
+ if (full.fLeft > mLast.fRight)
+ full.fLeft = mLast.fRight;
+ else if (full.fRight < mLast.fLeft)
+ full.fRight = mLast.fLeft;
+ }
+ mSelectRegion->op(full, SkRegion::kUnion_Op);
+ mLast = full;
+ mLastBase = base();
+ if (mStart == mEnd)
+ mCapture = false;
+ }
+ return false;
+ }
+protected:
+ SkIRect mStart;
+ SkIRect mEnd;
+ SkIRect mLast;
+ int mLastBase;
+ SkRegion* mSelectRegion;
+ bool mCapture;
+};
+
+class TextCanvas : public SkCanvas {
+public:
+
+ TextCanvas(CommonCheck* bounder, const SkPicture& picture, const SkIRect& area)
+ : mBounder(*bounder) {
+ setBounder(bounder);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(),
+ area.height());
+ setBitmapDevice(bitmap);
+ translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop));
+ }
+
+ virtual ~TextCanvas() {
+ setBounder(NULL);
+ }
+
+ 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 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);
+ SkCanvas::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);
+ SkCanvas::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;
+};
+
+void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area,
+ const SkIRect& selStart, const SkIRect& selEnd, 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, selEnd, area.fLeft, area.fTop, region);
+ TextCanvas checker(&builder, picture, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ region->translate(area.fLeft, area.fTop);
+}
+
+SkIRect CopyPaste::findClosest(const SkPicture& picture, const SkIRect& area,
+ int x, int y) {
+ FirstCheck _check(x - area.fLeft, y - area.fTop);
+ DBG_NAV_LOGD("area=(%d, %d, %d, %d) x=%d y=%d", area.fLeft, area.fTop,
+ area.fRight, area.fBottom, x, y);
+ TextCanvas checker(&_check, picture, area);
+ checker.drawPicture(const_cast<SkPicture&>(picture));
+ _check.offsetBounds(area.fLeft, area.fTop);
+ return _check.bestBounds();
+}
diff --git a/WebKit/android/nav/SelectText.h b/WebKit/android/nav/SelectText.h
new file mode 100644
index 0000000..b991198
--- /dev/null
+++ b/WebKit/android/nav/SelectText.h
@@ -0,0 +1,42 @@
+/*
+ * 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 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 SELECT_TEXT_H
+#define SELECT_TEXT_H
+
+class SkPicture;
+struct SkIRect;
+struct SkIPoint;
+class SkRegion;
+
+class CopyPaste {
+public:
+ static void buildSelection(const SkPicture& , const SkIRect& area,
+ const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region);
+ static SkIRect findClosest(const SkPicture& , const SkIRect& area,
+ int x, int y);
+};
+
+#endif
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
new file mode 100644
index 0000000..52bb6a5
--- /dev/null
+++ b/WebKit/android/nav/WebView.cpp
@@ -0,0 +1,2322 @@
+/*
+ * 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 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 "webviewglue"
+
+#include <config.h>
+
+#include "android_graphics.h"
+#include "AndroidLog.h"
+#include "AtomicString.h"
+#include "CachedFrame.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "FindCanvas.h"
+#include "Frame.h"
+#include "GraphicsJNI.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "SelectText.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkCornerPathEffect.h"
+#include "SkDumpCanvas.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkPixelXorXfermode.h"
+#include "SkRect.h"
+#include "SkTime.h"
+#include "WebCoreJni.h"
+#include "WebViewCore.h"
+#include "jni_utility.h"
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#ifdef GET_NATIVE_VIEW
+#undef GET_NATIVE_VIEW
+#endif
+
+#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField))
+
+#include <ui/KeycodeLabels.h>
+#include <JNIHelp.h>
+#include <jni.h>
+
+#define REPLAY_BUFFER_SIZE 4096
+
+namespace android {
+
+struct CommonParams {
+ enum Trigger {
+ NoData,
+ ClearFocusParams,
+ FirstMoveFocusParams,
+ MoveFocusParams,
+ MotionUpParams
+ } m_trigger;
+ int m_generation;
+};
+
+struct CacheParams {
+ void setFocus(const CachedNode* node,
+ const CachedFrame* frame, const CachedRoot* root,
+ const WebCore::IntPoint& focusLocation)
+ {
+ m_node = (WebCore::Node*) (node ? node->nodePointer() : 0);
+ m_frame = (WebCore::Frame*) (node ? frame->framePointer() : 0);
+ m_x = focusLocation.x();
+ m_y = focusLocation.y();
+ }
+
+ WebCore::Node* m_node;
+ WebCore::Frame* m_frame;
+ int m_x;
+ int m_y;
+};
+
+struct ClearFocusParams {
+ CommonParams d;
+ CacheParams c;
+ int m_x;
+ int m_y;
+};
+
+struct MotionUpParams {
+ CommonParams d;
+ int m_x;
+ int m_y;
+ int m_slop;
+ bool m_isClick;
+};
+
+struct FirstMoveFocusParams {
+ CommonParams d;
+ int m_keyCode;
+ int m_count;
+ bool m_ignoreScroll;
+};
+
+struct MoveFocusParams {
+ FirstMoveFocusParams d;
+ CacheParams c;
+ void* m_sentFocus;
+ WebCore::IntRect m_sentBounds;
+ WebCore::IntRect m_visibleRect;
+ CachedHistory m_history; // FIXME: make this a subset
+ int m_xMax;
+ int m_yMax;
+};
+
+typedef MoveFocusParams LargestParams;
+
+#if DEBUG_NAV_UI
+static const char* TriggerNames[] = {
+ "*** no data ! ***",
+ "clearFocus",
+ "firstMoveFocus",
+ "moveFocus",
+ "motionUp"
+};
+#endif
+
+class FocusReplay {
+public:
+FocusReplay() : m_start(m_buffer), m_end(m_buffer), m_lastGeneration(0)
+{
+}
+
+// find the most recent common data
+void add(const CommonParams& data, size_t len)
+{
+ DBG_NAV_LOGD("m_start=%d m_end=%d trigger=%s moveGeneration=%d", m_start - m_buffer,
+ m_end - m_buffer, TriggerNames[data.m_trigger], data.m_generation);
+ m_lastGeneration = data.m_generation;
+ char* limit = m_buffer + sizeof(m_buffer);
+ int used = m_end - m_start;
+ if (used < 0)
+ used += sizeof(m_buffer);
+ int needed = (int) len - ((int) sizeof(m_buffer) - used);
+ if (needed >= 0)
+ reclaim(++needed);
+ if (m_end + len <= limit) {
+ memcpy(m_end, (void*) &data, len);
+ m_end += len;
+ DBG_NAV_LOGD("m_start=%d m_end=%d", m_start - m_buffer, m_end - m_buffer);
+ return;
+ }
+ size_t partial = limit - m_end;
+ memcpy(m_end, (void*) &data, partial);
+ const void* remainder = (const void*) ((const char*) &data + partial);
+ partial = len - partial;
+ memcpy(m_buffer, remainder, partial);
+ m_end = m_buffer + partial;
+ DBG_NAV_LOGD("wrap m_start=%d m_end=%d",
+ m_start - m_buffer, m_end - m_buffer);
+}
+
+int count()
+{
+ DBG_NAV_LOGD("m_start=%d m_end=%d",
+ m_start - m_buffer, m_end - m_buffer);
+ if (m_start == m_end)
+ return 0;
+ char* limit = m_buffer + sizeof(m_buffer);
+ char* saveStart = m_start;
+ int result = 0;
+ while (true) {
+ ++result;
+ m_start += triggerSize();
+ if (m_start == m_end)
+ break;
+ if (m_start < limit)
+ continue;
+ m_start -= sizeof(m_buffer);
+ if (m_start == m_end)
+ break;
+ }
+ m_start = saveStart;
+ DBG_NAV_LOGD("count=%d", result);
+ return result;
+}
+
+void discard(int generation)
+{
+ DBG_NAV_LOGD("generation=%d", generation);
+ LargestParams storage;
+ const CommonParams& params = storage.d.d;
+ char* pos = position();
+ retrieve(&storage.d.d);
+ if (params.m_generation > generation) {
+ DBG_NAV_LOGD("params.m_generation=%d > generation=%d",
+ params.m_generation, generation);
+ rewind(pos);
+ DBG_NAV_LOGD("m_start=%d m_end=%d", m_start - m_buffer, m_end - m_buffer);
+ return;
+ }
+ LOG_ASSERT(params.m_generation == generation, "params.m_generation != generation");
+ DBG_NAV_LOGD("m_start=%d m_end=%d", m_start - m_buffer, m_end - m_buffer);
+}
+
+int lastAdd()
+{
+ return m_lastGeneration;
+}
+
+char* position()
+{
+ return m_start;
+}
+
+int retrieve(CommonParams* data)
+{
+ if (m_end == m_start) {
+ // changed from LOGD to LOGV, as it always fires when I click to center
+ // text (mrr)
+ LOGV("%s *** no data to retrieve (error condition) ***", __FUNCTION__);
+ data->m_trigger = CommonParams::NoData;
+ return data->m_generation = INT_MAX;
+ }
+ DBG_NAV_LOGD("m_start=%d m_end=%d",
+ m_start - m_buffer, m_end - m_buffer);
+ char* limit = m_buffer + sizeof(m_buffer);
+ size_t size = triggerSize();
+ if (m_start < m_end) {
+ LOG_ASSERT((size_t) (m_end - m_start) >= size, "m_end - m_start < size");
+ memcpy(data, m_start, size);
+ m_start += size;
+ } else {
+ int partial = limit - m_start;
+ if (partial > (int) size)
+ partial = size;
+ memcpy(data, m_start, partial);
+ m_start += partial;
+ void* remainder = (void*) ((char*) data + partial);
+ partial = size - partial;
+ if (partial > 0) {
+ memcpy(remainder, m_buffer, partial);
+ m_start = m_buffer + partial;
+ LOG_ASSERT(m_start <= m_end, "m_start > m_end");
+ }
+ }
+ if (m_start == limit) {
+ m_start = m_buffer;
+ if (m_end == limit)
+ m_end = m_buffer;
+ }
+ DBG_NAV_LOGD("m_start=%d m_end=%d trigger=%s moveGeneration=%d",
+ m_start - m_buffer, m_end - m_buffer, TriggerNames[data->m_trigger],
+ data->m_generation);
+ return data->m_generation;
+}
+
+void rewind(char* pos)
+{
+ m_start = pos;
+}
+
+private:
+void reclaim(int needed)
+{
+ DBG_NAV_LOGD("needed=%d", needed);
+ char* limit = m_buffer + sizeof(m_buffer);
+ do {
+ size_t size = triggerSize();
+ m_start += size;
+ needed -= size;
+ if (m_start >= limit) {
+ m_start = m_buffer + (m_start - limit);
+ if (m_end == limit)
+ m_end = m_buffer;
+ }
+ } while (needed > 0 && m_start != m_end);
+ DBG_NAV_LOGD("m_start=%d m_end=%d",
+ m_start - m_buffer, m_end - m_buffer);
+}
+
+size_t triggerSize()
+{
+ LOG_ASSERT(m_start != m_end, "m_start == m_end");
+ char* limit = m_buffer + sizeof(m_buffer);
+ LOG_ASSERT(m_start + sizeof(CommonParams::Trigger) <= limit, "trigger not in limit");
+ CommonParams::Trigger trigger;
+ memcpy(&trigger, m_start, sizeof(trigger));
+ switch (trigger) {
+ case CommonParams::ClearFocusParams:
+ return sizeof(ClearFocusParams);
+ case CommonParams::FirstMoveFocusParams:
+ return sizeof(FirstMoveFocusParams);
+ case CommonParams::MoveFocusParams:
+ return sizeof(MoveFocusParams);
+ case CommonParams::MotionUpParams:
+ return sizeof(MotionUpParams);
+ default:
+ LOG_ASSERT(0, "trigger undefined");
+ }
+ return 0;
+}
+
+char m_buffer[REPLAY_BUFFER_SIZE];
+char* m_start;
+char* m_end;
+int m_lastGeneration;
+}; // end of helper class ReplayFocus
+
+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 OutOfFocusFix {
+ DoNothing,
+ ClearTextEntry,
+ UpdateTextEntry
+};
+
+struct JavaGlue {
+ jobject m_obj;
+ jmethodID m_clearTextEntry;
+ jmethodID m_overrideLoading;
+ jmethodID m_scrollBy;
+ jmethodID m_sendFinalFocus;
+ jmethodID m_sendKitFocus;
+ jmethodID m_sendMotionUp;
+ jmethodID m_setFocusData;
+ jmethodID m_getScaledMaxXScroll;
+ jmethodID m_getScaledMaxYScroll;
+ jmethodID m_getVisibleRect;
+ jmethodID m_updateTextEntry;
+ jmethodID m_displaySoftKeyboard;
+ jmethodID m_viewInvalidate;
+ jmethodID m_viewInvalidateRect;
+ jmethodID m_postInvalidateDelayed;
+ jfieldID m_rectLeft;
+ jfieldID m_rectTop;
+ jmethodID m_rectWidth;
+ jmethodID m_rectHeight;
+ jfieldID m_focusNode;
+ jmethodID m_setAll;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+} m_javaGlue;
+
+WebView(JNIEnv* env, jobject javaWebView, int viewImpl)
+{
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ // m_javaGlue = new JavaGlue;
+ m_javaGlue.m_obj = adoptGlobalRef(env, javaWebView);
+ m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)V");
+ m_javaGlue.m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
+ m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V");
+ m_javaGlue.m_sendFinalFocus = GetJMethod(env, clazz, "sendFinalFocus", "(IIII)V");
+ m_javaGlue.m_sendKitFocus = GetJMethod(env, clazz, "sendKitFocus", "()V");
+ m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIIIIIZZ)V");
+ m_javaGlue.m_setFocusData = GetJMethod(env, clazz, "setFocusData", "(IIIIIIZ)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_updateTextEntry = GetJMethod(env, clazz, "updateTextEntry", "()V");
+ m_javaGlue.m_displaySoftKeyboard = GetJMethod(env, clazz, "displaySoftKeyboard", "()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");
+ 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");
+
+ // Set up class for updateFocusNode
+ jclass focusnodeClass = env->FindClass("android/webkit/WebView$FocusNode");
+ LOG_ASSERT(focusnodeClass, "Could not find FocusNode class!");
+ m_javaGlue.m_focusNode = env->GetFieldID(clazz, "mFocusNode", "Landroid/webkit/WebView$FocusNode;");
+ m_javaGlue.m_setAll = GetJMethod(env, focusnodeClass, "setAll", "(ZZZZZIIIIIIIILjava/lang/String;Ljava/lang/String;I)V");
+ env->DeleteLocalRef(focusnodeClass);
+
+ env->SetIntField(javaWebView, gWebViewField, (jint)this);
+ m_viewImpl = (WebViewCore*) viewImpl;
+ m_frameCacheUI = 0;
+ m_navPictureUI = 0;
+ m_invalidNode = 0;
+ m_generation = 0;
+ m_heightCanMeasure = false;
+ m_followedLink = false;
+ m_lastDx = 0;
+ m_lastDxTime = 0;
+ m_ringAnimationEnd = 0;
+ m_selStart.setEmpty();
+ m_selEnd.setEmpty();
+ m_matches = 0;
+ m_hasCurrentLocation = false;
+ m_isFindPaintSetUp = false;
+}
+
+~WebView()
+{
+ if (m_javaGlue.m_obj)
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteGlobalRef(m_javaGlue.m_obj);
+ m_javaGlue.m_obj = 0;
+ }
+ delete m_frameCacheUI;
+ delete m_navPictureUI;
+ if (m_matches)
+ delete m_matches;
+}
+
+void clearFocus(int x, int y, bool inval)
+{
+ DBG_NAV_LOGD("x=%d y=%d inval=%s", x, y,
+ inval ? "true" : "false");
+ clearTextEntry();
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ const CachedFrame* oldFrame = 0;
+ const CachedNode* oldFocusNode = root->currentFocus(&oldFrame);
+ WebCore::IntPoint focusLocation = WebCore::IntPoint(0, 0);
+ setFocusData(root->generation(), 0, 0, x, y, !oldFocusNode);
+ sendKitFocus();
+ if (oldFocusNode) {
+ DBG_NAV_LOG("oldFocusNode");
+ focusLocation = root->focusLocation();
+ root->setCachedFocus(0, 0);
+ if (inval)
+ viewInvalidate();
+ }
+ ClearFocusParams params;
+ params.d.m_trigger = CommonParams::ClearFocusParams;
+ params.d.m_generation = m_generation;
+ params.c.setFocus(oldFocusNode, oldFrame, root, focusLocation);
+ params.m_x = x;
+ params.m_y = y;
+ m_replay.add(params.d, sizeof(params));
+}
+
+void clearTextEntry()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_clearTextEntry);
+ checkException(env);
+}
+
+#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 focus 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 focused or pressed state
+void nativeRecordButtons(bool hasFocus, bool pressed, bool invalidate)
+{
+ bool focusIsButton = false;
+ const CachedNode* cachedFocus = 0;
+ // Lock the mutex, since we now share with the WebCore thread.
+ m_viewImpl->gButtonMutex.lock();
+ if (m_viewImpl->m_buttons.size()) {
+ // Find the focused node so we can determine which node has focus, and
+ // therefore which state to paint them in.
+ // FIXME: In a future change, we should keep track of whether the focus
+ // has changed to short circuit (note that we would still need to update
+ // if we received new buttons from the WebCore thread).
+ WebCore::Node* focus = 0;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ cachedFocus = root->currentFocus();
+ if (cachedFocus)
+ focus = (WebCore::Node*) cachedFocus->nodePointer();
+ }
+
+ // Traverse the array, and update each button, depending on whether it
+ // is focused.
+ Container* end = m_viewImpl->m_buttons.end();
+ for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
+ WebCore::RenderSkinAndroid::State state;
+ if (ptr->matches(focus)) {
+ focusIsButton = true;
+ // If the WebView is out of focus/window focus, set the state to
+ // normal, but still keep track of the fact that the focus is a
+ // button
+ if (!hasFocus) {
+ state = WebCore::RenderSkinAndroid::kNormal;
+ } else if (m_followedLink || pressed) {
+ state = WebCore::RenderSkinAndroid::kPressed;
+ } else {
+ state = WebCore::RenderSkinAndroid::kFocused;
+ }
+ } else {
+ state = WebCore::RenderSkinAndroid::kNormal;
+ }
+ ptr->updateFocusState(state);
+ }
+ }
+ m_viewImpl->gButtonMutex.unlock();
+ if (invalidate && cachedFocus && focusIsButton) {
+ const WebCore::IntRect& b = cachedFocus->getBounds();
+ viewInvalidateRect(b.x(), b.y(), b.right(), b.bottom());
+ }
+}
+
+// These two functions separate out the particular look of the drawn find
+// matches from the code that draws them. This function sets up the paints that
+// are used to draw the matches.
+void 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;
+}
+
+// Draw the match specified by region to the canvas.
+void 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);
+}
+
+// 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 drawMatches(SkCanvas* canvas)
+{
+ if (!m_matches || !m_matches->size()) {
+ return;
+ }
+ if (m_findIndex >= m_matches->size()) {
+ m_findIndex = 0;
+ }
+ const MatchInfo& matchInfo = (*m_matches)[m_findIndex];
+ const SkRegion& currentMatchRegion = matchInfo.getLocation();
+ const SkIRect& currentMatchBounds = currentMatchRegion.getBounds();
+ int left = currentMatchBounds.fLeft;
+ int top = currentMatchBounds.fTop;
+ int right = currentMatchBounds.fRight;
+ int bottom = currentMatchBounds.fBottom;
+ WebCore::IntRect visible;
+ getVisibleRect(&visible);
+ // Check to make sure that the highlighted match is on screen. If not,
+ // scroll it onscreen and return.
+ int dx = 0;
+ if (left < visible.x()) {
+ dx = left - visible.x();
+ // Only scroll right if the entire width can fit on screen.
+ } else if (right > visible.right() && right - left < visible.width()) {
+ dx = right - visible.right();
+ }
+ int dy = 0;
+ if (top < visible.y()) {
+ dy = top - visible.y();
+ // Only scroll down if the entire height can fit on screen
+ } else if (bottom > visible.bottom() && bottom - top < visible.height()) {
+ dy = bottom - visible.bottom();
+ }
+ if ((dx|dy)) {
+ scrollBy(dx, dy);
+ viewInvalidate();
+ return;
+ }
+ // Set up the paints used for drawing the matches
+ if (!m_isFindPaintSetUp)
+ setUpFindPaint();
+
+ // Draw the current match
+ drawMatch(currentMatchRegion, canvas, true);
+ // Now draw the picture, so that it shows up on top of the rectangle
+ canvas->drawPicture(*matchInfo.getPicture());
+
+ // Draw the rest
+ unsigned numberOfMatches = m_matches->size();
+ if (numberOfMatches > 1
+ && numberOfMatches < MAX_NUMBER_OF_MATCHES_TO_DRAW) {
+ SkIRect visibleIRect;
+ android_setrect(&visibleIRect, visible);
+ for(unsigned i = 0; i < numberOfMatches; i++) {
+ // The current match has already been drawn
+ if (i == m_findIndex)
+ 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)
+ || !region.intersects(visibleIRect))
+ continue;
+ drawMatch(region, canvas, false);
+ }
+ }
+}
+
+void drawFocusRing(SkCanvas* canvas)
+{
+ const CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ m_followedLink = false;
+ return;
+ }
+ const CachedNode* node = root->currentFocus();
+ if (!node) {
+ DBG_NAV_LOG("!node");
+ m_followedLink = false;
+ return;
+ }
+ if (!node->hasFocusRing()) {
+ DBG_NAV_LOG("!node->hasFocusRing()");
+ return;
+ }
+ const WTF::Vector<WebCore::IntRect>& rings = node->focusRings();
+ if (!rings.size()) {
+ DBG_NAV_LOG("!rings.size()");
+ return;
+ }
+
+ bool isButton = false;
+ m_viewImpl->gButtonMutex.lock();
+ // If this is a button drawn by us (rather than webkit) do not draw the
+ // focus ring, since its focus will be shown by a change in what we draw.
+ // Should be in sync with recordButtons, since that will be called
+ // before this.
+ if (m_viewImpl->m_buttons.size() > 0) {
+ WebCore::Node* focusPointer = (WebCore::Node*) node->nodePointer();
+ Container* end = m_viewImpl->m_buttons.end();
+ for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
+ if (ptr->matches(focusPointer)) {
+ isButton = true;
+ break;
+ }
+ }
+ }
+ m_viewImpl->gButtonMutex.unlock();
+ WebCore::IntRect bounds = node->bounds();
+ bounds.inflate(SkScalarCeil(FOCUS_RING_OUTER_DIAMETER));
+ SkRect sbounds;
+ android_setrect(&sbounds, bounds);
+ if (canvas->quickReject(sbounds, SkCanvas::kAA_EdgeType)) {
+ DBG_NAV_LOG("canvas->quickReject");
+ m_followedLink = false;
+ return;
+ }
+ FocusRing::Flavor flavor = FocusRing::NORMAL_FLAVOR;
+ if (!isButton) {
+ flavor = node->type() != NORMAL_CACHEDNODETYPE ?
+ FocusRing::FAKE_FLAVOR : node->nodePointer() == m_invalidNode ?
+ FocusRing::INVALID_FLAVOR : FocusRing::NORMAL_FLAVOR;
+ if (flavor != FocusRing::INVALID_FLAVOR && m_followedLink) {
+ flavor = (FocusRing::Flavor) (flavor + FocusRing::NORMAL_ANIMATING);
+ }
+#if DEBUG_NAV_UI
+ const WebCore::IntRect& ring = rings[0];
+ DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p) flavor=%s rings=%d"
+ " (%d, %d, %d, %d)", node->index(), node->nodePointer(),
+ flavor == FocusRing::FAKE_FLAVOR ? "FAKE_FLAVOR" :
+ flavor == FocusRing::INVALID_FLAVOR ? "INVALID_FLAVOR" :
+ flavor == FocusRing::NORMAL_ANIMATING ? "NORMAL_ANIMATING" :
+ flavor == FocusRing::FAKE_ANIMATING ? "FAKE_ANIMATING" : "NORMAL_FLAVOR",
+ rings.size(), ring.x(), ring.y(), ring.width(), ring.height());
+#endif
+ }
+ if (isButton || flavor >= FocusRing::NORMAL_ANIMATING) {
+ SkMSec time = SkTime::GetMSecs();
+ if (time < m_ringAnimationEnd) {
+ // views assume that inval bounds coordinates are non-negative
+ bounds.intersect(WebCore::IntRect(0, 0, INT_MAX, INT_MAX));
+ postInvalidateDelayed(m_ringAnimationEnd - time, bounds);
+ } else {
+ m_followedLink = false;
+ flavor = (FocusRing::Flavor) (flavor - FocusRing::NORMAL_ANIMATING);
+ }
+ }
+ if (!isButton)
+ FocusRing::DrawRing(canvas, rings, flavor);
+}
+
+OutOfFocusFix fixOutOfDateFocus(bool useReplay)
+{
+ if (!m_frameCacheUI) {
+ DBG_NAV_LOG("!m_frameCacheUI");
+ return DoNothing;
+ }
+ const CachedFrame* cachedFrame = 0;
+ const CachedNode* cachedFocusNode = m_frameCacheUI->currentFocus(&cachedFrame);
+ if (!cachedFocusNode) {
+ DBG_NAV_LOG("!cachedFocusNode");
+ return DoNothing;
+ }
+ CachedRoot* webRoot = m_viewImpl->m_frameCacheKit;
+ if (!webRoot) {
+ DBG_NAV_LOG("!webRoot");
+ return DoNothing;
+ }
+ int uiWidth = m_frameCacheUI->width();
+ int webWidth = webRoot->width();
+ if (uiWidth != webWidth) {
+ DBG_NAV_LOGD("uiWidth=%d webWidth=%d", uiWidth, webWidth);
+ return DoNothing; // allow text inputs to preserve their state
+ } else {
+ const WebCore::IntRect& cachedBounds = m_frameCacheUI->focusBounds();
+ const CachedFrame* webFrame = 0;
+ const CachedNode* webFocusNode = webRoot->currentFocus(&webFrame);
+ DBG_NAV_LOGD("cachedBounds=(%d,%d,w=%d,h=%d) cachedFrame=%p (%d)"
+ " webFocusNode=%p (%d) webFrame=%p (%d)",
+ cachedBounds.x(), cachedBounds.y(),
+ cachedBounds.width(), cachedBounds.height(),
+ cachedFrame, cachedFrame ? cachedFrame->indexInParent() : -1,
+ webFocusNode, webFocusNode ? webFocusNode->index() : -1,
+ webFrame, webFrame ? webFrame->indexInParent() : -1);
+ if (webFocusNode && webFrame && webFrame->sameFrame(cachedFrame)) {
+ if (useReplay && !m_replay.count()) {
+ DBG_NAV_LOG("!m_replay.count()");
+ return DoNothing;
+ }
+ if (webFocusNode->index() == cachedFocusNode->index()) {
+ DBG_NAV_LOG("index ==");
+ return DoNothing;
+ }
+ const WebCore::IntRect& webBounds = webRoot->focusBounds();
+ DBG_NAV_LOGD("webBounds=(%d,%d,w=%d,h=%d)",
+ webBounds.x(), webBounds.y(),
+ webBounds.width(), webBounds.height());
+ if (cachedBounds.contains(webBounds)) {
+ DBG_NAV_LOG("contains");
+ return DoNothing;
+ }
+ if (webBounds.contains(cachedBounds)) {
+ DBG_NAV_LOG("webBounds contains");
+ return DoNothing;
+ }
+ }
+ const CachedFrame* foundFrame = 0;
+ int x, y;
+ const CachedNode* found = findAt(webRoot, cachedBounds, &foundFrame, &x, &y);
+#if DEBUG_NAV_UI
+ DBG_NAV_LOGD("found=%p (%d) frame=%p (%d)",
+ found, found ? found->index() : -1,
+ foundFrame, foundFrame ? foundFrame->indexInParent() : -1);
+ if (found) {
+ WebCore::IntRect newBounds = found->bounds();
+ DBG_NAV_LOGD("found=(%d,%d,w=%d,h=%d) x=%d y=%d",
+ newBounds.x(), newBounds.y(), newBounds.width(),
+ newBounds.height(), x, y);
+ }
+#endif
+ webRoot->setCachedFocus(const_cast<CachedFrame*>(foundFrame),
+ const_cast<CachedNode*>(found));
+ if (found)
+ webRoot->rootHistory()->setNavBounds(found->bounds());
+ WebCore::Frame* framePointer = foundFrame ? (WebCore::Frame*) foundFrame->framePointer() : 0;
+ WebCore::Node* nodePointer = found ? (WebCore::Node*) found->nodePointer() : 0;
+ setFocusData(webRoot->generation(), framePointer, nodePointer, x, y, !found);
+ sendFinalFocus(framePointer, nodePointer, x, y);
+ if (found && (found->isTextArea() || found->isTextField()))
+ return UpdateTextEntry;
+ }
+checkOldFocus:
+ return cachedFocusNode->isTextArea() || cachedFocusNode->isTextField() ? ClearTextEntry : DoNothing;
+}
+
+bool focusIsTextArea(FrameCachePermission allowNewer)
+{
+ CachedRoot* root = getFrameCache(allowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* focus = root->currentFocus();
+ if (!focus)
+ return false;
+ return focus->isTextArea() || focus->isTextField();
+}
+
+void focusRingBounds(WebCore::IntRect* bounds)
+{
+ DBG_NAV_LOGD("%s", "");
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedNode* cachedNode = root->currentFocus();
+ if (cachedNode) {
+ cachedNode->focusRingBounds(bounds);
+ DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(),
+ bounds->width(), bounds->height());
+ return;
+ }
+ }
+ *bounds = WebCore::IntRect(0, 0, 0, 0);
+}
+
+CachedRoot* getFrameCache(FrameCachePermission allowNewer)
+{
+ if (!m_viewImpl->m_updatedFrameCache)
+ return m_frameCacheUI;
+ m_viewImpl->gRecomputeFocusMutex.lock();
+ bool recomputeInProgress = m_viewImpl->m_recomputeEvents.size() > 0;
+ m_viewImpl->gRecomputeFocusMutex.unlock();
+ if (allowNewer != AllowNewest && recomputeInProgress)
+ return m_frameCacheUI;
+ if (allowNewer == DontAllowNewer && m_viewImpl->m_lastGeneration < m_generation)
+ return m_frameCacheUI;
+ DBG_NAV_LOGD("%s", "m_viewImpl->m_updatedFrameCache == true");
+ m_viewImpl->gFrameCacheMutex.lock();
+ OutOfFocusFix fix = DoNothing;
+ if (allowNewer != DontAllowNewer)
+ fix = fixOutOfDateFocus(m_viewImpl->m_useReplay);
+ delete m_frameCacheUI;
+ delete 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 (fix == UpdateTextEntry)
+ updateTextEntry();
+ else if (fix == ClearTextEntry)
+ clearTextEntry();
+ 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;
+}
+
+void getVisibleRect(WebCore::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);
+ int left = (int) env->GetIntField(jRect, m_javaGlue.m_rectLeft);
+ checkException(env);
+ rect->setX(left);
+ int top = (int) env->GetIntField(jRect, m_javaGlue.m_rectTop);
+ checkException(env);
+ rect->setY(top);
+ int width = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectWidth);
+ checkException(env);
+ rect->setWidth(width);
+ int height = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectHeight);
+ checkException(env);
+ rect->setHeight(height);
+ env->DeleteLocalRef(jRect);
+ checkException(env);
+}
+
+static CachedFrame::Direction KeyToDirection(KeyCode keyCode)
+{
+ switch (keyCode) {
+ case kKeyCodeDpadRight:
+ DBG_NAV_LOGD("keyCode=%s", "right");
+ return CachedFrame::RIGHT;
+ case kKeyCodeDpadLeft:
+ DBG_NAV_LOGD("keyCode=%s", "left");
+ return CachedFrame::LEFT;
+ case kKeyCodeDpadDown:
+ DBG_NAV_LOGD("keyCode=%s", "down");
+ return CachedFrame::DOWN;
+ case kKeyCodeDpadUp:
+ DBG_NAV_LOGD("keyCode=%s", "up");
+ return CachedFrame::UP;
+ default:
+ LOGD("------- bad key sent to WebView::moveFocus");
+ return CachedFrame::UNINITIALIZED;
+ }
+}
+
+bool invalidFrame(WebCore::Frame* frame, const CachedRoot* root)
+{
+ if (!frame)
+ return false;
+ int frameBuild = m_viewImpl->retrieveFrameGeneration(frame);
+ int rootBuild = root->generation();
+ return frameBuild > rootBuild;
+}
+
+WebCore::String imageURI(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ return root ? root->imageURI(x, y) : WebCore::String();
+}
+
+bool focusNodeWantsKeyEvents()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedNode* focus = root->currentFocus();
+ if (focus) {
+ return focus->isWantsKeyEvents();
+ }
+ }
+ return false;
+}
+
+/* returns true if the key had no effect (neither scrolled nor changed focus) */
+bool moveFocus(int keyCode, int count, bool ignoreScroll, bool inval,
+ void* lastSentFocus, const WebCore::IntRect* lastSentBounds)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ setFocusData(0, 0, 0, 0, 0, true);
+ sendKitFocus(); // will build cache and retry
+ FirstMoveFocusParams params;
+ params.d.m_trigger = CommonParams::FirstMoveFocusParams;
+ params.d.m_generation = m_generation;
+ params.m_keyCode = keyCode;
+ params.m_count = count;
+ params.m_ignoreScroll = ignoreScroll;
+ m_replay.add(params.d, sizeof(params));
+ return true;
+ }
+
+ CachedFrame::Direction direction = KeyToDirection((KeyCode) keyCode);
+ const CachedFrame* cachedFrame, * oldFrame = 0;
+ const CachedNode* focus = root->currentFocus(&oldFrame);
+ WebCore::IntPoint focusLocation = root->focusLocation();
+ DBG_NAV_LOGD("old focus %d (nativeNode=%p) focusLocation={%d, %d}",
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : 0, focusLocation.x(), focusLocation.y());
+ WebCore::IntRect visibleRect;
+ getVisibleRect(&visibleRect);
+ DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
+ visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
+ root->setVisibleRect(visibleRect);
+ int xMax = getScaledMaxXScroll();
+ int yMax = getScaledMaxYScroll();
+ root->setMaxScroll(xMax, yMax);
+ CachedHistory savedHistory = *root->rootHistory();
+ bool oldNodeIsTextArea = focusIsTextArea(DontAllowNewer);
+ const CachedNode* cachedNode = 0;
+ int dx = 0;
+ int dy = 0;
+ int counter = count;
+ if (!focus || !focus->isInput() || !m_followedLink)
+ root->setScrollOnly(m_followedLink);
+ while (--counter >= 0) {
+ WebCore::IntPoint scroll = WebCore::IntPoint(0, 0);
+ cachedNode = root->moveFocus(direction, &cachedFrame, &scroll);
+ dx += scroll.x();
+ dy += scroll.y();
+ }
+ DBG_NAV_LOGD("new focus %d (nativeNode=%p) focusLocation={%d, %d}",
+ cachedNode ? cachedNode->index() : 0,
+ cachedNode ? cachedNode->nodePointer() : 0, root->focusLocation().x(),
+ root->focusLocation().y());
+ // If !m_heightCanMeasure (such as in the browser), we want to scroll no
+ // matter what
+ if (!ignoreScroll && (!m_heightCanMeasure ||
+ !cachedNode ||
+ (focus && focus->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();
+ ignoreScroll = true; // if move re-executes, don't scroll the second time
+ }
+ bool result = false;
+ if (cachedNode) {
+ WebCore::IntPoint pos;
+ root->setCachedFocus((CachedFrame*) cachedFrame, (CachedNode*) cachedNode);
+ root->getSimulatedMousePosition(&pos);
+ if (lastSentFocus == cachedNode->nodePointer() && lastSentBounds &&
+ *lastSentBounds == cachedNode->bounds())
+ {
+ sendFinalFocus((WebCore::Frame*) cachedFrame->framePointer(),
+ (WebCore::Node*) cachedNode->nodePointer(), pos.x(), pos.y());
+ } else {
+ setFocusData(root->generation(),
+ (WebCore::Frame*) cachedFrame->framePointer(),
+ (WebCore::Node*) cachedNode->nodePointer(), pos.x(), pos.y(),
+ true);
+ sendKitFocus();
+ if (inval)
+ viewInvalidate();
+ MoveFocusParams params;
+ params.d.d.m_trigger = CommonParams::MoveFocusParams;
+ params.d.d.m_generation = m_generation;
+ params.c.setFocus(focus, oldFrame, root, focusLocation);
+ params.m_sentFocus = cachedNode->nodePointer();
+ params.m_sentBounds = cachedNode->bounds();
+ params.m_visibleRect = visibleRect;
+ params.m_history = savedHistory;
+ DBG_NAV_LOGD("history.mDidFirstLayout=%s",
+ params.m_history.didFirstLayout() ? "true" : "false");
+ params.m_xMax = xMax;
+ params.m_yMax = yMax;
+ params.d.m_keyCode = keyCode;
+ params.d.m_count = count;
+ params.d.m_ignoreScroll = ignoreScroll;
+ m_replay.add(params.d.d, sizeof(params));
+ }
+ } else {
+ if (visibleRect.intersects(root->focusBounds()) == false) {
+ setFocusData(root->generation(), 0, 0, 0, 0, true);
+ sendKitFocus(); // will build cache and retry
+ }
+ FirstMoveFocusParams params;
+ params.d.m_trigger = CommonParams::FirstMoveFocusParams;
+ params.d.m_generation = m_generation;
+ params.m_keyCode = keyCode;
+ params.m_count = count;
+ params.m_ignoreScroll = ignoreScroll;
+ m_replay.add(params.d, sizeof(params));
+ 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;
+ }
+ if (focusIsTextArea(DontAllowNewer))
+ updateTextEntry();
+ else if (oldNodeIsTextArea)
+ clearTextEntry();
+ return result;
+}
+
+void notifyFocusSet(FrameCachePermission inEditingMode)
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ // make sure the mFocusData in WebView.java is in sync with WebView.cpp
+ const CachedFrame* frame = 0;
+ const CachedNode* node = root->currentFocus(&frame);
+ const WebCore::IntPoint& focusLocation = root->focusLocation();
+ setFocusData(root->generation(),
+ frame ? (WebCore::Frame*) frame->framePointer() : 0,
+ node ? (WebCore::Node*) node->nodePointer() : 0,
+ focusLocation.x(), focusLocation.y(), false);
+ }
+
+ if (focusIsTextArea(inEditingMode))
+ updateTextEntry();
+ else if (inEditingMode)
+ clearTextEntry();
+#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
+}
+
+void notifyProgressFinished()
+{
+ DBG_NAV_LOGD("focusIsTextArea=%d", focusIsTextArea(DontAllowNewer));
+ updateTextEntry();
+#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
+}
+
+void recomputeFocus()
+{
+ int generation;
+ do {
+ m_viewImpl->gRecomputeFocusMutex.lock();
+ if (!m_viewImpl->m_recomputeEvents.size()) {
+ m_viewImpl->gRecomputeFocusMutex.unlock();
+ return;
+ }
+ generation = m_viewImpl->m_recomputeEvents.first();
+ m_viewImpl->m_recomputeEvents.remove(0);
+ m_viewImpl->gRecomputeFocusMutex.unlock();
+ DBG_NAV_LOGD("generation=%d", generation);
+ CachedRoot* root = getFrameCache(AllowNewest);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return;
+ }
+ LargestParams storage;
+ const CommonParams& params = storage.d.d;
+ char* pos = m_replay.position();
+ while (m_replay.retrieve(&storage.d.d) < generation)
+ DBG_NAV_LOGD("dropped ", params.m_generation);
+ if (params.m_generation > generation) {
+ DBG_NAV_LOGD("params.m_generation=%d > generation=%d",
+ params.m_generation, generation);
+ m_replay.rewind(pos);
+ return;
+ }
+ int lastAdd = m_replay.lastAdd();
+ do {
+ LOG_ASSERT(params.m_trigger != CommonParams::NoData, "expected data");
+ bool inval = generation == m_generation;
+ switch (params.m_trigger) {
+ case CommonParams::ClearFocusParams: {
+ const ClearFocusParams& sParams = *(ClearFocusParams*) &storage;
+ const CacheParams& cParams = sParams.c;
+ if (invalidFrame(cParams.m_frame, root)) {
+ DBG_NAV_LOGD("dropped %s generation=%d",
+ TriggerNames[params.m_trigger], generation);
+ return;
+ }
+ root->setFocus(cParams.m_frame, cParams.m_node, cParams.m_x, cParams.m_y);
+ clearFocus(sParams.m_x, sParams.m_y, inval);
+ DBG_NAV_LOGD("clearFocus(x,y)={%d,%d}", sParams.m_x, sParams.m_y);
+ } break;
+ case CommonParams::MotionUpParams: {
+ const MotionUpParams& mParams = *(MotionUpParams*) &storage;
+ // const CacheParams& cParams = mParams.c;
+ // if (invalidFrame(cParams.m_frame, root) == false)
+ // root->setFocus(cParams.m_frame, cParams.m_node,
+ // cParams.m_x, cParams.m_y);
+ motionUp(mParams.m_x, mParams.m_y, mParams.m_slop, mParams.m_isClick, inval, true);
+ DBG_NAV_LOGD("motionUp m_x=%d m_y=%d", mParams.m_x, mParams.m_y);
+ } break;
+ case CommonParams::FirstMoveFocusParams: {
+ if (invalidFrame((WebCore::Frame*) root->framePointer(), root)) {
+ DBG_NAV_LOGD("dropped %s generation=%d",
+ TriggerNames[params.m_trigger], generation);
+ return;
+ }
+ const FirstMoveFocusParams& fParams = *(FirstMoveFocusParams*) &storage;
+ DBG_NAV_LOGD("first moveFocus keyCode=%d count=%d"
+ " ignoreScroll=%s", fParams.m_keyCode, fParams.m_count,
+ fParams.m_ignoreScroll ? "true" : "false");
+ moveFocus(fParams.m_keyCode, fParams.m_count,
+ fParams.m_ignoreScroll, inval, 0, 0);
+ } break;
+ case CommonParams::MoveFocusParams: {
+ const MoveFocusParams& mParams = *(MoveFocusParams*) &storage;
+ const CacheParams& cParams = mParams.c;
+ if (invalidFrame(cParams.m_frame, root)) {
+ DBG_NAV_LOGD("dropped %s generation=%d",
+ TriggerNames[params.m_trigger], generation);
+ return;
+ }
+ DBG_NAV_LOGD("moveFocus keyCode=%d count=%d ignoreScroll=%s "
+ "history.mDidFirstLayout=%s", mParams.d.m_keyCode,
+ mParams.d.m_count, mParams.d.m_ignoreScroll ? "true" : "false",
+ mParams.m_history.didFirstLayout() ? "true" : "false");
+ if (!root->setFocus(cParams.m_frame, cParams.m_node,
+ cParams.m_x, cParams.m_y)) {
+ DBG_NAV_LOGD("can't restore focus frame=%p node=%p",
+ "x=%d y=%d %s", cParams.m_frame, cParams.m_node,
+ cParams.m_x, cParams.m_y, TriggerNames[params.m_trigger]);
+ return;
+ }
+ root->setVisibleRect(mParams.m_visibleRect);
+ root->setMaxScroll(mParams.m_xMax, mParams.m_yMax);
+ *root->rootHistory() = mParams.m_history;
+ moveFocus(mParams.d.m_keyCode, mParams.d.m_count,
+ mParams.d.m_ignoreScroll, inval,
+ mParams.m_sentFocus, &mParams.m_sentBounds);
+ } break;
+ default:
+ LOG_ASSERT(0, "unknown trigger");
+ }
+ if (params.m_generation >= lastAdd)
+ break;
+ root = getFrameCache(DontAllowNewer); // re-execution may have retrieved newer cache
+ m_replay.retrieve(&storage.d.d);
+ DBG_NAV_LOGD("continuation m_generation %d", params.m_generation);
+ } while (true);
+ } while (true);
+}
+
+void resetFocus()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ root->setCachedFocus(0, 0);
+}
+
+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;
+ WebCore::IntRect visibleRect;
+ getVisibleRect(&visibleRect);
+ root->setVisibleRect(visibleRect);
+ return root->findAt(rect, framePtr, rxPtr, ryPtr);
+}
+
+void selectBestAt(const WebCore::IntRect& rect)
+{
+ const CachedFrame* frame;
+ int rx, ry;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ const CachedNode* node = findAt(root, rect, &frame, &rx, &ry);
+ int rootGeneration = root ? root->generation() : 0;
+ setFocusData(rootGeneration,
+ frame ? (WebCore::Frame*) frame->framePointer() : 0,
+ node ? (WebCore::Node*) node->nodePointer() : 0, rx, ry, false);
+ if (!node) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ if (root) {
+ root->clearFocus();
+ root->setCachedFocus(0, 0);
+ }
+ sendKitFocus();
+ viewInvalidate();
+ clearTextEntry();
+ return;
+ }
+ DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index());
+ const CachedFrame* oldFrame = 0;
+ const CachedNode* oldFocusNode = root->currentFocus(&oldFrame);
+ bool oldNodeIsTextArea = focusIsTextArea(DontAllowNewer);
+ root->setCachedFocus(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(node));
+ viewInvalidate();
+ if (focusIsTextArea(DontAllowNewer))
+ updateTextEntry();
+ else if (oldNodeIsTextArea)
+ clearTextEntry();
+}
+
+WebCore::IntRect getNavBounds()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return WebCore::IntRect(0, 0, 0, 0);
+ return root->rootHistory()->navBounds();
+}
+
+void setNavBounds(const WebCore::IntRect& rect)
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ root->rootHistory()->setNavBounds(rect);
+}
+
+void markNodeInvalid(WebCore::Node* node)
+{
+ DBG_NAV_LOGD("node=%p", node);
+ m_invalidNode = node;
+ viewInvalidate();
+}
+
+bool motionUp(int x, int y, int slop, bool isClick, bool inval, bool retry)
+{
+ bool pageScrolled = false;
+ m_followedLink = false;
+ const CachedFrame* frame;
+ WebCore::IntRect rect = WebCore::IntRect(x - slop, y - slop, slop * 2, slop * 2);
+ int rx, ry;
+ CachedRoot* root = getFrameCache(AllowNewer);
+ const CachedNode* result = findAt(root, rect, &frame, &rx, &ry);
+ if (!result) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ int rootGeneration = 0;
+ if (root) {
+ root->clearFocus();
+ rootGeneration = root->generation();
+ if (!retry) { // scroll first time only
+ int dx = root->checkForCenter(x, y);
+ if (dx) {
+ scrollBy(dx, 0);
+ retry = true; // don't recompute later since we scrolled
+ pageScrolled = true;
+ }
+ }
+ }
+ sendMotionUp(rootGeneration, frame ?
+ (WebCore::Frame*) frame->framePointer() : 0,
+ 0, x, y, slop, isClick, retry);
+ if (inval)
+ viewInvalidate();
+ if (!retry) {
+ MotionUpParams params;
+ params.d.m_trigger = CommonParams::MotionUpParams;
+ params.d.m_generation = m_generation;
+ params.m_x = x;
+ params.m_y = y;
+ params.m_slop = slop;
+ params.m_isClick = isClick;
+ m_replay.add(params.d, sizeof(params));
+ }
+ clearTextEntry();
+ return pageScrolled;
+ }
+ DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result,
+ result->index(), x, y, rx, ry);
+ // const CachedFrame* oldFrame = 0;
+ // const CachedNode* oldFocusNode = root->currentFocus(&oldFrame);
+ // WebCore::IntPoint focusLocation = root->focusLocation();
+ bool oldNodeIsTextArea = !retry && focusIsTextArea(DontAllowNewer);
+ root->setCachedFocus(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(result));
+ bool newNodeIsTextArea = focusIsTextArea(DontAllowNewer);
+ CachedNodeType type = result->type();
+ if (type == NORMAL_CACHEDNODETYPE || newNodeIsTextArea) {
+ sendMotionUp(root->generation(),
+ frame ? (WebCore::Frame*) frame->framePointer() : 0,
+ result ? (WebCore::Node*) result->nodePointer() : 0, rx, ry,
+ slop, isClick, retry);
+ if (inval)
+ viewInvalidate();
+ if (!retry) {
+ MotionUpParams params;
+ params.d.m_trigger = CommonParams::MotionUpParams;
+ params.d.m_generation = m_generation;
+ params.m_x = x;
+ params.m_y = y;
+ params.m_slop = slop;
+ params.m_isClick = isClick;
+ // params.c.setFocus(oldFocusNode, oldFrame, root, focusLocation);
+ m_replay.add(params.d, sizeof(params));
+ }
+ } else if (inval)
+ viewInvalidate();
+ if (newNodeIsTextArea) {
+ updateTextEntry();
+ displaySoftKeyboard();
+ } else {
+ if (isClick) {
+ setFollowedLink(true);
+ if (type != NORMAL_CACHEDNODETYPE) {
+ overrideUrlLoading(result->getExport());
+ }
+ }
+ if (oldNodeIsTextArea)
+ clearTextEntry();
+ }
+ return pageScrolled;
+}
+
+void overrideUrlLoading(const WebCore::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jName = env->NewString((jchar*) url.characters(), url.length());
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_overrideLoading, jName);
+ env->DeleteLocalRef(jName);
+}
+
+void setFindIsUp(bool up)
+{
+ m_viewImpl->m_findIsUp = up;
+ if (!up)
+ m_hasCurrentLocation = false;
+}
+
+void setFollowedLink(bool followed)
+{
+ if ((m_followedLink = followed) != false) {
+ m_ringAnimationEnd = SkTime::GetMSecs() + 500;
+ viewInvalidate();
+ }
+}
+
+void setHeightCanMeasure(bool measure)
+{
+ m_heightCanMeasure = measure;
+}
+
+SkIRect m_selStart, m_selEnd;
+SkRegion m_selRegion;
+#define MIN_ARROW_DISTANCE (20 * 20)
+
+void moveSelection(int x, int y, bool extendSelection)
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ const SkPicture& picture = *m_navPictureUI;
+ WebCore::IntRect r;
+ getVisibleRect(&r);
+ SkIRect area;
+ area.set(r.x(), r.y(), r.right(), r.bottom());
+ if (!extendSelection)
+ m_selStart = m_selEnd = CopyPaste::findClosest(picture, area, x, y);
+ else
+ m_selEnd = CopyPaste::findClosest(picture, area, x, y);
+ DBG_NAV_LOGD("x=%d y=%d extendSelection=%s m_selStart=(%d, %d, %d, %d)"
+ " m_selEnd=(%d, %d, %d, %d)", x, y, extendSelection ? "true" : "false",
+ m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom,
+ m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom);
+}
+
+const SkRegion& getSelection()
+{
+ return m_selRegion;
+}
+
+void drawSelection(SkCanvas* canvas, int x, int y, bool extendSelection)
+{
+ if (!extendSelection) {
+ int dx = x - m_selStart.fLeft;
+ dx *= dx;
+ int otherX = x - m_selStart.fRight;
+ if (dx > (otherX *= otherX))
+ dx = otherX;
+ int dy = y - m_selStart.fTop;
+ int dist = dx * dx + dy * dy;
+ if (dist > MIN_ARROW_DISTANCE)
+ drawSelectionArrow(canvas, x, y);
+ else
+ drawSelectionPointer(canvas, x, y, true);
+ } else {
+ drawSelectionRegion(canvas);
+ drawSelectionPointer(canvas, x, y, false);
+ }
+}
+
+void drawSelectionRegion(SkCanvas* canvas)
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ WebCore::IntRect r;
+ getVisibleRect(&r);
+ SkIRect area;
+ area.set(r.x(), r.y(), r.right(), r.bottom());
+ m_selRegion.setEmpty();
+ CopyPaste::buildSelection(*m_navPictureUI, area, m_selStart, m_selEnd, &m_selRegion);
+ SkPath path;
+ m_selRegion.getBoundaryPath(&path);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SkColorSetARGB(0x40, 255, 51, 204));
+ canvas->drawPath(path, paint);
+}
+
+void drawSelectionPointer(SkCanvas* canvas, int x, int y, bool gridded)
+{
+ SkPath path;
+ getSelectionCaret(&path);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorBLACK);
+ SkPixelXorXfermode xorMode(SK_ColorWHITE);
+ paint.setXfermode(&xorMode);
+ int sc = canvas->save();
+ if (gridded) {
+ bool useLeft = x <= (m_selStart.fLeft + m_selStart.fRight) >> 1;
+ canvas->translate(SkIntToScalar(useLeft ? m_selStart.fLeft :
+ m_selStart.fRight), SkIntToScalar(m_selStart.fTop));
+ } else
+ canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+ canvas->drawPath(path, paint);
+ canvas->restoreToCount(sc);
+}
+
+void drawSelectionArrow(SkCanvas* canvas, int x, int y)
+{
+ SkPath path;
+ getSelectionArrow(&path);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorBLACK);
+ paint.setStrokeWidth(SK_Scalar1 * 2);
+ int sc = canvas->save();
+ canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+ canvas->drawPath(path, paint);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorWHITE);
+ canvas->drawPath(path, paint);
+ canvas->restoreToCount(sc);
+}
+
+void 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(SkIntToScalar(arrow[index]), SkIntToScalar(arrow[index + 1]));
+ path->close();
+}
+
+void getSelectionCaret(SkPath* path)
+{
+ SkScalar height = SkIntToScalar(m_selStart.fBottom - m_selStart.fTop);
+ SkScalar dist = height / 4;
+ path->lineTo(0, height);
+ SkScalar bottom = height + dist;
+ path->lineTo(-dist, bottom);
+ SkScalar edge = bottom - SK_Scalar1/2;
+ path->moveTo(-dist, edge);
+ path->lineTo(dist, edge);
+ path->moveTo(dist, bottom);
+ path->lineTo(0, height);
+}
+
+void sendFinalFocus(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);
+ 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_sendFinalFocus,
+ (jint) framePtr, (jint) nodePtr, x, y);
+ checkException(env);
+}
+
+void sendKitFocus()
+{
+ 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_sendKitFocus);
+ checkException(env);
+}
+
+void sendMotionUp(int buildGeneration,
+ WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y, int slop,
+ bool isClick, bool retry)
+{
+ m_viewImpl->m_touchGeneration = m_viewImpl->m_generation = ++m_generation;
+ DBG_NAV_LOGD("buildGeneration=%d m_generation=%d framePtr=%p nodePtr=%p"
+ " x=%d y=%d slop=%d", buildGeneration,
+ m_generation, framePtr, nodePtr, x, y, slop);
+ 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,
+ buildGeneration, (jint) framePtr, (jint) nodePtr, x, y, slop, isClick, retry);
+ checkException(env);
+}
+
+void setFocusData(int buildGeneration, WebCore::Frame* framePtr,
+ WebCore::Node* nodePtr, int x, int y, bool ignoreNullFocus)
+{
+ m_viewImpl->m_moveGeneration = m_viewImpl->m_generation = ++m_generation;
+ DBG_NAV_LOGD("moveGeneration=%d buildGeneration=%d framePtr=%p nodePtr=%p"
+ " x=%d y=%d", m_generation, buildGeneration, framePtr, nodePtr, x, y);
+ 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_setFocusData, m_generation,
+ buildGeneration, (jint) framePtr, (jint) nodePtr, x, y, ignoreNullFocus);
+ checkException(env);
+}
+
+// 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 inline 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;
+}
+
+void findNext(bool forward)
+{
+ if (!m_matches || !m_matches->size())
+ 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();
+ viewInvalidate();
+}
+
+// With this call, WebView takes ownership of matches, and is responsible for
+// deleting it.
+void 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;
+ viewInvalidate();
+ 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;
+ }
+ viewInvalidate();
+}
+
+void 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();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_scrollBy,
+ dx, dy, true);
+ checkException(env);
+}
+
+bool updateFocusNode(JNIEnv* env)
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedFrame* cachedFrame = 0;
+ const CachedNode* cachedFocusNode = root->currentFocus(&cachedFrame);
+ if (!cachedFocusNode) {
+ DBG_NAV_LOG("!cachedFocusNode");
+ return false;
+ }
+ DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
+ cachedFocusNode->index(),
+ cachedFocusNode->nodePointer());
+ jobject focusnode = env->GetObjectField(m_javaGlue.object(env).get(), m_javaGlue.m_focusNode);
+ LOG_ASSERT(focusnode, "Could not find WebView's FocusNode");
+
+ bool isTextArea = cachedFocusNode->isTextArea();
+ bool isTextField = cachedFocusNode->isTextField();
+ int maxLength;
+ jstring jName;
+ if (isTextField) {
+ maxLength = cachedFocusNode->maxLength();
+ const WebCore::String& name = cachedFocusNode->name();
+ jName = env->NewString((jchar*)name.characters(), name.length());
+ } else {
+ maxLength = -1;
+ jName = 0;
+ }
+ WebCore::IntRect bounds = cachedFocusNode->bounds();
+ WebCore::String value = cachedFocusNode->getExport();
+ jstring val = !value.isEmpty() ? env->NewString((jchar *)value.characters(), value.length()) : 0;
+ env->CallVoidMethod(focusnode, m_javaGlue.m_setAll, isTextField, isTextArea, cachedFocusNode->isPassword(),
+ cachedFocusNode->isAnchor(), cachedFocusNode->isRtlText(), maxLength, cachedFocusNode->textSize(),
+ bounds.x(), bounds.y(), bounds.right(), bounds.bottom(), (int)(cachedFocusNode->nodePointer()),
+ (int)(cachedFrame->framePointer()), val, jName, root->textGeneration());
+ env->DeleteLocalRef(val);
+ env->DeleteLocalRef(focusnode);
+ if (isTextField)
+ env->DeleteLocalRef(jName);
+ return true;
+}
+
+void updateTextEntry()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_updateTextEntry);
+ checkException(env);
+}
+
+void displaySoftKeyboard()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_displaySoftKeyboard);
+ 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);
+}
+
+private: // local state for WebView
+ // private to getFrameCache(); other functions operate in a different thread
+ CachedRoot* m_frameCacheUI; // navigation data ready for use
+ FocusReplay m_replay;
+ WebViewCore* m_viewImpl;
+ WebCore::Node* m_invalidNode;
+ int m_generation; // associate unique ID with sent kit focus to match with ui
+ SkPicture* m_navPictureUI;
+ bool m_followedLink;
+ SkMSec m_ringAnimationEnd;
+ // Corresponds to the same-named boolean on the java side.
+ bool m_heightCanMeasure;
+ int m_lastDx;
+ SkMSec m_lastDxTime;
+ 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;
+}; // end of WebView class
+
+/*
+ * Native JNI methods
+ */
+static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string)
+{
+ int length = string.length();
+ if (!length)
+ return 0;
+ jstring ret = env->NewString((jchar *)string.characters(), length);
+ env->DeleteLocalRef(ret);
+ return ret;
+}
+
+static void nativeClearFocus(JNIEnv *env, jobject obj, int x, int y)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->clearFocus(x, y, true);
+}
+
+static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl)
+{
+ WebView* webview = new WebView(env, obj, viewImpl);
+ // NEED THIS OR SOMETHING LIKE IT!
+ //Release(obj);
+}
+
+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 void nativeDrawMatches(JNIEnv *env, jobject obj, jobject canv)
+{
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ if (!canv) {
+ DBG_NAV_LOG("!canv");
+ return;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view) {
+ DBG_NAV_LOG("!view");
+ return;
+ }
+ view->drawMatches(canvas);
+}
+
+static void nativeDrawFocusRing(JNIEnv *env, jobject obj,
+ jobject canv)
+{
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ if (!canv) {
+ DBG_NAV_LOG("!canv");
+ return;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view) {
+ DBG_NAV_LOG("!view");
+ return;
+ }
+ view->drawFocusRing(canvas);
+}
+
+static void nativeDrawSelection(JNIEnv *env, jobject obj,
+ jobject canv, jint x, jint y, bool ex)
+{
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ if (!canv) {
+ DBG_NAV_LOG("!canv");
+ return;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view) {
+ DBG_NAV_LOG("!view");
+ return;
+ }
+ view->drawSelection(canvas, x, y, ex);
+}
+
+static void nativeDrawSelectionRegion(JNIEnv *env, jobject obj, jobject canv)
+{
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ if (!canv) {
+ DBG_NAV_LOG("!canv");
+ return;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view) {
+ DBG_NAV_LOG("!view");
+ return;
+ }
+ view->drawSelectionRegion(canvas);
+}
+
+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__);
+ WebCore::String uri = view->imageURI(x, y);
+ jstring ret = 0;
+ unsigned len = uri.length();
+ if (len) {
+ ret = env->NewString((jchar*) uri.characters(), len);
+ env->DeleteLocalRef(ret);
+ }
+ return ret;
+}
+
+static bool nativeFocusNodeWantsKeyEvents(JNIEnv* env, jobject jwebview) {
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->focusNodeWantsKeyEvents();
+}
+
+static void nativeInstrumentReport(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::reportNow();
+#endif
+}
+
+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 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 nativeMarkNodeInvalid(JNIEnv *env, jobject obj, int node)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->markNodeInvalid((WebCore::Node*) node);
+}
+
+static bool nativeMotionUp(JNIEnv *env, jobject obj,
+ int x, int y, int slop, bool isClick)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->motionUp(x, y, slop, isClick, true, false);
+}
+
+static bool nativeUpdateFocusNode(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->updateFocusNode(env);
+}
+
+static bool nativeMoveFocus(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->moveFocus(key, count, ignoreScroll, true, 0, 0);
+}
+
+static void nativeNotifyFocusSet(JNIEnv *env, jobject obj, bool inEditingMode)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->notifyFocusSet((WebView::FrameCachePermission) inEditingMode);
+}
+
+static void nativeRecomputeFocus(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->recomputeFocus();
+}
+
+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 nativeResetFocus(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->resetFocus();
+}
+
+static void nativeSetFindIsDown(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setFindIsUp(false);
+}
+
+static void nativeSetFollowedLink(JNIEnv *env, jobject obj, bool followed)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setFollowedLink(followed);
+}
+
+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 nativeGetFocusRingBounds(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->focusRingBounds(&webRect);
+ jobject rect = env->NewObject(rectClass, init, webRect.x(),
+ webRect.y(), webRect.right(), webRect.bottom());
+ return rect;
+}
+
+static jobject nativeGetNavBounds(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->getNavBounds();
+ jobject rect = env->NewObject(rectClass, init, webRect.x(),
+ webRect.y(), webRect.right(), webRect.bottom());
+ return rect;
+}
+
+static void nativeSetNavBounds(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->setNavBounds(rect);
+}
+
+static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
+ jstring findUpper)
+{
+ // 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");
+ view->setFindIsUp(true);
+ 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);
+ canvas.drawPicture(*(root->getPicture()));
+ WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
+ // With setMatches, the WebView takes ownership of matches
+ view->setMatches(matches);
+
+ 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 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->isTextField() && !cachedFocusNode->isTextArea()))
+ return;
+ WebCore::String webcoreString = to_string(env, updatedText);
+ (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString);
+ root->setTextGeneration(generation);
+ checkException(env);
+}
+
+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 nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y, bool ex)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->moveSelection(x, y, ex);
+}
+
+static jobject nativeGetSelection(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return GraphicsJNI::createRegion(env, new SkRegion(view->getSelection()));
+}
+
+#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__);
+
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (root) {
+ SkPicture* picture = root->getPicture();
+ if (picture) {
+ 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
+ picture->draw(&canvas);
+ // we're done with the file now
+ fwrite("\n", 1, 1, file);
+ fclose(file);
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gJavaWebViewMethods[] = {
+ { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I",
+ (void*) nativeFindAll },
+ { "nativeFindNext", "(Z)V",
+ (void*) nativeFindNext },
+ { "nativeClearFocus", "(II)V",
+ (void*) nativeClearFocus },
+ { "nativeCreate", "(I)V",
+ (void*) nativeCreate },
+ { "nativeDebugDump", "()V",
+ (void*) nativeDebugDump },
+ { "nativeDestroy", "()V",
+ (void*) nativeDestroy },
+ { "nativeDrawMatches", "(Landroid/graphics/Canvas;)V",
+ (void*) nativeDrawMatches },
+ { "nativeDrawFocusRing", "(Landroid/graphics/Canvas;)V",
+ (void*) nativeDrawFocusRing },
+ { "nativeDrawSelection", "(Landroid/graphics/Canvas;IIZ)V",
+ (void*) nativeDrawSelection },
+ { "nativeDrawSelectionRegion", "(Landroid/graphics/Canvas;)V",
+ (void*) nativeDrawSelectionRegion },
+ { "nativeUpdateFocusNode", "()Z",
+ (void*) nativeUpdateFocusNode },
+ { "nativeGetFocusRingBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeGetFocusRingBounds },
+ { "nativeGetNavBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeGetNavBounds },
+ { "nativeInstrumentReport", "()V",
+ (void*) nativeInstrumentReport },
+ { "nativeMarkNodeInvalid", "(I)V",
+ (void*) nativeMarkNodeInvalid },
+ { "nativeMotionUp", "(IIIZ)Z",
+ (void*) nativeMotionUp },
+ { "nativeMoveFocus", "(IIZ)Z",
+ (void*) nativeMoveFocus },
+ { "nativeNotifyFocusSet", "(Z)V",
+ (void*) nativeNotifyFocusSet },
+ { "nativeRecomputeFocus", "()V",
+ (void*) nativeRecomputeFocus },
+ { "nativeRecordButtons", "(ZZZ)V",
+ (void*) nativeRecordButtons },
+ { "nativeResetFocus", "()V",
+ (void*) nativeResetFocus },
+ { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
+ (void*) nativeSelectBestAt },
+ { "nativeSetFindIsDown", "()V",
+ (void*) nativeSetFindIsDown },
+ { "nativeSetFollowedLink", "(Z)V",
+ (void*) nativeSetFollowedLink },
+ { "nativeSetHeightCanMeasure", "(Z)V",
+ (void*) nativeSetHeightCanMeasure },
+ { "nativeSetNavBounds", "(Landroid/graphics/Rect;)V",
+ (void*) nativeSetNavBounds },
+ { "nativeImageURI", "(II)Ljava/lang/String;",
+ (void*) nativeImageURI },
+ { "nativeFocusNodeWantsKeyEvents", "()Z",
+ (void*)nativeFocusNodeWantsKeyEvents },
+ { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
+ (void*) nativeUpdateCachedTextfield },
+ { "nativeMoveSelection", "(IIZ)V",
+ (void*) nativeMoveSelection },
+ { "nativeGetSelection", "()Landroid/graphics/Region;",
+ (void*) nativeGetSelection },
+ { "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
+ (void*) nativeDumpDisplayTree }
+};
+
+int register_webview(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");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
+}
+
+} // namespace android
diff --git a/WebKit/android/plugins/ANPCanvasInterface.cpp b/WebKit/android/plugins/ANPCanvasInterface.cpp
new file mode 100644
index 0000000..ba79691
--- /dev/null
+++ b/WebKit/android/plugins/ANPCanvasInterface.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 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.
+ */
+
+// 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_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_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, getTotalMatrix);
+ ASSIGN(i, getLocalClipBounds);
+ ASSIGN(i, getDeviceClipBounds);
+ ASSIGN(i, drawColor);
+ ASSIGN(i, drawPaint);
+ ASSIGN(i, drawRect);
+ ASSIGN(i, drawOval);
+ ASSIGN(i, drawPath);
+ ASSIGN(i, drawText);
+ ASSIGN(i, drawPosText);
+ ASSIGN(i, drawBitmap);
+ ASSIGN(i, drawBitmapRect);
+}
+
diff --git a/WebKit/android/plugins/ANPKeyCodes.h b/WebKit/android/plugins/ANPKeyCodes.h
new file mode 100644
index 0000000..5213f57
--- /dev/null
+++ b/WebKit/android/plugins/ANPKeyCodes.h
@@ -0,0 +1,125 @@
+/*
+ * 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 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 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 include/ui/KeycodeLabels.h, which contains more than
+ I want to publish to plugin authors.
+*/
+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
+};
+
+#endif
+
diff --git a/WebKit/android/plugins/ANPLogInterface.cpp b/WebKit/android/plugins/ANPLogInterface.cpp
new file mode 100644
index 0000000..6ccca0a
--- /dev/null
+++ b/WebKit/android/plugins/ANPLogInterface.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 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 "plugin"
+
+#include "utils/Log.h"
+#include "android_npapi.h"
+#include <stdarg.h>
+
+static void anp_log(NPP inst, 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/WebKit/android/plugins/ANPMatrixInterface.cpp b/WebKit/android/plugins/ANPMatrixInterface.cpp
new file mode 100644
index 0000000..815b954
--- /dev/null
+++ b/WebKit/android/plugins/ANPMatrixInterface.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+// 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/WebKit/android/plugins/ANPPaintInterface.cpp b/WebKit/android/plugins/ANPPaintInterface.cpp
new file mode 100644
index 0000000..4b561f0
--- /dev/null
+++ b/WebKit/android/plugins/ANPPaintInterface.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 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.
+ */
+
+// 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/WebKit/android/plugins/ANPSoundInterface.cpp b/WebKit/android/plugins/ANPSoundInterface.cpp
new file mode 100644
index 0000000..6b019d1
--- /dev/null
+++ b/WebKit/android/plugins/ANPSoundInterface.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "android_npapi.h"
+
+#include "media/AudioTrack.h"
+
+struct ANPAudioTrack {
+ void* mUser;
+ ANPAudioCallbackProc mProc;
+ android::AudioTrack* mTrack;
+};
+
+static ANPSampleFormat toANPFormat(int fm) {
+ switch (fm) {
+ case android::AudioSystem::PCM_16_BIT:
+ return kPCM16Bit_ANPSampleFormat;
+ case android::AudioSystem::PCM_8_BIT:
+ return kPCM8Bit_ANPSampleFormat;
+ default:
+ return kUnknown_ANPSamleFormat;
+ }
+}
+
+static android::AudioSystem::audio_format fromANPFormat(ANPSampleFormat fm) {
+ switch (fm) {
+ case kPCM16Bit_ANPSampleFormat:
+ return android::AudioSystem::PCM_16_BIT;
+ case kPCM8Bit_ANPSampleFormat:
+ return android::AudioSystem::PCM_8_BIT;
+ default:
+ return android::AudioSystem::INVALID_FORMAT;
+ }
+}
+
+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(android::AudioSystem::MUSIC,
+ sampleRate,
+ fromANPFormat(format),
+ channelCount,
+ 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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
diff --git a/WebKit/android/plugins/ANPTypefaceInterface.cpp b/WebKit/android/plugins/ANPTypefaceInterface.cpp
new file mode 100644
index 0000000..5878f3f
--- /dev/null
+++ b/WebKit/android/plugins/ANPTypefaceInterface.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+
+static ANPTypeface* anp_createFromName(const char name[], ANPTypefaceStyle s) {
+ SkTypeface* tf = SkTypeface::Create(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) {
+ tf->safeRef();
+}
+
+static void anp_unref(ANPTypeface* tf) {
+ tf->safeUnref();
+}
+
+static ANPTypefaceStyle anp_getStyle(const ANPTypeface* tf) {
+ SkTypeface::Style s = tf ? tf->getStyle() : SkTypeface::kNormal;
+ return static_cast<ANPTypefaceStyle>(s);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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);
+}
+
diff --git a/WebKit/android/plugins/ANPWindowInterface.cpp b/WebKit/android/plugins/ANPWindowInterface.cpp
new file mode 100644
index 0000000..4aa862b
--- /dev/null
+++ b/WebKit/android/plugins/ANPWindowInterface.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.h"
+
+static bool anp_lockRect(void* window, const ANPRectI* inval,
+ ANPBitmap* bitmap) {
+ if (window) {
+ // todo
+ return true;
+ }
+ return false;
+}
+
+static bool anp_lockRegion(void* window, const ANPRegion* inval,
+ ANPBitmap* bitmap) {
+ if (window) {
+ // todo
+ return true;
+ }
+ return false;
+}
+
+static void anp_unlock(void* window) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ASSIGN(obj, name) (obj)->name = anp_##name
+
+void ANPWindowInterfaceV0_Init(ANPInterface* value) {
+ ANPWindowInterfaceV0* i = reinterpret_cast<ANPWindowInterfaceV0*>(value);
+
+ ASSIGN(i, lockRect);
+ ASSIGN(i, lockRegion);
+ ASSIGN(i, unlock);
+}
+
diff --git a/WebKit/android/plugins/PluginDebugAndroid.h b/WebKit/android/plugins/PluginDebugAndroid.h
new file mode 100644
index 0000000..992e953
--- /dev/null
+++ b/WebKit/android/plugins/PluginDebugAndroid.h
@@ -0,0 +1,46 @@
+/*
+ * 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 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 PLUGIN_DEBUG_ANDROID_H__
+#define PLUGIN_DEBUG_ANDROID_H__
+
+#ifdef ANDROID_PLUGINS
+
+// 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 direectory.
+#define PLUGIN_DEBUG_GLOBAL 0
+
+#if PLUGIN_DEBUG_GLOBAL || defined(PLUGIN_DEBUG_LOCAL)
+# define PLUGIN_LOG(A, B...) do { LOGI( A , ## B ); } while(0)
+#else
+# define PLUGIN_LOG(A, B...) do { } while(0)
+#endif
+
+#endif
+
+#endif // defined(PLUGIN_DEBUG_ANDROID_H__)
diff --git a/WebKit/android/plugins/PluginTimer.cpp b/WebKit/android/plugins/PluginTimer.cpp
new file mode 100644
index 0000000..1097330
--- /dev/null
+++ b/WebKit/android/plugins/PluginTimer.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 "PluginTimer.h"
+
+namespace WebCore {
+
+ static uint32 gTimerID;
+
+ PluginTimer::PluginTimer(PluginTimer** list, NPP instance, bool repeat,
+ void (*timerFunc)(NPP npp, uint32 timerID))
+ : m_list(list),
+ m_instance(instance),
+ m_timerFunc(timerFunc),
+ m_repeat(repeat)
+ {
+ m_timerID = ++gTimerID;
+
+ m_next = *list;
+ if (m_next) {
+ m_next->m_prev = this;
+ }
+ m_prev = 0;
+ *list = this;
+ }
+
+ 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()
+ {
+ m_timerFunc(m_instance, m_timerID);
+ if (!m_repeat) {
+ delete this;
+ }
+ }
+
+ // may return null if timerID is not found
+ PluginTimer* PluginTimer::Find(PluginTimer* list, uint32 timerID)
+ {
+ PluginTimer* curr = list;
+ while (curr) {
+ if (curr->m_timerID == timerID) {
+ break;
+ }
+ curr = curr->m_next;
+ }
+ return curr;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ PluginTimerList::~PluginTimerList()
+ {
+ while (m_list) {
+ delete m_list;
+ }
+ }
+
+ uint32 PluginTimerList::schedule(NPP instance, uint32 interval, bool repeat,
+ void (*proc)(NPP npp, uint32 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 timerID)
+ {
+ delete PluginTimer::Find(m_list, timerID);
+ }
+
+} // namespace WebCore
diff --git a/WebKit/android/plugins/PluginTimer.h b/WebKit/android/plugins/PluginTimer.h
new file mode 100644
index 0000000..ccb49da
--- /dev/null
+++ b/WebKit/android/plugins/PluginTimer.h
@@ -0,0 +1,78 @@
+/*
+ * 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 PluginTimer_H
+#define PluginTimer_H
+
+#include "Timer.h"
+#include "npapi.h"
+
+namespace WebCore {
+
+ class PluginTimerList;
+
+ class PluginTimer : public TimerBase {
+ public:
+ PluginTimer(PluginTimer** list, NPP instance, bool repeat,
+ void (*proc)(NPP npp, uint32 timerID));
+ virtual ~PluginTimer();
+
+ uint32 timerID() const { return m_timerID; }
+
+ static PluginTimer* Find(PluginTimer* list, uint32 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);
+ uint32 m_timerID;
+ bool m_repeat;
+ };
+
+ class PluginTimerList {
+ public:
+ PluginTimerList() : m_list(0) {}
+ ~PluginTimerList();
+
+ uint32 schedule(NPP instance, uint32 interval, bool repeat,
+ void (*proc)(NPP npp, uint32 timerID));
+ void unschedule(NPP instance, uint32 timerID);
+
+ private:
+ PluginTimer* m_list;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/WebKit/android/plugins/PluginViewBridgeAndroid.cpp b/WebKit/android/plugins/PluginViewBridgeAndroid.cpp
new file mode 100644
index 0000000..945115a
--- /dev/null
+++ b/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 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 "PluginViewBridgeAndroid.h"
+
+namespace WebCore {
+
+ void PluginViewBridgeAndroid::draw(GraphicsContext* gc,
+ const IntRect& rect) {}
+
+ bool PluginViewBridgeAndroid::forPluginView() const {
+ return true;
+ }
+
+}
diff --git a/WebKit/android/plugins/PluginViewBridgeAndroid.h b/WebKit/android/plugins/PluginViewBridgeAndroid.h
new file mode 100644
index 0000000..e17a2dc
--- /dev/null
+++ b/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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 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/WebKit/android/plugins/PluginWidgetAndroid.cpp b/WebKit/android/plugins/PluginWidgetAndroid.cpp
new file mode 100644
index 0000000..ea13a0c
--- /dev/null
+++ b/WebKit/android/plugins/PluginWidgetAndroid.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 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 "android_graphics.h"
+#include "PluginPackage.h"
+#include "PluginView.h"
+#include "PluginWidgetAndroid.h"
+#include "SkANP.h"
+#include "SkFlipPixelRef.h"
+#include "WebViewCore.h"
+
+PluginWidgetAndroid::PluginWidgetAndroid(WebCore::PluginView* view)
+ : m_pluginView(view) {
+ m_flipPixelRef = NULL;
+ m_core = NULL;
+ m_drawingModel = kBitmap_ANPDrawingModel;
+ m_x = m_y = 0;
+}
+
+PluginWidgetAndroid::~PluginWidgetAndroid() {
+ if (m_core) {
+ m_core->removePlugin(this);
+ }
+ m_flipPixelRef->safeUnref();
+}
+
+void PluginWidgetAndroid::init(android::WebViewCore* core) {
+ m_core = core;
+ m_core->addPlugin(this);
+}
+
+static SkBitmap::Config computeConfig(bool isTransparent) {
+ return isTransparent ? SkBitmap::kARGB_8888_Config
+ : SkBitmap::kRGB_565_Config;
+}
+
+void PluginWidgetAndroid::setWindow(int x, int y, int width, int height,
+ bool isTransparent) {
+ m_x = x;
+ m_y = y;
+ m_flipPixelRef->safeUnref();
+ m_flipPixelRef = new SkFlipPixelRef(computeConfig(isTransparent),
+ width, height);
+}
+
+void PluginWidgetAndroid::setDrawingModel(ANPDrawingModel model) {
+ m_drawingModel = model;
+}
+
+void PluginWidgetAndroid::localToPageCoords(SkIRect* rect) const {
+ rect->offset(m_x, m_y);
+}
+
+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();
+ }
+ return true;
+ }
+}
+
+void PluginWidgetAndroid::inval(const WebCore::IntRect& rect,
+ bool signalRedraw) {
+ // nothing to do if we haven't had setWindow() called yet
+ if (NULL == m_flipPixelRef) {
+ return;
+ }
+
+ SkIRect r;
+ m_flipPixelRef->inval(*android_setrect(&r, rect));
+
+ if (signalRedraw && m_flipPixelRef->isDirty()) {
+ m_core->invalPlugin(this);
+ }
+}
+
+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.drawContext.model = m_drawingModel;
+ SkANP::SetRect(&event.data.drawContext.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.drawContext.data.bitmap,
+ bitmap) &&
+ pkg->pluginFuncs()->event(instance, &event)) {
+
+ if (canvas) {
+ SkBitmap bm(bitmap);
+ bm.setPixelRef(m_flipPixelRef);
+ canvas->drawBitmap(bm, SkIntToScalar(m_x),
+ SkIntToScalar(m_y), NULL);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
diff --git a/WebKit/android/plugins/PluginWidgetAndroid.h b/WebKit/android/plugins/PluginWidgetAndroid.h
new file mode 100644
index 0000000..f5e9363
--- /dev/null
+++ b/WebKit/android/plugins/PluginWidgetAndroid.h
@@ -0,0 +1,94 @@
+/*
+ * 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 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 PluginWidgetAndroid_H
+#define PluginWidgetAndroid_H
+
+#include "android_npapi.h"
+
+namespace WebCore {
+ class PluginView;
+}
+
+namespace android {
+ 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();
+
+ /* 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(int x, int y, int width, int height, bool isTransparent);
+ /* Called whenever the plugin itself requests a new drawing model
+ */
+ void setDrawingModel(ANPDrawingModel);
+
+ /* Utility method to convert from local (plugin) coordinates to docuemnt
+ coordinates. Needed (for instance) to convert the dirty rectangle into
+ document coordinates to inturn inval the screen.
+ */
+ void localToPageCoords(SkIRect*) const;
+
+ /* Returns true (and optionally updates rect with the dirty bounds) 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);
+
+private:
+ WebCore::PluginView* m_pluginView;
+ android::WebViewCore* m_core;
+ SkFlipPixelRef* m_flipPixelRef;
+ ANPDrawingModel m_drawingModel;
+ int m_x;
+ int m_y;
+};
+
+#endif
diff --git a/WebKit/android/plugins/SkANP.cpp b/WebKit/android/plugins/SkANP.cpp
new file mode 100644
index 0000000..3912f99
--- /dev/null
+++ b/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 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.
+ */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include "config.h"
+#include "SkANP.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/WebKit/android/plugins/SkANP.h b/WebKit/android/plugins/SkANP.h
new file mode 100644
index 0000000..f319c9b
--- /dev/null
+++ b/WebKit/android/plugins/SkANP.h
@@ -0,0 +1,80 @@
+/*
+ * 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 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 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/WebKit/android/plugins/android_npapi.h b/WebKit/android/plugins/android_npapi.h
new file mode 100644
index 0000000..f64c8ce
--- /dev/null
+++ b/WebKit/android/plugins/android_npapi.h
@@ -0,0 +1,620 @@
+/*
+ * 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 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.
+ */
+
+/* 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 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 kTypefaceInterfaceV0_ANPGetValue ((NPNVariable)1005)
+#define kWindowInterfaceV0_ANPGetValue ((NPNVariable)1006)
+
+/* queries for which drawing model is desired (for the draw event)
+
+ Should be called inside NPP_New(...)
+
+ NPN_GetValue(inst, ANPSupportedDrawingModel_EnumValue, uint32_t* bits)
+ */
+#define kSupportedDrawingModel_ANPGetValue ((NPNVariable)2000)
+
+///////////////////////////////////////////////////////////////////////////////
+// NPN_GetValue
+
+/** Reqeust to set the drawing model.
+
+ NPN_SetValue(inst, ANPRequestDrawingModel_EnumValue, (void*)foo_DrawingModel)
+ */
+#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 = 0,
+};
+typedef int32_t ANPDrawingModel;
+
+/* Interfaces provide additional functionality to the plugin via function ptrs.
+ Once an interface is retrived, 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)(NPP instance, ANPLogType, const char format[], ...);
+};
+
+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);
+};
+
+typedef uint32_t ANPColor;
+#define ANP_MAKE_COLOR(a, r, g, b) \
+ (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+
+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;
+
+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
+
+ @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*);
+};
+
+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 (*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 {
+ /** Given the window field from the NPWindow struct, and an optional rect
+ describing the subset of the window that will be drawn to (may be null)
+ return true if the bitmap for that window can be accessed, and if so,
+ fill out the specified ANPBitmap to point to the window's pixels.
+
+ When drawing is complete, call unlock(window)
+ */
+ bool (*lockRect)(void* window, const ANPRectI* inval, ANPBitmap*);
+ /** The same as lockRect, but takes a region instead of a rect to specify
+ the area that will be changed/drawn.
+ */
+ bool (*lockRegion)(void* window, const ANPRegion* inval, ANPBitmap*);
+ /** Given a successful call to lock(window, inval, &bitmap), call unlock
+ to release access to the pixels, and allow the browser to display the
+ results. If lock returned false, unlock should not be called.
+ */
+ void (*unlock)(void* window);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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.
+ */
+ ANPAudioTrack* (*newTrack)(uint32_t sampleRate, // sampling rate in Hz
+ ANPSampleFormat,
+ int channelCount, // MONO=1, STEREO=2
+ ANPAudioCallbackProc,
+ void* user);
+ 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*);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HandleEvent
+
+enum ANPEventTypes {
+ kNull_ANPEventType = 0,
+ kKey_ANPEventType = 1,
+ kTouch_ANPEventType = 2,
+ kDraw_ANPEventType = 3,
+};
+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 ANPTouchActions {
+ kDown_ANPTouchAction = 0,
+ kUp_ANPTouchAction = 1,
+};
+typedef int32_t ANPTouchAction;
+
+struct ANPDrawContext {
+ ANPDrawingModel model;
+ // relative to (0,0) in top-left of your plugin
+ ANPRectI clip;
+ // use based on the value in model
+ union {
+ ANPBitmap bitmap;
+ } data;
+};
+
+/* 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 {
+ ANPTouchAction action;
+ ANPKeyModifier modifiers;
+ int32_t x; // relative to your "window" (0...width)
+ int32_t y; // relative to your "window" (0...height)
+ } touch;
+ ANPDrawContext drawContext;
+ int32_t other[8];
+ } data;
+};
+
+#endif
+
diff --git a/WebKit/android/plugins/sample/Android.mk b/WebKit/android/plugins/sample/Android.mk
new file mode 100644
index 0000000..328ddc5
--- /dev/null
+++ b/WebKit/android/plugins/sample/Android.mk
@@ -0,0 +1,50 @@
+##
+##
+## 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 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 \
+ PluginObject.cpp \
+ pluginGraphics.cpp
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH) \
+ external/webkit/WebCore/bridge \
+ external/webkit/WebCore/plugins \
+ external/webkit/WebCore/platform/android/JavaVM \
+ external/webkit/WebKit/android/plugins
+
+LOCAL_SRC_FILES := $(LOCAL_SRC_FILES)
+LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_PRELINK_MODULE:=false
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_MODULE:= browsertestplugin
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/WebKit/android/plugins/sample/PluginObject.cpp b/WebKit/android/plugins/sample/PluginObject.cpp
new file mode 100644
index 0000000..5499072
--- /dev/null
+++ b/WebKit/android/plugins/sample/PluginObject.cpp
@@ -0,0 +1,178 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "main.h"
+#include "PluginObject.h"
+
+static void pluginInvalidate(NPObject *obj);
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
+static void pluginDeallocate(NPObject *obj);
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+
+
+
+static NPClass pluginClass = {
+ NP_CLASS_STRUCT_VERSION,
+ pluginAllocate,
+ pluginDeallocate,
+ pluginInvalidate,
+ pluginHasMethod,
+ pluginInvoke,
+ pluginInvokeDefault,
+ pluginHasProperty,
+ pluginGetProperty,
+ pluginSetProperty,
+ pluginRemoveProperty,
+ pluginEnumerate
+};
+
+NPClass *getPluginClass(void)
+{
+ return &pluginClass;
+}
+
+static bool identifiersInitialized = false;
+
+#define ID_TESTFILE_PROPERTY 0
+#define NUM_PROPERTY_IDENTIFIERS 1
+
+static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
+static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
+ "testfile"
+};
+
+#define ID_GETTESTFILE_METHOD 0
+#define NUM_METHOD_IDENTIFIERS 1
+
+static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "getTestFile"
+};
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
+ browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
+}
+
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
+ if (name == pluginPropertyIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
+ if (name == pluginMethodIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
+ BOOLEAN_TO_NPVARIANT(true, *variant);
+ return true;
+ }
+ return false;
+}
+
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
+{
+ return false;
+}
+
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
+ return true;
+ }
+ return false;
+}
+
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ return false;
+}
+
+static void pluginInvalidate(NPObject *obj)
+{
+ // Release any remaining references to JavaScript objects.
+}
+
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
+{
+ PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
+ newInstance->header._class = theClass;
+ newInstance->header.referenceCount = 1;
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ newInstance->npp = npp;
+
+ return &newInstance->header;
+}
+
+static void pluginDeallocate(NPObject *obj)
+{
+ free(obj);
+}
+
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
+{
+ return false;
+}
+
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
+{
+ return false;
+}
+
diff --git a/WebKit/android/plugins/sample/PluginObject.h b/WebKit/android/plugins/sample/PluginObject.h
new file mode 100644
index 0000000..ae8963d
--- /dev/null
+++ b/WebKit/android/plugins/sample/PluginObject.h
@@ -0,0 +1,70 @@
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PluginObject__DEFINED
+#define PluginObject__DEFINED
+
+#include "npapi.h"
+
+struct ANPCanvas;
+struct ANPAudioTrack;
+
+class Animation {
+public:
+ Animation(NPP inst) : m_inst(inst) {}
+ virtual ~Animation() {}
+ virtual void draw(ANPCanvas*) = 0;
+
+ NPP inst() const { return m_inst; }
+
+private:
+ NPP m_inst;
+};
+
+typedef struct PluginObject {
+ NPObject header;
+ NPP npp;
+ NPWindow* window;
+ Animation* anim;
+ ANPAudioTrack* track;
+ int32_t mUnichar;
+
+ bool mTestTimers;
+ uint32_t mStartTime;
+ uint32_t mPrevTime;
+ int mTimerCount;
+} PluginObject;
+
+NPClass *getPluginClass(void);
+
+#endif // PluginObject__DEFINED
diff --git a/WebKit/android/plugins/sample/main.cpp b/WebKit/android/plugins/sample/main.cpp
new file mode 100644
index 0000000..e34cee6
--- /dev/null
+++ b/WebKit/android/plugins/sample/main.cpp
@@ -0,0 +1,435 @@
+/*
+ * 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 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "main.h"
+#include "PluginObject.h"
+#include "pluginGraphics.h"
+#include "android_npapi.h"
+
+NPNetscapeFuncs* browser;
+#define EXPORT __attribute__((visibility("default")))
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved);
+NPError NPP_Destroy(NPP instance, NPSavedData** save);
+NPError NPP_SetWindow(NPP instance, NPWindow* window);
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+int32 NPP_WriteReady(NPP instance, NPStream* stream);
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
+ void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16 NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
+ void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
+
+extern "C" {
+EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context);
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
+EXPORT const char* NP_GetMIMEDescription(void);
+EXPORT void NP_Shutdown(void);
+};
+
+ANPAudioTrackInterfaceV0 gSoundI;
+ANPCanvasInterfaceV0 gCanvasI;
+ANPLogInterfaceV0 gLogI;
+ANPPaintInterfaceV0 gPaintI;
+ANPTypefaceInterfaceV0 gTypefaceI;
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+
+NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context)
+{
+ // Make sure we have a function table equal or larger than we are built against.
+ if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Copy the function table (structure)
+ browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
+ memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
+
+ // Build the plugin function table
+ pluginFuncs->version = 11;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ static const struct {
+ NPNVariable v;
+ uint32_t size;
+ ANPInterface* i;
+ } gPairs[] = {
+ { kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
+ { kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
+ { kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
+ { kTypefaceInterfaceV0_ANPGetValue, sizeof(gPaintI), &gTypefaceI },
+ { kAudioTrackInterfaceV0_ANPGetValue, sizeof(gSoundI), &gSoundI },
+ };
+ for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
+ gPairs[i].i->inSize = gPairs[i].size;
+ NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void NP_Shutdown(void)
+{
+
+}
+
+const char *NP_GetMIMEDescription(void)
+{
+ return "application/x-testplugin:tst:Test plugin mimetype is application/x-testplugin";
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved)
+{
+ PluginObject *obj = NULL;
+
+ // Scripting functions appeared in NPAPI version 14
+ if (browser->version >= 14) {
+ instance->pdata = browser->createobject (instance, getPluginClass());
+ obj = static_cast<PluginObject*>(instance->pdata);
+ bzero(obj, sizeof(*obj));
+ }
+
+ uint32_t bits;
+ NPError err = browser->getvalue(instance, kSupportedDrawingModel_ANPGetValue, &bits);
+ if (err) {
+ gLogI.log(instance, kError_ANPLogType, "supported model err %d", err);
+ return err;
+ }
+
+ ANPDrawingModel model = kBitmap_ANPDrawingModel;
+
+ int count = argc;
+ for (int i = 0; i < count; i++) {
+ if (!strcmp(argn[i], "DrawingModel")) {
+ if (!strcmp(argv[i], "Bitmap")) {
+ model = kBitmap_ANPDrawingModel;
+ }
+ if (!strcmp(argv[i], "Canvas")) {
+ // obj->mTestTimers = true;
+ }
+ gLogI.log(instance, kDebug_ANPLogType, "------ %p DrawingModel is %d", instance, model);
+ break;
+ }
+ }
+
+ // comment this out to draw via bitmaps (the default)
+ err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
+ reinterpret_cast<void*>(model));
+ if (err) {
+ gLogI.log(instance, kError_ANPLogType, "request model %d err %d", model, err);
+ }
+ return err;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ delete obj->anim;
+ gSoundI.deleteTrack(obj->track);
+
+ return NPERR_NO_ERROR;
+}
+
+static void timer_oneshot(NPP instance, uint32 timerID) {
+ gLogI.log(instance, kDebug_ANPLogType, "-------- oneshot timer\n");
+}
+
+static int gTimerRepeatCount;
+static void timer_repeat(NPP instance, uint32 timerID) {
+
+ gLogI.log(instance, kDebug_ANPLogType, "-------- repeat timer %d\n",
+ gTimerRepeatCount);
+ if (--gTimerRepeatCount == 0) {
+ browser->unscheduletimer(instance, timerID);
+ }
+}
+
+static void timer_neverfires(NPP instance, uint32 timerID) {
+ gLogI.log(instance, kError_ANPLogType, "-------- timer_neverfires!!!\n");
+}
+
+#define TIMER_INTERVAL 50
+
+static void timer_latency(NPP instance, uint32 timerID) {
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ obj->mTimerCount += 1;
+
+ uint32_t now = getMSecs();
+ uint32_t interval = now - obj->mPrevTime;
+
+ uint32_t dur = now - obj->mStartTime;
+ uint32_t expectedDur = obj->mTimerCount * TIMER_INTERVAL;
+ int32_t drift = dur - expectedDur;
+ int32_t aveDrift = drift / obj->mTimerCount;
+
+ obj->mPrevTime = now;
+
+ gLogI.log(instance, kDebug_ANPLogType,
+ "-------- latency test: [%3d] interval %d expected %d, total %d expected %d, drift %d ave %d\n",
+ obj->mTimerCount, interval, TIMER_INTERVAL, dur, expectedDur,
+ drift, aveDrift);
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* window)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
+ if (obj != NULL) {
+ obj->window = window;
+ }
+
+ static bool gTestTimers;
+ if (!gTestTimers) {
+ gTestTimers = true;
+ // test for bogus timerID
+ browser->unscheduletimer(instance, 999999);
+ // test oneshot
+ browser->scheduletimer(instance, 100, false, timer_oneshot);
+ // test repeat
+ gTimerRepeatCount = 10;
+ browser->scheduletimer(instance, 50, true, timer_repeat);
+ // test unschedule immediately
+ uint32 id = browser->scheduletimer(instance, 100, false, timer_neverfires);
+ browser->unscheduletimer(instance, id);
+ // test double unschedlue (should be no-op)
+ browser->unscheduletimer(instance, id);
+ }
+
+ if (obj->mTestTimers) {
+ browser->scheduletimer(instance, TIMER_INTERVAL, true, timer_latency);
+ obj->mStartTime = obj->mPrevTime = getMSecs();
+ obj->mTestTimers = false;
+ }
+
+ browser->invalidaterect(instance, NULL);
+
+ return NPERR_NO_ERROR;
+}
+
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+ *stype = NP_ASFILEONLY;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
+{
+ return NPERR_NO_ERROR;
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream* stream)
+{
+ return 0;
+}
+
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+ return 0;
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+
+}
+
+struct SoundPlay {
+ NPP instance;
+ ANPAudioTrack* track;
+ FILE* file;
+};
+
+static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) {
+ switch (evt) {
+ case kMoreData_ANPAudioEvent: {
+ SoundPlay* play = reinterpret_cast<SoundPlay*>(user);
+ size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file);
+ buffer->size = amount;
+ if (amount == 0) {
+ gSoundI.stop(play->track);
+ fclose(play->file);
+ play->file = NULL;
+ // need to notify our main thread to delete the track now
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static ANPAudioTrack* createTrack(NPP instance, const char path[]) {
+ FILE* f = fopen(path, "r");
+ gLogI.log(instance, kWarning_ANPLogType, "--- path %s FILE %p", path, f);
+ if (NULL == f) {
+ return NULL;
+ }
+ SoundPlay* play = new SoundPlay;
+ play->file = f;
+ play->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, play);
+ if (NULL == play->track) {
+ fclose(f);
+ delete play;
+ return NULL;
+ }
+ return play->track;
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event)
+{
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
+
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ switch (evt->data.drawContext.model) {
+ case kBitmap_ANPDrawingModel:
+ drawPlugin(instance, evt->data.drawContext.data.bitmap,
+ evt->data.drawContext.clip);
+ return 1;
+ default:
+ break; // unknown drawing model
+ }
+
+ case kKey_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Key action=%d"
+ " code=%d vcode=%d unichar=%d repeat=%d mods=%x", instance,
+ evt->data.key.action,
+ evt->data.key.nativeCode,
+ evt->data.key.virtualCode,
+ evt->data.key.unichar,
+ evt->data.key.repeatCount,
+ evt->data.key.modifiers);
+ if (evt->data.key.action == kDown_ANPKeyAction) {
+ obj->mUnichar = evt->data.key.unichar;
+ browser->invalidaterect(instance, NULL);
+ }
+ return 1;
+
+ case kTouch_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Touch action=%d [%d %d]",
+ instance, evt->data.touch.action, evt->data.touch.x,
+ evt->data.touch.y);
+ if (kUp_ANPTouchAction == evt->data.touch.action) {
+ if (obj->track) {
+ if (gSoundI.isStopped(obj->track)) {
+ gSoundI.start(obj->track);
+ } else {
+ gSoundI.pause(obj->track);
+ }
+ } else {
+ obj->track = createTrack(instance, "/sdcard/sample.snd");
+ gLogI.log(instance, kDebug_ANPLogType, "track %p %d",
+ obj->track, gSoundI.isStopped(obj->track));
+ gSoundI.start(obj->track);
+ gLogI.log(instance, kDebug_ANPLogType, "track %p %d",
+ obj->track, gSoundI.isStopped(obj->track));
+ }
+ }
+ return 1;
+
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+
+}
+
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
+
+ if (variable == NPPVpluginNameString) {
+ const char **str = (const char **)value;
+ *str = "Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ if (variable == NPPVpluginDescriptionString) {
+ const char **str = (const char **)value;
+ *str = "Description of Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+ if (variable == NPPVpluginScriptableNPObject) {
+ void **v = (void **)value;
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ if (obj)
+ browser->retainobject((NPObject*)obj);
+
+ *v = obj;
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ return NPERR_GENERIC_ERROR;
+}
+
diff --git a/WebKit/android/plugins/sample/main.h b/WebKit/android/plugins/sample/main.h
new file mode 100644
index 0000000..8bf520e
--- /dev/null
+++ b/WebKit/android/plugins/sample/main.h
@@ -0,0 +1,30 @@
+/*
+ * 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 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 <npapi.h>
+#include <npfunctions.h>
+#include <npruntime.h>
+
+extern NPNetscapeFuncs* browser;
diff --git a/WebKit/android/plugins/sample/pluginGraphics.cpp b/WebKit/android/plugins/sample/pluginGraphics.cpp
new file mode 100644
index 0000000..ffa43e5
--- /dev/null
+++ b/WebKit/android/plugins/sample/pluginGraphics.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 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 "pluginGraphics.h"
+
+#include "android_npapi.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+
+static void inval(NPP instance) {
+ browser->invalidaterect(instance, NULL);
+}
+
+static uint16 rnd16(float x, int inset) {
+ int ix = (int)roundf(x) + inset;
+ if (ix < 0) {
+ ix = 0;
+ }
+ return static_cast<uint16>(ix);
+}
+
+static void inval(NPP instance, const ANPRectF& r, bool doAA) {
+ const int inset = doAA ? -1 : 0;
+
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ NPRect inval;
+ inval.left = rnd16(r.left, inset);
+ inval.top = rnd16(r.top, inset);
+ inval.right = rnd16(r.right, -inset);
+ inval.bottom = rnd16(r.bottom, -inset);
+ browser->invalidaterect(instance, &inval);
+}
+
+uint32_t getMSecs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class BallAnimation : public Animation {
+public:
+ BallAnimation(NPP inst);
+ virtual ~BallAnimation();
+ virtual void draw(ANPCanvas*);
+private:
+ float m_x;
+ float m_y;
+ float m_dx;
+ float m_dy;
+
+ ANPRectF m_oval;
+ ANPPaint* m_paint;
+
+ static const float SCALE = 0.1;
+};
+
+BallAnimation::BallAnimation(NPP inst) : Animation(inst) {
+ m_x = m_y = 0;
+ m_dx = 7 * SCALE;
+ m_dy = 5 * SCALE;
+
+ memset(&m_oval, 0, sizeof(m_oval));
+
+ m_paint = gPaintI.newPaint();
+ gPaintI.setFlags(m_paint, gPaintI.getFlags(m_paint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paint, 0xFFFF0000);
+ gPaintI.setTextSize(m_paint, 24);
+
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(m_paint, tf);
+ gTypefaceI.unref(tf);
+}
+
+BallAnimation::~BallAnimation() {
+ gPaintI.deletePaint(m_paint);
+}
+
+static void bounce(float* x, float* dx, const float max) {
+ *x += *dx;
+ if (*x < 0) {
+ *x = 0;
+ if (*dx < 0) {
+ *dx = -*dx;
+ }
+ } else if (*x > max) {
+ *x = max;
+ if (*dx > 0) {
+ *dx = -*dx;
+ }
+ }
+}
+
+void BallAnimation::draw(ANPCanvas* canvas) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ const float OW = 20;
+ const float OH = 20;
+
+ inval(instance, m_oval, true); // inval the old
+ m_oval.left = m_x;
+ m_oval.top = m_y;
+ m_oval.right = m_x + OW;
+ m_oval.bottom = m_y + OH;
+ inval(instance, m_oval, true); // inval the new
+
+ gCanvasI.drawColor(canvas, 0xFFFFFFFF);
+
+ gPaintI.setColor(m_paint, 0xFFFF0000);
+ gCanvasI.drawOval(canvas, &m_oval, m_paint);
+
+ bounce(&m_x, &m_dx, obj->window->width - OW);
+ bounce(&m_y, &m_dy, obj->window->height - OH);
+
+ if (obj->mUnichar) {
+ ANPFontMetrics fm;
+ gPaintI.getFontMetrics(m_paint, &fm);
+
+ gPaintI.setColor(m_paint, 0xFF0000FF);
+ char c = static_cast<char>(obj->mUnichar);
+ gCanvasI.drawText(canvas, &c, 1, 10, -fm.fTop, m_paint);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void drawPlugin(NPP instance, const ANPBitmap& bitmap, const ANPRectI& clip) {
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ ANPRectF clipR;
+ clipR.left = clip.left;
+ clipR.top = clip.top;
+ clipR.right = clip.right;
+ clipR.bottom = clip.bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+
+ drawPlugin(instance, canvas);
+
+ gCanvasI.deleteCanvas(canvas);
+}
+
+void drawPlugin(NPP instance, ANPCanvas* canvas) {
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ if (obj->anim == NULL) {
+ obj->anim = new BallAnimation(instance);
+ }
+ obj->anim->draw(canvas);
+}
+
diff --git a/WebKit/android/plugins/sample/pluginGraphics.h b/WebKit/android/plugins/sample/pluginGraphics.h
new file mode 100644
index 0000000..4dceb26
--- /dev/null
+++ b/WebKit/android/plugins/sample/pluginGraphics.h
@@ -0,0 +1,40 @@
+/*
+ * 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 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 "main.h" // for NPAPI definitions
+#include "PluginObject.h"
+
+#ifndef pluginGraphics__DEFINED
+#define pluginGraphics__DEFINED
+
+struct ANPBitmap;
+struct ANPCanvas;
+struct ANPRectI;
+
+void drawPlugin(NPP instance, const ANPBitmap& bitmap, const ANPRectI& clip);
+void drawPlugin(NPP instance, ANPCanvas*);
+uint32_t getMSecs();
+
+#endif // pluginGraphics__DEFINED
diff --git a/WebKit/android/sort.cpp b/WebKit/android/sort.cpp
new file mode 100644
index 0000000..de7aa6c
--- /dev/null
+++ b/WebKit/android/sort.cpp
@@ -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 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"
+
+namespace std
+{
+ void sort(const void** start, const void** end, const void** temp, Comparator comp)
+ {
+ size_t endlen = end - start;
+ size_t midlen = endlen / 2;
+ const void** mid = start + midlen;
+ if (midlen > 1)
+ sort(start, mid, temp, comp);
+ if (end - mid > 1)
+ sort(mid, end, temp, comp);
+ memcpy(temp, start, midlen * sizeof(*start));
+ size_t i = 0;
+ size_t j = midlen;
+ size_t off = 0;
+ while (i < midlen && j < endlen)
+ start[off++] = (*comp)(start[j], temp[i]) ? start[j++] : temp[i++];
+ if (i < midlen)
+ memcpy(&start[off], &temp[i], (midlen - i) * sizeof(*start));
+ }
+
+ void sort(const void** start, const void** end, Comparator comp) {
+ if (end - start > 1) {
+ const void** temp = new sortType[(end - start) / 2];
+ sort(start, end, temp, comp);
+ delete[] temp;
+ }
+ }
+}
diff --git a/WebKit/android/stl/algorithm b/WebKit/android/stl/algorithm
new file mode 100644
index 0000000..8255def
--- /dev/null
+++ b/WebKit/android/stl/algorithm
@@ -0,0 +1,274 @@
+#ifdef __cplusplus
+
+/*
+ *
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+ // extracted from stl_algobase.h
+ // full STL is not compatible with the ARM build
+ // a limited number of STL functions is used by webkit: swap, max, and min are
+ // included below for webkit compatibility
+
+#ifdef __GLIBCPP_INTERNAL_ALGOBASE_H
+#error "real STL defined"
+#endif
+
+#ifndef __ANDROID_ALGORITHM
+#define __ANDROID_ALGORITHM
+
+#ifndef __ANDROID_LIMITS
+#include <limits>
+#endif
+
+#ifndef _CPP_UTILITY
+#include <utility>
+#endif
+
+#include <SkScalar.h> // for SK_ScalarNaN
+#ifdef PREFIX_FOR_WEBCORE
+#include <SkTSearch.h>
+namespace WebCore {
+ class InlineTextBox;
+ class RenderLayer;
+}
+#endif
+
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+
+#ifndef WCHAR_MAX
+ #define WCHAR_MAX 0xFFFF
+#endif
+
+namespace JSC {
+ class ProfileNode;
+}
+
+namespace WTF {
+ template <typename T> class RefPtr;
+}
+
+namespace std
+{
+ template<typename _Tp>
+ inline void
+ swap(_Tp& __a, _Tp& __b)
+ {
+ _Tp __tmp = __a;
+ __a = __b;
+ __b = __tmp;
+ }
+
+ #undef min
+ #undef max
+
+ template<typename _Tp>
+ inline const _Tp&
+ min(const _Tp& __a, const _Tp& __b)
+ {
+ return __b < __a ? __b : __a;
+ }
+
+ template<typename _Tp>
+ inline const _Tp&
+ max(const _Tp& __a, const _Tp& __b)
+ {
+ return __a < __b ? __b : __a;
+ }
+
+template <class _InputIter, class _OutputIter>
+inline _OutputIter copy(_InputIter __first, _InputIter __last,
+ _OutputIter __result)
+{
+ for (size_t __n = __last - __first; __n > 0; --__n) {
+ *__result = *__first;
+ ++__first;
+ ++__result;
+ }
+ return __result;
+}
+
+template <class _ForwardIter, class _Tp>
+void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) {
+ for ( ; __first != __last; ++__first)
+ *__first = __value;
+}
+
+#ifndef UINTPTR_MAX
+#define UINTPTR_MAX UINT32_MAX
+#endif
+
+#ifndef UINT32_MAX
+#define UINT32_MAX (0xffffffff)
+#endif
+
+template <typename T>
+struct numeric_limits {
+ /// Returns the minimum value for type T.
+ static inline T min (void) { return (T(0)); }
+ /// Returns the minimum value for type T.
+ static inline T max (void) { return (T(0)); }
+ static const bool is_signed = false; ///< True if the type is signed.
+ static const bool is_integer = false; ///< True if stores an exact value.
+ static const bool is_integral = false; ///< True if fixed size and cast-copyable.
+};
+
+template <typename T>
+struct numeric_limits<T*> {
+ static inline T* min (void) { return (NULL); }
+ static inline T* max (void) { return (UINTPTR_MAX); }
+ static const bool is_signed = false;
+ static const bool is_integer = true;
+ static const bool is_integral = true;
+};
+
+#define _NUMERIC_LIMITS(type, minVal, maxVal, quietNaN, bSigned, bInteger, bIntegral) \
+template <> \
+struct numeric_limits<type> { \
+ static inline type infinity (void) { return (maxVal); } \
+ static inline type min (void) { return (minVal); } \
+ static inline type max (void) { return (maxVal); } \
+ static inline type quiet_NaN() { return (quietNaN); } \
+ static const bool is_signed = bSigned; \
+ static const bool is_integer = bInteger; \
+ static const bool is_integral = bIntegral; \
+}
+
+//--------------------------------------------------------------------------------------
+// type min max NaN signed integer integral
+//--------------------------------------------------------------------------------------
+_NUMERIC_LIMITS (bool, false, true, 0, false, true, true);
+_NUMERIC_LIMITS (char, SCHAR_MIN, SCHAR_MAX, 0, true, true, true);
+_NUMERIC_LIMITS (int, INT_MIN, INT_MAX, 0, true, true, true);
+_NUMERIC_LIMITS (short, SHRT_MIN, SHRT_MAX, 0, true, true, true);
+_NUMERIC_LIMITS (long, LONG_MIN, LONG_MAX, 0, true, true, true);
+#if HAVE_THREE_CHAR_TYPES
+_NUMERIC_LIMITS (signed char, SCHAR_MIN, SCHAR_MAX, 0, true, true, true);
+#endif
+_NUMERIC_LIMITS (unsigned char, 0, UCHAR_MAX, 0, false, true, true);
+_NUMERIC_LIMITS (unsigned int, 0, UINT_MAX, 0, false, true, true);
+_NUMERIC_LIMITS (unsigned short,0, USHRT_MAX, 0, false, true, true);
+_NUMERIC_LIMITS (unsigned long, 0, ULONG_MAX, 0, false, true, true);
+_NUMERIC_LIMITS (wchar_t, 0, WCHAR_MAX, 0, false, true, true);
+_NUMERIC_LIMITS (float, FLT_MIN, FLT_MAX, SK_ScalarNaN, true, false, true);
+_NUMERIC_LIMITS (double, DBL_MIN, DBL_MAX, SK_ScalarNaN, true, false, true);
+_NUMERIC_LIMITS (long double, LDBL_MIN, LDBL_MAX, SK_ScalarNaN, true, false, true);
+#ifdef HAVE_LONG_LONG
+_NUMERIC_LIMITS (long long, LLONG_MIN, LLONG_MAX, 0, true, true, true);
+_NUMERIC_LIMITS (unsigned long long, 0, ULLONG_MAX, 0, false, true, true);
+#endif
+//--------------------------------------------------------------------------------------
+
+typedef int ptrdiff_t;
+
+typedef bool (* Comparator)(const void*, const void*);
+extern void sort(const void** start, const void** end, Comparator comp);
+
+#ifdef PREFIX_FOR_WEBCORE
+ typedef const void* sortType;
+
+ inline bool binary_search(const unsigned short* const base,
+ const unsigned short* const end,
+ short target)
+ {
+ return SkTSearch<unsigned short>(base, end - base, target, sizeof(unsigned short)) >= 0;
+ }
+
+
+ inline void sort (WebCore::InlineTextBox** start, WebCore::InlineTextBox**end,
+ bool (* comp)(const WebCore::InlineTextBox*, const WebCore::InlineTextBox*))
+ {
+ sort((const void**) start, (const void**) end, (Comparator) comp);
+ }
+
+ template<typename P> inline void stable_sort(P** start, P** end,
+ bool (* comp)(P*, P*))
+ {
+ sort((const void**) start, (const void**) end, (Comparator) comp);
+ }
+
+ template<typename P> inline void stable_sort(P** start, P** end,
+ bool (& comp)(const P*, const P*))
+ {
+ sort((const void**) start, (const void**) end, (Comparator) &comp);
+ }
+
+ template<typename P> void stable_sort(P* start, P* end, P* temp,
+ bool (& comp)(const P&, const P&)) {
+ size_t endlen = end - start;
+ size_t midlen = endlen / 2;
+ P* mid = start + midlen;
+ if (midlen > 1)
+ stable_sort(start, mid, temp, comp);
+ if (end - mid > 1)
+ stable_sort(mid, end, temp, comp);
+ memcpy(temp, start, midlen * sizeof(*start));
+ size_t i = 0;
+ size_t j = midlen;
+ size_t off = 0;
+ while (i < midlen && j < endlen) {
+ P* dst = (comp)(start[j], temp[i]) ? &start[j++] : &temp[i++];
+ memcpy(&start[off++], dst, sizeof(*start));
+ }
+ if (i < midlen)
+ memcpy(&start[off], &temp[i], (midlen - i) * sizeof(*start));
+ }
+
+ template<typename P> void stable_sort(P* start, P* end,
+ bool (& comp)(const P&, const P&)) {
+ if (end - start > 1) {
+ size_t count = (end - start) / 2;
+ P* temp = static_cast<P*>(malloc(count * sizeof(P)));
+ stable_sort(start, end, temp, comp);
+ free(temp);
+ }
+ }
+
+ template<typename P> void stable_sort(P* start, P* end,
+ bool (& comp)(P, P)) {
+ stable_sort(start, end, (bool (&)(const P&, const P&))(comp));
+ }
+
+ class ostream {
+ int this_class_intentionally_left_empty;
+ };
+#endif
+
+typedef bool (* ProfileComparator)(const WTF::RefPtr<JSC::ProfileNode>&,
+ const WTF::RefPtr<JSC::ProfileNode>&);
+
+inline void sort(WTF::RefPtr<JSC::ProfileNode>* start,
+ WTF::RefPtr<JSC::ProfileNode>* end, ProfileComparator comp)
+{
+ sort((const void**) start, (const void**) end, (Comparator) comp);
+}
+
+}
+
+#endif
+
+#endif // __cplusplus
+
diff --git a/WebKit/android/stl/concept_checks.h b/WebKit/android/stl/concept_checks.h
new file mode 100644
index 0000000..72cc138
--- /dev/null
+++ b/WebKit/android/stl/concept_checks.h
@@ -0,0 +1,812 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (c) 1999
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+#ifndef __CONCEPT_CHECKS_H
+#define __CONCEPT_CHECKS_H
+
+/*
+ Use these macro like assertions, but they assert properties
+ on types (usually template arguments). In technical terms they
+ verify whether a type "models" a "concept".
+
+ This set of requirements and the terminology used here is derived
+ from the book "Generic Programming and the STL" by Matt Austern
+ (Addison Wesley). For further information please consult that
+ book. The requirements also are intended to match the ANSI/ISO C++
+ standard.
+
+ This file covers the basic concepts and the iterator concepts.
+ There are several other files that provide the requirements
+ for the STL containers:
+ container_concepts.h
+ sequence_concepts.h
+ assoc_container_concepts.h
+
+ Jeremy Siek, 1999
+
+ TO DO:
+ - some issues with regards to concept classification and mutability
+ including AssociativeContianer -> ForwardContainer
+ and SortedAssociativeContainer -> ReversibleContainer
+ - HashedAssociativeContainer
+ - Allocator
+ - Function Object Concepts
+
+ */
+
+#ifndef __STL_USE_CONCEPT_CHECKS
+
+// Some compilers lack the features that are necessary for concept checks.
+// On those compilers we define the concept check macros to do nothing.
+#define __STL_REQUIRES(__type_var, __concept) do {} while(0)
+#define __STL_CLASS_REQUIRES(__type_var, __concept) \
+ static int __##__type_var##_##__concept
+#define __STL_CONVERTIBLE(__type_x, __type_y) do {} while(0)
+#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) do {} while(0)
+#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y) \
+ static int __##__type_x##__type_y##_require_same_type
+#define __STL_GENERATOR_CHECK(__func, __ret) do {} while(0)
+#define __STL_CLASS_GENERATOR_CHECK(__func, __ret) \
+ static int __##__func##__ret##_generator_check
+#define __STL_UNARY_FUNCTION_CHECK(__func, __ret, __arg) do {} while(0)
+#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \
+ static int __##__func##__ret##__arg##_unary_function_check
+#define __STL_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \
+ do {} while(0)
+#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \
+ static int __##__func##__ret##__first##__second##_binary_function_check
+#define __STL_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \
+ do {} while(0)
+#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \
+ static int __##__opname##__ret##__first##__second##_require_binary_op
+
+#else /* __STL_USE_CONCEPT_CHECKS */
+
+// This macro tests whether the template argument "__type_var"
+// satisfies the requirements of "__concept". Here is a list of concepts
+// that we know how to check:
+// _Allocator
+// _Assignable
+// _DefaultConstructible
+// _EqualityComparable
+// _LessThanComparable
+// _TrivialIterator
+// _InputIterator
+// _OutputIterator
+// _ForwardIterator
+// _BidirectionalIterator
+// _RandomAccessIterator
+// _Mutable_TrivialIterator
+// _Mutable_ForwardIterator
+// _Mutable_BidirectionalIterator
+// _Mutable_RandomAccessIterator
+
+#define __STL_REQUIRES(__type_var, __concept) \
+do { \
+ void (*__x)( __type_var ) = __concept##_concept_specification< __type_var >\
+ ::__concept##_requirement_violation; __x = __x; } while (0)
+
+// Use this to check whether type X is convertible to type Y
+#define __STL_CONVERTIBLE(__type_x, __type_y) \
+do { \
+ void (*__x)( __type_x , __type_y ) = _STL_CONVERT_ERROR< __type_x , \
+ __type_y >::__type_X_is_not_convertible_to_type_Y; \
+ __x = __x; } while (0)
+
+// Use this to test whether two template arguments are the same type
+#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) \
+do { \
+ void (*__x)( __type_x , __type_y ) = _STL_SAME_TYPE_ERROR< __type_x, \
+ __type_y >::__type_X_not_same_as_type_Y; \
+ __x = __x; } while (0)
+
+
+// function object checks
+#define __STL_GENERATOR_CHECK(__func, __ret) \
+do { \
+ __ret (*__x)( __func&) = \
+ _STL_GENERATOR_ERROR< \
+ __func, __ret>::__generator_requirement_violation; \
+ __x = __x; } while (0)
+
+
+#define __STL_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \
+do { \
+ __ret (*__x)( __func&, const __arg& ) = \
+ _STL_UNARY_FUNCTION_ERROR< \
+ __func, __ret, __arg>::__unary_function_requirement_violation; \
+ __x = __x; } while (0)
+
+
+#define __STL_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \
+do { \
+ __ret (*__x)( __func&, const __first&, const __second& ) = \
+ _STL_BINARY_FUNCTION_ERROR< \
+ __func, __ret, __first, __second>::__binary_function_requirement_violation; \
+ __x = __x; } while (0)
+
+
+#define __STL_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \
+ do { \
+ __ret (*__x)( __first&, __second& ) = _STL_BINARY##__opname##_ERROR< \
+ __ret, __first, __second>::__binary_operator_requirement_violation; \
+ __ret (*__y)( const __first&, const __second& ) = \
+ _STL_BINARY##__opname##_ERROR< __ret, __first, __second>:: \
+ __const_binary_operator_requirement_violation; \
+ __y = __y; __x = __x; } while (0)
+
+
+#ifdef __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE
+
+#define __STL_CLASS_REQUIRES(__type_var, __concept)
+#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y)
+#define __STL_CLASS_GENERATOR_CHECK(__func, __ret)
+#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg)
+#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second)
+#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second)
+
+#else
+
+// Use this macro inside of template classes, where you would
+// like to place requirements on the template arguments to the class
+// Warning: do not pass pointers and such (e.g. T*) in as the __type_var,
+// since the type_var is used to construct identifiers. Instead typedef
+// the pointer type, then use the typedef name for the __type_var.
+#define __STL_CLASS_REQUIRES(__type_var, __concept) \
+ typedef void (* __func##__type_var##__concept)( __type_var ); \
+ template <__func##__type_var##__concept _Tp1> \
+ struct __dummy_struct_##__type_var##__concept { }; \
+ static __dummy_struct_##__type_var##__concept< \
+ __concept##_concept_specification< \
+ __type_var>::__concept##_requirement_violation> \
+ __dummy_ptr_##__type_var##__concept
+
+
+#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y) \
+ typedef void (* __func_##__type_x##__type_y##same_type)( __type_x, \
+ __type_y ); \
+ template < __func_##__type_x##__type_y##same_type _Tp1> \
+ struct __dummy_struct_##__type_x##__type_y##_same_type { }; \
+ static __dummy_struct_##__type_x##__type_y##_same_type< \
+ _STL_SAME_TYPE_ERROR<__type_x, __type_y>::__type_X_not_same_as_type_Y> \
+ __dummy_ptr_##__type_x##__type_y##_same_type
+
+
+#define __STL_CLASS_GENERATOR_CHECK(__func, __ret) \
+ typedef __ret (* __f_##__func##__ret##_generator)( __func& ); \
+ template <__f_##__func##__ret##_generator _Tp1> \
+ struct __dummy_struct_##__func##__ret##_generator { }; \
+ static __dummy_struct_##__func##__ret##_generator< \
+ _STL_GENERATOR_ERROR< \
+ __func, __ret>::__generator_requirement_violation> \
+ __dummy_ptr_##__func##__ret##_generator
+
+
+#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \
+ typedef __ret (* __f_##__func##__ret##__arg##_unary_check)( __func&, \
+ const __arg& ); \
+ template <__f_##__func##__ret##__arg##_unary_check _Tp1> \
+ struct __dummy_struct_##__func##__ret##__arg##_unary_check { }; \
+ static __dummy_struct_##__func##__ret##__arg##_unary_check< \
+ _STL_UNARY_FUNCTION_ERROR< \
+ __func, __ret, __arg>::__unary_function_requirement_violation> \
+ __dummy_ptr_##__func##__ret##__arg##_unary_check
+
+
+#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \
+ typedef __ret (* __f_##__func##__ret##__first##__second##_binary_check)( __func&, const __first&,\
+ const __second& ); \
+ template <__f_##__func##__ret##__first##__second##_binary_check _Tp1> \
+ struct __dummy_struct_##__func##__ret##__first##__second##_binary_check { }; \
+ static __dummy_struct_##__func##__ret##__first##__second##_binary_check< \
+ _STL_BINARY_FUNCTION_ERROR<__func, __ret, __first, __second>:: \
+ __binary_function_requirement_violation> \
+ __dummy_ptr_##__func##__ret##__first##__second##_binary_check
+
+
+#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \
+ typedef __ret (* __f_##__func##__ret##__first##__second##_binary_op)(const __first&, \
+ const __second& ); \
+ template <__f_##__func##__ret##__first##__second##_binary_op _Tp1> \
+ struct __dummy_struct_##__func##__ret##__first##__second##_binary_op { }; \
+ static __dummy_struct_##__func##__ret##__first##__second##_binary_op< \
+ _STL_BINARY##__opname##_ERROR<__ret, __first, __second>:: \
+ __binary_operator_requirement_violation> \
+ __dummy_ptr_##__func##__ret##__first##__second##_binary_op
+
+#endif
+
+/* helper class for finding non-const version of a type. Need to have
+ something to assign to etc. when testing constant iterators. */
+
+template <class _Tp>
+struct _Mutable_trait {
+ typedef _Tp _Type;
+};
+template <class _Tp>
+struct _Mutable_trait<const _Tp> {
+ typedef _Tp _Type;
+};
+
+
+/* helper function for avoiding compiler warnings about unused variables */
+template <class _Type>
+void __sink_unused_warning(_Type) { }
+
+template <class _TypeX, class _TypeY>
+struct _STL_CONVERT_ERROR {
+ static void
+ __type_X_is_not_convertible_to_type_Y(_TypeX __x, _TypeY) {
+ _TypeY __y = __x;
+ __sink_unused_warning(__y);
+ }
+};
+
+
+template <class _Type> struct __check_equal { };
+
+template <class _TypeX, class _TypeY>
+struct _STL_SAME_TYPE_ERROR {
+ static void
+ __type_X_not_same_as_type_Y(_TypeX , _TypeY ) {
+ __check_equal<_TypeX> t1 = __check_equal<_TypeY>();
+ }
+};
+
+
+// Some Functon Object Checks
+
+template <class _Func, class _Ret>
+struct _STL_GENERATOR_ERROR {
+ static _Ret __generator_requirement_violation(_Func& __f) {
+ return __f();
+ }
+};
+
+template <class _Func>
+struct _STL_GENERATOR_ERROR<_Func, void> {
+ static void __generator_requirement_violation(_Func& __f) {
+ __f();
+ }
+};
+
+
+template <class _Func, class _Ret, class _Arg>
+struct _STL_UNARY_FUNCTION_ERROR {
+ static _Ret
+ __unary_function_requirement_violation(_Func& __f,
+ const _Arg& __arg) {
+ return __f(__arg);
+ }
+};
+
+template <class _Func, class _Arg>
+struct _STL_UNARY_FUNCTION_ERROR<_Func, void, _Arg> {
+ static void
+ __unary_function_requirement_violation(_Func& __f,
+ const _Arg& __arg) {
+ __f(__arg);
+ }
+};
+
+template <class _Func, class _Ret, class _First, class _Second>
+struct _STL_BINARY_FUNCTION_ERROR {
+ static _Ret
+ __binary_function_requirement_violation(_Func& __f,
+ const _First& __first,
+ const _Second& __second) {
+ return __f(__first, __second);
+ }
+};
+
+template <class _Func, class _First, class _Second>
+struct _STL_BINARY_FUNCTION_ERROR<_Func, void, _First, _Second> {
+ static void
+ __binary_function_requirement_violation(_Func& __f,
+ const _First& __first,
+ const _Second& __second) {
+ __f(__first, __second);
+ }
+};
+
+
+#define __STL_DEFINE_BINARY_OP_CHECK(_OP, _NAME) \
+template <class _Ret, class _First, class _Second> \
+struct _STL_BINARY##_NAME##_ERROR { \
+ static _Ret \
+ __const_binary_operator_requirement_violation(const _First& __first, \
+ const _Second& __second) { \
+ return __first _OP __second; \
+ } \
+ static _Ret \
+ __binary_operator_requirement_violation(_First& __first, \
+ _Second& __second) { \
+ return __first _OP __second; \
+ } \
+}
+
+__STL_DEFINE_BINARY_OP_CHECK(==, _OP_EQUAL);
+__STL_DEFINE_BINARY_OP_CHECK(!=, _OP_NOT_EQUAL);
+__STL_DEFINE_BINARY_OP_CHECK(<, _OP_LESS_THAN);
+__STL_DEFINE_BINARY_OP_CHECK(<=, _OP_LESS_EQUAL);
+__STL_DEFINE_BINARY_OP_CHECK(>, _OP_GREATER_THAN);
+__STL_DEFINE_BINARY_OP_CHECK(>=, _OP_GREATER_EQUAL);
+__STL_DEFINE_BINARY_OP_CHECK(+, _OP_PLUS);
+__STL_DEFINE_BINARY_OP_CHECK(*, _OP_TIMES);
+__STL_DEFINE_BINARY_OP_CHECK(/, _OP_DIVIDE);
+__STL_DEFINE_BINARY_OP_CHECK(-, _OP_SUBTRACT);
+__STL_DEFINE_BINARY_OP_CHECK(%, _OP_MOD);
+// ...
+
+// TODO, add unary operators (prefix and postfix)
+
+/*
+ The presence of this class is just to trick EDG into displaying
+ these error messages before any other errors. Without the
+ classes, the errors in the functions get reported after
+ other class errors deep inside the library. The name
+ choice just makes for an eye catching error message :)
+ */
+struct _STL_ERROR {
+
+ template <class _Type>
+ static _Type
+ __default_constructor_requirement_violation(_Type) {
+ return _Type();
+ }
+ template <class _Type>
+ static _Type
+ __assignment_operator_requirement_violation(_Type __a) {
+ __a = __a;
+ return __a;
+ }
+ template <class _Type>
+ static _Type
+ __copy_constructor_requirement_violation(_Type __a) {
+ _Type __c(__a);
+ return __c;
+ }
+ template <class _Type>
+ static _Type
+ __const_parameter_required_for_copy_constructor(_Type /* __a */,
+ const _Type& __b) {
+ _Type __c(__b);
+ return __c;
+ }
+ template <class _Type>
+ static _Type
+ __const_parameter_required_for_assignment_operator(_Type __a,
+ const _Type& __b) {
+ __a = __b;
+ return __a;
+ }
+ template <class _Type>
+ static _Type
+ __less_than_comparable_requirement_violation(_Type __a, _Type __b) {
+ if (__a < __b || __a > __b || __a <= __b || __a >= __b) return __a;
+ return __b;
+ }
+ template <class _Type>
+ static _Type
+ __equality_comparable_requirement_violation(_Type __a, _Type __b) {
+ if (__a == __b || __a != __b) return __a;
+ return __b;
+ }
+ template <class _Iterator>
+ static void
+ __dereference_operator_requirement_violation(_Iterator __i) {
+ __sink_unused_warning(*__i);
+ }
+ template <class _Iterator>
+ static void
+ __dereference_operator_and_assignment_requirement_violation(_Iterator __i) {
+ *__i = *__i;
+ }
+ template <class _Iterator>
+ static void
+ __preincrement_operator_requirement_violation(_Iterator __i) {
+ ++__i;
+ }
+ template <class _Iterator>
+ static void
+ __postincrement_operator_requirement_violation(_Iterator __i) {
+ __i++;
+ }
+ template <class _Iterator>
+ static void
+ __predecrement_operator_requirement_violation(_Iterator __i) {
+ --__i;
+ }
+ template <class _Iterator>
+ static void
+ __postdecrement_operator_requirement_violation(_Iterator __i) {
+ __i--;
+ }
+ template <class _Iterator, class _Type>
+ static void
+ __postincrement_operator_and_assignment_requirement_violation(_Iterator __i,
+ _Type __t) {
+ *__i++ = __t;
+ }
+ template <class _Iterator, class _Distance>
+ static _Iterator
+ __iterator_addition_assignment_requirement_violation(_Iterator __i,
+ _Distance __n) {
+ __i += __n;
+ return __i;
+ }
+ template <class _Iterator, class _Distance>
+ static _Iterator
+ __iterator_addition_requirement_violation(_Iterator __i, _Distance __n) {
+ __i = __i + __n;
+ __i = __n + __i;
+ return __i;
+ }
+ template <class _Iterator, class _Distance>
+ static _Iterator
+ __iterator_subtraction_assignment_requirement_violation(_Iterator __i,
+ _Distance __n) {
+ __i -= __n;
+ return __i;
+ }
+ template <class _Iterator, class _Distance>
+ static _Iterator
+ __iterator_subtraction_requirement_violation(_Iterator __i, _Distance __n) {
+ __i = __i - __n;
+ return __i;
+ }
+ template <class _Iterator, class _Distance>
+ static _Distance
+ __difference_operator_requirement_violation(_Iterator __i, _Iterator __j,
+ _Distance __n) {
+ __n = __i - __j;
+ return __n;
+ }
+ template <class _Exp, class _Type, class _Distance>
+ static _Type
+ __element_access_operator_requirement_violation(_Exp __x, _Type*,
+ _Distance __n) {
+ return __x[__n];
+ }
+ template <class _Exp, class _Type, class _Distance>
+ static void
+ __element_assignment_operator_requirement_violation(_Exp __x,
+ _Type* __t,
+ _Distance __n) {
+ __x[__n] = *__t;
+ }
+
+}; /* _STL_ERROR */
+
+/* Associated Type Requirements */
+
+__STL_BEGIN_NAMESPACE
+template <class _Iterator> struct iterator_traits;
+__STL_END_NAMESPACE
+
+template <class _Iter>
+struct __value_type_type_definition_requirement_violation {
+ typedef typename __STD::iterator_traits<_Iter>::value_type value_type;
+};
+
+template <class _Iter>
+struct __difference_type_type_definition_requirement_violation {
+ typedef typename __STD::iterator_traits<_Iter>::difference_type
+ difference_type;
+};
+
+template <class _Iter>
+struct __reference_type_definition_requirement_violation {
+ typedef typename __STD::iterator_traits<_Iter>::reference reference;
+};
+
+template <class _Iter>
+struct __pointer_type_definition_requirement_violation {
+ typedef typename __STD::iterator_traits<_Iter>::pointer pointer;
+};
+
+template <class _Iter>
+struct __iterator_category_type_definition_requirement_violation {
+ typedef typename __STD::iterator_traits<_Iter>::iterator_category
+ iterator_category;
+};
+
+/* Assignable Requirements */
+
+
+template <class _Type>
+struct _Assignable_concept_specification {
+ static void _Assignable_requirement_violation(_Type __a) {
+ _STL_ERROR::__assignment_operator_requirement_violation(__a);
+ _STL_ERROR::__copy_constructor_requirement_violation(__a);
+ _STL_ERROR::__const_parameter_required_for_copy_constructor(__a,__a);
+ _STL_ERROR::__const_parameter_required_for_assignment_operator(__a,__a);
+ }
+};
+
+/* DefaultConstructible Requirements */
+
+
+template <class _Type>
+struct _DefaultConstructible_concept_specification {
+ static void _DefaultConstructible_requirement_violation(_Type __a) {
+ _STL_ERROR::__default_constructor_requirement_violation(__a);
+ }
+};
+
+/* EqualityComparable Requirements */
+
+template <class _Type>
+struct _EqualityComparable_concept_specification {
+ static void _EqualityComparable_requirement_violation(_Type __a) {
+ _STL_ERROR::__equality_comparable_requirement_violation(__a, __a);
+ }
+};
+
+/* LessThanComparable Requirements */
+template <class _Type>
+struct _LessThanComparable_concept_specification {
+ static void _LessThanComparable_requirement_violation(_Type __a) {
+ _STL_ERROR::__less_than_comparable_requirement_violation(__a, __a);
+ }
+};
+
+/* TrivialIterator Requirements */
+
+template <class _TrivialIterator>
+struct _TrivialIterator_concept_specification {
+static void
+_TrivialIterator_requirement_violation(_TrivialIterator __i) {
+ typedef typename
+ __value_type_type_definition_requirement_violation<_TrivialIterator>::
+ value_type __T;
+ // Refinement of Assignable
+ _Assignable_concept_specification<_TrivialIterator>::
+ _Assignable_requirement_violation(__i);
+ // Refinement of DefaultConstructible
+ _DefaultConstructible_concept_specification<_TrivialIterator>::
+ _DefaultConstructible_requirement_violation(__i);
+ // Refinement of EqualityComparable
+ _EqualityComparable_concept_specification<_TrivialIterator>::
+ _EqualityComparable_requirement_violation(__i);
+ // Valid Expressions
+ _STL_ERROR::__dereference_operator_requirement_violation(__i);
+}
+};
+
+template <class _TrivialIterator>
+struct _Mutable_TrivialIterator_concept_specification {
+static void
+_Mutable_TrivialIterator_requirement_violation(_TrivialIterator __i) {
+ _TrivialIterator_concept_specification<_TrivialIterator>::
+ _TrivialIterator_requirement_violation(__i);
+ // Valid Expressions
+ _STL_ERROR::__dereference_operator_and_assignment_requirement_violation(__i);
+}
+};
+
+/* InputIterator Requirements */
+
+template <class _InputIterator>
+struct _InputIterator_concept_specification {
+static void
+_InputIterator_requirement_violation(_InputIterator __i) {
+ // Refinement of TrivialIterator
+ _TrivialIterator_concept_specification<_InputIterator>::
+ _TrivialIterator_requirement_violation(__i);
+ // Associated Types
+ __difference_type_type_definition_requirement_violation<_InputIterator>();
+ __reference_type_definition_requirement_violation<_InputIterator>();
+ __pointer_type_definition_requirement_violation<_InputIterator>();
+ __iterator_category_type_definition_requirement_violation<_InputIterator>();
+ // Valid Expressions
+ _STL_ERROR::__preincrement_operator_requirement_violation(__i);
+ _STL_ERROR::__postincrement_operator_requirement_violation(__i);
+}
+};
+
+/* OutputIterator Requirements */
+
+template <class _OutputIterator>
+struct _OutputIterator_concept_specification {
+static void
+_OutputIterator_requirement_violation(_OutputIterator __i) {
+ // Refinement of Assignable
+ _Assignable_concept_specification<_OutputIterator>::
+ _Assignable_requirement_violation(__i);
+ // Associated Types
+ __iterator_category_type_definition_requirement_violation<_OutputIterator>();
+ // Valid Expressions
+ _STL_ERROR::__dereference_operator_requirement_violation(__i);
+ _STL_ERROR::__preincrement_operator_requirement_violation(__i);
+ _STL_ERROR::__postincrement_operator_requirement_violation(__i);
+ _STL_ERROR::
+ __postincrement_operator_and_assignment_requirement_violation(__i, *__i);
+}
+};
+
+/* ForwardIterator Requirements */
+
+template <class _ForwardIterator>
+struct _ForwardIterator_concept_specification {
+static void
+_ForwardIterator_requirement_violation(_ForwardIterator __i) {
+ // Refinement of InputIterator
+ _InputIterator_concept_specification<_ForwardIterator>::
+ _InputIterator_requirement_violation(__i);
+}
+};
+
+template <class _ForwardIterator>
+struct _Mutable_ForwardIterator_concept_specification {
+static void
+_Mutable_ForwardIterator_requirement_violation(_ForwardIterator __i) {
+ _ForwardIterator_concept_specification<_ForwardIterator>::
+ _ForwardIterator_requirement_violation(__i);
+ // Refinement of OutputIterator
+ _OutputIterator_concept_specification<_ForwardIterator>::
+ _OutputIterator_requirement_violation(__i);
+}
+};
+
+/* BidirectionalIterator Requirements */
+
+template <class _BidirectionalIterator>
+struct _BidirectionalIterator_concept_specification {
+static void
+_BidirectionalIterator_requirement_violation(_BidirectionalIterator __i) {
+ // Refinement of ForwardIterator
+ _ForwardIterator_concept_specification<_BidirectionalIterator>::
+ _ForwardIterator_requirement_violation(__i);
+ // Valid Expressions
+ _STL_ERROR::__predecrement_operator_requirement_violation(__i);
+ _STL_ERROR::__postdecrement_operator_requirement_violation(__i);
+}
+};
+
+template <class _BidirectionalIterator>
+struct _Mutable_BidirectionalIterator_concept_specification {
+static void
+_Mutable_BidirectionalIterator_requirement_violation(
+ _BidirectionalIterator __i)
+{
+ _BidirectionalIterator_concept_specification<_BidirectionalIterator>::
+ _BidirectionalIterator_requirement_violation(__i);
+ // Refinement of mutable_ForwardIterator
+ _Mutable_ForwardIterator_concept_specification<_BidirectionalIterator>::
+ _Mutable_ForwardIterator_requirement_violation(__i);
+ typedef typename
+ __value_type_type_definition_requirement_violation<
+ _BidirectionalIterator>::value_type __T;
+ typename _Mutable_trait<__T>::_Type* __tmp_ptr = 0;
+ // Valid Expressions
+ _STL_ERROR::
+ __postincrement_operator_and_assignment_requirement_violation(__i,
+ *__tmp_ptr);
+}
+};
+
+/* RandomAccessIterator Requirements */
+
+template <class _RandAccIter>
+struct _RandomAccessIterator_concept_specification {
+static void
+_RandomAccessIterator_requirement_violation(_RandAccIter __i) {
+ // Refinement of BidirectionalIterator
+ _BidirectionalIterator_concept_specification<_RandAccIter>::
+ _BidirectionalIterator_requirement_violation(__i);
+ // Refinement of LessThanComparable
+ _LessThanComparable_concept_specification<_RandAccIter>::
+ _LessThanComparable_requirement_violation(__i);
+ typedef typename
+ __value_type_type_definition_requirement_violation<_RandAccIter>
+ ::value_type
+ value_type;
+ typedef typename
+ __difference_type_type_definition_requirement_violation<_RandAccIter>
+ ::difference_type
+ _Dist;
+ typedef typename _Mutable_trait<_Dist>::_Type _MutDist;
+
+ // Valid Expressions
+ _STL_ERROR::__iterator_addition_assignment_requirement_violation(__i,
+ _MutDist());
+ _STL_ERROR::__iterator_addition_requirement_violation(__i,
+ _MutDist());
+ _STL_ERROR::
+ __iterator_subtraction_assignment_requirement_violation(__i,
+ _MutDist());
+ _STL_ERROR::__iterator_subtraction_requirement_violation(__i,
+ _MutDist());
+ _STL_ERROR::__difference_operator_requirement_violation(__i, __i,
+ _MutDist());
+ typename _Mutable_trait<value_type>::_Type* __dummy_ptr = 0;
+ _STL_ERROR::__element_access_operator_requirement_violation(__i,
+ __dummy_ptr,
+ _MutDist());
+}
+};
+
+template <class _RandAccIter>
+struct _Mutable_RandomAccessIterator_concept_specification {
+static void
+_Mutable_RandomAccessIterator_requirement_violation(_RandAccIter __i)
+{
+ _RandomAccessIterator_concept_specification<_RandAccIter>::
+ _RandomAccessIterator_requirement_violation(__i);
+ // Refinement of mutable_BidirectionalIterator
+ _Mutable_BidirectionalIterator_concept_specification<_RandAccIter>::
+ _Mutable_BidirectionalIterator_requirement_violation(__i);
+ typedef typename
+ __value_type_type_definition_requirement_violation<_RandAccIter>
+ ::value_type
+ value_type;
+ typedef typename
+ __difference_type_type_definition_requirement_violation<_RandAccIter>
+ ::difference_type
+ _Dist;
+
+ typename _Mutable_trait<value_type>::_Type* __tmp_ptr = 0;
+ // Valid Expressions
+ _STL_ERROR::__element_assignment_operator_requirement_violation(__i,
+ __tmp_ptr, _Dist());
+}
+};
+
+#define __STL_TYPEDEF_REQUIREMENT(__REQUIREMENT) \
+template <class Type> \
+struct __##__REQUIREMENT##__typedef_requirement_violation { \
+ typedef typename Type::__REQUIREMENT __REQUIREMENT; \
+}
+
+__STL_TYPEDEF_REQUIREMENT(value_type);
+__STL_TYPEDEF_REQUIREMENT(difference_type);
+__STL_TYPEDEF_REQUIREMENT(size_type);
+__STL_TYPEDEF_REQUIREMENT(reference);
+__STL_TYPEDEF_REQUIREMENT(const_reference);
+__STL_TYPEDEF_REQUIREMENT(pointer);
+__STL_TYPEDEF_REQUIREMENT(const_pointer);
+
+
+template <class _Alloc>
+struct _Allocator_concept_specification {
+static void
+_Allocator_requirement_violation(_Alloc __a) {
+ // Refinement of DefaultConstructible
+ _DefaultConstructible_concept_specification<_Alloc>::
+ _DefaultConstructible_requirement_violation(__a);
+ // Refinement of EqualityComparable
+ _EqualityComparable_concept_specification<_Alloc>::
+ _EqualityComparable_requirement_violation(__a);
+ // Associated Types
+ __value_type__typedef_requirement_violation<_Alloc>();
+ __difference_type__typedef_requirement_violation<_Alloc>();
+ __size_type__typedef_requirement_violation<_Alloc>();
+ __reference__typedef_requirement_violation<_Alloc>();
+ __const_reference__typedef_requirement_violation<_Alloc>();
+ __pointer__typedef_requirement_violation<_Alloc>();
+ __const_pointer__typedef_requirement_violation<_Alloc>();
+ typedef typename _Alloc::value_type _Tp;
+ //__STL_REQUIRES_SAME_TYPE(typename _Alloc::__STL_TEMPLATE rebind<_Tp>::other,
+ // _Alloc);
+}
+};
+
+#endif /* __STL_USE_CONCEPT_CHECKS */
+
+#endif /* __CONCEPT_CHECKS_H */
+
+// Local Variables:
+// mode:C++
+// End:
diff --git a/WebKit/android/stl/cstring b/WebKit/android/stl/cstring
new file mode 100644
index 0000000..77c9175
--- /dev/null
+++ b/WebKit/android/stl/cstring
@@ -0,0 +1,128 @@
+// -*- C++ -*- forwarding header.
+
+// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002
+// Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+//
+// ISO C++ 14882: 20.4.6 C library
+//
+
+/** @file cstring
+ * This is a Standard C++ Library file. You should @c #include this file
+ * in your programs, rather than any of the "*.h" implementation files.
+ *
+ * This is the C++ version of the Standard C Library header @c string.h,
+ * and its contents are (mostly) the same as that header, but are all
+ * contained in the namespace @c std.
+ */
+
+#ifndef _GLIBCXX_CSTRING
+#define _GLIBCXX_CSTRING 1
+
+#pragma GCC system_header
+
+#include <cstddef>
+
+#include <string.h>
+
+// Get rid of those macros defined in <string.h> in lieu of real functions.
+#undef memcpy
+#undef memmove
+#undef strcpy
+#undef strncpy
+#undef strcat
+#undef strncat
+#undef memcmp
+#undef strcmp
+#undef strcoll
+#undef strncmp
+#undef strxfrm
+#undef memchr
+#undef strchr
+#undef strcspn
+#undef strpbrk
+#undef strrchr
+#undef strspn
+#undef strstr
+#undef strtok
+#undef memset
+#undef strerror
+#undef strlen
+
+namespace std
+{
+ using ::memcpy;
+ using ::memmove;
+ using ::strcpy;
+ using ::strncpy;
+ using ::strcat;
+ using ::strncat;
+ using ::memcmp;
+ using ::strcmp;
+// using ::strcoll;
+ using ::strncmp;
+// using ::strxfrm;
+ using ::strcspn;
+ using ::strspn;
+ using ::strtok;
+ using ::memset;
+ using ::strerror;
+ using ::strlen;
+
+ using ::memchr;
+
+ inline void*
+ memchr(void* __p, int __c, size_t __n)
+ { return memchr(const_cast<const void*>(__p), __c, __n); }
+
+ using ::strchr;
+
+ inline char*
+ strchr(char* __s1, int __n)
+ { return __builtin_strchr(const_cast<const char*>(__s1), __n); }
+
+ using ::strpbrk;
+
+ inline char*
+ strpbrk(char* __s1, const char* __s2)
+ { return __builtin_strpbrk(const_cast<const char*>(__s1), __s2); }
+
+ using ::strrchr;
+
+ inline char*
+ strrchr(char* __s1, int __n)
+ { return __builtin_strrchr(const_cast<const char*>(__s1), __n); }
+
+ using ::strstr;
+
+ inline char*
+ strstr(char* __s1, const char* __s2)
+ { return __builtin_strstr(const_cast<const char*>(__s1), __s2); }
+}
+
+#endif
diff --git a/WebKit/android/stl/heap.h b/WebKit/android/stl/heap.h
new file mode 100644
index 0000000..70d2249
--- /dev/null
+++ b/WebKit/android/stl/heap.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * Copyright (c) 1997
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+#ifndef __SGI_STL_HEAP_H
+#define __SGI_STL_HEAP_H
+
+#include <concept_checks.h>
+#include <stl_config.h>
+#include <stl_heap.h>
+
+#ifdef __STL_USE_NAMESPACES
+
+using __STD::push_heap;
+using __STD::pop_heap;
+using __STD::make_heap;
+using __STD::sort_heap;
+
+#endif /* __STL_USE_NAMESPACES */
+
+
+#endif /* __SGI_STL_HEAP_H */
+
+// Local Variables:
+// mode:C++
+// End:
diff --git a/WebKit/android/stl/limits b/WebKit/android/stl/limits
new file mode 100644
index 0000000..672e605
--- /dev/null
+++ b/WebKit/android/stl/limits
@@ -0,0 +1,31 @@
+/*
+ * 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 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 __ANDROID_LIMITS
+#define __ANDROID_LIMITS
+
+#include <limits.h>
+
+#endif // __ANDROID_LIMITS
diff --git a/WebKit/android/stl/memory b/WebKit/android/stl/memory
new file mode 100644
index 0000000..b224c33
--- /dev/null
+++ b/WebKit/android/stl/memory
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1997-1999
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ */
+
+//minimal file to get build to work
+
+#ifdef _CPP_MEMORY
+#error "real STL defined"
+#endif
+
+#ifndef __ANDROID_MEMORY
+#define __ANDROID_MEMORY
+//we good to do this?
+#define __STL_MEMBER_TEMPLATES
+
+#define __STL_NOTHROW
+
+/*#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \*/
+
+#if defined(__STL_MEMBER_TEMPLATES)
+template<class _Tp1> struct auto_ptr_ref {
+ _Tp1* _M_ptr;
+ auto_ptr_ref(_Tp1* __p) : _M_ptr(__p) {}
+};
+#endif
+
+namespace std {
+
+template <class _Tp> class auto_ptr {
+private:
+ _Tp* _M_ptr;
+
+public:
+ typedef _Tp element_type;
+
+ explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}
+ auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}
+
+#ifdef __STL_MEMBER_TEMPLATES
+ template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW
+ : _M_ptr(__a.release()) {}
+#endif /* __STL_MEMBER_TEMPLATES */
+
+ auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {
+ if (&__a != this) {
+ delete _M_ptr;
+ _M_ptr = __a.release();
+ }
+ return *this;
+ }
+
+#ifdef __STL_MEMBER_TEMPLATES
+ template <class _Tp1>
+ auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {
+ if (__a.get() != this->get()) {
+ delete _M_ptr;
+ _M_ptr = __a.release();
+ }
+ return *this;
+ }
+#endif /* __STL_MEMBER_TEMPLATES */
+
+ // Note: The C++ standard says there is supposed to be an empty throw
+ // specification here, but omitting it is standard conforming. Its
+ // presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2)
+ // this is prohibited.
+ ~auto_ptr() { delete _M_ptr; }
+
+ _Tp& operator*() const __STL_NOTHROW {
+ return *_M_ptr;
+ }
+ _Tp* operator->() const __STL_NOTHROW {
+ return _M_ptr;
+ }
+ _Tp* get() const __STL_NOTHROW {
+ return _M_ptr;
+ }
+ _Tp* release() __STL_NOTHROW {
+ _Tp* __tmp = _M_ptr;
+ _M_ptr = 0;
+ return __tmp;
+ }
+ void reset(_Tp* __p = 0) __STL_NOTHROW {
+ if (__p != _M_ptr) {
+ delete _M_ptr;
+ _M_ptr = __p;
+ }
+ }
+
+/*#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \*/
+#if defined(__STL_MEMBER_TEMPLATES)
+
+public:
+ auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
+ : _M_ptr(__ref._M_ptr) {}
+
+ auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {
+ if (__ref._M_ptr != this->get()) {
+ delete _M_ptr;
+ _M_ptr = __ref._M_ptr;
+ }
+ return *this;
+ }
+
+ template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW
+ { return auto_ptr_ref<_Tp1>(this->release()); }
+ template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW
+ { return auto_ptr<_Tp1>(this->release()); }
+
+#endif /* auto ptr conversions && member templates */
+
+};
+
+} //namespace std
+
+#endif /* __ANDROID_MEMORY */
+
+
+// Local Variables:
+// mode:C++
+// End:
+
diff --git a/WebKit/android/stl/new.h b/WebKit/android/stl/new.h
new file mode 100644
index 0000000..599a890
--- /dev/null
+++ b/WebKit/android/stl/new.h
@@ -0,0 +1,25 @@
+/*
+ * 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 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.
+ */
+
diff --git a/WebKit/android/stl/stl_config.h b/WebKit/android/stl/stl_config.h
new file mode 100644
index 0000000..448babc
--- /dev/null
+++ b/WebKit/android/stl/stl_config.h
@@ -0,0 +1,577 @@
+/*
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * Copyright (c) 1997
+ * Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ */
+
+#ifndef __STL_CONFIG_H
+# define __STL_CONFIG_H
+
+// Flags:
+// * __STL_NO_BOOL: defined if the compiler doesn't have bool as a builtin
+// type.
+// * __STL_HAS_WCHAR_T: defined if the compier has wchar_t as a builtin type.
+// * __STL_NO_DRAND48: defined if the compiler doesn't have the drand48
+// function.
+// * __STL_STATIC_TEMPLATE_MEMBER_BUG: defined if the compiler can't handle
+// static members of template classes.
+// * __STL_STATIC_CONST_INIT_BUG: defined if the compiler can't handle a
+// constant-initializer in the declaration of a static const data member
+// of integer type. (See section 9.4.2, paragraph 4, of the C++ standard.)
+// * __STL_CLASS_PARTIAL_SPECIALIZATION: defined if the compiler supports
+// partial specialization of template classes.
+// * __STL_PARTIAL_SPECIALIZATION_SYNTAX: defined if the compiler
+// supports partial specialization syntax for full specialization of
+// class templates. (Even if it doesn't actually support partial
+// specialization itself.)
+// * __STL_FUNCTION_TMPL_PARTIAL_ORDER: defined if the compiler supports
+// partial ordering of function templates. (a.k.a partial specialization
+// of function templates.)
+// * __STL_MEMBER_TEMPLATES: defined if the compiler supports template
+// member functions of classes.
+// * __STL_MEMBER_TEMPLATE_CLASSES: defined if the compiler supports
+// nested classes that are member templates of other classes.
+// * __STL_TEMPLATE_FRIENDS: defined if the compiler supports templatized
+// friend declarations.
+// * __STL_EXPLICIT_FUNCTION_TMPL_ARGS: defined if the compiler
+// supports calling a function template by providing its template
+// arguments explicitly.
+// * __STL_LIMITED_DEFAULT_TEMPLATES: defined if the compiler is unable
+// to handle default template parameters that depend on previous template
+// parameters.
+// * __STL_NON_TYPE_TMPL_PARAM_BUG: defined if the compiler has trouble with
+// function template argument deduction for non-type template parameters.
+// * __SGI_STL_NO_ARROW_OPERATOR: defined if the compiler is unable
+// to support the -> operator for iterators.
+// * __STL_DEFAULT_CONSTRUCTOR_BUG: defined if T() does not work properly
+// when T is a builtin type.
+// * __STL_USE_EXCEPTIONS: defined if the compiler (in the current compilation
+// mode) supports exceptions.
+// * __STL_USE_NAMESPACES: defined if the compiler has the necessary
+// support for namespaces.
+// * __STL_NO_EXCEPTION_HEADER: defined if the compiler does not have a
+// standard-conforming header <exception>.
+// * __STL_NO_BAD_ALLOC: defined if the compiler does not have a <new>
+// header, or if <new> does not contain a bad_alloc class. If a bad_alloc
+// class exists, it is assumed to be in namespace std.
+// * __STL_SGI_THREADS: defined if this is being compiled for an SGI IRIX
+// system in multithreaded mode, using native SGI threads instead of
+// pthreads.
+// * __STL_WIN32THREADS: defined if this is being compiled on a WIN32
+// compiler in multithreaded mode.
+// * __STL_PTHREADS: defined if we should use portable pthreads
+// synchronization.
+// * __STL_UITHREADS: defined if we should use UI / solaris / UnixWare threads
+// synchronization. UIthreads are similar to pthreads, but are based
+// on an earlier version of the Posix threads standard.
+// * __STL_LONG_LONG if the compiler has long long and unsigned long long
+// types. (They're not in the C++ standard, but they are expected to be
+// included in the forthcoming C9X standard.)
+// * __STL_THREADS is defined if thread safety is needed.
+// * __STL_VOLATILE is defined to be "volatile" if threads are being
+// used, and the empty string otherwise.
+// * __STL_USE_CONCEPT_CHECKS enables some extra compile-time error
+// checking to make sure that user-defined template arguments satisfy
+// all of the appropriate requirements. This may result in more
+// comprehensible error messages. It incurs no runtime overhead. This
+// feature requires member templates and partial specialization.
+// * __STL_NO_USING_CLAUSE_IN_CLASS: The compiler does not handle "using"
+// clauses inside of class definitions.
+// * __STL_NO_FRIEND_TEMPLATE_CLASS: The compiler does not handle friend
+// declaractions where the friend is a template class.
+// * __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE: The compiler does not
+// support the use of a function pointer type as the argument
+// for a template.
+// * __STL_MEMBER_TEMPLATE_KEYWORD: standard C++ requires the template
+// keyword in a few new places (14.2.4). This flag is set for
+// compilers that support (and require) this usage.
+
+
+// User-settable macros that control compilation:
+// * __STL_USE_SGI_ALLOCATORS: if defined, then the STL will use older
+// SGI-style allocators, instead of standard-conforming allocators,
+// even if the compiler supports all of the language features needed
+// for standard-conforming allocators.
+// * __STL_NO_NAMESPACES: if defined, don't put the library in namespace
+// std, even if the compiler supports namespaces.
+// * __STL_NO_RELOPS_NAMESPACE: if defined, don't put the relational
+// operator templates (>, <=. >=, !=) in namespace std::rel_ops, even
+// if the compiler supports namespaces and partial ordering of
+// function templates.
+// * __STL_ASSERTIONS: if defined, then enable runtime checking through the
+// __stl_assert macro.
+// * _PTHREADS: if defined, use Posix threads for multithreading support.
+// * _UITHREADS:if defined, use SCO/Solaris/UI threads for multithreading
+// support
+// * _NOTHREADS: if defined, don't use any multithreading support.
+// * _STL_NO_CONCEPT_CHECKS: if defined, disables the error checking that
+// we get from __STL_USE_CONCEPT_CHECKS.
+// * __STL_USE_NEW_IOSTREAMS: if defined, then the STL will use new,
+// standard-conforming iostreams (e.g. the <iosfwd> header). If not
+// defined, the STL will use old cfront-style iostreams (e.g. the
+// <iostream.h> header).
+
+// Other macros defined by this file:
+
+// * bool, true, and false, if __STL_NO_BOOL is defined.
+// * typename, as a null macro if it's not already a keyword.
+// * explicit, as a null macro if it's not already a keyword.
+// * namespace-related macros (__STD, __STL_BEGIN_NAMESPACE, etc.)
+// * exception-related macros (__STL_TRY, __STL_UNWIND, etc.)
+// * __stl_assert, either as a test or as a null macro, depending on
+// whether or not __STL_ASSERTIONS is defined.
+
+# if defined(_PTHREADS) && !defined(_NOTHREADS)
+# define __STL_PTHREADS
+# endif
+
+# if defined(_UITHREADS) && !defined(_PTHREADS) && !defined(_NOTHREADS)
+# define __STL_UITHREADS
+# endif
+
+# if defined(__sgi) && !defined(__GNUC__)
+# include <standards.h>
+# if !defined(_BOOL)
+# define __STL_NO_BOOL
+# endif
+# if defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32
+# define __STL_STATIC_CONST_INIT_BUG
+# endif
+# if defined(_WCHAR_T_IS_KEYWORD)
+# define __STL_HAS_WCHAR_T
+# endif
+# if !defined(_TYPENAME_IS_KEYWORD)
+# define __STL_NEED_TYPENAME
+# endif
+# ifdef _PARTIAL_SPECIALIZATION_OF_CLASS_TEMPLATES
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# endif
+# if (_COMPILER_VERSION >= 730) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32
+# define __STL_FUNCTION_TMPL_PARTIAL_ORDER
+# endif
+# ifdef _MEMBER_TEMPLATES
+# define __STL_MEMBER_TEMPLATES
+# define __STL_TEMPLATE_FRIENDS
+# define __STL_MEMBER_TEMPLATE_CLASSES
+# endif
+# if defined(_MEMBER_TEMPLATE_KEYWORD)
+# define __STL_MEMBER_TEMPLATE_KEYWORD
+# endif
+# if defined(_STANDARD_C_PLUS_PLUS)
+# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS
+# endif
+# if (_COMPILER_VERSION >= 730) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32
+# define __STL_MEMBER_TEMPLATE_KEYWORD
+# endif
+# if COMPILER_VERSION < 720 || (defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32)
+# define __STL_DEFAULT_CONSTRUCTOR_BUG
+# endif
+# if !defined(_EXPLICIT_IS_KEYWORD)
+# define __STL_NEED_EXPLICIT
+# endif
+# ifdef __EXCEPTIONS
+# define __STL_USE_EXCEPTIONS
+# endif
+# if (_COMPILER_VERSION >= 721) && defined(_NAMESPACES)
+# define __STL_HAS_NAMESPACES
+# endif
+# if (_COMPILER_VERSION < 721) || \
+ !defined(__STL_HAS_NAMESPACES) || defined(__STL_NO_NAMESPACES)
+# define __STL_NO_EXCEPTION_HEADER
+# endif
+# if _COMPILER_VERSION < 730 || !defined(_STANDARD_C_PLUS_PLUS) || \
+ !defined(_NAMESPACES)
+# define __STL_NO_BAD_ALLOC
+# endif
+# if !defined(_NOTHREADS) && !defined(__STL_PTHREADS)
+# define __STL_SGI_THREADS
+# endif
+# if defined(_LONGLONG) && defined(_SGIAPI) && _SGIAPI
+# define __STL_LONG_LONG
+# endif
+# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS)
+# define __STL_USE_NEW_IOSTREAMS
+# endif
+# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS)
+# define __STL_CAN_THROW_RANGE_ERRORS
+# endif
+# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS)
+# define __SGI_STL_USE_AUTO_PTR_CONVERSIONS
+# endif
+# endif
+
+
+/*
+ * Jochen Schlick '1999 - added new #defines (__STL)_UITHREADS (for
+ * providing SCO / Solaris / UI thread support)
+ * - added the necessary defines for the SCO UDK 7
+ * compiler (and its template friend behavior)
+ * - all UDK7 specific STL changes are based on the
+ * macro __USLC__ being defined
+ */
+// SCO UDK 7 compiler (UnixWare 7x, OSR 5, UnixWare 2x)
+# if defined(__USLC__)
+# define __STL_HAS_WCHAR_T
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# define __STL_PARTIAL_SPECIALIZATION_SYNTAX
+# define __STL_FUNCTION_TMPL_PARTIAL_ORDER
+# define __STL_MEMBER_TEMPLATES
+# define __STL_MEMBER_TEMPLATE_CLASSES
+# define __STL_USE_EXCEPTIONS
+# define __STL_HAS_NAMESPACES
+# define __STL_USE_NAMESPACES
+# define __STL_LONG_LONG
+# if defined(_REENTRANT)
+# define _UITHREADS /* if UnixWare < 7.0.1 */
+# define __STL_UITHREADS
+// use the following defines instead of the UI threads defines when
+// you want to use POSIX threads
+//# define _PTHREADS /* only if UnixWare >=7.0.1 */
+//# define __STL_PTHREADS
+# endif
+# endif
+
+
+
+# ifdef __GNUC__
+# if __GNUC__ == 2 && __GNUC_MINOR__ <= 7
+# define __STL_STATIC_TEMPLATE_MEMBER_BUG
+# endif
+# if __GNUC__ < 2
+# define __STL_NEED_TYPENAME
+# define __STL_NEED_EXPLICIT
+# endif
+# if __GNUC__ == 2 && __GNUC_MINOR__ <= 8
+# define __STL_NO_EXCEPTION_HEADER
+# define __STL_NO_BAD_ALLOC
+# endif
+# if __GNUC__ == 2 && __GNUC_MINOR__ >= 8 || __GNUC__ > 2
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# define __STL_FUNCTION_TMPL_PARTIAL_ORDER
+# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS
+# define __STL_MEMBER_TEMPLATES
+# define __STL_CAN_THROW_RANGE_ERRORS
+ // g++ 2.8.1 supports member template functions, but not member
+ // template nested classes.
+# if __GNUC_MINOR__ >= 9 || __GNUC__ > 2
+# define __STL_MEMBER_TEMPLATE_CLASSES
+# define __STL_TEMPLATE_FRIENDS
+# define __SGI_STL_USE_AUTO_PTR_CONVERSIONS
+# define __STL_HAS_NAMESPACES
+//# define __STL_USE_NEW_IOSTREAMS
+# endif
+# endif
+# define __STL_DEFAULT_CONSTRUCTOR_BUG
+# ifdef __EXCEPTIONS
+# define __STL_USE_EXCEPTIONS
+# endif
+# ifdef _REENTRANT
+# define __STL_PTHREADS
+# endif
+# if (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 95)
+# define __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE
+# endif
+# endif
+
+# if defined(__SUNPRO_CC)
+# define __STL_NO_BOOL
+# define __STL_NEED_TYPENAME
+# define __STL_NEED_EXPLICIT
+# define __STL_USE_EXCEPTIONS
+# ifdef _REENTRANT
+# define __STL_PTHREADS
+# endif
+# define __SGI_STL_NO_ARROW_OPERATOR
+# define __STL_PARTIAL_SPECIALIZATION_SYNTAX
+# define __STL_NO_EXCEPTION_HEADER
+# define __STL_NO_BAD_ALLOC
+# endif
+
+# if defined(__COMO__)
+# define __STL_MEMBER_TEMPLATES
+# define __STL_MEMBER_TEMPLATE_CLASSES
+# define __STL_TEMPLATE_FRIENDS
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# define __STL_USE_EXCEPTIONS
+# define __STL_HAS_NAMESPACES
+# endif
+
+// Intel compiler, which uses the EDG front end.
+# if defined(__ICL)
+# define __STL_LONG_LONG
+# define __STL_MEMBER_TEMPLATES
+# define __STL_MEMBER_TEMPLATE_CLASSES
+# define __STL_TEMPLATE_FRIENDS
+# define __STL_FUNCTION_TMPL_PARTIAL_ORDER
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# define __STL_NO_DRAND48
+# define __STL_HAS_NAMESPACES
+# define __STL_USE_EXCEPTIONS
+# define __STL_MEMBER_TEMPLATE_KEYWORD
+# ifdef _CPPUNWIND
+# define __STL_USE_EXCEPTIONS
+# endif
+# ifdef _MT
+# define __STL_WIN32THREADS
+# endif
+# endif
+
+// Mingw32, egcs compiler using the Microsoft C runtime
+# if defined(__MINGW32__)
+# define __STL_NO_DRAND48
+# ifdef _MT
+# define __STL_WIN32THREADS
+# endif
+# endif
+
+// Cygwin32, egcs compiler on MS Windows
+# if defined(__CYGWIN__)
+# define __STL_NO_DRAND48
+# endif
+
+
+
+// Microsoft compiler.
+# if defined(_MSC_VER) && !defined(__ICL) && !defined(__MWERKS__)
+# define __STL_NO_DRAND48
+# define __STL_STATIC_CONST_INIT_BUG
+# define __STL_NEED_TYPENAME
+# define __STL_NO_USING_CLAUSE_IN_CLASS
+# define __STL_NO_FRIEND_TEMPLATE_CLASS
+# if _MSC_VER < 1100 /* 1000 is version 4.0, 1100 is 5.0, 1200 is 6.0. */
+# define __STL_NEED_EXPLICIT
+# define __STL_NO_BOOL
+# define __STL_NO_BAD_ALLOC
+# endif
+# if _MSC_VER > 1000
+# include <yvals.h>
+# define __STL_DONT_USE_BOOL_TYPEDEF
+# endif
+# define __STL_NON_TYPE_TMPL_PARAM_BUG
+# define __SGI_STL_NO_ARROW_OPERATOR
+# define __STL_DEFAULT_CONSTRUCTOR_BUG
+# ifdef _CPPUNWIND
+# define __STL_USE_EXCEPTIONS
+# endif
+# ifdef _MT
+# define __STL_WIN32THREADS
+# endif
+# if _MSC_VER >= 1200
+# define __STL_PARTIAL_SPECIALIZATION_SYNTAX
+# define __STL_HAS_NAMESPACES
+# define __STL_CAN_THROW_RANGE_ERRORS
+# define NOMINMAX
+# undef min
+# undef max
+// disable warning 'initializers put in unrecognized initialization area'
+# pragma warning ( disable : 4075 )
+// disable warning 'empty controlled statement found'
+# pragma warning ( disable : 4390 )
+// disable warning 'debug symbol greater than 255 chars'
+# pragma warning ( disable : 4786 )
+# endif
+# if _MSC_VER < 1100
+# define __STL_NO_EXCEPTION_HEADER
+# define __STL_NO_BAD_ALLOC
+# endif
+ // Because of a Microsoft front end bug, we must not provide a
+ // namespace qualifier when declaring a friend function.
+# define __STD_QUALIFIER
+# endif
+
+# if defined(__BORLANDC__)
+# define __STL_NO_BAD_ALLOC
+# define __STL_NO_DRAND48
+# define __STL_DEFAULT_CONSTRUCTOR_BUG
+# if __BORLANDC__ >= 0x540 /* C++ Builder 4.0 */
+# define __STL_CLASS_PARTIAL_SPECIALIZATION
+# define __STL_FUNCTION_TMPL_PARTIAL_ORDER
+# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS
+# define __STL_MEMBER_TEMPLATES
+# define __STL_TEMPLATE_FRIENDS
+# else
+# define __STL_NEED_TYPENAME
+# define __STL_LIMITED_DEFAULT_TEMPLATES
+# define __SGI_STL_NO_ARROW_OPERATOR
+# define __STL_NON_TYPE_TMPL_PARAM_BUG
+# endif
+# ifdef _CPPUNWIND
+# define __STL_USE_EXCEPTIONS
+# endif
+# ifdef __MT__
+# define __STL_WIN32THREADS
+# endif
+# endif
+
+# if defined(__STL_NO_BOOL) && !defined(__STL_DONT_USE_BOOL_TYPEDEF)
+ typedef int bool;
+# define true 1
+# define false 0
+# endif
+
+# ifdef __STL_NEED_TYPENAME
+# define typename
+# endif
+
+# ifdef __STL_LIMITED_DEFAULT_TEMPLATES
+# define __STL_DEPENDENT_DEFAULT_TMPL(_Tp)
+# else
+# define __STL_DEPENDENT_DEFAULT_TMPL(_Tp) = _Tp
+# endif
+
+# ifdef __STL_MEMBER_TEMPLATE_KEYWORD
+# define __STL_TEMPLATE template
+# else
+# define __STL_TEMPLATE
+# endif
+
+# ifdef __STL_NEED_EXPLICIT
+# define explicit
+# endif
+
+# ifdef __STL_EXPLICIT_FUNCTION_TMPL_ARGS
+# define __STL_NULL_TMPL_ARGS <>
+# else
+# define __STL_NULL_TMPL_ARGS
+# endif
+
+# if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) \
+ || defined (__STL_PARTIAL_SPECIALIZATION_SYNTAX)
+# define __STL_TEMPLATE_NULL template<>
+# else
+# define __STL_TEMPLATE_NULL
+# endif
+
+// Use standard-conforming allocators if we have the necessary language
+// features. __STL_USE_SGI_ALLOCATORS is a hook so that users can
+// disable new-style allocators, and continue to use the same kind of
+// allocators as before, without having to edit library headers.
+# if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) && \
+ defined(__STL_MEMBER_TEMPLATES) && \
+ defined(__STL_MEMBER_TEMPLATE_CLASSES) && \
+ !defined(__STL_NO_BOOL) && \
+ !defined(__STL_NON_TYPE_TMPL_PARAM_BUG) && \
+ !defined(__STL_LIMITED_DEFAULT_TEMPLATES) && \
+ !defined(__STL_USE_SGI_ALLOCATORS)
+# define __STL_USE_STD_ALLOCATORS
+# endif
+
+# ifndef __STL_DEFAULT_ALLOCATOR
+# ifdef __STL_USE_STD_ALLOCATORS
+# define __STL_DEFAULT_ALLOCATOR(T) allocator< T >
+# else
+# define __STL_DEFAULT_ALLOCATOR(T) alloc
+# endif
+# endif
+
+// __STL_NO_NAMESPACES is a hook so that users can disable namespaces
+// without having to edit library headers. __STL_NO_RELOPS_NAMESPACE is
+// a hook so that users can disable the std::rel_ops namespace, keeping
+// the relational operator template in namespace std, without having to
+// edit library headers.
+# if defined(__STL_HAS_NAMESPACES) && !defined(__STL_NO_NAMESPACES)
+# define __STL_USE_NAMESPACES
+# define __STD std
+# define __STL_BEGIN_NAMESPACE namespace std {
+# define __STL_END_NAMESPACE }
+# if defined(__STL_FUNCTION_TMPL_PARTIAL_ORDER) && \
+ !defined(__STL_NO_RELOPS_NAMESPACE)
+# define __STL_USE_NAMESPACE_FOR_RELOPS
+# define __STL_BEGIN_RELOPS_NAMESPACE namespace std { namespace rel_ops {
+# define __STL_END_RELOPS_NAMESPACE } }
+# define __STD_RELOPS std::rel_ops
+# else /* Use std::rel_ops namespace */
+# define __STL_USE_NAMESPACE_FOR_RELOPS
+# define __STL_BEGIN_RELOPS_NAMESPACE namespace std {
+# define __STL_END_RELOPS_NAMESPACE }
+# define __STD_RELOPS std
+# endif /* Use std::rel_ops namespace */
+# else
+# define __STD
+# define __STL_BEGIN_NAMESPACE
+# define __STL_END_NAMESPACE
+# undef __STL_USE_NAMESPACE_FOR_RELOPS
+# define __STL_BEGIN_RELOPS_NAMESPACE
+# define __STL_END_RELOPS_NAMESPACE
+# define __STD_RELOPS
+# undef __STL_USE_NAMESPACES
+# endif
+
+// Some versions of the EDG front end sometimes require an explicit
+// namespace spec where they shouldn't. This macro facilitates that.
+// If the bug becomes irrelevant, then all uses of __STD_QUALIFIER
+// should be removed. The 7.3 beta SGI compiler has this bug, but the
+// MR version is not expected to have it.
+
+# if defined(__STL_USE_NAMESPACES) && !defined(__STD_QUALIFIER)
+# define __STD_QUALIFIER std::
+# else
+# define __STD_QUALIFIER
+# endif
+
+# ifdef __STL_USE_EXCEPTIONS
+# define __STL_TRY try
+# define __STL_CATCH_ALL catch(...)
+# define __STL_THROW(x) throw x
+# define __STL_RETHROW throw
+# define __STL_NOTHROW throw()
+# define __STL_UNWIND(action) catch(...) { action; throw; }
+# else
+# define __STL_TRY
+# define __STL_CATCH_ALL if (false)
+# define __STL_THROW(x)
+# define __STL_RETHROW
+# define __STL_NOTHROW
+# define __STL_UNWIND(action)
+# endif
+
+#ifdef __STL_ASSERTIONS
+# include <stdio.h>
+# define __stl_assert(expr) \
+ if (!(expr)) { fprintf(stderr, "%s:%d STL assertion failure: %s\n", \
+ __FILE__, __LINE__, # expr); abort(); }
+#else
+# define __stl_assert(expr)
+#endif
+
+#if defined(__STL_WIN32THREADS) || defined(__STL_SGI_THREADS) \
+ || defined(__STL_PTHREADS) || defined(__STL_UITHREADS)
+# define __STL_THREADS
+# define __STL_VOLATILE volatile
+#else
+# define __STL_VOLATILE
+#endif
+
+#if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) \
+ && defined(__STL_MEMBER_TEMPLATES) \
+ && !defined(_STL_NO_CONCEPT_CHECKS)
+# define __STL_USE_CONCEPT_CHECKS
+#endif
+
+
+#endif /* __STL_CONFIG_H */
+
+// Local Variables:
+// mode:C++
+// End:
diff --git a/WebKit/android/stl/stl_heap.h b/WebKit/android/stl/stl_heap.h
new file mode 100644
index 0000000..9e3cf13
--- /dev/null
+++ b/WebKit/android/stl/stl_heap.h
@@ -0,0 +1,298 @@
+/*
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * Copyright (c) 1997
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+/* NOTE: This is an internal header file, included by other STL headers.
+ * You should not attempt to use it directly.
+ */
+
+#ifndef __SGI_STL_INTERNAL_HEAP_H
+#define __SGI_STL_INTERNAL_HEAP_H
+
+__STL_BEGIN_NAMESPACE
+
+#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
+#pragma set woff 1209
+#endif
+
+// Heap-manipulation functions: push_heap, pop_heap, make_heap, sort_heap.
+
+template <class _RandomAccessIterator, class _Distance, class _Tp>
+void
+__push_heap(_RandomAccessIterator __first,
+ _Distance __holeIndex, _Distance __topIndex, _Tp __value)
+{
+ _Distance __parent = (__holeIndex - 1) / 2;
+ while (__holeIndex > __topIndex && *(__first + __parent) < __value) {
+ *(__first + __holeIndex) = *(__first + __parent);
+ __holeIndex = __parent;
+ __parent = (__holeIndex - 1) / 2;
+ }
+ *(__first + __holeIndex) = __value;
+}
+
+template <class _RandomAccessIterator, class _Distance, class _Tp>
+inline void
+__push_heap_aux(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Distance*, _Tp*)
+{
+ __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),
+ _Tp(*(__last - 1)));
+}
+
+template <class _RandomAccessIterator>
+inline void
+push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
+ _LessThanComparable);
+ __push_heap_aux(__first, __last,
+ __DISTANCE_TYPE(__first), __VALUE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator, class _Distance, class _Tp,
+ class _Compare>
+void
+__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
+ _Distance __topIndex, _Tp __value, _Compare __comp)
+{
+ _Distance __parent = (__holeIndex - 1) / 2;
+ while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
+ *(__first + __holeIndex) = *(__first + __parent);
+ __holeIndex = __parent;
+ __parent = (__holeIndex - 1) / 2;
+ }
+ *(__first + __holeIndex) = __value;
+}
+
+template <class _RandomAccessIterator, class _Compare,
+ class _Distance, class _Tp>
+inline void
+__push_heap_aux(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Compare __comp,
+ _Distance*, _Tp*)
+{
+ __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),
+ _Tp(*(__last - 1)), __comp);
+}
+
+template <class _RandomAccessIterator, class _Compare>
+inline void
+push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _Compare __comp)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __push_heap_aux(__first, __last, __comp,
+ __DISTANCE_TYPE(__first), __VALUE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator, class _Distance, class _Tp>
+void
+__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
+ _Distance __len, _Tp __value)
+{
+ _Distance __topIndex = __holeIndex;
+ _Distance __secondChild = 2 * __holeIndex + 2;
+ while (__secondChild < __len) {
+ if (*(__first + __secondChild) < *(__first + (__secondChild - 1)))
+ __secondChild--;
+ *(__first + __holeIndex) = *(__first + __secondChild);
+ __holeIndex = __secondChild;
+ __secondChild = 2 * (__secondChild + 1);
+ }
+ if (__secondChild == __len) {
+ *(__first + __holeIndex) = *(__first + (__secondChild - 1));
+ __holeIndex = __secondChild - 1;
+ }
+ __push_heap(__first, __holeIndex, __topIndex, __value);
+}
+
+template <class _RandomAccessIterator, class _Tp, class _Distance>
+inline void
+__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __result, _Tp __value, _Distance*)
+{
+ *__result = *__first;
+ __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value);
+}
+
+template <class _RandomAccessIterator, class _Tp>
+inline void
+__pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _Tp*)
+{
+ __pop_heap(__first, __last - 1, __last - 1,
+ _Tp(*(__last - 1)), __DISTANCE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator>
+inline void pop_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
+ _LessThanComparable);
+ __pop_heap_aux(__first, __last, __VALUE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator, class _Distance,
+ class _Tp, class _Compare>
+void
+__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
+ _Distance __len, _Tp __value, _Compare __comp)
+{
+ _Distance __topIndex = __holeIndex;
+ _Distance __secondChild = 2 * __holeIndex + 2;
+ while (__secondChild < __len) {
+ if (__comp(*(__first + __secondChild), *(__first + (__secondChild - 1))))
+ __secondChild--;
+ *(__first + __holeIndex) = *(__first + __secondChild);
+ __holeIndex = __secondChild;
+ __secondChild = 2 * (__secondChild + 1);
+ }
+ if (__secondChild == __len) {
+ *(__first + __holeIndex) = *(__first + (__secondChild - 1));
+ __holeIndex = __secondChild - 1;
+ }
+ __push_heap(__first, __holeIndex, __topIndex, __value, __comp);
+}
+
+template <class _RandomAccessIterator, class _Tp, class _Compare,
+ class _Distance>
+inline void
+__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _RandomAccessIterator __result, _Tp __value, _Compare __comp,
+ _Distance*)
+{
+ *__result = *__first;
+ __adjust_heap(__first, _Distance(0), _Distance(__last - __first),
+ __value, __comp);
+}
+
+template <class _RandomAccessIterator, class _Tp, class _Compare>
+inline void
+__pop_heap_aux(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Tp*, _Compare __comp)
+{
+ __pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp,
+ __DISTANCE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator, class _Compare>
+inline void
+pop_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Compare __comp)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __pop_heap_aux(__first, __last, __VALUE_TYPE(__first), __comp);
+}
+
+template <class _RandomAccessIterator, class _Tp, class _Distance>
+void
+__make_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Tp*, _Distance*)
+{
+ if (__last - __first < 2) return;
+ _Distance __len = __last - __first;
+ _Distance __parent = (__len - 2)/2;
+
+ while (true) {
+ __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));
+ if (__parent == 0) return;
+ __parent--;
+ }
+}
+
+template <class _RandomAccessIterator>
+inline void
+make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
+ _LessThanComparable);
+ __make_heap(__first, __last,
+ __VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator, class _Compare,
+ class _Tp, class _Distance>
+void
+__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ _Compare __comp, _Tp*, _Distance*)
+{
+ if (__last - __first < 2) return;
+ _Distance __len = __last - __first;
+ _Distance __parent = (__len - 2)/2;
+
+ while (true) {
+ __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)),
+ __comp);
+ if (__parent == 0) return;
+ __parent--;
+ }
+}
+
+template <class _RandomAccessIterator, class _Compare>
+inline void
+make_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Compare __comp)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __make_heap(__first, __last, __comp,
+ __VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
+}
+
+template <class _RandomAccessIterator>
+void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
+ _LessThanComparable);
+ while (__last - __first > 1)
+ pop_heap(__first, __last--);
+}
+
+template <class _RandomAccessIterator, class _Compare>
+void
+sort_heap(_RandomAccessIterator __first,
+ _RandomAccessIterator __last, _Compare __comp)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
+ while (__last - __first > 1)
+ pop_heap(__first, __last--, __comp);
+}
+
+#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
+#pragma reset woff 1209
+#endif
+
+__STL_END_NAMESPACE
+
+#endif /* __SGI_STL_INTERNAL_HEAP_H */
+
+// Local Variables:
+// mode:C++
+// End:
diff --git a/WebKit/android/stl/stl_iterator_base.h b/WebKit/android/stl/stl_iterator_base.h
new file mode 100644
index 0000000..746d1d4
--- /dev/null
+++ b/WebKit/android/stl/stl_iterator_base.h
@@ -0,0 +1,274 @@
+/*
+ *
+ * Copyright 2009, The Android Open Source Project
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+/* NOTE: This is an internal header file, included by other STL headers.
+ * You should not attempt to use it directly.
+ */
+
+#ifndef __SGI_STL_INTERNAL_ITERATOR_BASE_H
+#define __SGI_STL_INTERNAL_ITERATOR_BASE_H
+
+// This file contains all of the general iterator-related utilities.
+// The internal file stl_iterator.h contains predefined iterators,
+// such as front_insert_iterator and istream_iterator.
+
+#include <concept_checks.h>
+
+struct input_iterator_tag {};
+struct output_iterator_tag {};
+struct forward_iterator_tag : public input_iterator_tag {};
+struct bidirectional_iterator_tag : public forward_iterator_tag {};
+struct random_access_iterator_tag : public bidirectional_iterator_tag {};
+
+// The base classes input_iterator, output_iterator, forward_iterator,
+// bidirectional_iterator, and random_access_iterator are not part of
+// the C++ standard. (They have been replaced by struct iterator.)
+// They are included for backward compatibility with the HP STL.
+
+template <class _Tp, class _Distance> struct input_iterator {
+ typedef input_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef _Distance difference_type;
+ typedef _Tp* pointer;
+ typedef _Tp& reference;
+};
+
+struct output_iterator {
+ typedef output_iterator_tag iterator_category;
+ typedef void value_type;
+ typedef void difference_type;
+ typedef void pointer;
+ typedef void reference;
+};
+
+template <class _Tp, class _Distance> struct forward_iterator {
+ typedef forward_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef _Distance difference_type;
+ typedef _Tp* pointer;
+ typedef _Tp& reference;
+};
+
+
+template <class _Tp, class _Distance> struct bidirectional_iterator {
+ typedef bidirectional_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef _Distance difference_type;
+ typedef _Tp* pointer;
+ typedef _Tp& reference;
+};
+
+template <class _Tp, class _Distance> struct random_access_iterator {
+ typedef random_access_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef _Distance difference_type;
+ typedef _Tp* pointer;
+ typedef _Tp& reference;
+};
+
+template <class _Category, class _Tp, class _Distance = ptrdiff_t,
+ class _Pointer = _Tp*, class _Reference = _Tp&>
+struct iterator {
+ typedef _Category iterator_category;
+ typedef _Tp value_type;
+ typedef _Distance difference_type;
+ typedef _Pointer pointer;
+ typedef _Reference reference;
+};
+
+template <class _Iterator>
+struct iterator_traits {
+ typedef typename _Iterator::iterator_category iterator_category;
+ typedef typename _Iterator::value_type value_type;
+ typedef typename _Iterator::difference_type difference_type;
+ typedef typename _Iterator::pointer pointer;
+ typedef typename _Iterator::reference reference;
+};
+
+template <class _Tp>
+struct iterator_traits<_Tp*> {
+ typedef random_access_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp* pointer;
+ typedef _Tp& reference;
+};
+
+template <class _Tp>
+struct iterator_traits<const _Tp*> {
+ typedef random_access_iterator_tag iterator_category;
+ typedef _Tp value_type;
+ typedef ptrdiff_t difference_type;
+ typedef const _Tp* pointer;
+ typedef const _Tp& reference;
+};
+
+// The overloaded functions iterator_category, distance_type, and
+// value_type are not part of the C++ standard. (They have been
+// replaced by struct iterator_traits.) They are included for
+// backward compatibility with the HP STL.
+
+// We introduce internal names for these functions.
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::iterator_category
+__iterator_category(const _Iter&)
+{
+ typedef typename iterator_traits<_Iter>::iterator_category _Category;
+ return _Category();
+}
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::difference_type*
+__distance_type(const _Iter&)
+{
+ return static_cast<typename iterator_traits<_Iter>::difference_type*>(0);
+}
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::value_type*
+__value_type(const _Iter&)
+{
+ return static_cast<typename iterator_traits<_Iter>::value_type*>(0);
+}
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::iterator_category
+iterator_category(const _Iter& __i) { return __iterator_category(__i); }
+
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::difference_type*
+distance_type(const _Iter& __i) { return __distance_type(__i); }
+
+template <class _Iter>
+inline typename iterator_traits<_Iter>::value_type*
+value_type(const _Iter& __i) { return __value_type(__i); }
+
+#define __ITERATOR_CATEGORY(__i) __iterator_category(__i)
+#define __DISTANCE_TYPE(__i) __distance_type(__i)
+#define __VALUE_TYPE(__i) __value_type(__i)
+
+template <class _InputIterator, class _Distance>
+inline void __distance(_InputIterator __first, _InputIterator __last,
+ _Distance& __n, input_iterator_tag)
+{
+ while (__first != __last) { ++__first; ++__n; }
+}
+
+template <class _RandomAccessIterator, class _Distance>
+inline void __distance(_RandomAccessIterator __first,
+ _RandomAccessIterator __last,
+ _Distance& __n, random_access_iterator_tag)
+{
+ __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
+ __n += __last - __first;
+}
+
+template <class _InputIterator, class _Distance>
+inline void distance(_InputIterator __first,
+ _InputIterator __last, _Distance& __n)
+{
+ __STL_REQUIRES(_InputIterator, _InputIterator);
+ __distance(__first, __last, __n, iterator_category(__first));
+}
+
+#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
+
+template <class _InputIterator>
+inline typename iterator_traits<_InputIterator>::difference_type
+__distance(_InputIterator __first, _InputIterator __last, input_iterator_tag)
+{
+ typename iterator_traits<_InputIterator>::difference_type __n = 0;
+ while (__first != __last) {
+ ++__first; ++__n;
+ }
+ return __n;
+}
+
+template <class _RandomAccessIterator>
+inline typename iterator_traits<_RandomAccessIterator>::difference_type
+__distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
+ random_access_iterator_tag) {
+ __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
+ return __last - __first;
+}
+
+template <class _InputIterator>
+inline typename iterator_traits<_InputIterator>::difference_type
+distance(_InputIterator __first, _InputIterator __last) {
+ typedef typename iterator_traits<_InputIterator>::iterator_category
+ _Category;
+ __STL_REQUIRES(_InputIterator, _InputIterator);
+ return __distance(__first, __last, _Category());
+}
+
+#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
+
+template <class _InputIter, class _Distance>
+inline void __advance(_InputIter& __i, _Distance __n, input_iterator_tag) {
+ while (__n--) ++__i;
+}
+
+#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
+#pragma set woff 1183
+#endif
+
+template <class _BidirectionalIterator, class _Distance>
+inline void __advance(_BidirectionalIterator& __i, _Distance __n,
+ bidirectional_iterator_tag) {
+ __STL_REQUIRES(_BidirectionalIterator, _BidirectionalIterator);
+ if (__n >= 0)
+ while (__n--) ++__i;
+ else
+ while (__n++) --__i;
+}
+
+#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
+#pragma reset woff 1183
+#endif
+
+template <class _RandomAccessIterator, class _Distance>
+inline void __advance(_RandomAccessIterator& __i, _Distance __n,
+ random_access_iterator_tag) {
+ __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
+ __i += __n;
+}
+
+template <class _InputIterator, class _Distance>
+inline void advance(_InputIterator& __i, _Distance __n) {
+ __STL_REQUIRES(_InputIterator, _InputIterator);
+ __advance(__i, __n, iterator_category(__i));
+}
+
+#endif /* __SGI_STL_INTERNAL_ITERATOR_BASE_H */
+
+
+
+// Local Variables:
+// mode:C++
+// End:
diff --git a/WebKit/android/stl/strings.h b/WebKit/android/stl/strings.h
new file mode 100644
index 0000000..599a890
--- /dev/null
+++ b/WebKit/android/stl/strings.h
@@ -0,0 +1,25 @@
+/*
+ * 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 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.
+ */
+
diff --git a/WebKit/android/wds/Command.cpp b/WebKit/android/wds/Command.cpp
new file mode 100644
index 0000000..e150d4f
--- /dev/null
+++ b/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 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 "wds"
+#include "config.h"
+
+#include "AndroidLog.h"
+#include "CString.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>
+
+#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/WebKit/android/wds/Command.h b/WebKit/android/wds/Command.h
new file mode 100644
index 0000000..753486d
--- /dev/null
+++ b/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 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 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/WebKit/android/wds/Connection.cpp b/WebKit/android/wds/Connection.cpp
new file mode 100644
index 0000000..1afd00c
--- /dev/null
+++ b/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 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 "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/WebKit/android/wds/Connection.h b/WebKit/android/wds/Connection.h
new file mode 100644
index 0000000..ce88316
--- /dev/null
+++ b/WebKit/android/wds/Connection.h
@@ -0,0 +1,85 @@
+/*
+ * 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 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 WDS_CONNECTION_H
+#define WDS_CONNECTION_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/WebKit/android/wds/DebugServer.cpp b/WebKit/android/wds/DebugServer.cpp
new file mode 100644
index 0000000..0990452
--- /dev/null
+++ b/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 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 "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/WebKit/android/wds/DebugServer.h b/WebKit/android/wds/DebugServer.h
new file mode 100644
index 0000000..a0a2aa8
--- /dev/null
+++ b/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 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 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();
+ Vector<Frame*> m_frames;
+ ThreadIdentifier m_threadId;
+ friend DebugServer* server();
+};
+
+DebugServer* server();
+
+} // end namespace WDS
+
+} // end namespace android
+
+#endif
diff --git a/WebKit/android/wds/client/AdbConnection.cpp b/WebKit/android/wds/client/AdbConnection.cpp
new file mode 100644
index 0000000..f83ad2f
--- /dev/null
+++ b/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 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 "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/WebKit/android/wds/client/AdbConnection.h b/WebKit/android/wds/client/AdbConnection.h
new file mode 100644
index 0000000..433d7ea
--- /dev/null
+++ b/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 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 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/WebKit/android/wds/client/Android.mk b/WebKit/android/wds/client/Android.mk
new file mode 100644
index 0000000..e6bf7cf
--- /dev/null
+++ b/WebKit/android/wds/client/Android.mk
@@ -0,0 +1,41 @@
+##
+##
+## 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 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 := \
+ AdbConnection.cpp \
+ ClientUtils.cpp \
+ Device.cpp \
+ main.cpp
+
+LOCAL_STATIC_LIBRARIES := liblog libutils libcutils
+
+LOCAL_MODULE:= wdsclient
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/WebKit/android/wds/client/ClientUtils.cpp b/WebKit/android/wds/client/ClientUtils.cpp
new file mode 100644
index 0000000..03514f6
--- /dev/null
+++ b/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 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 "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/WebKit/android/wds/client/ClientUtils.h b/WebKit/android/wds/client/ClientUtils.h
new file mode 100644
index 0000000..0aa068e
--- /dev/null
+++ b/WebKit/android/wds/client/ClientUtils.h
@@ -0,0 +1,38 @@
+/*
+ * 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 WDS_CLIENT_UTILS_H
+#define WDS_CLIENT_UTILS_H
+
+#include <arpa/inet.h>
+
+// 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/WebKit/android/wds/client/Device.cpp b/WebKit/android/wds/client/Device.cpp
new file mode 100644
index 0000000..6c36979
--- /dev/null
+++ b/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 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 "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/WebKit/android/wds/client/Device.h b/WebKit/android/wds/client/Device.h
new file mode 100644
index 0000000..df076cb
--- /dev/null
+++ b/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 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 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/WebKit/android/wds/client/DeviceList.h b/WebKit/android/wds/client/DeviceList.h
new file mode 100644
index 0000000..0b278d2
--- /dev/null
+++ b/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 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 WDS_DEVICE_LIST_H
+#define WDS_DEVICE_LIST_H
+
+#include <utils/Vector.h>
+
+class Device;
+
+typedef android::Vector<Device*> DeviceList;
+
+#endif
diff --git a/WebKit/android/wds/client/main.cpp b/WebKit/android/wds/client/main.cpp
new file mode 100644
index 0000000..bc8df86
--- /dev/null
+++ b/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 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 "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;
+}