diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-09 17:51:23 -0800 |
commit | e933faefa1e899dbd5bf371f499cc682aff46c83 (patch) | |
tree | 8fb31ff5c9a41aec9912d0253be7ef0445e2f58a | |
parent | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (diff) | |
download | external_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.zip external_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.tar.gz external_webkit-e933faefa1e899dbd5bf371f499cc682aff46c83.tar.bz2 |
auto import from //branches/cupcake/...@125939
54 files changed, 1569 insertions, 560 deletions
@@ -18,6 +18,9 @@ BASE_PATH := $(call my-dir) include $(CLEAR_VARS) +# if you need to make webcore huge (for debugging), enable this line +#LOCAL_PRELINK_MODULE := false + # Define our module and find the intermediates directory LOCAL_MODULE := libwebcore LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -77,7 +80,11 @@ LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) \ external/icu4c/common \ external/libxml2/include \ - external/skia/libsgl/ports \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/include/ports \ + external/skia/include/utils \ + external/skia/src/ports \ external/sqlite/dist \ frameworks/base/core/jni/android/graphics \ $(LOCAL_PATH)/WebCore \ @@ -176,3 +183,6 @@ include $(BUILD_SHARED_LIBRARY) # Build the plugin test separately from libwebcore include $(BASE_PATH)/WebKit/android/plugins/sample/Android.mk + +# Build the wds client +include $(BASE_PATH)/WebKit/android/wds/client/Android.mk diff --git a/WebCore/WebCorePrefixAndroid.h b/WebCore/WebCorePrefixAndroid.h index 1f4287f..c933562 100644 --- a/WebCore/WebCorePrefixAndroid.h +++ b/WebCore/WebCorePrefixAndroid.h @@ -68,23 +68,11 @@ typedef unsigned char flex_uint8_t; #define ANDROID_EXPOSE_COLUMN_GAP -// This change was made before we changed ListBoxes to operate the same way -// as drop down lists. -// FIXME: Check to make sure we can delete it. -#define ANDROID_NAVIGATE_LISTBOX - -// Allows us to get the rectangle of an <area> element so we can navigate to it -// This could be submitted back to webkit if anyone else wants to use the -// hit rectangles for navigation. -#define ANDROID_NAVIGATE_AREAMAPS - #define ANDROID_LAYOUT // Allows us to select all of the text in a <textarea> in onfocus #define ANDROID_SELECT_TEXT_AREAS -#define ANDROID_KEYBOARD_NAVIGATION -#define ANDROID_NO_BODY_INNER_HTML #define ANDROID_FIX // note: if uncomment ANDROID_INSTRUMENT here, you must also // uncomment it on line 31 of JavaScriptCore/kjs/config.h @@ -101,14 +89,6 @@ typedef unsigned char flex_uint8_t; // Unnecessary for us, since we handle scrolling outside of WebKit. #define ANDROID_SCROLL_FIX -// Fixes an issue where going back to a page that sets focus to a textfield -// results in restoring the selection rather than selecting all. -#define ANDROID_DO_NOT_RESTORE_PREVIOUS_SELECTION - -// Fix for issue 986508. May be possible to combine with -// ANDROID_DO_NOT_RESTORE_PREVIOUS_SELECTION -#define ANDROID_RESET_SELECTION - #define ANDROID_META_SUPPORT // Give public access to a private method in HTMLSelectElement so that @@ -131,7 +111,7 @@ typedef unsigned char flex_uint8_t; // Changes needed to support native plugins (npapi.h). If the change is generic, // it may be under a different #define (see: PLUGIN_PLATFORM_SETVALUE, -// PLUGIN_SCHEDULE_TIMER, ANDROID_PLUGIN_MAIN_THREAD_SCHEDULER_FIXES) +// PLUGIN_SCHEDULE_TIMER) #define ANDROID_PLUGINS // Prevent Webkit from drawing the selection in textfields/textareas, since we @@ -144,15 +124,6 @@ typedef unsigned char flex_uint8_t; // ResourceRequest. #define ANDROID_USER_GESTURE -// Inform webkit (Font.cpp) that we NEVER want to perform rounding hacks for -// text, since we always measure/draw in subpixel mode (performance) -#define ANDROID_NEVER_ROUND_FONT_METRICS - -// Add bool to GlyphBuffer as a drawing hint, marking buffers whose array of -// widths happen to exactly match the values returned from the font. This allows -// the drawing code to ignore the position array if they choose (performace) -#define ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - // Add support for the orientation window property #define ANDROID_ORIENTATION_SUPPORT @@ -164,12 +135,6 @@ typedef unsigned char flex_uint8_t; // Will submit this as a patch to apple #define PLUGIN_PLATFORM_SETVALUE -// This fixes a bug in PluginMainThreadScheduler: it is obviously -// missing a reset of m_callPending. This means that it never wakes up -// the main thread after the first time triggered. -// https://bugs.webkit.org/show_bug.cgi?id=21503 -#define ANDROID_PLUGIN_MAIN_THREAD_SCHEDULER_FIXES - // This enables logging the DOM tree, Render tree even for the release build #define ANDROID_DOM_LOGGING @@ -187,4 +152,3 @@ typedef unsigned char flex_uint8_t; // Enable dumping the display tree to a file (triggered in WebView.java) #define ANDROID_DUMP_DISPLAY_TREE - diff --git a/WebCore/dom/ContainerNode.cpp b/WebCore/dom/ContainerNode.cpp index cef9b7d..bcd54c1 100644 --- a/WebCore/dom/ContainerNode.cpp +++ b/WebCore/dom/ContainerNode.cpp @@ -182,16 +182,6 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce if (oldChild == newChild) // nothing to do return true; -#ifdef ANDROID_FIX - // if oldChild is null, it will cause crash in checkReplaceChild(). We - // should check null first. - // Fix Android bug, http://b/issue?id=847893 - if (!oldChild) { - ec = NOT_FOUND_ERR; - return false; - } -#endif - // Make sure replacing the old child with the new is ok checkReplaceChild(newChild.get(), oldChild, ec); if (ec) diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp index 3e866a6..c35177a 100644 --- a/WebCore/dom/Document.cpp +++ b/WebCore/dom/Document.cpp @@ -2510,19 +2510,6 @@ bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) if (m_inPageCache) return false; -#ifdef ANDROID_RESET_SELECTION - WebCore::Node* oldFocus = frame()->getCacheBuilder().currentFocus(); - if (oldFocus) { - if (oldFocus->hasTagName(WebCore::HTMLNames::inputTag)) { - WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(oldFocus); - if (input->isTextField()) - input->setSelectionRange(-1, -1); - } else if (oldFocus->hasTagName(WebCore::HTMLNames::textareaTag)) { - WebCore::HTMLTextAreaElement* textArea = static_cast<WebCore::HTMLTextAreaElement*>(oldFocus); - textArea->setSelectionRange(-1, -1); - } - } -#endif bool focusChangeBlocked = false; RefPtr<Node> oldFocusedNode = m_focusedNode; m_focusedNode = 0; diff --git a/WebCore/dom/Element.h b/WebCore/dom/Element.h index 3aae0b5..b9dc0fd 100644 --- a/WebCore/dom/Element.h +++ b/WebCore/dom/Element.h @@ -157,15 +157,7 @@ public: virtual bool isURLAttribute(Attribute*) const; virtual const QualifiedName& imageSourceAttributeName() const; virtual String target() const { return String(); } -#ifdef ANDROID_DO_NOT_RESTORE_PREVIOUS_SELECTION - // Set the default to not restore the previous selection, since - // we want the selection to cover the whole textfield. - // FIXME: Would it be a better fix to simply ignore the input - // value in the implementation? - virtual void focus(bool restorePreviousSelection = false); -#else virtual void focus(bool restorePreviousSelection = true); -#endif virtual void updateFocusAppearance(bool restorePreviousSelection); void blur(); diff --git a/WebCore/dom/Node.cpp b/WebCore/dom/Node.cpp index 6a16b3c..214a19e 100644 --- a/WebCore/dom/Node.cpp +++ b/WebCore/dom/Node.cpp @@ -1876,8 +1876,8 @@ void Node::showTreeAndMark(const Node* markedNode1, const char* markedLabel1, co #else for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentNode()) fprintf(stderr, "\t"); -#endif node->showNode(); +#endif } } diff --git a/WebCore/history/HistoryItem.cpp b/WebCore/history/HistoryItem.cpp index d7cc311..d4e58e4 100644 --- a/WebCore/history/HistoryItem.cpp +++ b/WebCore/history/HistoryItem.cpp @@ -414,13 +414,6 @@ FormData* HistoryItem::formData() return m_formData.get(); } -#ifdef ANDROID_FIX -const FormData* HistoryItem::formData() const -{ - return m_formData.get(); -} -#endif - bool HistoryItem::isCurrentDocument(Document* doc) const { // FIXME: We should find a better way to check if this is the current document. diff --git a/WebCore/history/HistoryItem.h b/WebCore/history/HistoryItem.h index cc131dd..839f47d 100644 --- a/WebCore/history/HistoryItem.h +++ b/WebCore/history/HistoryItem.h @@ -109,9 +109,6 @@ public: bool isTargetItem() const; FormData* formData(); -#ifdef ANDROID_FIX - const FormData* formData() const; -#endif String formContentType() const; String formReferrer() const; String rssFeedReferrer() const; diff --git a/WebCore/html/HTMLAreaElement.cpp b/WebCore/html/HTMLAreaElement.cpp index 7c6d65f..a865122 100644 --- a/WebCore/html/HTMLAreaElement.cpp +++ b/WebCore/html/HTMLAreaElement.cpp @@ -29,10 +29,6 @@ #include "Length.h" #include "RenderObject.h" -#ifdef ANDROID_NAVIGATE_AREAMAPS -#include "RenderImage.h" -#endif - using namespace std; namespace WebCore { @@ -45,9 +41,6 @@ HTMLAreaElement::HTMLAreaElement(Document *doc) , m_coordsLen(0) , m_lastSize(-1, -1) , m_shape(Unknown) -#ifdef ANDROID_NAVIGATE_AREAMAPS - , m_map(0) -#endif { } @@ -231,15 +224,4 @@ void HTMLAreaElement::setTarget(const String& value) setAttribute(targetAttr, value); } -#ifdef ANDROID_NAVIGATE_AREAMAPS -IntRect HTMLAreaElement::getAreaRect() const -{ - if (m_map) { - if (isDefault()) - return m_map->absoluteBoundingBoxRect(); - return getRect(m_map); - } - return IntRect(); -} -#endif } diff --git a/WebCore/html/HTMLAreaElement.h b/WebCore/html/HTMLAreaElement.h index 2cce901..986116b 100644 --- a/WebCore/html/HTMLAreaElement.h +++ b/WebCore/html/HTMLAreaElement.h @@ -29,14 +29,6 @@ namespace WebCore { -#ifdef ANDROID_NAVIGATE_AREAMAPS - // in android, we have no pointer, so we can't access area elements - // via mapMouseEvent. instead, we store the RenderImage here so we - // can use it to find its dimensions to focus on it and draw a ring - // around it - class RenderImage; -#endif - class HitTestResult; class HTMLAreaElement : public HTMLAnchorElement { @@ -78,11 +70,6 @@ public: virtual String target() const; void setTarget(const String&); -#ifdef ANDROID_NAVIGATE_AREAMAPS - IntRect getAreaRect() const; - void setMap(RenderImage* map) { m_map = map; } -#endif - private: enum Shape { Default, Poly, Rect, Circle, Unknown }; Path getRegion(const IntSize&) const; @@ -91,9 +78,6 @@ private: int m_coordsLen; IntSize m_lastSize; Shape m_shape; -#ifdef ANDROID_NAVIGATE_AREAMAPS - RenderImage* m_map; -#endif }; } //namespace diff --git a/WebCore/html/HTMLElement.cpp b/WebCore/html/HTMLElement.cpp index 3f8a983..572a7a5 100644 --- a/WebCore/html/HTMLElement.cpp +++ b/WebCore/html/HTMLElement.cpp @@ -241,10 +241,6 @@ void HTMLElement::parseMappedAttribute(MappedAttribute *attr) String HTMLElement::innerHTML() const { -#ifdef ANDROID_NO_BODY_INNER_HTML - if (id()==bodyTag || id()==htmlTag) - return "fastinnerhtml!"; -#endif return createMarkup(this, ChildrenOnly); } diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp index cebfb51..c5c55fd 100644 --- a/WebCore/html/HTMLInputElement.cpp +++ b/WebCore/html/HTMLInputElement.cpp @@ -58,7 +58,7 @@ #if USE(LOW_BANDWIDTH_DISPLAY) #include "FrameLoader.h" #endif -#ifdef ANDROID // multiple additions: see below +#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS #include "WebViewCore.h" #endif #include "TextIterator.h" @@ -197,7 +197,6 @@ bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const if (name().isEmpty()) return false; -#ifndef ANDROID_KEYBOARD_NAVIGATION // Never allow keyboard tabbing to leave you in the same radio group. Always // skip any other elements in the group. Node* currentFocusedNode = document()->focusedNode(); @@ -207,7 +206,6 @@ bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const focusedInput->name() == name()) return false; } -#endif // Allow keyboard focus if we're checked or if nothing in the group is checked. return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name()); @@ -1297,11 +1295,6 @@ void HTMLInputElement::defaultEventHandler(Event* evt) clickElement = true; break; case RADIO: -#ifdef ANDROID_KEYBOARD_NAVIGATION -// allow enter to change state of radio - if (!checked()) - clickElement = true; -#endif break; // Don't do anything for enter on a radio button. } } else if (charCode == ' ') { @@ -1348,7 +1341,6 @@ void HTMLInputElement::defaultEventHandler(Event* evt) } } -#ifndef ANDROID_KEYBOARD_NAVIGATION // allow enter to change state of radio if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) { // Left and up mean "previous radio button". @@ -1384,7 +1376,6 @@ void HTMLInputElement::defaultEventHandler(Event* evt) } } } -#endif } if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) { diff --git a/WebCore/html/HTMLSelectElement.cpp b/WebCore/html/HTMLSelectElement.cpp index 44e7fe4..e95bfd3 100644 --- a/WebCore/html/HTMLSelectElement.cpp +++ b/WebCore/html/HTMLSelectElement.cpp @@ -586,12 +586,10 @@ void HTMLSelectElement::dispatchFocusEvent() void HTMLSelectElement::dispatchBlurEvent() { -#ifndef ANDROID_NAVIGATE_LISTBOX // We only need to fire onChange here for menu lists, because we fire onChange for list boxes whenever the selection change is actually made. // This matches other browsers' behavior. if (usesMenuList()) menuListOnChange(); -#endif HTMLFormControlElementWithState::dispatchBlurEvent(); } diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp index ace938b..9059f25 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/CachedCSSStyleSheet.cpp @@ -76,7 +76,7 @@ void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv if (m_data.get()) { m_sheet = m_decoder->decode(m_data->data(), encodedSize()); m_sheet += m_decoder->flush(); -#ifdef ANDROID_FIX +#ifdef ANDROID_FIX // FIXME Newer webkit makes decode temporary; remove on webkit update // report decoded size too setDecodedSize(m_sheet.length() * sizeof(UChar)); #endif diff --git a/WebCore/loader/CachedScript.cpp b/WebCore/loader/CachedScript.cpp index 9666eea..c8caea8 100644 --- a/WebCore/loader/CachedScript.cpp +++ b/WebCore/loader/CachedScript.cpp @@ -79,7 +79,7 @@ void CachedScript::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) setEncodedSize(m_data.get() ? m_data->size() : 0); if (m_data.get()) m_script = m_encoding.decode(m_data->data(), encodedSize()); -#ifdef ANDROID_FIX +#ifdef ANDROID_FIX // FIXME Newer webkit calls setDecodedSize in CachedScript::script(); remove on webkit update // report decoded size too setDecodedSize(m_script.length() * sizeof(UChar)); #endif diff --git a/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp index a47fb08..72e57fe 100644 --- a/WebCore/loader/icon/IconDatabase.cpp +++ b/WebCore/loader/icon/IconDatabase.cpp @@ -1356,28 +1356,14 @@ void* IconDatabase::syncThreadMainLoop() bool didAnyWork = true; while (didAnyWork) { -#ifdef ANDROID_FIX - // We should write the pending icons to the database before trying - // to read any requested icons to ensure that a requested icon has - // the correct data. bool didWrite = writeToDatabase(); if (shouldStopThreadActivity()) break; - - didAnyWork = readFromDatabase(); - if (shouldStopThreadActivity()) - break; -#else + didAnyWork = readFromDatabase(); if (shouldStopThreadActivity()) break; - - bool didWrite = writeToDatabase(); - if (shouldStopThreadActivity()) - break; -#endif - - + // Prune unretained icons after the first time we sync anything out to the database // This way, pruning won't be the only operation we perform to the database by itself // We also don't want to bother doing this if the thread should be terminating (the user is quitting) diff --git a/WebCore/page/android/DragControllerAndroid.cpp b/WebCore/page/android/DragControllerAndroid.cpp index a56411d..eee0cdd 100644 --- a/WebCore/page/android/DragControllerAndroid.cpp +++ b/WebCore/page/android/DragControllerAndroid.cpp @@ -42,7 +42,7 @@ DragOperation DragController::dragOperation(DragData* dragData) { //FIXME: This logic is incomplete ASSERT(0); - if (dragData->containsURL()) + if (dragData->containsURL()) return DragOperationCopy; return DragOperationNone; diff --git a/WebCore/page/android/EventHandlerAndroid.cpp b/WebCore/page/android/EventHandlerAndroid.cpp index aa522a3..e594482 100644 --- a/WebCore/page/android/EventHandlerAndroid.cpp +++ b/WebCore/page/android/EventHandlerAndroid.cpp @@ -42,8 +42,6 @@ namespace WebCore { -// using namespace EventNames; - unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::AltKey; bool EventHandler::tabsToAllControls(KeyboardEvent* ) const @@ -86,10 +84,10 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const return false; } -// This function is called for mouse events by FrameView::handleMousePressEvent() -// It is used to ensure that events are sync. correctly between frames, for example +// This function is called for mouse events by FrameView::handleMousePressEvent(). +// It is used to ensure that events are sync'ed correctly between frames. For example // if the user presses down in one frame and up in another frame, this function will -// return true if that is the case, and pass the event to the correct frame +// returns true, and pass the event to the correct frame. bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode) { @@ -97,8 +95,8 @@ bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& eve return false; } -// This is called to route Wheel Events to child widgets when they are a RenderWidget -// as the parent usually gets Wheel Event. Don't have a mouse with a wheel to confirm +// This is called to route wheel events to child widgets when they are RenderWidget +// as the parent usually gets wheel event. Don't have a mouse with a wheel to confirm // the operation of this function. bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& , Widget* widget) { @@ -127,6 +125,6 @@ class Clipboard : public RefCounted<Clipboard> {}; PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { return PassRefPtr<Clipboard>(NULL); } - // new as of SVN change 36269, Sept 8, 2008 +// new as of SVN change 36269, Sept 8, 2008 const double EventHandler::TextDragDelay = 0.0; } diff --git a/WebCore/page/android/InspectorControllerAndroid.cpp b/WebCore/page/android/InspectorControllerAndroid.cpp index c3b88b7..befcf4a 100644 --- a/WebCore/page/android/InspectorControllerAndroid.cpp +++ b/WebCore/page/android/InspectorControllerAndroid.cpp @@ -21,13 +21,12 @@ #include "Node.h" #include "Profile.h" -/* // This stub file was created to avoid building and linking in all the // Inspector codebase. If you would like to enable the Inspector, do the // following steps: -// 1. Replace this file in WebCore/Makefile.android with the common +// 1. Replace this file in WebCore/Android.mk with the common // implementation, ie page/InsepctorController.cpp -// 2. Add the JS API files to JavaScriptCore/Makefile.android: +// 2. Add the JS API files to JavaScriptCore/Android.mk: // ? API/JSBase.cpp \ // API/JSCallbackConstructor.cpp \ // API/JSCallbackFunction.cpp \ @@ -37,14 +36,13 @@ // API/JSObjectRef.cpp \ // API/JSStringRef.cpp \ // API/JSValueRef.cpp -// 3. Add the following LOCAL_C_INCLUDES to JavaScriptCore/Makefile.android: +// 3. Add the following LOCAL_C_INCLUDES to JavaScriptCore/Android.mk: // ?$(LOCAL_PATH)/API \ // $(LOCAL_PATH)/ForwardingHeaders \ // $(LOCAL_PATH)/../../WebKit \ // 4. Rebuild WebKit // -// Note, for a functional Inspector, you must implement InspectorClientAndroid -*/ +// Note, for a functional Inspector, you must implement InspectorClientAndroid. namespace WebCore { @@ -54,8 +52,11 @@ struct InspectorResource : public RefCounted<InspectorResource> { struct InspectorDatabaseResource : public RefCounted<InspectorDatabaseResource> { }; -InspectorController::InspectorController(Page*, InspectorClient*) : - m_startProfiling(this, NULL) {} +InspectorController::InspectorController(Page*, InspectorClient*) + : m_startProfiling(this, NULL) +{ +} + InspectorController::~InspectorController() {} void InspectorController::windowScriptObjectAvailable() {} diff --git a/WebCore/platform/android/KeyEventAndroid.cpp b/WebCore/platform/android/KeyEventAndroid.cpp index 6a59e58..302f6fe 100644 --- a/WebCore/platform/android/KeyEventAndroid.cpp +++ b/WebCore/platform/android/KeyEventAndroid.cpp @@ -205,13 +205,13 @@ PlatformKeyboardEvent::PlatformKeyboardEvent(int keyCode, UChar32 unichar, , m_keyIdentifier(keyIdentifierForAndroidKeyCode(keyCode)) , m_autoRepeat(repeatCount > 0) , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(keyCode)) + , m_nativeVirtualKeyCode(keyCode) , m_isKeypad(false) , m_shiftKey((mods & ShiftKey) != 0) , m_ctrlKey((mods & CtrlKey) != 0) , m_altKey((mods & AltKey) != 0) , m_metaKey((mods & MetaKey) != 0) // added for android - , m_nativeVirtualKeyCode(keyCode) , m_repeatCount(repeatCount) , m_unichar(unichar) { diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index a78d27b..138e322 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -417,9 +417,6 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl WidthIterator it(this, run); it.advance(from); float beforeWidth = it.m_runWidthSoFar; -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - bool adjustedWidths = -#endif it.advance(to, &glyphBuffer); // We couldn't generate any glyphs for the run. Give up. @@ -432,9 +429,6 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl float finalRoundingWidth = it.m_finalRoundingWidth; it.advance(run.length()); startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - adjustedWidths = true; // give up on simple/fast case -#endif } else startX += beforeWidth; @@ -443,11 +437,6 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) glyphBuffer.swap(i, end); -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - // mark the GlyphBuffer as having adjusted widths or not - // used by drawGlyph as an optimization hint - glyphBuffer.setHasAdjustedWidths(adjustedWidths); -#endif // Calculate the starting point of the glyphs to be displayed by adding // all the advances up to the first glyph. FloatPoint startPoint(startX, point.y()); diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index 110b3c2..18957d5 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -61,10 +61,6 @@ typedef FloatSize GlyphBufferAdvance; class GlyphBuffer { public: -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - GlyphBuffer() : m_hasAdjustedWidths(true) {} -#endif - bool isEmpty() const { return m_fontData.isEmpty(); } int size() const { return m_fontData.size(); } @@ -159,18 +155,6 @@ public: #endif } -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - void setHasAdjustedWidths(bool adjustedWidths) { - m_hasAdjustedWidths = adjustedWidths; - } - /** Returns true in the general case, which means that one or more of the - glyphs may have a width or height that has been changed from the raw - value returned by the font. If this returns false, then the drawing - code can use that as a hint if it means it can draw the run faster. - */ - bool hasAdjustedWidths() const { return m_hasAdjustedWidths; } -#endif - void add(Glyph glyph, const SimpleFontData* font, GlyphBufferAdvance advance) { m_fontData.append(font); @@ -192,12 +176,6 @@ private: #if PLATFORM(WIN) Vector<FloatSize, 2048> m_offsets; #endif -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - // defaults to true for general case. Set to false sometimes in - // drawSimpleText as a hint to drawGlphs that the widths are exactly those - // from the font (i.e. no tweaks for rounding or CSS styling - bool m_hasAdjustedWidths; -#endif }; } diff --git a/WebCore/platform/graphics/WidthIterator.cpp b/WebCore/platform/graphics/WidthIterator.cpp index a66b234..a16d739 100644 --- a/WebCore/platform/graphics/WidthIterator.cpp +++ b/WebCore/platform/graphics/WidthIterator.cpp @@ -65,13 +65,7 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run) } } -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS -#define SIGNAL_ADJUSTED_WIDTHS() adjustedWidths = true -bool WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) -#else -#define SIGNAL_ADJUSTED_WIDTHS() void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) -#endif { if (offset > m_end) offset = m_end; @@ -85,9 +79,6 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) float runWidthSoFar = m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - bool adjustedWidths = false; -#endif while (currentCharacter < offset) { UChar32 c = *cp; unsigned clusterLength = 1; @@ -129,26 +120,21 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) if (c == '\t' && m_run.allowTabs()) { float tabWidth = m_font->tabWidth(); width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); - SIGNAL_ADJUSTED_WIDTHS(); } else { width = fontData->widthForGlyph(glyph); -#ifndef ANDROID_NEVER_ROUND_FONT_METRICS // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all characters that // match the width of the space character have the same width as the space character. if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) { width = fontData->m_adjustedSpaceWidth; - SIGNAL_ADJUSTED_WIDTHS(); } -#endif } if (hasExtraSpacing) { // Account for letter-spacing. if (width && m_font->letterSpacing()) { width += m_font->letterSpacing(); - SIGNAL_ADJUSTED_WIDTHS(); } if (Font::treatAsSpace(c)) { @@ -163,14 +149,12 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) width += m_padPerSpace; m_padding -= m_padPerSpace; } - SIGNAL_ADJUSTED_WIDTHS(); } // Account for word spacing. // We apply additional space between "words" by adding width to the space character. if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) { width += m_font->wordSpacing(); - SIGNAL_ADJUSTED_WIDTHS(); } } } @@ -186,12 +170,10 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) float oldWidth = width; -#ifndef ANDROID_NEVER_ROUND_FONT_METRICS // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so following words will start on an integer boundary. if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) { width = ceilf(width); - SIGNAL_ADJUSTED_WIDTHS(); } // Check to see if the next character is a "rounding hack character", if so, adjust @@ -200,9 +182,7 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) || (m_run.applyRunRounding() && currentCharacter >= m_end)) { float totalWidth = runWidthSoFar + width; width += ceilf(totalWidth) - totalWidth; - SIGNAL_ADJUSTED_WIDTHS(); } -#endif runWidthSoFar += width; @@ -215,10 +195,6 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) m_currentCharacter = currentCharacter; m_runWidthSoFar = runWidthSoFar; m_finalRoundingWidth = lastRoundingWidth; - -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - return adjustedWidths; -#endif } bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) diff --git a/WebCore/platform/graphics/WidthIterator.h b/WebCore/platform/graphics/WidthIterator.h index a0eb26d..5706d1e 100644 --- a/WebCore/platform/graphics/WidthIterator.h +++ b/WebCore/platform/graphics/WidthIterator.h @@ -33,11 +33,7 @@ class TextRun; struct WidthIterator { WidthIterator(const Font*, const TextRun&); -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - bool advance(int to, GlyphBuffer* = 0); -#else void advance(int to, GlyphBuffer* = 0); -#endif bool advanceOneCharacter(float& width, GlyphBuffer* = 0); const Font* m_font; diff --git a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp index 6c5abae..712dea5 100644 --- a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp +++ b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp @@ -195,8 +195,7 @@ bool AffineTransform::operator==(const AffineTransform &m2) const AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) { - // is this the correct order??? - m_transform.setConcat(m_transform, m2.m_transform); + m_transform.setConcat(m2.m_transform, m_transform); return *this; } @@ -204,8 +203,7 @@ AffineTransform AffineTransform::operator* (const AffineTransform &m2) { AffineTransform cat; - // is this the correct order??? - cat.m_transform.setConcat(m_transform, m2.m_transform); + cat.m_transform.setConcat(m2.m_transform, m_transform); return cat; } diff --git a/WebCore/platform/graphics/android/FontAndroid.cpp b/WebCore/platform/graphics/android/FontAndroid.cpp index d53c3ea..569e8db 100644 --- a/WebCore/platform/graphics/android/FontAndroid.cpp +++ b/WebCore/platform/graphics/android/FontAndroid.cpp @@ -107,35 +107,31 @@ static bool setupForText(SkPaint* paint, GraphicsContext* gc, void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkPaint paint; + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + SkPaint paint; if (!setupForText(&paint, gc, font)) { return; } - SkCanvas* canvas = gc->platformContext()->mCanvas; - - SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert - - const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + + /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us + point.xy + [width, height, width, height, ...], so we have to convert + */ + for (int i = 0; i < numGlyphs; i++) { + pos[i].set(x, y); + x += SkFloatToScalar(adv[i].width()); + y += SkFloatToScalar(adv[i].height()); + } -#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS - if (glyphBuffer.hasAdjustedWidths()) { - const GlyphBufferAdvance* adv = glyphBuffer.advances(from); - SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); - SkPoint* pos = storage.get(); - - for (int i = 0; i < numGlyphs; i++) { - pos[i].set(x, y); - x += SkFloatToScalar(adv[i].width()); - y += SkFloatToScalar(adv[i].height()); - } - canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); - } else -#endif - canvas->drawText(glyphs, numGlyphs << 1, x, y, paint); + SkCanvas* canvas = gc->platformContext()->mCanvas; + canvas->drawPosText(glyphs, numGlyphs * sizeof(*glyphs), pos, paint); } FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int, int) const diff --git a/WebCore/rendering/RenderImage.cpp b/WebCore/rendering/RenderImage.cpp index 3a25990..604c407 100644 --- a/WebCore/rendering/RenderImage.cpp +++ b/WebCore/rendering/RenderImage.cpp @@ -39,9 +39,6 @@ #ifdef ANDROID_LAYOUT #include "Settings.h" #endif -#ifdef ANDROID_NAVIGATE_AREAMAPS -#include "HTMLAreaElement.h" -#endif #include "SystemTime.h" @@ -188,11 +185,6 @@ RenderImage::RenderImage(Node* node) RenderImage::~RenderImage() { -#ifdef ANDROID_NAVIGATE_AREAMAPS - // Set area elements' RenderImage to null, so they do not reference - // the deleted RenderImage. - setAreaElements(NULL); -#endif if (m_cachedImage) m_cachedImage->removeClient(this); RenderImageScaleObserver::imageDestroyed(this); @@ -598,18 +590,4 @@ Image* RenderImage::nullImage() return Image::nullImage(); } -#ifdef ANDROID_NAVIGATE_AREAMAPS -void RenderImage::setAreaElements(RenderImage* image) -{ - HTMLMapElement* map = imageMap(); - if (map) { - for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) { - if (current->hasTagName(WebCore::HTMLNames::areaTag)) { - HTMLAreaElement* area = static_cast<HTMLAreaElement*>(current); - area->setMap(image); - } - } - } -} -#endif } // namespace WebCore diff --git a/WebCore/rendering/RenderImage.h b/WebCore/rendering/RenderImage.h index 15d7aa1..477fdeb 100644 --- a/WebCore/rendering/RenderImage.h +++ b/WebCore/rendering/RenderImage.h @@ -71,15 +71,6 @@ public: void highQualityRepaintTimerFired(Timer<RenderImage>*); -#ifdef ANDROID_NAVIGATE_AREAMAPS - // If this RenderImage has an imagemap, set its area elements to point to it so they - // can know its bounds for focus navigation and drawing the focus ring. - void setImageForAreaElements() { setAreaElements(this); } -private: - // Helper function, also used to set the area elements to have no RenderImage in the - // destructor, so they do not reference the deleted image. - void setAreaElements(RenderImage* image); -#endif protected: virtual Image* image(int w = 0, int h = 0) { return m_cachedImage ? m_cachedImage->image() : nullImage(); } virtual bool errorOccurred() const { return m_cachedImage && m_cachedImage->errorOccurred(); } diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp index 8384a98..56fb7df 100644 --- a/WebCore/rendering/RenderObject.cpp +++ b/WebCore/rendering/RenderObject.cpp @@ -533,7 +533,12 @@ int RenderObject::offsetTop() const RenderObject* offsetPar = offsetParent(); if (!offsetPar) return 0; +#ifdef ANDROID_FIX + // This is to fix https://bugs.webkit.org/show_bug.cgi?id=23178. + int y = yPos() - borderTopExtra() + offsetPar->borderTopExtra() - offsetPar->borderTop(); +#else int y = yPos() - borderTopExtra() - offsetPar->borderTop(); +#endif if (!isPositioned()) { if (isRelPositioned()) y += static_cast<const RenderBox*>(this)->relativePositionOffsetY(); diff --git a/WebKit/Android.mk b/WebKit/Android.mk index ea4abed..14bb359 100644 --- a/WebKit/Android.mk +++ b/WebKit/Android.mk @@ -59,4 +59,8 @@ LOCAL_SRC_FILES := \ android/plugins/PluginViewAndroid.cpp \ android/plugins/PluginViewBridgeAndroid.cpp \ android/plugins/PluginWidgetAndroid.cpp \ - android/plugins/SkANP.cpp + android/plugins/SkANP.cpp \ + \ + android/wds/Command.cpp \ + android/wds/Connection.cpp \ + android/wds/DebugServer.cpp 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; +} |