diff options
Diffstat (limited to 'Source/WebKit/android/WebCoreSupport')
67 files changed, 11316 insertions, 0 deletions
diff --git a/Source/WebKit/android/WebCoreSupport/CacheResult.cpp b/Source/WebKit/android/WebCoreSupport/CacheResult.cpp new file mode 100644 index 0000000..5309c66 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/CacheResult.cpp @@ -0,0 +1,251 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CacheResult.h" + +#include "WebResponse.h" +#include "WebUrlLoaderClient.h" +#include <platform/FileSystem.h> +#include <wtf/text/CString.h> + +using namespace base; +using namespace disk_cache; +using namespace net; +using namespace std; + +namespace android { + +// All public methods are called on a UI thread but we do work on the +// Chromium thread. However, because we block the WebCore thread while this +// work completes, we can never receive new public method calls while the +// Chromium thread work is in progress. + +// Copied from HttpCache +enum { + kResponseInfoIndex = 0, + kResponseContentIndex +}; + +CacheResult::CacheResult(disk_cache::Entry* entry, String url) + : m_entry(entry) + , m_onResponseHeadersDoneCallback(this, &CacheResult::onResponseHeadersDone) + , m_onReadNextChunkDoneCallback(this, &CacheResult::onReadNextChunkDone) + , m_url(url) +{ + ASSERT(m_entry); +} + +CacheResult::~CacheResult() +{ + m_entry->Close(); + // TODO: Should we also call DoneReadingFromEntry() on the cache for our + // entry? +} + +int64 CacheResult::contentSize() const +{ + // The android stack does not take the content length from the HTTP response + // headers but calculates it when writing the content to disk. It can never + // overflow a long because we limit the cache size. + return m_entry->GetDataSize(kResponseContentIndex); +} + +bool CacheResult::firstResponseHeader(const char* name, String* result, bool allowEmptyString) const +{ + string value; + if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, name, &value) && (!value.empty() || allowEmptyString)) { + *result = String(value.c_str()); + return true; + } + return false; +} + +String CacheResult::mimeType() const +{ + string mimeType; + if (responseHeaders()) + responseHeaders()->GetMimeType(&mimeType); + if (!mimeType.length() && m_url.length()) + mimeType = WebResponse::resolveMimeType(std::string(m_url.utf8().data(), m_url.length()), ""); + return String(mimeType.c_str()); +} + +int64 CacheResult::expires() const +{ + // We have to do this manually, rather than using HttpResponseHeaders::GetExpiresValue(), + // to handle the "-1" and "0" special cases. + string expiresString; + if (responseHeaders() && responseHeaders()->EnumerateHeader(NULL, "expires", &expiresString)) { + wstring expiresStringWide(expiresString.begin(), expiresString.end()); // inflate ascii + // We require the time expressed as ms since the epoch. + Time time; + if (Time::FromString(expiresStringWide.c_str(), &time)) { + // Will not overflow for a very long time! + return static_cast<int64>(1000.0 * time.ToDoubleT()); + } + + if (expiresString == "-1" || expiresString == "0") + return 0; + } + + // TODO + // The Android stack applies a heuristic to set an expiry date if the + // expires header is not set or can't be parsed. I'm not sure whether the Chromium cache + // does this, and if so, it may not be possible for us to get hold of it + // anyway to set it on the result. + return -1; +} + +int CacheResult::responseCode() const +{ + return responseHeaders() ? responseHeaders()->response_code() : 0; +} + +bool CacheResult::writeToFile(const String& filePath) const +{ + // Getting the headers is potentially async, so post to the Chromium thread + // and block here. + MutexLocker lock(m_mutex); + + base::Thread* thread = WebUrlLoaderClient::ioThread(); + if (!thread) + return false; + + CacheResult* me = const_cast<CacheResult*>(this); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::writeToFileImpl)); + + m_filePath = filePath.threadsafeCopy(); + m_isAsyncOperationInProgress = true; + while (m_isAsyncOperationInProgress) + m_condition.wait(m_mutex); + + return m_wasWriteToFileSuccessful; +} + +void CacheResult::writeToFileImpl() +{ + m_bufferSize = m_entry->GetDataSize(kResponseContentIndex); + m_readOffset = 0; + m_wasWriteToFileSuccessful = false; + readNextChunk(); +} + +void CacheResult::readNextChunk() +{ + m_buffer = new IOBuffer(m_bufferSize); + int rv = m_entry->ReadData(kResponseInfoIndex, m_readOffset, m_buffer, m_bufferSize, &m_onReadNextChunkDoneCallback); + if (rv == ERR_IO_PENDING) + return; + + onReadNextChunkDone(rv); +}; + +void CacheResult::onReadNextChunkDone(int size) +{ + if (size > 0) { + // Still more reading to be done. + if (writeChunkToFile()) { + // TODO: I assume that we need to clear and resize the buffer for the next read? + m_readOffset += size; + m_bufferSize -= size; + readNextChunk(); + } else + onWriteToFileDone(); + return; + } + + if (!size) { + // Reached end of file. + if (writeChunkToFile()) + m_wasWriteToFileSuccessful = true; + } + onWriteToFileDone(); +} + +bool CacheResult::writeChunkToFile() +{ + PlatformFileHandle file; + file = openFile(m_filePath, OpenForWrite); + if (!isHandleValid(file)) + return false; + return WebCore::writeToFile(file, m_buffer->data(), m_bufferSize) == m_bufferSize; +} + +void CacheResult::onWriteToFileDone() +{ + MutexLocker lock(m_mutex); + m_isAsyncOperationInProgress = false; + m_condition.signal(); +} + +HttpResponseHeaders* CacheResult::responseHeaders() const +{ + MutexLocker lock(m_mutex); + if (m_responseHeaders) + return m_responseHeaders; + + // Getting the headers is potentially async, so post to the Chromium thread + // and block here. + base::Thread* thread = WebUrlLoaderClient::ioThread(); + if (!thread) + return 0; + + CacheResult* me = const_cast<CacheResult*>(this); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(me, &CacheResult::responseHeadersImpl)); + + m_isAsyncOperationInProgress = true; + while (m_isAsyncOperationInProgress) + m_condition.wait(m_mutex); + + return m_responseHeaders; +} + +void CacheResult::responseHeadersImpl() +{ + m_bufferSize = m_entry->GetDataSize(kResponseInfoIndex); + m_buffer = new IOBuffer(m_bufferSize); + + int rv = m_entry->ReadData(kResponseInfoIndex, 0, m_buffer, m_bufferSize, &m_onResponseHeadersDoneCallback); + if (rv == ERR_IO_PENDING) + return; + + onResponseHeadersDone(rv); +}; + +void CacheResult::onResponseHeadersDone(int size) +{ + MutexLocker lock(m_mutex); + // It's OK to throw away the HttpResponseInfo object as we hold our own ref + // to the headers. + HttpResponseInfo response; + bool truncated = false; // TODO: Waht is this param for? + if (size == m_bufferSize && HttpCache::ParseResponseInfo(m_buffer->data(), m_bufferSize, &response, &truncated)) + m_responseHeaders = response.headers; + m_isAsyncOperationInProgress = false; + m_condition.signal(); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/CacheResult.h b/Source/WebKit/android/WebCoreSupport/CacheResult.h new file mode 100644 index 0000000..c39570c --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/CacheResult.h @@ -0,0 +1,86 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CacheResult_h +#define CacheResult_h + +#include "ChromiumIncludes.h" + +#include <wtf/RefCounted.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/text/WTFString.h> + +namespace android { + +// A wrapper around a disk_cache::Entry. Provides fields appropriate for constructing a Java CacheResult object. +class CacheResult : public base::RefCountedThreadSafe<CacheResult> { +public: + // Takes ownership of the Entry passed to the constructor. + CacheResult(disk_cache::Entry*, String url); + ~CacheResult(); + + int64 contentSize() const; + bool firstResponseHeader(const char* name, WTF::String* result, bool allowEmptyString) const; + // The Android stack uses the empty string if no Content-Type headers are + // found, so we use the same default here. + WTF::String mimeType() const; + // Returns the value of the expires header as milliseconds since the epoch. + int64 expires() const; + int responseCode() const; + bool writeToFile(const WTF::String& filePath) const; +private: + net::HttpResponseHeaders* responseHeaders() const; + void responseHeadersImpl(); + void onResponseHeadersDone(int size); + + void writeToFileImpl(); + void readNextChunk(); + void onReadNextChunkDone(int size); + bool writeChunkToFile(); + void onWriteToFileDone(); + + disk_cache::Entry* m_entry; + + scoped_refptr<net::HttpResponseHeaders> m_responseHeaders; + + int m_readOffset; + bool m_wasWriteToFileSuccessful; + mutable String m_filePath; + + int m_bufferSize; + scoped_refptr<net::IOBuffer> m_buffer; + mutable bool m_isAsyncOperationInProgress; + mutable WTF::Mutex m_mutex; + mutable WTF::ThreadCondition m_condition; + + net::CompletionCallbackImpl<CacheResult> m_onResponseHeadersDoneCallback; + net::CompletionCallbackImpl<CacheResult> m_onReadNextChunkDoneCallback; + + String m_url; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp new file mode 100644 index 0000000..30f374f --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "CachedFramePlatformDataAndroid.h" +#include "Settings.h" + +namespace android { + +CachedFramePlatformDataAndroid::CachedFramePlatformDataAndroid(WebCore::Settings* settings) +{ +#ifdef ANDROID_META_SUPPORT + m_viewport_width = settings->viewportWidth(); + m_viewport_height = settings->viewportHeight(); + m_viewport_initial_scale = settings->viewportInitialScale(); + m_viewport_minimum_scale = settings->viewportMinimumScale(); + m_viewport_maximum_scale = settings->viewportMaximumScale(); + m_viewport_target_densitydpi = settings->viewportTargetDensityDpi(); + m_viewport_user_scalable = settings->viewportUserScalable(); + m_format_detection_address = settings->formatDetectionAddress(); + m_format_detection_email = settings->formatDetectionEmail(); + m_format_detection_telephone = settings->formatDetectionTelephone(); +#endif + +} + +#ifdef ANDROID_META_SUPPORT +void CachedFramePlatformDataAndroid::restoreMetadata(WebCore::Settings* settings) +{ + settings->setViewportWidth(m_viewport_width); + settings->setViewportHeight(m_viewport_height); + settings->setViewportInitialScale(m_viewport_initial_scale); + settings->setViewportMinimumScale(m_viewport_minimum_scale); + settings->setViewportMaximumScale(m_viewport_maximum_scale); + settings->setViewportTargetDensityDpi(m_viewport_target_densitydpi); + settings->setViewportUserScalable(m_viewport_user_scalable); + settings->setFormatDetectionAddress(m_format_detection_address); + settings->setFormatDetectionEmail(m_format_detection_email); + settings->setFormatDetectionTelephone(m_format_detection_telephone); +} +#endif + +} diff --git a/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h new file mode 100644 index 0000000..20c7be4 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/CachedFramePlatformDataAndroid.h @@ -0,0 +1,63 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CachedFramePlatformDatatAndroid_h +#define CachedFramePlatformDatatAndroid_h + +#include "CachedFramePlatformData.h" + +namespace WebCore { + class Settings; +} + +namespace android { + +class CachedFramePlatformDataAndroid : public WebCore::CachedFramePlatformData { +public: + CachedFramePlatformDataAndroid(WebCore::Settings* settings); + +#ifdef ANDROID_META_SUPPORT + void restoreMetadata(WebCore::Settings* settings); +#endif + +private: +#ifdef ANDROID_META_SUPPORT + // meta data of the frame + int m_viewport_width; + int m_viewport_height; + int m_viewport_initial_scale; + int m_viewport_minimum_scale; + int m_viewport_maximum_scale; + int m_viewport_target_densitydpi; + bool m_viewport_user_scalable : 1; + bool m_format_detection_address : 1; + bool m_format_detection_email : 1; + bool m_format_detection_telephone : 1; +#endif +}; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp new file mode 100644 index 0000000..6f872b8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.cpp @@ -0,0 +1,605 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebCore" + +#include "config.h" + +#include "ApplicationCacheStorage.h" +#include "ChromeClientAndroid.h" +#include "DatabaseTracker.h" +#include "Document.h" +#include "PlatformString.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "Geolocation.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "Icon.h" +#include "LayerAndroid.h" +#include "Page.h" +#include "PopupMenuAndroid.h" +#include "ScriptController.h" +#include "SearchPopupMenuAndroid.h" +#include "WebCoreFrameBridge.h" +#include "WebCoreViewBridge.h" +#include "WebViewCore.h" +#include "WindowFeatures.h" +#include "Settings.h" +#include "UserGestureIndicator.h" +#include <wtf/text/CString.h> + +namespace android { + +#if ENABLE(DATABASE) +static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota); +#endif + +#if USE(ACCELERATED_COMPOSITING) + +WebCore::GraphicsLayer* ChromeClientAndroid::layersSync() +{ + if (m_rootGraphicsLayer && m_needsLayerSync && m_webFrame) { + if (FrameView* frameView = m_webFrame->page()->mainFrame()->view()) + frameView->syncCompositingStateRecursive(); + } + m_needsLayerSync = false; + return m_rootGraphicsLayer; +} + +void ChromeClientAndroid::scheduleCompositingLayerSync() +{ + if (m_needsLayerSync) + return; + m_needsLayerSync = true; + WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view()); + if (webViewCore) + webViewCore->layersDraw(); +} + +void ChromeClientAndroid::setNeedsOneShotDrawingSynchronization() +{ + // This should not be needed +} + +void ChromeClientAndroid::attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* layer) +{ + // frame is not used in Android as we should only get root graphics layer for the main frame + m_rootGraphicsLayer = layer; + if (!layer) + return; + scheduleCompositingLayerSync(); +} + +#endif + +void ChromeClientAndroid::setWebFrame(android::WebFrame* webframe) +{ + Release(m_webFrame); + m_webFrame = webframe; + Retain(m_webFrame); +} + +void ChromeClientAndroid::chromeDestroyed() +{ + Release(m_webFrame); + delete this; +} + +void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); } + +FloatRect ChromeClientAndroid::windowRect() { + ASSERT(m_webFrame); + if (!m_webFrame) + return FloatRect(); + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + if (!frameView) + return FloatRect(); + const WebCoreViewBridge* bridge = frameView->platformWidget(); + const IntRect& rect = bridge->getWindowBounds(); + FloatRect fRect(rect.x(), rect.y(), rect.width(), rect.height()); + return fRect; +} + +FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); } + +float ChromeClientAndroid::scaleFactor() +{ + ASSERT(m_webFrame); + return m_webFrame->density(); +} + +void ChromeClientAndroid::focus() +{ + ASSERT(m_webFrame); + bool isUserGesture = UserGestureIndicator::processingUserGesture(); + + // Ask the application to focus this WebView if the action is intiated by the user + if (isUserGesture) + m_webFrame->requestFocus(); +} +void ChromeClientAndroid::unfocus() { notImplemented(); } + +bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; } +void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); } + +void ChromeClientAndroid::focusedNodeChanged(Node* node) +{ + android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->focusNodeChanged(node); +} + +void ChromeClientAndroid::focusedFrameChanged(Frame*) { notImplemented(); } + +Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&, + const WindowFeatures& features, const NavigationAction&) +{ + ASSERT(frame); +#ifdef ANDROID_MULTIPLE_WINDOWS + if (frame->settings() && !(frame->settings()->supportMultipleWindows())) + // If the client doesn't support multiple windows, just return the current page + return frame->page(); +#endif + + const WebCoreViewBridge* bridge = frame->view()->platformWidget(); + bool dialog = features.dialog || !features.resizable + || (features.heightSet && features.height < bridge->height() + && features.widthSet && features.width < bridge->width()) + || (!features.menuBarVisible && !features.statusBarVisible + && !features.toolBarVisible && !features.locationBarVisible + && !features.scrollbarsVisible); + // fullscreen definitely means no dialog + if (features.fullscreen) + dialog = false; + WebCore::Frame* newFrame = m_webFrame->createWindow(dialog, + ScriptController::processingUserGesture()); + if (newFrame) { + WebCore::Page* page = newFrame->page(); + page->setGroupName(frame->page()->groupName()); + return page; + } + return NULL; +} + +void ChromeClientAndroid::show() { notImplemented(); } + +bool ChromeClientAndroid::canRunModal() { notImplemented(); return false; } +void ChromeClientAndroid::runModal() { notImplemented(); } + +void ChromeClientAndroid::setToolbarsVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::toolbarsVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setStatusbarVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::statusbarVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setScrollbarsVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::scrollbarsVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setMenubarVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::menubarVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setResizable(bool) { notImplemented(); } + +#if ENABLE(CONTEXT_MENUS) +void ChromeClientAndroid::showContextMenu() { notImplemented(); } +#endif + +// This function is called by the JavaScript bindings to print usually an error to +// a message console. Pass the message to the java side so that the client can +// handle it as it sees fit. +void ChromeClientAndroid::addMessageToConsole(MessageSource, MessageType, MessageLevel msgLevel, const String& message, unsigned int lineNumber, const String& sourceID) { + android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->addMessageToConsole(message, lineNumber, sourceID, msgLevel); +} + +void ChromeClientAndroid::formDidBlur(const WebCore::Node* node) +{ + android::WebViewCore::getWebViewCore(m_webFrame->page()->mainFrame()->view())->formDidBlur(node); +} + +bool ChromeClientAndroid::canRunBeforeUnloadConfirmPanel() { return true; } +bool ChromeClientAndroid::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) { + String url = frame->document()->documentURI(); + return android::WebViewCore::getWebViewCore(frame->view())->jsUnload(url, message); +} + +void ChromeClientAndroid::closeWindowSoon() +{ + ASSERT(m_webFrame); + Page* page = m_webFrame->page(); + Frame* mainFrame = page->mainFrame(); + // This will prevent javascript cross-scripting during unload + page->setGroupName(String()); + // Stop loading but do not send the unload event + mainFrame->loader()->stopLoading(UnloadEventPolicyNone); + // Cancel all pending loaders + mainFrame->loader()->stopAllLoaders(); + // Remove all event listeners so that no javascript can execute as a result + // of mouse/keyboard events. + mainFrame->document()->removeAllEventListeners(); + // Close the window. + m_webFrame->closeWindow(android::WebViewCore::getWebViewCore(mainFrame->view())); +} + +void ChromeClientAndroid::runJavaScriptAlert(Frame* frame, const String& message) +{ + String url = frame->document()->documentURI(); + + android::WebViewCore::getWebViewCore(frame->view())->jsAlert(url, message); +} + +bool ChromeClientAndroid::runJavaScriptConfirm(Frame* frame, const String& message) +{ + String url = frame->document()->documentURI(); + + return android::WebViewCore::getWebViewCore(frame->view())->jsConfirm(url, message); +} + +/* This function is called for the javascript method Window.prompt(). A dialog should be shown on + * the screen with an input put box. First param is the text, the second is the default value for + * the input box, third is return param. If the function returns true, the value set in the third parameter + * is provided to javascript, else null is returned to the script. + */ +bool ChromeClientAndroid::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result) +{ + String url = frame->document()->documentURI(); + return android::WebViewCore::getWebViewCore(frame->view())->jsPrompt(url, message, defaultValue, result); +} +void ChromeClientAndroid::setStatusbarText(const String&) { notImplemented(); } + +// This is called by the JavaScript interpreter when a script has been running for a long +// time. A dialog should be shown to the user asking them if they would like to cancel the +// Javascript. If true is returned, the script is cancelled. +// To make a device more responsive, we default to return true to disallow long running script. +// This implies that some of scripts will not be completed. +bool ChromeClientAndroid::shouldInterruptJavaScript() { + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + return android::WebViewCore::getWebViewCore(frameView)->jsInterrupt(); +} + +bool ChromeClientAndroid::tabsToLinks() const { return false; } + +IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); } + +void ChromeClientAndroid::invalidateWindow(const IntRect&, bool) +{ + notImplemented(); +} + +void ChromeClientAndroid::invalidateContentsAndWindow(const IntRect& updateRect, bool /*immediate*/) +{ + notImplemented(); +} + +void ChromeClientAndroid::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate) +{ + notImplemented(); +} + +// new to change 38068 (Nov 6, 2008) +void ChromeClientAndroid::scroll(const IntSize& scrollDelta, + const IntRect& rectToScroll, const IntRect& clipRect) { + notImplemented(); +} + +// new to change 38068 (Nov 6, 2008) +IntPoint ChromeClientAndroid::screenToWindow(const IntPoint&) const { + notImplemented(); + return IntPoint(); +} + +// new to change 38068 (Nov 6, 2008) +IntRect ChromeClientAndroid::windowToScreen(const IntRect&) const { + notImplemented(); + return IntRect(); +} + +PlatformPageClient ChromeClientAndroid::platformPageClient() const { + Page* page = m_webFrame->page(); + Frame* mainFrame = page->mainFrame(); + FrameView* view = mainFrame->view(); + PlatformWidget viewBridge = view->platformWidget(); + return viewBridge; +} + +void ChromeClientAndroid::contentsSizeChanged(Frame*, const IntSize&) const +{ + notImplemented(); +} + +void ChromeClientAndroid::scrollRectIntoView(const IntRect&, const ScrollView*) const +{ + notImplemented(); +} + +void ChromeClientAndroid::formStateDidChange(const Node*) +{ + notImplemented(); +} + +void ChromeClientAndroid::scrollbarsModeDidChange() const +{ + notImplemented(); +} + +void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {} +void ChromeClientAndroid::setToolTip(const String&, TextDirection) {} +void ChromeClientAndroid::print(Frame*) {} + +/* + * This function is called on the main (webcore) thread by SQLTransaction::deliverQuotaIncreaseCallback. + * The way that the callback mechanism is designed inside SQLTransaction means that there must be a new quota + * (which may be equal to the old quota if the user did not allow more quota) when this function returns. As + * we call into the browser thread to ask what to do with the quota, we block here and get woken up when the + * browser calls the native WebViewCore::SetDatabaseQuota method with the new quota value. + */ +#if ENABLE(DATABASE) +void ChromeClientAndroid::exceededDatabaseQuota(Frame* frame, const String& name) +{ + SecurityOrigin* origin = frame->document()->securityOrigin(); + DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker(); + + // We want to wait on a new quota from the UI thread. Reset the m_newQuota variable to represent we haven't received a new quota. + m_newQuota = -1; + + // This origin is being tracked and has exceeded it's quota. Call into + // the Java side of things to inform the user. + unsigned long long currentQuota = 0; + if (tracker.hasEntryForOrigin(origin)) + currentQuota = tracker.quotaForOrigin(origin); + + unsigned long long estimatedSize = 0; + + // Only update estimatedSize if we are trying to create a a new database, i.e. the usage for the database is 0. + if (tracker.usageForDatabase(name, origin) == 0) + estimatedSize = tracker.detailsForNameAndOrigin(name, origin).expectedUsage(); + + android::WebViewCore::getWebViewCore(frame->view())->exceededDatabaseQuota(frame->document()->documentURI(), name, currentQuota, estimatedSize); + + // We've sent notification to the browser so now wait for it to come back. + m_quotaThreadLock.lock(); + while (m_newQuota == -1) { + m_quotaThreadCondition.wait(m_quotaThreadLock); + } + m_quotaThreadLock.unlock(); + + // If new quota is unavailable, we may be able to resolve the situation by shrinking the quota of an origin that asked for a lot but is only using a little. + // If we find such a site, shrink it's quota and ask Java to try again. + + if ((unsigned long long) m_newQuota == currentQuota && !m_triedToReclaimDBQuota) { + m_triedToReclaimDBQuota = true; // we should only try this once per quota overflow. + unsigned long long reclaimedQuotaBytes = tryToReclaimDatabaseQuota(origin); + + // If we were able to free up enough space, try asking Java again. + // Otherwise, give up and deny the new database. :( + if (reclaimedQuotaBytes >= estimatedSize) { + exceededDatabaseQuota(frame, name); + return; + } + } + + // Update the DatabaseTracker with the new quota value (if the user declined + // new quota, this may equal the old quota) + tracker.setQuota(origin, m_newQuota); + m_triedToReclaimDBQuota = false; +} + +static unsigned long long tryToReclaimDatabaseQuota(SecurityOrigin* originNeedingQuota) { + DatabaseTracker& tracker = WebCore::DatabaseTracker::tracker(); + Vector<RefPtr<SecurityOrigin> > origins; + tracker.origins(origins); + unsigned long long reclaimedQuotaBytes = 0; + for (unsigned i = 0; i < origins.size(); i++) { + SecurityOrigin* originToReclaimFrom = origins[i].get(); + + // Don't try to reclaim from the origin that has exceeded its quota. + if (originToReclaimFrom->equal(originNeedingQuota)) + continue; + + unsigned long long originUsage = tracker.usageForOrigin(originToReclaimFrom); + unsigned long long originQuota = tracker.quotaForOrigin(originToReclaimFrom); + // If the origin has a quota that is more than it's current usage +1MB, shrink it. + static const int ONE_MB = 1 * 1024 * 1024; + if (originUsage + ONE_MB < originQuota) { + unsigned long long newQuota = originUsage + ONE_MB; + tracker.setQuota(originToReclaimFrom, newQuota); + reclaimedQuotaBytes += originQuota - newQuota; + } + } + return reclaimedQuotaBytes; +} +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +void ChromeClientAndroid::reachedMaxAppCacheSize(int64_t spaceNeeded) +{ + // Set m_newQuota before calling into the Java side. If we do this after, + // we could overwrite the result passed from the Java side and deadlock in the + // wait call below. + m_newQuota = -1; + Page* page = m_webFrame->page(); + Frame* mainFrame = page->mainFrame(); + FrameView* view = mainFrame->view(); + android::WebViewCore::getWebViewCore(view)->reachedMaxAppCacheSize(spaceNeeded); + + // We've sent notification to the browser so now wait for it to come back. + m_quotaThreadLock.lock(); + while (m_newQuota == -1) { + m_quotaThreadCondition.wait(m_quotaThreadLock); + } + m_quotaThreadLock.unlock(); + if (m_newQuota > 0) { + WebCore::cacheStorage().setMaximumSize(m_newQuota); + // Now the app cache will retry the saving the previously failed cache. + } +} +#endif + +void ChromeClientAndroid::populateVisitedLinks() +{ + Page* page = m_webFrame->page(); + Frame* mainFrame = page->mainFrame(); + FrameView* view = mainFrame->view(); + android::WebViewCore::getWebViewCore(view)->populateVisitedLinks(&page->group()); +} + +void ChromeClientAndroid::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation) +{ + ASSERT(geolocation); + if (!m_geolocationPermissions) { + m_geolocationPermissions = new GeolocationPermissions(android::WebViewCore::getWebViewCore(frame->view()), + m_webFrame->page()->mainFrame()); + } + m_geolocationPermissions->queryPermissionState(frame); +} + +void ChromeClientAndroid::cancelGeolocationPermissionRequestForFrame(Frame* frame, WebCore::Geolocation*) +{ + if (m_geolocationPermissions) + m_geolocationPermissions->cancelPermissionStateQuery(frame); +} + +void ChromeClientAndroid::provideGeolocationPermissions(const String &origin, bool allow, bool remember) +{ + ASSERT(m_geolocationPermissions); + m_geolocationPermissions->providePermissionState(origin, allow, remember); +} + +void ChromeClientAndroid::storeGeolocationPermissions() +{ + GeolocationPermissions::maybeStorePermanentPermissions(); +} + +void ChromeClientAndroid::onMainFrameLoadStarted() +{ + if (m_geolocationPermissions.get()) + m_geolocationPermissions->resetTemporaryPermissionStates(); +} + +void ChromeClientAndroid::runOpenPanel(Frame* frame, + PassRefPtr<FileChooser> chooser) +{ + android::WebViewCore* core = android::WebViewCore::getWebViewCore( + frame->view()); + core->openFileChooser(chooser); +} + +void ChromeClientAndroid::chooseIconForFiles(const Vector<WTF::String>&, FileChooser*) +{ + notImplemented(); +} + +void ChromeClientAndroid::setCursor(const Cursor&) +{ + notImplemented(); +} + +void ChromeClientAndroid::wakeUpMainThreadWithNewQuota(long newQuota) { + MutexLocker locker(m_quotaThreadLock); + m_newQuota = newQuota; + m_quotaThreadCondition.signal(); +} + +#if ENABLE(TOUCH_EVENTS) +void ChromeClientAndroid::needTouchEvents(bool needTouchEvents) +{ + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView); + if (core) + core->needTouchEvents(needTouchEvents); +} +#endif + +bool ChromeClientAndroid::selectItemWritingDirectionIsNatural() +{ + return false; +} + +PassRefPtr<PopupMenu> ChromeClientAndroid::createPopupMenu(PopupMenuClient* client) const +{ + return adoptRef(new PopupMenuAndroid(static_cast<ListPopupMenuClient*>(client))); +} + +PassRefPtr<SearchPopupMenu> ChromeClientAndroid::createSearchPopupMenu(PopupMenuClient*) const +{ + return adoptRef(new SearchPopupMenuAndroid); +} + +void ChromeClientAndroid::reachedApplicationCacheOriginQuota(SecurityOrigin*) +{ + notImplemented(); +} + +#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS) +void ChromeClientAndroid::webAppCanBeInstalled() +{ + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView); + if (core) + core->notifyWebAppCanBeInstalled(); +} +#endif + +#if ENABLE(VIDEO) +bool ChromeClientAndroid::supportsFullscreenForNode(const Node* node) +{ + return node->hasTagName(HTMLNames::videoTag); +} + +void ChromeClientAndroid::enterFullscreenForNode(Node* node) +{ + if (!node->hasTagName(HTMLNames::videoTag)) + return; + + HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(node); + String url = videoElement->currentSrc(); + LayerAndroid* layer = videoElement->platformLayer(); + if (!layer) + return; + + FrameView* frameView = m_webFrame->page()->mainFrame()->view(); + android::WebViewCore* core = android::WebViewCore::getWebViewCore(frameView); + m_webFrame->page()->mainFrame()->document()->webkitWillEnterFullScreenForElement(videoElement); + if (core) + core->enterFullscreenForVideoLayer(layer->uniqueId(), url); +} + +void ChromeClientAndroid::exitFullscreenForNode(Node* node) +{ +} +#endif + +#if ENABLE(FULLSCREEN_API) +void ChromeClientAndroid::exitFullScreenForElement(Element* element) +{ + if (!element) + return; + + HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(element); + videoElement->exitFullscreen(); +} +#endif + +} diff --git a/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h new file mode 100644 index 0000000..d49cec8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h @@ -0,0 +1,216 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ChromeClientAndroid_h +#define ChromeClientAndroid_h + +#include "ChromeClient.h" + +#include "GeolocationPermissions.h" +#include "PopupMenu.h" +#include "SearchPopupMenu.h" +#include "Timer.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { +class PopupMenuClient; +class Geolocation; +} + +using namespace WebCore; + +namespace android { + class WebFrame; + + class ChromeClientAndroid : public ChromeClient { + public: + ChromeClientAndroid() : m_webFrame(0), m_geolocationPermissions(0) +#if USE(ACCELERATED_COMPOSITING) + , m_rootGraphicsLayer(0) + , m_needsLayerSync(false) +#endif + , m_triedToReclaimDBQuota(false) + { } + virtual void chromeDestroyed(); + + virtual void setWindowRect(const FloatRect&); + virtual FloatRect windowRect(); + + virtual FloatRect pageRect(); + + virtual float scaleFactor(); + + virtual void focus(); + virtual void unfocus(); + virtual void formDidBlur(const WebCore::Node*); + virtual bool canTakeFocus(FocusDirection); + virtual void takeFocus(FocusDirection); + + virtual void focusedNodeChanged(Node*); + virtual void focusedFrameChanged(Frame*); + + // The Frame pointer provides the ChromeClient with context about which + // Frame wants to create the new Page. Also, the newly created window + // should not be shown to the user until the ChromeClient of the newly + // created Page has its show method called. + virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&); + virtual void show(); + + virtual bool canRunModal(); + virtual void runModal(); + + virtual void setToolbarsVisible(bool); + virtual bool toolbarsVisible(); + + virtual void setStatusbarVisible(bool); + virtual bool statusbarVisible(); + + virtual void setScrollbarsVisible(bool); + virtual bool scrollbarsVisible(); + + virtual void setMenubarVisible(bool); + virtual bool menubarVisible(); + + virtual void setResizable(bool); + + virtual void addMessageToConsole(MessageSource, MessageType, MessageLevel, const String& message, unsigned int lineNumber, const String& sourceID); + + virtual bool canRunBeforeUnloadConfirmPanel(); + virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame); + + virtual void closeWindowSoon(); + + virtual void runJavaScriptAlert(Frame*, const String&); + virtual bool runJavaScriptConfirm(Frame*, const String&); + virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result); + virtual void setStatusbarText(const String&); + virtual bool shouldInterruptJavaScript(); + virtual bool tabsToLinks() const; + + virtual IntRect windowResizerRect() const; + + // Methods used by HostWindow. + virtual void invalidateWindow(const WebCore::IntRect&, bool); + virtual void invalidateContentsAndWindow(const WebCore::IntRect&, bool); + virtual void invalidateContentsForSlowScroll(const WebCore::IntRect&, bool); + virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect); + virtual IntPoint screenToWindow(const IntPoint&) const; + virtual IntRect windowToScreen(const IntRect&) const; + virtual PlatformPageClient platformPageClient() const; + virtual void contentsSizeChanged(Frame*, const IntSize&) const; + virtual void scrollRectIntoView(const IntRect&, const ScrollView*) const; + // End methods used by HostWindow. + + virtual void scrollbarsModeDidChange() const; + virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned int); + + virtual void setToolTip(const String&, TextDirection); + + virtual void print(Frame*); +#if ENABLE(DATABASE) + virtual void exceededDatabaseQuota(Frame*, const String&); +#endif +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + virtual void reachedMaxAppCacheSize(int64_t spaceNeeded); + virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*); +#endif + + virtual void populateVisitedLinks(); + +#if ENABLE(TOUCH_EVENTS) + virtual void needTouchEvents(bool); +#endif + + // Methods used to request and provide Geolocation permissions. + virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*); + virtual void cancelGeolocationPermissionRequestForFrame(WebCore::Frame*, WebCore::Geolocation*); + // Android-specific + void provideGeolocationPermissions(const String &origin, bool allow, bool remember); + void storeGeolocationPermissions(); + void onMainFrameLoadStarted(); + + virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>); + virtual void setCursor(const Cursor&); + virtual void chooseIconForFiles(const WTF::Vector<WTF::String>&, FileChooser*); + + // Notification that the given form element has changed. This function + // will be called frequently, so handling should be very fast. + virtual void formStateDidChange(const Node*); + + virtual PassOwnPtr<HTMLParserQuirks> createHTMLParserQuirks() { return 0; } + + // Android-specific + void setWebFrame(android::WebFrame* webframe); + android::WebFrame* webFrame() { return m_webFrame; } + void wakeUpMainThreadWithNewQuota(long newQuota); + +#if USE(ACCELERATED_COMPOSITING) + virtual void attachRootGraphicsLayer(WebCore::Frame*, WebCore::GraphicsLayer* g); + virtual void setNeedsOneShotDrawingSynchronization(); + virtual void scheduleCompositingLayerSync(); + virtual bool allowsAcceleratedCompositing() const { return true; } + WebCore::GraphicsLayer* layersSync(); +#endif + + virtual bool selectItemWritingDirectionIsNatural(); + virtual PassRefPtr<WebCore::PopupMenu> createPopupMenu(WebCore::PopupMenuClient*) const; + virtual PassRefPtr<WebCore::SearchPopupMenu> createSearchPopupMenu(WebCore::PopupMenuClient*) const; + +#if ENABLE(CONTEXT_MENUS) + virtual void showContextMenu(); +#endif + +#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS) + virtual void webAppCanBeInstalled(); +#endif + +#if ENABLE(FULLSCREEN_API) + virtual void exitFullScreenForElement(Element*); +#endif + +#if ENABLE(VIDEO) + virtual bool supportsFullscreenForNode(const WebCore::Node*); + virtual void enterFullscreenForNode(WebCore::Node*); + virtual void exitFullscreenForNode(WebCore::Node*); +#endif + + private: + android::WebFrame* m_webFrame; + // The Geolocation permissions manager. + OwnPtr<GeolocationPermissions> m_geolocationPermissions; +#if USE(ACCELERATED_COMPOSITING) + WebCore::GraphicsLayer* m_rootGraphicsLayer; + bool m_needsLayerSync; +#endif + WTF::ThreadCondition m_quotaThreadCondition; + WTF::Mutex m_quotaThreadLock; + long m_newQuota; + bool m_triedToReclaimDBQuota; + }; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h new file mode 100644 index 0000000..8166eb7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ChromiumIncludes.h @@ -0,0 +1,104 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ChromiumIncludes_h +#define ChromiumIncludes_h + +#include "config.h" + +// Include all external/chromium files in this file so the problems with the LOG +// and LOG_ASSERT defines can be handled in one place. + +// Undefine LOG and LOG_ASSERT before including chrome code, and if they were +// defined attempt to set the macros to the Android logging macros (which are +// the only ones that actually log). + +#ifdef LOG +#define LOG_WAS_DEFINED LOG +#undef LOG +#endif + +#ifdef LOG_ASSERT +#define LOG_ASSERT_WAS_DEFINED LOG_ASSERT +#undef LOG_ASSERT +#endif + +#include <android/net/android_network_library_impl.h> +#include <base/callback.h> +#include <base/condition_variable.h> +#include <base/lock.h> +#include <base/message_loop_proxy.h> +#include <base/ref_counted.h> +#include <base/string_util.h> +#include <base/sys_string_conversions.h> +#include <base/thread.h> +#include <base/time.h> +#include <base/tuple.h> +#include <chrome/browser/net/sqlite_persistent_cookie_store.h> +#include <net/base/auth.h> +#include <net/base/cookie_monster.h> +#include <net/base/cookie_policy.h> +#include <net/base/data_url.h> +#include <net/base/host_resolver.h> +#include <net/base/io_buffer.h> +#include <net/base/load_flags.h> +#include <net/base/net_errors.h> +#include <net/base/mime_util.h> +#include <net/base/ssl_config_service.h> +#include <net/disk_cache/disk_cache.h> +#include <net/http/http_auth_handler_factory.h> +#include <net/http/http_cache.h> +#include <net/http/http_network_layer.h> +#include <net/http/http_response_headers.h> +#include <net/proxy/proxy_config_service_android.h> +#include <net/proxy/proxy_service.h> +#include <net/url_request/url_request.h> +#include <net/url_request/url_request_context.h> + +#if ENABLE(WEB_AUTOFILL) +#include <autofill/autofill_manager.h> +#include <autofill/autofill_profile.h> +#include <autofill/personal_data_manager.h> +#include <base/logging.h> +#include <base/scoped_vector.h> +#include <base/string16.h> +#include <base/utf_string_conversions.h> +#include <chrome/browser/autofill/autofill_host.h> +#include <chrome/browser/profile.h> +#include <chrome/browser/tab_contents/tab_contents.h> +#include <webkit/glue/form_data.h> +#endif + +#undef LOG +#if defined(LOG_WAS_DEFINED) && defined(LOG_PRI) +#define LOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) +#endif + +#undef LOG_ASSERT +#if defined(LOG_ASSERT_WAS_DEFINED) && defined(LOG_FATAL_IF) +#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) +#endif + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp b/Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp new file mode 100644 index 0000000..1872fb9 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ChromiumInit.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ChromiumInit.h" + +#include "ChromiumIncludes.h" + +#include <cutils/log.h> +#include <string> + +namespace android { + +bool logMessageHandler(int severity, const char* file, int line, size_t message_start, const std::string& str) { + int androidSeverity = ANDROID_LOG_VERBOSE; + switch(severity) { + case logging::LOG_FATAL: + androidSeverity = ANDROID_LOG_FATAL; + break; + case logging::LOG_ERROR_REPORT: + case logging::LOG_ERROR: + androidSeverity = ANDROID_LOG_ERROR; + break; + case logging::LOG_WARNING: + androidSeverity = ANDROID_LOG_WARN; + break; + default: + androidSeverity = ANDROID_LOG_VERBOSE; + break; + } + android_printLog(androidSeverity, "chromium", "%s:%d: %s", file, line, str.c_str()); + return false; +} + +namespace { + scoped_ptr<net::NetworkChangeNotifier> networkChangeNotifier; +} + +void initChromium() +{ + static Lock lock; + AutoLock aLock(lock); + static bool initCalled = false; + if (!initCalled) { + logging::SetLogMessageHandler(logMessageHandler); + networkChangeNotifier.reset(net::NetworkChangeNotifier::Create()); + net::HttpNetworkLayer::EnableSpdy("npn"); + initCalled = true; + } +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/ChromiumInit.h b/Source/WebKit/android/WebCoreSupport/ChromiumInit.h new file mode 100644 index 0000000..235c3dc --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ChromiumInit.h @@ -0,0 +1,38 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ChromiumLogging_h +#define ChromiumLogging_h + +namespace android { + +// Sends chromium logs to logcat +// +// This only calls into chromium once, but can be called multiple times. +// It should be called before any other calls into external/chromium. +void initChromium(); +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp new file mode 100644 index 0000000..3dc4b00 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ContextMenuClientAndroid.h" + +#include "NotImplemented.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +void ContextMenuClientAndroid::contextMenuDestroyed() { delete this; } + +PlatformMenuDescription ContextMenuClientAndroid::getCustomMenuFromDefaultItems(ContextMenu*) { notImplemented(); return 0; } +void ContextMenuClientAndroid::contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { notImplemented(); } + +void ContextMenuClientAndroid::downloadURL(const KURL& url) { notImplemented(); } +void ContextMenuClientAndroid::copyImageToClipboard(const HitTestResult&) { notImplemented(); } +void ContextMenuClientAndroid::searchWithGoogle(const Frame*) { notImplemented(); } +void ContextMenuClientAndroid::lookUpInDictionary(Frame*) { notImplemented(); } +void ContextMenuClientAndroid::speak(const String&) { notImplemented(); } +void ContextMenuClientAndroid::stopSpeaking() { notImplemented(); } +bool ContextMenuClientAndroid::isSpeaking() +{ + notImplemented(); + return false; +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h new file mode 100644 index 0000000..4563829 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ContextMenuClientAndroid.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ContextMenuClientAndroid_h +#define ContextMenuClientAndroid_h + +#include "ContextMenuClient.h" + +namespace WebCore { + +class ContextMenuClientAndroid : public ContextMenuClient { +public: + virtual void contextMenuDestroyed(); + + virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*); + virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*); + + virtual void downloadURL(const KURL& url); + virtual void copyImageToClipboard(const HitTestResult&); + virtual void searchWithGoogle(const Frame*); + virtual void lookUpInDictionary(Frame*); + virtual void speak(const String&); + virtual void stopSpeaking(); + virtual bool isSpeaking(); +}; + +} // namespace WebCore + +#endif // ContextMenuClientAndroid_h diff --git a/Source/WebKit/android/WebCoreSupport/CookieClient.h b/Source/WebKit/android/WebCoreSupport/CookieClient.h new file mode 100644 index 0000000..56d9382 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/CookieClient.h @@ -0,0 +1,46 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef COOKIE_CLIENT_H +#define COOKIE_CLIENT_H + +#include <KURL.h> +#include <PlatformString.h> + +using namespace WebCore; + +namespace android { + +class CookieClient { + +public: + virtual ~CookieClient() {} + virtual void setCookies(const KURL& url, const String& value) = 0; + virtual String cookies(const KURL& url) = 0; + virtual bool cookiesEnabled() = 0; +}; + +} +#endif diff --git a/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp new file mode 100644 index 0000000..f8de733 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DeviceMotionClientAndroid.h" + +#include "WebViewCore.h" + +using namespace WebCore; + +namespace android { + +DeviceMotionClientAndroid::DeviceMotionClientAndroid() + : m_client(0) +{ +} + +void DeviceMotionClientAndroid::setWebViewCore(WebViewCore* webViewCore) +{ + m_webViewCore = webViewCore; + ASSERT(m_webViewCore); +} + +void DeviceMotionClientAndroid::setController(DeviceMotionController* controller) +{ + // This will be called by the Page constructor before the WebViewCore + // has been configured regarding the mock. We cache the controller for + // later use. + m_controller = controller; + ASSERT(m_controller); +} + +void DeviceMotionClientAndroid::startUpdating() +{ + client()->startUpdating(); +} + +void DeviceMotionClientAndroid::stopUpdating() +{ + client()->stopUpdating(); +} + +DeviceMotionData* DeviceMotionClientAndroid::currentDeviceMotion() const +{ + return client()->currentDeviceMotion(); +} + +void DeviceMotionClientAndroid::deviceMotionControllerDestroyed() +{ + delete this; +} + +DeviceMotionClient* DeviceMotionClientAndroid::client() const +{ + if (!m_client) { + m_client = m_webViewCore->deviceMotionAndOrientationManager()->motionClient(); + m_client->setController(m_controller); + } + return m_client; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h new file mode 100644 index 0000000..98d4709 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DeviceMotionClientAndroid.h @@ -0,0 +1,65 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DeviceMotionClientAndroid_h +#define DeviceMotionClientAndroid_h + +#include <DeviceMotionClient.h> + +namespace WebCore { +class DeviceMotionController; +} + +namespace android { + +class WebViewCore; + +// The Android implementation of DeviceMotionClient. Acts as a proxy to +// the real or mock impl, which is owned by the WebViewCore. +class DeviceMotionClientAndroid : public WebCore::DeviceMotionClient { +public: + DeviceMotionClientAndroid(); + + void setWebViewCore(WebViewCore*); + + // DeviceMotionClient methods + virtual void setController(WebCore::DeviceMotionController*); + virtual void startUpdating(); + virtual void stopUpdating(); + virtual WebCore::DeviceMotionData* currentDeviceMotion() const; + virtual void deviceMotionControllerDestroyed(); + +private: + WebCore::DeviceMotionClient* client() const; + + WebViewCore* m_webViewCore; + WebCore::DeviceMotionController* m_controller; + // Lazily obtained cache of the client owned by the WebViewCore. + mutable WebCore::DeviceMotionClient* m_client; +}; + +} // namespace android + +#endif // DeviceMotionClientAndroid_h diff --git a/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp new file mode 100644 index 0000000..9d7145c --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DeviceOrientationClientAndroid.h" + +#include "WebViewCore.h" + +using namespace WebCore; + +namespace android { + +DeviceOrientationClientAndroid::DeviceOrientationClientAndroid() + : m_client(0) +{ +} + +void DeviceOrientationClientAndroid::setWebViewCore(WebViewCore* webViewCore) +{ + m_webViewCore = webViewCore; + ASSERT(m_webViewCore); +} + +void DeviceOrientationClientAndroid::setController(DeviceOrientationController* controller) +{ + // This will be called by the Page constructor before the WebViewCore + // has been configured regarding the mock. We cache the controller for + // later use. + m_controller = controller; + ASSERT(m_controller); +} + +void DeviceOrientationClientAndroid::startUpdating() +{ + client()->startUpdating(); +} + +void DeviceOrientationClientAndroid::stopUpdating() +{ + client()->stopUpdating(); +} + +DeviceOrientation* DeviceOrientationClientAndroid::lastOrientation() const +{ + return client()->lastOrientation(); +} + +void DeviceOrientationClientAndroid::deviceOrientationControllerDestroyed() +{ + delete this; +} + +DeviceOrientationClient* DeviceOrientationClientAndroid::client() const +{ + if (!m_client) { + m_client = m_webViewCore->deviceMotionAndOrientationManager()->orientationClient(); + m_client->setController(m_controller); + } + return m_client; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h new file mode 100644 index 0000000..7842b95 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DeviceOrientationClientAndroid.h @@ -0,0 +1,65 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DeviceOrientationClientAndroid_h +#define DeviceOrientationClientAndroid_h + +#include <DeviceOrientationClient.h> + +namespace WebCore { +class DeviceOrientationController; +} + +namespace android { + +class WebViewCore; + +// The Android implementation of DeviceOrientationClient. Acts as a proxy to +// the real or mock impl, which is owned by the WebViewCore. +class DeviceOrientationClientAndroid : public WebCore::DeviceOrientationClient { +public: + DeviceOrientationClientAndroid(); + + void setWebViewCore(WebViewCore*); + + // DeviceOrientationClient methods + virtual void setController(WebCore::DeviceOrientationController*); + virtual void startUpdating(); + virtual void stopUpdating(); + virtual WebCore::DeviceOrientation* lastOrientation() const; + virtual void deviceOrientationControllerDestroyed(); + +private: + WebCore::DeviceOrientationClient* client() const; + + WebViewCore* m_webViewCore; + WebCore::DeviceOrientationController* m_controller; + // Lazily obtained cache of the client owned by the WebViewCore. + mutable WebCore::DeviceOrientationClient* m_client; +}; + +} // namespace android + +#endif // DeviceOrientationClientAndroid_h diff --git a/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp new file mode 100644 index 0000000..f64b80c --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebCore" + +#include "config.h" +#include "DragClientAndroid.h" +#include "NotImplemented.h" + +namespace android { + +void DragClientAndroid::dragControllerDestroyed() { notImplemented(); delete this; } + +void DragClientAndroid::willPerformDragDestinationAction(DragDestinationAction, DragData*) { notImplemented(); } + +DragDestinationAction DragClientAndroid::actionMaskForDrag(DragData*) { notImplemented(); return DragDestinationActionNone; } + +DragSourceAction DragClientAndroid::dragSourceActionMaskForPoint(const IntPoint&) { notImplemented(); return DragSourceActionNone; } + +void* DragClientAndroid::createDragImageForLink(KURL&, String const&, Frame*) { return NULL; } +void DragClientAndroid::willPerformDragSourceAction(DragSourceAction, IntPoint const&, Clipboard*) {} +void DragClientAndroid::startDrag(void*, IntPoint const&, IntPoint const&, Clipboard*, Frame*, bool) {} + +} diff --git a/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h new file mode 100644 index 0000000..020e1f1 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/DragClientAndroid.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DragClientAndroid_h +#define DragClientAndroid_h + +#include "DragClient.h" + +using namespace WebCore; + +namespace android { + + class DragClientAndroid : public DragClient { + public: + virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*); + virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*); + virtual DragDestinationAction actionMaskForDrag(DragData*); + //We work in window rather than view coordinates here + virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&); + + virtual void startDrag(DragImageRef dragImage, const IntPoint& dragImageOrigin, const IntPoint& eventPos, Clipboard*, Frame*, bool linkDrag = false); + virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*); + + virtual void dragControllerDestroyed(); + }; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp new file mode 100644 index 0000000..250fdbf --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebCore" + +#include "config.h" +#include "Editor.h" +#include "EditorClientAndroid.h" +#include "Event.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "NotImplemented.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformString.h" +#include "WebViewCore.h" +#include "WindowsKeyboardCodes.h" + +using namespace WebCore::HTMLNames; + +namespace android { + +void EditorClientAndroid::pageDestroyed() { + delete this; +} + +bool EditorClientAndroid::shouldDeleteRange(Range*) { return true; } +bool EditorClientAndroid::shouldShowDeleteInterface(HTMLElement*) { notImplemented(); return false; } +bool EditorClientAndroid::smartInsertDeleteEnabled() { notImplemented(); return false; } +bool EditorClientAndroid::isSelectTrailingWhitespaceEnabled(){ notImplemented(); return false; } +bool EditorClientAndroid::isContinuousSpellCheckingEnabled() { notImplemented(); return false; } +void EditorClientAndroid::toggleContinuousSpellChecking() { notImplemented(); } +bool EditorClientAndroid::isGrammarCheckingEnabled() { notImplemented(); return false; } +void EditorClientAndroid::toggleGrammarChecking() { notImplemented(); } +int EditorClientAndroid::spellCheckerDocumentTag() { notImplemented(); return -1; } + +bool EditorClientAndroid::isEditable() { /* notImplemented(); */ return false; } + +// Following Qt's implementation. For shouldBeginEditing and shouldEndEditing. +// Returning true for these fixes issue http://b/issue?id=735185 +bool EditorClientAndroid::shouldBeginEditing(Range*) +{ + return true; +} + +bool EditorClientAndroid::shouldEndEditing(Range*) +{ + return true; +} + +bool EditorClientAndroid::shouldInsertNode(Node*, Range*, EditorInsertAction) { notImplemented(); return true; } +bool EditorClientAndroid::shouldInsertText(const String&, Range*, EditorInsertAction) { return true; } +bool EditorClientAndroid::shouldApplyStyle(CSSStyleDeclaration*, Range*) { notImplemented(); return true; } + +void EditorClientAndroid::didBeginEditing() { notImplemented(); } + +// This function is called so that the platform can handle changes to content. It is called +// after the contents have been edited or unedited (ie undo) +void EditorClientAndroid::respondToChangedContents() { notImplemented(); } + +void EditorClientAndroid::didEndEditing() { notImplemented(); } +void EditorClientAndroid::didWriteSelectionToPasteboard() { notImplemented(); } +void EditorClientAndroid::didSetSelectionTypesForPasteboard() { notImplemented(); } + +// Copied from the Window's port of WebKit. +static const unsigned AltKey = 1 << 0; +static const unsigned ShiftKey = 1 << 1; + +struct KeyDownEntry { + unsigned virtualKey; + unsigned modifiers; + const char* name; +}; + +struct KeyPressEntry { + unsigned charCode; + unsigned modifiers; + const char* name; +}; + +static const KeyDownEntry keyDownEntries[] = { + { VK_LEFT, 0, "MoveLeft" }, + { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, + { VK_LEFT, AltKey, "MoveWordLeft" }, + { VK_LEFT, AltKey | ShiftKey, "MoveWordLeftAndModifySelection" }, + { VK_RIGHT, 0, "MoveRight" }, + { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, + { VK_RIGHT, AltKey, "MoveWordRight" }, + { VK_RIGHT, AltKey | ShiftKey, "MoveWordRightAndModifySelection" }, + { VK_UP, 0, "MoveUp" }, + { VK_UP, ShiftKey, "MoveUpAndModifySelection" }, + { VK_DOWN, 0, "MoveDown" }, + { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" }, + + { VK_BACK, 0, "BackwardDelete" }, + { VK_BACK, ShiftKey, "ForwardDelete" }, + { VK_BACK, AltKey, "DeleteWordBackward" }, + { VK_BACK, AltKey | ShiftKey, "DeleteWordForward" }, + + { VK_ESCAPE, 0, "Cancel" }, + { VK_TAB, 0, "InsertTab" }, + { VK_TAB, ShiftKey, "InsertBacktab" }, + { VK_RETURN, 0, "InsertNewline" }, + { VK_RETURN, AltKey, "InsertNewline" }, + { VK_RETURN, AltKey | ShiftKey, "InsertNewline" } +}; + +static const KeyPressEntry keyPressEntries[] = { + { '\t', 0, "InsertTab" }, + { '\t', ShiftKey, "InsertBackTab" }, + { '\r', 0, "InsertNewline" }, + { '\r', AltKey, "InsertNewline" }, + { '\r', AltKey | ShiftKey, "InsertNewline" } +}; + +static const char* interpretKeyEvent(const KeyboardEvent* evt) +{ + const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); + + static HashMap<int, const char*>* keyDownCommandsMap = 0; + static HashMap<int, const char*>* keyPressCommandsMap = 0; + + if (!keyDownCommandsMap) { + keyDownCommandsMap = new HashMap<int, const char*>; + keyPressCommandsMap = new HashMap<int, const char*>; + + for (unsigned i = 0; i < sizeof(keyDownEntries)/sizeof(KeyDownEntry); i++) + keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name); + + for (unsigned i = 0; i < sizeof(keyPressEntries)/sizeof(KeyPressEntry); i++) + keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name); + } + + unsigned modifiers = 0; + if (keyEvent->shiftKey()) + modifiers |= ShiftKey; + if (keyEvent->altKey()) + modifiers |= AltKey; + + if (evt->type() == eventNames().keydownEvent) { + int mapKey = modifiers << 16 | evt->keyCode(); + return mapKey ? keyDownCommandsMap->get(mapKey) : 0; + } + + int mapKey = modifiers << 16 | evt->charCode(); + return mapKey ? keyPressCommandsMap->get(mapKey) : 0; +} + +void EditorClientAndroid::handleKeyboardEvent(KeyboardEvent* event) { + ASSERT(m_page); + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return; + + const PlatformKeyboardEvent* keyEvent = event->keyEvent(); + // TODO: If the event is not coming from Android Java, e.g. from JavaScript, + // PlatformKeyboardEvent is null. We should support this later. + if (!keyEvent) + return; + + Editor::Command command = frame->editor()->command(interpretKeyEvent(event)); + if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) { + if (!command.isTextInsertion() && command.execute(event)) { + // This function mimics the Windows version. However, calling event->setDefaultHandled() + // prevents the javascript key events for the delete key from happening. + // Update: Safari doesn't send delete key events to javascript so + // we will mimic that behavior. + event->setDefaultHandled(); + } + return; + } + + if (command.execute(event)) { + event->setDefaultHandled(); + return; + } + + // Don't insert null or control characters as they can result in unexpected behaviour + if (event->charCode() < ' ') + return; + + if (frame->editor()->insertText(keyEvent->text(), event)) + event->setDefaultHandled(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// we just don't support Undo/Redo at the moment + +void EditorClientAndroid::registerCommandForUndo(PassRefPtr<EditCommand>) {} +void EditorClientAndroid::registerCommandForRedo(PassRefPtr<EditCommand>) {} +void EditorClientAndroid::clearUndoRedoOperations() {} +bool EditorClientAndroid::canUndo() const { return false; } +bool EditorClientAndroid::canRedo() const { return false; } +void EditorClientAndroid::undo() {} +void EditorClientAndroid::redo() {} + +// functions new to Jun-07 tip of tree merge: +void EditorClientAndroid::showSpellingUI(bool) {} +void EditorClientAndroid::getGuessesForWord(String const&, const String&, WTF::Vector<String>&) {} +bool EditorClientAndroid::spellingUIIsShowing() { return false; } +void EditorClientAndroid::checkGrammarOfString(unsigned short const*, int, WTF::Vector<GrammarDetail>&, int*, int*) {} +void EditorClientAndroid::checkSpellingOfString(unsigned short const*, int, int*, int*) {} +String EditorClientAndroid::getAutoCorrectSuggestionForMisspelledWord(const String&) { return String(); } +void EditorClientAndroid::textFieldDidEndEditing(Element*) {} +void EditorClientAndroid::textDidChangeInTextArea(Element*) {} +void EditorClientAndroid::textDidChangeInTextField(Element*) {} +void EditorClientAndroid::textFieldDidBeginEditing(Element*) {} +void EditorClientAndroid::ignoreWordInSpellDocument(String const&) {} + +// We need to pass the selection up to the WebTextView +void EditorClientAndroid::respondToChangedSelection() { + if (m_uiGeneratedSelectionChange) + return; + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame || !frame->view()) + return; + WebViewCore* webViewCore = WebViewCore::getWebViewCore(frame->view()); + webViewCore->updateTextSelection(); +} + +bool EditorClientAndroid::shouldChangeSelectedRange(Range*, Range*, EAffinity, + bool) { + return m_shouldChangeSelectedRange; +} + +bool EditorClientAndroid::doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; } +void EditorClientAndroid::textWillBeDeletedInTextField(Element*) {} +void EditorClientAndroid::updateSpellingUIWithGrammarString(String const&, GrammarDetail const&) {} +void EditorClientAndroid::updateSpellingUIWithMisspelledWord(String const&) {} +void EditorClientAndroid::learnWord(String const&) {} + +// functions new to the Nov-16-08 tip of tree merge: +bool EditorClientAndroid::shouldMoveRangeAfterDelete(Range*, Range*) { return true; } +void EditorClientAndroid::setInputMethodState(bool) {} + +// functions new to Feb-19 tip of tree merge: +void EditorClientAndroid::handleInputMethodKeydown(KeyboardEvent*) {} + +void EditorClientAndroid::willSetInputMethodState() +{ + notImplemented(); +} + +void EditorClientAndroid::requestCheckingOfString(SpellChecker*, int, const String&) {} + +#if ENABLE(WEB_AUTOFILL) +WebAutoFill* EditorClientAndroid::getAutoFill() +{ + if (!m_autoFill) + m_autoFill.set(new WebAutoFill()); + + return m_autoFill.get(); +} +#endif + +} diff --git a/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h new file mode 100644 index 0000000..94a6518 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/EditorClientAndroid.h @@ -0,0 +1,135 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EditorClientAndroid_h +#define EditorClientAndroid_h + +#include "config.h" + + +#include "EditorClient.h" +#include "Page.h" +#include "autofill/WebAutoFill.h" + +#include <wtf/OwnPtr.h> + +using namespace WebCore; + +namespace android { + +class EditorClientAndroid : public EditorClient { +public: + EditorClientAndroid() { + m_shouldChangeSelectedRange = true; + m_uiGeneratedSelectionChange = false; + } + virtual void pageDestroyed(); + + virtual bool shouldDeleteRange(Range*); + virtual bool shouldShowDeleteInterface(HTMLElement*); + virtual bool smartInsertDeleteEnabled(); + virtual bool isSelectTrailingWhitespaceEnabled(); + virtual bool isContinuousSpellCheckingEnabled(); + virtual void toggleContinuousSpellChecking(); + virtual bool isGrammarCheckingEnabled(); + virtual void toggleGrammarChecking(); + virtual int spellCheckerDocumentTag(); + + virtual bool isEditable(); + + virtual bool shouldBeginEditing(Range*); + virtual bool shouldEndEditing(Range*); + virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction); + virtual bool shouldInsertText(const String&, Range*, EditorInsertAction); + virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting); + + virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*); +// virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle); +// virtual bool doCommandBySelector(SEL selector); + virtual bool shouldMoveRangeAfterDelete(Range*, Range*); + + virtual void didBeginEditing(); + virtual void respondToChangedContents(); + virtual void respondToChangedSelection(); + virtual void didEndEditing(); + virtual void didWriteSelectionToPasteboard(); + virtual void didSetSelectionTypesForPasteboard(); +// virtual void didChangeTypingStyle:(NSNotification *)notification; +// virtual void didChangeSelection:(NSNotification *)notification; +// virtual NSUndoManager* undoManager:(WebView *)webView; + + virtual void registerCommandForUndo(PassRefPtr<EditCommand>); + virtual void registerCommandForRedo(PassRefPtr<EditCommand>); + virtual void clearUndoRedoOperations(); + + virtual bool canUndo() const; + virtual bool canRedo() const; + + virtual void undo(); + virtual void redo(); + + virtual void handleKeyboardEvent(KeyboardEvent*); + virtual void handleInputMethodKeydown(KeyboardEvent*); + + virtual void textFieldDidBeginEditing(Element*); + virtual void textFieldDidEndEditing(Element*); + virtual void textDidChangeInTextField(Element*); + virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*); + virtual void textWillBeDeletedInTextField(Element*); + virtual void textDidChangeInTextArea(Element*); + + virtual void ignoreWordInSpellDocument(const String&); + virtual void learnWord(const String&); + virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength); + virtual String getAutoCorrectSuggestionForMisspelledWord(const String& misspelledWorld); + virtual void checkGrammarOfString(const UChar*, int length, WTF::Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength); + virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail); + virtual void updateSpellingUIWithMisspelledWord(const String&); + virtual void showSpellingUI(bool show); + virtual bool spellingUIIsShowing(); + virtual void getGuessesForWord(const String&, const String& context, WTF::Vector<String>& guesses); + virtual void willSetInputMethodState(); + virtual void setInputMethodState(bool); + virtual void requestCheckingOfString(SpellChecker*, int, const String&); + + // Android specific: + void setPage(Page* page) { m_page = page; } + void setShouldChangeSelectedRange(bool shouldChangeSelectedRange) { m_shouldChangeSelectedRange = shouldChangeSelectedRange; } + void setUiGeneratedSelectionChange(bool uiGenerated) { m_uiGeneratedSelectionChange = uiGenerated; } +#if ENABLE(WEB_AUTOFILL) + WebAutoFill* getAutoFill(); +#endif +private: + Page* m_page; + bool m_shouldChangeSelectedRange; + bool m_uiGeneratedSelectionChange; +#if ENABLE(WEB_AUTOFILL) + OwnPtr<WebAutoFill> m_autoFill; +#endif +}; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/FileSystemClient.h b/Source/WebKit/android/WebCoreSupport/FileSystemClient.h new file mode 100644 index 0000000..5bde18a --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/FileSystemClient.h @@ -0,0 +1,41 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FILESYSTEM_CLIENT_H +#define FILESYSTEM_CLIENT_H + +#include "PlatformString.h" + +using namespace WebCore; + +namespace android { + +class FileSystemClient { +public: + virtual ~FileSystemClient() { } + virtual String resolveFilePathForContentUri(const String&) = 0; +}; +} +#endif diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp new file mode 100644 index 0000000..946a4a7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp @@ -0,0 +1,1351 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebCore" + +#include "config.h" +#include "FrameLoaderClientAndroid.h" + +#include "BackForwardList.h" +#include "CachedFrame.h" +#include "CachedFramePlatformDataAndroid.h" +#include "Chrome.h" +#include "ChromeClientAndroid.h" +#include "DOMImplementation.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "EditorClientAndroid.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameNetworkingContextAndroid.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLPlugInElement.h" +#include "HistoryItem.h" +#include "IconDatabase.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "PackageNotifier.h" +#include "Page.h" +#include "PlatformBridge.h" +#include "PlatformGraphicsContext.h" +#include "PlatformString.h" +#include "PluginDatabase.h" +#include "PluginView.h" +#include "PluginViewBase.h" +#include "ProgressTracker.h" +#include "RenderPart.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceHandleInternal.h" +#include "SelectionController.h" +#include "Settings.h" +#include "SkCanvas.h" +#include "SkRect.h" +#include "TextEncoding.h" +#include "WebCoreFrameBridge.h" +#include "WebCoreResourceLoader.h" +#include "WebHistory.h" +#include "WebIconDatabase.h" +#include "WebFrameView.h" +#include "WebViewClientError.h" +#include "WebViewCore.h" +#include "autofill/WebAutoFill.h" +#include "android_graphics.h" + +#include <utils/AssetManager.h> +#include <wtf/text/CString.h> + +extern android::AssetManager* globalAssetManager(); + +namespace android { + +static const int EXTRA_LAYOUT_DELAY = 1000; + +FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe) + : m_frame(NULL) + , m_webFrame(webframe) + , m_manualLoader(NULL) + , m_hasSentResponseToPlugin(false) + , m_onDemandPluginsEnabled(false) { + Retain(m_webFrame); +} + +FrameLoaderClientAndroid* FrameLoaderClientAndroid::get(const WebCore::Frame* frame) +{ + return static_cast<FrameLoaderClientAndroid*> (frame->loader()->client()); +} + +void FrameLoaderClientAndroid::frameLoaderDestroyed() { + registerForIconNotification(false); + m_frame = 0; + Release(m_webFrame); + delete this; +} + +bool FrameLoaderClientAndroid::hasWebView() const { + // FIXME, + // there is one web view per page, or top frame. + // as android's view is created from Java side, it is always there. + return true; +} + +void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) { + m_onDemandPluginsEnabled = false; + // don't use representation + verifiedOk(); +} + +void FrameLoaderClientAndroid::forceLayout() { + ASSERT(m_frame); + m_frame->view()->forceLayout(); + // FIXME, should we adjust view size here? + m_frame->view()->adjustViewSize(); +} + +void FrameLoaderClientAndroid::forceLayoutForNonHTML() { + notImplemented(); +} + +void FrameLoaderClientAndroid::setCopiesOnScroll() { + // this is a hint about whether we need to force redraws, or can + // just copy the scrolled content. Since we always force a redraw + // anyways, we can ignore this call. + verifiedOk(); +} + +void FrameLoaderClientAndroid::detachedFromParent2() { + // FIXME, ready to detach frame from view +} + +void FrameLoaderClientAndroid::detachedFromParent3() { + // FIXME, ready to release view + notImplemented(); +} + +// This function is responsible for associating the "id" with a given +// subresource load. The following functions that accept an "id" are +// called for each subresource, so they should not be dispatched to the m_frame. +void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id, + DocumentLoader*, const ResourceRequest&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id, + ResourceRequest&, const ResourceResponse&) { + lowPriority_notImplemented(); +} + +bool FrameLoaderClientAndroid::shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) +{ + notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, + unsigned long id, const AuthenticationChallenge&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*, + unsigned long id, const AuthenticationChallenge&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*, + unsigned long id, const ResourceResponse&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*, + unsigned long id, int lengthReceived) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*, + unsigned long id) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader, + unsigned long id, const ResourceError&) { + lowPriority_notImplemented(); +} + +bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, + const ResourceRequest&, const ResourceResponse&, int length) { + notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() { +} + +void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() { + ASSERT(m_frame); + // Tell the load it was a redirect. + m_webFrame->loadStarted(m_frame); +} + +void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&, + double interval, double fireDate) { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidPushStateWithinPage() +{ + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReplaceStateWithinPage() +{ + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidPopStateWithinPage() +{ + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillClose() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveIcon() { + ASSERT(m_frame); + if (m_frame->tree() && m_frame->tree()->parent()) + return; + WTF::String url(m_frame->loader()->url().string()); + // Try to obtain the icon image. + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL( + url, WebCore::IntSize(16, 16)); + // If the request fails, try the original request url. + if (!icon) { + DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); + KURL originalURL = docLoader->originalRequest().url(); + icon = WebCore::iconDatabase()->iconForPageURL( + originalURL, WebCore::IntSize(16, 16)); + } + // There is a bug in webkit where cancelling an icon load is treated as a + // failure. When this is fixed, we can ASSERT again that we have an icon. + if (icon) { + LOGV("Received icon (%p) for %s", icon, + url.utf8().data()); + m_webFrame->didReceiveIcon(icon); + } else { + LOGV("Icon data for %s unavailable, registering for notification...", + url.utf8().data()); + registerForIconNotification(); + } +} + +void FrameLoaderClientAndroid::dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) { + ASSERT(m_frame); + // Do not report sub frame touch icons + if (m_frame->tree() && m_frame->tree()->parent()) + return; + m_webFrame->didReceiveTouchIconURL(url, precomposed); +} + +void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const String& title) { + ASSERT(m_frame); + // Used to check for FrameLoadTypeStandard but we only want to send the title for + // the top frame and not sub-frames. + if (!m_frame->tree() || !m_frame->tree()->parent()) { + m_webFrame->setTitle(title); + } +} + +void FrameLoaderClientAndroid::dispatchDidCommitLoad() { +#if ENABLE(WEB_AUTOFILL) + if (m_frame == m_frame->page()->mainFrame()) { + EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(m_frame->page()->editorClient()); + WebAutoFill* autoFill = editorC->getAutoFill(); + autoFill->reset(); + } +#endif + verifiedOk(); +} + +static void loadDataIntoFrame(Frame* frame, KURL baseUrl, const String& url, + const String& data) { + if (baseUrl.isEmpty()) { + baseUrl = blankURL(); + } + ResourceRequest request(baseUrl); + CString cstr = data.utf8(); + RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length()); + SubstituteData subData(buf, String("text/html"), String("utf-8"), + KURL(KURL(), url)); + frame->loader()->load(request, subData, false); +} + +void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) { + ASSERT(m_frame); + // Ignore ErrorInterrupted since it is due to a policy interruption. This + // is caused by a decision to download the main resource rather than + // display it. + if (error.errorCode() == InternalErrorInterrupted + || error.errorCode() == InternalErrorCancelled) { + // If we decided to download the main resource or if the user cancelled + // it, make sure we report that the load is done. + didFinishLoad(); + return; + } + + AssetManager* am = globalAssetManager(); + + // Check to see if the error code was not generated internally + WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain; + if ((error.errorCode() == ErrorFile || + error.errorCode() == ErrorFileNotFound) && + (!error.localizedDescription().isEmpty())) { + id = WebCore::PlatformBridge::LoadError; + } + String filename = m_webFrame->getRawResourceFilename(id); + if (filename.isEmpty()) + return; + + // Grab the error page from the asset manager + Asset* a = am->openNonAsset( + filename.utf8().data(), Asset::ACCESS_BUFFER); + if (!a) + return; + + // Take the failing url and encode html entities so javascript urls are not + // executed. + CString failingUrl = error.failingURL().utf8(); + WTF::Vector<char> url; + int len = failingUrl.length(); + const char* data = failingUrl.data(); + for (int i = 0; i < len; i++) { + char c = data[i]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9')) + url.append(c); + else { + char buf[16]; + int res = sprintf(buf, "&#%d;", c); + buf[res] = 0; + url.append(buf, res); + } + } + + // Replace all occurances of %s with the failing url. + String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength()); + s = s.replace("%s", String(url.data(), url.size())); + + // Replace all occurances of %e with the error text + s = s.replace("%e", error.localizedDescription()); + + // Create the request and the substitute data and tell the FrameLoader to + // load with the replacement data. + // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for + // invalidate URL string. + loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s); + + // Delete the asset. + delete a; + + // Report that the load is finished, since it failed. + didFinishLoad(); +} + +void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) { + // called when page is completed with error + didFinishLoad(); +} + +void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() { + // called when finishedParsing + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFinishLoad() { + didFinishLoad(); +} + +void FrameLoaderClientAndroid::dispatchDidFirstLayout() { + ASSERT(m_frame); + // set EXTRA_LAYOUT_DELAY if the loader is not completed yet + if (!m_frame->loader()->isComplete()) + m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); + // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout + // so that about:blank will update the screen. + if (!m_frame->tree()->parent()) { + // Only need to notify Java side for the top frame + WebViewCore::getWebViewCore(m_frame->view())->didFirstLayout(); + } +} + +void FrameLoaderClientAndroid::dispatchDidFirstVisuallyNonEmptyLayout() +{ + notImplemented(); +} + +Frame* FrameLoaderClientAndroid::dispatchCreatePage(const NavigationAction&) { + ASSERT(m_frame); +#ifdef ANDROID_MULTIPLE_WINDOWS + if (m_frame->settings() && m_frame->settings()->supportMultipleWindows()) + // Always a user gesture since window.open maps to + // ChromeClientAndroid::createWindow + return m_webFrame->createWindow(false, true); + else +#endif + // If the client doesn't support multiple windows, just replace the + // current frame's contents. + return m_frame; +} + +void FrameLoaderClientAndroid::dispatchShow() { + ASSERT(m_frame); + m_frame->view()->invalidate(); +} + + +static bool TreatAsAttachment(const String& content_disposition) { + // Some broken sites just send + // Content-Disposition: ; filename="file" + // screen those out here. + if (content_disposition.startsWith(";")) + return false; + + if (content_disposition.startsWith("inline", false)) + return false; + + // Some broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + if (content_disposition.startsWith("filename", false)) + return false; + + // Also in use is Content-Disposition: name="file" + if (content_disposition.startsWith("name", false)) + return false; + + // We have a content-disposition of "attachment" or unknown. + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + return true; +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForMIMEType(FramePolicyFunction func, + const String& MIMEType, const ResourceRequest& request) { + ASSERT(m_frame); + ASSERT(func); + if (!func) + return; + + PolicyChecker* policy = m_frame->loader()->policyChecker(); + + if (request.isNull()) { + (policy->*func)(PolicyIgnore); + return; + } + // Default to Use (display internally). + PolicyAction action = PolicyUse; + // Check if we should Download instead. + const ResourceResponse& response = m_frame->loader()->activeDocumentLoader()->response(); + const String& content_disposition = response.httpHeaderField("Content-Disposition"); + if (!content_disposition.isEmpty() && + TreatAsAttachment(content_disposition)) { + // Server wants to override our normal policy. + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + (policy->*func)(action); + return; + } + + // Ask if it can be handled internally. + if (!canShowMIMEType(MIMEType)) { + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + (policy->*func)(action); + return; + } + // A status code of 204 indicates no content change. Ignore the result. + WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); + if (docLoader->response().httpStatusCode() == 204) + action = PolicyIgnore; + (policy->*func)(action); +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func, + const NavigationAction& action, const ResourceRequest& request, + PassRefPtr<FormState> formState, const String& frameName) { + ASSERT(m_frame); + ASSERT(func); + if (!func) + return; + + if (request.isNull()) { + (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); + return; + } + + if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted) + m_frame->loader()->resetMultipleFormSubmissionProtection(); + + // If we get to this point it means that a link has a target that was not + // found by the frame tree. Instead of creating a new frame, return the + // current frame in dispatchCreatePage. + if (canHandleRequest(request)) + (m_frame->loader()->policyChecker()->*func)(PolicyUse); + else + (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); +} + +void FrameLoaderClientAndroid::cancelPolicyCheck() { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func, + const NavigationAction& action, const ResourceRequest& request, + PassRefPtr<FormState> formState) { + ASSERT(m_frame); + ASSERT(func); + if (!func) + return; + if (request.isNull()) { + (m_frame->loader()->policyChecker()->*func)(PolicyIgnore); + return; + } + + // Reset multiple form submission protection. If this is a resubmission, we check with the + // user and reset the protection if they choose to resubmit the form (see WebCoreFrameBridge.cpp) + if (action.type() == NavigationTypeFormSubmitted) + m_frame->loader()->resetMultipleFormSubmissionProtection(); + + if (action.type() == NavigationTypeFormResubmitted) { + m_webFrame->decidePolicyForFormResubmission(func); + return; + } else + (m_frame->loader()->policyChecker()->*func)(PolicyUse); +} + +void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) { + ASSERT(m_frame); + ASSERT(func); + (m_frame->loader()->policyChecker()->*func)(PolicyUse); +} + +void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form) +{ + if (m_webFrame->shouldSaveFormData()) + m_webFrame->saveFormData(form); +} + +void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) { + ASSERT(m_frame); + if (m_manualLoader) { + m_manualLoader->didFail(error); + m_manualLoader = NULL; + m_hasSentResponseToPlugin = false; + } else { + if (!error.isNull() && error.errorCode() >= InternalErrorLast && error.errorCode() != ERROR_OK) + m_webFrame->reportError(error.errorCode(), + error.localizedDescription(), error.failingURL()); + } +} + +// This function is called right before the progress is updated. +void FrameLoaderClientAndroid::willChangeEstimatedProgress() { + verifiedOk(); +} + +// This function is called after the progress has been updated. The bad part +// about this is that when a page is completed, this function is called after +// the progress has been reset to 0. +void FrameLoaderClientAndroid::didChangeEstimatedProgress() { + verifiedOk(); +} + +// This will give us the initial estimate when the page first starts to load. +void FrameLoaderClientAndroid::postProgressStartedNotification() { + ASSERT(m_frame); + if (m_frame->page()) + m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); +} + +// This will give us any updated progress including the final progress. +void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() { + ASSERT(m_frame); + if (m_frame->page()) + m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress()); +} + +// This is just a notification that the progress has finished. Don't call +// setProgress(1) because postProgressEstimateChangedNotification will do so. +void FrameLoaderClientAndroid::postProgressFinishedNotification() { + WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view()); + if (!m_frame->tree()->parent()) { + // only need to notify Java for the top frame + core->notifyProgressFinished(); + } + // notify plugins that the frame has loaded + core->notifyPluginsOnFrameLoad(m_frame); +} + +void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) { + // this is only interesting once we provide an external API for the DOM + notImplemented(); +} + +void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) { + notImplemented(); +} + +void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) { + verifiedOk(); +} + +void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) { + verifiedOk(); +} + +void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) { + // Telling the frame we received some data and passing 0 as the data is our + // way to get work done that is normally done when the first bit of data is + // received, even for the case of a document with no data (like about:blank) + if (!m_manualLoader) { + committedLoad(docLoader, 0, 0); + return; + } + + m_manualLoader->didFinishLoading(); + m_manualLoader = NULL; + m_hasSentResponseToPlugin = false; +} + +void FrameLoaderClientAndroid::updateGlobalHistory() { + ASSERT(m_frame); + + DocumentLoader* docLoader = m_frame->loader()->documentLoader(); + ASSERT(docLoader); + + // Code copied from FrameLoader.cpp:createHistoryItem + // Only add this URL to the database if it is a valid page + if (docLoader->unreachableURL().isEmpty() + && docLoader->response().httpStatusCode() < 400) { + m_webFrame->updateVisitedHistory(docLoader->urlForHistory(), false); + if (!docLoader->serverRedirectSourceForHistory().isNull()) + m_webFrame->updateVisitedHistory(KURL(ParsedURLString, docLoader->serverRedirectDestinationForHistory()), false); + } +} + +void FrameLoaderClientAndroid::updateGlobalHistoryRedirectLinks() { + // Note, do we need to do anything where there is no HistoryItem? If we call + // updateGlobalHistory(), we will add bunch of "data:xxx" urls for gmail.com + // which is not what we want. Opt to do nothing now. +} + +bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const { + // hmmm, seems like we might do a more thoughtful check + ASSERT(m_frame); + return item != NULL; +} + +void FrameLoaderClientAndroid::didDisplayInsecureContent() +{ + notImplemented(); +} + +void FrameLoaderClientAndroid::didRunInsecureContent(SecurityOrigin*) +{ + notImplemented(); +} + +void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) { + if (!m_manualLoader) + loader->commitData(data, length); + + // commit data may have created a manual plugin loader + if (m_manualLoader) { + if (!m_hasSentResponseToPlugin) { + m_manualLoader->didReceiveResponse(loader->response()); + // Failure could cause the main document to have an error causing + // the manual loader to be reset. + if (!m_manualLoader) + return; + m_hasSentResponseToPlugin = true; + } + m_manualLoader->didReceiveData(data, length); + } +} + +ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorCancelled, request.url(), String()); +} + +ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorCannotShowUrl, request.url(), String()); +} + +ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorInterrupted, request.url(), String()); +} + +ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) { + return ResourceError(String(), InternalErrorCannotShowMimeType, request.url(), String()); +} + +ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) { + return ResourceError(String(), InternalErrorFileDoesNotExist, request.url(), String()); +} + +ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) { + return ResourceError(String(), InternalErrorPluginWillHandleLoadError, request.url(), String()); +} + +bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) { + notImplemented(); + return false; +} + +bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const { + ASSERT(m_frame); + // Don't allow hijacking of intrapage navigation + if (WebCore::equalIgnoringFragmentIdentifier(request.url(), m_frame->loader()->url())) + return true; + + // Don't allow hijacking of iframe urls that are http or https + if (request.url().protocol().startsWith("http", false) && + m_frame->tree() && m_frame->tree()->parent()) + return true; + + return m_webFrame->canHandleRequest(request); +} + +bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const { + // FIXME: This looks like it has to do with whether or not a type can be + // shown "internally" (i.e. inside the browser) regardless of whether + // or not the browser is doing the rendering, e.g. a full page plugin. + if (MIMETypeRegistry::isSupportedImageResourceMIMEType(mimeType) || + MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) || + MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || + (m_frame && m_frame->settings() + && m_frame->settings()->arePluginsEnabled() + && PluginDatabase::installedPlugins()->isMIMETypeRegistered( + mimeType)) || + (DOMImplementation::isTextMIMEType(mimeType) && + !mimeType.startsWith("text/vnd")) || + DOMImplementation::isXMLMIMEType(mimeType)) + return true; + return false; +} + +bool FrameLoaderClientAndroid::canShowMIMETypeAsHTML(const String& mimeType) const { + return false; +} + +bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const { + // don't use representation + verifiedOk(); + return false; +} + +String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const { + // FIXME, copy from Apple's port + String mimetype("x-apple-web-kit/"); + mimetype.append(URLScheme.lower()); + return mimetype; +} + +void FrameLoaderClientAndroid::frameLoadCompleted() { + // copied from Apple port, without this back with sub-frame will trigger ASSERT + ASSERT(m_frame); +} + +void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) { + ASSERT(m_frame); + ASSERT(item); + // store the current scale (only) for the top frame + if (!m_frame->tree()->parent()) { + // We should have added a bridge when the child item was added to its + // parent. + AndroidWebHistoryBridge* bridge = item->bridge(); + ASSERT(bridge); + WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); + bridge->setScale(webViewCore->scale()); + bridge->setTextWrapScale(webViewCore->textWrapScale()); + } + + WebCore::notifyHistoryItemChanged(item); +} + +void FrameLoaderClientAndroid::restoreViewState() { + WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); + HistoryItem* item = m_frame->loader()->history()->currentItem(); + AndroidWebHistoryBridge* bridge = item->bridge(); + // restore the scale (only) for the top frame + if (!m_frame->tree()->parent()) { + webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale()); + } +} + +void FrameLoaderClientAndroid::dispatchDidAddBackForwardItem(HistoryItem* item) const { + ASSERT(m_frame); + m_webFrame->addHistoryItem(item); +} + +void FrameLoaderClientAndroid::dispatchDidRemoveBackForwardItem(HistoryItem* item) const { + ASSERT(m_frame); + m_webFrame->removeHistoryItem(0); +} + +void FrameLoaderClientAndroid::dispatchDidChangeBackForwardIndex() const { + ASSERT(m_frame); + BackForwardList* list = m_frame->page()->backForwardList(); + ASSERT(list); + m_webFrame->updateHistoryIndex(list->backListCount()); +} + +void FrameLoaderClientAndroid::provisionalLoadStarted() { + ASSERT(m_frame); + m_webFrame->loadStarted(m_frame); +} + +void FrameLoaderClientAndroid::didFinishLoad() { + ASSERT(m_frame); + m_frame->document()->setExtraLayoutDelay(0); + m_webFrame->didFinishLoad(m_frame); +} + +void FrameLoaderClientAndroid::prepareForDataSourceReplacement() { + verifiedOk(); +} + +PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader( + const ResourceRequest& request, const SubstituteData& data) { + RefPtr<DocumentLoader> loader = DocumentLoader::create(request, data); + return loader.release(); +} + +void FrameLoaderClientAndroid::setTitle(const String& title, const KURL& url) { + // Not needed. dispatchDidReceiveTitle is called immediately after this. + // url is used to update the Apple port history items. + verifiedOk(); +} + +String FrameLoaderClientAndroid::userAgent(const KURL& u) { + return m_webFrame->userAgentForURL(&u); +} + +void FrameLoaderClientAndroid::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame) { + CachedFramePlatformDataAndroid* platformData = new CachedFramePlatformDataAndroid(m_frame->settings()); + cachedFrame->setCachedFramePlatformData(platformData); +} + +void FrameLoaderClientAndroid::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame) { + CachedFramePlatformDataAndroid* platformData = reinterpret_cast<CachedFramePlatformDataAndroid*>(cachedFrame->cachedFramePlatformData()); +#ifdef ANDROID_META_SUPPORT + platformData->restoreMetadata(m_frame->settings()); +#endif + m_webFrame->transitionToCommitted(m_frame); +} + +void FrameLoaderClientAndroid::transitionToCommittedForNewPage() { + ASSERT(m_frame); + +#ifdef ANDROID_META_SUPPORT + // reset metadata settings for the main frame as they are not preserved cross page + if (m_frame == m_frame->page()->mainFrame() && m_frame->settings()) + m_frame->settings()->resetMetadataSettings(); +#endif + + // Save the old WebViewCore before creating a new FrameView. There is one + // WebViewCore per page. Each frame, including the main frame and sub frame, + // has a 1:1 FrameView and WebFrameView. + WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view()); + Retain(webViewCore); + + // Save the old WebFrameView's bounds and apply them to the new WebFrameView + WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (m_frame->view()->platformWidget()); + IntRect bounds = oldWebFrameView->getBounds(); + IntRect visBounds = oldWebFrameView->getVisibleBounds(); + IntRect windowBounds = oldWebFrameView->getWindowBounds(); + WebCore::FrameView* oldFrameView = oldWebFrameView->view(); + const float oldZoomFactor = oldFrameView->frame()->textZoomFactor(); + m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(), + oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout()); + if (oldZoomFactor != 1.0f && oldZoomFactor != m_frame->textZoomFactor()) { + m_frame->setTextZoomFactor(oldZoomFactor); + } + + // Create a new WebFrameView for the new FrameView + WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore); + newFrameView->setLocation(bounds.x(), bounds.y()); + newFrameView->setSize(bounds.width(), bounds.height()); + newFrameView->setVisibleSize(visBounds.width(), visBounds.height()); + newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height()); + // newFrameView attaches itself to FrameView which Retains the reference, so + // call Release for newFrameView + Release(newFrameView); + // WebFrameView Retains webViewCore, so call Release for webViewCore + Release(webViewCore); + + m_webFrame->transitionToCommitted(m_frame); +} + +void FrameLoaderClientAndroid::dispatchDidBecomeFrameset(bool) +{ +} + +bool FrameLoaderClientAndroid::canCachePage() const { + return true; +} + +void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&, + const ResourceRequest&, const ResourceResponse&) { + // Get the C++ side of the load listener and tell it to handle the download + handle->getInternal()->m_loader->downloadFile(); +} + +WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name, + HTMLFrameOwnerElement* ownerElement, const String& referrer, + bool allowsScrolling, int marginWidth, int marginHeight) +{ + Frame* parent = ownerElement->document()->frame(); + FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(m_webFrame); + RefPtr<Frame> pFrame = Frame::create(parent->page(), ownerElement, loaderC); + Frame* newFrame = pFrame.get(); + loaderC->setFrame(newFrame); + // Append the subframe to the parent and set the name of the subframe. The name must be set after + // appending the child so that the name becomes unique. + parent->tree()->appendChild(newFrame); + newFrame->tree()->setName(name); + // Create a new FrameView and WebFrameView for the child frame to draw into. + RefPtr<FrameView> frameView = FrameView::create(newFrame); + WebFrameView* webFrameView = new WebFrameView(frameView.get(), + WebViewCore::getWebViewCore(parent->view())); + // frameView Retains webFrameView, so call Release for webFrameView + Release(webFrameView); + // Attach the frameView to the newFrame. + newFrame->setView(frameView); + newFrame->init(); + newFrame->selection()->setFocused(true); + LOGV("::WebCore:: createSubFrame returning %p", newFrame); + + // The creation of the frame may have run arbitrary JavaScript that removed it from the page already. + if (!pFrame->page()) + return 0; + + parent->loader()->loadURLIntoChildFrame(url, referrer, pFrame.get()); + + // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame. + if (!pFrame->tree()->parent()) + return NULL; + + return pFrame.release(); +} + +// YouTube flash url path starts with /v/ +static const char slash_v_slash[] = { '/', 'v', '/' }; +static const char slash_e_slash[] = { '/', 'e', '/' }; + +static bool isValidYouTubeVideo(const String& path) +{ + if (!charactersAreAllASCII(path.characters(), path.length())) + return false; + unsigned int len = path.length(); + if (len <= sizeof(slash_v_slash)) // check for more than just /v/ + return false; + CString str = path.lower().utf8(); + const char* data = str.data(); + // Youtube flash url can start with /v/ or /e/ + if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0) + if (memcmp(data, slash_e_slash, sizeof(slash_e_slash)) != 0) + return false; + // Start after /v/ + for (unsigned int i = sizeof(slash_v_slash); i < len; i++) { + char c = data[i]; + // Check for alpha-numeric characters only. + if (WTF::isASCIIAlphanumeric(c) || c == '_' || c == '-') + continue; + // The url can have more parameters such as &hl=en after the video id. + // Once we start seeing extra parameters we can return true. + return c == '&' && i > sizeof(slash_v_slash); + } + return true; +} + +static bool isYouTubeUrl(const KURL& url, const String& mimeType) +{ + String host = url.host(); + bool youtube = host.endsWith("youtube.com") + || host.endsWith("youtube-nocookie.com"); + return youtube && isValidYouTubeVideo(url.path()) + && equalIgnoringCase(mimeType, "application/x-shockwave-flash"); +} + +static bool isYouTubeInstalled() { + return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube"); +} + +// Use PluginViewBase rather than an Android specific sub class as we do not require any +// Android specific functionality; this just renders a placeholder which will later +// activate the real plugin. +class PluginToggleWidget : public PluginViewBase { +public: + PluginToggleWidget(Frame* parent, const IntSize& size, + HTMLPlugInElement* elem, const KURL& url, + const WTF::Vector<String>& paramNames, + const WTF::Vector<String>& paramValues, const String& mimeType, + bool loadManually) + : PluginViewBase(0) + , m_parent(parent) + , m_size(size) + , m_element(elem) + , m_url(url) + , m_paramNames(paramNames) + , m_paramValues(paramValues) + , m_mimeType(mimeType) + , m_loadManually(loadManually) + { + resize(size); + } + + virtual void paint(GraphicsContext* ctx, const IntRect& rect) + { + // Most of this code is copied from PluginView::paintMissingPluginIcon + // with slight modification. + + static RefPtr<Image> image; + if (!image) { + image = Image::loadPlatformResource("togglePlugin"); + } + + IntRect imageRect(x(), y(), image->width(), image->height()); + + int xOffset = (width() - imageRect.width()) >> 1; + int yOffset = (height() - imageRect.height()) >> 1; + + imageRect.move(xOffset, yOffset); + + if (!rect.intersects(imageRect)) + return; + + // FIXME: We need to clip similarly to paintMissingPluginIcon but it is + // way screwed up right now. It has something to do with how we tell + // webkit the scroll position and it causes the placeholder to get + // clipped very badly. http://b/issue?id=2533303 + + ctx->save(); + ctx->clip(frameRect()); + + ctx->setFillColor(Color::white, ColorSpaceDeviceRGB); + ctx->fillRect(frameRect()); + if (frameRect().contains(imageRect)) { + // Leave a 2 pixel padding. + const int pixelWidth = 2; + IntRect innerRect = frameRect(); + innerRect.inflate(-pixelWidth); + // Draw a 2 pixel light gray border. + ctx->setStrokeColor(Color::lightGray, ColorSpaceDeviceRGB); + ctx->strokeRect(innerRect, pixelWidth); + } + + // Draw the image in the center + ctx->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect.location()); + ctx->restore(); + } + + virtual void handleEvent(Event* event) + { + if (event->type() != eventNames().clickEvent) + return; + + Frame* frame = m_parent->page()->mainFrame(); + while (frame) { + RenderView* view = frame->contentRenderer(); + const HashSet<RenderWidget*> widgets = view->widgets(); + HashSet<RenderWidget*>::const_iterator it = widgets.begin(); + HashSet<RenderWidget*>::const_iterator end = widgets.end(); + for (; it != end; ++it) { + Widget* widget = (*it)->widget(); + // PluginWidget is used only with PluginToggleWidget + if (widget && widget->isPluginViewBase()) { + PluginToggleWidget* ptw = + static_cast<PluginToggleWidget*>(widget); + ptw->swapPlugin(*it); + } + } + frame = frame->tree()->traverseNext(); + } + } + + void swapPlugin(RenderWidget* renderer) { + typedef FrameLoaderClientAndroid FLCA; + FLCA* client = static_cast<FLCA*>(m_parent->loader()->client()); + client->enableOnDemandPlugins(); + WTF::PassRefPtr<PluginView> prpWidget = + PluginView::create(m_parent.get(), + m_size, + m_element, + m_url, + m_paramNames, + m_paramValues, + m_mimeType, + m_loadManually); + RefPtr<Widget> myProtector(this); + prpWidget->focusPluginElement(); + renderer->setWidget(prpWidget); + } + +private: + void invalidateRect(const IntRect& rect) { } + + RefPtr<Frame> m_parent; + IntSize m_size; + HTMLPlugInElement* m_element; + KURL m_url; + WTF::Vector<String> m_paramNames; + WTF::Vector<String> m_paramValues; + String m_mimeType; + bool m_loadManually; +}; + +WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createPlugin( + const IntSize& size, + HTMLPlugInElement* element, + const KURL& url, + const WTF::Vector<String>& names, + const WTF::Vector<String>& values, + const String& mimeType, + bool loadManually) { + WTF::PassRefPtr<PluginView> prpWidget = 0; +#ifdef ANDROID_PLUGINS + // This is copied from PluginView.cpp. We need to determine if a plugin + // will be found before doing some of the work in PluginView. + String mimeTypeCopy = mimeType; + PluginPackage* plugin = + PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); + if (!plugin && PluginDatabase::installedPlugins()->refresh()) { + mimeTypeCopy = mimeType; + plugin = PluginDatabase::installedPlugins()->findPlugin(url, + mimeTypeCopy); + } + Settings* settings = m_frame->settings(); + // Do the placeholder if plugins are on-demand and there is a plugin for the + // given mime type. + if (settings && settings->arePluginsOnDemand() && plugin && + !m_onDemandPluginsEnabled) { + return adoptRef(new PluginToggleWidget(m_frame, size, element, url, + names, values, mimeType, loadManually)); + } + prpWidget = PluginView::create(m_frame, + size, + element, + url, + names, + values, + mimeType, + loadManually); + // Return the plugin if it was loaded successfully. Otherwise, fallback to + // the youtube placeholder if possible. No need to check prpWidget as + // PluginView::create will create a PluginView for missing plugins. + // Note: this check really only checks if the plugin was found and not if + // the plugin was loaded. + if (prpWidget->status() == PluginStatusLoadedSuccessfully) + return prpWidget; +#endif + // Create an iframe for youtube urls. + if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) { + WTF::RefPtr<Frame> frame = createFrame(blankURL(), String(), element, + String(), false, 0, 0); + if (frame) { + // grab everything after /v/ + String videoId = url.path().substring(sizeof(slash_v_slash)); + // Extract just the video id + unsigned videoIdEnd = 0; + for (; videoIdEnd < videoId.length(); videoIdEnd++) { + if (videoId[videoIdEnd] == '&') { + videoId = videoId.left(videoIdEnd); + break; + } + } + AssetManager* am = globalAssetManager(); + Asset* a = am->open("webkit/youtube.html", + Asset::ACCESS_BUFFER); + if (!a) + return NULL; + String s = String((const char*)a->getBuffer(false), a->getLength()); + s = s.replace("VIDEO_ID", videoId); + delete a; + loadDataIntoFrame(frame.get(), + KURL(ParsedURLString, "file:///android_asset/webkit/"), String(), s); + // Transfer ownership to a local refptr. + WTF::RefPtr<Widget> widget(frame->view()); + return widget.release(); + } + } + return prpWidget; +} + +void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) { + // Do not redirect data if the Widget is our plugin placeholder. + if (pluginWidget->isPluginView()) { + m_manualLoader = static_cast<PluginView*>(pluginWidget); + } +} + +WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, HTMLAppletElement*, + const KURL& baseURL, const WTF::Vector<String>& paramNames, + const WTF::Vector<String>& paramValues) { + // don't support widget yet + notImplemented(); + return 0; +} + +void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument(WebCore::Page*) +{ + ASSERT(m_frame); + // m_webFrame points to the WebFrame for the page that our frame previously + // belonged to. If the frame now belongs to a new page, we need to update + // m_webFrame to point to the WebFrame for the new page. + Page* newPage = m_frame->page(); + if (newPage != m_webFrame->page()) { + ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client()); + Release(m_webFrame); + m_webFrame = chromeClient->webFrame(); + Retain(m_webFrame); + } +} + +void FrameLoaderClientAndroid::transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*) +{ + notImplemented(); +} + +// This function is used by the <OBJECT> element to determine the type of +// the contents and work out if it can render it. +ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, + const String& mimeType) { + return FrameLoader::defaultObjectContentType(url, mimeType); +} + +// This function allows the application to set the correct CSS media +// style. Android could use it to set the media style 'handheld'. Safari +// may use it to set the media style to 'print' when the user wants to print +// a particular web page. +String FrameLoaderClientAndroid::overrideMediaType() const { + lowPriority_notImplemented(); + return String(); +} + +// This function is used to re-attach Javascript<->native code classes. +void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) +{ + if (world != mainThreadNormalWorld()) + return; + + ASSERT(m_frame); + LOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n", + m_frame, m_frame->loader()->url().string().ascii().data()); + m_webFrame->windowObjectCleared(m_frame); +} + +void FrameLoaderClientAndroid::documentElementAvailable() { +} + +// functions new to Jun-07 tip of tree merge: +ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) { + return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String()); +} + +// functions new to Nov-07 tip of tree merge: +void FrameLoaderClientAndroid::didPerformFirstNavigation() const { + // This seems to be just a notification that the UI can listen to, to + // know if the user has performed first navigation action. + // It is called from + // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) + // "Navigation" here means a transition from one page to another that + // ends up in the back/forward list. +} + +void FrameLoaderClientAndroid::registerForIconNotification(bool listen) { + if (listen) + WebIconDatabase::RegisterForIconNotification(this); + else + WebIconDatabase::UnregisterForIconNotification(this); +} + +// This is the WebIconDatabaseClient method for receiving a notification when we +// get the icon for the page. +void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) { + // This call must happen before dispatchDidReceiveIcon since that method + // may register for icon notifications again since the icon data may have + // to be read from disk. + registerForIconNotification(false); + KURL u(ParsedURLString, pageUrl); + if (equalIgnoringFragmentIdentifier(u, m_frame->loader()->url())) { + dispatchDidReceiveIcon(); + } +} + +void FrameLoaderClientAndroid::dispatchDidChangeIcons() { + notImplemented(); +} + +PassRefPtr<FrameNetworkingContext> FrameLoaderClientAndroid::createNetworkingContext() +{ + return FrameNetworkingContextAndroid::create(getFrame()); +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h new file mode 100644 index 0000000..25561a8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h @@ -0,0 +1,271 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FrameLoaderClientAndroid_h +#define FrameLoaderClientAndroid_h + +#include "CacheBuilder.h" +#include "FrameLoaderClient.h" +#include "ResourceResponse.h" +#include "WebIconDatabase.h" +#include <wtf/Forward.h> + +namespace WebCore { +class PluginManualLoader; +} + +using namespace WebCore; + +namespace android { + class WebFrame; + + class FrameLoaderClientAndroid : public FrameLoaderClient, + WebIconDatabaseClient { + public: + FrameLoaderClientAndroid(WebFrame* webframe); + + Frame* getFrame() { return m_frame; } + static FrameLoaderClientAndroid* get(const Frame* frame); + + void setFrame(Frame* frame) { m_frame = frame; } + WebFrame* webFrame() const { return m_webFrame; } + + virtual void frameLoaderDestroyed(); + + virtual bool hasWebView() const; // mainly for assertions + + virtual void makeRepresentation(DocumentLoader*); + virtual void forceLayout(); + virtual void forceLayoutForNonHTML(); + + virtual void setCopiesOnScroll(); + + virtual void detachedFromParent2(); + virtual void detachedFromParent3(); + + virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); + + virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); + virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier); + virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&); + virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&); + virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&); + virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived); + virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier); + virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&); + virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length); + + virtual void dispatchDidHandleOnloadEvents(); + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(); + virtual void dispatchDidCancelClientRedirect(); + virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate); + virtual void dispatchDidChangeLocationWithinPage(); + virtual void dispatchDidPushStateWithinPage(); + virtual void dispatchDidReplaceStateWithinPage(); + virtual void dispatchDidPopStateWithinPage(); + virtual void dispatchWillClose(); + virtual void dispatchDidReceiveIcon(); + virtual void dispatchDidStartProvisionalLoad(); + virtual void dispatchDidReceiveTitle(const String& title); + virtual void dispatchDidCommitLoad(); + virtual void dispatchDidFailProvisionalLoad(const ResourceError&); + virtual void dispatchDidFailLoad(const ResourceError&); + virtual void dispatchDidFinishDocumentLoad(); + virtual void dispatchDidFinishLoad(); + virtual void dispatchDidFirstLayout(); + virtual void dispatchDidFirstVisuallyNonEmptyLayout(); + + virtual Frame* dispatchCreatePage(const NavigationAction&); + virtual void dispatchShow(); + + virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&); + virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName); + virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>); + virtual void cancelPolicyCheck(); + + virtual void dispatchUnableToImplementPolicy(const ResourceError&); + + virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>); + + virtual void dispatchDidLoadMainResource(DocumentLoader*); + virtual void revertToProvisionalState(DocumentLoader*); + virtual void setMainDocumentError(DocumentLoader*, const ResourceError&); + + virtual void willChangeEstimatedProgress(); + virtual void didChangeEstimatedProgress(); + virtual void postProgressStartedNotification(); + virtual void postProgressEstimateChangedNotification(); + virtual void postProgressFinishedNotification(); + + virtual void setMainFrameDocumentReady(bool); + + virtual void startDownload(const ResourceRequest&); + + virtual void willChangeTitle(DocumentLoader*); + virtual void didChangeTitle(DocumentLoader*); + + virtual void committedLoad(DocumentLoader*, const char*, int); + virtual void finishedLoading(DocumentLoader*); + + virtual void updateGlobalHistory(); + virtual void updateGlobalHistoryRedirectLinks(); + + virtual bool shouldGoToHistoryItem(HistoryItem*) const; + + virtual void didDisplayInsecureContent(); + virtual void didRunInsecureContent(SecurityOrigin*); + + virtual void dispatchDidAddBackForwardItem(HistoryItem*) const; + virtual void dispatchDidRemoveBackForwardItem(HistoryItem*) const; + virtual void dispatchDidChangeBackForwardIndex() const; + + virtual ResourceError cancelledError(const ResourceRequest&); + virtual ResourceError blockedError(const ResourceRequest&); + virtual ResourceError cannotShowURLError(const ResourceRequest&); + virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&); + + virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&); + virtual ResourceError fileDoesNotExistError(const ResourceResponse&); + virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&); + + virtual bool shouldFallBack(const ResourceError&); + + virtual bool canHandleRequest(const ResourceRequest&) const; + virtual bool canShowMIMEType(const String& MIMEType) const; + virtual bool canShowMIMETypeAsHTML(const String& MIMEType) const; + virtual bool representationExistsForURLScheme(const String& URLScheme) const; + virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const; + + virtual void frameLoadCompleted(); + virtual void saveViewStateToItem(HistoryItem*); + virtual void restoreViewState(); + virtual void provisionalLoadStarted(); + virtual void didFinishLoad(); + virtual void prepareForDataSourceReplacement(); + + virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&); + virtual void setTitle(const String& title, const KURL&); + + // This provides the userAgent to WebCore. It is used by WebCore to + // populate navigator.userAgent and to set the HTTP header in + // ResourceRequest objects. We also set a userAgent on WebRequestContext + // for the Chromium HTTP stack, which overrides the value on the + // ResourceRequest. + virtual String userAgent(const KURL&); + + virtual void savePlatformDataToCachedFrame(WebCore::CachedFrame*); + virtual void transitionToCommittedFromCachedFrame(WebCore::CachedFrame*); + virtual void transitionToCommittedForNewPage(); + + virtual void dispatchDidBecomeFrameset(bool isFrameSet); + + virtual bool canCachePage() const; + virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&); + + virtual WTF::PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight); + virtual void didTransferChildFrameToNewDocument(WebCore::Page*); + virtual void transferLoadingResourceFromPage(unsigned long identifier, DocumentLoader*, const ResourceRequest&, Page* oldPage); + virtual WTF::PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const WTF::Vector<String>&, const WTF::Vector<String>&, const String&, bool loadManually); + virtual void redirectDataToPlugin(Widget* pluginWidget); + + virtual WTF::PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL& baseURL, const WTF::Vector<String>& paramNames, const WTF::Vector<String>& paramValues); + + virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType); + virtual String overrideMediaType() const; + + virtual void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*); + virtual void documentElementAvailable(); + virtual void didPerformFirstNavigation() const; + +#if USE(V8) + // TODO(benm): Implement + virtual void didCreateScriptContextForFrame() { } + virtual void didDestroyScriptContextForFrame() { } + virtual void didCreateIsolatedScriptContext() { } + + virtual bool allowScriptExtension(const String& extensionName, int extensionGroup) { return false; } +#endif + + virtual void registerForIconNotification(bool listen = true); + + virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed); + + virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext(); + + // WebIconDatabaseClient api + virtual void didAddIconForPageUrl(const String& pageUrl); + + // FIXME: this doesn't really go here, but it's better than Frame + CacheBuilder& getCacheBuilder() { return m_cacheBuilder; } + + void enableOnDemandPlugins() { m_onDemandPluginsEnabled = true; } + + void dispatchDidChangeIcons(); + void dispatchWillSendSubmitEvent(HTMLFormElement*); + + virtual void didSaveToPageCache() { } + virtual void didRestoreFromPageCache() { } + private: + CacheBuilder m_cacheBuilder; + Frame* m_frame; + WebFrame* m_webFrame; + PluginManualLoader* m_manualLoader; + bool m_hasSentResponseToPlugin; + bool m_onDemandPluginsEnabled; + + enum ResourceErrors { + InternalErrorCancelled = -99, + InternalErrorCannotShowUrl, + InternalErrorInterrupted, + InternalErrorCannotShowMimeType, + InternalErrorFileDoesNotExist, + InternalErrorPluginWillHandleLoadError, + InternalErrorLast + }; + + /* XXX: These must match android.net.http.EventHandler */ + enum EventHandlerErrors { + Error = -1, + ErrorLookup = -2, + ErrorUnsupportedAuthScheme = -3, + ErrorAuth = -4, + ErrorProxyAuth = -5, + ErrorConnect = -6, + ErrorIO = -7, + ErrorTimeout = -8, + ErrorRedirectLoop = -9, + ErrorUnsupportedScheme = -10, + ErrorFailedSslHandshake = -11, + ErrorBadUrl = -12, + ErrorFile = -13, + ErrorFileNotFound = -14, + ErrorTooManyRequests = -15 + }; + friend class CacheBuilder; + }; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp new file mode 100644 index 0000000..a5fe494 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FrameNetworkingContextAndroid.h" + +#include "DocumentLoader.h" +#include "MainResourceLoader.h" +#include "Settings.h" + +using namespace WebCore; + +namespace android { + +FrameNetworkingContextAndroid::FrameNetworkingContextAndroid(WebCore::Frame* frame) + : WebCore::FrameNetworkingContext(frame) +{ +} + +MainResourceLoader* FrameNetworkingContextAndroid::mainResourceLoader() const +{ + return frame()->loader()->activeDocumentLoader()->mainResourceLoader(); +} + +FrameLoaderClient* FrameNetworkingContextAndroid::frameLoaderClient() const +{ + return frame()->loader()->client(); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h new file mode 100644 index 0000000..d0ff979 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/FrameNetworkingContextAndroid.h @@ -0,0 +1,54 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FrameNetworkingContextAndroid_h +#define FrameNetworkingContextAndroid_h + +#include "FrameNetworkingContext.h" + +namespace WebCore { +class MainResourceLoader; +class FrameLoaderClient; +} + +namespace android { + +class FrameNetworkingContextAndroid : public WebCore::FrameNetworkingContext { +public: + static PassRefPtr<FrameNetworkingContextAndroid> create(WebCore::Frame* frame) + { + return adoptRef(new FrameNetworkingContextAndroid(frame)); + } + +private: + FrameNetworkingContextAndroid(WebCore::Frame*); + + virtual WebCore::MainResourceLoader* mainResourceLoader() const; + virtual WebCore::FrameLoaderClient* frameLoaderClient() const; +}; + +} // namespace android + +#endif // FrameNetworkingContextAndroid_h diff --git a/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp new file mode 100755 index 0000000..36a9b61 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GeolocationPermissions.h" + +#include "DOMWindow.h" +#include "Frame.h" +#include "Geolocation.h" +#include "Navigator.h" +#include "SQLiteDatabase.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include "WebViewCore.h" + +#include <text/CString.h> + +using namespace WebCore; + +namespace android { + +GeolocationPermissions::PermissionsMap GeolocationPermissions::s_permanentPermissions; +GeolocationPermissions::GeolocationPermissionsVector GeolocationPermissions::s_instances; +bool GeolocationPermissions::s_alwaysDeny = false; +bool GeolocationPermissions::s_permanentPermissionsLoaded = false; +bool GeolocationPermissions::s_permanentPermissionsModified = false; +String GeolocationPermissions::s_databasePath; + +static const char* databaseName = "GeolocationPermissions.db"; + +GeolocationPermissions::GeolocationPermissions(WebViewCore* webViewCore, Frame* mainFrame) + : m_webViewCore(webViewCore) + , m_mainFrame(mainFrame) + , m_timer(this, &GeolocationPermissions::timerFired) + +{ + ASSERT(m_webViewCore); + maybeLoadPermanentPermissions(); + s_instances.append(this); +} + +GeolocationPermissions::~GeolocationPermissions() +{ + size_t index = s_instances.find(this); + s_instances.remove(index); +} + +void GeolocationPermissions::queryPermissionState(Frame* frame) +{ + ASSERT(s_permanentPermissionsLoaded); + + // We use SecurityOrigin::toString to key the map. Note that testing + // the SecurityOrigin pointer for equality is insufficient. + String originString = frame->document()->securityOrigin()->toString(); + + // If we've been told to always deny requests, do so. + if (s_alwaysDeny) { + makeAsynchronousCallbackToGeolocation(originString, false); + return; + } + + // See if we have a record for this origin in the permanent permissions. + // These take precedence over temporary permissions so that changes made + // from the browser settings work as intended. + PermissionsMap::const_iterator iter = s_permanentPermissions.find(originString); + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + if (iter != end) { + bool allow = iter->second; + makeAsynchronousCallbackToGeolocation(originString, allow); + return; + } + + // Check the temporary permisions. + iter = m_temporaryPermissions.find(originString); + end = m_temporaryPermissions.end(); + if (iter != end) { + bool allow = iter->second; + makeAsynchronousCallbackToGeolocation(originString, allow); + return; + } + + // If there's no pending request, prompt the user. + if (nextOriginInQueue().isEmpty()) { + // Although multiple tabs may request permissions for the same origin + // simultaneously, the routing in WebViewCore/CallbackProxy ensures that + // the result of the request will make it back to this object, so + // there's no need for a globally unique ID for the request. + m_webViewCore->geolocationPermissionsShowPrompt(originString); + } + + // Add this request to the queue so we can track which frames requested it. + if (m_queuedOrigins.find(originString) == WTF::notFound) { + m_queuedOrigins.append(originString); + FrameSet frameSet; + frameSet.add(frame); + m_queuedOriginsToFramesMap.add(originString, frameSet); + } else { + ASSERT(m_queuedOriginsToFramesMap.contains(originString)); + m_queuedOriginsToFramesMap.find(originString)->second.add(frame); + } +} + +void GeolocationPermissions::cancelPermissionStateQuery(WebCore::Frame* frame) +{ + // We cancel any queued request for the given frame. There can be at most + // one of these, since each frame maps to a single origin. We only cancel + // the request if this frame is the only one reqesting permission for this + // origin. + // + // We can use the origin string to avoid searching the map. + String originString = frame->document()->securityOrigin()->toString(); + size_t index = m_queuedOrigins.find(originString); + if (index == WTF::notFound) + return; + + ASSERT(m_queuedOriginsToFramesMap.contains(originString)); + OriginToFramesMap::iterator iter = m_queuedOriginsToFramesMap.find(originString); + ASSERT(iter->second.contains(frame)); + iter->second.remove(frame); + if (!iter->second.isEmpty()) + return; + + m_queuedOrigins.remove(index); + m_queuedOriginsToFramesMap.remove(iter); + + // If this is the origin currently being shown, cancel the prompt + // and show the next in the queue, if present. + if (index == 0) { + m_webViewCore->geolocationPermissionsHidePrompt(); + if (!nextOriginInQueue().isEmpty()) + m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue()); + } +} + +void GeolocationPermissions::makeAsynchronousCallbackToGeolocation(String origin, bool allow) +{ + m_callbackData.origin = origin; + m_callbackData.allow = allow; + m_timer.startOneShot(0); +} + +void GeolocationPermissions::providePermissionState(String origin, bool allow, bool remember) +{ + ASSERT(s_permanentPermissionsLoaded); + + // It's possible that this method is called with an origin that doesn't + // match m_originInProgress. This can occur if this object is reset + // while a permission result is in the process of being marshalled back to + // the WebCore thread from the browser. In this case, we simply ignore the + // call. + if (origin != nextOriginInQueue()) + return; + + maybeCallbackFrames(origin, allow); + recordPermissionState(origin, allow, remember); + + // If the permissions are set to be remembered, cancel any queued requests + // for this domain in other tabs. + if (remember) + cancelPendingRequestsInOtherTabs(origin); + + // Clear the origin from the queue. + ASSERT(!m_queuedOrigins.isEmpty()); + m_queuedOrigins.remove(0); + ASSERT(m_queuedOriginsToFramesMap.contains(origin)); + m_queuedOriginsToFramesMap.remove(origin); + + // If there are other requests queued, start the next one. + if (!nextOriginInQueue().isEmpty()) + m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue()); +} + +void GeolocationPermissions::recordPermissionState(String origin, bool allow, bool remember) +{ + if (remember) { + s_permanentPermissions.set(origin, allow); + s_permanentPermissionsModified = true; + } else { + // It's possible that another tab recorded a permanent permission for + // this origin while our request was in progress, but we record it + // anyway. + m_temporaryPermissions.set(origin, allow); + } +} + +void GeolocationPermissions::cancelPendingRequestsInOtherTabs(String origin) +{ + for (GeolocationPermissionsVector::const_iterator iter = s_instances.begin(); + iter != s_instances.end(); + ++iter) + (*iter)->cancelPendingRequests(origin); +} + +void GeolocationPermissions::cancelPendingRequests(String origin) +{ + size_t index = m_queuedOrigins.find(origin); + + // Don't cancel the request if it's currently being shown, in which case + // it's at index 0. + if (index == WTF::notFound || !index) + return; + + // Get the permission from the permanent list. + ASSERT(s_permanentPermissions.contains(origin)); + PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin); + bool allow = iter->second; + + maybeCallbackFrames(origin, allow); + + m_queuedOrigins.remove(index); + ASSERT(m_queuedOriginsToFramesMap.contains(origin)); + m_queuedOriginsToFramesMap.remove(origin); +} + +void GeolocationPermissions::timerFired(Timer<GeolocationPermissions>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_timer); + maybeCallbackFrames(m_callbackData.origin, m_callbackData.allow); +} + +void GeolocationPermissions::resetTemporaryPermissionStates() +{ + ASSERT(s_permanentPermissionsLoaded); + m_queuedOrigins.clear(); + m_queuedOriginsToFramesMap.clear(); + m_temporaryPermissions.clear(); + // If any permission results are being marshalled back to this thread, this + // will render them inefective. + m_timer.stop(); + + m_webViewCore->geolocationPermissionsHidePrompt(); +} + +const WTF::String& GeolocationPermissions::nextOriginInQueue() +{ + static const String emptyString = ""; + return m_queuedOrigins.isEmpty() ? emptyString : m_queuedOrigins[0]; +} + +void GeolocationPermissions::maybeCallbackFrames(String origin, bool allow) +{ + // We can't track which frame issued the request, as frames can be deleted + // or have their contents replaced. Even uniqueChildName is not unique when + // frames are dynamically deleted and created. Instead, we simply call back + // to the Geolocation object in all frames from the correct origin. + for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) { + if (origin == frame->document()->securityOrigin()->toString()) { + // If the page has changed, it may no longer have a Geolocation + // object. + Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation(); + if (geolocation) + geolocation->setIsAllowed(allow); + } + } +} + +GeolocationPermissions::OriginSet GeolocationPermissions::getOrigins() +{ + maybeLoadPermanentPermissions(); + OriginSet origins; + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) + origins.add(iter->first); + return origins; +} + +bool GeolocationPermissions::getAllowed(String origin) +{ + maybeLoadPermanentPermissions(); + bool allowed = false; + PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin); + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + if (iter != end) + allowed = iter->second; + return allowed; +} + +void GeolocationPermissions::clear(String origin) +{ + maybeLoadPermanentPermissions(); + PermissionsMap::iterator iter = s_permanentPermissions.find(origin); + if (iter != s_permanentPermissions.end()) { + s_permanentPermissions.remove(iter); + s_permanentPermissionsModified = true; + } +} + +void GeolocationPermissions::allow(String origin) +{ + maybeLoadPermanentPermissions(); + // We replace any existing permanent permission. + s_permanentPermissions.set(origin, true); + s_permanentPermissionsModified = true; +} + +void GeolocationPermissions::clearAll() +{ + maybeLoadPermanentPermissions(); + s_permanentPermissions.clear(); + s_permanentPermissionsModified = true; +} + +void GeolocationPermissions::maybeLoadPermanentPermissions() +{ + if (s_permanentPermissionsLoaded) + return; + s_permanentPermissionsLoaded = true; + + SQLiteDatabase database; + if (!openDatabase(&database)) + return; + + // Create the table here, such that even if we've just created the DB, the + // commands below should succeed. + if (!database.executeCommand("CREATE TABLE IF NOT EXISTS Permissions (origin TEXT UNIQUE NOT NULL, allow INTEGER NOT NULL)")) { + database.close(); + return; + } + + SQLiteStatement statement(database, "SELECT * FROM Permissions"); + if (statement.prepare() != SQLResultOk) { + database.close(); + return; + } + + ASSERT(s_permanentPermissions.size() == 0); + while (statement.step() == SQLResultRow) + s_permanentPermissions.set(statement.getColumnText(0), statement.getColumnInt64(1)); + + database.close(); +} + +void GeolocationPermissions::maybeStorePermanentPermissions() +{ + // If the permanent permissions haven't been modified, there's no need to + // save them to the DB. (If we haven't even loaded them, writing them now + // would overwrite the stored permissions with the empty set.) + if (!s_permanentPermissionsModified) + return; + + SQLiteDatabase database; + if (!openDatabase(&database)) + return; + + SQLiteTransaction transaction(database); + + // The number of entries should be small enough that it's not worth trying + // to perform a diff. Simply clear the table and repopulate it. + if (!database.executeCommand("DELETE FROM Permissions")) { + database.close(); + return; + } + + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) { + SQLiteStatement statement(database, "INSERT INTO Permissions (origin, allow) VALUES (?, ?)"); + if (statement.prepare() != SQLResultOk) + continue; + statement.bindText(1, iter->first); + statement.bindInt64(2, iter->second); + statement.executeCommand(); + } + + transaction.commit(); + database.close(); + + s_permanentPermissionsModified = false; +} + +void GeolocationPermissions::setDatabasePath(String path) +{ + // Take the first non-empty value. + if (s_databasePath.length() > 0) + return; + s_databasePath = path; +} + +bool GeolocationPermissions::openDatabase(SQLiteDatabase* database) +{ + ASSERT(database); + String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(s_databasePath, databaseName); + if (!database->open(filename)) + return false; + if (chmod(filename.utf8().data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) { + database->close(); + return false; + } + return true; +} + +void GeolocationPermissions::setAlwaysDeny(bool deny) +{ + s_alwaysDeny = deny; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h new file mode 100644 index 0000000..fb31dfe --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.h @@ -0,0 +1,178 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GeolocationPermissions_h +#define GeolocationPermissions_h + +#include "PlatformString.h" +#include "Timer.h" + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + class Frame; + class Geolocation; + class SQLiteDatabase; +} + +namespace android { + + class WebViewCore; + + // The GeolocationPermissions class manages Geolocation permissions for the + // browser. Permissions are managed on a per-origin basis, as required by + // the Geolocation spec - http://dev.w3.org/geo/api/spec-source.html. An + // origin specifies the scheme, host and port of particular frame. An + // origin is represented here as a string, using the output of + // WebCore::SecurityOrigin::toString. + // + // Each instance handles permissions for a given main frame. The class + // enforces the following policy. + // - Non-remembered permissions last for the dureation of the main frame. + // - Remembered permissions last indefinitely. + // - All permissions are shared between child frames of a main frame. + // - Only remembered permissions are shared between main frames. + // - Remembered permissions are made available for use in the browser + // settings menu. + class GeolocationPermissions : public RefCounted<GeolocationPermissions> { + public: + // Creates the GeolocationPermissions object to manage permissions for + // the specified main frame (i.e. tab). The WebViewCore is used to + // communicate with the browser to display UI. + GeolocationPermissions(WebViewCore* webViewCore, WebCore::Frame* mainFrame); + virtual ~GeolocationPermissions(); + + // Queries the permission state for the specified frame. If the + // permission state has not yet been set, prompts the user. Once the + // permission state has been determined, asynchronously calls back to + // the Geolocation objects in all frames in this WebView that are from + // the same origin as the requesting frame. + void queryPermissionState(WebCore::Frame* frame); + void cancelPermissionStateQuery(WebCore::Frame*); + + // Provides this object with a permission state set by the user. The + // permission is specified by 'allow' and applied to 'origin'. If + // 'remember' is set, the permission state is remembered permanently. + // The new permission state is recorded and will trigger callbacks to + // geolocation objects as described above. If any other permission + // requests are queued, the next is started. + void providePermissionState(WTF::String origin, bool allow, bool remember); + + // Clears the temporary permission state and any pending requests. Used + // when the main frame is refreshed or navigated to a new URL. + void resetTemporaryPermissionStates(); + + // Static methods for use from Java. These are used to interact with the + // browser settings menu and to update the permanent permissions when + // system settings are changed. + // Gets the list of all origins for which permanent permissions are + // recorded. + typedef HashSet<WTF::String> OriginSet; + static OriginSet getOrigins(); + // Gets whether the specified origin is allowed. + static bool getAllowed(WTF::String origin); + // Clears the permission state for the specified origin. + static void clear(WTF::String origin); + // Sets the permission state for the specified origin to allowed. + static void allow(WTF::String origin); + // Clears the permission state for all origins. + static void clearAll(); + // Sets whether the GeolocationPermissions object should always deny + // permission requests, irrespective of previously recorded permission + // states. + static void setAlwaysDeny(bool deny); + + static void setDatabasePath(WTF::String path); + static bool openDatabase(WebCore::SQLiteDatabase*); + + // Saves the permanent permissions to the DB if required. + static void maybeStorePermanentPermissions(); + + private: + // Records the permission state for the specified origin and whether + // this should be remembered. + void recordPermissionState(WTF::String origin, bool allow, bool remember); + + // Used to make an asynchronous callback to the Geolocation objects. + void makeAsynchronousCallbackToGeolocation(WTF::String origin, bool allow); + void timerFired(WebCore::Timer<GeolocationPermissions>* timer); + + // Calls back to the Geolocation objects in all frames from the + // specified origin. There may be no such objects, as the frames using + // Geolocation from the specified origin may no longer use Geolocation, + // or may have been navigated to a different origin.. + void maybeCallbackFrames(WTF::String origin, bool allow); + + // Cancels pending permission requests for the specified origin in + // other main frames (ie browser tabs). This is used when the user + // specifies permission to be remembered. + static void cancelPendingRequestsInOtherTabs(WTF::String origin); + void cancelPendingRequests(WTF::String origin); + + static void maybeLoadPermanentPermissions(); + + const WTF::String& nextOriginInQueue(); + + WebViewCore* m_webViewCore; + WebCore::Frame* m_mainFrame; + // A vector of the origins queued to make a permission request. + // The first in the vector is the origin currently making the request. + typedef Vector<WTF::String> OriginVector; + OriginVector m_queuedOrigins; + // A map from a queued origin to the set of frames that have requested + // permission for that origin. + typedef HashSet<WebCore::Frame*> FrameSet; + typedef HashMap<WTF::String, FrameSet> OriginToFramesMap; + OriginToFramesMap m_queuedOriginsToFramesMap; + + typedef WTF::HashMap<WTF::String, bool> PermissionsMap; + PermissionsMap m_temporaryPermissions; + static PermissionsMap s_permanentPermissions; + + typedef WTF::Vector<GeolocationPermissions*> GeolocationPermissionsVector; + static GeolocationPermissionsVector s_instances; + + WebCore::Timer<GeolocationPermissions> m_timer; + + struct CallbackData { + WTF::String origin; + bool allow; + }; + CallbackData m_callbackData; + + static bool s_alwaysDeny; + + static bool s_permanentPermissionsLoaded; + static bool s_permanentPermissionsModified; + static WTF::String s_databasePath; + }; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h b/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h new file mode 100644 index 0000000..9d734e8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/InspectorClientAndroid.h @@ -0,0 +1,54 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InspectorClientAndroid_h +#define InspectorClientAndroid_h + +#include "InspectorClient.h" + +#include <wtf/Forward.h> + +namespace android { + +class InspectorClientAndroid : public InspectorClient { +public: + virtual ~InspectorClientAndroid() { } + + virtual void inspectorDestroyed() { delete this; } + + virtual void openInspectorFrontend(WebCore::InspectorController*) {} + + virtual void highlight(Node*) {} + virtual void hideHighlight() {} + + virtual void populateSetting(const String& key, String* value) {} + virtual void storeSetting(const String& key, const String& value) {} + + virtual bool sendMessageToFrontend(const WTF::String&) { return false; } +}; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h b/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h new file mode 100644 index 0000000..1bcd8e8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/KeyGeneratorClient.h @@ -0,0 +1,46 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KEY_GENERATOR_CLIENT_H +#define KEY_GENERATOR_CLIENT_H + +#include "KURL.h" +#include "PlatformString.h" + +#include <wtf/Vector.h> + +using namespace WebCore; + +namespace android { + +class KeyGeneratorClient { +public: + virtual ~KeyGeneratorClient() { } + virtual WTF::Vector<String> getSupportedKeyStrengthList() = 0; + virtual String getSignedPublicKeyAndChallengeString(unsigned index, + const String& challenge, const KURL& url) = 0; + }; +} +#endif diff --git a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp new file mode 100644 index 0000000..e6a2710 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp @@ -0,0 +1,654 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MediaPlayerPrivateAndroid.h" + +#if ENABLE(VIDEO) + +#include "BaseLayerAndroid.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "SkiaUtils.h" +#include "VideoLayerAndroid.h" +#include "WebCoreJni.h" +#include "WebViewCore.h" +#include <GraphicsJNI.h> +#include <JNIHelp.h> +#include <JNIUtility.h> +#include <SkBitmap.h> +#include <gui/SurfaceTexture.h> + +using namespace android; +// Forward decl +namespace android { +sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +}; + +namespace WebCore { + +static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy"; +static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio"; + +struct MediaPlayerPrivate::JavaGlue { + jobject m_javaProxy; + jmethodID m_play; + jmethodID m_teardown; + jmethodID m_seek; + jmethodID m_pause; + // Audio + jmethodID m_newInstance; + jmethodID m_setDataSource; + jmethodID m_getMaxTimeSeekable; + // Video + jmethodID m_getInstance; + jmethodID m_loadPoster; +}; + +MediaPlayerPrivate::~MediaPlayerPrivate() +{ + // m_videoLayer is reference counted, unref is enough here. + m_videoLayer->unref(); + if (m_glue->m_javaProxy) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (env) { + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown); + env->DeleteGlobalRef(m_glue->m_javaProxy); + } + } + delete m_glue; +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + registrar(create, getSupportedTypes, supportsType); +} + +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + if (WebViewCore::isSupportedMediaMimeType(type)) + return MediaPlayer::MayBeSupported; + return MediaPlayer::IsNotSupported; +} + +void MediaPlayerPrivate::pause() +{ + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_glue->m_javaProxy || !m_url.length()) + return; + + m_paused = true; + m_player->playbackStateChanged(); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_pause); + checkException(env); +} + +void MediaPlayerPrivate::setVisible(bool visible) +{ + m_isVisible = visible; + if (m_isVisible) + createJavaPlayerIfNeeded(); +} + +void MediaPlayerPrivate::seek(float time) +{ + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length()) + return; + + if (m_glue->m_javaProxy) { + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f)); + m_currentTime = time; + } + checkException(env); +} + +void MediaPlayerPrivate::prepareToPlay() +{ + // We are about to start playing. Since our Java VideoView cannot + // buffer any data, we just simply transition to the HaveEnoughData + // state in here. This will allow the MediaPlayer to transition to + // the "play" state, at which point our VideoView will start downloading + // the content and start the playback. + m_networkState = MediaPlayer::Loaded; + m_player->networkStateChanged(); + m_readyState = MediaPlayer::HaveEnoughData; + m_player->readyStateChanged(); +} + +MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) + : m_player(player), + m_glue(0), + m_duration(1), // keep this minimal to avoid initial seek problem + m_currentTime(0), + m_paused(true), + m_readyState(MediaPlayer::HaveNothing), + m_networkState(MediaPlayer::Empty), + m_poster(0), + m_naturalSize(100, 100), + m_naturalSizeUnknown(true), + m_isVisible(false), + m_videoLayer(new VideoLayerAndroid()) +{ +} + +void MediaPlayerPrivate::onEnded() +{ + m_currentTime = duration(); + m_player->timeChanged(); + m_paused = true; + m_player->playbackStateChanged(); + m_networkState = MediaPlayer::Idle; +} + +void MediaPlayerPrivate::onPaused() +{ + m_paused = true; + m_player->playbackStateChanged(); + m_networkState = MediaPlayer::Idle; + m_player->playbackStateChanged(); +} + +void MediaPlayerPrivate::onTimeupdate(int position) +{ + m_currentTime = position / 1000.0f; + m_player->timeChanged(); +} + +void MediaPlayerPrivate::onStopFullscreen() +{ + if (m_player && m_player->mediaPlayerClient() + && m_player->mediaPlayerClient()->mediaPlayerOwningDocument()) { + m_player->mediaPlayerClient()->mediaPlayerOwningDocument()->webkitCancelFullScreen(); + } +} + +class MediaPlayerVideoPrivate : public MediaPlayerPrivate { +public: + void load(const String& url) + { + m_url = url; + // Cheat a bit here to make sure Window.onLoad event can be triggered + // at the right time instead of real video play time, since only full + // screen video play is supported in Java's VideoView. + // See also comments in prepareToPlay function. + m_networkState = MediaPlayer::Loading; + m_player->networkStateChanged(); + m_readyState = MediaPlayer::HaveCurrentData; + m_player->readyStateChanged(); + } + + void play() + { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length() || !m_glue->m_javaProxy) + return; + + // We only play video fullscreen on Android, so stop sites playing fullscreen video in the onload handler. + Frame* frame = m_player->frameView()->frame(); + if (frame && !frame->loader()->documentLoader()->wasOnloadHandled()) + return; + + m_paused = false; + m_player->playbackStateChanged(); + + if (m_currentTime == duration()) + m_currentTime = 0; + + jstring jUrl = wtfStringToJstring(env, m_url); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl, + static_cast<jint>(m_currentTime * 1000.0f), + m_videoLayer->uniqueId()); + env->DeleteLocalRef(jUrl); + + checkException(env); + } + bool canLoadPoster() const { return true; } + void setPoster(const String& url) + { + if (m_posterUrl == url) + return; + + m_posterUrl = url; + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_glue->m_javaProxy || !m_posterUrl.length()) + return; + // Send the poster + jstring jUrl = wtfStringToJstring(env, m_posterUrl); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); + env->DeleteLocalRef(jUrl); + } + void paint(GraphicsContext* ctxt, const IntRect& r) + { + if (ctxt->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef())) + return; + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + // We paint with the following rules in mind: + // - only downscale the poster, never upscale + // - maintain the natural aspect ratio of the poster + // - the poster should be centered in the target rect + float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height()); + int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width(); + int posterHeight = posterWidth / originalRatio; + int posterX = ((r.width() - posterWidth) / 2) + r.x(); + int posterY = ((r.height() - posterHeight) / 2) + r.y(); + IntRect targetRect(posterX, posterY, posterWidth, posterHeight); + canvas->drawBitmapRect(*m_poster, 0, targetRect, 0); + } + + void onPosterFetched(SkBitmap* poster) + { + m_poster = poster; + if (m_naturalSizeUnknown) { + // We had to fake the size at startup, or else our paint + // method would not be called. If we haven't yet received + // the onPrepared event, update the intrinsic size to the size + // of the poster. That will be overriden when onPrepare comes. + // In case of an error, we should report the poster size, rather + // than our initial fake value. + m_naturalSize = IntSize(poster->width(), poster->height()); + m_player->sizeChanged(); + } + } + + void onPrepared(int duration, int width, int height) + { + m_duration = duration / 1000.0f; + m_naturalSize = IntSize(width, height); + m_naturalSizeUnknown = false; + m_player->durationChanged(); + m_player->sizeChanged(); + } + + virtual bool hasAudio() const { return false; } // do not display the audio UI + virtual bool hasVideo() const { return true; } + virtual bool supportsFullscreen() const { return true; } + + MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) + { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClass); + + if (!clazz) + return; + + m_glue = new JavaGlue; + m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;"); + m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V"); + m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;II)V"); + + m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V"); + m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V"); + m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V"); + m_glue->m_javaProxy = 0; + env->DeleteLocalRef(clazz); + // An exception is raised if any of the above fails. + checkException(env); + } + + void createJavaPlayerIfNeeded() + { + // Check if we have been already created. + if (m_glue->m_javaProxy) + return; + + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClass); + + if (!clazz) + return; + + jobject obj = 0; + + FrameView* frameView = m_player->frameView(); + if (!frameView) + return; + WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView); + ASSERT(webViewCore); + + // Get the HTML5VideoViewProxy instance + obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this); + m_glue->m_javaProxy = env->NewGlobalRef(obj); + // Send the poster + jstring jUrl = 0; + if (m_posterUrl.length()) + jUrl = wtfStringToJstring(env, m_posterUrl); + // Sending a NULL jUrl allows the Java side to try to load the default poster. + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl); + if (jUrl) + env->DeleteLocalRef(jUrl); + + // Clean up. + if (obj) + env->DeleteLocalRef(obj); + env->DeleteLocalRef(clazz); + checkException(env); + } + + float maxTimeSeekable() const + { + return m_duration; + } +}; + +class MediaPlayerAudioPrivate : public MediaPlayerPrivate { +public: + void load(const String& url) + { + m_url = url; + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length()) + return; + + createJavaPlayerIfNeeded(); + + if (!m_glue->m_javaProxy) + return; + + jstring jUrl = wtfStringToJstring(env, m_url); + // start loading the data asynchronously + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl); + env->DeleteLocalRef(jUrl); + checkException(env); + } + + void play() + { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env || !m_url.length()) + return; + + createJavaPlayerIfNeeded(); + + if (!m_glue->m_javaProxy) + return; + + m_paused = false; + m_player->playbackStateChanged(); + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play); + checkException(env); + } + + virtual bool hasAudio() const { return true; } + virtual bool hasVideo() const { return false; } + virtual bool supportsFullscreen() const { return false; } + + float maxTimeSeekable() const + { + if (m_glue->m_javaProxy) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (env) { + float maxTime = env->CallFloatMethod(m_glue->m_javaProxy, + m_glue->m_getMaxTimeSeekable); + checkException(env); + return maxTime; + } + } + return 0; + } + + MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) + { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClassAudio); + + if (!clazz) + return; + + m_glue = new JavaGlue; + m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V"); + m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V"); + m_glue->m_play = env->GetMethodID(clazz, "play", "()V"); + m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F"); + m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V"); + m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V"); + m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V"); + m_glue->m_javaProxy = 0; + env->DeleteLocalRef(clazz); + // An exception is raised if any of the above fails. + checkException(env); + } + + void createJavaPlayerIfNeeded() + { + // Check if we have been already created. + if (m_glue->m_javaProxy) + return; + + JNIEnv* env = JSC::Bindings::getJNIEnv(); + if (!env) + return; + + jclass clazz = env->FindClass(g_ProxyJavaClassAudio); + + if (!clazz) + return; + + jobject obj = 0; + + // Get the HTML5Audio instance + obj = env->NewObject(clazz, m_glue->m_newInstance, this); + m_glue->m_javaProxy = env->NewGlobalRef(obj); + + // Clean up. + if (obj) + env->DeleteLocalRef(obj); + env->DeleteLocalRef(clazz); + checkException(env); + } + + void onPrepared(int duration, int width, int height) + { + // Android media player gives us a duration of 0 for a live + // stream, so in that case set the real duration to infinity. + // We'll still be able to handle the case that we genuinely + // get an audio clip with a duration of 0s as we'll get the + // ended event when it stops playing. + if (duration > 0) { + m_duration = duration / 1000.0f; + } else { + m_duration = std::numeric_limits<float>::infinity(); + } + m_player->durationChanged(); + m_player->sizeChanged(); + m_player->prepareToPlay(); + } +}; + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + if (player->mediaElementType() == MediaPlayer::Video) + return new MediaPlayerVideoPrivate(player); + return new MediaPlayerAudioPrivate(player); +} + +} + +namespace android { + +static void OnPrepared(JNIEnv* env, jobject obj, int duration, int width, int height, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + player->onPrepared(duration, width, height); + } +} + +static void OnEnded(JNIEnv* env, jobject obj, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + player->onEnded(); + } +} + +static void OnPaused(JNIEnv* env, jobject obj, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + player->onPaused(); + } +} + +static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer) +{ + if (!pointer || !poster) + return; + + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster); + if (!posterNative) + return; + player->onPosterFetched(posterNative); +} + +static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + // TODO: player->onBuffering(percent); + } +} + +static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + player->onTimeupdate(position); + } +} + +// This is called on the UI thread only. +// The video layers are composited on the webkit thread and then copied over +// to the UI thread with the same ID. For rendering, we are only using the +// video layers on the UI thread. Therefore, on the UI thread, we have to use +// the videoLayerId from Java side to find the exact video layer in the tree +// to set the surface texture. +// Every time a play call into Java side, the videoLayerId will be sent and +// saved in Java side. Then every time setBaseLayer call, the saved +// videoLayerId will be passed to this function to find the Video Layer. +// Return value: true when the video layer is found. +static bool SendSurfaceTexture(JNIEnv* env, jobject obj, jobject surfTex, + int baseLayer, int videoLayerId, + int textureName, int playerState) { + if (!surfTex) + return false; + + sp<SurfaceTexture> texture = android::SurfaceTexture_getSurfaceTexture(env, surfTex); + if (!texture.get()) + return false; + + BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(baseLayer); + if (!layerImpl) + return false; + if (!layerImpl->countChildren()) + return false; + LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(layerImpl->getChild(0)); + if (!compositedRoot) + return false; + + VideoLayerAndroid* videoLayer = + static_cast<VideoLayerAndroid*>(compositedRoot->findById(videoLayerId)); + if (!videoLayer) + return false; + + // Set the SurfaceTexture to the layer we found + videoLayer->setSurfaceTexture(texture, textureName, static_cast<PlayerState>(playerState)); + return true; +} + +static void OnStopFullscreen(JNIEnv* env, jobject obj, int pointer) +{ + if (pointer) { + WebCore::MediaPlayerPrivate* player = + reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer); + player->onStopFullscreen(); + } +} + +/* + * JNI registration + */ +static JNINativeMethod g_MediaPlayerMethods[] = { + { "nativeOnPrepared", "(IIII)V", + (void*) OnPrepared }, + { "nativeOnEnded", "(I)V", + (void*) OnEnded }, + { "nativeOnStopFullscreen", "(I)V", + (void*) OnStopFullscreen }, + { "nativeOnPaused", "(I)V", + (void*) OnPaused }, + { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V", + (void*) OnPosterFetched }, + { "nativeSendSurfaceTexture", "(Landroid/graphics/SurfaceTexture;IIII)Z", + (void*) SendSurfaceTexture }, + { "nativeOnTimeupdate", "(II)V", + (void*) OnTimeupdate }, +}; + +static JNINativeMethod g_MediaAudioPlayerMethods[] = { + { "nativeOnBuffering", "(II)V", + (void*) OnBuffering }, + { "nativeOnEnded", "(I)V", + (void*) OnEnded }, + { "nativeOnPrepared", "(IIII)V", + (void*) OnPrepared }, + { "nativeOnTimeupdate", "(II)V", + (void*) OnTimeupdate }, +}; + +int registerMediaPlayerVideo(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, g_ProxyJavaClass, + g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods)); +} + +int registerMediaPlayerAudio(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio, + g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods)); +} + +} +#endif // VIDEO diff --git a/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp new file mode 100644 index 0000000..32cdebf --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/MemoryUsage.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2010 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MemoryUsage.h" + +#include <malloc.h> +#include <wtf/CurrentTime.h> + +#if USE(V8) +#include <v8.h> +#endif // USE(V8) + +using namespace WTF; + +class MemoryUsageCache { +public: + MemoryUsageCache() + : m_cachedMemoryUsage(0) + , m_cacheTime(0) + { + } + + int getCachedMemoryUsage(bool forceFresh); + +private: + unsigned m_cachedMemoryUsage; + double m_cacheTime; + static const int CACHE_VALIDITY_MS = 2000; +}; + +int MemoryUsageCache::getCachedMemoryUsage(bool forceFresh) +{ + if (!forceFresh && currentTimeMS() <= m_cacheTime + CACHE_VALIDITY_MS) + return m_cachedMemoryUsage; + + struct mallinfo minfo = mallinfo(); + m_cachedMemoryUsage = (minfo.hblkhd + minfo.arena) >> 20; + +#if USE(V8) + v8::HeapStatistics stat; + v8::V8::GetHeapStatistics(&stat); + unsigned v8Usage = stat.total_heap_size() >> 20; + m_cachedMemoryUsage += v8Usage; +#endif // USE(V8) + + m_cacheTime = currentTimeMS(); + return m_cachedMemoryUsage; +} + +int MemoryUsage::memoryUsageMb(bool forceFresh) +{ + static MemoryUsageCache cache; + return cache.getCachedMemoryUsage(forceFresh); +} + +int MemoryUsage::m_lowMemoryUsageMb = 0; +int MemoryUsage::m_highMemoryUsageMb = 0; +int MemoryUsage::m_highUsageDeltaMb = 0; diff --git a/Source/WebKit/android/WebCoreSupport/MemoryUsage.h b/Source/WebKit/android/WebCoreSupport/MemoryUsage.h new file mode 100644 index 0000000..2a3d7ed --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/MemoryUsage.h @@ -0,0 +1,45 @@ +/* + * Copyright 2010 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MemoryUsage_h +#define MemoryUsage_h + +class MemoryUsage { +public: + static int memoryUsageMb(bool forceFresh); + static int lowMemoryUsageMb() { return m_lowMemoryUsageMb; } + static int highMemoryUsageMb() { return m_highMemoryUsageMb; } + static int highUsageDeltaMb() { return m_highUsageDeltaMb; } + static void setHighMemoryUsageMb(int highMemoryUsageMb) { m_highMemoryUsageMb = highMemoryUsageMb; } + static void setLowMemoryUsageMb(int lowMemoryUsageMb) { m_lowMemoryUsageMb = lowMemoryUsageMb; } + static void setHighUsageDeltaMb(int highUsageDeltaMb) { m_highUsageDeltaMb = highUsageDeltaMb; } + +private: + static int m_lowMemoryUsageMb; + static int m_highMemoryUsageMb; + static int m_highUsageDeltaMb; +}; + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp new file mode 100644 index 0000000..8d8d809 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include <PlatformBridge.h> + +#include "CookieClient.h" +#include "Document.h" +#include "FileSystemClient.h" +#include "FrameView.h" +#include "JavaSharedClient.h" +#include "KeyGeneratorClient.h" +#include "MemoryUsage.h" +#include "PluginView.h" +#include "Settings.h" +#include "WebCookieJar.h" +#include "WebRequestContext.h" +#include "WebViewCore.h" +#include "npruntime.h" + +#include <surfaceflinger/SurfaceComposerClient.h> +#include <ui/DisplayInfo.h> +#include <ui/PixelFormat.h> +#include <wtf/android/AndroidThreading.h> +#include <wtf/MainThread.h> + +using namespace android; + +namespace WebCore { + +WTF::Vector<String> PlatformBridge::getSupportedKeyStrengthList() +{ + KeyGeneratorClient* client = JavaSharedClient::GetKeyGeneratorClient(); + if (!client) + return WTF::Vector<String>(); + + return client->getSupportedKeyStrengthList(); +} + +String PlatformBridge::getSignedPublicKeyAndChallengeString(unsigned index, const String& challenge, const KURL& url) +{ + KeyGeneratorClient* client = JavaSharedClient::GetKeyGeneratorClient(); + if (!client) + return String(); + + return client->getSignedPublicKeyAndChallengeString(index, challenge, url); +} + +void PlatformBridge::setCookies(const Document* document, const KURL& url, const String& value) +{ +#if USE(CHROME_NETWORK_STACK) + std::string cookieValue(value.utf8().data()); + GURL cookieGurl(url.string().utf8().data()); + bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); + WebCookieJar::get(isPrivateBrowsing)->cookieStore()->SetCookie(cookieGurl, cookieValue); +#else + CookieClient* client = JavaSharedClient::GetCookieClient(); + if (!client) + return; + + client->setCookies(url, value); +#endif +} + +String PlatformBridge::cookies(const Document* document, const KURL& url) +{ +#if USE(CHROME_NETWORK_STACK) + GURL cookieGurl(url.string().utf8().data()); + bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); + std::string cookies = WebCookieJar::get(isPrivateBrowsing)->cookieStore()->GetCookies(cookieGurl); + String cookieString(cookies.c_str()); + return cookieString; +#else + CookieClient* client = JavaSharedClient::GetCookieClient(); + if (!client) + return String(); + + return client->cookies(url); +#endif +} + +bool PlatformBridge::cookiesEnabled(const Document* document) +{ +#if USE(CHROME_NETWORK_STACK) + bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); + return WebCookieJar::get(isPrivateBrowsing)->allowCookies(); +#else + CookieClient* client = JavaSharedClient::GetCookieClient(); + if (!client) + return false; + + return client->cookiesEnabled(); +#endif +} + +NPObject* PlatformBridge::pluginScriptableObject(Widget* widget) +{ +#if USE(V8) + if (!widget->isPluginView()) + return 0; + + PluginView* pluginView = static_cast<PluginView*>(widget); + return pluginView->getNPObject(); +#else + return 0; +#endif +} + +bool PlatformBridge::isWebViewPaused(const WebCore::FrameView* frameView) +{ + android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); + return webViewCore->isPaused(); +} + +bool PlatformBridge::popupsAllowed(NPP) +{ + return false; +} + +String PlatformBridge::resolveFilePathForContentUri(const String& contentUri) +{ + FileSystemClient* client = JavaSharedClient::GetFileSystemClient(); + return client->resolveFilePathForContentUri(contentUri); +} + +int PlatformBridge::PlatformBridge::screenDepth() +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return info.pixelFormatInfo.bitsPerPixel; +} + +FloatRect PlatformBridge::screenRect() +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return FloatRect(0.0, 0.0, info.w, info.h); +} + +// The visible size on screen in document coordinate +int PlatformBridge::screenWidthInDocCoord(const WebCore::FrameView* frameView) +{ + android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); + return webViewCore->screenWidth(); +} + +int PlatformBridge::screenHeightInDocCoord(const WebCore::FrameView* frameView) +{ + android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); + return webViewCore->screenHeight(); +} + +String PlatformBridge::computeDefaultLanguage() +{ +#if USE(CHROME_NETWORK_STACK) + String acceptLanguages = WebRequestContext::acceptLanguage(); + size_t length = acceptLanguages.find(','); + if (length == std::string::npos) + length = acceptLanguages.length(); + return acceptLanguages.substring(0, length); +#else + return "en"; +#endif +} + +void PlatformBridge::updateViewport(FrameView* frameView) +{ + android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); + webViewCore->updateViewport(); +} + +void PlatformBridge::updateTextfield(FrameView* frameView, Node* nodePtr, bool changeToPassword, const WTF::String& text) +{ + android::WebViewCore* webViewCore = android::WebViewCore::getWebViewCore(frameView); + webViewCore->updateTextfield(nodePtr, changeToPassword, text); +} + +void PlatformBridge::setScrollPosition(ScrollView* scrollView, int x, int y) { + // Check to make sure the view is the main FrameView. + android::WebViewCore *webViewCore = android::WebViewCore::getWebViewCore(scrollView); + if (webViewCore->mainFrame()->view() == scrollView) + webViewCore->scrollTo(x, y); +} + +int PlatformBridge::lowMemoryUsageMB() +{ + return MemoryUsage::lowMemoryUsageMb(); +} + +int PlatformBridge::highMemoryUsageMB() +{ + return MemoryUsage::highMemoryUsageMb(); +} + +int PlatformBridge::highUsageDeltaMB() +{ + return MemoryUsage::highUsageDeltaMb(); +} + +int PlatformBridge::memoryUsageMB() +{ + return MemoryUsage::memoryUsageMb(false); +} + +int PlatformBridge::actualMemoryUsageMB() +{ + return MemoryUsage::memoryUsageMb(true); +} + +} // namespace WebCore + + +// This is the implementation of AndroidThreading, which is declared in +// JavaScriptCore/wtf/android/AndroidThreading.h. It is provided here, rather +// than in its own source file, to avoid linker problems. +// +// By default, when building a shared library, the linker strips from static +// libraries any compilation units which do not contain any code referenced from +// that static library. Since +// AndroidThreading::scheduleDispatchFunctionsOnMainThread is not referenced +// from libwebcore.a, implementing it in its own compilation unit results in it +// being stripped. This stripping can be avoided by using the linker option +// --whole-archive for libwebcore.a, but this adds considerably to the size of +// libwebcore.so. + +namespace WTF { + +// Callback in the main thread. +static void timeoutFired(void*) +{ + dispatchFunctionsFromMainThread(); +} + +void AndroidThreading::scheduleDispatchFunctionsOnMainThread() +{ + JavaSharedClient::EnqueueFunctionPtr(timeoutFired, 0); +} + +} // namespace WTF diff --git a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp new file mode 100644 index 0000000..7f54810 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <config.h> +#include <ResourceLoaderAndroid.h> + +#include "Frame.h" +#include "FrameLoaderClientAndroid.h" +#include "WebCoreFrameBridge.h" +#include "WebCoreResourceLoader.h" +#include "WebUrlLoader.h" +#include "WebViewCore.h" + +using namespace android; + +namespace WebCore { + +PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start( + ResourceHandle* handle, const ResourceRequest& request, FrameLoaderClient* client, bool isMainResource, bool isSync) +{ + // Called on main thread + FrameLoaderClientAndroid* clientAndroid = static_cast<FrameLoaderClientAndroid*>(client); +#if USE(CHROME_NETWORK_STACK) + WebViewCore* webViewCore = WebViewCore::getWebViewCore(clientAndroid->getFrame()->view()); + bool isMainFrame = !(clientAndroid->getFrame()->tree() && clientAndroid->getFrame()->tree()->parent()); + return WebUrlLoader::start(client, handle, request, isMainResource, isMainFrame, isSync, webViewCore->webRequestContext()); +#else + return clientAndroid->webFrame()->startLoadingResource(handle, request, isMainResource, isSync); +#endif +} + +bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t identifier) +{ +#if USE(CHROME_NETWORK_STACK) + // This method is used to determine if a POST request can be repeated from + // cache, but you cannot really know until you actually try to read from the + // cache. Even if we checked now, something else could come along and wipe + // out the cache entry by the time we fetch it. + // + // So, we always say yes here, to prevent the FrameLoader from initiating a + // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we + // fix-up the cache policy of the request to force a load from the cache. + return true; +#else + return WebCoreResourceLoader::willLoadFromCache(url, identifier); +#endif +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp new file mode 100644 index 0000000..3779ba8 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "UrlInterceptResponse" +#include "config.h" + +#include "JNIUtility.h" +#include "UrlInterceptResponse.h" +#include "WebCoreJni.h" + +#include <utils/Log.h> + +namespace android { + +class JavaInputStreamWrapper { +public: + JavaInputStreamWrapper(JNIEnv* env, jobject inputStream) + : m_inputStream(env->NewGlobalRef(inputStream)) + , m_buffer(0) { + LOG_ALWAYS_FATAL_IF(!inputStream); + jclass inputStreamClass = env->FindClass("java/io/InputStream"); + LOG_ALWAYS_FATAL_IF(!inputStreamClass); + m_read = env->GetMethodID(inputStreamClass, "read", "([B)I"); + LOG_ALWAYS_FATAL_IF(!m_read); + m_close = env->GetMethodID(inputStreamClass, "close", "()V"); + LOG_ALWAYS_FATAL_IF(!m_close); + } + + ~JavaInputStreamWrapper() { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + env->CallVoidMethod(m_inputStream, m_close); + checkException(env); + env->DeleteGlobalRef(m_inputStream); + // In case we never call read(). + if (m_buffer) + env->DeleteGlobalRef(m_buffer); + } + + void read(std::vector<char>* out) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + // Initialize our read buffer to the capacity of out. + if (!m_buffer) { + m_buffer = env->NewByteArray(out->capacity()); + m_buffer = (jbyteArray) env->NewGlobalRef(m_buffer); + } + int size = (int) env->CallIntMethod(m_inputStream, m_read, m_buffer); + if (checkException(env) || size < 0) + return; + // Copy from m_buffer to out. + out->resize(size); + env->GetByteArrayRegion(m_buffer, 0, size, (jbyte*)&out->front()); + } + +private: + jobject m_inputStream; + jbyteArray m_buffer; + jmethodID m_read; + jmethodID m_close; +}; + +UrlInterceptResponse::UrlInterceptResponse(JNIEnv* env, jobject response) { + jclass javaResponse = env->FindClass("android/webkit/WebResourceResponse"); + LOG_ALWAYS_FATAL_IF(!javaResponse); + jfieldID mimeType = env->GetFieldID(javaResponse, "mMimeType", + "Ljava/lang/String;"); + LOG_ALWAYS_FATAL_IF(!mimeType); + jfieldID encoding = env->GetFieldID(javaResponse, "mEncoding", + "Ljava/lang/String;"); + LOG_ALWAYS_FATAL_IF(!encoding); + jfieldID inputStream = env->GetFieldID(javaResponse, "mInputStream", + "Ljava/io/InputStream;"); + LOG_ALWAYS_FATAL_IF(!inputStream); + + jobject stream = env->GetObjectField(response, inputStream); + if (stream) + m_inputStream.set(new JavaInputStreamWrapper(env, stream)); + + jstring mimeStr = (jstring) env->GetObjectField(response, mimeType); + jstring encodingStr = (jstring) env->GetObjectField(response, encoding); + + if (mimeStr) { + const char* s = env->GetStringUTFChars(mimeStr, NULL); + m_mimeType.assign(s, env->GetStringUTFLength(mimeStr)); + env->ReleaseStringUTFChars(mimeStr, s); + } + if (encodingStr) { + const char* s = env->GetStringUTFChars(encodingStr, NULL); + m_encoding.assign(s, env->GetStringUTFLength(encodingStr)); + env->ReleaseStringUTFChars(encodingStr, s); + } + + env->DeleteLocalRef(javaResponse); + env->DeleteLocalRef(mimeStr); + env->DeleteLocalRef(encodingStr); +} + +UrlInterceptResponse::~UrlInterceptResponse() { + // Cannot be inlined because of JavaInputStreamWrapper visibility. +} + +bool UrlInterceptResponse::readStream(std::vector<char>* out) const { + if (!m_inputStream) + return false; + m_inputStream->read(out); + return true; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h new file mode 100644 index 0000000..64dad69 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.h @@ -0,0 +1,70 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UrlInterceptResponse_h +#define UrlInterceptResponse_h + +#include "PlatformString.h" +#include "wtf/Noncopyable.h" +#include "wtf/OwnPtr.h" + +#include <jni.h> +#include <string> +#include <vector> + +namespace android { + +class JavaInputStreamWrapper; + +class UrlInterceptResponse : public Noncopyable { +public: + UrlInterceptResponse(JNIEnv* env, jobject response); + ~UrlInterceptResponse(); + + const std::string& mimeType() const { + return m_mimeType; + } + + const std::string& encoding() const { + return m_encoding; + } + + int status() const { + return m_inputStream ? 200 : 404; + } + + // Read from the input stream. Returns false if reading failed. + // A size of 0 indicates eof. + bool readStream(std::vector<char>* out) const; + +private: + std::string m_mimeType; + std::string m_encoding; + OwnPtr<JavaInputStreamWrapper> m_inputStream; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/V8Counters.cpp b/Source/WebKit/android/WebCoreSupport/V8Counters.cpp new file mode 100644 index 0000000..d164f9a --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/V8Counters.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifdef ANDROID_INSTRUMENT + +#define LOG_TAG "WebCore" + +#include "config.h" +#include "V8Counters.h" + +#include "NotImplemented.h" +#include <utils/Log.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +#if USE(V8) + +namespace WebCore { + +V8Counters::Counter::Counter(bool isHistogram) + : m_count(0), m_sampleTotal(0), m_isHistogram(isHistogram) { } + +void V8Counters::Counter::addSample(int sample) +{ + m_count++; + m_sampleTotal += sample; +} + +HashMap<String, V8Counters::Counter*> V8Counters::m_counters; + +// static +int* V8Counters::counterForName(const char* name) +{ + Counter* counter = m_counters.get(name); + if (!counter) { + counter = new Counter(false); + m_counters.add(name, counter); + } + return *counter; +} + +// static +void* V8Counters::createHistogram(const char* name, int min, int max, + size_t buckets) +{ + Counter* counter = new Counter(true); + m_counters.add(name, counter); + return counter; +} + +// static +void V8Counters::addHistogramSample(void* histogram, int sample) +{ + Counter* counter = reinterpret_cast<Counter*>(histogram); + counter->addSample(sample); +} + +// static +void V8Counters::initCounters() +{ + static bool isInitialized = false; + if (!isInitialized) { + v8::V8::SetCounterFunction(counterForName); + v8::V8::SetCreateHistogramFunction(createHistogram); + v8::V8::SetAddHistogramSampleFunction(addHistogramSample); + isInitialized = true; + } +} + +// static +void V8Counters::dumpCounters() +{ + LOGD("+----------------------------------------+-------------+\n"); + LOGD("| Name | Value |\n"); + LOGD("+----------------------------------------+-------------+\n"); + typedef HashMap<String, V8Counters::Counter*>::iterator CounterIterator; + for (CounterIterator iter = m_counters.begin(); iter != m_counters.end(); ++iter) { + Counter* counter = iter->second; + if (counter->isHistogram()) { + LOGD("| c:%-36s | %11i |\n", iter->first.latin1().data(), counter->count()); + LOGD("| t:%-36s | %11i |\n", iter->first.latin1().data(), counter->sampleTotal()); + } else { + LOGD("| %-38s | %11i |\n", iter->first.latin1().data(), counter->count()); + } + } + LOGD("+----------------------------------------+-------------+\n"); +} + +} + +#endif // ANDROID_INSTRUMENT + +#endif // USE(V8) diff --git a/Source/WebKit/android/WebCoreSupport/V8Counters.h b/Source/WebKit/android/WebCoreSupport/V8Counters.h new file mode 100644 index 0000000..499b856 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/V8Counters.h @@ -0,0 +1,77 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8Counters_h +#define V8Counters_h + +#if USE(V8) + +#ifdef ANDROID_INSTRUMENT + +#include <PlatformString.h> +#include <v8.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class V8Counters { +public: + // Counter callbacks, see v8.h + static int* counterForName(const char* name); + + static void* createHistogram(const char* name, + int min, + int max, + size_t buckets); + + static void addHistogramSample(void* histogram, int sample); + + static void initCounters(); + static void dumpCounters(); +private: + class Counter { + public: + Counter(bool isHistogram); + + int count() { return m_count; } + int sampleTotal() { return m_sampleTotal; } + bool isHistogram() { return m_isHistogram; } + void addSample(int32_t sample); + + operator int*() { return &m_count; } + private: + int m_count; + int m_sampleTotal; + bool m_isHistogram; + }; + + static HashMap<String, Counter*> m_counters; +}; + +} + +#endif // ANDROID_INSTRUMENT +#endif // USE(V8) +#endif // V8Counters_h diff --git a/Source/WebKit/android/WebCoreSupport/WebCache.cpp b/Source/WebKit/android/WebCoreSupport/WebCache.cpp new file mode 100644 index 0000000..be9a700 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebCache.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebCache.h" + +#include "JNIUtility.h" +#include "WebCoreJni.h" +#include "WebRequestContext.h" +#include "WebUrlLoaderClient.h" + +#include <wtf/text/CString.h> + +using namespace WTF; +using namespace disk_cache; +using namespace net; +using namespace std; + +namespace android { + +static WTF::Mutex instanceMutex; + +static const string& rootDirectory() +{ + // This method may be called on any thread, as the Java method is + // synchronized. + static WTF::Mutex mutex; + MutexLocker lock(mutex); + static string cacheDirectory; + if (cacheDirectory.empty()) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); + jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;"); + cacheDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); + env->DeleteLocalRef(bridgeClass); + } + return cacheDirectory; +} + +static string storageDirectory() +{ + // Private cache is currently in memory only + static const char* const kDirectory = "/webviewCacheChromium"; + string storageDirectory = rootDirectory(); + storageDirectory.append(kDirectory); + return storageDirectory; +} + +static scoped_refptr<WebCache>* instance(bool isPrivateBrowsing) +{ + static scoped_refptr<WebCache> regularInstance; + static scoped_refptr<WebCache> privateInstance; + return isPrivateBrowsing ? &privateInstance : ®ularInstance; +} + +WebCache* WebCache::get(bool isPrivateBrowsing) +{ + MutexLocker lock(instanceMutex); + scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing); + if (!instancePtr->get()) + *instancePtr = new WebCache(isPrivateBrowsing); + return instancePtr->get(); +} + +void WebCache::cleanup(bool isPrivateBrowsing) +{ + MutexLocker lock(instanceMutex); + scoped_refptr<WebCache>* instancePtr = instance(isPrivateBrowsing); + *instancePtr = 0; +} + +WebCache::WebCache(bool isPrivateBrowsing) + : m_doomAllEntriesCallback(this, &WebCache::doomAllEntries) + , m_onClearDoneCallback(this, &WebCache::onClearDone) + , m_isClearInProgress(false) + , m_openEntryCallback(this, &WebCache::openEntry) + , m_onGetEntryDoneCallback(this, &WebCache::onGetEntryDone) + , m_isGetEntryInProgress(false) + , m_cacheBackend(0) +{ + base::Thread* ioThread = WebUrlLoaderClient::ioThread(); + scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy(); + + static const int kMaximumCacheSizeBytes = 20 * 1024 * 1024; + m_hostResolver = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0); + + m_proxyConfigService = new ProxyConfigServiceAndroid(); + net::HttpCache::BackendFactory* backendFactory; + if (isPrivateBrowsing) + backendFactory = net::HttpCache::DefaultBackend::InMemory(kMaximumCacheSizeBytes / 2); + else { + FilePath directoryPath(storageDirectory().c_str()); + backendFactory = new net::HttpCache::DefaultBackend(net::DISK_CACHE, directoryPath, kMaximumCacheSizeBytes, cacheMessageLoopProxy); + } + + m_cache = new net::HttpCache(m_hostResolver.get(), + 0, // dnsrr_resolver + 0, // dns_cert_checker + net::ProxyService::CreateWithoutProxyResolver(m_proxyConfigService, 0 /* net_log */), + net::SSLConfigService::CreateSystemSSLConfigService(), + net::HttpAuthHandlerFactory::CreateDefault(m_hostResolver.get()), + 0, // network_delegate + 0, // net_log + backendFactory); +} + +void WebCache::clear() +{ + base::Thread* thread = WebUrlLoaderClient::ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::clearImpl)); +} + +void WebCache::clearImpl() +{ + if (m_isClearInProgress) + return; + m_isClearInProgress = true; + + if (!m_cacheBackend) { + int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback); + // Code ERR_IO_PENDING indicates that the operation is still in progress and + // the supplied callback will be invoked when it completes. + if (code == ERR_IO_PENDING) + return; + else if (code != OK) { + onClearDone(0 /*unused*/); + return; + } + } + doomAllEntries(0 /*unused*/); +} + +void WebCache::doomAllEntries(int) +{ + if (!m_cacheBackend) { + onClearDone(0 /*unused*/); + return; + } + + // Code ERR_IO_PENDING indicates that the operation is still in progress and + // the supplied callback will be invoked when it completes. + if (m_cacheBackend->DoomAllEntries(&m_onClearDoneCallback) == ERR_IO_PENDING) + return; + onClearDone(0 /*unused*/); +} + +void WebCache::onClearDone(int) +{ + m_isClearInProgress = false; +} + +scoped_refptr<CacheResult> WebCache::getCacheResult(String url) +{ + // This is called on the UI thread. + MutexLocker lock(m_getEntryMutex); + if (m_isGetEntryInProgress) + return 0; // TODO: OK? Or can we queue 'em up? + + // The Chromium methods are asynchronous, but we need this method to be + // synchronous. Do the work on the Chromium thread but block this thread + // here waiting for the work to complete. + base::Thread* thread = WebUrlLoaderClient::ioThread(); + if (!thread) + return 0; + + m_entry = 0; + m_isGetEntryInProgress = true; + m_entryUrl = url.threadsafeCopy(); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::getEntryImpl)); + + while (m_isGetEntryInProgress) + m_getEntryCondition.wait(m_getEntryMutex); + + if (!m_entry) + return 0; + + return new CacheResult(m_entry, url); +} + +void WebCache::getEntryImpl() +{ + if (!m_cacheBackend) { + int code = m_cache->GetBackend(&m_cacheBackend, &m_openEntryCallback); + if (code == ERR_IO_PENDING) + return; + else if (code != OK) { + onGetEntryDone(0 /*unused*/); + return; + } + } + openEntry(0 /*unused*/); +} + +void WebCache::openEntry(int) +{ + if (!m_cacheBackend) { + onGetEntryDone(0 /*unused*/); + return; + } + + if (m_cacheBackend->OpenEntry(string(m_entryUrl.utf8().data()), &m_entry, &m_onGetEntryDoneCallback) == ERR_IO_PENDING) + return; + onGetEntryDone(0 /*unused*/); +} + +void WebCache::onGetEntryDone(int) +{ + // Unblock the UI thread in getEntry(); + MutexLocker lock(m_getEntryMutex); + m_isGetEntryInProgress = false; + m_getEntryCondition.signal(); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebCache.h b/Source/WebKit/android/WebCoreSupport/WebCache.h new file mode 100644 index 0000000..7149fcc --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebCache.h @@ -0,0 +1,88 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebCache_h +#define WebCache_h + +#include "CacheResult.h" +#include "ChromiumIncludes.h" + +#include <OwnPtr.h> +#include <platform/text/PlatformString.h> +#include <wtf/ThreadingPrimitives.h> + +namespace android { + +// This class is not generally threadsafe. However, get() and cleanup() are +// threadsafe. +class WebCache : public base::RefCountedThreadSafe<WebCache> { +public: + static WebCache* get(bool isPrivateBrowsing); + static void cleanup(bool isPrivateBrowsing); + + void clear(); + scoped_refptr<CacheResult> getCacheResult(WTF::String url); + net::HostResolver* hostResolver() { return m_hostResolver.get(); } + net::HttpCache* cache() { return m_cache.get(); } + net::ProxyConfigServiceAndroid* proxy() { return m_proxyConfigService; } + +private: + WebCache(bool isPrivateBrowsing); + + // For clear() + void clearImpl(); + void doomAllEntries(int); + void onClearDone(int); + + // For getEntry() + void getEntryImpl(); + void openEntry(int); + void onGetEntryDone(int); + + OwnPtr<net::HostResolver> m_hostResolver; + OwnPtr<net::HttpCache> m_cache; + // This is owned by the ProxyService, which is owned by the HttpNetworkLayer, + // which is owned by the HttpCache, which is owned by this class. + net::ProxyConfigServiceAndroid* m_proxyConfigService; + + // For clear() + net::CompletionCallbackImpl<WebCache> m_doomAllEntriesCallback; + net::CompletionCallbackImpl<WebCache> m_onClearDoneCallback; + bool m_isClearInProgress; + // For getEntry() + net::CompletionCallbackImpl<WebCache> m_openEntryCallback; + net::CompletionCallbackImpl<WebCache> m_onGetEntryDoneCallback; + bool m_isGetEntryInProgress; + String m_entryUrl; + disk_cache::Entry* m_entry; + WTF::Mutex m_getEntryMutex; + WTF::ThreadCondition m_getEntryCondition; + + disk_cache::Backend* m_cacheBackend; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp new file mode 100644 index 0000000..99de67e --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.cpp @@ -0,0 +1,263 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebCookieJar.h" + +#include "JNIUtility.h" +#include "WebCoreJni.h" +#include "WebRequestContext.h" +#include "WebUrlLoaderClient.h" + +#include <cutils/log.h> +#include <dirent.h> + +#undef ASSERT +#define ASSERT(assertion, ...) do \ + if (!(assertion)) { \ + android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \ + } \ +while (0) + +namespace android { + +static WTF::Mutex instanceMutex; +static bool isFirstInstanceCreated = false; +static bool fileSchemeCookiesEnabled = false; + +static const std::string& databaseDirectory() +{ + // This method may be called on any thread, as the Java method is + // synchronized. + static WTF::Mutex databaseDirectoryMutex; + MutexLocker lock(databaseDirectoryMutex); + static std::string databaseDirectory; + if (databaseDirectory.empty()) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); + jmethodID method = env->GetStaticMethodID(bridgeClass, "getDatabaseDirectory", "()Ljava/lang/String;"); + databaseDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); + env->DeleteLocalRef(bridgeClass); + } + return databaseDirectory; +} + +static void removeFileOrDirectory(const char* filename) +{ + struct stat filetype; + if (stat(filename, &filetype) != 0) + return; + if (S_ISDIR(filetype.st_mode)) { + DIR* directory = opendir(filename); + if (directory) { + while (struct dirent* entry = readdir(directory)) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + std::string entryName(filename); + entryName.append("/"); + entryName.append(entry->d_name); + removeFileOrDirectory(entryName.c_str()); + } + closedir(directory); + rmdir(filename); + } + return; + } + unlink(filename); +} + +static std::string databaseDirectory(bool isPrivateBrowsing) +{ + static const char* const kDatabaseFilename = "/webviewCookiesChromium.db"; + static const char* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db"; + + std::string databaseFilePath = databaseDirectory(); + databaseFilePath.append(isPrivateBrowsing ? kDatabaseFilenamePrivateBrowsing : kDatabaseFilename); + return databaseFilePath; +} + +scoped_refptr<WebCookieJar>* instance(bool isPrivateBrowsing) +{ + static scoped_refptr<WebCookieJar> regularInstance; + static scoped_refptr<WebCookieJar> privateInstance; + return isPrivateBrowsing ? &privateInstance : ®ularInstance; +} + +WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing) +{ + MutexLocker lock(instanceMutex); + if (!isFirstInstanceCreated && fileSchemeCookiesEnabled) + net::CookieMonster::EnableFileScheme(); + isFirstInstanceCreated = true; + scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing); + if (!instancePtr->get()) + *instancePtr = new WebCookieJar(databaseDirectory(isPrivateBrowsing)); + return instancePtr->get(); +} + +void WebCookieJar::cleanup(bool isPrivateBrowsing) +{ + // This is called on the UI thread. + MutexLocker lock(instanceMutex); + scoped_refptr<WebCookieJar>* instancePtr = instance(isPrivateBrowsing); + *instancePtr = 0; + removeFileOrDirectory(databaseDirectory(isPrivateBrowsing).c_str()); +} + +WebCookieJar::WebCookieJar(const std::string& databaseFilePath) + : m_allowCookies(true) +{ + // Setup the permissions for the file + const char* cDatabasePath = databaseFilePath.c_str(); + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + if (access(cDatabasePath, F_OK) == 0) + chmod(cDatabasePath, mode); + else { + int fd = open(cDatabasePath, O_CREAT, mode); + if (fd >= 0) + close(fd); + } + + FilePath cookiePath(databaseFilePath.c_str()); + m_cookieDb = new SQLitePersistentCookieStore(cookiePath); + m_cookieStore = new net::CookieMonster(m_cookieDb.get(), 0); +} + +bool WebCookieJar::allowCookies() +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies; +} + +void WebCookieJar::setAllowCookies(bool allow) +{ + MutexLocker lock(m_allowCookiesMutex); + m_allowCookies = allow; +} + +int WebCookieJar::getNumCookiesInDatabase() +{ + if (!m_cookieStore) + return 0; + return m_cookieStore->GetCookieMonster()->GetAllCookies().size(); +} + +// From CookiePolicy in chromium +int WebCookieJar::CanGetCookies(const GURL&, const GURL&, net::CompletionCallback*) +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; +} + +// From CookiePolicy in chromium +int WebCookieJar::CanSetCookie(const GURL&, const GURL&, const std::string&, net::CompletionCallback*) +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; +} + +class FlushSemaphore : public base::RefCounted<FlushSemaphore> +{ +public: + FlushSemaphore() + : m_condition(&m_lock) + , m_count(0) + {} + + void SendFlushRequest(net::CookieMonster* monster) { + // FlushStore() needs to run on a Chrome thread (because it will need + // to post the callback, and it may want to do so on its own thread.) + // We use the IO thread for this purpose. + // + // TODO(husky): Our threads are hidden away in various files. Clean this + // up and consider integrating with Chrome's browser_thread.h. Might be + // a better idea to use the DB thread here rather than the IO thread. + + base::Thread* ioThread = WebUrlLoaderClient::ioThread(); + if (ioThread) { + Task* callback = NewRunnableMethod(this, &FlushSemaphore::Callback); + ioThread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + monster, &net::CookieMonster::FlushStore, callback)); + } else { + Callback(); + } + } + + // Block until the given number of callbacks has been made. + void Wait(int numCallbacks) { + AutoLock al(m_lock); + int lastCount = m_count; + while (m_count < numCallbacks) { + // TODO(husky): Maybe use TimedWait() here? But it's not obvious what + // to do if the flush fails. Might be okay just to let the OS kill us. + m_condition.Wait(); + ASSERT(lastCount != m_count, "Wait finished without incrementing m_count %d %d", m_count, lastCount); + lastCount = m_count; + } + m_count -= numCallbacks; + } + +private: + friend class base::RefCounted<FlushSemaphore>; + + void Callback() { + AutoLock al(m_lock); + m_count++; + m_condition.Broadcast(); + } + + Lock m_lock; + ConditionVariable m_condition; + volatile int m_count; +}; + +void WebCookieJar::flush() +{ + // Flush both cookie stores (private and non-private), wait for 2 callbacks. + static scoped_refptr<FlushSemaphore> semaphore(new FlushSemaphore()); + semaphore->SendFlushRequest(get(false)->cookieStore()->GetCookieMonster()); + semaphore->SendFlushRequest(get(true)->cookieStore()->GetCookieMonster()); + semaphore->Wait(2); +} + +bool WebCookieJar::acceptFileSchemeCookies() +{ + MutexLocker lock(instanceMutex); + return fileSchemeCookiesEnabled; +} + +void WebCookieJar::setAcceptFileSchemeCookies(bool accept) +{ + // The Chromium HTTP stack only reflects changes to this flag when creating + // a new CookieMonster instance. While we could track whether any + // CookieMonster instances currently exist, this would be complicated and is + // not required, so we only allow this flag to be changed before the first + // instance is created. + MutexLocker lock(instanceMutex); + if (!isFirstInstanceCreated) + fileSchemeCookiesEnabled = accept; +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/WebCookieJar.h b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h new file mode 100644 index 0000000..1f4266c --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebCookieJar.h @@ -0,0 +1,78 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebCookieJar_h +#define WebCookieJar_h + +#include "ChromiumIncludes.h" + +#include <wtf/ThreadingPrimitives.h> + +namespace android { + +// This class is threadsafe. It is used from the IO, WebCore and Chromium IO +// threads. +class WebCookieJar : public net::CookiePolicy, public base::RefCountedThreadSafe<WebCookieJar> { +public: + static WebCookieJar* get(bool isPrivateBrowsing); + static void cleanup(bool isPrivateBrowsing); + + // Flush all cookies to disk. Synchronous. + static void flush(); + + // CookiePolicy implementation from external/chromium + virtual int CanGetCookies(const GURL& url, const GURL& first_party_for_cookies, net::CompletionCallback*); + virtual int CanSetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie_line, net::CompletionCallback*); + + bool allowCookies(); + void setAllowCookies(bool allow); + + // Getter and setter for whether we accept cookies for file scheme URLS. + // Defaults to false. Note that calls to the setter are ignored once the + // first instance of this class has been created. + static bool acceptFileSchemeCookies(); + static void setAcceptFileSchemeCookies(bool); + + // Instead of this it would probably be better to add the cookie methods + // here so the rest of WebKit doesn't have to know about Chromium classes + net::CookieStore* cookieStore() { return m_cookieStore.get(); } + net::CookiePolicy* cookiePolicy() { return this; } + + // Get the number of cookies that have actually been saved to flash. + // (This is used to implement CookieManager.hasCookies() in the Java framework.) + int getNumCookiesInDatabase(); + +private: + WebCookieJar(const std::string& databaseFilePath); + + scoped_refptr<SQLitePersistentCookieStore> m_cookieDb; + scoped_refptr<net::CookieStore> m_cookieStore; + bool m_allowCookies; + WTF::Mutex m_allowCookiesMutex; +}; + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.cpp b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp new file mode 100644 index 0000000..a7321da --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebRequest.cpp @@ -0,0 +1,531 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebRequest.h" + +#include "JNIUtility.h" +#include "MainThread.h" +#include "UrlInterceptResponse.h" +#include "WebCoreFrameBridge.h" +#include "WebRequestContext.h" +#include "WebResourceRequest.h" +#include "WebUrlLoaderClient.h" +#include "jni.h" + +#include <cutils/log.h> +#include <string> +#include <utils/AssetManager.h> + +extern android::AssetManager* globalAssetManager(); + +// TODO: +// - Finish the file upload. Testcase is mobile buzz +// - Add network throttle needed by Android plugins + +// TODO: Turn off asserts crashing before release +// http://b/issue?id=2951985 +#undef ASSERT +#define ASSERT(assertion, ...) do \ + if (!(assertion)) { \ + android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \ + } \ +while (0) + +namespace android { + +namespace { + const int kInitialReadBufSize = 32768; +} + +WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest) + : m_urlLoader(loader) + , m_androidUrl(false) + , m_url(webResourceRequest.url()) + , m_userAgent(webResourceRequest.userAgent()) + , m_loadState(Created) + , m_authRequestCount(0) + , m_cacheMode(0) + , m_runnableFactory(this) + , m_wantToPause(false) + , m_isPaused(false) + , m_isSync(false) +{ + GURL gurl(m_url); + + m_request = new URLRequest(gurl, this); + + m_request->SetExtraRequestHeaders(webResourceRequest.requestHeaders()); + m_request->set_referrer(webResourceRequest.referrer()); + m_request->set_method(webResourceRequest.method()); + m_request->set_load_flags(webResourceRequest.loadFlags()); +} + +// This is a special URL for Android. Query the Java InputStream +// for data and send to WebCore +WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& webResourceRequest, UrlInterceptResponse* intercept) + : m_urlLoader(loader) + , m_interceptResponse(intercept) + , m_androidUrl(true) + , m_url(webResourceRequest.url()) + , m_userAgent(webResourceRequest.userAgent()) + , m_loadState(Created) + , m_authRequestCount(0) + , m_cacheMode(0) + , m_runnableFactory(this) + , m_wantToPause(false) + , m_isPaused(false) + , m_isSync(false) +{ +} + +WebRequest::~WebRequest() +{ + ASSERT(m_loadState == Finished, "dtor called on a WebRequest in a different state than finished (%d)", m_loadState); + + m_loadState = Deleted; +} + +const std::string& WebRequest::getUrl() const +{ + return m_url; +} + +const std::string& WebRequest::getUserAgent() const +{ + return m_userAgent; +} + +#ifdef LOG_REQUESTS +namespace { +int remaining = 0; +} +#endif + +void WebRequest::finish(bool success) +{ + m_runnableFactory.RevokeAll(); + ASSERT(m_loadState < Finished, "(%p) called finish on an already finished WebRequest (%d) (%s)", this, m_loadState, m_url.c_str()); + if (m_loadState >= Finished) + return; +#ifdef LOG_REQUESTS + time_t finish; + time(&finish); + finish = finish - m_startTime; + struct tm * timeinfo; + char buffer[80]; + timeinfo = localtime(&finish); + strftime(buffer, 80, "Time: %M:%S",timeinfo); + android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) finish (%d) (%s) (%d) (%s)", this, --remaining, buffer, success, m_url.c_str()); +#endif + + // Make sure WebUrlLoaderClient doesn't delete us in the middle of this method. + scoped_refptr<WebRequest> guard(this); + + m_loadState = Finished; + if (success) { + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didFinishLoading)); + } else { + if (m_interceptResponse == NULL) { + OwnPtr<WebResponse> webResponse(new WebResponse(m_request.get())); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release())); + } else { + OwnPtr<WebResponse> webResponse(new WebResponse(m_url, m_interceptResponse->mimeType(), 0, + m_interceptResponse->encoding(), m_interceptResponse->status())); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didFail, webResponse.release())); + } + } + m_networkBuffer = 0; + m_request = 0; + m_urlLoader = 0; +} + +void WebRequest::appendFileToUpload(const std::string& filename) +{ + // AppendFileToUpload is only valid before calling start + ASSERT(m_loadState == Created, "appendFileToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); + FilePath filePath(filename); + m_request->AppendFileToUpload(filePath); +} + +void WebRequest::appendBytesToUpload(WTF::Vector<char>* data) +{ + // AppendBytesToUpload is only valid before calling start + ASSERT(m_loadState == Created, "appendBytesToUpload called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); + m_request->AppendBytesToUpload(data->data(), data->size()); + delete data; +} + +void WebRequest::setRequestContext(WebRequestContext* context) +{ + m_cacheMode = context->getCacheMode(); + if (m_request) + m_request->set_context(context); +} + +void WebRequest::updateLoadFlags(int& loadFlags) +{ + if (m_cacheMode == 1) { // LOAD_CACHE_ELSE_NETWORK + loadFlags |= net::LOAD_PREFERRING_CACHE; + loadFlags &= ~net::LOAD_VALIDATE_CACHE; + } + if (m_cacheMode == 2) // LOAD_NO_CACHE + loadFlags |= net::LOAD_BYPASS_CACHE; + if (m_cacheMode == 3) // LOAD_CACHE_ONLY + loadFlags |= net::LOAD_ONLY_FROM_CACHE; + + if (m_isSync) + loadFlags |= net::LOAD_IGNORE_LIMITS; +} + +void WebRequest::start() +{ + ASSERT(m_loadState == Created, "Start called on a WebRequest not in CREATED state: (%s)", m_url.c_str()); +#ifdef LOG_REQUESTS + android_printLog(ANDROID_LOG_DEBUG, "KM", "(%p) start (%d) (%s)", this, ++remaining, m_url.c_str()); + time(&m_startTime); +#endif + + m_loadState = Started; + + if (m_interceptResponse != NULL) + return handleInterceptedURL(); + + // Handle data urls before we send it off to the http stack + if (m_request->url().SchemeIs("data")) + return handleDataURL(m_request->url()); + + if (m_request->url().SchemeIs("browser")) + return handleBrowserURL(m_request->url()); + + // Update load flags with settings from WebSettings + int loadFlags = m_request->load_flags(); + updateLoadFlags(loadFlags); + m_request->set_load_flags(loadFlags); + + m_request->Start(); +} + +void WebRequest::cancel() +{ + ASSERT(m_loadState >= Started, "Cancel called on a not started WebRequest: (%s)", m_url.c_str()); + ASSERT(m_loadState != Cancelled, "Cancel called on an already cancelled WebRequest: (%s)", m_url.c_str()); + + // There is a possible race condition between the IO thread finishing the request and + // the WebCore thread cancelling it. If the request has already finished, do + // nothing to avoid sending duplicate finish messages to WebCore. + if (m_loadState > Cancelled) { + return; + } + ASSERT(m_request, "Request set to 0 before it is finished"); + + m_loadState = Cancelled; + + m_request->Cancel(); + finish(true); +} + +void WebRequest::pauseLoad(bool pause) +{ + ASSERT(m_loadState >= GotData, "PauseLoad in state other than RESPONSE and GOTDATA"); + if (pause) { + if (!m_isPaused) + m_wantToPause = true; + } else { + m_wantToPause = false; + if (m_isPaused) { + m_isPaused = false; + MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading)); + } + } +} + +void WebRequest::handleInterceptedURL() +{ + m_loadState = Response; + + const std::string& mime = m_interceptResponse->mimeType(); + // Get the MIME type from the URL. "text/html" is a last resort, hopefully overridden. + std::string mimeType("text/html"); + if (mime == "") { + // Gmail appends the MIME to the end of the URL, with a ? separator. + size_t mimeTypeIndex = m_url.find_last_of('?'); + if (mimeTypeIndex != std::string::npos) { + mimeType.assign(m_url.begin() + mimeTypeIndex + 1, m_url.end()); + } else { + // Get the MIME type from the file extension, if any. + FilePath path(m_url); + net::GetMimeTypeFromFile(path, &mimeType); + } + } else { + // Set from the intercept response. + mimeType = mime; + } + + + OwnPtr<WebResponse> webResponse(new WebResponse(m_url, mimeType, 0, m_interceptResponse->encoding(), m_interceptResponse->status())); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); + + do { + // data is deleted in WebUrlLoaderClient::didReceiveAndroidFileData + // data is sent to the webcore thread + OwnPtr<std::vector<char> > data(new std::vector<char>); + data->reserve(kInitialReadBufSize); + + // Read returns false on error and size of 0 on eof. + if (!m_interceptResponse->readStream(data.get()) || data->size() == 0) + break; + + m_loadState = GotData; + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveAndroidFileData, data.release())); + } while (true); + + finish(m_interceptResponse->status() == 200); +} + +void WebRequest::handleDataURL(GURL url) +{ + OwnPtr<std::string> data(new std::string); + std::string mimeType; + std::string charset; + + if (net::DataURL::Parse(url, &mimeType, &charset, data.get())) { + // PopulateURLResponse from chrome implementation + // weburlloader_impl.cc + m_loadState = Response; + OwnPtr<WebResponse> webResponse(new WebResponse(url.spec(), mimeType, data->size(), charset, 200)); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); + + if (!data->empty()) { + m_loadState = GotData; + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveDataUrl, data.release())); + } + } else { + // handle the failed case + } + + finish(true); +} + +void WebRequest::handleBrowserURL(GURL url) +{ + std::string data("data:text/html;charset=utf-8,"); + if (url.spec() == "browser:incognito") { + AssetManager* assetManager = globalAssetManager(); + Asset* asset = assetManager->open("webkit/incognito_mode_start_page.html", Asset::ACCESS_BUFFER); + if (asset) { + data.append((const char*)asset->getBuffer(false), asset->getLength()); + delete asset; + } + } + GURL dataURL(data.c_str()); + handleDataURL(dataURL); +} + +// Called upon a server-initiated redirect. The delegate may call the +// request's Cancel method to prevent the redirect from being followed. +// Since there may be multiple chained redirects, there may also be more +// than one redirect call. +// +// When this function is called, the request will still contain the +// original URL, the destination of the redirect is provided in 'new_url'. +// If the delegate does not cancel the request and |*defer_redirect| is +// false, then the redirect will be followed, and the request's URL will be +// changed to the new URL. Otherwise if the delegate does not cancel the +// request and |*defer_redirect| is true, then the redirect will be +// followed once FollowDeferredRedirect is called on the URLRequest. +// +// The caller must set |*defer_redirect| to false, so that delegates do not +// need to set it if they are happy with the default behavior of not +// deferring redirect. +void WebRequest::OnReceivedRedirect(URLRequest* newRequest, const GURL& newUrl, bool* deferRedirect) +{ + ASSERT(m_loadState < Response, "Redirect after receiving response"); + ASSERT(newRequest && newRequest->status().is_success(), "Invalid redirect"); + + OwnPtr<WebResponse> webResponse(new WebResponse(newRequest)); + webResponse->setUrl(newUrl.spec()); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::willSendRequest, webResponse.release())); + + // Defer the redirect until followDeferredRedirect() is called. + *deferRedirect = true; +} + +// Called when we receive an authentication failure. The delegate should +// call request->SetAuth() with the user's credentials once it obtains them, +// or request->CancelAuth() to cancel the login and display the error page. +// When it does so, the request will be reissued, restarting the sequence +// of On* callbacks. +void WebRequest::OnAuthRequired(URLRequest* request, net::AuthChallengeInfo* authInfo) +{ + ASSERT(m_loadState == Started, "OnAuthRequired called on a WebRequest not in STARTED state (state=%d)", m_loadState); + + scoped_refptr<net::AuthChallengeInfo> authInfoPtr(authInfo); + bool firstTime = (m_authRequestCount == 0); + ++m_authRequestCount; + + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr, firstTime)); +} + +// Called when we received an SSL certificate error. The delegate will provide +// the user the options to proceed, cancel, or view certificates. +void WebRequest::OnSSLCertificateError(URLRequest* request, int cert_error, net::X509Certificate* cert) +{ + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::reportSslCertError, cert_error, cert)); +} + +// After calling Start(), the delegate will receive an OnResponseStarted +// callback when the request has completed. If an error occurred, the +// request->status() will be set. On success, all redirects have been +// followed and the final response is beginning to arrive. At this point, +// meta data about the response is available, including for example HTTP +// response headers if this is a request for a HTTP resource. +void WebRequest::OnResponseStarted(URLRequest* request) +{ + ASSERT(m_loadState == Started, "Got response after receiving response"); + + m_loadState = Response; + if (request && request->status().is_success()) { + OwnPtr<WebResponse> webResponse(new WebResponse(request)); + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveResponse, webResponse.release())); + + // Start reading the response + startReading(); + } else { + finish(false); + } +} + +void WebRequest::setAuth(const string16& username, const string16& password) +{ + ASSERT(m_loadState == Started, "setAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState); + + m_request->SetAuth(username, password); +} + +void WebRequest::cancelAuth() +{ + ASSERT(m_loadState == Started, "cancelAuth called on a WebRequest not in STARTED state (state=%d)", m_loadState); + + m_request->CancelAuth(); +} + +void WebRequest::followDeferredRedirect() +{ + ASSERT(m_loadState < Response, "Redirect after receiving response"); + + m_request->FollowDeferredRedirect(); +} + +void WebRequest::proceedSslCertError() +{ + m_request->ContinueDespiteLastError(); +} + +void WebRequest::cancelSslCertError(int cert_error) +{ + m_request->SimulateError(cert_error); +} + +void WebRequest::startReading() +{ + ASSERT(m_networkBuffer == 0, "startReading called with a nonzero buffer"); + ASSERT(m_isPaused == 0, "startReading called in paused state"); + ASSERT(m_loadState == Response || m_loadState == GotData, "StartReading in state other than RESPONSE and GOTDATA"); + if (m_loadState > GotData) // We have been cancelled between reads + return; + + if (m_wantToPause) { + m_isPaused = true; + return; + } + + int bytesRead = 0; + + if (!read(&bytesRead)) { + if (m_request && m_request->status().is_io_pending()) + return; // Wait for OnReadCompleted() + return finish(false); + } + + // bytesRead == 0 indicates finished + if (!bytesRead) + return finish(true); + + m_loadState = GotData; + // Read ok, forward buffer to webcore + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod(m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead)); + m_networkBuffer = 0; + MessageLoop::current()->PostTask(FROM_HERE, m_runnableFactory.NewRunnableMethod(&WebRequest::startReading)); +} + +bool WebRequest::read(int* bytesRead) +{ + ASSERT(m_loadState == Response || m_loadState == GotData, "read in state other than RESPONSE and GOTDATA"); + ASSERT(m_networkBuffer == 0, "Read called with a nonzero buffer"); + + // TODO: when asserts work, check that the buffer is 0 here + m_networkBuffer = new net::IOBuffer(kInitialReadBufSize); + return m_request->Read(m_networkBuffer, kInitialReadBufSize, bytesRead); +} + +// This is called when there is data available + +// Called when the a Read of the response body is completed after an +// IO_PENDING status from a Read() call. +// The data read is filled into the buffer which the caller passed +// to Read() previously. +// +// If an error occurred, request->status() will contain the error, +// and bytes read will be -1. +void WebRequest::OnReadCompleted(URLRequest* request, int bytesRead) +{ + ASSERT(m_loadState == Response || m_loadState == GotData, "OnReadCompleted in state other than RESPONSE and GOTDATA"); + + if (request->status().is_success()) { + m_loadState = GotData; + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( + m_urlLoader.get(), &WebUrlLoaderClient::didReceiveData, m_networkBuffer, bytesRead)); + m_networkBuffer = 0; + + // Get the rest of the data + startReading(); + } else { + finish(false); + } +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebRequest.h b/Source/WebKit/android/WebCoreSupport/WebRequest.h new file mode 100644 index 0000000..252267b --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebRequest.h @@ -0,0 +1,125 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebRequest_h +#define WebRequest_h + +#include "ChromiumIncludes.h" +#include <wtf/Vector.h> + +class MessageLoop; + +namespace android { + +enum LoadState { + Created, + Started, + Response, + GotData, + Cancelled, + Finished, + Deleted +}; + +class UrlInterceptResponse; +class WebFrame; +class WebRequestContext; +class WebResourceRequest; +class WebUrlLoaderClient; + +// All methods in this class must be called on the io thread +class WebRequest : public URLRequest::Delegate, public base::RefCountedThreadSafe<WebRequest> { +public: + WebRequest(WebUrlLoaderClient*, const WebResourceRequest&); + + // If this is an android specific url or the application wants to load + // custom data, we load the data through an input stream. + // Used for: + // - file:///android_asset + // - file:///android_res + // - content:// + WebRequest(WebUrlLoaderClient*, const WebResourceRequest&, UrlInterceptResponse* intercept); + + // Optional, but if used has to be called before start + void appendBytesToUpload(Vector<char>* data); + void appendFileToUpload(const std::string& filename); + + void setRequestContext(WebRequestContext* context); + void start(); + void cancel(); + void pauseLoad(bool pause); + + // From URLRequest::Delegate + virtual void OnReceivedRedirect(URLRequest*, const GURL&, bool* deferRedirect); + virtual void OnResponseStarted(URLRequest*); + virtual void OnReadCompleted(URLRequest*, int bytesRead); + virtual void OnAuthRequired(URLRequest*, net::AuthChallengeInfo*); + virtual void OnSSLCertificateError(URLRequest* request, int cert_error, net::X509Certificate* cert); + + // Methods called during a request by the UI code (via WebUrlLoaderClient). + void setAuth(const string16& username, const string16& password); + void cancelAuth(); + void followDeferredRedirect(); + void proceedSslCertError(); + void cancelSslCertError(int cert_error); + + const std::string& getUrl() const; + const std::string& getUserAgent() const; + + void setSync(bool sync) { m_isSync = sync; } +private: + void startReading(); + bool read(int* bytesRead); + + friend class base::RefCountedThreadSafe<WebRequest>; + virtual ~WebRequest(); + void handleDataURL(GURL); + void handleBrowserURL(GURL); + void handleInterceptedURL(); + void finish(bool success); + void updateLoadFlags(int& loadFlags); + + scoped_refptr<WebUrlLoaderClient> m_urlLoader; + OwnPtr<URLRequest> m_request; + scoped_refptr<net::IOBuffer> m_networkBuffer; + scoped_ptr<UrlInterceptResponse> m_interceptResponse; + bool m_androidUrl; + std::string m_url; + std::string m_userAgent; + LoadState m_loadState; + int m_authRequestCount; + int m_cacheMode; + ScopedRunnableMethodFactory<WebRequest> m_runnableFactory; + bool m_wantToPause; + bool m_isPaused; + bool m_isSync; +#ifdef LOG_REQUESTS + time_t m_startTime; +#endif +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp b/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp new file mode 100644 index 0000000..78c3501 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebRequestContext.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebRequestContext.h" + +#include "ChromiumIncludes.h" +#include "ChromiumInit.h" +#include "WebCache.h" +#include "WebCookieJar.h" + +#include <wtf/text/CString.h> + +static std::string acceptLanguageStdString(""); +static WTF::String acceptLanguageWtfString(""); +static WTF::Mutex acceptLanguageMutex; + +static int numPrivateBrowsingInstances; + +extern void ANPSystemInterface_CleanupIncognito(); + +using namespace WTF; + +namespace android { + +WebRequestContext::WebRequestContext(bool isPrivateBrowsing) + : m_isPrivateBrowsing(isPrivateBrowsing) +{ + // Initialize chromium logging, needs to be done before any chromium code is called. + initChromium(); + + if (m_isPrivateBrowsing) { + // Delete the old files if this is the first private browsing instance + // They are probably leftovers from a power cycle + // We do not need to clear the cache as it is in memory only for private browsing + if (!numPrivateBrowsingInstances) + WebCookieJar::cleanup(true); + numPrivateBrowsingInstances++; + } + + WebCache* cache = WebCache::get(m_isPrivateBrowsing); + host_resolver_ = cache->hostResolver(); + http_transaction_factory_ = cache->cache(); + + WebCookieJar* cookieJar = WebCookieJar::get(m_isPrivateBrowsing); + cookie_store_ = cookieJar->cookieStore(); + cookie_policy_ = cookieJar; + + // Also hardcoded in FrameLoader.java + accept_charset_ = "utf-8, iso-8859-1, utf-16, *;q=0.7"; +} + +WebRequestContext::~WebRequestContext() +{ + if (m_isPrivateBrowsing) { + numPrivateBrowsingInstances--; + + // This is the last private browsing context, delete the cookies and cache + if (!numPrivateBrowsingInstances) { + WebCookieJar::cleanup(true); + WebCache::cleanup(true); + ANPSystemInterface_CleanupIncognito(); + } + } +} + +void WebRequestContext::setUserAgent(const String& string) +{ + MutexLocker lock(m_userAgentMutex); + m_userAgent = string.utf8().data(); +} + +void WebRequestContext::setCacheMode(int mode) +{ + m_cacheMode = mode; +} + +int WebRequestContext::getCacheMode() +{ + return m_cacheMode; +} + +const std::string& WebRequestContext::GetUserAgent(const GURL& url) const +{ + MutexLocker lock(m_userAgentMutex); + return m_userAgent; +} + +void WebRequestContext::setAcceptLanguage(const String& string) +{ + MutexLocker lock(acceptLanguageMutex); + acceptLanguageStdString = string.utf8().data(); + acceptLanguageWtfString = string; +} + +const std::string& WebRequestContext::GetAcceptLanguage() const +{ + MutexLocker lock(acceptLanguageMutex); + return acceptLanguageStdString; +} + +const String& WebRequestContext::acceptLanguage() +{ + MutexLocker lock(acceptLanguageMutex); + return acceptLanguageWtfString; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebRequestContext.h b/Source/WebKit/android/WebCoreSupport/WebRequestContext.h new file mode 100644 index 0000000..51f0514 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebRequestContext.h @@ -0,0 +1,64 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebRequestContext_h +#define WebRequestContext_h + +#include "ChromiumIncludes.h" +#include "PlatformString.h" + +#include <wtf/ThreadingPrimitives.h> + +namespace android { + +// This class is generally not threadsafe. +class WebRequestContext : public URLRequestContext { +public: + // URLRequestContext overrides. + virtual const std::string& GetUserAgent(const GURL&) const; + virtual const std::string& GetAcceptLanguage() const; + + WebRequestContext(bool isPrivateBrowsing); + + // These methods are threadsafe. + void setUserAgent(const WTF::String&); + void setCacheMode(int); + int getCacheMode(); + static void setAcceptLanguage(const WTF::String&); + static const WTF::String& acceptLanguage(); + +private: + WebRequestContext(); + ~WebRequestContext(); + + std::string m_userAgent; + int m_cacheMode; + mutable WTF::Mutex m_userAgentMutex; + bool m_isPrivateBrowsing; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp new file mode 100644 index 0000000..9b70fce --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebResourceRequest.h" + +#include "ResourceRequest.h" + +#include <wtf/text/CString.h> + +using namespace WebCore; + +namespace android { + +WebResourceRequest::WebResourceRequest(const WebCore::ResourceRequest& resourceRequest) +{ + // Set the load flags based on the WebCore request. + m_loadFlags = net::LOAD_NORMAL; + switch (resourceRequest.cachePolicy()) { + case ReloadIgnoringCacheData: + m_loadFlags |= net::LOAD_VALIDATE_CACHE; + break; + case ReturnCacheDataElseLoad: + m_loadFlags |= net::LOAD_PREFERRING_CACHE; + break; + case ReturnCacheDataDontLoad: + m_loadFlags |= net::LOAD_ONLY_FROM_CACHE; + break; + case UseProtocolCachePolicy: + break; + } + + // TODO: We should consider setting these flags and net::LOAD_DO_NOT_SEND_AUTH_DATA + // when FrameLoaderClient::shouldUseCredentialStorage() is false. However, + // the required WebKit logic is not yet in place. See Chromium's + // FrameLoaderClientImpl::shouldUseCredentialStorage(). + if (!resourceRequest.allowCookies()) { + m_loadFlags |= net::LOAD_DO_NOT_SAVE_COOKIES; + m_loadFlags |= net::LOAD_DO_NOT_SEND_COOKIES; + } + + + // Set the request headers + const HTTPHeaderMap& map = resourceRequest.httpHeaderFields(); + for (HTTPHeaderMap::const_iterator it = map.begin(); it != map.end(); ++it) { + const std::string& nameUtf8 = it->first.string().utf8().data(); + const std::string& valueUtf8 = it->second.utf8().data(); + + // Skip over referrer headers found in the header map because we already + // pulled it out as a separate parameter. We likewise prune the UA since + // that will be added back by the network layer. + if (LowerCaseEqualsASCII(nameUtf8, "referer") || LowerCaseEqualsASCII(nameUtf8, "user-agent")) + continue; + + // Skip over "Cache-Control: max-age=0" header if the corresponding + // load flag is already specified. FrameLoader sets both the flag and + // the extra header -- the extra header is redundant since our network + // implementation will add the necessary headers based on load flags. + // See http://code.google.com/p/chromium/issues/detail?id=3434. + if ((m_loadFlags & net::LOAD_VALIDATE_CACHE) && + LowerCaseEqualsASCII(nameUtf8, "cache-control") && LowerCaseEqualsASCII(valueUtf8, "max-age=0")) + continue; + + m_requestHeaders.SetHeader(nameUtf8, valueUtf8); + } + + m_method = resourceRequest.httpMethod().utf8().data(); + m_referrer = resourceRequest.httpReferrer().utf8().data(); + m_userAgent = resourceRequest.httpUserAgent().utf8().data(); + + m_url = resourceRequest.url().string().utf8().data(); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebResourceRequest.h b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.h new file mode 100644 index 0000000..38f37b5 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebResourceRequest.h @@ -0,0 +1,86 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebResourceRequest_h +#define WebResourceRequest_h + +#include "ChromiumIncludes.h" + +#include <string> + +namespace WebCore { +class ResourceRequest; +} + +namespace android { + +class WebResourceRequest { + +public: + WebResourceRequest(const WebCore::ResourceRequest&); + + const std::string& method() const + { + return m_method; + } + + const std::string& referrer() const + { + return m_referrer; + } + + const std::string& userAgent() const + { + return m_userAgent; + } + + const net::HttpRequestHeaders& requestHeaders() const + { + return m_requestHeaders; + } + + const std::string& url() const + { + return m_url; + } + + int loadFlags() const + { + return m_loadFlags; + } + +private: + std::string m_method; + std::string m_referrer; + std::string m_userAgent; + net::HttpRequestHeaders m_requestHeaders; + std::string m_url; + int m_loadFlags; +}; + +} // namespace android + + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebResponse.cpp b/Source/WebKit/android/WebCoreSupport/WebResponse.cpp new file mode 100644 index 0000000..4d297d7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebResponse.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebResponse.h" + +#include "MIMETypeRegistry.h" +#include "PlatformString.h" +#include "ResourceResponse.h" +#include "ResourceError.h" + +#include <wtf/text/CString.h> + +using namespace std; + +namespace android { + +WebResponse::WebResponse(URLRequest* request) + : m_httpStatusCode(0) +{ + // The misleadingly-named os_error() is actually a net::Error enum constant. + m_error = net::Error(request->status().os_error()); + + m_url = request->url().spec(); + m_host = request->url().HostNoBrackets(); + request->GetMimeType(&m_mime); + + request->GetCharset(&m_encoding); + m_expectedSize = request->GetExpectedContentSize(); + + m_sslInfo = request->ssl_info(); + + net::HttpResponseHeaders* responseHeaders = request->response_headers(); + if (!responseHeaders) + return; + + m_httpStatusCode = responseHeaders->response_code(); + m_httpStatusText = responseHeaders->GetStatusText(); + + string value; + string name; + void* iter = 0; + while (responseHeaders->EnumerateHeaderLines(&iter, &name, &value)) + m_headerFields[name] = value; +} + +WebResponse::WebResponse(const string &url, const string &mimeType, long long expectedSize, const string &encoding, int httpStatusCode) + : m_error(net::OK) + , m_encoding(encoding) + , m_httpStatusCode(httpStatusCode) + , m_expectedSize(expectedSize) + , m_mime(mimeType) + , m_url(url) +{ +} + +WebCore::ResourceResponse WebResponse::createResourceResponse() +{ + WebCore::ResourceResponse resourceResponse(createKurl(), getMimeType().c_str(), m_expectedSize, m_encoding.c_str(), ""); + resourceResponse.setHTTPStatusCode(m_httpStatusCode); + resourceResponse.setHTTPStatusText(m_httpStatusText.c_str()); + + map<string, string>::const_iterator it; + for (it = m_headerFields.begin(); it != m_headerFields.end(); ++it) + resourceResponse.setHTTPHeaderField(it->first.c_str(), it->second.c_str()); + + return resourceResponse; +} + +WebCore::ResourceError WebResponse::createResourceError() +{ + WebCore::ResourceError error(m_host.c_str(), ToWebViewClientError(m_error), m_url.c_str(), WTF::String()); + return error; +} + + +WebCore::KURL WebResponse::createKurl() +{ + WebCore::KURL kurl(WebCore::ParsedURLString, m_url.c_str()); + return kurl; +} + +const string& WebResponse::getUrl() const +{ + return m_url; +} + +void WebResponse::setUrl(const string& url) +{ + m_url = url; +} + +// Calls WebCore APIs so should only be called from the WebCore thread. +// TODO: can we return a WTF::String directly? Need to check all callsites. +const string& WebResponse::getMimeType() +{ + if (!m_url.length()) + return m_mime; + + if (!m_mime.length() || !m_mime.compare("text/plain") || !m_mime.compare("application/octet-stream")) + m_mime = resolveMimeType(m_url, m_mime); + + return m_mime; +} + +const string WebResponse::resolveMimeType(const string& url, const string& old_mime) +{ + // Use "text/html" as a default (matching the behaviour of the Apache + // HTTP stack -- see guessMimeType() in LoadListener.java). + string mimeType = old_mime.length() ? old_mime : "text/html"; + // Try to guess a better MIME type from the URL. We call + // getMIMETypeForExtension rather than getMIMETypeForPath because the + // latter defaults to "application/octet-stream" on failure. + WebCore::KURL kurl(WebCore::ParsedURLString, url.c_str()); + WTF::String path = kurl.path(); + size_t extensionPos = path.reverseFind('.'); + if (extensionPos != WTF::notFound) { + // We found a file extension. + path.remove(0, extensionPos + 1); + // TODO: Should use content-disposition instead of url if it is there + WTF::String mime = WebCore::MIMETypeRegistry::getMIMETypeForExtension(path); + if (!mime.isEmpty()) { + // Great, we found a MIME type. + mimeType = std::string(mime.utf8().data(), mime.length()); + } + } + return mimeType; +} + +bool WebResponse::getHeader(const string& header, string* result) const +{ + map<string, string>::const_iterator iter = m_headerFields.find(header); + if (iter == m_headerFields.end()) + return false; + if (result) + *result = iter->second; + return true; +} + +long long WebResponse::getExpectedSize() const +{ + return m_expectedSize; +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebResponse.h b/Source/WebKit/android/WebCoreSupport/WebResponse.h new file mode 100644 index 0000000..88c8917 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebResponse.h @@ -0,0 +1,91 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebResponse_h +#define WebResponse_h + +#include "ChromiumIncludes.h" +#include "KURL.h" +#include "WebViewClientError.h" + +#include <map> +#include <string> + +namespace WebCore { +class ResourceResponse; +class ResourceError; +} + +namespace android { + +class WebResponse { + +public: + WebResponse() {} + WebResponse(URLRequest*); + WebResponse(const std::string &url, const std::string &mimeType, long long expectedSize, const std::string &encoding, int httpStatusCode); + + const std::string& getUrl() const; + void setUrl(const std::string&); + + const std::string& getMimeType(); // Use only on WebCore thread. + bool getHeader(const std::string& header, std::string* result) const; + long long getExpectedSize() const; + + const net::SSLInfo& getSslInfo() const { return m_sslInfo; } + + // The create() methods create WebCore objects. They must only be called on the WebKit thread. + WebCore::KURL createKurl(); + WebCore::ResourceResponse createResourceResponse(); + WebCore::ResourceError createResourceError(); + + static const std::string resolveMimeType(const std::string& url, const std::string& old_mime); + +private: + net::Error m_error; + std::string m_encoding; + int m_httpStatusCode; + std::string m_host; + std::string m_httpStatusText; + long long m_expectedSize; + std::string m_mime; + std::string m_url; + net::SSLInfo m_sslInfo; + + struct CaseInsensitiveLessThan { + bool operator()(const std::string& lhs, const std::string& rhs) const { + return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; + } + }; + + // Header fields are case insensitive, so we use a case-insensitive map. + // See RFC 822, 3.4.7, "CASE INDEPENDENCE". + std::map<std::string, std::string, CaseInsensitiveLessThan> m_headerFields; + +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp new file mode 100644 index 0000000..0c90bc5 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "WebUrlLoader.h" + +#include "FrameLoaderClientAndroid.h" +#include "WebCoreFrameBridge.h" +#include "WebUrlLoaderClient.h" + +namespace android { + +// on main thread +WebUrlLoader::WebUrlLoader(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest) +{ + m_loaderClient = new WebUrlLoaderClient(webFrame, resourceHandle, resourceRequest); +} + +// on main thread +WebUrlLoader::~WebUrlLoader() +{ +} + +PassRefPtr<WebUrlLoader> WebUrlLoader::start(FrameLoaderClient* client, WebCore::ResourceHandle* resourceHandle, + const WebCore::ResourceRequest& resourceRequest, bool isMainResource, bool isMainFrame, bool isSync, WebRequestContext* context) +{ + FrameLoaderClientAndroid* androidClient = static_cast<FrameLoaderClientAndroid*>(client); + WebFrame* webFrame = androidClient->webFrame(); + + if (webFrame->blockNetworkLoads() && + (resourceRequest.url().protocolIs("http") || + resourceRequest.url().protocolIs("https"))) + return NULL; + + webFrame->maybeSavePassword(androidClient->getFrame(), resourceRequest); + + RefPtr<WebUrlLoader> loader = WebUrlLoader::create(webFrame, resourceHandle, resourceRequest); + loader->m_loaderClient->start(isMainResource, isMainFrame, isSync, context); + + return loader.release(); +} + +PassRefPtr<WebUrlLoader> WebUrlLoader::create(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest) +{ + return adoptRef(new WebUrlLoader(webFrame, resourceHandle, resourceRequest)); +} + +// on main thread +void WebUrlLoader::cancel() +{ + m_loaderClient->cancel(); +} + +void WebUrlLoader::downloadFile() +{ + m_loaderClient->downloadFile(); +} + +void WebUrlLoader::pauseLoad(bool pause) +{ + m_loaderClient->pauseLoad(pause); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h new file mode 100644 index 0000000..dd88e73 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoader.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebUrlLoader_h +#define WebUrlLoader_h + +#include "ChromiumIncludes.h" +#include "ResourceLoaderAndroid.h" + +using namespace WebCore; + +namespace android { +class WebUrlLoaderClient; +class WebFrame; +class WebRequestContext; + +class WebUrlLoader : public ResourceLoaderAndroid { +public: + virtual ~WebUrlLoader(); + static PassRefPtr<WebUrlLoader> start(FrameLoaderClient* client, WebCore::ResourceHandle*, const WebCore::ResourceRequest&, bool isMainResource, bool isMainFrame, bool sync, WebRequestContext*); + + virtual void cancel(); + virtual void downloadFile(); + virtual void pauseLoad(bool pause); + +private: + WebUrlLoader(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&); + static PassRefPtr<WebUrlLoader> create(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&); + + scoped_refptr<WebUrlLoaderClient> m_loaderClient; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp new file mode 100644 index 0000000..cf218e7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebUrlLoaderClient" + +#include "config.h" +#include "WebUrlLoaderClient.h" + +#include "ChromiumIncludes.h" +#include "OwnPtr.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceResponse.h" +#include "WebCoreFrameBridge.h" +#include "WebRequest.h" +#include "WebResourceRequest.h" + +#include <wtf/text/CString.h> + +namespace android { + +base::Thread* WebUrlLoaderClient::ioThread() +{ + static base::Thread* networkThread = 0; + static Lock networkThreadLock; + + // Multiple threads appear to access the ioThread so we must ensure the + // critical section ordering. + AutoLock lock(networkThreadLock); + + if (!networkThread) + networkThread = new base::Thread("network"); + + if (!networkThread) + return 0; + + if (networkThread->IsRunning()) + return networkThread; + + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + if (!networkThread->StartWithOptions(options)) { + delete networkThread; + networkThread = 0; + } + + return networkThread; +} + +Lock* WebUrlLoaderClient::syncLock() { + static Lock s_syncLock; + return &s_syncLock; +} + +ConditionVariable* WebUrlLoaderClient::syncCondition() { + static ConditionVariable s_syncCondition(syncLock()); + return &s_syncCondition; +} + +WebUrlLoaderClient::~WebUrlLoaderClient() +{ +} + +bool WebUrlLoaderClient::isActive() const +{ + if (m_cancelling) + return false; + if (!m_resourceHandle) + return false; + if (!m_resourceHandle->client()) + return false; + if (m_finished) + return false; + + return true; +} + +WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest) + : m_webFrame(webFrame) + , m_resourceHandle(resourceHandle) + , m_isMainResource(false) + , m_isMainFrame(false) + , m_isCertMimeType(false) + , m_cancelling(false) + , m_sync(false) + , m_finished(false) +{ + WebResourceRequest webResourceRequest(resourceRequest); + UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string()); + if (intercept) { + m_request = new WebRequest(this, webResourceRequest, intercept); + return; + } + + m_request = new WebRequest(this, webResourceRequest); + + // Set uploads before start is called on the request + if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) { + Vector<FormDataElement>::iterator iter; + Vector<FormDataElement> elements = resourceRequest.httpBody()->elements(); + for (iter = elements.begin(); iter != elements.end(); iter++) { + FormDataElement element = *iter; + + switch (element.m_type) { + case FormDataElement::data: + if (!element.m_data.isEmpty()) { + // WebKit sometimes gives up empty data to append. These aren't + // necessary so we just optimize those out here. + base::Thread* thread = ioThread(); + if (thread) { + Vector<char>* data = new Vector<char>(element.m_data); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data)); + } + } + break; + case FormDataElement::encodedFile: + { + // Chromium check if it is a directory by checking + // element.m_fileLength, that doesn't work in Android + std::string filename = element.m_filename.utf8().data(); + if (filename.size()) { + // Change from a url string to a filename + if (filename.find("file://") == 0) // Found at pos 0 + filename.erase(0, 7); + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename)); + } + } + break; +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + LOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob"); + break; +#endif // ENABLE(BLOB) + default: + LOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp"); + break; + } + } + } +} + +bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context) +{ + base::Thread* thread = ioThread(); + if (!thread) { + return false; + } + + m_isMainResource = isMainResource; + m_isMainFrame = isMainFrame; + m_sync = sync; + if (m_sync) { + AutoLock autoLock(*syncLock()); + m_request->setSync(sync); + m_request->setRequestContext(context); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start)); + + // Run callbacks until the queue is exhausted and m_finished is true. + // Sometimes, a sync load can wait forever and lock up the WebCore thread, + // here we use TimedWait() with multiple tries to avoid locking. + const int kMaxNumTimeout = 3; + const int kCallbackWaitingTime = 10; + int num_timeout = 0; + while(!m_finished) { + while (!m_queue.empty()) { + OwnPtr<Task> task(m_queue.front()); + m_queue.pop_front(); + task->Run(); + } + if (m_finished) break; + + syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime)); + if (m_queue.empty()) { + LOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s", + kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str()); + num_timeout++; + if (num_timeout >= kMaxNumTimeout) { + cancel(); + m_resourceHandle = 0; + return false; + } + } + } + + // This may be the last reference to us, so we may be deleted now. + // Don't access any more member variables after releasing this reference. + m_resourceHandle = 0; + } else { + // Asynchronous start. + // Important to set this before the thread starts so it has a reference and can't be deleted + // before the task starts running on the IO thread. + m_request->setRequestContext(context); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start)); + } + return true; +} + +namespace { +// Check if the mime type is for certificate installation. +// The items must be consistent with the sCertificateTypeMap +// in frameworks/base/core/java/android/webkit/CertTool.java. +bool isMimeTypeForCert(const std::string& mimeType) +{ + static std::hash_set<std::string> sCertificateTypeSet; + if (sCertificateTypeSet.empty()) { + sCertificateTypeSet.insert("application/x-x509-ca-cert"); + sCertificateTypeSet.insert("application/x-x509-user-cert"); + sCertificateTypeSet.insert("application/x-pkcs12"); + } + return sCertificateTypeSet.find(mimeType) != sCertificateTypeSet.end(); +} +} + +void WebUrlLoaderClient::downloadFile() +{ + if (m_response) { + std::string contentDisposition; + m_response->getHeader("content-disposition", &contentDisposition); + m_webFrame->downloadStart(m_response->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_response->getExpectedSize()); + + m_isCertMimeType = isMimeTypeForCert(m_response->getMimeType()); + // Currently, only certificate mime type needs to receive the data. + // Other mime type, e.g. wav, will send the url to other application + // which will load the data by url. + if (!m_isCertMimeType) + cancel(); + } else { + LOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str()); + // TODO: Turn off asserts crashing before release + // http://b/issue?id=2951985 + CRASH(); + } +} + +void WebUrlLoaderClient::cancel() +{ + if (!isActive()) + return; + + m_cancelling = true; + + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel)); +} + +void WebUrlLoaderClient::pauseLoad(bool pause) +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::pauseLoad, pause)); +} + +void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password) +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (!thread) { + return; + } + string16 username16 = ASCIIToUTF16(username); + string16 password16 = ASCIIToUTF16(password); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16)); +} + +void WebUrlLoaderClient::cancelAuth() +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (!thread) { + return; + } + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth)); +} + +void WebUrlLoaderClient::proceedSslCertError() +{ + base::Thread* thread = ioThread(); + if (isActive() && thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError)); + this->Release(); +} + +void WebUrlLoaderClient::cancelSslCertError(int cert_error) +{ + base::Thread* thread = ioThread(); + if (isActive() && thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error)); + this->Release(); +} + + +void WebUrlLoaderClient::finish() +{ + m_finished = true; + if (!m_sync) { + // This is the last reference to us, so we will be deleted now. + // We only release the reference here if start() was called asynchronously! + m_resourceHandle = 0; + } + m_request = 0; +} + +namespace { +// Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*. +static void RunTask(void* v) { + OwnPtr<Task> task(static_cast<Task*>(v)); + task->Run(); +} +} + +// This is called from the IO thread, and dispatches the callback to the main thread. +void WebUrlLoaderClient::maybeCallOnMainThread(Task* task) +{ + if (m_sync) { + AutoLock autoLock(*syncLock()); + if (m_queue.empty()) { + syncCondition()->Broadcast(); + } + m_queue.push_back(task); + } else { + // Let WebKit handle it. + callOnMainThread(RunTask, task); + } +} + +// Response methods +void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse) +{ + if (!isActive()) + return; + + m_response = webResponse; + m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse()); + + // Set the main page's certificate to WebView. + if (m_isMainResource && m_isMainFrame) { + const net::SSLInfo& ssl_info = m_response->getSslInfo(); + if (ssl_info.is_valid()) { + std::vector<std::string> chain_bytes; + ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes); + m_webFrame->setCertificate(chain_bytes[0]); + } + + // Look for X-Auto-Login on the main resource to log in the user. + std::string login; + if (m_response->getHeader("x-auto-login", &login)) + m_webFrame->autoLogin(login); + } +} + +void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size) +{ + if (m_isMainResource && m_isCertMimeType) { + m_webFrame->didReceiveData(buf->data(), size); + } + + if (!isActive() || !size) + return; + + // didReceiveData will take a copy of the data + if (m_resourceHandle && m_resourceHandle->client()) + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size); +} + +// For data url's +void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str) +{ + if (!isActive() || !str->size()) + return; + + // didReceiveData will take a copy of the data + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size()); +} + +// For special android files +void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector) +{ + if (!isActive() || !vector->size()) + return; + + // didReceiveData will take a copy of the data + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size()); +} + +void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse) +{ + if (isActive()) + m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError()); + + // Always finish a request, if not it will leak + finish(); +} + +void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse) +{ + if (!isActive()) + return; + + KURL url = webResponse->createKurl(); + OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url)); + m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse()); + + // WebKit may have killed the request. + if (!isActive()) + return; + + // Like Chrome, we only follow the redirect if WebKit left the URL unmodified. + if (url == resourceRequest->url()) { + ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect)); + } else { + cancel(); + } +} + +void WebUrlLoaderClient::didFinishLoading() +{ + if (isActive()) + m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0); + + if (m_isMainResource && m_isCertMimeType) { + m_webFrame->didFinishLoading(); + } + + // Always finish a request, if not it will leak + finish(); +} + +void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime) +{ + if (!isActive()) + return; + + std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port); + std::string realm = base::SysWideToUTF8(authChallengeInfo->realm); + + m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime); +} + +void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert) +{ + if (!isActive()) + return; + + std::vector<std::string> chain_bytes; + cert->GetChainDEREncodedBytes(&chain_bytes); + this->AddRef(); + m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0]); +} + +} // namespace android diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h new file mode 100644 index 0000000..dc101db --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h @@ -0,0 +1,130 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebUrlLoaderClient_h +#define WebUrlLoaderClient_h + +#include "ChromiumIncludes.h" +#include "RefCounted.h" +#include "WebResponse.h" +#include "WebUrlLoader.h" + +#include <string> +#include <deque> +#include <string> +#include <vector> + +class Lock; +class ConditionVariable; + +namespace base { +class Thread; +} + +namespace net { +class IOBuffer; +class AuthChallengeInfo; +} + +namespace android { + +class WebFrame; +class WebRequest; +class WebRequestContext; + +// This class handles communication between the IO thread where loading happens +// and the webkit main thread. +// TODO: +// - Implement didFail +// - Implement sync requests +// - Implement downloadFile +// - Implement pauseLoad +class WebUrlLoaderClient : public base::RefCountedThreadSafe<WebUrlLoaderClient> { +public: + WebUrlLoaderClient(WebFrame*, WebCore::ResourceHandle*, const WebCore::ResourceRequest&); + + // Called from WebCore, will be forwarded to the IO thread + bool start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext*); + void cancel(); + void downloadFile(); + void pauseLoad(bool pause); + void setAuth(const std::string& username, const std::string& password); + void cancelAuth(); + void proceedSslCertError(); + void cancelSslCertError(int cert_error); + + typedef void CallbackFunction(void*); + + // This is called from the IO thread, and dispatches the callback to the main thread. + // (For asynchronous calls, we just delegate to WebKit's callOnMainThread.) + void maybeCallOnMainThread(Task* task); + + // Called by WebRequest (using maybeCallOnMainThread), should be forwarded to WebCore. + void didReceiveResponse(PassOwnPtr<WebResponse>); + void didReceiveData(scoped_refptr<net::IOBuffer>, int size); + void didReceiveDataUrl(PassOwnPtr<std::string>); + void didReceiveAndroidFileData(PassOwnPtr<std::vector<char> >); + void didFinishLoading(); + void didFail(PassOwnPtr<WebResponse>); + void willSendRequest(PassOwnPtr<WebResponse>); + void authRequired(scoped_refptr<net::AuthChallengeInfo>, bool firstTime); + void reportSslCertError(int cert_error, net::X509Certificate* cert); + + // Handle to the chrome IO thread + static base::Thread* ioThread(); + +private: + friend class base::RefCountedThreadSafe<WebUrlLoaderClient>; + virtual ~WebUrlLoaderClient(); + + void finish(); + + WebFrame* m_webFrame; + RefPtr<WebCore::ResourceHandle> m_resourceHandle; + bool m_isMainResource; + bool m_isMainFrame; + bool m_isCertMimeType; + bool m_cancelling; + bool m_sync; + volatile bool m_finished; + + scoped_refptr<WebRequest> m_request; + OwnPtr<WebResponse> m_response; // NULL until didReceiveResponse is called. + + // Check if a request is active + bool isActive() const; + + // Mutex and condition variable used for synchronous requests. + // Note that these are static. This works because there's only one main thread. + static Lock* syncLock(); + static ConditionVariable* syncCondition(); + + // Queue of callbacks to be executed by the main thread. Must only be accessed inside mutex. + std::deque<Task*> m_queue; +}; + +} // namespace android + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp new file mode 100644 index 0000000..59542da --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebViewClientError.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebViewClientError.h" + +using namespace net; + +namespace android { + +WebViewClientError ToWebViewClientError(net::Error error) { + // Note: many net::Error constants don't have an obvious mapping. + // These will be handled by the default case, ERROR_UNKNOWN. + switch(error) { + case ERR_UNSUPPORTED_AUTH_SCHEME: + return ERROR_UNSUPPORTED_AUTH_SCHEME; + + case ERR_INVALID_AUTH_CREDENTIALS: + case ERR_MISSING_AUTH_CREDENTIALS: + case ERR_MISCONFIGURED_AUTH_ENVIRONMENT: + return ERROR_AUTHENTICATION; + + case ERR_TOO_MANY_REDIRECTS: + return ERROR_REDIRECT_LOOP; + + case ERR_UPLOAD_FILE_CHANGED: + return ERROR_FILE_NOT_FOUND; + + case ERR_INVALID_URL: + return ERROR_BAD_URL; + + case ERR_DISALLOWED_URL_SCHEME: + case ERR_UNKNOWN_URL_SCHEME: + return ERROR_UNSUPPORTED_SCHEME; + + case ERR_IO_PENDING: + case ERR_NETWORK_IO_SUSPENDED: + return ERROR_IO; + + case ERR_CONNECTION_TIMED_OUT: + case ERR_TIMED_OUT: + return ERROR_TIMEOUT; + + case ERR_FILE_TOO_BIG: + return ERROR_FILE; + + case ERR_HOST_RESOLVER_QUEUE_TOO_LARGE: + case ERR_INSUFFICIENT_RESOURCES: + case ERR_OUT_OF_MEMORY: + return ERROR_TOO_MANY_REQUESTS; + + case ERR_CONNECTION_CLOSED: + case ERR_CONNECTION_RESET: + case ERR_CONNECTION_REFUSED: + case ERR_CONNECTION_ABORTED: + case ERR_CONNECTION_FAILED: + case ERR_SOCKET_NOT_CONNECTED: + return ERROR_CONNECT; + + case ERR_ADDRESS_INVALID: + case ERR_ADDRESS_UNREACHABLE: + case ERR_NAME_NOT_RESOLVED: + case ERR_NAME_RESOLUTION_FAILED: + return ERROR_HOST_LOOKUP; + + case ERR_SSL_PROTOCOL_ERROR: + case ERR_SSL_CLIENT_AUTH_CERT_NEEDED: + case ERR_TUNNEL_CONNECTION_FAILED: + case ERR_NO_SSL_VERSIONS_ENABLED: + case ERR_SSL_VERSION_OR_CIPHER_MISMATCH: + case ERR_SSL_RENEGOTIATION_REQUESTED: + case ERR_CERT_ERROR_IN_SSL_RENEGOTIATION: + case ERR_BAD_SSL_CLIENT_AUTH_CERT: + case ERR_SSL_NO_RENEGOTIATION: + case ERR_SSL_DECOMPRESSION_FAILURE_ALERT: + case ERR_SSL_BAD_RECORD_MAC_ALERT: + case ERR_SSL_UNSAFE_NEGOTIATION: + case ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: + case ERR_SSL_SNAP_START_NPN_MISPREDICTION: + case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED: + case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY: + return ERROR_FAILED_SSL_HANDSHAKE; + + case ERR_PROXY_AUTH_UNSUPPORTED: + case ERR_PROXY_AUTH_REQUESTED: + case ERR_PROXY_CONNECTION_FAILED: + case ERR_UNEXPECTED_PROXY_AUTH: + return ERROR_PROXY_AUTHENTICATION; + + /* The certificate errors are handled by their own dialog + * and don't need to be reported to the framework again. + */ + case ERR_CERT_COMMON_NAME_INVALID: + case ERR_CERT_DATE_INVALID: + case ERR_CERT_AUTHORITY_INVALID: + case ERR_CERT_CONTAINS_ERRORS: + case ERR_CERT_NO_REVOCATION_MECHANISM: + case ERR_CERT_UNABLE_TO_CHECK_REVOCATION: + case ERR_CERT_REVOKED: + case ERR_CERT_INVALID: + case ERR_CERT_WEAK_SIGNATURE_ALGORITHM: + case ERR_CERT_NOT_IN_DNS: + return ERROR_OK; + + default: + return ERROR_UNKNOWN; + } +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/WebViewClientError.h b/Source/WebKit/android/WebCoreSupport/WebViewClientError.h new file mode 100644 index 0000000..d274dc7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebViewClientError.h @@ -0,0 +1,74 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebViewClientError_h +#define WebViewClientError_h + +#include "ChromiumIncludes.h" + +namespace android { + +// This enum must be kept in sync with WebViewClient.java +enum WebViewClientError { + /** Success */ + ERROR_OK = 0, + /** Generic error */ + ERROR_UNKNOWN = -1, + /** Server or proxy hostname lookup failed */ + ERROR_HOST_LOOKUP = -2, + /** Unsupported authentication scheme (not basic or digest) */ + ERROR_UNSUPPORTED_AUTH_SCHEME = -3, + /** User authentication failed on server */ + ERROR_AUTHENTICATION = -4, + /** User authentication failed on proxy */ + ERROR_PROXY_AUTHENTICATION = -5, + /** Failed to connect to the server */ + ERROR_CONNECT = -6, + /** Failed to read or write to the server */ + ERROR_IO = -7, + /** Connection timed out */ + ERROR_TIMEOUT = -8, + /** Too many redirects */ + ERROR_REDIRECT_LOOP = -9, + /** Unsupported URI scheme */ + ERROR_UNSUPPORTED_SCHEME = -10, + /** Failed to perform SSL handshake */ + ERROR_FAILED_SSL_HANDSHAKE = -11, + /** Malformed URL */ + ERROR_BAD_URL = -12, + /** Generic file error */ + ERROR_FILE = -13, + /** File not found */ + ERROR_FILE_NOT_FOUND = -14, + /** Too many requests during this load */ + ERROR_TOO_MANY_REQUESTS = -15, +}; + +// Get the closest WebViewClient match to the given Chrome error code. +WebViewClientError ToWebViewClientError(net::Error); + +} // namespace android + +#endif // WebViewClientError_h diff --git a/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp new file mode 100644 index 0000000..5bc4c92 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AutoFillHostAndroid.h" + +#include "autofill/WebAutoFill.h" + +namespace android { + +AutoFillHostAndroid::AutoFillHostAndroid(WebAutoFill* autoFill) + : mAutoFill(autoFill) +{ +} + +void AutoFillHostAndroid::AutoFillSuggestionsReturned(const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<string16>& icons, const std::vector<int>& uniqueIds) +{ + // TODO: what do we do with icons? + if (mAutoFill) + mAutoFill->querySuccessful(names[0], labels[0], uniqueIds[0]); +} + +void AutoFillHostAndroid::AutoFillFormDataFilled(int queryId, const webkit_glue::FormData& form) +{ + if (mAutoFill) + mAutoFill->fillFormInPage(queryId, form); +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h new file mode 100644 index 0000000..9677b46 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/AutoFillHostAndroid.h @@ -0,0 +1,54 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AutoFillHostAndroid_h +#define AutoFillHostAndroid_h + +#include "ChromiumIncludes.h" + +#include <vector> + +namespace webkit_glue { +class FormData; +} + +namespace android { +class WebAutoFill; + +// This class receives the callbacks from the AutoFillManager in the Chromium code. +class AutoFillHostAndroid : public AutoFillHost { +public: + AutoFillHostAndroid(WebAutoFill* autoFill); + virtual ~AutoFillHostAndroid() { } + + virtual void AutoFillSuggestionsReturned(const std::vector<string16>& names, const std::vector<string16>& labels, const std::vector<string16>& icons, const std::vector<int>& uniqueIds); + virtual void AutoFillFormDataFilled(int queryId, const webkit_glue::FormData&); + +private: + WebAutoFill* mAutoFill; +}; +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp new file mode 100644 index 0000000..6af0875 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FormFieldAndroid.h" + +#include "ChromiumIncludes.h" +#include "Element.h" +#include "HTMLFormControlElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" +#include "StringUtils.h" +#include <wtf/Vector.h> + +using WebCore::Element; +using WebCore::HTMLFormControlElement; +using WebCore::HTMLInputElement; +using WebCore::HTMLOptionElement; +using WebCore::HTMLSelectElement; + +using namespace WebCore::HTMLNames; + +// TODO: This file is taken from chromium/webkit/glue/form_field.cc and +// customised to use WebCore types rather than WebKit API types. It would +// be nice and would ease future merge pain if the two could be combined. + +namespace webkit_glue { + +FormField::FormField() + : max_length_(0), + is_autofilled_(false) { +} + +// TODO: This constructor should probably be deprecated and the +// functionality moved to FormManager. +FormField::FormField(const HTMLFormControlElement& element) + : max_length_(0), + is_autofilled_(false) { + name_ = nameForAutoFill(element); + + // TODO: Extract the field label. For now we just use the field + // name. + label_ = name_; + + form_control_type_ = formControlType(element); + if (form_control_type_ == kText) { + const HTMLInputElement& input_element = static_cast<const HTMLInputElement&>(element); + value_ = WTFStringToString16(input_element.value()); + max_length_ = input_element.size(); + is_autofilled_ = input_element.isAutofilled(); + } else if (form_control_type_ == kSelectOne) { + const HTMLSelectElement& const_select_element = static_cast<const HTMLSelectElement&>(element); + HTMLSelectElement& select_element = const_cast<HTMLSelectElement&>(const_select_element); + value_ = WTFStringToString16(select_element.value()); + + // For select-one elements copy option strings. + WTF::Vector<Element*> list_items = select_element.listItems(); + option_strings_.reserve(list_items.size()); + for (size_t i = 0; i < list_items.size(); ++i) { + if (list_items[i]->hasTagName(optionTag)) + option_strings_.push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value())); + } + } + + TrimWhitespace(value_, TRIM_LEADING, &value_); +} + +FormField::FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int max_length, bool is_autofilled) + : label_(label), + name_(name), + value_(value), + form_control_type_(form_control_type), + max_length_(max_length), + is_autofilled_(is_autofilled) { +} + +FormField::~FormField() { +} + +bool FormField::operator==(const FormField& field) const { + // A FormField stores a value, but the value is not part of the identity of + // the field, so we don't want to compare the values. + return (label_ == field.label_ && + name_ == field.name_ && + form_control_type_ == field.form_control_type_ && + max_length_ == field.max_length_); +} + +bool FormField::operator!=(const FormField& field) const { + return !operator==(field); +} + +bool FormField::StrictlyEqualsHack(const FormField& field) const { + return (label_ == field.label_ && + name_ == field.name_ && + value_ == field.value_ && + form_control_type_ == field.form_control_type_ && + max_length_ == field.max_length_); +} + +std::ostream& operator<<(std::ostream& os, const FormField& field) { + return os + << UTF16ToUTF8(field.label()) + << " " + << UTF16ToUTF8(field.name()) + << " " + << UTF16ToUTF8(field.value()) + << " " + << UTF16ToUTF8(field.form_control_type()) + << " " + << field.max_length(); +} + +} // namespace webkit_glue diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h new file mode 100644 index 0000000..c5e3ecc --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/FormFieldAndroid.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FormFieldAndroid_h +#define FormFieldAndroid_h + +#include <base/string16.h> +#include <vector> + +// TODO: This file is taken from chromium/webkit/glue/form_field.h and +// customised to use WebCore types rather than WebKit API types. It would +// be nice and would ease future merge pain if the two could be combined. + +namespace WebCore { +class HTMLFormControlElement; +} + +namespace webkit_glue { + +// Stores information about a field in a form. +class FormField { +public: + FormField(); + explicit FormField(const WebCore::HTMLFormControlElement& element); + FormField(const string16& label, const string16& name, const string16& value, const string16& form_control_type, int max_length, bool is_autofilled); + virtual ~FormField(); + + const string16& label() const { return label_; } + const string16& name() const { return name_; } + const string16& value() const { return value_; } + const string16& form_control_type() const { return form_control_type_; } + int max_length() const { return max_length_; } + bool is_autofilled() const { return is_autofilled_; } + + // Returns option string for elements for which they make sense (select-one, + // for example) for the rest of elements return an empty array. + const std::vector<string16>& option_strings() const { return option_strings_; } + + void set_label(const string16& label) { label_ = label; } + void set_name(const string16& name) { name_ = name; } + void set_value(const string16& value) { value_ = value; } + void set_form_control_type(const string16& form_control_type) { form_control_type_ = form_control_type; } + void set_max_length(int max_length) { max_length_ = max_length; } + void set_autofilled(bool is_autofilled) { is_autofilled_ = is_autofilled; } + void set_option_strings(const std::vector<string16>& strings) { option_strings_ = strings; } + + // Equality tests for identity which does not include |value_| or |size_|. + // Use |StrictlyEqualsHack| method to test all members. + // TODO: These operators need to be revised when we implement field + // ids. + bool operator==(const FormField& field) const; + bool operator!=(const FormField& field) const; + + // Test equality of all data members. + // TODO: This will be removed when we implement field ids. + bool StrictlyEqualsHack(const FormField& field) const; + +private: + string16 label_; + string16 name_; + string16 value_; + string16 form_control_type_; + int max_length_; + bool is_autofilled_; + std::vector<string16> option_strings_; +}; + +// So we can compare FormFields with EXPECT_EQ(). +std::ostream& operator<<(std::ostream& os, const FormField& field); + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_FORM_FIELD_H_ diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp new file mode 100644 index 0000000..9652794 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.cpp @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FormManagerAndroid.h" + +#include "DocumentLoader.h" +#include "Element.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTMLCollection.h" +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLLabelElement.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" +#include "Node.h" +#include "NodeList.h" +#include "HTMLCollection.h" +#include "FormAssociatedElement.h" +#include "FormFieldAndroid.h" +#include "QualifiedName.h" +#include "StringUtils.h" + +// TODO: This file is taken from chromium/chrome/renderer/form_manager.cc and +// customised to use WebCore types rather than WebKit API types. It would be +// nice and would ease future merge pain if the two could be combined. + +using webkit_glue::FormData; +using webkit_glue::FormField; +using WebCore::Element; +using WebCore::FormAssociatedElement; +using WebCore::HTMLCollection; +using WebCore::HTMLElement; +using WebCore::HTMLFormControlElement; +using WebCore::HTMLFormElement; +using WebCore::HTMLInputElement; +using WebCore::HTMLLabelElement; +using WebCore::HTMLOptionElement; +using WebCore::HTMLSelectElement; +using WebCore::Node; +using WebCore::NodeList; + +using namespace WebCore::HTMLNames; + +namespace { + +// The number of fields required by AutoFill. Ideally we could send the forms +// to AutoFill no matter how many fields are in the forms; however, finding the +// label for each field is a costly operation and we can't spare the cycles if +// it's not necessary. +// Note the on ANDROID we reduce this from Chromium's 3 as it allows us to +// autofill simple name/email forms for example. This improves the mobile +// device experience where form filling can be time consuming and frustrating. +const size_t kRequiredAutoFillFields = 2; + +// The maximum length allowed for form data. +const size_t kMaxDataLength = 1024; + +// This is a helper function for the FindChildText() function. +// Returns the aggregated values of the descendants or siblings of |node| that +// are non-empty text nodes. This is a faster alternative to |innerText()| for +// performance critical operations. It does a full depth-first search so +// can be used when the structure is not directly known. The text is +// accumulated after the whitespace has been stropped. Search depth is limited +// with the |depth| parameter. +string16 FindChildTextInner(Node* node, int depth) { + string16 element_text; + if (!node || depth <= 0) + return element_text; + + string16 node_text = WTFStringToString16(node->nodeValue()); + TrimWhitespace(node_text, TRIM_ALL, &node_text); + if (!node_text.empty()) + element_text = node_text; + + string16 child_text = FindChildTextInner(node->firstChild(), depth-1); + if (!child_text.empty()) + element_text = element_text + child_text; + + string16 sibling_text = FindChildTextInner(node->nextSibling(), depth-1); + if (!sibling_text.empty()) + element_text = element_text + sibling_text; + + return element_text; +} + +// Returns the node value of the first decendant of |element| that is a +// non-empty text node. "Non-empty" in this case means non-empty after the +// whitespace has been stripped. Search is limited to withing 10 siblings and/or +// descendants. +string16 FindChildText(Element* element) { + Node* child = element->firstChild(); + + const int kChildSearchDepth = 10; + return FindChildTextInner(child, kChildSearchDepth); +} + +string16 InferLabelFromPrevious(const HTMLFormControlElement& element) { + string16 inferred_label; + Node* previous = element.previousSibling(); + if (!previous) + return string16(); + + if (previous->isTextNode()) { + inferred_label = WTFStringToString16(previous->nodeValue()); + TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); + } + + // If we didn't find text, check for previous paragraph. + // Eg. <p>Some Text</p><input ...> + // Note the lack of whitespace between <p> and <input> elements. + if (inferred_label.empty() && previous->isElementNode()) { + Element* element = static_cast<Element*>(previous); + if (element->hasTagName(pTag)) + inferred_label = FindChildText(element); + } + + // If we didn't find paragraph, check for previous paragraph to this. + // Eg. <p>Some Text</p> <input ...> + // Note the whitespace between <p> and <input> elements. + if (inferred_label.empty()) { + Node* sibling = previous->previousSibling(); + if (sibling && sibling->isElementNode()) { + Element* element = static_cast<Element*>(sibling); + if (element->hasTagName(pTag)) + inferred_label = FindChildText(element); + } + } + + // Look for text node prior to <img> tag. + // Eg. Some Text<img/><input ...> + if (inferred_label.empty()) { + while (inferred_label.empty() && previous) { + if (previous->isTextNode()) { + inferred_label = WTFStringToString16(previous->nodeValue()); + TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); + } else if (previous->isElementNode()) { + Element* element = static_cast<Element*>(previous); + if (!element->hasTagName(imgTag)) + break; + } else + break; + previous = previous->previousSibling(); + } + } + + // Look for label node prior to <input> tag. + // Eg. <label>Some Text</label><input ...> + if (inferred_label.empty()) { + while (inferred_label.empty() && previous) { + if (previous->isTextNode()) { + inferred_label = WTFStringToString16(previous->nodeValue()); + TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); + } else if (previous->isElementNode()) { + Element* element = static_cast<Element*>(previous); + if (element->hasTagName(labelTag)) { + inferred_label = FindChildText(element); + } else { + break; + } + } else { + break; + } + + previous = previous->previousSibling(); + } + } + + return inferred_label; +} + +// Helper for |InferLabelForElement()| that infers a label, if possible, from +// surrounding table structure. +// Eg. <tr><td>Some Text</td><td><input ...></td></tr> +// Eg. <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr> +string16 InferLabelFromTable(const HTMLFormControlElement& element) { + string16 inferred_label; + Node* parent = element.parentNode(); + while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(tdTag)) + parent = parent->parentNode(); + + // Check all previous siblings, skipping non-element nodes, until we find a + // non-empty text block. + Node* previous = parent; + while(previous) { + if (previous->isElementNode()) { + Element* e = static_cast<Element*>(previous); + if (e->hasTagName(tdTag)) { + inferred_label = FindChildText(e); + if (!inferred_label.empty()) + break; + } + } + previous = previous->previousSibling(); + } + return inferred_label; +} + +// Helper for |InferLabelForElement()| that infers a label, if possible, from +// a surrounding div table. +// Eg. <div>Some Text<span><input ...></span></div> +string16 InferLabelFromDivTable(const HTMLFormControlElement& element) { + Node* parent = element.parentNode(); + while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(divTag)) + parent = parent->parentNode(); + + if (!parent || !parent->isElementNode()) + return string16(); + + Element* e = static_cast<Element*>(parent); + if (!e || !e->hasTagName(divTag)) + return string16(); + + return FindChildText(e); +} + +// Helper for |InferLabelForElement()| that infers a label, if possible, from +// a surrounding definition list. +// Eg. <dl><dt>Some Text</dt><dd><input ...></dd></dl> +// Eg. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl> +string16 InferLabelFromDefinitionList(const HTMLFormControlElement& element) { + string16 inferred_label; + Node* parent = element.parentNode(); + while (parent && parent->isElementNode() && !static_cast<Element*>(parent)->hasTagName(ddTag)) + parent = parent->parentNode(); + + if (parent && parent->isElementNode()) { + Element* element = static_cast<Element*>(parent); + if (element->hasTagName(ddTag)) { + Node* previous = parent->previousSibling(); + + // Skip by any intervening text nodes. + while (previous && previous->isTextNode()) + previous = previous->previousSibling(); + + if (previous && previous->isElementNode()) { + element = static_cast<Element*>(previous); + if (element->hasTagName(dtTag)) + inferred_label = FindChildText(element); + } + } + } + return inferred_label; +} + +void GetOptionStringsFromElement(HTMLFormControlElement* element, std::vector<string16>* option_strings) { + DCHECK(element); + DCHECK(option_strings); + option_strings->clear(); + if (formControlType(*element) == kSelectOne) { + HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element); + + // For select-one elements copy option strings. + WTF::Vector<Element*> list_items = select_element->listItems(); + option_strings->reserve(list_items.size()); + for (size_t i = 0; i < list_items.size(); ++i) { + if (list_items[i]->hasTagName(optionTag)) + option_strings->push_back(WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value())); + } + } +} + +} // namespace + +namespace android { + +struct FormManager::FormElement { + RefPtr<HTMLFormElement> form_element; + std::vector<RefPtr<HTMLFormControlElement> > control_elements; + std::vector<string16> control_values; +}; + +FormManager::FormManager() { +} + +FormManager::~FormManager() { + Reset(); +} + +// static +void FormManager::HTMLFormControlElementToFormField(HTMLFormControlElement* element, ExtractMask extract_mask, FormField* field) { + DCHECK(field); + + // The label is not officially part of a HTMLFormControlElement; however, the + // labels for all form control elements are scraped from the DOM and set in + // WebFormElementToFormData. + field->set_name(nameForAutoFill(*element)); + field->set_form_control_type(formControlType(*element)); + + if (extract_mask & EXTRACT_OPTIONS) { + std::vector<string16> option_strings; + GetOptionStringsFromElement(element, &option_strings); + field->set_option_strings(option_strings); + } + + if (formControlType(*element) == kText) { + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + field->set_max_length(input_element->maxLength()); + field->set_autofilled(input_element->isAutofilled()); + } + + if (!(extract_mask & EXTRACT_VALUE)) + return; + + // TODO: In WebKit, move value() and setValue() to + // WebFormControlElement. + string16 value; + if (formControlType(*element) == kText || + formControlType(*element) == kHidden) { + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + value = WTFStringToString16(input_element->value()); + } else if (formControlType(*element) == kSelectOne) { + HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element); + value = WTFStringToString16(select_element->value()); + + // Convert the |select_element| value to text if requested. + if (extract_mask & EXTRACT_OPTION_TEXT) { + Vector<Element*> list_items = select_element->listItems(); + for (size_t i = 0; i < list_items.size(); ++i) { + if (list_items[i]->hasTagName(optionTag) && + WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->value()) == value) { + value = WTFStringToString16(static_cast<HTMLOptionElement*>(list_items[i])->text()); + break; + } + } + } + } + + // TODO: This is a temporary stop-gap measure designed to prevent + // a malicious site from DOS'ing the browser with extremely large profile + // data. The correct solution is to parse this data asynchronously. + // See http://crbug.com/49332. + if (value.size() > kMaxDataLength) + value = value.substr(0, kMaxDataLength); + + field->set_value(value); +} + +// static +string16 FormManager::LabelForElement(const HTMLFormControlElement& element) { + // Don't scrape labels for hidden elements. + if (formControlType(element) == kHidden) + return string16(); + + RefPtr<NodeList> labels = element.document()->getElementsByTagName("label"); + for (unsigned i = 0; i < labels->length(); ++i) { + Node* e = labels->item(i); + if (e->hasTagName(labelTag)) { + HTMLLabelElement* label = static_cast<HTMLLabelElement*>(e); + if (label->control() == &element) + return FindChildText(label); + } + } + + // Infer the label from context if not found in label element. + return FormManager::InferLabelForElement(element); +} + +// static +bool FormManager::HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, ExtractMask extract_mask, FormData* form) { + DCHECK(form); + + Frame* frame = element->document()->frame(); + if (!frame) + return false; + + if (requirements & REQUIRE_AUTOCOMPLETE && !element->autoComplete()) + return false; + + form->name = WTFStringToString16(element->name()); + form->method = WTFStringToString16(element->method()); + form->origin = GURL(WTFStringToString16(frame->loader()->documentLoader()->url().string())); + form->action = GURL(WTFStringToString16(frame->document()->completeURL(element->action()))); + form->user_submitted = element->wasUserSubmitted(); + + // If the completed URL is not valid, just use the action we get from + // WebKit. + if (!form->action.is_valid()) + form->action = GURL(WTFStringToString16(element->action())); + + // A map from a FormField's name to the FormField itself. + std::map<string16, FormField*> name_map; + + // The extracted FormFields. We use pointers so we can store them in + // |name_map|. + ScopedVector<FormField> form_fields; + + WTF::Vector<WebCore::FormAssociatedElement*> control_elements = element->associatedElements(); + + // A vector of bools that indicate whether each field in the form meets the + // requirements and thus will be in the resulting |form|. + std::vector<bool> fields_extracted(control_elements.size(), false); + + for (size_t i = 0; i < control_elements.size(); ++i) { + if (!control_elements[i]->isFormControlElement()) + continue; + + HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]); + if (!(control_element->hasTagName(inputTag) || control_element->hasTagName(selectTag))) + continue; + + if (requirements & REQUIRE_AUTOCOMPLETE && + formControlType(*control_element) == kText) { + const WebCore::HTMLInputElement* input_element = static_cast<const WebCore::HTMLInputElement*>(control_element); + if (!input_element->autoComplete()) + continue; + } + + if (requirements & REQUIRE_ENABLED && !control_element->isEnabledFormControl()) + continue; + + // Create a new FormField, fill it out and map it to the field's name. + FormField* field = new FormField; + HTMLFormControlElementToFormField(control_element, extract_mask, field); + form_fields.push_back(field); + // TODO: A label element is mapped to a form control element's id. + // field->name() will contain the id only if the name does not exist. Add + // an id() method to HTMLFormControlElement and use that here. + name_map[field->name()] = field; + fields_extracted[i] = true; + } + + // Don't extract field labels if we have no fields. + if (form_fields.empty()) + return false; + + // Loop through the label elements inside the form element. For each label + // element, get the corresponding form control element, use the form control + // element's name as a key into the <name, FormField> map to find the + // previously created FormField and set the FormField's label to the + // label.firstChild().nodeValue() of the label element. + RefPtr<WebCore::NodeList> labels = element->getElementsByTagName("label"); + for (unsigned i = 0; i < labels->length(); ++i) { + HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(labels->item(i)); + HTMLFormControlElement* field_element = label->control(); + if (!field_element || field_element->type() == "hidden") + continue; + + std::map<string16, FormField*>::iterator iter = + name_map.find(nameForAutoFill(*field_element)); + if (iter != name_map.end()) + iter->second->set_label(FindChildText(label)); + } + + // Loop through the form control elements, extracting the label text from the + // DOM. We use the |fields_extracted| vector to make sure we assign the + // extracted label to the correct field, as it's possible |form_fields| will + // not contain all of the elements in |control_elements|. + for (size_t i = 0, field_idx = 0; i < control_elements.size() && field_idx < form_fields.size(); ++i) { + // This field didn't meet the requirements, so don't try to find a label for + // it. + if (!fields_extracted[i]) + continue; + + if (!control_elements[i]->isFormControlElement()) + continue; + + const HTMLFormControlElement* control_element = static_cast<HTMLFormControlElement*>(control_elements[i]); + if (form_fields[field_idx]->label().empty()) + form_fields[field_idx]->set_label(FormManager::InferLabelForElement(*control_element)); + + ++field_idx; + + } + // Copy the created FormFields into the resulting FormData object. + for (ScopedVector<FormField>::const_iterator iter = form_fields.begin(); iter != form_fields.end(); ++iter) + form->fields.push_back(**iter); + + return true; +} + +void FormManager::ExtractForms(Frame* frame) { + + ResetFrame(frame); + + WTF::PassRefPtr<HTMLCollection> web_forms = frame->document()->forms(); + + for (size_t i = 0; i < web_forms->length(); ++i) { + FormElement* form_element = new FormElement; + HTMLFormElement* html_form_element = static_cast<HTMLFormElement*>(web_forms->item(i)); + form_element->form_element = html_form_element; + + WTF::Vector<FormAssociatedElement*> control_elements = html_form_element->associatedElements(); + for (size_t j = 0; j < control_elements.size(); ++j) { + if (!control_elements[j]->isFormControlElement()) + continue; + + HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(control_elements[j]); + form_element->control_elements.push_back(element); + + // Save original values of "select-one" inputs so we can restore them + // when |ClearFormWithNode()| is invoked. + if (formControlType(*element) == kSelectOne) { + HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element); + string16 value = WTFStringToString16(select_element->value()); + form_element->control_values.push_back(value); + } else + form_element->control_values.push_back(string16()); + } + + form_elements_.push_back(form_element); + } +} + +void FormManager::GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<FormData>* forms) { + DCHECK(frame); + DCHECK(forms); + + for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) { + FormElement* form_element = *form_iter; + + if (form_element->form_element->document()->frame() != frame) + continue; + + // We need at least |kRequiredAutoFillFields| fields before appending this + // form to |forms|. + if (form_element->control_elements.size() < kRequiredAutoFillFields) + continue; + + if (requirements & REQUIRE_AUTOCOMPLETE && !form_element->form_element->autoComplete()) + continue; + + FormData form; + HTMLFormElementToFormData(form_element->form_element.get(), requirements, EXTRACT_VALUE, &form); + if (form.fields.size() >= kRequiredAutoFillFields) + forms->push_back(form); + } +} + +bool FormManager::FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, FormData* form) { + DCHECK(form); + + const Frame* frame = element->document()->frame(); + if (!frame) + return false; + + for (FormElementList::const_iterator iter = form_elements_.begin(); iter != form_elements_.end(); ++iter) { + const FormElement* form_element = *iter; + + if (form_element->form_element->document()->frame() != frame) + continue; + + for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = form_element->control_elements.begin(); iter != form_element->control_elements.end(); ++iter) { + HTMLFormControlElement* candidate = iter->get(); + if (nameForAutoFill(*candidate) == nameForAutoFill(*element)) { + ExtractMask extract_mask = static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS); + return HTMLFormElementToFormData(form_element->form_element.get(), requirements, extract_mask, form); + } + } + } + return false; +} + +bool FormManager::FillForm(const FormData& form, Node* node) { + FormElement* form_element = NULL; + if (!FindCachedFormElement(form, &form_element)) + return false; + + RequirementsMask requirements = static_cast<RequirementsMask>(REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY); + ForEachMatchingFormField(form_element, node, requirements, form, NewCallback(this, &FormManager::FillFormField)); + + return true; +} + +bool FormManager::PreviewForm(const FormData& form, Node* node) { + FormElement* form_element = NULL; + if (!FindCachedFormElement(form, &form_element)) + return false; + + RequirementsMask requirements = static_cast<RequirementsMask>(REQUIRE_AUTOCOMPLETE | REQUIRE_ENABLED | REQUIRE_EMPTY); + ForEachMatchingFormField(form_element, node, requirements, form, NewCallback(this, &FormManager::PreviewFormField)); + + return true; +} + +bool FormManager::ClearFormWithNode(Node* node) { + FormElement* form_element = NULL; + if (!FindCachedFormElementWithNode(node, &form_element)) + return false; + + for (size_t i = 0; i < form_element->control_elements.size(); ++i) { + HTMLFormControlElement* element = form_element->control_elements[i].get(); + if (formControlType(*element) == kText) { + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + + // We don't modify the value of disabled fields. + if (!input_element->isEnabledFormControl()) + continue; + + input_element->setValue(""); + input_element->setAutofilled(false); + // Clearing the value in the focused node (above) can cause selection + // to be lost. We force selection range to restore the text cursor. + if (node == input_element) { + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); + } + } else if (formControlType(*element) == kSelectOne) { + HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(element); + select_element->setValue(form_element->control_values[i].c_str()); + } + } + + return true; +} + +bool FormManager::ClearPreviewedFormWithNode(Node* node, bool was_autofilled) { + FormElement* form_element = NULL; + if (!FindCachedFormElementWithNode(node, &form_element)) + return false; + + for (size_t i = 0; i < form_element->control_elements.size(); ++i) { + HTMLFormControlElement* element = form_element->control_elements[i].get(); + + // Only input elements can be previewed. + if (formControlType(*element) != kText) + continue; + + // If the input element has not been auto-filled, FormManager has not + // previewed this field, so we have nothing to reset. + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + if (!input_element->isAutofilled()) + continue; + + // There might be unrelated elements in this form which have already been + // auto-filled. For example, the user might have already filled the address + // part of a form and now be dealing with the credit card section. We only + // want to reset the auto-filled status for fields that were previewed. + if (input_element->suggestedValue().isEmpty()) + continue; + + // Clear the suggested value. For the initiating node, also restore the + // original value. + input_element->setSuggestedValue(""); + bool is_initiating_node = (node == input_element); + if (is_initiating_node) { + // Call |setValue()| to force the renderer to update the field's displayed + // value. + input_element->setValue(input_element->value()); + input_element->setAutofilled(was_autofilled); + } else { + input_element->setAutofilled(false); + } + + // Clearing the suggested value in the focused node (above) can cause + // selection to be lost. We force selection range to restore the text + // cursor. + if (is_initiating_node) { + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); + } + } + + return true; +} + +void FormManager::Reset() { + STLDeleteElements(&form_elements_); +} + +void FormManager::ResetFrame(const Frame* frame) { + FormElementList::iterator iter = form_elements_.begin(); + while (iter != form_elements_.end()) { + if ((*iter)->form_element->document()->frame() == frame) { + delete *iter; + iter = form_elements_.erase(iter); + } else + ++iter; + } +} + +bool FormManager::FormWithNodeIsAutoFilled(Node* node) { + FormElement* form_element = NULL; + if (!FindCachedFormElementWithNode(node, &form_element)) + return false; + + for (size_t i = 0; i < form_element->control_elements.size(); ++i) { + HTMLFormControlElement* element = form_element->control_elements[i].get(); + if (formControlType(*element) != kText) + continue; + + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + if (input_element->isAutofilled()) + return true; + } + + return false; +} + +// static +string16 FormManager::InferLabelForElement(const HTMLFormControlElement& element) { + // Don't scrape labels for hidden elements. + if (formControlType(element) == kHidden) + return string16(); + + string16 inferred_label = InferLabelFromPrevious(element); + + // If we didn't find a label, check for table cell case. + if (inferred_label.empty()) + inferred_label = InferLabelFromTable(element); + + // If we didn't find a label, check for div table case. + if (inferred_label.empty()) + inferred_label = InferLabelFromDivTable(element); + + // If we didn't find a label, check for definition list case. + if (inferred_label.empty()) + inferred_label = InferLabelFromDefinitionList(element); + + return inferred_label; +} + +bool FormManager::FindCachedFormElementWithNode(Node* node, + FormElement** form_element) { + for (FormElementList::const_iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) { + for (std::vector<RefPtr<HTMLFormControlElement> >::const_iterator iter = (*form_iter)->control_elements.begin(); iter != (*form_iter)->control_elements.end(); ++iter) { + if (iter->get() == node) { + *form_element = *form_iter; + return true; + } + } + } + + return false; +} + +bool FormManager::FindCachedFormElement(const FormData& form, FormElement** form_element) { + for (FormElementList::iterator form_iter = form_elements_.begin(); form_iter != form_elements_.end(); ++form_iter) { + // TODO: matching on form name here which is not guaranteed to + // be unique for the page, nor is it guaranteed to be non-empty. Need to + // find a way to uniquely identify the form cross-process. For now we'll + // check form name and form action for identity. + // http://crbug.com/37990 test file sample8.html. + // Also note that WebString() == WebString(string16()) does not seem to + // evaluate to |true| for some reason TBD, so forcing to string16. + string16 element_name(WTFStringToString16((*form_iter)->form_element->name())); + GURL action(WTFStringToString16((*form_iter)->form_element->document()->completeURL((*form_iter)->form_element->action()).string())); + if (element_name == form.name && action == form.action) { + *form_element = *form_iter; + return true; + } + } + + return false; +} + + +void FormManager::ForEachMatchingFormField(FormElement* form, Node* node, RequirementsMask requirements, const FormData& data, Callback* callback) { + // It's possible that the site has injected fields into the form after the + // page has loaded, so we can't assert that the size of the cached control + // elements is equal to the size of the fields in |form|. Fortunately, the + // one case in the wild where this happens, paypal.com signup form, the fields + // are appended to the end of the form and are not visible. + for (size_t i = 0, j = 0; i < form->control_elements.size() && j < data.fields.size(); ++i) { + HTMLFormControlElement* element = form->control_elements[i].get(); + string16 element_name = nameForAutoFill(*element); + + if (element_name.empty()) + continue; + + // Search forward in the |form| for a corresponding field. + size_t k = j; + while (k < data.fields.size() && element_name != data.fields[k].name()) + k++; + + if (k >= data.fields.size()) + continue; + + DCHECK_EQ(data.fields[k].name(), element_name); + + bool is_initiating_node = false; + + // More than likely |requirements| will contain REQUIRE_AUTOCOMPLETE and/or + // REQUIRE_EMPTY, which both require text form control elements, so special- + // case this type of element. + if (formControlType(*element) == kText) { + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(element); + + // TODO: WebKit currently doesn't handle the autocomplete + // attribute for select control elements, but it probably should. + if (requirements & REQUIRE_AUTOCOMPLETE && !input_element->autoComplete()) + continue; + + is_initiating_node = (input_element == node); + // Don't require the node that initiated the auto-fill process to be + // empty. The user is typing in this field and we should complete the + // value when the user selects a value to fill out. + if (requirements & REQUIRE_EMPTY && !is_initiating_node && !input_element->value().isEmpty()) + continue; + } + + if (requirements & REQUIRE_ENABLED && !element->isEnabledFormControl()) + continue; + + callback->Run(element, &data.fields[k], is_initiating_node); + + // We found a matching form field so move on to the next. + ++j; + } + + delete callback; +} + +void FormManager::FillFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) { + // Nothing to fill. + if (data->value().empty()) + return; + + if (formControlType(*field) == kText) { + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field); + + // If the maxlength attribute contains a negative value, maxLength() + // returns the default maxlength value. + input_element->setValue(data->value().substr(0, input_element->maxLength()).c_str()); + input_element->setAutofilled(true); + if (is_initiating_node) { + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); + } + } else if (formControlType(*field) == kSelectOne) { + HTMLSelectElement* select_element = static_cast<HTMLSelectElement*>(field); + select_element->setValue(data->value().c_str()); + } +} + +void FormManager::PreviewFormField(HTMLFormControlElement* field, const FormField* data, bool is_initiating_node) { + // Nothing to preview. + if (data->value().empty()) + return; + + // Only preview input fields. + if (formControlType(*field) != kText) + return; + + HTMLInputElement* input_element = static_cast<HTMLInputElement*>(field); + + // If the maxlength attribute contains a negative value, maxLength() + // returns the default maxlength value. + input_element->setSuggestedValue(data->value().substr(0, input_element->maxLength()).c_str()); + input_element->setAutofilled(true); + if (is_initiating_node) + input_element->setSelectionRange(0, input_element->suggestedValue().length()); +} + +} diff --git a/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h new file mode 100644 index 0000000..e844981 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/FormManagerAndroid.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2010 The Chromium Authors. All rights reserved. + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FormManagerAndroid_h +#define FormManagerAndroid_h + +#include "ChromiumIncludes.h" + +#include <map> +#include <vector> + +// TODO: This file is taken from chromium/chrome/renderer/form_manager.h and +// customised to use WebCore types rather than WebKit API types. It would be +// nice and would ease future merge pain if the two could be combined. + +namespace webkit_glue { +struct FormData; +class FormField; +} // namespace webkit_glue + +namespace WebCore { +class Frame; +class HTMLFormControlElement; +class HTMLFormElement; +class Node; +} + +using WebCore::Frame; +using WebCore::HTMLFormControlElement; +using WebCore::HTMLFormElement; +using WebCore::Node; + +namespace android { + +// Manages the forms in a Document. +class FormManager { +public: + // A bit field mask for form requirements. + enum RequirementsMask { + REQUIRE_NONE = 0, // No requirements. + REQUIRE_AUTOCOMPLETE = 1 << 0, // Require that autocomplete != off. + REQUIRE_ENABLED = 1 << 1, // Require that disabled attribute is off. + REQUIRE_EMPTY = 1 << 2, // Require that the fields are empty. + }; + + // A bit field mask to extract data from HTMLFormControlElement. + enum ExtractMask { + EXTRACT_NONE = 0, + EXTRACT_VALUE = 1 << 0, // Extract value from HTMLFormControlElement. + EXTRACT_OPTION_TEXT = 1 << 1, // Extract option text from HTMLFormSelectElement. Only valid when |EXTRACT_VALUE| is set. This is used for form submission where humand readable value is captured. + EXTRACT_OPTIONS = 1 << 2, // Extract options from HTMLFormControlElement. + }; + + FormManager(); + virtual ~FormManager(); + + // Fills out a FormField object from a given HTMLFormControlElement. + // |extract_mask|: See the enum ExtractMask above for details. + static void HTMLFormControlElementToFormField(HTMLFormControlElement* element, ExtractMask extract_mask, webkit_glue::FormField* field); + + // Returns the corresponding label for |element|. WARNING: This method can + // potentially be very slow. Do not use during any code paths where the page + // is loading. + static string16 LabelForElement(const HTMLFormControlElement& element); + + // Fills out a FormData object from a given WebFormElement. If |get_values| + // is true, the fields in |form| will have the values filled out. Returns + // true if |form| is filled out; it's possible that |element| won't meet the + // requirements in |requirements|. This also returns false if there are no + // fields in |form|. + // TODO: Remove the user of this in RenderView and move this to + // private. + static bool HTMLFormElementToFormData(HTMLFormElement* element, RequirementsMask requirements, ExtractMask extract_mask, webkit_glue::FormData* form); + + // Scans the DOM in |frame| extracting and storing forms. + void ExtractForms(Frame* frame); + + // Returns a vector of forms in |frame| that match |requirements|. + void GetFormsInFrame(const Frame* frame, RequirementsMask requirements, std::vector<webkit_glue::FormData>* forms); + + // Finds the form that contains |element| and returns it in |form|. Returns + // false if the form is not found. + bool FindFormWithFormControlElement(HTMLFormControlElement* element, RequirementsMask requirements, webkit_glue::FormData* form); + + // Fills the form represented by |form|. |form| should have the name set to + // the name of the form to fill out, and the number of elements and values + // must match the number of stored elements in the form. |node| is the form + // control element that initiated the auto-fill process. + // TODO: Is matching on name alone good enough? It's possible to + // store multiple forms with the same names from different frames. + bool FillForm(const webkit_glue::FormData& form, Node* node); + + // Previews the form represented by |form|. |node| is the form control element + // that initiated the preview process. Same conditions as FillForm. + bool PreviewForm(const webkit_glue::FormData& form, Node* node); + + // Clears the values of all input elements in the form that contains |node|. + // Returns false if the form is not found. + bool ClearFormWithNode(Node* node); + + // Clears the placeholder values and the auto-filled background for any fields + // in the form containing |node| that have been previewed. Resets the + // autofilled state of |node| to |was_autofilled|. Returns false if the form + // is not found. + bool ClearPreviewedFormWithNode(Node* node, bool was_autofilled); + + // Resets the stored set of forms. + void Reset(); + + // Resets the forms for the specified |frame|. + void ResetFrame(const Frame* frame); + + // Returns true if |form| has any auto-filled fields. + bool FormWithNodeIsAutoFilled(Node* node); + +private: + // Stores the HTMLFormElement and the form control elements for a form. + // Original form values are stored so when we clear a form we can reset + // "select-one" values to their original state. + struct FormElement; + + // Type for cache of FormElement objects. + typedef std::vector<FormElement*> FormElementList; + + // The callback type used by ForEachMatchingFormField(). + typedef Callback3<HTMLFormControlElement*, const webkit_glue::FormField*, bool>::Type Callback; + + // Infers corresponding label for |element| from surrounding context in the + // DOM. Contents of preceeding <p> tag or preceeding text element found in + // the form. + static string16 InferLabelForElement(const HTMLFormControlElement& element); + + // Finds the cached FormElement that contains |node|. + bool FindCachedFormElementWithNode(Node* node, FormElement** form_element); + + // Uses the data in |form| to find the cached FormElement. + bool FindCachedFormElement(const webkit_glue::FormData& form, FormElement** form_element); + + // For each field in |data| that matches the corresponding field in |form| + // and meets the |requirements|, |callback| is called with the actual + // WebFormControlElement and the FormField data from |form|. The field that + // matches |node| is not required to be empty if |requirements| includes + // REQUIRE_EMPTY. This method owns |callback|. + void ForEachMatchingFormField(FormElement* form, Node* node, RequirementsMask requirements, const webkit_glue::FormData& data, Callback* callback); + + // A ForEachMatchingFormField() callback that sets |field|'s value using the + // value in |data|. This method also sets the autofill attribute, causing the + // background to be yellow. + void FillFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data, bool is_initiating_node); + + // A ForEachMatchingFormField() callback that sets |field|'s placeholder value + // using the value in |data|, causing the test to be greyed-out. This method + // also sets the autofill attribute, causing the background to be yellow. + void PreviewFormField(HTMLFormControlElement* field, const webkit_glue::FormField* data, bool is_initiaiting_node); + + // The cached FormElement objects. + FormElementList form_elements_; + + DISALLOW_COPY_AND_ASSIGN(FormManager); +}; + +} // namespace android + +#endif // FormManagerAndroid_h diff --git a/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp new file mode 100644 index 0000000..598b9c4 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "MainThreadProxy.h" + +#include <wtf/MainThread.h> + +void MainThreadProxy::CallOnMainThread(CallOnMainThreadFunction f, void* c) +{ + callOnMainThread(f, c); +} diff --git a/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h new file mode 100644 index 0000000..d9310bb --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/MainThreadProxy.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MAIN_THREAD_PROXY_H +#define MAIN_THREAD_PROXY_H + +typedef void CallOnMainThreadFunction(void*); + +class MainThreadProxy +{ +public: + static void CallOnMainThread(CallOnMainThreadFunction, void*); +}; + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h b/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h new file mode 100644 index 0000000..aa408a5 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/StringUtils.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010 The Android Open Source Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef AutoFillStringUtils_h_ +#define AutoFillStringUtils_h_ + +#include "ChromiumIncludes.h" +#include "HTMLFormControlElement.h" +#include <wtf/text/WTFString.h> + +using WebCore::HTMLFormControlElement; + +const string16 kText = ASCIIToUTF16("text"); +const string16 kHidden = ASCIIToUTF16("hidden"); +const string16 kSelectOne = ASCIIToUTF16("select-one"); + +inline string16 WTFStringToString16(const WTF::String& wtfString) +{ + WTF::String str = wtfString; + + if (str.charactersWithNullTermination()) + return string16(str.charactersWithNullTermination()); + else + return string16(); +} + +inline string16 nameForAutoFill(const HTMLFormControlElement& element) +{ + // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported + // to use WebCore types for accessing element properties. + String name = element.name(); + String trimmedName = name.stripWhiteSpace(); + if (!trimmedName.isEmpty()) + return WTFStringToString16(trimmedName); + name = element.getIdAttribute(); + trimmedName = name.stripWhiteSpace(); + if (!trimmedName.isEmpty()) + return WTFStringToString16(trimmedName); + return string16(); +} + +inline string16 formControlType(const HTMLFormControlElement& element) +{ + // Taken from WebKit/chromium/src/WebFormControlElement.cpp, ported + // to use WebCore types for accessing element properties. + return WTFStringToString16(element.type()); +} + +#endif + diff --git a/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp new file mode 100644 index 0000000..a80636c --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp @@ -0,0 +1,296 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebAutoFill.h" + +#if ENABLE(WEB_AUTOFILL) + +#include "AutoFillHostAndroid.h" +#include "Frame.h" +#include "FormData.h" +#include "FormManagerAndroid.h" +#include "FrameLoader.h" +#include "HTMLFormControlElement.h" +#include "MainThreadProxy.h" +#include "Node.h" +#include "Page.h" +#include "Settings.h" +#include "WebFrame.h" +#include "WebRequestContext.h" +#include "WebUrlLoaderClient.h" +#include "WebViewCore.h" + +#define NO_PROFILE_SET 0 +#define FORM_NOT_AUTOFILLABLE -1 + +namespace android +{ +WebAutoFill::WebAutoFill() + : mQueryId(1) + , mWebViewCore(0) + , mLastSearchDomVersion(0) + , mParsingForms(false) +{ + mTabContents = new TabContents(); + setEmptyProfile(); +} + +void WebAutoFill::init() +{ + if (mAutoFillManager) + return; + + mFormManager = new FormManager(); + // We use the WebView's WebRequestContext, which may be a private browsing context. + ASSERT(mWebViewCore); + mAutoFillManager = new AutoFillManager(mTabContents.get()); + mAutoFillHost = new AutoFillHostAndroid(this); + mTabContents->SetProfileRequestContext(new AndroidURLRequestContextGetter(mWebViewCore->webRequestContext(), WebUrlLoaderClient::ioThread())); + mTabContents->SetAutoFillHost(mAutoFillHost.get()); +} + +WebAutoFill::~WebAutoFill() +{ + cleanUpQueryMap(); + mUniqueIdMap.clear(); +} + +void WebAutoFill::cleanUpQueryMap() +{ + for (AutoFillQueryFormDataMap::iterator it = mQueryMap.begin(); it != mQueryMap.end(); it++) + delete it->second; + mQueryMap.clear(); +} + +void WebAutoFill::searchDocument(WebCore::Frame* frame) +{ + if (!enabled()) + return; + + MutexLocker lock(mFormsSeenMutex); + + init(); + + cleanUpQueryMap(); + mUniqueIdMap.clear(); + mForms.clear(); + mQueryId = 1; + + ASSERT(mFormManager); + ASSERT(mAutoFillManager); + + mAutoFillManager->Reset(); + mFormManager->Reset(); + + mFormManager->ExtractForms(frame); + mFormManager->GetFormsInFrame(frame, FormManager::REQUIRE_AUTOCOMPLETE, &mForms); + + // Needs to be done on a Chrome thread as it will make a URL request to the AutoFill server. + // TODO: Use our own Autofill thread instead of the IO thread. + // TODO: For now, block here. Would like to make this properly async. + base::Thread* thread = WebUrlLoaderClient::ioThread(); + mParsingForms = true; + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebAutoFill::formsSeenImpl)); + while (mParsingForms) + mFormsSeenCondition.wait(mFormsSeenMutex); +} + +// Called on the Chromium IO thread. +void WebAutoFill::formsSeenImpl() +{ + MutexLocker lock(mFormsSeenMutex); + mAutoFillManager->FormsSeen(mForms); + mParsingForms = false; + mFormsSeenCondition.signal(); +} + +void WebAutoFill::formFieldFocused(WebCore::HTMLFormControlElement* formFieldElement) +{ + ASSERT(formFieldElement); + + Document* doc = formFieldElement->document(); + Frame* frame = doc->frame(); + + // FIXME: AutoFill only works in main frame for now. Should consider + // child frames. + if (frame != frame->page()->mainFrame()) + return; + + unsigned domVersion = doc->domTreeVersion(); + ASSERT(domVersion > 0); + + if (mLastSearchDomVersion != domVersion) { + // Need to extract forms as DOM version has changed since the last time + // we searched. + searchDocument(formFieldElement->document()->frame()); + mLastSearchDomVersion = domVersion; + } + + if (!enabled()) { + // In case that we've just been disabled and the last time we got autofill + // suggestions and told Java about them, clear that bit Java side now + // we're disabled. + mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16()); + return; + } + + // Get the FormField from the Node. + webkit_glue::FormField* formField = new webkit_glue::FormField; + FormManager::HTMLFormControlElementToFormField(formFieldElement, FormManager::EXTRACT_NONE, formField); + formField->set_label(FormManager::LabelForElement(*formFieldElement)); + + webkit_glue::FormData* form = new webkit_glue::FormData; + mFormManager->FindFormWithFormControlElement(formFieldElement, FormManager::REQUIRE_AUTOCOMPLETE, form); + mQueryMap[mQueryId] = new FormDataAndField(form, formField); + + bool suggestions = mAutoFillManager->GetAutoFillSuggestions(*form, *formField); + + mQueryId++; + if (!suggestions) { + ASSERT(mWebViewCore); + // Tell Java no autofill suggestions for this form. + mWebViewCore->setWebTextViewAutoFillable(FORM_NOT_AUTOFILLABLE, string16()); + return; + } +} + +void WebAutoFill::querySuccessful(const string16& value, const string16& label, int uniqueId) +{ + if (!enabled()) + return; + + // Store the unique ID for the query and inform java that autofill suggestions for this form are available. + // Pass java the queryId so that it can pass it back if the user decides to use autofill. + mUniqueIdMap[mQueryId] = uniqueId; + + ASSERT(mWebViewCore); + mWebViewCore->setWebTextViewAutoFillable(mQueryId, mAutoFillProfile->Label()); +} + +void WebAutoFill::fillFormFields(int queryId) +{ + if (!enabled()) + return; + + webkit_glue::FormData* form = mQueryMap[queryId]->form(); + webkit_glue::FormField* field = mQueryMap[queryId]->field(); + ASSERT(form); + ASSERT(field); + + AutoFillQueryToUniqueIdMap::iterator iter = mUniqueIdMap.find(queryId); + if (iter == mUniqueIdMap.end()) { + // The user has most likely tried to AutoFill the form again without + // refocussing the form field. The UI should protect against this + // but stop here to be certain. + return; + } + mAutoFillManager->FillAutoFillFormData(queryId, *form, *field, iter->second); + mUniqueIdMap.erase(iter); +} + +void WebAutoFill::fillFormInPage(int queryId, const webkit_glue::FormData& form) +{ + if (!enabled()) + return; + + // FIXME: Pass a pointer to the Node that triggered the AutoFill flow here instead of 0. + // The consquence of passing 0 is that we should always fail the test in FormManader::ForEachMathcingFormField():169 + // that says "only overwrite an elements current value if the user triggered autofill through that element" + // for elements that have a value already. But by a quirk of Android text views we are OK. We should still + // fix this though. + mFormManager->FillForm(form, 0); +} + +bool WebAutoFill::enabled() const +{ + Page* page = mWebViewCore->mainFrame()->page(); + return page ? page->settings()->autoFillEnabled() : false; +} + +void WebAutoFill::setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName, + const string16& addressLine1, const string16& addressLine2, const string16& city, + const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber) +{ + if (!mAutoFillProfile) + mAutoFillProfile.set(new AutoFillProfile()); + + // Update the profile. + // Constants for AutoFill field types are found in external/chromium/chrome/browser/autofill/field_types.h. + mAutoFillProfile->SetInfo(AutoFillType(NAME_FULL), fullName); + mAutoFillProfile->SetInfo(AutoFillType(EMAIL_ADDRESS), emailAddress); + mAutoFillProfile->SetInfo(AutoFillType(COMPANY_NAME), companyName); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1), addressLine1); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2), addressLine2); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_CITY), city); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_STATE), state); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP), zipCode); + mAutoFillProfile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), country); + mAutoFillProfile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), phoneNumber); + + std::vector<AutoFillProfile> profiles; + profiles.push_back(*mAutoFillProfile); + updateProfileLabel(); + mTabContents->profile()->GetPersonalDataManager()->SetProfiles(&profiles); +} + +bool WebAutoFill::updateProfileLabel() +{ + std::vector<AutoFillProfile*> profiles; + profiles.push_back(mAutoFillProfile.get()); + return AutoFillProfile::AdjustInferredLabels(&profiles); +} + +void WebAutoFill::clearProfiles() +{ + if (!mAutoFillProfile) + return; + // For now Chromium only ever knows about one profile, so we can just + // remove it. If we support multiple profiles in the future + // we need to remove them all here. + std::string profileGuid = mAutoFillProfile->guid(); + mTabContents->profile()->GetPersonalDataManager()->RemoveProfile(profileGuid); + setEmptyProfile(); +} + +void WebAutoFill::setEmptyProfile() +{ + // Set an empty profile. This will ensure that when autofill is enabled, + // we will still search the document for autofillable forms and inform + // java of their presence so we can invite the user to set up + // their own profile. + + // Chromium code will strip the values sent into the profile so we need them to be + // at least one non-whitespace character long. We need to set all fields of the + // profile to a non-empty string so that any field type can trigger the autofill + // suggestion. AutoFill will not detect form fields if the profile value for that + // field is an empty string. + static const string16 empty = string16(ASCIIToUTF16("a")); + setProfile(empty, empty, empty, empty, empty, empty, empty, empty, empty, empty); +} + +} + +#endif diff --git a/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h new file mode 100644 index 0000000..97e478e --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/autofill/WebAutoFill.h @@ -0,0 +1,127 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebAutoFill_h +#define WebAutoFill_h + +#if ENABLE(WEB_AUTOFILL) + +#include "ChromiumIncludes.h" + +#include <map> +#include <vector> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/ThreadingPrimitives.h> + +class AutoFillManager; +class AutoFillProfile; +class AutoFillHost; + +namespace WebCore { +class Frame; +class HTMLFormControlElement; +} + +namespace android +{ +class FormManager; +class WebViewCore; + +class FormDataAndField { +public: + FormDataAndField(webkit_glue::FormData* form, webkit_glue::FormField* field) + : mForm(form) + , mField(field) + { + } + + webkit_glue::FormData* form() { return mForm.get(); } + webkit_glue::FormField* field() { return mField.get(); } + +private: + OwnPtr<webkit_glue::FormData> mForm; + OwnPtr<webkit_glue::FormField> mField; +}; + +class WebAutoFill : public Noncopyable +{ +public: + WebAutoFill(); + virtual ~WebAutoFill(); + void formFieldFocused(WebCore::HTMLFormControlElement*); + void fillFormFields(int queryId); + void querySuccessful(const string16& value, const string16& label, int uniqueId); + void fillFormInPage(int queryId, const webkit_glue::FormData& form); + void setWebViewCore(WebViewCore* webViewCore) { mWebViewCore = webViewCore; } + bool enabled() const; + + void setProfile(const string16& fullName, const string16& emailAddress, const string16& companyName, + const string16& addressLine1, const string16& addressLine2, const string16& city, + const string16& state, const string16& zipCode, const string16& country, const string16& phoneNumber); + void clearProfiles(); + + bool updateProfileLabel(); + + void reset() { mLastSearchDomVersion = 0; } + +private: + void init(); + void searchDocument(WebCore::Frame*); + void setEmptyProfile(); + void formsSeenImpl(); + void cleanUpQueryMap(); + + OwnPtr<FormManager> mFormManager; + OwnPtr<AutoFillManager> mAutoFillManager; + OwnPtr<AutoFillHost> mAutoFillHost; + OwnPtr<TabContents> mTabContents; + OwnPtr<AutoFillProfile> mAutoFillProfile; + + typedef std::vector<webkit_glue::FormData, std::allocator<webkit_glue::FormData> > FormList; + FormList mForms; + + typedef std::map<int, FormDataAndField*> AutoFillQueryFormDataMap; + AutoFillQueryFormDataMap mQueryMap; + + typedef std::map<int, int> AutoFillQueryToUniqueIdMap; + AutoFillQueryToUniqueIdMap mUniqueIdMap; + int mQueryId; + + WebViewCore* mWebViewCore; + + unsigned mLastSearchDomVersion; + + WTF::Mutex mFormsSeenMutex; // Guards mFormsSeenCondition and mParsingForms. + WTF::ThreadCondition mFormsSeenCondition; + bool volatile mParsingForms; +}; + +} + +DISABLE_RUNNABLE_METHOD_REFCOUNT(android::WebAutoFill); + +#endif // ENABLE(WEB_AUTOFILL) +#endif // WebAutoFill_h |