diff options
author | Ben Murdoch <benm@google.com> | 2010-09-13 10:40:13 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-09-13 10:40:13 -0700 |
commit | 9149fda254e7ff835dfcd93d8d7a5b3521a788d9 (patch) | |
tree | 86e789709760d6a28457f42798bef401e246793a | |
parent | 43df208144c6a77abc560712195027cbc13ce9de (diff) | |
parent | d0fc68897b5a7379951aaa5d50f0802dbe9e00d4 (diff) | |
download | external_webkit-9149fda254e7ff835dfcd93d8d7a5b3521a788d9.zip external_webkit-9149fda254e7ff835dfcd93d8d7a5b3521a788d9.tar.gz external_webkit-9149fda254e7ff835dfcd93d8d7a5b3521a788d9.tar.bz2 |
Merge "Initial autofill changes in libwebcore."
19 files changed, 1533 insertions, 4 deletions
@@ -101,6 +101,20 @@ ifneq ($(HTTP_STACK),chrome) endif endif +# Read the environment variable to determine if Autofill is enabled. +# The default is off. Chrome HTTP stack must be used when Autofill +# is turned on. + +ifneq ($(ENABLE_AUTOFILL),true) + ENABLE_AUTOFILL=false +endif + +ifneq ($(HTTP_STACK),chrome) + ifeq ($(ENABLE_AUTOFILL),true) + ENABLE_AUTOFILL = false + endif +endif + BASE_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -218,6 +232,16 @@ LOCAL_C_INCLUDES := $(LOCAL_C_INCLUDES) \ $(base_intermediates)/WebCore/html \ $(base_intermediates)/WebCore/platform +# The following includes are needed by the AutoFill feature. +LOCAL_C_INCLUDES := $(LOCAL_C_INCLUDES) \ + $(LOCAL_PATH)/WebKit/chromium \ + $(LOCAL_PATH)/WebKit/chromium/public \ + external/chromium/chrome/browser \ + external/chromium/chrome/renderer \ + external/chromium/android \ + external/chromium/chrome \ + external/skia + ifeq ($(JAVASCRIPT_ENGINE),v8) # Include WTF source file. d := JavaScriptCore @@ -364,6 +388,10 @@ LOCAL_STATIC_LIBRARIES += libchromium_net LOCAL_SHARED_LIBRARIES += libcrypto libssl libz endif # HTTP_STACK == chrome +ifeq ($(ENABLE_AUTOFILL),true) +LOCAL_SHARED_LIBRARIES += libexpat +endif + # Redefine LOCAL_SRC_FILES to be all the WebKit source files LOCAL_SRC_FILES := $(WEBKIT_SRC_FILES) @@ -428,6 +456,19 @@ LOCAL_C_INCLUDES := $(WEBKIT_C_INCLUDES) LOCAL_PATH := $(BASE_PATH) LOCAL_SRC_FILES := \ WebKit/android/jni/WebCoreJniOnLoad.cpp + +ifeq ($(ENABLE_AUTOFILL),true) +# AutoFill requires some cpp files from Chromium to link with +# libchromium_net. We cannot compile them into libchromium_net +# because they have cpp file extensions, not .cc. +LOCAL_CFLAGS += -DWEBKIT_IMPLEMENTATION=1 +LOCAL_SRC_FILES += \ + WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp \ + WebKit/chromium/src/WebCString.cpp \ + WebKit/chromium/src/WebRegularExpression.cpp \ + WebKit/chromium/src/WebString.cpp +endif + # Do this dependency by hand. The reason we have to do this is because the # headers that this file pulls in are generated during the build of webcore. # We make all of our object files depend on those files so that they are built diff --git a/WebCore/Android.derived.mk b/WebCore/Android.derived.mk index e913a48..e2cf096 100644 --- a/WebCore/Android.derived.mk +++ b/WebCore/Android.derived.mk @@ -176,3 +176,51 @@ $(GEN): xlink_attrs := $(LOCAL_PATH)/svg/xlinkattrs.in $(GEN): $(LOCAL_PATH)/dom/make_names.pl $(xlink_attrs) $(transform-generated-source) LOCAL_GENERATED_SOURCES += $(GEN) + +ifeq ($(ENABLE_AUTOFILL),true) +# This is really ugly, but necessary. The following rules are taken from +# external/chromium/Android.mk. +# Chromium uses several third party libraries and headers that are already +# present on Android, but in different include paths. Generate a set of +# forwarding headers in the location that Chromium expects. We need to do +# this in both the Chromium and WebCore projects because of the +# WebKit <-> Chromium bindings which include headers from the Chromuim project. + +THIRD_PARTY = $(intermediates)/third_party +CHROMIUM_SRC_DIR := $(LOCAL_PATH)/../../chromium +SCRIPT := $(CHROMIUM_SRC_DIR)/android/generateAndroidForwardingHeader.pl + +GEN := $(THIRD_PARTY)/expat/files/lib/expat.h +$(GEN): $(SCRIPT) +$(GEN): + perl $(SCRIPT) $@ "lib/expat.h" +LOCAL_GENERATED_SOURCES += $(GEN) + +GEN := $(THIRD_PARTY)/skia/include/core/SkBitmap.h +$(GEN): $(SCRIPT) +$(GEN): + perl $(SCRIPT) $@ "include/core/SkBitmap.h" +LOCAL_GENERATED_SOURCES += $(GEN) + +GEN := $(THIRD_PARTY)/WebKit/WebKit/chromium/public/WebFormControlElement.h +$(GEN): $(SCRIPT) +$(GEN): + perl $(SCRIPT) $@ "public/WebFormControlElement.h" +LOCAL_GENERATED_SOURCES += $(GEN) + +GEN := $(THIRD_PARTY)/WebKit/WebKit/chromium/public/WebRegularExpression.h +$(GEN): $(SCRIPT) +$(GEN): + perl $(SCRIPT) $@ "public/WebRegularExpression.h" +LOCAL_GENERATED_SOURCES += $(GEN) + +GEN := $(THIRD_PARTY)/WebKit/WebKit/chromium/public/WebString.h +$(GEN): $(SCRIPT) +$(GEN): + perl $(SCRIPT) $@ "public/WebString.h" +LOCAL_GENERATED_SOURCES += $(GEN) + +endif + +LOCAL_SRC_FILES += $(LOCAL_GENERATED_SOURCES) + diff --git a/WebKit/Android.mk b/WebKit/Android.mk index 01ede0f..a361974 100644 --- a/WebKit/Android.mk +++ b/WebKit/Android.mk @@ -106,3 +106,14 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ android/wds/Command.cpp \ android/wds/Connection.cpp \ android/wds/DebugServer.cpp + +# Needed for autofill. +ifeq ($(ENABLE_AUTOFILL),true) +LOCAL_CFLAGS += -DENABLE_WEB_AUTOFILL + +LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ + android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp \ + android/WebCoreSupport/autofill/FormFieldAndroid.cpp \ + android/WebCoreSupport/autofill/FormManagerAndroid.cpp \ + android/WebCoreSupport/autofill/WebAutoFill.cpp +endif # ENABLE_AUTOFILL == true diff --git a/WebKit/android/WebCoreSupport/ChromiumIncludes.h b/WebKit/android/WebCoreSupport/ChromiumIncludes.h index a7abef1..1a69ff1 100644 --- a/WebKit/android/WebCoreSupport/ChromiumIncludes.h +++ b/WebKit/android/WebCoreSupport/ChromiumIncludes.h @@ -26,6 +26,8 @@ #ifndef ChromiumIncludes_h #define ChromiumIncludes_h +#include "config.h" + // Include all external/chromium files in this file so the problems with the LOG // and LOG_ASSERT defines can be handled in one place. @@ -64,6 +66,20 @@ #include <net/url_request/url_request.h> #include <net/url_request/url_request_context.h> +#if ENABLE(WEB_AUTOFILL) +#include <autofill/autofill_manager.h> +#include <autofill/autofill_profile.h> +#include <autofill/personal_data_manager.h> +#include <base/logging.h> +#include <base/scoped_vector.h> +#include <base/string16.h> +#include <base/utf_string_conversions.h> +#include <chrome/browser/autofill/autofill_host.h> +#include <chrome/browser/profile.h> +#include <chrome/browser/tab_contents/tab_contents.h> +#include <webkit/glue/form_data.h> +#endif + #undef LOG #if defined(LOG_WAS_DEFINED) && defined(LOG_PRI) #define LOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) diff --git a/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp index 45498c0..f5f8211 100644 --- a/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp +++ b/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp @@ -32,6 +32,7 @@ #include "EventNames.h" #include "FocusController.h" #include "Frame.h" +#include "HTMLNames.h" #include "KeyboardEvent.h" #include "NotImplemented.h" #include "PlatformKeyboardEvent.h" @@ -39,6 +40,8 @@ #include "WebViewCore.h" #include "WindowsKeyboardCodes.h" +using namespace WebCore::HTMLNames; + namespace android { void EditorClientAndroid::pageDestroyed() { @@ -235,6 +238,19 @@ void EditorClientAndroid::respondToChangedSelection() { Frame* frame = m_page->focusController()->focusedOrMainFrame(); if (!frame || !frame->view()) return; + +#if ENABLE(WEB_AUTOFILL) + WebCore::Node* focusedNode = frame->document()->focusedNode(); + if (focusedNode && focusedNode->hasTagName(inputTag)) { + WebCore::HTMLInputElement* element = static_cast<WebCore::HTMLInputElement*>(focusedNode); + // TODO: If it's a text field, inform AutoFill that it should get AutoFill suggestions for + // the form it belongs to. AutoFill can also work with select-one elements (i.e. <select> + // without the "multiple" attribute set. Is it safe to call this function with select elements? + // How should AutoFill communicate select suggestions to Java? + if (element->isTextField()) + m_autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusedNode)); + } +#endif WebViewCore* webViewCore = WebViewCore::getWebViewCore(frame->view()); webViewCore->updateTextSelection(); } @@ -261,4 +277,15 @@ void EditorClientAndroid::willSetInputMethodState() { notImplemented(); } + +#if ENABLE(WEB_AUTOFILL) +WebAutoFill* EditorClientAndroid::getAutoFill() +{ + if (!m_autoFill) + m_autoFill.set(new WebAutoFill()); + + return m_autoFill.get(); +} +#endif + } diff --git a/WebKit/android/WebCoreSupport/EditorClientAndroid.h b/WebKit/android/WebCoreSupport/EditorClientAndroid.h index 6d49f48..9aed88c 100644 --- a/WebKit/android/WebCoreSupport/EditorClientAndroid.h +++ b/WebKit/android/WebCoreSupport/EditorClientAndroid.h @@ -26,8 +26,14 @@ #ifndef EditorClientAndroid_h #define EditorClientAndroid_h +#include "config.h" + + #include "EditorClient.h" #include "Page.h" +#include "autofill/WebAutoFill.h" + +#include <wtf/OwnPtr.h> using namespace WebCore; @@ -111,10 +117,16 @@ public: void setPage(Page* page) { m_page = page; } void setShouldChangeSelectedRange(bool shouldChangeSelectedRange) { m_shouldChangeSelectedRange = shouldChangeSelectedRange; } void setUiGeneratedSelectionChange(bool uiGenerated) { m_uiGeneratedSelectionChange = uiGenerated; } +#if ENABLE(WEB_AUTOFILL) + WebAutoFill* getAutoFill(); +#endif private: Page* m_page; bool m_shouldChangeSelectedRange; bool m_uiGeneratedSelectionChange; +#if ENABLE(WEB_AUTOFILL) + OwnPtr<WebAutoFill> m_autoFill; +#endif }; } diff --git a/WebKit/android/WebCoreSupport/WebRequestContext.cpp b/WebKit/android/WebCoreSupport/WebRequestContext.cpp index dc9feae..0dda816 100644 --- a/WebKit/android/WebCoreSupport/WebRequestContext.cpp +++ b/WebKit/android/WebCoreSupport/WebRequestContext.cpp @@ -28,6 +28,7 @@ #include "WebRequestContext.h" #include "JNIUtility.h" +#include "WebUrlLoaderClient.h" #include "jni.h" #include <dirent.h> #include <sys/types.h> @@ -121,7 +122,8 @@ scoped_refptr<WebRequestContext> WebRequestContext::GetAndroidContextForPath(con scoped_refptr<WebRequestContext> androidContext = new WebRequestContext(); androidContext->host_resolver_ = net::CreateSystemHostResolver(0); - scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = base::MessageLoopProxy::CreateForCurrentThread(); + base::Thread* ioThread = WebUrlLoaderClient::ioThread(); + scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy(); // Todo: check if the context takes ownership of the cache net::HttpCache::DefaultBackend* defaultBackend = new net::HttpCache::DefaultBackend(net::DISK_CACHE, cachePath, 20 * 1024 * 1024, cacheMessageLoopProxy); androidContext->http_transaction_factory_ = new net::HttpCache(androidContext->host_resolver(), net::ProxyService::CreateNull(), net::SSLConfigService::CreateSystemSSLConfigService(), net::HttpAuthHandlerFactory::CreateDefault(), 0, 0, defaultBackend); diff --git a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h index 1a8118c..92c7bb5 100644 --- a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h +++ b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h @@ -83,6 +83,9 @@ public: static void didFail(void*); static void willSendRequest(void*); + // Handle to the chrome IO thread + static base::Thread* ioThread(); + private: void finish(); RefPtr<WebCore::ResourceHandle> m_resourceHandle; @@ -93,9 +96,6 @@ private: // Not an OwnPtr since it should be deleted on another thread WebRequest* m_request; - // Handle to the chrome IO thread - static base::Thread* ioThread(); - // Check if a request is active bool isActive() const; diff --git a/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp b/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp new file mode 100644 index 0000000..51f7a93 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AutoFillHostAndroid.h" + +#include "autofill/WebAutoFill.h" + +namespace android { + +AutoFillHostAndroid::AutoFillHostAndroid(WebAutoFill* autoFill) + : mAutoFill(autoFill) +{ +} + +void AutoFillHostAndroid::AutoFillSuggestionsReturned(int queryId, const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<int>& uniqueIds) +{ + if (mAutoFill) + mAutoFill->fillFormFields(queryId, names[0], labels[0], uniqueIds[0]); +} + +void AutoFillHostAndroid::AutoFillFormDataFilled(int queryId, const webkit_glue::FormData& form) +{ + if (mAutoFill) + mAutoFill->fillFormInPage(queryId, form); +} + +} diff --git a/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h b/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h new file mode 100644 index 0000000..1984098 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h @@ -0,0 +1,54 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AutoFillHostAndroid_h +#define AutoFillHostAndroid_h + +#include "ChromiumIncludes.h" + +#include <vector> + +namespace webkit_glue { +class FormData; +} + +namespace android { +class WebAutoFill; + +// This class receives the callbacks from the AutoFillManager in the Chromium code. +class AutoFillHostAndroid : public AutoFillHost { +public: + AutoFillHostAndroid(WebAutoFill* autoFill); + virtual ~AutoFillHostAndroid() { } + + virtual void AutoFillSuggestionsReturned(int queryId, const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<int>& uniqueIds); + virtual void AutoFillFormDataFilled(int queryId, const webkit_glue::FormData&); + +private: + WebAutoFill* mAutoFill; +}; +} + +#endif diff --git a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp new file mode 100644 index 0000000..00213a1 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FormFieldAndroid.h" + +#include "ChromiumIncludes.h" +#include "HTMLFormControlElement.h" +#include "HTMLInputElement.h" +#include "HTMLSelectElement.h" + +// TODO: This file is taken from chromium/webkit/glue/form_field.h and +// customised to use WebCore types rather than WebKit API types. It would +// be nice and would ease future merge pain if the two could be combined. + +namespace webkit_glue { +FormField::FormField() + : size_(0) { +} + +FormField::FormField(WebCore::HTMLFormControlElement& element) + : size_(0) { + name_ = string16(element.name().characters()); + + // TODO: Extract the field label. For now we just use the field + // name. + label_ = name_; + + form_control_type_ = string16(element.type().characters()); + if (form_control_type_ == ASCIIToUTF16("text")) { + WebCore::HTMLInputElement* input_element = static_cast<WebCore::HTMLInputElement*>(&element); + value_ = string16(input_element->value().characters()); + size_ = input_element->size(); + } else if (form_control_type_ == ASCIIToUTF16("select-one")) { + WebCore::HTMLSelectElement* select_element = static_cast<WebCore::HTMLSelectElement*>(&element); + value_ = string16(select_element->value().characters()); + } + + TrimWhitespace(value_, TRIM_LEADING, &value_); +} + +FormField::FormField(const string16& label, + const string16& name, + const string16& value, + const string16& form_control_type, + int size) + : label_(label), + name_(name), + value_(value), + form_control_type_(form_control_type), + size_(size) { +} + +bool FormField::operator!=(const FormField& field) const { + return !operator==(field); +} +} // namespace webkit_glue diff --git a/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h new file mode 100644 index 0000000..51bb24b --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FormFieldAndroid_h +#define FormFieldAndroid_h + +#include <base/string16.h> +#include <vector> + +// TODO: This file is taken from chromium/webkit/glue/form_field.cc and +// customised to use WebCore types rather than WebKit API types. It would +// be nice and would ease future merge pain if the two could be combined. + +namespace WebCore { +class HTMLFormControlElement; +} + +namespace webkit_glue { + +// Stores information about a field in a form. +class FormField { +public: + FormField(); + explicit FormField(WebCore::HTMLFormControlElement& element); + FormField(const string16& label, + const string16& name, + const string16& value, + const string16& form_control_type, + int size); + + const string16& label() const { return label_; } + const string16& name() const { return name_; } + const string16& value() const { return value_; } + const string16& form_control_type() const { return form_control_type_; } + int size() const { return size_; } + // Returns option string for elements for which they make sense (select-one, + // for example) for the rest of elements return an empty array. + const std::vector<string16>& option_strings() const { + return option_strings_; + } + + + void set_label(const string16& label) { label_ = label; } + void set_name(const string16& name) { name_ = name; } + void set_value(const string16& value) { value_ = value; } + void set_form_control_type(const string16& form_control_type) { + form_control_type_ = form_control_type; + } + void set_size(int size) { size_ = size; } + void set_option_strings(const std::vector<string16>& strings) { + option_strings_ = strings; + } + + bool operator==(const FormField& field) const; + bool operator!=(const FormField& field) const; + +private: + string16 label_; + string16 name_; + string16 value_; + string16 form_control_type_; + int size_; + std::vector<string16> option_strings_; +}; + +inline bool FormField::operator==(const FormField& field) const { + // A FormField stores a value, but the value is not part of the identity of + // the field, so we don't want to compare the values. + return (label_ == field.label_ && + name_ == field.name_ && + form_control_type_ == field.form_control_type_); + +} + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_FORM_FIELD_H_ diff --git a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp new file mode 100644 index 0000000..c97a2a8 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FormManagerAndroid.h" + +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLTextAreaElement.h" +#include "HTMLInputElement.h" +#include "HTMLLabelElement.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "NodeList.h" +#include "HTMLCollection.h" +#include "FormFieldAndroid.h" +#include "QualifiedName.h" + + +// TODO: This file is taken from chromium/chrome/renderer/form_manager.cc and +// customised to use WebCore types rather than WebKit API types. It would be +// nice and would ease future merge pain if the two could be combined. + +using webkit_glue::FormData; +using webkit_glue::FormField; +using WebCore::HTMLElement; +using WebCore::HTMLFormControlElement; +using WebCore::HTMLFormElement; +using WebCore::HTMLLabelElement; +using WebCore::HTMLSelectElement; +using WebCore::Node; +using WebCore::NodeList; + +using namespace WebCore::HTMLNames; + +namespace { + +// The number of fields required by AutoFill. Ideally we could send the forms +// to AutoFill no matter how many fields are in the forms; however, finding the +// label for each field is a costly operation and we can't spare the cycles if +// it's not necessary. +// Note the on ANDROID we reduce this from Chromium's 3 as it allows us to +// autofill simple name/email forms for example. This improves the mobile +// device experience where form filling can be time consuming and frustrating. +const size_t kRequiredAutoFillFields = 2; + +string16 WTFStringToString16(const WTF::String& wtfString) +{ + WTF::String str = wtfString; + + if (str.charactersWithNullTermination()) + return string16(str.charactersWithNullTermination()); + else + return string16(); +} + +string16 S(const WTF::AtomicString& str) +{ + return WTFStringToString16(str.string()); +} + +string16 S(const WTF::String& string) +{ + return WTFStringToString16(string); +} + +// This is a helper function for the FindChildText() function. +// Returns the node value of the descendant or sibling of |node| that is a +// non-empty text node. This is a faster alternative to |innerText()| for +// performance critical operations. It does a full depth-first search so +// can be used when the structure is not directly known. It does not aggregate +// the text of multiple nodes, it just returns the value of the first found. +// "Non-empty" in this case means non-empty after the whitespace has been +// stripped. +string16 FindChildTextInner(const Node* node) { + string16 element_text; + if (!node) + return element_text; + + element_text = S(node->nodeValue()); + TrimWhitespace(element_text, TRIM_ALL, &element_text); + if (!element_text.empty()) + return element_text; + + element_text = FindChildTextInner(node->firstChild()); + if (!element_text.empty()) + return element_text; + + element_text = FindChildTextInner(node->nextSibling()); + if (!element_text.empty()) + return element_text; + + return element_text; +} + +// Returns the node value of the first decendant of |element| that is a +// non-empty text node. "Non-empty" in this case means non-empty after the +// whitespace has been stripped. +string16 FindChildText(const HTMLElement* element) { + Node* child = element->firstChild(); + return FindChildTextInner(child); +} + +} // namespace + +namespace android { + +FormManager::FormManager() { +} + +FormManager::~FormManager() { + Reset(); +} + +// static +void FormManager::HTMLFormControlElementToFormField( + const HTMLFormControlElement& element, bool get_value, FormField* field) { + ASSERT(field); + + // The label is not officially part of a HTMLFormControlElement; however, the + // labels for all form control elements are scraped from the DOM and set in + // WebFormElementToFormData. + field->set_name(S(element.name())); + field->set_form_control_type(S(element.type())); + + if (!get_value) + return; + + string16 value; + if (element.type() == WTF::AtomicString("text")) { + const WebCore::HTMLTextAreaElement* input_element = static_cast<const WebCore::HTMLTextAreaElement*>(&element); + if (input_element->renderer()) + value = S(input_element->value()); + } else if (element.type() == WTF::AtomicString("select-one")) { + // TODO: This is ugly. SelectElement::value() is a non-const + // method. Look into fixing this on the WebKit side. + HTMLFormControlElement& e = const_cast<HTMLFormControlElement&>(element); + HTMLSelectElement& select_element = static_cast<HTMLSelectElement&>(e); + value = S(select_element.value()); + } + field->set_value(value); +} + +// static +string16 FormManager::LabelForElement(const HTMLFormControlElement& element) { + RefPtr<NodeList> labels = element.document()->getElementsByTagName("label"); + for (unsigned i = 0; i < labels->length(); ++i) { + Node* e = labels->item(i); + if (e->hasTagName(labelTag)) { + HTMLLabelElement* label = static_cast<HTMLLabelElement*>(e); + if (label->control() == &element) + return FindChildText(label); + } + } + + // Infer the label from context if not found in label element. + return FormManager::InferLabelForElement(element); +} + +// static +bool FormManager::HTMLFormElementToFormData(HTMLFormElement& element, + RequirementsMask requirements, + bool get_values, + FormData* form) { + ASSERT(form); + + const WebCore::Document* document = element.document(); + if (!document) + return false; + + if (requirements & REQUIRE_AUTOCOMPLETE && !element.autoComplete()) + return false; + + form->name = S(element.name()); + form->method = S(element.method()); + form->origin = GURL(S(document->documentURI())); + form->action = GURL(S(document->completeURL(element.action()))); + // If the completed URL is not valid, just use the action we get from + // WebKit. + if (!form->action.is_valid()) + form->action = GURL(S(element.action())); + + // A map from a FormField's name to the FormField itself. + std::map<string16, FormField*> name_map; + + // The extracted FormFields. We use pointers so we can store them in + // |name_map|. + ScopedVector<FormField> form_fields; + + WTF::Vector<WebCore::HTMLFormControlElement*> control_elements = element.associatedElements(); + + // A vector of bools that indicate whether each field in the form meets the + // requirements and thus will be in the resulting |form|. + std::vector<bool> fields_extracted(control_elements.size(), false); + for (size_t i = 0; i < control_elements.size(); ++i) { + const HTMLFormControlElement* control_element = control_elements[i]; + + if (requirements & REQUIRE_AUTOCOMPLETE && + control_element->type() == WTF::String("text")) { + const WebCore::HTMLInputElement* input_element = static_cast<const WebCore::HTMLInputElement*>(control_element); + if (!input_element->autoComplete()) + continue; + } + + if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element->isEnabledFormControl()) + continue; + + // Create a new FormField, fill it out and map it to the field's name. + FormField* field = new FormField; + HTMLFormControlElementToFormField(*control_element, get_values, field); + form_fields.push_back(field); + // TODO: A label element is mapped to a form control element's id. + // field->name() will contain the id only if the name does not exist. Add + // an id() method to HTMLFormControlElement and use that here. + name_map[field->name()] = field; + fields_extracted[i] = true; + } + + // Don't extract field labels if we have no fields. + if (form_fields.empty()) + return false; + + // Loop through the label elements inside the form element. For each label + // element, get the corresponding form control element, use the form control + // element's name as a key into the <name, FormField> map to find the + // previously created FormField and set the FormField's label to the + // label.firstChild().nodeValue() of the label element. + RefPtr<WebCore::NodeList> labels = element.getElementsByTagName("label"); + for (unsigned i = 0; i < labels->length(); ++i) { + WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(labels->item(i)); + HTMLFormControlElement* field_element = label->control(); + if (!field_element || field_element->type() == "hidden") + continue; + + std::map<string16, FormField*>::iterator iter = + name_map.find(S(field_element->name())); + if (iter != name_map.end()) + iter->second->set_label(FindChildText(label)); + } + + // Loop through the form control elements, extracting the label text from the + // DOM. We use the |fields_extracted| vector to make sure we assign the + // extracted label to the correct field, as it's possible |form_fields| will + // not contain all of the elements in |control_elements|. + for (size_t i = 0, field_idx = 0; + i < control_elements.size() && field_idx < form_fields.size(); ++i) { + // This field didn't meet the requirements, so don't try to find a label for + // it. + if (!fields_extracted[i]) + continue; + + const HTMLFormControlElement* control_element = control_elements[i]; + if (form_fields[field_idx]->label().empty()) + form_fields[field_idx]->set_label( + FormManager::InferLabelForElement(*control_element)); + + ++field_idx; + + } + // Copy the created FormFields into the resulting FormData object. + for (ScopedVector<FormField>::const_iterator iter = form_fields.begin(); + iter != form_fields.end(); ++iter) { + form->fields.push_back(**iter); + } + + return true; +} + +void FormManager::ExtractForms(WebCore::Document* document) { + + WTF::PassRefPtr<WebCore::HTMLCollection> collection = document->forms(); + + WebCore::HTMLFormElement* form; + WebCore::HTMLInputElement* input; + for (Node* node = collection->firstItem(); + node && !node->namespaceURI().isNull() && !node->namespaceURI().isEmpty(); + node = collection->nextItem()) { + FormElement* form_elements = new FormElement; + form = static_cast<WebCore::HTMLFormElement*>(node); + if (form->autoComplete()) { + WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i < size; i++) { + WebCore::HTMLFormControlElement* e = elements[i]; + form_elements->control_elements.push_back(e); + } + form_elements->form_element = form; + } + form_elements_map_[document].push_back(form_elements); + } +} + +void FormManager::GetForms(RequirementsMask requirements, + std::vector<FormData>* forms) { + ASSERT(forms); + + for (DocumentFormElementMap::iterator iter = form_elements_map_.begin(); + iter != form_elements_map_.end(); ++iter) { + for (std::vector<FormElement*>::iterator form_iter = iter->second.begin(); + form_iter != iter->second.end(); ++form_iter) { + FormData form; + if (HTMLFormElementToFormData(*(*form_iter)->form_element, + requirements, + true, + &form)) + forms->push_back(form); + } + } +} + +void FormManager::GetFormsInDocument(const WebCore::Document* document, + RequirementsMask requirements, + std::vector<FormData>* forms) { + ASSERT(document); + ASSERT(forms); + + DocumentFormElementMap::iterator iter = form_elements_map_.find(document); + if (iter == form_elements_map_.end()) + return; + + // TODO: Factor this out and use it here and in GetForms. + const std::vector<FormElement*>& form_elements = iter->second; + for (std::vector<FormElement*>::const_iterator form_iter = + form_elements.begin(); + form_iter != form_elements.end(); ++form_iter) { + FormElement* form_element = *form_iter; + + // We need at least |kRequiredAutoFillFields| fields before appending this + // form to |forms|. + if (form_element->control_elements.size() < kRequiredAutoFillFields) + continue; + + if (requirements & REQUIRE_AUTOCOMPLETE && + !form_element->form_element->autoComplete()) + continue; + + FormData form; + FormElementToFormData(document, form_element, requirements, &form); + if (form.fields.size() >= kRequiredAutoFillFields) + forms->push_back(form); + } +} + +bool FormManager::FindForm(const HTMLFormElement& element, + RequirementsMask requirements, + FormData* form) { + ASSERT(form); + + const WebCore::Document* document = element.document(); + DocumentFormElementMap::const_iterator document_iter = + form_elements_map_.find(document); + if (document_iter == form_elements_map_.end()) + return false; + + for (std::vector<FormElement*>::const_iterator iter = + document_iter->second.begin(); + iter != document_iter->second.end(); ++iter) { + if ((*iter)->form_element->name() != element.name()) + continue; + return FormElementToFormData(document, *iter, requirements, form); + } + return false; +} + +bool FormManager::FindFormWithFormControlElement( + const HTMLFormControlElement& element, + RequirementsMask requirements, + FormData* form) { + ASSERT(form); + + const WebCore::Document* document = element.document(); + if (form_elements_map_.find(document) == form_elements_map_.end()) + return false; + + const std::vector<FormElement*> forms = form_elements_map_[document]; + for (std::vector<FormElement*>::const_iterator iter = forms.begin(); + iter != forms.end(); ++iter) { + const FormElement* form_element = *iter; + + for (std::vector<HTMLFormControlElement*>::const_iterator iter = + form_element->control_elements.begin(); + iter != form_element->control_elements.end(); ++iter) { + if ((*iter)->name() == element.name()) { + HTMLFormElementToFormData( + *form_element->form_element, requirements, true, form); + return true; + } + } + } + + return false; +} + +bool FormManager::FillForm(const FormData& form) { + FormElement* form_element = NULL; + + for (DocumentFormElementMap::iterator iter = form_elements_map_.begin(); + iter != form_elements_map_.end(); ++iter) { + const WebCore::Document* document = iter->first; + + for (std::vector<FormElement*>::iterator form_iter = iter->second.begin(); + form_iter != iter->second.end(); ++form_iter) { + // TODO: matching on form name here which is not guaranteed to + // be unique for the page, nor is it guaranteed to be non-empty. Need to + // find a way to uniquely identify the form cross-process. For now we'll + // check form name and form action for identity. + // http://crbug.com/37990 test file sample8.html. + // Also note that WebString() == WebString(string16()) does not seem to + // evaluate to |true| for some reason TBD, so forcing to string16. + string16 element_name(S((*form_iter)->form_element->name())); + GURL action( + S(document->completeURL((*form_iter)->form_element->action()).string())); + if (element_name == form.name && action == form.action) { + form_element = *form_iter; + break; + } + } + } + + if (!form_element) + return false; + + // It's possible that the site has injected fields into the form after the + // page has loaded, so we can't assert that the size of the cached control + // elements is equal to the size of the fields in |form|. Fortunately, the + // one case in the wild where this happens, paypal.com signup form, the fields + // are appended to the end of the form and are not visible. + + for (size_t i = 0, j = 0; + i < form_element->control_elements.size() && j < form.fields.size(); + ++i, ++j) { + // Once again, empty WebString != empty string16, so we have to explicitly + // check for this case. + if (form_element->control_elements[i]->name().length() == 0 && + form.fields[j].name().empty()) + continue; + + // We assume that the intersection of the fields in + // |form_element->control_elements| and |form.fields| is ordered, but it's + // possible that one or the other sets may have more fields than the other, + // so loop past non-matching fields in the set with more elements. + while (S(form_element->control_elements[i]->name()) != + form.fields[j].name()) { + if (form_element->control_elements.size() > form.fields.size()) { + // We're at the end of the elements already. + if (i + 1 == form_element->control_elements.size()) + break; + ++i; + } else if (form.fields.size() > form_element->control_elements.size()) { + // We're at the end of the elements already. + if (j + 1 == form.fields.size()) + break; + ++j; + } else { + // Shouldn't get here. + ASSERT(false); + } + + continue; + } + + HTMLFormControlElement* element = form_element->control_elements[i]; + if (!form.fields[j].value().empty() && + element->type() != WTF::String("submit")) { + if (element->type() == WTF::String("text")) { + WebCore::HTMLInputElement* input_element = static_cast<WebCore::HTMLInputElement*>(element); + // If the maxlength attribute contains a negative value, maxLength() + // returns the default maxlength value. + input_element->setValue( + WTF::String(form.fields[j].value().substr(0, input_element->maxLength()).c_str())); + input_element->setAutofilled(true); + } else if (element->type() == + WTF::String("select-one")) { + WebCore::HTMLSelectElement* select_element = static_cast<WebCore::HTMLSelectElement*>(element); + select_element->setValue(WTF::String(form.fields[j].value().c_str())); + } + } + } + + return true; +} + +void FormManager::FillForms(const std::vector<webkit_glue::FormData>& forms) { + for (std::vector<webkit_glue::FormData>::const_iterator iter = forms.begin(); + iter != forms.end(); ++iter) { + FillForm(*iter); + } +} + +void FormManager::Reset() { + for (DocumentFormElementMap::iterator iter = form_elements_map_.begin(); + iter != form_elements_map_.end(); ++iter) { + STLDeleteElements(&iter->second); + } + form_elements_map_.clear(); +} + +void FormManager::ResetFrame(const WebCore::Document* document) { + + DocumentFormElementMap::iterator iter = form_elements_map_.find(document); + if (iter != form_elements_map_.end()) { + STLDeleteElements(&iter->second); + form_elements_map_.erase(iter); + } +} + +// static +bool FormManager::FormElementToFormData(const WebCore::Document* document, + const FormElement* form_element, + RequirementsMask requirements, + FormData* form) { + if (requirements & REQUIRE_AUTOCOMPLETE && + !form_element->form_element->autoComplete()) + return false; + + form->name = S(form_element->form_element->name()); + form->method = S(form_element->form_element->method()); + form->origin = GURL(S(document->documentURI())); + form->action = GURL(S(document->completeURL(form_element->form_element->action()))); + + // If the completed URL is not valid, just use the action we get from + // WebKit. + if (!form->action.is_valid()) + form->action = GURL(S(form_element->form_element->action())); + + // Form elements loop. + for (std::vector<HTMLFormControlElement*>::const_iterator element_iter = + form_element->control_elements.begin(); + element_iter != form_element->control_elements.end(); ++element_iter) { + const HTMLFormControlElement* control_element = *element_iter; + + if (requirements & REQUIRE_AUTOCOMPLETE && + control_element->type() == WTF::String("text")) { + const WebCore::HTMLInputElement* input_element = + static_cast<const WebCore::HTMLInputElement*>(control_element); + if (!input_element->autoComplete()) + continue; + } + + if (requirements & REQUIRE_ELEMENTS_ENABLED && !control_element->isEnabledFormControl()) + continue; + + FormField field; + HTMLFormControlElementToFormField(*control_element, false, &field); + form->fields.push_back(field); + } + + return true; +} + +// static +string16 FormManager::InferLabelForElement( + const HTMLFormControlElement& element) { + string16 inferred_label; + Node* previous = element.previousSibling(); + if (previous) { + if (previous->isTextNode()) { + inferred_label = S(previous->nodeValue()); + TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); + } + + // If we didn't find text, check for previous paragraph. + // Eg. <p>Some Text</p><input ...> + // Note the lack of whitespace between <p> and <input> elements. + if (inferred_label.empty()) { + if (previous->isElementNode()) { + if (previous->hasTagName(pTag)) { + inferred_label = FindChildText((HTMLElement*)previous); + } + } + } + + // If we didn't find paragraph, check for previous paragraph to this. + // Eg. <p>Some Text</p> <input ...> + // Note the whitespace between <p> and <input> elements. + if (inferred_label.empty()) { + previous = previous->previousSibling(); + if (previous) { + if (previous->hasTagName(pTag)) { + inferred_label = FindChildText((HTMLElement*)previous); + } + } + } + } + + // If we didn't find paragraph, check for table cell case. + // Eg. <tr><td>Some Text</td><td><input ...></td></tr> + // Eg. <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr> + if (inferred_label.empty()) { + Node* parent = element.parentNode(); + while (parent && + !parent->hasTagName(tdTag)) + parent = parent->parentNode(); + + if (parent) { + if (parent->hasTagName(tdTag)) { + previous = parent->previousSibling(); + + // Skip by any intervening text nodes. + while (previous && previous->isTextNode()) + previous = previous->previousSibling(); + + if (previous) { + if (previous->hasTagName(tdTag)) { + inferred_label = FindChildText((HTMLElement*)previous); + } + } + } + } + } + return inferred_label; +} + +} diff --git a/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h new file mode 100644 index 0000000..395e651 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FormManagerAndroid_h +#define FormManagerAndroid_h + +#include "ChromiumIncludes.h" + +#include <map> +#include <vector> + +// TODO: This file is taken from chromium/chrome/renderer/form_manager.h and +// customised to use WebCore types rather than WebKit API types. It would be +// nice and would ease future merge pain if the two could be combined. + +namespace webkit_glue { +struct FormData; +class FormField; +} // namespace webkit_glue + +namespace WebCore { + class HTMLFormControlElement; + class HTMLFormElement; + class Document; +} + +namespace android { + +// Manages the forms in a Document. +class FormManager { + public: + // A bit field mask for form requirements. + enum RequirementsMask { + REQUIRE_NONE = 0x0, // No requirements. + REQUIRE_AUTOCOMPLETE = 0x1, // Require that autocomplete != off. + REQUIRE_ELEMENTS_ENABLED = 0x2 // Require that disabled attribute is off. + }; + + FormManager(); + virtual ~FormManager(); + + // Fills out a FormField object from a given WebFormControlElement. + // If |get_value| is true, |field| will have the value set from |element|. + static void HTMLFormControlElementToFormField( + const WebCore::HTMLFormControlElement& element, + bool get_value, + webkit_glue::FormField* field); + + // Returns the corresponding label for |element|. WARNING: This method can + // potentially be very slow. Do not use during any code paths where the page + // is loading. + static string16 LabelForElement(const WebCore::HTMLFormControlElement& element); + + // Fills out a FormData object from a given WebFormElement. If |get_values| + // is true, the fields in |form| will have the values filled out. Returns + // true if |form| is filled out; it's possible that |element| won't meet the + // requirements in |requirements|. This also returns false if there are no + // fields in |form|. + // TODO: Remove the user of this in RenderView and move this to + // private. + static bool HTMLFormElementToFormData(WebCore::HTMLFormElement& element, + RequirementsMask requirements, + bool get_values, + webkit_glue::FormData* form); + + // Scans the DOM in |document| extracting and storing forms. + void ExtractForms(WebCore::Document* document); + + // Returns a vector of forms that match |requirements|. + void GetForms(RequirementsMask requirements, + std::vector<webkit_glue::FormData>* forms); + + // Returns a vector of forms in |document| that match |requirements|. + void GetFormsInDocument(const WebCore::Document* document, + RequirementsMask requirements, + std::vector<webkit_glue::FormData>* forms); + + // Returns the cached FormData for |element|. Returns true if the form was + // found in the cache. + bool FindForm(const WebCore::HTMLFormElement& element, + RequirementsMask requirements, + webkit_glue::FormData* form); + + // Finds the form that contains |element| and returns it in |form|. Returns + // false if the form is not found. + bool FindFormWithFormControlElement( + const WebCore::HTMLFormControlElement& element, + RequirementsMask requirements, + webkit_glue::FormData* form); + + // Fills the form represented by |form|. |form| should have the name set to + // the name of the form to fill out, and the number of elements and values + // must match the number of stored elements in the form. + // TODO: Is matching on name alone good enough? It's possible to + // store multiple forms with the same names from different frames. + bool FillForm(const webkit_glue::FormData& form); + + // Fills all of the forms in the cache with form data from |forms|. + void FillForms(const std::vector<webkit_glue::FormData>& forms); + + // Resets the stored set of forms. + void Reset(); + + // Resets the forms for the specified |document|. + void ResetFrame(const WebCore::Document* document); + + private: + // Stores the HTMLFormElement and the form control elements for a form. + struct FormElement { + WebCore::HTMLFormElement* form_element; + std::vector<WebCore::HTMLFormControlElement*> control_elements; + }; + + // A map of vectors of FormElements keyed by the Document containing each + // form. + typedef std::map<const WebCore::Document*, std::vector<FormElement*> > + DocumentFormElementMap; + + // Converts a FormElement to FormData storage. Returns false if the form does + // not meet all the requirements in the requirements mask. + static bool FormElementToFormData(const WebCore::Document* document, + const FormElement* form_element, + RequirementsMask requirements, + webkit_glue::FormData* form); + + // Infers corresponding label for |element| from surrounding context in the + // DOM. Contents of preceeding <p> tag or preceeding text element found in + // the form. + static string16 InferLabelForElement( + const WebCore::HTMLFormControlElement& element); + + // The map of form elements. + DocumentFormElementMap form_elements_map_; + + DISALLOW_COPY_AND_ASSIGN(FormManager); +}; + +} // namespace android + +#endif // FormManagerAndroid_h diff --git a/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp b/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp new file mode 100644 index 0000000..598b9c4 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "MainThreadProxy.h" + +#include <wtf/MainThread.h> + +void MainThreadProxy::CallOnMainThread(CallOnMainThreadFunction f, void* c) +{ + callOnMainThread(f, c); +} diff --git a/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h b/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h new file mode 100644 index 0000000..d9310bb --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MAIN_THREAD_PROXY_H +#define MAIN_THREAD_PROXY_H + +typedef void CallOnMainThreadFunction(void*); + +class MainThreadProxy +{ +public: + static void CallOnMainThread(CallOnMainThreadFunction, void*); +}; + +#endif diff --git a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp new file mode 100644 index 0000000..08d5e58 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebAutoFill.h" + +#if ENABLE(WEB_AUTOFILL) + +#include "AutoFillHostAndroid.h" +#include "Document.h" +#include "Frame.h" +#include "FormData.h" +#include "FormManagerAndroid.h" +#include "FrameLoader.h" +#include "HTMLFormControlElement.h" +#include "MainThreadProxy.h" +#include "Node.h" +#include "WebFrame.h" +#include "WebRequestContext.h" +#include "WebUrlLoaderClient.h" +#include "WebViewCore.h" + +namespace android +{ + +WebAutoFill::WebAutoFill() +{ + mFormManager = new FormManager(); + mQueryId = 1; + + AndroidURLRequestContextGetter::Get()->SetURLRequestContext(WebRequestContext::GetAndroidContext()); + AndroidURLRequestContextGetter::Get()->SetIOThread(WebUrlLoaderClient::ioThread()); + TabContents* tabContents = new TabContents(); + mAutoFillManager = new AutoFillManager(tabContents); + + // FIXME: For testing use a precanned profile. This should come from Java land! + mAutoFillProfile = new AutoFillProfile(); + mAutoFillProfile->SetInfo(AutoFillType(NAME_FULL), string16(ASCIIToUTF16("John Smith"))); + mAutoFillProfile->SetInfo(AutoFillType(EMAIL_ADDRESS), string16(ASCIIToUTF16("jsmith@gmail.com"))); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), string16(ASCIIToUTF16("AB12 3DE"))); + mAutoFillProfile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), string16(ASCIIToUTF16("0123456789"))); + + std::vector<AutoFillProfile> profiles; + profiles.push_back(*mAutoFillProfile); + tabContents->profile()->GetPersonalDataManager()->SetProfiles(&profiles); + mAutoFillHost = new AutoFillHostAndroid(this); + tabContents->SetAutoFillHost(mAutoFillHost.get()); +} + +WebAutoFill::~WebAutoFill() +{ + mQueryMap.clear(); +} + +void WebAutoFill::searchDocument(WebCore::Document* document) +{ + // TODO: This method is called when the main frame finishes loading and + // scans the document for forms and tries to work out the type of the + // fields in those forms. It's currently synchronous and might be slow + // if the page has many or complex forms. Might want to make this an + // async method. + if (!mFormManager) + return; + + mQueryMap.clear(); + mQueryId = 1; + mAutoFillManager->Reset(); + mFormManager->Reset(); + mFormManager->ExtractForms(document); + mForms.clear(); + mFormManager->GetForms(FormManager::REQUIRE_AUTOCOMPLETE, &mForms); + mAutoFillManager->FormsSeen(mForms); +} + +void WebAutoFill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldElement) +{ + // Get the FormField from the Node. + webkit_glue::FormField formField; + FormManager::HTMLFormControlElementToFormField(*formFieldElement, false, &formField); + formField.set_label(FormManager::LabelForElement(*formFieldElement)); + + webkit_glue::FormData* form = new webkit_glue::FormData; + mFormManager->FindFormWithFormControlElement(*formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form); + mQueryMap[mQueryId] = form; + + mAutoFillManager->GetAutoFillSuggestions(mQueryId, false, formField); + mQueryId++; +} + +void WebAutoFill::fillFormFields(int queryId, const string16& value, const string16& label, int uniqueId) +{ + webkit_glue::FormData* form = mQueryMap[queryId]; + mAutoFillManager->FillAutoFillFormData(queryId, *form, value, label, uniqueId); +} + +void WebAutoFill::fillFormInPage(int queryId, const webkit_glue::FormData& form) +{ + mFormManager->FillForm(form); +} + +} + +#endif diff --git a/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h new file mode 100644 index 0000000..335f9b2 --- /dev/null +++ b/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h @@ -0,0 +1,80 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebAutoFill_h +#define WebAutoFill_h + +#if ENABLE(WEB_AUTOFILL) + +#include "ChromiumIncludes.h" + +#include <map> +#include <vector> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> + +class AutoFillManager; +class AutoFillProfile; +class AutoFillHost; + +namespace WebCore { +class Document; +class HTMLFormControlElement; +} + +namespace android +{ +class FormManager; +class WebViewCore; + +class WebAutoFill : public Noncopyable +{ +public: + WebAutoFill(); + virtual ~WebAutoFill(); + + void searchDocument(WebCore::Document*); + void formFieldFocused(WebCore::HTMLFormControlElement*); + void fillFormFields(int queryId, const string16& value, const string16& label, int uniqueId); + void fillFormInPage(int queryId, const webkit_glue::FormData& form); + +private: + OwnPtr<FormManager> mFormManager; + OwnPtr<AutoFillManager> mAutoFillManager; + OwnPtr<AutoFillProfile> mAutoFillProfile; + OwnPtr<AutoFillHost> mAutoFillHost; + + typedef std::vector<webkit_glue::FormData, std::allocator<webkit_glue::FormData> > FormList; + FormList mForms; + + typedef std::map<int, webkit_glue::FormData*> AutoFillQueryMap; + AutoFillQueryMap mQueryMap; + int mQueryId; +}; + +} + +#endif // ENABLE(WEB_AUTOFILL) +#endif // WebAutoFill_h diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp index a717301..c1cb907 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -120,6 +120,10 @@ #include "WebArchiveAndroid.h" #endif +#if ENABLE(WEB_AUTOFILL) +#include "autofill/WebAutoFill.h" +#endif + using namespace JSC::Bindings; static String* gUploadFileLabel; @@ -639,6 +643,13 @@ WebFrame::didFinishLoad(WebCore::Frame* frame) (int)loadType, isMainFrame); checkException(env); env->DeleteLocalRef(urlStr); +#if ENABLE(WEB_AUTOFILL) + // TODO: Need to consider child frames. + if (isMainFrame) { + EditorClientAndroid* editorClient = static_cast<EditorClientAndroid*>(mPage->editorClient()); + editorClient->getAutoFill()->searchDocument(frame->document()); + } +#endif } void |