summaryrefslogtreecommitdiffstats
path: root/WebKit/android
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-01-09 17:51:23 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-01-09 17:51:23 -0800
commite933faefa1e899dbd5bf371f499cc682aff46c83 (patch)
tree8fb31ff5c9a41aec9912d0253be7ef0445e2f58a /WebKit/android
parent1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (diff)
downloadexternal_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.zip
external_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.tar.gz
external_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.tar.bz2
auto import from //branches/cupcake/...@125939
Diffstat (limited to 'WebKit/android')
-rw-r--r--WebKit/android/jni/WebCoreFrameBridge.cpp7
-rw-r--r--WebKit/android/jni/WebViewCore.cpp135
-rw-r--r--WebKit/android/jni/WebViewCore.h17
-rw-r--r--WebKit/android/nav/CacheBuilder.cpp47
-rw-r--r--WebKit/android/nav/CacheBuilder.h4
-rw-r--r--WebKit/android/nav/FindCanvas.cpp197
-rw-r--r--WebKit/android/nav/FindCanvas.h65
-rw-r--r--WebKit/android/nav/WebView.cpp111
-rw-r--r--WebKit/android/plugins/sample/main.cpp1
-rw-r--r--WebKit/android/wds/Command.cpp145
-rw-r--r--WebKit/android/wds/Command.h99
-rw-r--r--WebKit/android/wds/Connection.cpp84
-rw-r--r--WebKit/android/wds/Connection.h76
-rw-r--r--WebKit/android/wds/DebugServer.cpp107
-rw-r--r--WebKit/android/wds/DebugServer.h68
-rw-r--r--WebKit/android/wds/client/AdbConnection.cpp228
-rw-r--r--WebKit/android/wds/client/AdbConnection.h38
-rw-r--r--WebKit/android/wds/client/Android.mk32
-rw-r--r--WebKit/android/wds/client/ClientUtils.cpp26
-rw-r--r--WebKit/android/wds/client/ClientUtils.h29
-rw-r--r--WebKit/android/wds/client/Device.cpp22
-rw-r--r--WebKit/android/wds/client/Device.h53
-rw-r--r--WebKit/android/wds/client/DeviceList.h26
-rw-r--r--WebKit/android/wds/client/main.cpp164
24 files changed, 1505 insertions, 276 deletions
diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp
index 9f55134..a173305 100644
--- a/WebKit/android/jni/WebCoreFrameBridge.cpp
+++ b/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -70,6 +70,7 @@
#include "WebIconDatabase.h"
#include "WebFrameView.h"
#include "WebViewCore.h"
+#include "wds/DebugServer.h"
#include <runtime_root.h>
#include <runtime_object.h>
@@ -736,6 +737,9 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss
// 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);
@@ -793,6 +797,9 @@ static void DestroyFrame(JNIEnv* env, jobject obj)
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)
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp
index 363f2be..42a0043 100644
--- a/WebKit/android/jni/WebViewCore.cpp
+++ b/WebKit/android/jni/WebViewCore.cpp
@@ -1156,10 +1156,22 @@ bool WebViewCore::finalKitFocus(WebCore::Frame* frame, WebCore::Node* node,
}
if (!node->isTextNode())
static_cast<WebCore::Element*>(node)->focus(false);
- //setFocus on things that WebCore doesn't recognize as supporting focus
- //for instance, if there is an onclick element that does not support focus
+ 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);
+ }
DBG_NAV_LOGD("setFocusedNode node=%p", node);
- node->document()->setFocusedNode(node);
m_lastFocused = node;
m_lastFocusedBounds = node->getRect();
return true;
@@ -1527,7 +1539,7 @@ public:
WebCore::HTMLOptionElement* option;
for (int i = 0; i < count; i++) {
option = static_cast<WebCore::HTMLOptionElement*>(
- m_select->item(array[m_select->listToOptionIndex(i)]));
+ m_select->item(m_select->listToOptionIndex(array[i])));
option->setSelected(true);
}
m_viewImpl->contentInvalidate(m_select->getRect());
@@ -1609,78 +1621,40 @@ void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, s
m_popupReply = reply;
}
-bool WebViewCore::keyUp(KeyCode keyCode, int keyVal)
+bool WebViewCore::key(int keyCode, UChar32 unichar, int repeatCount, bool isShift, bool isAlt, bool isDown)
{
- DBG_NAV_LOGD("keyCode=%d", keyCode);
- bool keyHandled = false;
+ DBG_NAV_LOGD("key: keyCode=%d", keyCode);
+
+ WebCore::EventHandler* eventHandler = m_mainFrame->eventHandler();
WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
if (focusNode) {
- WebCore::Frame* focusFrame = focusNode->document()->frame();
- switch (keyCode) {
- case kKeyCodeNewline:
- case kKeyCodeDpadCenter: {
- focusFrame->loader()->resetMultipleFormSubmissionProtection();
- WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
- webFrame->setUserInitiatedClick(true);
- if ((focusNode->hasTagName(WebCore::HTMLNames::inputTag) &&
- ((WebCore::HTMLInputElement*)focusNode)->isTextField()) ||
- focusNode->hasTagName(WebCore::HTMLNames::textareaTag)) {
- // Create the key down event.
- WebCore::PlatformKeyboardEvent keydown(keyCode, keyVal,
- WebCore::PlatformKeyboardEvent::KeyDown, 0,
- NO_MODIFIER_KEYS);
- // Create the key up event.
- WebCore::PlatformKeyboardEvent keyup(keyCode, keyVal,
- WebCore::PlatformKeyboardEvent::KeyUp, 0,
- NO_MODIFIER_KEYS);
- // Send both events.
- keyHandled = focusFrame->eventHandler()->keyEvent(keydown);
- keyHandled |= focusFrame->eventHandler()->keyEvent(keyup);
- } else {
- keyHandled = handleMouseClick(focusFrame, focusNode);
- }
- webFrame->setUserInitiatedClick(false);
- break;
- }
- default:
- keyHandled = false;
- }
+ eventHandler = focusNode->document()->frame()->eventHandler();
}
- m_blockFocusChange = false;
- return keyHandled;
+
+ 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::sendKeyToFocusNode(int keyCode, UChar32 unichar,
- int repeatCount, bool isShift, bool isAlt, KeyAction action) {
+bool WebViewCore::click() {
+ bool keyHandled = false;
WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
if (focusNode) {
- WebCore::Frame* focusFrame = focusNode->document()->frame();
- WebCore::PlatformKeyboardEvent::Type type;
- switch (action) {
- case DownKeyAction:
- type = WebCore::PlatformKeyboardEvent::KeyDown;
- break;
- case UpKeyAction:
- type = WebCore::PlatformKeyboardEvent::KeyUp;
- break;
- default:
- SkDebugf("---- unexpected KeyAction %d\n", action);
- return false;
- }
-
- int mods = 0; // PlatformKeyboardEvent::ModifierKey
- if (isShift) {
- mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
- }
- if (isAlt) {
- mods |= WebCore::PlatformKeyboardEvent::AltKey;
- }
-
- WebCore::PlatformKeyboardEvent evt(keyCode, unichar, type, repeatCount,
- static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
- return focusFrame->eventHandler()->keyEvent(evt);
+ WebFrame::getWebFrame(m_mainFrame)->setUserInitiatedClick(true);
+ keyHandled = handleMouseClick(focusNode->document()->frame(), focusNode);
+ WebFrame::getWebFrame(m_mainFrame)->setUserInitiatedClick(false);
}
- return false;
+ // match in setFinalFocus()
+ m_blockFocusChange = false;
+ return keyHandled;
}
bool WebViewCore::handleTouchEvent(int action, int x, int y)
@@ -2008,29 +1982,27 @@ static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
viewImpl->setGlobalBounds(x, y, h, v);
}
-static jboolean KeyUp(JNIEnv *env, jobject obj, jint key, jint val)
+static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
+ jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isDown)
{
#ifdef ANDROID_INSTRUMENT
TimeCounterWV counter;
#endif
-// LOGV("webviewcore::nativeKeyUp(%u)\n", (unsigned)key);
WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
- LOG_ASSERT(viewImpl, "viewImpl not set in nativeKeyUp");
- return viewImpl->keyUp((KeyCode)key, val);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Key");
+
+ return viewImpl->key(keyCode, unichar, repeatCount, isShift, isAlt, isDown);
}
-static bool SendKeyToFocusNode(JNIEnv *env, jobject jwebviewcore,
- jint keyCode, jint unichar, jint repeatCount,
- jboolean isShift, jboolean isAlt, jint action)
+static jboolean Click(JNIEnv *env, jobject obj)
{
#ifdef ANDROID_INSTRUMENT
TimeCounterWV counter;
#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Click");
- WebViewCore* viewImpl = GET_NATIVE_VIEW(env, jwebviewcore);
- LOG_ASSERT(viewImpl, "viewImpl not set in SendKeyToFocusNode");
- return viewImpl->sendKeyToFocusNode((KeyCode)keyCode, unichar, repeatCount,
- isShift, isAlt, static_cast<WebViewCore::KeyAction>(action));
+ return viewImpl->click();
}
static void DeleteSelection(JNIEnv *env, jobject obj,
@@ -2390,9 +2362,10 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = {
(void*) CopyContentToPicture },
{ "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z",
(void*) DrawContent } ,
- { "nativeKeyUp", "(II)Z",
- (void*) KeyUp },
- { "nativeSendKeyToFocusNode", "(IIIZZI)Z", (void*) SendKeyToFocusNode },
+ { "nativeKey", "(IIIZZZ)Z",
+ (void*) Key },
+ { "nativeClick", "()Z",
+ (void*) Click },
{ "nativeSendListBoxChoices", "([ZI)V",
(void*) SendListBoxChoices },
{ "nativeSendListBoxChoice", "(I)V",
diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h
index 8a073c2..a6c44f8 100644
--- a/WebKit/android/jni/WebViewCore.h
+++ b/WebKit/android/jni/WebViewCore.h
@@ -189,24 +189,15 @@ namespace android {
void setSizeScreenWidthAndScale(int width, int height, int screenWidth, int scale);
/**
- * Handle keyDown events from Java.
- * @param keyCode The key pressed.
+ * Handle key events from Java.
* @return Whether keyCode was handled by this class.
*/
- bool keyUp(KeyCode keyCode, int keyValue);
+ bool key(int keyCode, UChar32 unichar, int repeatCount, bool isShift, bool isAlt, bool isDown);
- // These need to be lock-step with the KEY_ACTION values in
- // WebViewCore.java
- enum KeyAction {
- DownKeyAction = 0,
- UpKeyAction = 1
- };
/**
- * Send the key event to the focus node (if there is one). Return true
- * if there is a node, and it claims to have handled the event.
+ * Handle (mouse) click event from Java
*/
- bool sendKeyToFocusNode(int code, UChar32 unichar, int repeatCount,
- bool isShift, bool isAlt, KeyAction);
+ bool click();
/**
* Handle touch event
diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp
index da32898..69d0a06 100644
--- a/WebKit/android/nav/CacheBuilder.cpp
+++ b/WebKit/android/nav/CacheBuilder.cpp
@@ -27,6 +27,7 @@
#include "HTMLAreaElement.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
+#include "HTMLMapElement.h"
#include "HTMLNames.h"
#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
@@ -398,7 +399,7 @@ void CacheBuilder::Debug::groups() {
properties.truncate(properties.length() - 3);
IntRect rect = node->getRect();
if (node->hasTagName(HTMLNames::areaTag))
- rect = static_cast<HTMLAreaElement*>(node)->getAreaRect();
+ rect = Builder(frame)->getAreaRect(static_cast<HTMLAreaElement*>(node));
char buffer[DEBUG_BUFFER_SIZE];
memset(buffer, 0, sizeof(buffer));
mBuffer = buffer;
@@ -1004,12 +1005,20 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
RenderStyle* style = nodeRenderer->style();
if (style->visibility() == HIDDEN)
continue;
-#ifdef ANDROID_NAVIGATE_AREAMAPS
if (nodeRenderer->isImage()) { // set all the area elements to have a link to their images
RenderImage* image = static_cast<RenderImage*>(nodeRenderer);
- image->setImageForAreaElements();
+ 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);
+ }
+ }
}
-#endif
isTransparent = style->hasBackground() == false;
#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
hasFocusRing = style->tapHighlightColor().alpha() > 0;
@@ -1043,10 +1052,9 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
IntRect absBounds;
Node* lastChild = node->lastChild();
WTF::Vector<IntRect>* columns = NULL;
-#ifdef ANDROID_NAVIGATE_AREAMAPS
if (isArea) {
HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
- bounds = area->getAreaRect();
+ bounds = getAreaRect(area);
bounds.move(globalOffsetX, globalOffsetY);
absBounds = bounds;
isUnclipped = true; // FIXME: areamaps require more effort to detect
@@ -1054,7 +1062,6 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
takesFocus = true;
goto keepNode;
}
-#endif
if (nodeRenderer == NULL)
continue;
@@ -1183,6 +1190,9 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
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)
@@ -1233,9 +1243,6 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
else
clip.intersect(parentClip);
hasClip = true;
- DBG_NAV_LOGD("absBounds={%d,%d,%d,%d} parentClip={%d,%d,%d,%d}\n",
- absBounds.x(), absBounds.y(), absBounds.width(), absBounds.height(),
- parentClip.x(), parentClip.y(), parentClip.width(), parentClip.height());
}
if (hasClip && cachedNode.clip(clip) == false) {
cachedNode.setBounds(clip);
@@ -2365,6 +2372,16 @@ void CacheBuilder::FindResetNumber(FindState* state)
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);
@@ -2459,13 +2476,11 @@ Node* CacheBuilder::findByCenter(int x, int y) const
if (node->isTextNode())
continue;
IntRect bounds;
-#ifdef ANDROID_NAVIGATE_AREAMAPS
if (node->hasTagName(HTMLNames::areaTag)) {
HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node);
- bounds = area->getAreaRect();
+ bounds = getAreaRect(area);
bounds.move(globalOffsetX, globalOffsetY);
} else
-#endif
bounds = node->getRect();
if (bounds.isEmpty())
continue;
@@ -2775,12 +2790,8 @@ bool CacheBuilder::validNode(void* matchFrame, void* matchNode) const
Node* node = frame->document();
while (node != NULL) {
if (node == matchNode) {
-#ifdef ANDROID_NAVIGATE_AREAMAPS
const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ?
- static_cast<HTMLAreaElement*>(node)->getAreaRect() : node->getRect();
-#else
- const IntRect& rect = node->getRect();
-#endif
+ 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())
diff --git a/WebKit/android/nav/CacheBuilder.h b/WebKit/android/nav/CacheBuilder.h
index 075042e..4a424a9 100644
--- a/WebKit/android/nav/CacheBuilder.h
+++ b/WebKit/android/nav/CacheBuilder.h
@@ -37,10 +37,12 @@ namespace WebCore {
class Document;
class Frame;
+class HTMLAreaElement;
class InlineTextBox;
class Node;
class PlatformGraphicsContext;
class RenderFlow;
+class RenderImage;
class RenderObject;
class RenderLayer;
class Text;
@@ -193,6 +195,7 @@ private:
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* );
@@ -208,6 +211,7 @@ private:
Node* mLastKnownFocus;
IntRect mLastKnownFocusBounds;
android::CachedNodeType mAllowableTypes;
+ WTF::HashMap<const HTMLAreaElement* , RenderImage* > m_areaBoundsMap;
#if DUMP_NAV_CACHE
public:
class Debug {
diff --git a/WebKit/android/nav/FindCanvas.cpp b/WebKit/android/nav/FindCanvas.cpp
index 5b6978c..60a7486 100644
--- a/WebKit/android/nav/FindCanvas.cpp
+++ b/WebKit/android/nav/FindCanvas.cpp
@@ -19,6 +19,31 @@
#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
////////////////////////////////////////////////////////////////////////////////
@@ -81,33 +106,17 @@ FindCanvas::FindCanvas(int width, int height, const UChar* lower,
setBounder(&mBounder);
mOutset = -SkIntToScalar(2);
- mRegions = new WTF::Vector<SkRegion>();
-#if RECORD_MATCHES
- mPicture = new SkPicture();
- mRecordingCanvas = mPicture->beginRecording(width, height);
-#endif
+ mMatches = new WTF::Vector<MatchInfo>();
mWorkingIndex = 0;
+ mWorkingCanvas = 0;
+ mWorkingPicture = 0;
}
FindCanvas::~FindCanvas() {
setBounder(NULL);
/* Just in case getAndClear was not called. */
- delete mRegions;
-#if RECORD_MATCHES
- mPicture->unref();
-#endif
-}
-
-// SkPaint.measureText is used to get a rectangle surrounding the specified
-// text. It is a tighter bounds than we want. We want the height to account
-// for the ascent and descent of the font, so that the rectangles will line up,
-// regardless of the characters contained in them.
-static void correctSize(const SkPaint& paint, SkRect& rect)
-{
- SkPaint::FontMetrics fontMetrics;
- paint.getFontMetrics(&fontMetrics);
- rect.fTop = fontMetrics.fAscent;
- rect.fBottom = fontMetrics.fDescent;
+ delete mMatches;
+ mWorkingPicture->safeUnref();
}
// Each version of addMatch returns a rectangle for a match.
@@ -117,21 +126,24 @@ SkRect FindCanvas::addMatchNormal(int index,
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),
- NULL);
+ SkScalar before = paint.measureText(lineStart, index * sizeof(uint16_t), 0);
SkRect rect;
- int countInBytes = count << 1;
- paint.measureText(glyphs, countInBytes, &rect);
- correctSize(paint, rect);
- rect.offset(pos[0] + before, y);
- getTotalMatrix().mapRect(&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.
-#if RECORD_MATCHES
- mRecordingCanvas->setMatrix(getTotalMatrix());
- SkPoint point;
- matrix.mapXY(pos[0] + before, y, &point);
- mRecordingCanvas->drawText(glyphs, countInBytes, point.fX, point.fY, paint);
-#endif
+ SkCanvas* canvas = getWorkingCanvas();
+ int saveCount = canvas->save();
+ canvas->concat(matrix);
+ canvas->drawText(glyphs, countInBytes, pos[0] + before, y, paint);
+ canvas->restoreToCount(saveCount);
return rect;
}
@@ -140,27 +152,31 @@ SkRect FindCanvas::addMatchPos(int index,
const SkScalar xPos[], SkScalar /* y */) {
SkRect r;
r.setEmpty();
- const SkScalar* pos = &xPos[index << 1];
- int countInBytes = count << 1;
+ 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);
- for (int j = 0; j < countInBytes; j += 2) {
+ // Need to check each character individually, since the heights may be
+ // different.
+ for (int j = 0; j < count; j++) {
SkRect bounds;
- paint.getTextWidths(&(glyphs[j >> 1]), 2, NULL, &bounds);
- bounds.fTop = fontMetrics.fAscent;
- bounds.fBottom = fontMetrics.fDescent;
- bounds.offset(pos[j], pos[j+1]);
- /* Accumulate and then add the resulting rect to mRegions */
+ 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);
}
- getTotalMatrix().mapRect(&r);
- // Add the text to our picture.
-#if RECORD_MATCHES
- mRecordingCanvas->setMatrix(getTotalMatrix());
- // FIXME: Need to do more work to get xPos and constY in the proper
- // coordinates.
- //mRecordingCanvas->drawPosText(glyphs, countInBytes, positions, paint);
-#endif
+ 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;
}
@@ -168,24 +184,28 @@ SkRect FindCanvas::addMatchPosH(int index,
const SkPaint& paint, int count, const uint16_t* glyphs,
const SkScalar position[], SkScalar constY) {
SkRect r;
- r.setEmpty();
+ // We only care about the positions starting at the index of our match
const SkScalar* xPos = &position[index];
- for (int j = 0; j < count; j++) {
- SkRect bounds;
- paint.getTextWidths(&glyphs[j], 1, NULL, &bounds);
- bounds.offset(xPos[j], constY);
- /* Accumulate and then add the resulting rect to mRegions */
- r.join(bounds);
- }
- correctSize(paint, r);
- getTotalMatrix().mapRect(&r);
- // Add the text to our picture.
-#if RECORD_MATCHES
- mRecordingCanvas->setMatrix(getTotalMatrix());
- // FIXME: Need to do more work to get xPos and constY in the proper
- // coordinates.
- //mRecordingCanvas->drawPosTextH(glyphs, count << 1, xPos, constY, paint);
-#endif
+ // 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;
}
@@ -236,10 +256,7 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
const uint16_t* glyphs,
const SkScalar positions[], SkScalar y)) {
SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
- SkASSERT(mRegions);
-#if RECORD_MATCHES
- SkASSERT(mRecordingCanvas);
-#endif
+ SkASSERT(mMatches);
GlyphSet* glyphSet = getGlyphs(paint);
const int count = glyphSet->getCount();
int numCharacters = byteLength >> 1;
@@ -249,7 +266,8 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
if (mWorkingIndex) {
SkPoint newY;
getTotalMatrix().mapXY(0, y, &newY);
- if (mWorkingRegion.getBounds().fBottom < SkScalarRound(newY.fY)) {
+ SkIRect bounds = mWorkingRegion.getBounds();
+ if (bounds.fBottom < SkScalarRound(newY.fY)) {
// Now we know that this line is lower than our partial match.
SkPaint clonePaint(paint);
clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
@@ -260,14 +278,16 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
if (mWorkingIndex == count) {
// We already know that it is not clipped out because we
// checked for that before saving the working region.
- mNumFound++;
- mRegions->append(mWorkingRegion);
+ 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();
}
@@ -288,7 +308,6 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
}
// The last count characters match, so we found the entire
// search string.
- mNumFound++;
int remaining = count - mWorkingIndex;
int matchIndex = index - remaining + 1;
// Set up a pointer to the matching text in 'chars'.
@@ -325,7 +344,7 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
// the previous line(s).
regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op);
}
- mRegions->append(regionToAdd);
+ 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
@@ -338,6 +357,9 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
// 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
@@ -389,6 +411,14 @@ void FindCanvas::findHelper(const void* text, size_t byteLength,
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();
@@ -402,3 +432,18 @@ GlyphSet* FindCanvas::getGlyphs(const SkPaint& paint) {
*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
index fe1e3d2..5558c8b 100644
--- a/WebKit/android/nav/FindCanvas.h
+++ b/WebKit/android/nav/FindCanvas.h
@@ -17,23 +17,37 @@
#ifndef Find_Canvas_h
#define Find_Canvas_h
-// The code marked with this is used to record the draw calls into an SkPicture,
-// which is passed to the caller to draw the matches on top of the opaque green
-// rectangles. The code is a checkpoint.
-#define RECORD_MATCHES 0
-
#include "SkBounder.h"
#include "SkCanvas.h"
+#include "SkPicture.h"
#include "SkRegion.h"
#include "SkTDArray.h"
#include "icu/unicode/umachine.h"
-#if RECORD_MATCHES
-class SkPicture;
-#endif
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 {
@@ -113,20 +127,14 @@ public:
int found() const { return mNumFound; }
- // This method detaches our array of regions and passes ownership to
+ // This method detaches our array of matches and passes ownership to
// the caller, who is then responsible for deleting them.
- WTF::Vector<SkRegion>* detachRegions() {
- WTF::Vector<SkRegion>* array = mRegions;
- mRegions = NULL;
+ WTF::Vector<MatchInfo>* detachMatches() {
+ WTF::Vector<MatchInfo>* array = mMatches;
+ mMatches = NULL;
return array;
}
-#if RECORD_MATCHES
- // This SkPicture contains only draw calls for the drawn text. This is
- // used to draw over the highlight rectangle so that it can be seen.
- SkPicture* getDrawnMatches() const { return mPicture; }
-#endif
-
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
@@ -144,7 +152,7 @@ private:
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,
@@ -152,14 +160,25 @@ private:
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<SkRegion>* mRegions;
+ WTF::Vector<MatchInfo>* mMatches;
const UChar* mLowerText;
const UChar* mUpperText;
size_t mLength;
@@ -167,11 +186,9 @@ private:
int mNumFound;
SkScalar mOutset;
SkTDArray<GlyphSet> mGlyphSets;
-#if RECORD_MATCHES
- SkPicture* mPicture;
- SkCanvas* mRecordingCanvas;
-#endif
+ SkPicture* mWorkingPicture;
+ SkCanvas* mWorkingCanvas;
SkRegion mWorkingRegion;
int mWorkingIndex;
};
diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp
index 7af96d7..63c6aeb 100644
--- a/WebKit/android/nav/WebView.cpp
+++ b/WebKit/android/nav/WebView.cpp
@@ -419,10 +419,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl)
m_matches = 0;
m_hasCurrentLocation = false;
m_isFindPaintSetUp = false;
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- m_matchesPicture = 0;
-#endif
}
~WebView()
@@ -437,10 +433,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl)
delete m_navPictureUI;
if (m_matches)
delete m_matches;
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- m_matchesPicture->safeUnref();
-#endif
}
void clearFocus(int x, int y, bool inval)
@@ -548,9 +540,7 @@ void setUpFindPaint()
const SkScalar roundiness = SkIntToScalar(2);
SkCornerPathEffect* cornerEffect = new SkCornerPathEffect(roundiness);
m_findPaint.setPathEffect(cornerEffect);
- // FIXME: Would like this to be opaque, but then the user cannot see the
- // text behind it. We will then need to redraw the text on top of it.
- m_findPaint.setARGB(204, 132, 190, 0);
+ m_findPaint.setARGB(255, 132, 190, 0);
// Set up the background blur paint.
m_findBlurPaint.setAntiAlias(true);
@@ -583,26 +573,34 @@ void drawMatch(const SkRegion& region, SkCanvas* canvas, bool focused)
// 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()
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- || !m_matchesPicture
-#endif
- ) {
+ if (!m_matches || !m_matches->size()) {
return;
}
if (m_findIndex >= m_matches->size()) {
m_findIndex = 0;
}
- const SkRegion& currentMatchRegion = (*m_matches)[m_findIndex];
+ 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;
@@ -636,36 +634,29 @@ void drawMatches(SkCanvas* canvas)
setUpFindPaint();
// Draw the current match
- drawMatch(currentMatchRegion, canvas, true);
+ 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();
- int saveCount = 0;
- if (numberOfMatches > 1) {
+ 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];
- // Do not draw matches which intersect the current one
- if (currentMatchRegion.intersects(region))
+ 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);
}
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- // Set a clip so we do not draw the text for the other matches.
- saveCount = canvas->save(SkCanvas::kClip_SaveFlag);
- canvas->clipRect(currentMatch);
-#endif
}
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- canvas->drawPicture(*m_matchesPicture);
- if (numberOfMatches > 1) {
- canvas->restoreToCount(saveCount);
- }
-#endif
}
void drawFocusRing(SkCanvas* canvas)
@@ -1112,6 +1103,18 @@ bool moveFocus(int keyCode, int count, bool ignoreScroll, bool inval,
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)
@@ -1608,8 +1611,8 @@ void setFocusData(int buildGeneration, WebCore::Frame* framePtr,
// m_currentMatchLocation.
void inline storeCurrentMatchLocation()
{
- SkASSERT(m_findIndex < m_matches->size() && m_findIndex >= 0);
- const SkIRect& bounds = (*m_matches)[m_findIndex].getBounds();
+ 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;
}
@@ -1635,12 +1638,7 @@ void findNext(bool forward)
// With this call, WebView takes ownership of matches, and is responsible for
// deleting it.
-void setMatches(WTF::Vector<SkRegion>* matches
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- , SkPicture* pic
-#endif
- )
+void setMatches(WTF::Vector<MatchInfo>* matches)
{
if (m_matches)
delete m_matches;
@@ -1648,7 +1646,7 @@ void setMatches(WTF::Vector<SkRegion>* matches
if (m_matches->size()) {
if (m_hasCurrentLocation) {
for (unsigned i = 0; i < m_matches->size(); i++) {
- const SkIRect& rect = (*m_matches)[i].getBounds();
+ const SkIRect& rect = (*m_matches)[i].getLocation().getBounds();
if (rect.fLeft == m_currentMatchLocation.fX
&& rect.fTop == m_currentMatchLocation.fY) {
m_findIndex = i;
@@ -1664,12 +1662,6 @@ void setMatches(WTF::Vector<SkRegion>* matches
} else {
m_hasCurrentLocation = false;
}
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- m_matchesPicture->safeUnref();
- m_matchesPicture = pic;
- m_matchesPicture->ref();
-#endif
viewInvalidate();
}
@@ -1778,7 +1770,7 @@ private: // local state for WebView
bool m_heightCanMeasure;
int m_lastDx;
SkMSec m_lastDxTime;
- WTF::Vector<SkRegion>* m_matches;
+ WTF::Vector<MatchInfo>* m_matches;
// Stores the location of the current match.
SkIPoint m_currentMatchLocation;
// Tells whether the value in m_currentMatchLocation is valid.
@@ -1789,10 +1781,6 @@ private: // local state for WebView
SkPaint m_findPaint;
// Paint used for the background of our Find matches.
SkPaint m_findBlurPaint;
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- SkPicture* m_matchesPicture;
-#endif
unsigned m_findIndex;
}; // end of WebView class
@@ -2096,14 +2084,9 @@ static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
canvas.setBitmapDevice(bitmap);
canvas.drawPicture(*(root->getPicture()));
- WTF::Vector<SkRegion>* matches = canvas.detachRegions();
+ WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
// With setMatches, the WebView takes ownership of matches
- view->setMatches(matches
-// RECORD_MATCHES is defined in FindCanvas.h
-#if RECORD_MATCHES
- , canvas.getDrawnMatches()
-#endif
- );
+ view->setMatches(matches);
env->ReleaseStringChars(findLower, findLowerChars);
env->ReleaseStringChars(findUpper, findUpperChars);
diff --git a/WebKit/android/plugins/sample/main.cpp b/WebKit/android/plugins/sample/main.cpp
index 5d2b0b8..5bb8ea0 100644
--- a/WebKit/android/plugins/sample/main.cpp
+++ b/WebKit/android/plugins/sample/main.cpp
@@ -349,6 +349,7 @@ int16 NPP_HandleEvent(NPP instance, void* event)
evt->data.key.modifiers);
if (evt->data.key.action == kDown_ANPKeyAction) {
obj->mUnichar = evt->data.key.unichar;
+ browser->invalidaterect(instance, NULL);
}
return 1;
diff --git a/WebKit/android/wds/Command.cpp b/WebKit/android/wds/Command.cpp
new file mode 100644
index 0000000..132b8e5
--- /dev/null
+++ b/WebKit/android/wds/Command.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..df45b2b
--- /dev/null
+++ b/WebKit/android/wds/Command.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..b6ebd75
--- /dev/null
+++ b/WebKit/android/wds/Connection.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..f7fa21b
--- /dev/null
+++ b/WebKit/android/wds/Connection.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..8581680
--- /dev/null
+++ b/WebKit/android/wds/DebugServer.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..64db031
--- /dev/null
+++ b/WebKit/android/wds/DebugServer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..79b49d7
--- /dev/null
+++ b/WebKit/android/wds/client/AdbConnection.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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..0b5419f
--- /dev/null
+++ b/WebKit/android/wds/client/AdbConnection.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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..5d07a31
--- /dev/null
+++ b/WebKit/android/wds/client/Android.mk
@@ -0,0 +1,32 @@
+##
+##
+## Copyright 2008, The Android Open Source Project
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+LOCAL_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..067775e
--- /dev/null
+++ b/WebKit/android/wds/client/ClientUtils.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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..dc8b23f
--- /dev/null
+++ b/WebKit/android/wds/client/ClientUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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..03b4507
--- /dev/null
+++ b/WebKit/android/wds/client/Device.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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..c6d77f3
--- /dev/null
+++ b/WebKit/android/wds/client/Device.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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..9139238
--- /dev/null
+++ b/WebKit/android/wds/client/DeviceList.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef 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..5899b16
--- /dev/null
+++ b/WebKit/android/wds/client/main.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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;
+}