diff options
Diffstat (limited to 'Source/WebKit/android/jni/WebCoreFrameBridge.cpp')
-rw-r--r-- | Source/WebKit/android/jni/WebCoreFrameBridge.cpp | 2125 |
1 files changed, 2125 insertions, 0 deletions
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp new file mode 100644 index 0000000..15b6d20 --- /dev/null +++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -0,0 +1,2125 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 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 "webcoreglue" + +#include "config.h" +#include "WebCoreFrameBridge.h" + +#include "Arena.h" +#include "BackForwardList.h" +#include "MemoryCache.h" +#include "Chrome.h" +#include "ChromeClientAndroid.h" +#include "ChromiumInit.h" +#include "ContextMenuClientAndroid.h" +#include "DeviceMotionClientAndroid.h" +#include "DeviceOrientationClientAndroid.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "DragClientAndroid.h" +#include "EditorClientAndroid.h" +#include "Element.h" +#include "FocusController.h" +#include "Font.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClientAndroid.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HistoryItem.h" +#include "HTMLCollection.h" +#include "HTMLElement.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "IconDatabase.h" +#include "Image.h" +#include "InspectorClientAndroid.h" +#include "KURL.h" +#include "Page.h" +#include "PageCache.h" +#include "PlatformString.h" +#include "RenderPart.h" +#include "RenderSkinAndroid.h" +#include "RenderTreeAsText.h" +#include "RenderView.h" +#include "ResourceHandle.h" +#include "ResourceHandleInternal.h" +#include "ScriptController.h" +#include "ScriptValue.h" +#include "SecurityOrigin.h" +#include "SelectionController.h" +#include "Settings.h" +#include "SubstituteData.h" +#include "UrlInterceptResponse.h" +#include "UserGestureIndicator.h" +#include "WebCache.h" +#include "WebCoreJni.h" +#include "WebCoreResourceLoader.h" +#include "WebHistory.h" +#include "WebIconDatabase.h" +#include "WebFrameView.h" +#include "WebUrlLoaderClient.h" +#include "WebViewCore.h" +#include "android_graphics.h" +#include "jni.h" +#include "wds/DebugServer.h" + +#include <JNIUtility.h> +#include <JNIHelp.h> +#include <SkGraphics.h> +#include <android_runtime/android_util_AssetManager.h> +#include <utils/misc.h> +#include <utils/AssetManager.h> +#include <wtf/CurrentTime.h> +#include <wtf/Platform.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +#if USE(JSC) +#include "GCController.h" +#include "JSDOMWindow.h" +#include "JavaInstanceJSC.h" +#include <runtime_object.h> +#include <runtime_root.h> +#include <runtime/JSLock.h> +#elif USE(V8) +#include "JavaNPObjectV8.h" +#include "JavaInstanceV8.h" +#include "V8Counters.h" +#endif // USE(JSC) + +#ifdef ANDROID_INSTRUMENT +#include "TimeCounter.h" +#endif + +#if ENABLE(ARCHIVE) +#include "WebArchiveAndroid.h" +#endif + +#if ENABLE(WEB_AUTOFILL) +#include "autofill/WebAutoFill.h" +#endif + +using namespace JSC::Bindings; + +static String* gUploadFileLabel; +static String* gResetLabel; +static String* gSubmitLabel; +static String* gNoFileChosenLabel; + +String* WebCore::PlatformBridge::globalLocalizedName( + WebCore::PlatformBridge::rawResId resId) +{ + switch (resId) { + case WebCore::PlatformBridge::FileUploadLabel: + return gUploadFileLabel; + case WebCore::PlatformBridge::ResetLabel: + return gResetLabel; + case WebCore::PlatformBridge::SubmitLabel: + return gSubmitLabel; + case WebCore::PlatformBridge::FileUploadNoFileChosenLabel: + return gNoFileChosenLabel; + + default: + return 0; + } +} +/** + * Instantiate the localized name desired. + */ +void initGlobalLocalizedName(WebCore::PlatformBridge::rawResId resId, + android::WebFrame* webFrame) +{ + String** pointer; + switch (resId) { + case WebCore::PlatformBridge::FileUploadLabel: + pointer = &gUploadFileLabel; + break; + case WebCore::PlatformBridge::ResetLabel: + pointer = &gResetLabel; + break; + case WebCore::PlatformBridge::SubmitLabel: + pointer = &gSubmitLabel; + break; + case WebCore::PlatformBridge::FileUploadNoFileChosenLabel: + pointer = &gNoFileChosenLabel; + break; + default: + return; + } + if (!(*pointer) && webFrame) { + (*pointer) = new String(webFrame->getRawResourceFilename(resId).impl()); + } +} + +namespace android { + +// ---------------------------------------------------------------------------- + +#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024 + +// ---------------------------------------------------------------------------- + +struct WebFrame::JavaBrowserFrame +{ + jweak mObj; + jweak mHistoryList; // WebBackForwardList object + jmethodID mStartLoadingResource; + jmethodID mMaybeSavePassword; + jmethodID mShouldInterceptRequest; + jmethodID mLoadStarted; + jmethodID mTransitionToCommitted; + jmethodID mLoadFinished; + jmethodID mReportError; + jmethodID mSetTitle; + jmethodID mWindowObjectCleared; + jmethodID mSetProgress; + jmethodID mDidReceiveIcon; + jmethodID mDidReceiveTouchIconUrl; + jmethodID mUpdateVisitedHistory; + jmethodID mHandleUrl; + jmethodID mCreateWindow; + jmethodID mCloseWindow; + jmethodID mDecidePolicyForFormResubmission; + jmethodID mRequestFocus; + jmethodID mGetRawResFilename; + jmethodID mDensity; + jmethodID mGetFileSize; + jmethodID mGetFile; + jmethodID mDidReceiveAuthenticationChallenge; + jmethodID mReportSslCertError; + jmethodID mDownloadStart; + jmethodID mDidReceiveData; + jmethodID mDidFinishLoading; + jmethodID mSetCertificate; + jmethodID mShouldSaveFormData; + jmethodID mSaveFormData; + jmethodID mAutoLogin; + AutoJObject frame(JNIEnv* env) { + return getRealObject(env, mObj); + } + AutoJObject history(JNIEnv* env) { + return getRealObject(env, mHistoryList); + } +}; + +static jfieldID gFrameField; +#define GET_NATIVE_FRAME(env, obj) ((WebCore::Frame*)env->GetIntField(obj, gFrameField)) +#define SET_NATIVE_FRAME(env, obj, frame) (env->SetIntField(obj, gFrameField, frame)) + +// ---------------------------------------------------------------------------- + +WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page) + : mPage(page) +{ + jclass clazz = env->GetObjectClass(obj); + mJavaFrame = new JavaBrowserFrame; + mJavaFrame->mObj = env->NewWeakGlobalRef(obj); + mJavaFrame->mHistoryList = env->NewWeakGlobalRef(historyList); + mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource", + "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BJIZZZLjava/lang/String;Ljava/lang/String;)Landroid/webkit/LoadListener;"); + mJavaFrame->mMaybeSavePassword = env->GetMethodID(clazz, "maybeSavePassword", + "([BLjava/lang/String;Ljava/lang/String;)V"); + mJavaFrame->mShouldInterceptRequest = + env->GetMethodID(clazz, "shouldInterceptRequest", + "(Ljava/lang/String;)Landroid/webkit/WebResourceResponse;"); + mJavaFrame->mLoadStarted = env->GetMethodID(clazz, "loadStarted", + "(Ljava/lang/String;Landroid/graphics/Bitmap;IZ)V"); + mJavaFrame->mTransitionToCommitted = env->GetMethodID(clazz, "transitionToCommitted", + "(IZ)V"); + mJavaFrame->mLoadFinished = env->GetMethodID(clazz, "loadFinished", + "(Ljava/lang/String;IZ)V"); + mJavaFrame->mReportError = env->GetMethodID(clazz, "reportError", + "(ILjava/lang/String;Ljava/lang/String;)V"); + mJavaFrame->mSetTitle = env->GetMethodID(clazz, "setTitle", + "(Ljava/lang/String;)V"); + mJavaFrame->mWindowObjectCleared = env->GetMethodID(clazz, "windowObjectCleared", + "(I)V"); + mJavaFrame->mSetProgress = env->GetMethodID(clazz, "setProgress", + "(I)V"); + mJavaFrame->mDidReceiveIcon = env->GetMethodID(clazz, "didReceiveIcon", + "(Landroid/graphics/Bitmap;)V"); + mJavaFrame->mDidReceiveTouchIconUrl = env->GetMethodID(clazz, "didReceiveTouchIconUrl", + "(Ljava/lang/String;Z)V"); + mJavaFrame->mUpdateVisitedHistory = env->GetMethodID(clazz, "updateVisitedHistory", + "(Ljava/lang/String;Z)V"); + mJavaFrame->mHandleUrl = env->GetMethodID(clazz, "handleUrl", + "(Ljava/lang/String;)Z"); + mJavaFrame->mCreateWindow = env->GetMethodID(clazz, "createWindow", + "(ZZ)Landroid/webkit/BrowserFrame;"); + mJavaFrame->mCloseWindow = env->GetMethodID(clazz, "closeWindow", + "(Landroid/webkit/WebViewCore;)V"); + mJavaFrame->mDecidePolicyForFormResubmission = env->GetMethodID(clazz, + "decidePolicyForFormResubmission", "(I)V"); + mJavaFrame->mRequestFocus = env->GetMethodID(clazz, "requestFocus", + "()V"); + mJavaFrame->mGetRawResFilename = env->GetMethodID(clazz, "getRawResFilename", + "(I)Ljava/lang/String;"); + mJavaFrame->mDensity = env->GetMethodID(clazz, "density","()F"); + mJavaFrame->mGetFileSize = env->GetMethodID(clazz, "getFileSize", "(Ljava/lang/String;)I"); + mJavaFrame->mGetFile = env->GetMethodID(clazz, "getFile", "(Ljava/lang/String;[BII)I"); + mJavaFrame->mDidReceiveAuthenticationChallenge = env->GetMethodID(clazz, "didReceiveAuthenticationChallenge", + "(ILjava/lang/String;Ljava/lang/String;Z)V"); + mJavaFrame->mReportSslCertError = env->GetMethodID(clazz, "reportSslCertError", "(II[B)V"); + mJavaFrame->mDownloadStart = env->GetMethodID(clazz, "downloadStart", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V"); + mJavaFrame->mDidReceiveData = env->GetMethodID(clazz, "didReceiveData", "([BI)V"); + mJavaFrame->mDidFinishLoading = env->GetMethodID(clazz, "didFinishLoading", "()V"); + mJavaFrame->mSetCertificate = env->GetMethodID(clazz, "setCertificate", "([B)V"); + mJavaFrame->mShouldSaveFormData = env->GetMethodID(clazz, "shouldSaveFormData", "()Z"); + mJavaFrame->mSaveFormData = env->GetMethodID(clazz, "saveFormData", "(Ljava/util/HashMap;)V"); + mJavaFrame->mAutoLogin = env->GetMethodID(clazz, "autoLogin", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + env->DeleteLocalRef(clazz); + + LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource"); + LOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword"); + LOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest"); + LOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted"); + LOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted"); + LOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished"); + LOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError"); + LOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle"); + LOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared"); + LOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress"); + LOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon"); + LOG_ASSERT(mJavaFrame->mDidReceiveTouchIconUrl, "Could not find method didReceiveTouchIconUrl"); + LOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory"); + LOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl"); + LOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow"); + LOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow"); + LOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission"); + LOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus"); + LOG_ASSERT(mJavaFrame->mGetRawResFilename, "Could not find method getRawResFilename"); + LOG_ASSERT(mJavaFrame->mDensity, "Could not find method density"); + LOG_ASSERT(mJavaFrame->mGetFileSize, "Could not find method getFileSize"); + LOG_ASSERT(mJavaFrame->mGetFile, "Could not find method getFile"); + LOG_ASSERT(mJavaFrame->mDidReceiveAuthenticationChallenge, "Could not find method didReceiveAuthenticationChallenge"); + LOG_ASSERT(mJavaFrame->mReportSslCertError, "Could not find method reportSslCertError"); + LOG_ASSERT(mJavaFrame->mDownloadStart, "Could not find method downloadStart"); + LOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData"); + LOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading"); + LOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate"); + LOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData"); + LOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData"); + LOG_ASSERT(mJavaFrame->mAutoLogin, "Could not find method autoLogin"); + + mUserAgent = WTF::String(); + mUserInitiatedAction = false; + mBlockNetworkLoads = false; + m_renderSkins = 0; +} + +WebFrame::~WebFrame() +{ + if (mJavaFrame->mObj) { + JNIEnv* env = getJNIEnv(); + env->DeleteWeakGlobalRef(mJavaFrame->mObj); + env->DeleteWeakGlobalRef(mJavaFrame->mHistoryList); + mJavaFrame->mObj = 0; + } + delete mJavaFrame; + delete m_renderSkins; +} + +WebFrame* WebFrame::getWebFrame(const WebCore::Frame* frame) +{ + FrameLoaderClientAndroid* client = + static_cast<FrameLoaderClientAndroid*> (frame->loader()->client()); + return client->webFrame(); +} + +static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHeaderMap& map) +{ + jclass mapClass = env->FindClass("java/util/HashMap"); + LOG_ASSERT(mapClass, "Could not find HashMap class!"); + jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V"); + LOG_ASSERT(init, "Could not find constructor for HashMap"); + jobject hashMap = env->NewObject(mapClass, init, map.size()); + LOG_ASSERT(hashMap, "Could not create a new HashMap"); + jmethodID put = env->GetMethodID(mapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + LOG_ASSERT(put, "Could not find put method on HashMap"); + + WebCore::HTTPHeaderMap::const_iterator end = map.end(); + for (WebCore::HTTPHeaderMap::const_iterator i = map.begin(); i != end; ++i) { + if (i->first.length() == 0 || i->second.length() == 0) + continue; + jstring key = wtfStringToJstring(env, i->first); + jstring val = wtfStringToJstring(env, i->second); + if (key && val) { + env->CallObjectMethod(hashMap, put, key, val); + } + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); + } + + env->DeleteLocalRef(mapClass); + + return hashMap; +} + +// This class stores the URI and the size of each file for upload. The URI is +// stored so we do not have to create it again. The size is stored so we can +// compare the actual size of the file with the stated size. If the actual size +// is larger, we will not copy it, since we will not have enough space in our +// buffer. +class FileInfo { +public: + FileInfo(JNIEnv* env, const WTF::String& name) { + m_uri = wtfStringToJstring(env, name); + checkException(env); + m_size = 0; + m_env = env; + } + ~FileInfo() { + m_env->DeleteLocalRef(m_uri); + } + int getSize() { return m_size; } + jstring getUri() { return m_uri; } + void setSize(int size) { m_size = size; } +private: + // This is only a pointer to the JNIEnv* returned by + // JSC::Bindings::getJNIEnv(). Used to delete the jstring when finished. + JNIEnv* m_env; + jstring m_uri; + int m_size; +}; + +PassRefPtr<WebCore::ResourceLoaderAndroid> +WebFrame::startLoadingResource(WebCore::ResourceHandle* loader, + const WebCore::ResourceRequest& request, + bool mainResource, + bool synchronous) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: startLoadingResource(%p, %s)", + loader, request.url().string().latin1().data()); + + WTF::String method = request.httpMethod(); + WebCore::HTTPHeaderMap headers = request.httpHeaderFields(); + + JNIEnv* env = getJNIEnv(); + WTF::String urlStr = request.url().string(); + int colon = urlStr.find(':'); + bool allLower = true; + for (int index = 0; index < colon; index++) { + UChar ch = urlStr[index]; + if (!WTF::isASCIIAlpha(ch)) + break; + allLower &= WTF::isASCIILower(ch); + if (index == colon - 1 && !allLower) { + urlStr = urlStr.substring(0, colon).lower() + + urlStr.substring(colon); + } + } + LOGV("%s lower=%s", __FUNCTION__, urlStr.latin1().data()); + jstring jUrlStr = wtfStringToJstring(env, urlStr); + jstring jMethodStr = NULL; + if (!method.isEmpty()) + jMethodStr = wtfStringToJstring(env, method); + WebCore::FormData* formdata = request.httpBody(); + jbyteArray jPostDataStr = getPostData(request); + jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers); + + // Convert the WebCore Cache Policy to a WebView Cache Policy. + int cacheMode = 0; // WebSettings.LOAD_NORMAL + switch (request.cachePolicy()) { + case WebCore::ReloadIgnoringCacheData: + cacheMode = 2; // WebSettings.LOAD_NO_CACHE + break; + case WebCore::ReturnCacheDataDontLoad: + cacheMode = 3; // WebSettings.LOAD_CACHE_ONLY + break; + case WebCore::ReturnCacheDataElseLoad: + cacheMode = 1; // WebSettings.LOAD_CACHE_ELSE_NETWORK + break; + case WebCore::UseProtocolCachePolicy: + default: + break; + } + + LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode); + + ResourceHandleInternal* loaderInternal = loader->getInternal(); + jstring jUsernameString = loaderInternal->m_user.isEmpty() ? + NULL : wtfStringToJstring(env, loaderInternal->m_user); + jstring jPasswordString = loaderInternal->m_pass.isEmpty() ? + NULL : wtfStringToJstring(env, loaderInternal->m_pass); + + bool isUserGesture = UserGestureIndicator::processingUserGesture(); + jobject jLoadListener = + env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mStartLoadingResource, + (int)loader, jUrlStr, jMethodStr, jHeaderMap, + jPostDataStr, formdata ? formdata->identifier(): 0, + cacheMode, mainResource, isUserGesture, + synchronous, jUsernameString, jPasswordString); + + env->DeleteLocalRef(jUrlStr); + env->DeleteLocalRef(jMethodStr); + env->DeleteLocalRef(jPostDataStr); + env->DeleteLocalRef(jHeaderMap); + env->DeleteLocalRef(jUsernameString); + env->DeleteLocalRef(jPasswordString); + if (checkException(env)) + return NULL; + + PassRefPtr<WebCore::ResourceLoaderAndroid> h; + if (jLoadListener) + h = WebCoreResourceLoader::create(env, jLoadListener); + env->DeleteLocalRef(jLoadListener); + return h; +} + +UrlInterceptResponse* +WebFrame::shouldInterceptRequest(const WTF::String& url) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data()); + + JNIEnv* env = getJNIEnv(); + jstring urlStr = wtfStringToJstring(env, url); + jobject response = env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mShouldInterceptRequest, urlStr); + env->DeleteLocalRef(urlStr); + if (response == 0) + return 0; + return new UrlInterceptResponse(env, response); +} + +void +WebFrame::reportError(int errorCode, const WTF::String& description, + const WTF::String& failingUrl) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data()); + JNIEnv* env = getJNIEnv(); + + jstring descStr = wtfStringToJstring(env, description); + jstring failUrl = wtfStringToJstring(env, failingUrl); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mReportError, + errorCode, descStr, failUrl); + env->DeleteLocalRef(descStr); + env->DeleteLocalRef(failUrl); +} + +void +WebFrame::loadStarted(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + // activeDocumentLoader() can return null. + DocumentLoader* documentLoader = frame->loader()->activeDocumentLoader(); + if (documentLoader == NULL) + return; + + const WebCore::KURL& url = documentLoader->url(); + if (url.isEmpty()) + return; + LOGV("::WebCore:: loadStarted %s", url.string().ascii().data()); + + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + WebCore::FrameLoadType loadType = frame->loader()->loadType(); + + if (loadType == WebCore::FrameLoadTypeReplace || + (loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList && + !isMainFrame)) + return; + + JNIEnv* env = getJNIEnv(); + const WTF::String& urlString = url.string(); + // If this is the main frame and we already have a favicon in the database, + // send it along with the page started notification. + jobject favicon = NULL; + if (isMainFrame) { + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlString, WebCore::IntSize(16, 16)); + if (icon) + favicon = webcoreImageToJavaBitmap(env, icon); + LOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data()); + } + jstring urlStr = wtfStringToJstring(env, urlString); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadStarted, urlStr, favicon, + (int)loadType, isMainFrame); + checkException(env); + env->DeleteLocalRef(urlStr); + if (favicon) + env->DeleteLocalRef(favicon); + + // Inform the client that the main frame has started a new load. + if (isMainFrame && mPage) { + Chrome* chrome = mPage->chrome(); + if (chrome) { + ChromeClientAndroid* client = static_cast<ChromeClientAndroid*>(chrome->client()); + if (client) + client->onMainFrameLoadStarted(); + } + } +} + +void +WebFrame::transitionToCommitted(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + WebCore::FrameLoadType loadType = frame->loader()->loadType(); + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mTransitionToCommitted, + (int)loadType, isMainFrame); + checkException(env); +} + +void +WebFrame::didFinishLoad(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + + // activeDocumentLoader() can return null. + WebCore::FrameLoader* loader = frame->loader(); + DocumentLoader* documentLoader = loader->activeDocumentLoader(); + if (documentLoader == NULL) + return; + + const WebCore::KURL& url = documentLoader->url(); + if (url.isEmpty()) + return; + LOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data()); + + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + WebCore::FrameLoadType loadType = loader->loadType(); + const WTF::String& urlString = url.string(); + jstring urlStr = wtfStringToJstring(env, urlString); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadFinished, urlStr, + (int)loadType, isMainFrame); + checkException(env); + env->DeleteLocalRef(urlStr); +} + +void +WebFrame::addHistoryItem(WebCore::HistoryItem* item) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: addHistoryItem"); + JNIEnv* env = getJNIEnv(); + WebHistory::AddItem(mJavaFrame->history(env), item); +} + +void +WebFrame::removeHistoryItem(int index) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: removeHistoryItem at %d", index); + JNIEnv* env = getJNIEnv(); + WebHistory::RemoveItem(mJavaFrame->history(env), index); +} + +void +WebFrame::updateHistoryIndex(int newIndex) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: updateHistoryIndex to %d", newIndex); + JNIEnv* env = getJNIEnv(); + WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex); +} + +void +WebFrame::setTitle(const WTF::String& title) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif +#ifndef NDEBUG + LOGV("setTitle(%s)", title.ascii().data()); +#endif + JNIEnv* env = getJNIEnv(); + jstring jTitleStr = wtfStringToJstring(env, title); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetTitle, jTitleStr); + checkException(env); + env->DeleteLocalRef(jTitleStr); +} + +void +WebFrame::windowObjectCleared(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOGV("::WebCore:: windowObjectCleared"); + JNIEnv* env = getJNIEnv(); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mWindowObjectCleared, (int)frame); + checkException(env); +} + +void +WebFrame::setProgress(float newProgress) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + int progress = (int) (100 * newProgress); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetProgress, progress); + checkException(env); +} + +const WTF::String +WebFrame::userAgentForURL(const WebCore::KURL* url) +{ + return mUserAgent; +} + +void +WebFrame::didReceiveIcon(WebCore::Image* icon) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + LOG_ASSERT(icon, "DidReceiveIcon called without an image!"); + JNIEnv* env = getJNIEnv(); + jobject bitmap = webcoreImageToJavaBitmap(env, icon); + if (!bitmap) + return; + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidReceiveIcon, bitmap); + env->DeleteLocalRef(bitmap); + checkException(env); +} + +void +WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + jstring jUrlStr = wtfStringToJstring(env, url); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mDidReceiveTouchIconUrl, jUrlStr, precomposed); + env->DeleteLocalRef(jUrlStr); + checkException(env); +} + +void +WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + const WTF::String& urlStr = url.string(); + JNIEnv* env = getJNIEnv(); + jstring jUrlStr = wtfStringToJstring(env, urlStr); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload); + env->DeleteLocalRef(jUrlStr); + checkException(env); +} + +bool +WebFrame::canHandleRequest(const WebCore::ResourceRequest& request) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + // always handle "POST" in place + if (equalIgnoringCase(request.httpMethod(), "POST")) + return true; + const WebCore::KURL& requestUrl = request.url(); + bool isUserGesture = UserGestureIndicator::processingUserGesture(); + if (!mUserInitiatedAction && !isUserGesture && + (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") || + requestUrl.protocolIs("file") || requestUrl.protocolIs("about") || + WebCore::protocolIsJavaScript(requestUrl.string()))) + return true; + const WTF::String& url = requestUrl.string(); + // Empty urls should not be sent to java + if (url.isEmpty()) + return true; + JNIEnv* env = getJNIEnv(); + jstring jUrlStr = wtfStringToJstring(env, url); + + // check to see whether browser app wants to hijack url loading. + // if browser app handles the url, we will return false to bail out WebCore loading + jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), mJavaFrame->mHandleUrl, jUrlStr); + checkException(env); + env->DeleteLocalRef(jUrlStr); + return (ret == 0); +} + +bool +WebFrame::shouldSaveFormData() +{ + JNIEnv* env = getJNIEnv(); + jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mShouldSaveFormData); + checkException(env); + return ret; +} + +WebCore::Frame* +WebFrame::createWindow(bool dialog, bool userGesture) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + jobject obj = env->CallObjectMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mCreateWindow, dialog, userGesture); + if (obj) { + WebCore::Frame* frame = GET_NATIVE_FRAME(env, obj); + return frame; + } + return NULL; +} + +void +WebFrame::requestFocus() const +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mRequestFocus); + checkException(env); +} + +void +WebFrame::closeWindow(WebViewCore* webViewCore) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + assert(webViewCore); + JNIEnv* env = getJNIEnv(); + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mCloseWindow, + webViewCore->getJavaObject().get()); +} + +struct PolicyFunctionWrapper { + WebCore::FramePolicyFunction func; +}; + +void +WebFrame::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + PolicyFunctionWrapper* p = new PolicyFunctionWrapper; + p->func = func; + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDecidePolicyForFormResubmission, p); +} + +WTF::String +WebFrame::getRawResourceFilename(WebCore::PlatformBridge::rawResId id) const +{ + JNIEnv* env = getJNIEnv(); + jstring ret = (jstring) env->CallObjectMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mGetRawResFilename, (int)id); + + return jstringToWtfString(env, ret); +} + +float +WebFrame::density() const +{ + JNIEnv* env = getJNIEnv(); + jfloat dpi = env->CallFloatMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDensity); + checkException(env); + return dpi; +} + +#if USE(CHROME_NETWORK_STACK) +void +WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + int jHandle = reinterpret_cast<int>(client); + jstring jHost = stdStringToJstring(env, host, true); + jstring jRealm = stdStringToJstring(env, realm, true); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mDidReceiveAuthenticationChallenge, jHandle, jHost, jRealm, useCachedCredentials); + env->DeleteLocalRef(jHost); + env->DeleteLocalRef(jRealm); + checkException(env); +} +#endif + +void +WebFrame::reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + int jHandle = reinterpret_cast<int>(client); + + int len = cert.length(); + jbyteArray jCert = env->NewByteArray(len); + jbyte* bytes = env->GetByteArrayElements(jCert, NULL); + cert.copy(reinterpret_cast<char*>(bytes), len); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mReportSslCertError, jHandle, cert_error, jCert); + env->DeleteLocalRef(jCert); + checkException(env); +} + +#if USE(CHROME_NETWORK_STACK) +void +WebFrame::downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, long long contentLength) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + jstring jUrl = stdStringToJstring(env, url, true); + jstring jUserAgent = stdStringToJstring(env, userAgent, true); + jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true); + jstring jMimetype = stdStringToJstring(env, mimetype, true); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mDownloadStart, jUrl, jUserAgent, jContentDisposition, jMimetype, contentLength); + + env->DeleteLocalRef(jUrl); + env->DeleteLocalRef(jUserAgent); + env->DeleteLocalRef(jContentDisposition); + env->DeleteLocalRef(jMimetype); + checkException(env); +} + +void +WebFrame::didReceiveData(const char* data, int size) { +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + + jbyteArray jData = env->NewByteArray(size); + jbyte* bytes = env->GetByteArrayElements(jData, NULL); + memcpy(reinterpret_cast<char*>(bytes), data, size); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mDidReceiveData, jData, size); + env->DeleteLocalRef(jData); + checkException(env); +} + +void +WebFrame::didFinishLoading() { +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidFinishLoading); + checkException(env); +} + +#endif + +#if USE(CHROME_NETWORK_STACK) +void WebFrame::setCertificate(const std::string& cert) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter); +#endif + JNIEnv* env = getJNIEnv(); + + int len = cert.length(); + jbyteArray jCert = env->NewByteArray(len); + jbyte* bytes = env->GetByteArrayElements(jCert, NULL); + cert.copy(reinterpret_cast<char*>(bytes), len); + + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetCertificate, jCert); + + env->DeleteLocalRef(jCert); + checkException(env); +} +#endif + +void WebFrame::autoLogin(const std::string& loginHeader) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimerCoutner::JavaCallbackTimeCounter); +#endif + WTF::String header(loginHeader.c_str(), loginHeader.length()); + WTF::Vector<WTF::String> split; + header.split('&', split); + if (!split.isEmpty()) { + WTF::String realm; + WTF::String account; + WTF::String args; + int len = split.size(); + while (len--) { + WTF::String& str = split[len]; + size_t equals = str.find('='); + if (equals == WTF::notFound) + continue; + + WTF::String* result = 0; + if (str.startsWith("realm", false)) + result = &realm; + else if (str.startsWith("account", false)) + result = &account; + else if (str.startsWith("args", false)) + result = &args; + + if (result) + // Decode url escape sequences before sending to the app. + *result = WebCore::decodeURLEscapeSequences(str.substring(equals + 1)); + } + + // realm and args are required parameters. + if (realm.isEmpty() || args.isEmpty()) + return; + + JNIEnv* env = getJNIEnv(); + jstring jRealm = wtfStringToJstring(env, realm, true); + jstring jAccount = wtfStringToJstring(env, account); + jstring jArgs = wtfStringToJstring(env, args, true); + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mAutoLogin, jRealm, jAccount, jArgs); + } +} + +void WebFrame::maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request) +{ + if (request.httpMethod() != "POST") + return; + + WTF::String username; + WTF::String password; + if (!getUsernamePasswordFromDom(frame, username, password)) + return; + + JNIEnv* env = getJNIEnv(); + jstring jUsername = wtfStringToJstring(env, username); + jstring jPassword = wtfStringToJstring(env, password); + jbyteArray jPostData = getPostData(request); + if (jPostData) { + env->CallVoidMethod(mJavaFrame->frame(env).get(), + mJavaFrame->mMaybeSavePassword, jPostData, jUsername, jPassword); + } + + env->DeleteLocalRef(jPostData); + env->DeleteLocalRef(jUsername); + env->DeleteLocalRef(jPassword); + checkException(env); +} + +bool WebFrame::getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password) +{ + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = frame->document()->forms(); + WebCore::Node* node = form->firstItem(); + while (node && !found && !node->namespaceURI().isNull() && + !node->namespaceURI().isEmpty()) { + const WTF::Vector<WebCore::FormAssociatedElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLElement* e = toHTMLElement(elements[i]); + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e; + if (input->autoComplete() == false) + continue; + if (input->isPasswordField()) + password = input->value(); + else if (input->isTextField() || input->isEmailField()) + username = input->value(); + if (!username.isNull() && !password.isNull()) + found = true; + } + } + node = form->nextItem(); + } + return found; +} + +jbyteArray WebFrame::getPostData(const WebCore::ResourceRequest& request) +{ + jbyteArray jPostDataStr = NULL; + WebCore::FormData* formdata = request.httpBody(); + if (formdata) { + JNIEnv* env = getJNIEnv(); + AutoJObject obj = mJavaFrame->frame(env); + + // We can use the formdata->flatten() but it will result in two + // memcpys, first through loading up the vector with the form data + // then another to copy it out of the vector and into the java byte + // array. Instead, we copy the form data ourselves below saving a + // memcpy. + const WTF::Vector<WebCore::FormDataElement>& elements = + formdata->elements(); + + // Sizing pass + int size = 0; + size_t n = elements.size(); + FileInfo** fileinfos = new FileInfo*[n]; + for (size_t i = 0; i < n; ++i) { + fileinfos[i] = 0; + const WebCore::FormDataElement& e = elements[i]; + if (e.m_type == WebCore::FormDataElement::data) { + size += e.m_data.size(); + } else if (e.m_type == WebCore::FormDataElement::encodedFile) { + fileinfos[i] = new FileInfo(env, e.m_filename); + int delta = env->CallIntMethod(obj.get(), + mJavaFrame->mGetFileSize, fileinfos[i]->getUri()); + checkException(env); + fileinfos[i]->setSize(delta); + size += delta; + } + } + + // Only create the byte array if there is POST data to pass up. + // The Java code is expecting null if there is no data. + if (size > 0) { + // Copy the actual form data. + jPostDataStr = env->NewByteArray(size); + if (jPostDataStr) { + // Write the form data to the java array. + jbyte* bytes = env->GetByteArrayElements(jPostDataStr, NULL); + int offset = 0; + for (size_t i = 0; i < n; ++i) { + const WebCore::FormDataElement& e = elements[i]; + if (e.m_type == WebCore::FormDataElement::data) { + int delta = e.m_data.size(); + memcpy(bytes + offset, e.m_data.data(), delta); + offset += delta; + } else if (e.m_type + == WebCore::FormDataElement::encodedFile) { + int delta = env->CallIntMethod(obj.get(), + mJavaFrame->mGetFile, fileinfos[i]->getUri(), + jPostDataStr, offset, fileinfos[i]->getSize()); + checkException(env); + offset += delta; + } + } + env->ReleaseByteArrayElements(jPostDataStr, bytes, 0); + } + } + delete[] fileinfos; + } + return jPostDataStr; +} + +// ---------------------------------------------------------------------------- + +static void CallPolicyFunction(JNIEnv* env, jobject obj, jint func, jint decision) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!"); + PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func; + LOG_ASSERT(pFunc, "nativeCallPolicyFunction must take a valid function pointer!"); + + // If we are resending the form then we should reset the multiple submission protection. + if (decision == WebCore::PolicyUse) + pFrame->loader()->resetMultipleFormSubmissionProtection(); + + (pFrame->loader()->policyChecker()->*(pFunc->func))((WebCore::PolicyAction)decision); +} + +static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAssetManager, jobject historyList) +{ + ScriptController::initializeThreading(); + +#if USE(CHROME_NETWORK_STACK) + // needs to be called before any other chromium code + initChromium(); +#endif + +#ifdef ANDROID_INSTRUMENT +#if USE(V8) + V8Counters::initCounters(); +#endif + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + // Create a new page + ChromeClientAndroid* chromeC = new ChromeClientAndroid; + EditorClientAndroid* editorC = new EditorClientAndroid; + DeviceMotionClientAndroid* deviceMotionC = new DeviceMotionClientAndroid; + DeviceOrientationClientAndroid* deviceOrientationC = new DeviceOrientationClientAndroid; + + WebCore::Page::PageClients pageClients; + pageClients.chromeClient = chromeC; + pageClients.contextMenuClient = new ContextMenuClientAndroid; + pageClients.editorClient = editorC; + pageClients.dragClient = new DragClientAndroid; + pageClients.inspectorClient = new InspectorClientAndroid; + pageClients.deviceMotionClient = deviceMotionC; + pageClients.deviceOrientationClient = deviceOrientationC; + WebCore::Page* page = new WebCore::Page(pageClients); + + editorC->setPage(page); + page->setGroupName("android.webkit"); + + // Create a WebFrame to access the Java BrowserFrame associated with this page + WebFrame* webFrame = new WebFrame(env, obj, historyList, page); + // Attach webFrame to pageClients.chromeClient and release our ownership + chromeC->setWebFrame(webFrame); + Release(webFrame); + + FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(webFrame); + // Create a Frame and the page holds its reference + WebCore::Frame* frame = WebCore::Frame::create(page, NULL, loaderC).get(); + loaderC->setFrame(frame); +#if ENABLE(WDS) + WDS::server()->addFrame(frame); +#endif + + // Create a WebViewCore to access the Java WebViewCore associated with this page + WebViewCore* webViewCore = new WebViewCore(env, javaview, frame); + +#if ENABLE(WEB_AUTOFILL) + editorC->getAutoFill()->setWebViewCore(webViewCore); +#endif + + // Create a FrameView + RefPtr<WebCore::FrameView> frameView = WebCore::FrameView::create(frame); + // Create a WebFrameView + WebFrameView* webFrameView = new WebFrameView(frameView.get(), webViewCore); + // As webFrameView Retains webViewCore, release our ownership + Release(webViewCore); + // As frameView Retains webFrameView, release our ownership + Release(webFrameView); + // Attach the frameView to the frame and release our ownership + frame->setView(frameView); + // Set the frame to active to turn on keyboard focus. + frame->init(); + frame->selection()->setFocused(true); + frame->page()->focusController()->setFocused(true); + deviceMotionC->setWebViewCore(webViewCore); + deviceOrientationC->setWebViewCore(webViewCore); + + // Allow local access to file:/// and substitute data + WebCore::SecurityOrigin::setLocalLoadPolicy( + WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData); + + LOGV("::WebCore:: createFrame %p", frame); + + // Set the mNativeFrame field in Frame + SET_NATIVE_FRAME(env, obj, (int)frame); + + String directory = webFrame->getRawResourceFilename( + WebCore::PlatformBridge::DrawableDir); + if (directory.isEmpty()) + LOGE("Can't find the drawable directory"); + else { + // Setup the asset manager. + AssetManager* am = assetManagerForJavaObject(env, jAssetManager); + // Initialize our skinning classes + webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(am, directory)); + } + for (int i = WebCore::PlatformBridge::FileUploadLabel; + i <= WebCore::PlatformBridge::FileUploadNoFileChosenLabel; i++) + initGlobalLocalizedName( + static_cast<WebCore::PlatformBridge::rawResId>(i), webFrame); +} + +static void DestroyFrame(JNIEnv* env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!"); + + LOGV("::WebCore:: deleting frame %p", pFrame); + + WebCore::FrameView* view = pFrame->view(); + view->ref(); + // detachFromParent will cause the page to be closed. + WebCore::FrameLoader* fl = pFrame->loader(); + // retain a pointer because detachFromParent will set the page to null. + WebCore::Page* page = pFrame->page(); + if (fl) + fl->detachFromParent(); + delete page; + view->deref(); + + SET_NATIVE_FRAME(env, obj, 0); +#if ENABLE(WDS) + WDS::server()->removeFrame(pFrame); +#endif +} + +static void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!"); + + WTF::String webcoreUrl = jstringToWtfString(env, url); + WebCore::KURL kurl(WebCore::KURL(), webcoreUrl); + WebCore::ResourceRequest request(kurl); + if (headers) { + // dalvikvm will raise exception if any of these fail + jclass mapClass = env->FindClass("java/util/Map"); + jmethodID entrySet = env->GetMethodID(mapClass, "entrySet", + "()Ljava/util/Set;"); + jobject set = env->CallObjectMethod(headers, entrySet); + + jclass setClass = env->FindClass("java/util/Set"); + jmethodID iterator = env->GetMethodID(setClass, "iterator", + "()Ljava/util/Iterator;"); + jobject iter = env->CallObjectMethod(set, iterator); + + jclass iteratorClass = env->FindClass("java/util/Iterator"); + jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); + jmethodID next = env->GetMethodID(iteratorClass, "next", + "()Ljava/lang/Object;"); + jclass entryClass = env->FindClass("java/util/Map$Entry"); + jmethodID getKey = env->GetMethodID(entryClass, "getKey", + "()Ljava/lang/Object;"); + jmethodID getValue = env->GetMethodID(entryClass, "getValue", + "()Ljava/lang/Object;"); + + while (env->CallBooleanMethod(iter, hasNext)) { + jobject entry = env->CallObjectMethod(iter, next); + jstring key = (jstring) env->CallObjectMethod(entry, getKey); + jstring value = (jstring) env->CallObjectMethod(entry, getValue); + request.setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, value)); + env->DeleteLocalRef(entry); + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } + + env->DeleteLocalRef(entryClass); + env->DeleteLocalRef(iteratorClass); + env->DeleteLocalRef(iter); + env->DeleteLocalRef(setClass); + env->DeleteLocalRef(set); + env->DeleteLocalRef(mapClass); + } + LOGV("LoadUrl %s", kurl.string().latin1().data()); + pFrame->loader()->load(request, false); +} + +static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativePostUrl must take a valid frame pointer!"); + + WebCore::KURL kurl(WebCore::KURL(), jstringToWtfString(env, url)); + WebCore::ResourceRequest request(kurl); + request.setHTTPMethod("POST"); + request.setHTTPContentType("application/x-www-form-urlencoded"); + + if (postData) { + jsize size = env->GetArrayLength(postData); + jbyte* bytes = env->GetByteArrayElements(postData, NULL); + RefPtr<FormData> formData = FormData::create((const void*)bytes, size); + // the identifier uses the same logic as generateFormDataIdentifier() in + // HTMLFormElement.cpp + formData->setIdentifier(static_cast<int64_t>(WTF::currentTime() * 1000000.0)); + request.setHTTPBody(formData); + env->ReleaseByteArrayElements(postData, bytes, 0); + } + + LOGV("PostUrl %s", kurl.string().latin1().data()); + WebCore::FrameLoadRequest frameRequest(pFrame->document()->securityOrigin(), request); + pFrame->loader()->loadFrameRequest(frameRequest, false, false, 0, 0, WebCore::SendReferrer); +} + +static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data, + jstring mimeType, jstring encoding, jstring failUrl) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!"); + + // Setup the resource request + WebCore::ResourceRequest request(jstringToWtfString(env, baseUrl)); + + // Setup the substituteData + const char* dataStr = env->GetStringUTFChars(data, NULL); + WTF::PassRefPtr<WebCore::SharedBuffer> sharedBuffer = + WebCore::SharedBuffer::create(); + LOG_ASSERT(dataStr, "nativeLoadData has a null data string."); + sharedBuffer->append(dataStr, strlen(dataStr)); + env->ReleaseStringUTFChars(data, dataStr); + + WebCore::SubstituteData substituteData(sharedBuffer, + jstringToWtfString(env, mimeType), jstringToWtfString(env, encoding), + WebCore::KURL(ParsedURLString, jstringToWtfString(env, failUrl))); + + // Perform the load + pFrame->loader()->load(request, substituteData, false); +} + +static void StopLoading(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!"); + LOGV("::WebCore:: stopLoading %p", pFrame); + + // Stop loading the page and do not send an unload event + pFrame->loader()->stopForUserCancel(); +} + +#if ENABLE(ARCHIVE) +static String saveArchiveAutoname(String basename, String name, String extension) { + if (name.isNull() || name.isEmpty()) { + name = String("index"); + } + + String testname = basename; + testname.append(name); + testname.append(extension); + + errno = 0; + struct stat permissions; + if (stat(testname.utf8().data(), &permissions) < 0) { + if (errno == ENOENT) + return testname; + return String(); + } + + const int maxAttempts = 100; + for (int i = 1; i < maxAttempts; i++) { + String testname = basename; + testname.append(name); + testname.append("-"); + testname.append(String::number(i)); + testname.append(extension); + + errno = 0; + if (stat(testname.utf8().data(), &permissions) < 0) { + if (errno == ENOENT) + return testname; + return String(); + } + } + + return String(); +} +#endif + +static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboolean autoname) +{ +#if ENABLE(ARCHIVE) + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeSaveWebArchive must take a valid frame pointer!"); + String mimeType = pFrame->loader()->documentLoader()->mainResource()->mimeType(); + if ((mimeType != "text/html") && (mimeType != "application/xhtml+xml")) + return NULL; + + const char* basenameNative = getCharactersFromJStringInEnv(env, basename); + String basenameString = String::fromUTF8(basenameNative); + String filename; + + if (autoname) { + String name = pFrame->loader()->documentLoader()->originalURL().lastPathComponent(); + String extension = String(".webarchivexml"); + filename = saveArchiveAutoname(basenameString, name, extension); + } else { + filename = basenameString; + } + + if (filename.isNull() || filename.isEmpty()) { + LOGD("saveWebArchive: Failed to select a filename to save."); + releaseCharactersForJStringInEnv(env, basename, basenameNative); + return NULL; + } + + const int noCompression = 0; + xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.utf8().data(), noCompression); + if (writer == NULL) { + LOGD("saveWebArchive: Failed to initialize xml writer."); + releaseCharactersForJStringInEnv(env, basename, basenameNative); + return NULL; + } + + RefPtr<WebArchiveAndroid> archive = WebCore::WebArchiveAndroid::create(pFrame); + + bool result = archive->saveWebArchive(writer); + + releaseCharactersForJStringInEnv(env, basename, basenameNative); + xmlFreeTextWriter(writer); + + if (result) + return wtfStringToJstring(env, filename); + + return NULL; +#endif +} + +static jstring ExternalRepresentation(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeExternalRepresentation must take a valid frame pointer!"); + + // Request external representation of the render tree + WTF::String renderDump = WebCore::externalRepresentation(pFrame); + return wtfStringToJstring(env, renderDump); +} + +static StringBuilder FrameAsText(WebCore::Frame *pFrame, jboolean dumpChildFrames) { + StringBuilder renderDump; + if (!pFrame) + return renderDump; + WebCore::Element *documentElement = pFrame->document()->documentElement(); + if (!documentElement) + return renderDump; + if (pFrame->tree()->parent()) { + renderDump.append("\n--------\nFrame: '"); + renderDump.append(pFrame->tree()->name()); + renderDump.append("'\n--------\n"); + } + renderDump.append(((WebCore::HTMLElement*)documentElement)->innerText()); + renderDump.append("\n"); + if (dumpChildFrames) { + for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) { + renderDump.append(FrameAsText(pFrame->tree()->child(i), dumpChildFrames).toString()); + } + } + return renderDump; +} + +static jstring DocumentAsText(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!"); + + WTF::String renderDump = FrameAsText(pFrame, false /* dumpChildFrames */).toString(); + return wtfStringToJstring(env, renderDump); +} + +static jstring ChildFramesAsText(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!"); + + StringBuilder renderDumpBuilder; + for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) { + renderDumpBuilder.append(FrameAsText(pFrame->tree()->child(i), true /* dumpChildFrames */).toString()); + } + WTF::String renderDump = renderDumpBuilder.toString(); + return wtfStringToJstring(env, renderDump); +} + +static void Reload(JNIEnv *env, jobject obj, jboolean allowStale) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!"); + + WebCore::FrameLoader* loader = pFrame->loader(); + if (allowStale) { + // load the current page with FrameLoadTypeIndexedBackForward so that it + // will use cache when it is possible + WebCore::Page* page = pFrame->page(); + WebCore::HistoryItem* item = page->backForwardList()->currentItem(); + if (item) + page->goToItem(item, FrameLoadTypeIndexedBackForward); + } else + loader->reload(true); +} + +static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!"); + + if (pos == 1) + pFrame->page()->goForward(); + else if (pos == -1) + pFrame->page()->goBack(); + else + pFrame->page()->goBackOrForward(pos); +} + +static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj, jstring script) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!"); + + WebCore::ScriptValue value = + pFrame->script()->executeScript(jstringToWtfString(env, script), true); + WTF::String result = WTF::String(); + ScriptState* scriptState = mainWorldScriptState(pFrame); + if (!value.getString(scriptState, result)) + return NULL; + return wtfStringToJstring(env, result); +} + +// Wrap the JavaInstance used when binding custom javascript interfaces. Use a +// weak reference so that the gc can collect the WebView. Override virtualBegin +// and virtualEnd and swap the weak reference for the real object. +class WeakJavaInstance : public JavaInstance { +public: +#if USE(JSC) + static PassRefPtr<WeakJavaInstance> create(jobject obj, PassRefPtr<RootObject> root) + { + return adoptRef(new WeakJavaInstance(obj, root)); + } +#elif USE(V8) + static PassRefPtr<WeakJavaInstance> create(jobject obj) + { + return adoptRef(new WeakJavaInstance(obj)); + } +#endif + +private: +#if USE(JSC) + WeakJavaInstance(jobject instance, PassRefPtr<RootObject> rootObject) + : JavaInstance(instance, rootObject) +#elif USE(V8) + WeakJavaInstance(jobject instance) + : JavaInstance(instance) +#endif + , m_beginEndDepth(0) + { + JNIEnv* env = getJNIEnv(); + // JavaInstance creates a global ref to instance in its constructor. + env->DeleteGlobalRef(m_instance->instance()); + // Set the object to a weak reference. + m_instance->setInstance(env->NewWeakGlobalRef(instance)); + } + ~WeakJavaInstance() + { + JNIEnv* env = getJNIEnv(); + // Store the weak reference so we can delete it later. + jweak weak = m_instance->instance(); + // The JavaInstance destructor attempts to delete the global ref stored + // in m_instance. Since we replaced it in our constructor with a weak + // reference, restore the global ref here so the vm will not complain. + m_instance->setInstance(env->NewGlobalRef( + getRealObject(env, m_instance->instance()).get())); + // Delete the weak reference. + env->DeleteWeakGlobalRef(weak); + } + + virtual void virtualBegin() + { + if (m_beginEndDepth++ > 0) + return; + m_weakRef = m_instance->instance(); + JNIEnv* env = getJNIEnv(); + // This is odd. getRealObject returns an AutoJObject which is used to + // cleanly create and delete a local reference. But, here we need to + // maintain the local reference across calls to virtualBegin() and + // virtualEnd(). So, release the local reference from the AutoJObject + // and delete the local reference in virtualEnd(). + m_realObject = getRealObject(env, m_weakRef).release(); + // Point to the real object + m_instance->setInstance(m_realObject); + // Call the base class method + INHERITED::virtualBegin(); + } + + virtual void virtualEnd() + { + if (--m_beginEndDepth > 0) + return; + // Call the base class method first to pop the local frame. + INHERITED::virtualEnd(); + // Get rid of the local reference to the real object. + getJNIEnv()->DeleteLocalRef(m_realObject); + // Point back to the WeakReference. + m_instance->setInstance(m_weakRef); + } + +private: + typedef JavaInstance INHERITED; + jobject m_realObject; + jweak m_weakRef; + // The current depth of nested calls to virtualBegin and virtualEnd. + int m_beginEndDepth; +}; + +static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer, + jobject javascriptObj, jstring interfaceName) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = 0; + if (nativeFramePointer == 0) + pFrame = GET_NATIVE_FRAME(env, obj); + else + pFrame = (WebCore::Frame*)nativeFramePointer; + LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!"); + + JavaVM* vm; + env->GetJavaVM(&vm); + LOGV("::WebCore:: addJSInterface: %p", pFrame); + +#if USE(JSC) + // Copied from qwebframe.cpp + JSC::JSLock lock(JSC::SilenceAssertionsOnly); + WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame, mainThreadNormalWorld()); + if (window) { + RootObject *root = pFrame->script()->bindingRootObject(); + setJavaVM(vm); + // Add the binding to JS environment + JSC::ExecState* exec = window->globalExec(); + JSC::JSObject* addedObject = WeakJavaInstance::create(javascriptObj, + root)->createRuntimeObject(exec); + const jchar* s = env->GetStringChars(interfaceName, NULL); + if (s) { + // Add the binding name to the window's table of child objects. + JSC::PutPropertySlot slot; + window->put(exec, JSC::Identifier(exec, (const UChar *)s, + env->GetStringLength(interfaceName)), addedObject, slot); + env->ReleaseStringChars(interfaceName, s); + checkException(env); + } + } +#elif USE(V8) + if (pFrame) { + RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj); + const char* name = getCharactersFromJStringInEnv(env, interfaceName); + // Pass ownership of the added object to bindToWindowObject. + NPObject* npObject = JavaInstanceToNPObject(addedObject.get()); + pFrame->script()->bindToWindowObject(pFrame, name, npObject); + // bindToWindowObject calls NPN_RetainObject on the + // returned one (see createV8ObjectForNPObject in V8NPObject.cpp). + // bindToWindowObject also increases obj's ref count and decreases + // the ref count when the object is not reachable from JavaScript + // side. Code here must release the reference count increased by + // bindToWindowObject. + + // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds + // we use WebCore/bindings/v8/npruntime.cpp (rather than + // WebCore/bridge/npruntime.cpp), so the function is implemented there. + // TODO: Combine the two versions of these NPAPI files. + NPN_ReleaseObject(npObject); + releaseCharactersForJString(interfaceName, name); + } +#endif + +} + +static void SetCacheDisabled(JNIEnv *env, jobject obj, jboolean disabled) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::cache()->setDisabled(disabled); +} + +static jboolean CacheDisabled(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + return WebCore::cache()->disabled(); +} + +static void ClearWebCoreCache() +{ + if (!WebCore::cache()->disabled()) { + // Disabling the cache will remove all resources from the cache. They may + // still live on if they are referenced by some Web page though. + WebCore::cache()->setDisabled(true); + WebCore::cache()->setDisabled(false); + } + + // clear page cache + int pageCapacity = WebCore::pageCache()->capacity(); + // Setting size to 0, makes all pages be released. + WebCore::pageCache()->setCapacity(0); + WebCore::pageCache()->releaseAutoreleasedPagesNow(); + WebCore::pageCache()->setCapacity(pageCapacity); +} + +static void ClearWebViewCache() +{ +#if USE(CHROME_NETWORK_STACK) + WebCache::get(false /*privateBrowsing*/)->clear(); +#else + // The Android network stack provides a WebView cache in CacheManager.java. + // Clearing this is handled entirely Java-side. +#endif +} + +static void ClearCache(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#if USE(JSC) + JSC::JSLock lock(false); + JSC::Heap::Statistics jsHeapStatistics = WebCore::JSDOMWindow::commonJSGlobalData()->heap.statistics(); + LOGD("About to gc and JavaScript heap size is %d and has %d bytes free", + jsHeapStatistics.size, jsHeapStatistics.free); +#endif // USE(JSC) + LOGD("About to clear cache and current cache has %d bytes live and %d bytes dead", + cache()->getLiveSize(), cache()->getDeadSize()); +#endif // ANDROID_INSTRUMENT + ClearWebCoreCache(); + ClearWebViewCache(); +#if USE(JSC) + // force JavaScript to GC when clear cache + WebCore::gcController().garbageCollectSoon(); +#elif USE(V8) + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + pFrame->script()->lowMemoryNotification(); +#endif // USE(JSC) +} + +static jboolean DocumentHasImages(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!"); + + return pFrame->document()->images()->length() > 0; +} + +static jboolean HasPasswordField(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!"); + + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms(); + WebCore::Node* node = form->firstItem(); + // Null/Empty namespace means that node is not created in HTMLFormElement + // class, but just normal Element class. + while (node && !found && !node->namespaceURI().isNull() && + !node->namespaceURI().isEmpty()) { + const WTF::Vector<WebCore::FormAssociatedElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLElement* e = toHTMLElement(elements[i]); + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + if (static_cast<WebCore::HTMLInputElement*>(e)->isPasswordField()) + found = true; + } + } + node = form->nextItem(); + } + return found; +} + +static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!"); + jobjectArray strArray = NULL; + WTF::String username; + WTF::String password; + if (WebFrame::getWebFrame(pFrame)->getUsernamePasswordFromDom(pFrame, username, password)) { + jclass stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(2, stringClass, NULL); + env->DeleteLocalRef(stringClass); + env->SetObjectArrayElement(strArray, 0, wtfStringToJstring(env, username)); + env->SetObjectArrayElement(strArray, 1, wtfStringToJstring(env, password)); + } + return strArray; +} + +static void SetUsernamePassword(JNIEnv *env, jobject obj, + jstring username, jstring password) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!"); + + WebCore::HTMLInputElement* usernameEle = NULL; + WebCore::HTMLInputElement* passwordEle = NULL; + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms(); + WebCore::Node* node = form->firstItem(); + while (node && !found && !node->namespaceURI().isNull() && + !node->namespaceURI().isEmpty()) { + const WTF::Vector<WebCore::FormAssociatedElement*>& elements = + ((WebCore::HTMLFormElement*)node)->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLElement* e = toHTMLElement(elements[i]); + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e; + if (input->autoComplete() == false) + continue; + if (input->isPasswordField()) + passwordEle = input; + else if (input->isTextField() || input->isEmailField()) + usernameEle = input; + if (usernameEle != NULL && passwordEle != NULL) + found = true; + } + } + node = form->nextItem(); + } + if (found) { + usernameEle->setValue(jstringToWtfString(env, username)); + passwordEle->setValue(jstringToWtfString(env, password)); + } +} + +void +WebFrame::saveFormData(HTMLFormElement* form) +{ + if (form->autoComplete()) { + JNIEnv* env = getJNIEnv(); + jclass mapClass = env->FindClass("java/util/HashMap"); + LOG_ASSERT(mapClass, "Could not find HashMap class!"); + jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V"); + LOG_ASSERT(init, "Could not find constructor for HashMap"); + jobject hashMap = env->NewObject(mapClass, init, 1); + LOG_ASSERT(hashMap, "Could not create a new HashMap"); + jmethodID put = env->GetMethodID(mapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + LOG_ASSERT(put, "Could not find put method on HashMap"); + WTF::Vector<WebCore::FormAssociatedElement*> elements = form->associatedElements(); + size_t size = elements.size(); + for (size_t i = 0; i < size; i++) { + WebCore::HTMLElement* e = toHTMLElement(elements[i]); + if (e->hasTagName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(e); + if (input->isTextField() && !input->isPasswordField() + && input->autoComplete()) { + WTF::String value = input->value(); + int len = value.length(); + if (len) { + const WTF::AtomicString& name = input->name(); + jstring key = wtfStringToJstring(env, name); + jstring val = wtfStringToJstring(env, value); + LOG_ASSERT(key && val, "name or value not set"); + env->CallObjectMethod(hashMap, put, key, val); + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); + } + } + } + } + env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSaveFormData, hashMap); + env->DeleteLocalRef(hashMap); + env->DeleteLocalRef(mapClass); + } +} + +static void OrientationChanged(JNIEnv *env, jobject obj, int orientation) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter); +#endif + WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj); + LOGV("Sending orientation: %d", orientation); + pFrame->sendOrientationChangeEvent(orientation); +} + +#if USE(CHROME_NETWORK_STACK) + +static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword) +{ + WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle); + std::string username = jstringToStdString(env, jUsername); + std::string password = jstringToStdString(env, jPassword); + client->setAuth(username, password); +} + +static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle) +{ + WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle); + client->cancelAuth(); +} + +static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle) +{ + WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle); + client->proceedSslCertError(); +} + +static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error) +{ + WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle); + client->cancelSslCertError(cert_error); +} + +#else + +static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword) +{ + LOGW("Chromium authentication API called, but libchromium is not available"); +} + +static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle) +{ + LOGW("Chromium authentication API called, but libchromium is not available"); +} + +static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle) +{ + LOGW("Chromium SSL API called, but libchromium is not available"); +} + +static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error) +{ + LOGW("Chromium SSL API called, but libchromium is not available"); +} + +#endif // USE(CHROME_NETWORK_STACK) + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gBrowserFrameNativeMethods[] = { + /* name, signature, funcPtr */ + { "nativeCallPolicyFunction", "(II)V", + (void*) CallPolicyFunction }, + { "nativeCreateFrame", "(Landroid/webkit/WebViewCore;Landroid/content/res/AssetManager;Landroid/webkit/WebBackForwardList;)V", + (void*) CreateFrame }, + { "nativeDestroyFrame", "()V", + (void*) DestroyFrame }, + { "nativeStopLoading", "()V", + (void*) StopLoading }, + { "nativeLoadUrl", "(Ljava/lang/String;Ljava/util/Map;)V", + (void*) LoadUrl }, + { "nativePostUrl", "(Ljava/lang/String;[B)V", + (void*) PostUrl }, + { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + (void*) LoadData }, + { "nativeSaveWebArchive", "(Ljava/lang/String;Z)Ljava/lang/String;", + (void*) SaveWebArchive }, + { "externalRepresentation", "()Ljava/lang/String;", + (void*) ExternalRepresentation }, + { "documentAsText", "()Ljava/lang/String;", + (void*) DocumentAsText }, + { "childFramesAsText", "()Ljava/lang/String;", + (void*) ChildFramesAsText }, + { "reload", "(Z)V", + (void*) Reload }, + { "nativeGoBackOrForward", "(I)V", + (void*) GoBackOrForward }, + { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;)V", + (void*) AddJavascriptInterface }, + { "stringByEvaluatingJavaScriptFromString", + "(Ljava/lang/String;)Ljava/lang/String;", + (void*) StringByEvaluatingJavaScriptFromString }, + { "setCacheDisabled", "(Z)V", + (void*) SetCacheDisabled }, + { "cacheDisabled", "()Z", + (void*) CacheDisabled }, + { "clearCache", "()V", + (void*) ClearCache }, + { "documentHasImages", "()Z", + (void*) DocumentHasImages }, + { "hasPasswordField", "()Z", + (void*) HasPasswordField }, + { "getUsernamePassword", "()[Ljava/lang/String;", + (void*) GetUsernamePassword }, + { "setUsernamePassword", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) SetUsernamePassword }, + { "nativeOrientationChanged", "(I)V", + (void*) OrientationChanged }, + { "nativeAuthenticationProceed", "(ILjava/lang/String;Ljava/lang/String;)V", + (void*) AuthenticationProceed }, + { "nativeAuthenticationCancel", "(I)V", + (void*) AuthenticationCancel }, + { "nativeSslCertErrorProceed", "(I)V", + (void*) SslCertErrorProceed }, + { "nativeSslCertErrorCancel", "(II)V", + (void*) SslCertErrorCancel }, +}; + +int registerWebFrame(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/webkit/BrowserFrame"); + LOG_ASSERT(clazz, "Cannot find BrowserFrame"); + gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I"); + LOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame"); + env->DeleteLocalRef(clazz); + + return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame", + gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods)); +} + +} /* namespace android */ |