blob: 4a9f21dffbbe035bcd6b46954e72c47daf476f4e [file] [log] [blame]
/*
* 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 "GeolocationClientAndroid.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 "JavaClassJobjectV8.h"
#include "JavaNPObjectV8.h"
#include "JavaInstanceJobjectV8.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 "WebArchiveAndroid.h"
#include "WebCache.h"
#include "WebCoreJni.h"
#include "WebHistory.h"
#include "WebIconDatabase.h"
#include "WebFrameView.h"
#include "WebUrlLoaderClient.h"
#include "WebViewCore.h"
#include "jni.h"
#include "wds/DebugServer.h"
#include <JNIUtility.h>
#include <JNIHelp.h>
#include <ScopedPrimitiveArray.h>
#include <ScopedLocalRef.h>
#include <SkGraphics.h>
#include <android_runtime/android_util_AssetManager.h>
#include <openssl/x509.h>
#include <utils/misc.h>
#include <androidfw/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 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 mRequestClientCert;
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->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;ZZ)V");
mJavaFrame->mReportSslCertError = env->GetMethodID(clazz, "reportSslCertError", "(II[BLjava/lang/String;)V");
mJavaFrame->mRequestClientCert = env->GetMethodID(clazz, "requestClientCert", "(ILjava/lang/String;)V");
mJavaFrame->mDownloadStart = env->GetMethodID(clazz, "downloadStart",
"(Ljava/lang/String;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);
ALOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword");
ALOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest");
ALOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted");
ALOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted");
ALOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished");
ALOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError");
ALOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle");
ALOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared");
ALOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress");
ALOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon");
ALOG_ASSERT(mJavaFrame->mDidReceiveTouchIconUrl, "Could not find method didReceiveTouchIconUrl");
ALOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory");
ALOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl");
ALOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow");
ALOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow");
ALOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission");
ALOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus");
ALOG_ASSERT(mJavaFrame->mGetRawResFilename, "Could not find method getRawResFilename");
ALOG_ASSERT(mJavaFrame->mDensity, "Could not find method density");
ALOG_ASSERT(mJavaFrame->mGetFileSize, "Could not find method getFileSize");
ALOG_ASSERT(mJavaFrame->mGetFile, "Could not find method getFile");
ALOG_ASSERT(mJavaFrame->mDidReceiveAuthenticationChallenge, "Could not find method didReceiveAuthenticationChallenge");
ALOG_ASSERT(mJavaFrame->mReportSslCertError, "Could not find method reportSslCertError");
ALOG_ASSERT(mJavaFrame->mRequestClientCert, "Could not find method requestClientCert");
ALOG_ASSERT(mJavaFrame->mDownloadStart, "Could not find method downloadStart");
ALOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData");
ALOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading");
ALOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate");
ALOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData");
ALOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData");
ALOG_ASSERT(mJavaFrame->mAutoLogin, "Could not find method autoLogin");
mUserAgent = WTF::String();
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");
ALOG_ASSERT(mapClass, "Could not find HashMap class!");
jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
ALOG_ASSERT(init, "Could not find constructor for HashMap");
jobject hashMap = env->NewObject(mapClass, init, map.size());
ALOG_ASSERT(hashMap, "Could not create a new HashMap");
jmethodID put = env->GetMethodID(mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
ALOG_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;
};
UrlInterceptResponse*
WebFrame::shouldInterceptRequest(const WTF::String& url)
{
ALOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data());
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return 0;
jstring urlStr = wtfStringToJstring(env, url);
jobject response = env->CallObjectMethod(javaFrame.get(), mJavaFrame->mShouldInterceptRequest, urlStr);
env->DeleteLocalRef(urlStr);
if (response == 0)
return 0;
UrlInterceptResponse* result = new UrlInterceptResponse(env, response);
env->DeleteLocalRef(response);
return result;
}
void
WebFrame::reportError(int errorCode, const WTF::String& description,
const WTF::String& failingUrl)
{
ALOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
jstring descStr = wtfStringToJstring(env, description);
jstring failUrl = wtfStringToJstring(env, failingUrl);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mReportError, errorCode, descStr, failUrl);
env->DeleteLocalRef(descStr);
env->DeleteLocalRef(failUrl);
}
WTF::String
WebFrame::convertIDNToUnicode(const WebCore::KURL& url) {
WTF::String converted = url.string();
const WTF::String host = url.host();
if (host.find("xn--") == notFound) // no punycode IDN found.
return converted;
std::wstring languages;
const WTF::CString cHost = host.utf8();
std::wstring result = net::IDNToUnicode(cHost.data(), cHost.length(), languages, 0);
const WTF::String convertedHost = String::fromUTF8(WideToUTF8(result).c_str());
if (convertedHost.length() && convertedHost.length() != host.length()) {
WebCore::KURL newUrl = url;
newUrl.setHost(convertedHost);
converted = newUrl.string();
}
return converted;
}
void
WebFrame::loadStarted(WebCore::Frame* frame)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
// activeDocumentLoader() can return null.
DocumentLoader* documentLoader = frame->loader()->activeDocumentLoader();
if (documentLoader == NULL)
return;
const WebCore::KURL& url = documentLoader->url();
if (url.isEmpty())
return;
ALOGV("::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;
const WTF::String urlString = convertIDNToUnicode(url);
// 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) {
// FIXME: This method should not be used from outside WebCore and will be removed.
// http://trac.webkit.org/changeset/81484
WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(urlString, WebCore::IntSize(16, 16));
if (icon)
favicon = webcoreImageToJavaBitmap(env, icon);
ALOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data());
}
jstring urlStr = wtfStringToJstring(env, urlString);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mLoadStarted, urlStr, favicon, static_cast<int>(loadType), isMainFrame);
checkException(env);
env->DeleteLocalRef(urlStr);
if (favicon)
env->DeleteLocalRef(favicon);
// The main frame has started a new load.
if (isMainFrame && mPage)
WebViewCore::getWebViewCore(mPage->mainFrame()->view())->geolocationManager()->resetRealClientTemporaryPermissionStates();
}
void
WebFrame::transitionToCommitted(WebCore::Frame* frame)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
WebCore::FrameLoadType loadType = frame->loader()->loadType();
bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mTransitionToCommitted, static_cast<int>(loadType), isMainFrame);
checkException(env);
}
void
WebFrame::didFinishLoad(WebCore::Frame* frame)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
// 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;
ALOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data());
bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
WebCore::FrameLoadType loadType = loader->loadType();
const WTF::String urlString = convertIDNToUnicode(url);
jstring urlStr = wtfStringToJstring(env, urlString);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mLoadFinished, urlStr, static_cast<int>(loadType), isMainFrame);
checkException(env);
env->DeleteLocalRef(urlStr);
}
void
WebFrame::addHistoryItem(WebCore::HistoryItem* item)
{
ALOGV("::WebCore:: addHistoryItem");
JNIEnv* env = getJNIEnv();
WebHistory::AddItem(mJavaFrame->history(env), item);
}
void
WebFrame::removeHistoryItem(int index)
{
ALOGV("::WebCore:: removeHistoryItem at %d", index);
JNIEnv* env = getJNIEnv();
WebHistory::RemoveItem(mJavaFrame->history(env), index);
}
void
WebFrame::updateHistoryIndex(int newIndex)
{
ALOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
JNIEnv* env = getJNIEnv();
WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
}
void
WebFrame::setTitle(const WTF::String& title)
{
#ifndef NDEBUG
ALOGV("setTitle(%s)", title.ascii().data());
#endif
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
jstring jTitleStr = wtfStringToJstring(env, title);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mSetTitle, jTitleStr);
checkException(env);
env->DeleteLocalRef(jTitleStr);
}
void
WebFrame::windowObjectCleared(WebCore::Frame* frame)
{
ALOGV("::WebCore:: windowObjectCleared");
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mWindowObjectCleared, (int)frame);
checkException(env);
}
void
WebFrame::setProgress(float newProgress)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
int progress = static_cast<int>(100 * newProgress);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mSetProgress, progress);
checkException(env);
}
const WTF::String
WebFrame::userAgentForURL(const WebCore::KURL* url)
{
return mUserAgent;
}
void
WebFrame::didReceiveIcon(WebCore::Image* icon)
{
ALOG_ASSERT(icon, "DidReceiveIcon called without an image!");
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
jobject bitmap = webcoreImageToJavaBitmap(env, icon);
if (!bitmap)
return;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDidReceiveIcon, bitmap);
env->DeleteLocalRef(bitmap);
checkException(env);
}
void
WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
jstring jUrlStr = wtfStringToJstring(env, url);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDidReceiveTouchIconUrl, jUrlStr, precomposed);
env->DeleteLocalRef(jUrlStr);
checkException(env);
}
void
WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
const WTF::String urlStr = convertIDNToUnicode(url);
jstring jUrlStr = wtfStringToJstring(env, urlStr);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload);
env->DeleteLocalRef(jUrlStr);
checkException(env);
}
bool
WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return true;
// Always handle "POST" in place
if (equalIgnoringCase(request.httpMethod(), "POST"))
return true;
const WebCore::KURL& requestUrl = request.url();
const WTF::String& url = requestUrl.string();
// Empty URLs should not be sent to Java
if (url.isEmpty())
return true;
jstring jUrlStr = wtfStringToJstring(env, url);
// Delegate to the Java side to make the decision. Note that the sense of
// the return value of the Java method is reversed. It will return true if
// the embedding application wishes to hijack the load and hence the WebView
// should _not_ proceed with the load.
jboolean ret = env->CallBooleanMethod(javaFrame.get(), mJavaFrame->mHandleUrl, jUrlStr);
checkException(env);
env->DeleteLocalRef(jUrlStr);
return ret == JNI_FALSE;
}
bool
WebFrame::shouldSaveFormData()
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return false;
jboolean ret = env->CallBooleanMethod(javaFrame.get(), mJavaFrame->mShouldSaveFormData);
checkException(env);
return ret;
}
WebCore::Frame*
WebFrame::createWindow(bool dialog, bool userGesture)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return 0;
jobject obj = env->CallObjectMethod(javaFrame.get(), mJavaFrame->mCreateWindow, dialog, userGesture);
if (!obj)
return 0;
return GET_NATIVE_FRAME(env, obj);
}
void
WebFrame::requestFocus() const
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mRequestFocus);
checkException(env);
}
void
WebFrame::closeWindow(WebViewCore* webViewCore)
{
assert(webViewCore);
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
AutoJObject javaObject = webViewCore->getJavaObject();
if (!javaObject.get())
return;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mCloseWindow, javaObject.get());
}
struct PolicyFunctionWrapper {
WebCore::FramePolicyFunction func;
};
void
WebFrame::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
PolicyFunctionWrapper* p = new PolicyFunctionWrapper;
p->func = func;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDecidePolicyForFormResubmission, p);
}
WTF::String
WebFrame::getRawResourceFilename(WebCore::PlatformBridge::rawResId id) const
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return String();
jstring ret = (jstring) env->CallObjectMethod(javaFrame.get(), mJavaFrame->mGetRawResFilename, static_cast<int>(id));
return jstringToWtfString(env, ret);
}
float
WebFrame::density() const
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return 0.0;
jfloat dpi = env->CallFloatMethod(javaFrame.get(), mJavaFrame->mDensity);
checkException(env);
return dpi;
}
void
WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials, bool suppressDialog)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
int jHandle = reinterpret_cast<int>(client);
jstring jHost = stdStringToJstring(env, host, true);
jstring jRealm = stdStringToJstring(env, realm, true);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDidReceiveAuthenticationChallenge, jHandle, jHost, jRealm, useCachedCredentials, suppressDialog);
env->DeleteLocalRef(jHost);
env->DeleteLocalRef(jRealm);
checkException(env);
}
void
WebFrame::reportSslCertError(WebUrlLoaderClient* client, int error, const std::string& cert, const std::string& url)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
int jHandle = reinterpret_cast<int>(client);
// Don't copy the null terminator.
int len = cert.length();
ScopedLocalRef<jbyteArray> jCert(env, env->NewByteArray(len));
env->SetByteArrayRegion(jCert.get(), 0, len, reinterpret_cast<const jbyte*>(cert.c_str()));
ScopedLocalRef<jstring> jUrl(env, env->NewStringUTF(url.c_str()));
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mReportSslCertError, jHandle, error, jCert.get(), jUrl.get());
checkException(env);
}
void
WebFrame::requestClientCert(WebUrlLoaderClient* client, const std::string& hostAndPort)
{
JNIEnv* env = getJNIEnv();
int jHandle = reinterpret_cast<int>(client);
int len = hostAndPort.length();
ScopedLocalRef<jstring> jHostAndPort(env, stdStringToJstring(env, hostAndPort, true));
env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mRequestClientCert, jHandle, jHostAndPort.get());
checkException(env);
}
void
WebFrame::downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, const std::string& referer, long long contentLength)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
jstring jUrl = stdStringToJstring(env, url, true);
jstring jUserAgent = stdStringToJstring(env, userAgent, true);
jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true);
jstring jMimetype = stdStringToJstring(env, mimetype, true);
jstring jReferer = stdStringToJstring(env, referer, true);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDownloadStart, jUrl, jUserAgent, jContentDisposition, jMimetype, jReferer, contentLength);
env->DeleteLocalRef(jUrl);
env->DeleteLocalRef(jUserAgent);
env->DeleteLocalRef(jContentDisposition);
env->DeleteLocalRef(jMimetype);
env->DeleteLocalRef(jReferer);
checkException(env);
}
void
WebFrame::didReceiveData(const char* data, int size) {
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
ScopedLocalRef<jbyteArray> jData(env, env->NewByteArray(size));
env->SetByteArrayRegion(jData.get(), 0, size, reinterpret_cast<const jbyte*>(data));
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDidReceiveData, jData.get(), size);
checkException(env);
}
void
WebFrame::didFinishLoading() {
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDidFinishLoading);
checkException(env);
}
void WebFrame::setCertificate(const std::string& cert)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
int len = cert.length();
ScopedLocalRef<jbyteArray> jCert(env, env->NewByteArray(len));
env->SetByteArrayRegion(jCert.get(), 0, len, reinterpret_cast<const jbyte*>(cert.c_str()));
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mSetCertificate, jCert.get());
checkException(env);
}
void WebFrame::autoLogin(const std::string& loginHeader)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
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;
jstring jRealm = wtfStringToJstring(env, realm, true);
jstring jAccount = wtfStringToJstring(env, account);
jstring jArgs = wtfStringToJstring(env, args, true);
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mAutoLogin, jRealm, jAccount, jArgs);
}
}
void WebFrame::maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
if (request.httpMethod() != "POST")
return;
WTF::String username;
WTF::String password;
if (!getUsernamePasswordFromDom(frame, username, password))
return;
jstring jUsername = wtfStringToJstring(env, username);
jstring jPassword = wtfStringToJstring(env, password);
jbyteArray jPostData = getPostData(request);
if (jPostData)
env->CallVoidMethod(javaFrame.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::RefPtr<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)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return 0;
jbyteArray jPostDataStr = 0;
WebCore::FormData* formdata = request.httpBody();
if (formdata) {
// 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(javaFrame.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(javaFrame.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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!");
PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func;
ALOG_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();
// needs to be called before any other chromium code
initChromium();
// Create a new page
ChromeClientAndroid* chromeC = new ChromeClientAndroid;
EditorClientAndroid* editorC = new EditorClientAndroid;
DeviceMotionClientAndroid* deviceMotionC = new DeviceMotionClientAndroid;
DeviceOrientationClientAndroid* deviceOrientationC = new DeviceOrientationClientAndroid;
GeolocationClientAndroid* geolocationC = new GeolocationClientAndroid;
WebCore::Page::PageClients pageClients;
pageClients.chromeClient = chromeC;
pageClients.contextMenuClient = new ContextMenuClientAndroid;
pageClients.editorClient = editorC;
pageClients.dragClient = new DragClientAndroid;
#if ENABLE(INSPECTOR)
pageClients.inspectorClient = new InspectorClientAndroid;
#endif
pageClients.deviceMotionClient = deviceMotionC;
pageClients.deviceOrientationClient = deviceOrientationC;
pageClients.geolocationClient = geolocationC;
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);
geolocationC->setWebViewCore(webViewCore);
// Allow local access to file:/// and substitute data
WebCore::SecurityOrigin::setLocalLoadPolicy(
WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData);
ALOGV("::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())
ALOGE("Can't find the drawable directory");
else {
// Initialize our skinning classes
webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!");
ALOGV("::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;
// Force remove all deleted pages in the page cache
WebCore::pageCache()->releaseAutoreleasedPagesNow();
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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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);
}
ALOGV("LoadUrl %s", kurl.string().latin1().data());
pFrame->loader()->load(request, false);
}
static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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);
}
ALOGV("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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!");
// Setup the resource request
WebCore::ResourceRequest request(jstringToWtfString(env, baseUrl));
// Setup the substituteData
WTF::CString cData = jstringToWtfString(env, data).utf8();
const char* dataStr = cData.data();
WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer =
WebCore::SharedBuffer::create();
ALOG_ASSERT(dataStr, "nativeLoadData has a null data string.");
sharedBuffer->append(dataStr, strlen(dataStr)); // copy 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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!");
ALOGV("::WebCore:: stopLoading %p", pFrame);
// Stop loading the page and do not send an unload event
pFrame->loader()->stopForUserCancel();
}
#if ENABLE(WEB_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 // ENABLE(WEB_ARCHIVE)
static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboolean autoname)
{
#if ENABLE(WEB_ARCHIVE)
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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()) {
ALOGD("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) {
ALOGD("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);
#endif // ENABLE(WEB_ARCHIVE)
return NULL;
}
static jstring ExternalRepresentation(JNIEnv *env, jobject obj)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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 JavaInstanceJobject {
public:
static PassRefPtr<WeakJavaInstance> create(jobject obj, bool requireAnnotation)
{
return adoptRef(new WeakJavaInstance(obj, requireAnnotation));
}
private:
WeakJavaInstance(jobject instance, bool requireAnnotation)
: JavaInstanceJobject(instance, requireAnnotation)
, m_beginEndDepth(0)
{
JNIEnv* env = getJNIEnv();
// JavaInstance creates a global ref to instance in its constructor.
env->DeleteGlobalRef(m_instance->instance());
// Create a weak ref, cache it, and set the underlying JavaInstance to use it.
m_weakRef = env->NewWeakGlobalRef(instance);
m_instance->setInstance(m_weakRef);
}
~WeakJavaInstance()
{
ALOG_ASSERT(!m_beginEndDepth, "Unbalanced calls to WeakJavaInstance::begin() / end()");
JNIEnv* env = getJNIEnv();
// 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(m_weakRef));
// Delete the weak reference.
env->DeleteWeakGlobalRef(m_weakRef);
}
virtual void begin()
{
if (m_beginEndDepth++ > 0)
return;
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_instance->setInstance(getRealObject(env, m_weakRef).release());
// Call the base class method
INHERITED::begin();
}
virtual void end()
{
if (--m_beginEndDepth > 0)
return;
// Call the base class method first to pop the local frame.
INHERITED::end();
// Get rid of the local reference to the real object.
getJNIEnv()->DeleteLocalRef(m_instance->instance());
// Point back to the WeakReference.
m_instance->setInstance(m_weakRef);
}
private:
typedef JavaInstanceJobject INHERITED;
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, jboolean requireAnnotation)
{
WebCore::Frame* pFrame = 0;
if (nativeFramePointer == 0)
pFrame = GET_NATIVE_FRAME(env, obj);
else
pFrame = (WebCore::Frame*)nativeFramePointer;
ALOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
JavaVM* vm;
env->GetJavaVM(&vm);
ALOGV("::WebCore:: addJSInterface: %p", pFrame);
if (pFrame) {
RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj,
requireAnnotation);
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);
}
}
static void ClearWebCoreCache()
{
if (!WebCore::memoryCache()->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::memoryCache()->setDisabled(true);
WebCore::memoryCache()->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()
{
WebCache::get(false /*privateBrowsing*/)->clear();
}
static void ClearCache(JNIEnv *env, jobject obj)
{
ClearWebCoreCache();
ClearWebViewCache();
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
pFrame->script()->lowMemoryNotification();
}
static jboolean DocumentHasImages(JNIEnv *env, jobject obj)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!");
return pFrame->document()->images()->length() > 0;
}
static jboolean HasPasswordField(JNIEnv *env, jobject obj)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!");
bool found = false;
WTF::RefPtr<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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_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)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!");
WebCore::HTMLInputElement* usernameEle = NULL;
WebCore::HTMLInputElement* passwordEle = NULL;
bool found = false;
WTF::RefPtr<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)
{
JNIEnv* env = getJNIEnv();
AutoJObject javaFrame = mJavaFrame->frame(env);
if (!javaFrame.get())
return;
if (form->autoComplete()) {
JNIEnv* env = getJNIEnv();
jclass mapClass = env->FindClass("java/util/HashMap");
ALOG_ASSERT(mapClass, "Could not find HashMap class!");
jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
ALOG_ASSERT(init, "Could not find constructor for HashMap");
jobject hashMap = env->NewObject(mapClass, init, 1);
ALOG_ASSERT(hashMap, "Could not create a new HashMap");
jmethodID put = env->GetMethodID(mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
ALOG_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);
ALOG_ASSERT(key && val, "name or value not set");
env->CallObjectMethod(hashMap, put, key, val);
env->DeleteLocalRef(key);
env->DeleteLocalRef(val);
}
}
}
}
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mSaveFormData, hashMap);
env->DeleteLocalRef(hashMap);
env->DeleteLocalRef(mapClass);
}
}
static void OrientationChanged(JNIEnv *env, jobject obj, int orientation)
{
WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
ALOGV("Sending orientation: %d", orientation);
pFrame->sendOrientationChangeEvent(orientation);
}
static jboolean GetShouldStartScrolledRight(JNIEnv *env, jobject obj,
jint browserFrame)
{
jboolean startScrolledRight = false; // default is start scrolled left
WebCore::Frame* frame = reinterpret_cast<WebCore::Frame*>(browserFrame);
WebCore::Document* document = frame->document();
if (document) {
RenderStyle* style = document->renderer()->style();
WritingMode writingMode = style->writingMode();
ALOG_ASSERT(writingMode != WebCore::BottomToTopWritingMode,
"BottomToTopWritingMode isn't possible in any "
"language and cannot be specified in w3c writing-mode.");
if (writingMode == WebCore::RightToLeftWritingMode)
startScrolledRight = true; // vertical-rl pages start scrolled right
else if (writingMode == WebCore::TopToBottomWritingMode)
startScrolledRight = !style->isLeftToRightDirection(); // RTL starts right
}
return startScrolledRight;
}
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);
}
static scoped_refptr<net::X509Certificate> getX509Cert(JNIEnv *env, jobjectArray chain)
{
// Based on Android's NativeCrypto_SSL_use_certificate
int length = env->GetArrayLength(chain);
if (length == 0) {
return NULL;
}
base::ScopedOpenSSL<X509, X509_free> first;
ScopedVector<base::ScopedOpenSSL<X509, X509_free> > rest;
for (int i = 0; i < length; i++) {
ScopedLocalRef<jbyteArray> cert(env,
reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(chain, i)));
if (cert.get() == NULL) {
return NULL;
}
ScopedByteArrayRO certBytes(env, cert.get());
if (certBytes.get() == NULL) {
return NULL;
}
const char* data = reinterpret_cast<const char*>(certBytes.get());
int length = certBytes.size();
X509* x509 = net::X509Certificate::CreateOSCertHandleFromBytes(data, length);
if (x509 == NULL) {
return NULL;
}
if (i == 0) {
first.reset(x509);
} else {
rest.push_back(new base::ScopedOpenSSL<X509, X509_free>(x509));
}
}
std::vector<X509*> certChain(rest.size());
for (size_t i = 0; i < rest.size(); i++) {
certChain[i] = rest[i]->get();
}
return net::X509Certificate::CreateFromHandle(first.get(),
net::X509Certificate::SOURCE_FROM_NETWORK,
certChain);
}
static void SslClientCertPKCS8(JNIEnv *env, jobject obj, int handle, jbyteArray pkey, jobjectArray chain)
{
WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
if (pkey == NULL || chain == NULL) {
client->sslClientCert(NULL, NULL);
return;
}
// Based on Android's NativeCrypto_SSL_use_PrivateKey
ScopedByteArrayRO pkeyBytes(env, pkey);
if (pkeyBytes.get() == NULL) {
client->sslClientCert(NULL, NULL);
return;
}
base::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8;
const unsigned char* pkeyChars = reinterpret_cast<const unsigned char*>(pkeyBytes.get());
pkcs8.reset(d2i_PKCS8_PRIV_KEY_INFO(NULL, &pkeyChars, pkeyBytes.size()));
if (!pkcs8.get()) {
client->sslClientCert(NULL, NULL);
return;
}
base::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> privateKey(EVP_PKCS82PKEY(pkcs8.get()));
if (!privateKey.get()) {
client->sslClientCert(NULL, NULL);
return;
}
scoped_refptr<net::X509Certificate> certificate = getX509Cert(env, chain);
if (certificate == NULL) {
client->sslClientCert(NULL, NULL);
return;
}
client->sslClientCert(privateKey.release(), certificate);
}
static void SslClientCertCtx(JNIEnv *env, jobject obj, int handle, jlong ctx, jobjectArray chain)
{
WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(static_cast<uintptr_t>(ctx));
if (pkey == NULL || chain == NULL) {
client->sslClientCert(NULL, NULL);
return;
}
scoped_refptr<net::X509Certificate> certificate = getX509Cert(env, chain);
if (certificate == NULL) {
client->sslClientCert(NULL, NULL);
return;
}
CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
client->sslClientCert(pkey, certificate);
}
// ----------------------------------------------------------------------------
/*
* 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;Z)V",
(void*) AddJavascriptInterface },
{ "stringByEvaluatingJavaScriptFromString",
"(Ljava/lang/String;)Ljava/lang/String;",
(void*) StringByEvaluatingJavaScriptFromString },
{ "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 },
{ "nativeSslClientCert", "(IJ[[B)V",
(void*) SslClientCertCtx },
{ "nativeSslClientCert", "(I[B[[B)V",
(void*) SslClientCertPKCS8 },
{ "nativeGetShouldStartScrolledRight", "(I)Z",
(void*) GetShouldStartScrolledRight },
};
int registerWebFrame(JNIEnv* env)
{
JavaClassJobject::RegisterJavaClassJobject(env);
jclass clazz = env->FindClass("android/webkit/BrowserFrame");
ALOG_ASSERT(clazz, "Cannot find BrowserFrame");
gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
ALOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame");
env->DeleteLocalRef(clazz);
return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
}
} /* namespace android */