// Copyright 2013 The Chromium Authors. All rights reserved.
-// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 2013-2014 Intel Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.ViewGroup;
+import android.webkit.ValueCallback;
import android.webkit.WebResourceResponse;
import android.widget.FrameLayout;
+import java.io.IOException;
+import java.io.InputStream;
+
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.ThreadUtils;
import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
-import org.chromium.content.browser.ContentVideoView;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content.browser.ContentViewRenderView;
* This class is the implementation class for XWalkView by calling internal
* various classes.
*/
-public class XWalkContent extends FrameLayout {
+class XWalkContent extends FrameLayout implements XWalkPreferences.KeyValueChangeListener {
+ private static String TAG = "XWalkContent";
private ContentViewCore mContentViewCore;
private ContentView mContentView;
private ContentViewRenderView mContentViewRenderView;
private ActivityWindowAndroid mWindow;
+ private XWalkDevToolsServer mDevToolsServer;
private XWalkView mXWalkView;
private XWalkContentsClientBridge mContentsClientBridge;
private XWalkContentsIoThreadClient mIoThreadClient;
private XWalkGeolocationPermissions mGeolocationPermissions;
private XWalkLaunchScreenManager mLaunchScreenManager;
- int mXWalkContent;
- int mWebContents;
+ long mXWalkContent;
+ long mWebContents;
boolean mReadyToLoad = false;
String mPendingUrl = null;
+ String mPendingData = null;
public XWalkContent(Context context, AttributeSet attrs, XWalkView xwView) {
super(context, attrs);
mContentViewRenderView = new ContentViewRenderView(context, mWindow) {
protected void onReadyToRender() {
if (mPendingUrl != null) {
- doLoadUrl(mPendingUrl);
+ doLoadUrl(mPendingUrl, mPendingData);
mPendingUrl = null;
+ mPendingData = null;
}
mReadyToLoad = true;
MediaPlayerBridge.setResourceLoadingFilter(
new XWalkMediaPlayerResourceLoadingFilter());
+
+ XWalkPreferences.load(this);
}
- void doLoadUrl(String url) {
- //TODO(Xingnan): Configure appropriate parameters here.
+ void doLoadUrl(String url, String content) {
// Handle the same url loading by parameters.
- if (TextUtils.equals(url, mContentView.getUrl())) {
+ if (url != null && !url.isEmpty() &&
+ TextUtils.equals(url, mContentView.getUrl())) {
mContentView.getContentViewCore().reload(true);
} else {
- LoadUrlParams params = new LoadUrlParams(url);
+ LoadUrlParams params = null;
+ if (content == null || content.isEmpty()) {
+ params = new LoadUrlParams(url);
+ } else {
+ params = LoadUrlParams.createLoadDataParamsWithBaseUrl(
+ content, "text/html", false, url, null);
+ }
params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
mContentView.loadUrl(params);
}
mContentView.requestFocus();
}
- public void loadUrl(String url) {
- if (url == null)
+ public void loadUrl(String url, String data) {
+ if ((url == null || url.isEmpty()) &&
+ (data == null || data.isEmpty())) {
return;
+ }
- if (mReadyToLoad)
- doLoadUrl(url);
- else
+ if (mReadyToLoad) {
+ doLoadUrl(url, data);
+ } else {
mPendingUrl = url;
+ mPendingData = data;
+ }
}
- public void reload() {
+ public void reload(int mode) {
if (mReadyToLoad) {
- mContentView.getContentViewCore().reload(true);
+ switch (mode) {
+ case XWalkView.RELOAD_IGNORE_CACHE:
+ mContentView.getContentViewCore().reloadIgnoringCache(true);
+ break;
+ case XWalkView.RELOAD_NORMAL:
+ default:
+ mContentView.getContentViewCore().reload(true);
+
+ }
}
}
}
public void addJavascriptInterface(Object object, String name) {
- mContentViewCore.addJavascriptInterface(object, name);
+ mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name,
+ JavascriptInterface.class);
+ }
+
+ public void evaluateJavascript(String script, ValueCallback<String> callback) {
+ final ValueCallback<String> fCallback = callback;
+ ContentViewCore.JavaScriptCallback coreCallback = new ContentViewCore.JavaScriptCallback() {
+ @Override
+ public void handleJavaScriptResult(String jsonResult) {
+ fCallback.onReceiveValue(jsonResult);
+ }
+ };
+ mContentViewCore.evaluateJavaScript(script, coreCallback);
+ }
+
+ public void setUIClient(XWalkUIClient client) {
+ mContentsClientBridge.setUIClient(client);
+ }
+
+ public void setResourceClient(XWalkResourceClient client) {
+ mContentsClientBridge.setResourceClient(client);
}
public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
mContentView.goForward();
}
+ void navigateTo(int offset) {
+ mContentView.getContentViewCore().goToOffset(offset);
+ }
+
public void stopLoading() {
mContentView.getContentViewCore().stopLoading();
}
return null;
}
- public String getVersion() {
+ public String getXWalkVersion() {
if (mXWalkContent == 0) return "";
return nativeGetVersion(mXWalkContent);
}
return mSettings;
}
- public void loadAppFromManifest(String path, String manifest) {
- if (path == null || manifest == null || mXWalkContent == 0) {
+ public void loadAppFromManifest(String url, String data) {
+ if (mXWalkContent == 0 ||
+ ((url == null || url.isEmpty()) &&
+ (data == null || data.isEmpty()))) {
return;
}
- if (!nativeSetManifest(mXWalkContent, path, manifest)) {
- throw new RuntimeException("Failed to parse the manifest file.");
+ String content = data;
+ // If the data of manifest.json is not set, try to load it.
+ if (data == null || data.isEmpty()) {
+ try {
+ content = AndroidProtocolHandler.getUrlContent(mXWalkView.getActivity(), url);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read the manifest: " + url);
+ }
+ }
+
+ // Calculate the base url of manifestUrl. Used by native side.
+ // TODO(yongsheng): It's from runtime side. Need to find a better way
+ // to get base url.
+ String baseUrl = url;
+ int position = url.lastIndexOf("/");
+ if (position != -1) {
+ baseUrl = url.substring(0, position + 1);
+ } else {
+ Log.w(TAG, "The url of manifest.json is probably not set correctly.");
+ }
+
+ if (!nativeSetManifest(mXWalkContent, baseUrl, content)) {
+ throw new RuntimeException("Failed to parse the manifest file: " + url);
}
}
- public WebBackForwardList copyBackForwardList() {
- return new WebBackForwardList(mContentViewCore.getNavigationHistory());
+ public XWalkNavigationHistory getNavigationHistory() {
+ return new XWalkNavigationHistory(mXWalkView, mContentViewCore.getNavigationHistory());
}
public static final String SAVE_RESTORE_STATE_KEY = "XWALKVIEW_STATE";
- public WebBackForwardList saveState(Bundle outState) {
+ public XWalkNavigationHistory saveState(Bundle outState) {
if (outState == null) return null;
byte[] state = nativeGetState(mXWalkContent);
if (state == null) return null;
outState.putByteArray(SAVE_RESTORE_STATE_KEY, state);
- return copyBackForwardList();
+ return getNavigationHistory();
}
- public WebBackForwardList restoreState(Bundle inState) {
+ public XWalkNavigationHistory restoreState(Bundle inState) {
if (inState == null) return null;
byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
mContentsClientBridge.onUpdateTitle(mContentViewCore.getTitle());
}
- return result ? copyBackForwardList() : null;
+ return result ? getNavigationHistory() : null;
}
- public boolean isFullscreen() {
- return mWebContents != 0 && getXWalkWebChromeClient() != null &&
- getXWalkWebChromeClient().isFullscreen();
+ boolean hasEnteredFullscreen() {
+ return mContentsClientBridge.hasEnteredFullscreen();
}
- public void exitFullscreen() {
- if (isFullscreen()) {
+ void exitFullscreen() {
+ if (hasEnteredFullscreen()) {
mContentsClientBridge.exitFullscreen(mWebContents);
}
}
@CalledByNative
public void onGetUrlFromManifest(String url) {
if (url != null && !url.isEmpty()) {
- loadUrl(url);
+ loadUrl(url, null);
}
}
@CalledByNative
- public void onGetUrlAndLaunchScreenFromManifest(String url, String readyWhen) {
+ public void onGetUrlAndLaunchScreenFromManifest(String url, String readyWhen, String imageBorder) {
if (url == null || url.isEmpty()) return;
- mLaunchScreenManager.displayLaunchScreen(readyWhen);
+ mLaunchScreenManager.displayLaunchScreen(readyWhen, imageBorder);
mContentsClientBridge.registerPageLoadListener(mLaunchScreenManager);
- loadUrl(url);
+ loadUrl(url, null);
}
@CalledByNative
public void onGetFullscreenFlagFromManifest(boolean enterFullscreen) {
- if (enterFullscreen) getXWalkWebChromeClient().onToggleFullscreen(true);
+ if (enterFullscreen) mContentsClientBridge.onToggleFullscreen(true);
}
public void destroy() {
if (mXWalkContent == 0) return;
+ XWalkPreferences.unload(this);
// Reset existing notification service in order to destruct it.
setNotificationService(null);
// Remove its children used for page rendering from view hierarchy.
public InterceptedRequestData shouldInterceptRequest(final String url,
boolean isMainFrame) {
+ // Notify a resource load is started. This is not the best place to start the callback
+ // but it's a workable way.
+ mContentsClientBridge.getCallbackHelper().postOnResourceLoadStarted(url);
+
WebResourceResponse webResourceResponse = mContentsClientBridge.shouldInterceptRequest(url);
InterceptedRequestData interceptedRequestData = null;
mContentsClientBridge.getCallbackHelper().postOnLoadResource(url);
} else {
if (isMainFrame && webResourceResponse.getData() == null) {
- mContentsClientBridge.getCallbackHelper().postOnReceivedError(-1, null, url);
+ mContentsClientBridge.getCallbackHelper().postOnReceivedError(
+ XWalkResourceClient.ERROR_UNKNOWN, null, url);
}
interceptedRequestData = new InterceptedRequestData(webResourceResponse.getMimeType(),
webResourceResponse.getEncoding(),
mContentsClientBridge.onGeolocationPermissionsHidePrompt();
}
- private native int nativeInit(XWalkWebContentsDelegate webViewContentsDelegate,
+ public String enableRemoteDebugging(int allowedUid) {
+ // Chrome looks for "devtools_remote" pattern in the name of a unix domain socket
+ // to identify a debugging page
+ final String socketName = getContext().getApplicationContext().getPackageName() + "_devtools_remote";
+ if (mDevToolsServer == null) {
+ mDevToolsServer = new XWalkDevToolsServer(socketName);
+ mDevToolsServer.allowConnectionFromUid(allowedUid);
+ mDevToolsServer.setRemoteDebuggingEnabled(true);
+ }
+ // devtools/page is hardcoded in devtools_http_handler_impl.cc (kPageUrlPrefix)
+ return "ws://" + socketName + "/devtools/page/" + devToolsAgentId();
+ }
+
+ // Enables remote debugging and returns the URL at which the dev tools server is listening
+ // for commands. Only the current process is allowed to connect to the server.
+ String enableRemoteDebugging() {
+ return enableRemoteDebugging(getContext().getApplicationInfo().uid);
+ }
+
+ void disableRemoteDebugging() {
+ if (mDevToolsServer == null) return;
+
+ if (mDevToolsServer.isRemoteDebuggingEnabled()) {
+ mDevToolsServer.setRemoteDebuggingEnabled(false);
+ }
+ mDevToolsServer.destroy();
+ mDevToolsServer = null;
+ }
+
+ @Override
+ public void onKeyValueChanged(String key, boolean value) {
+ if (key == XWalkPreferences.REMOTE_DEBUGGING) {
+ if (value) enableRemoteDebugging();
+ else disableRemoteDebugging();
+ }
+ }
+
+ public void setOverlayVideoMode(boolean enabled) {
+ if (mContentViewRenderView != null) {
+ mContentViewRenderView.setOverlayVideoMode(enabled);
+ }
+ }
+
+ private native long nativeInit(XWalkWebContentsDelegate webViewContentsDelegate,
XWalkContentsClientBridge bridge);
- private static native void nativeDestroy(int nativeXWalkContent);
- private native int nativeGetWebContents(int nativeXWalkContent,
+ private static native void nativeDestroy(long nativeXWalkContent);
+ private native long nativeGetWebContents(long nativeXWalkContent,
XWalkContentsIoThreadClient ioThreadClient,
InterceptNavigationDelegate delegate);
- private native void nativeClearCache(int nativeXWalkContent, boolean includeDiskFiles);
- private native String nativeDevToolsAgentId(int nativeXWalkContent);
- private native String nativeGetVersion(int nativeXWalkContent);
- private native void nativeSetJsOnlineProperty(int nativeXWalkContent, boolean networkUp);
- private native boolean nativeSetManifest(int nativeXWalkContent, String path, String manifest);
- private native int nativeGetRoutingID(int nativeXWalkContent);
+ private native void nativeClearCache(long nativeXWalkContent, boolean includeDiskFiles);
+ private native String nativeDevToolsAgentId(long nativeXWalkContent);
+ private native String nativeGetVersion(long nativeXWalkContent);
+ private native void nativeSetJsOnlineProperty(long nativeXWalkContent, boolean networkUp);
+ private native boolean nativeSetManifest(long nativeXWalkContent, String path, String manifest);
+ private native int nativeGetRoutingID(long nativeXWalkContent);
private native void nativeInvokeGeolocationCallback(
- int nativeXWalkContent, boolean value, String requestingFrame);
- private native byte[] nativeGetState(int nativeXWalkContent);
- private native boolean nativeSetState(int nativeXWalkContent, byte[] state);
+ long nativeXWalkContent, boolean value, String requestingFrame);
+ private native byte[] nativeGetState(long nativeXWalkContent);
+ private native boolean nativeSetState(long nativeXWalkContent, byte[] state);
}