blob: 1745d589a2e46f2a1071ff928db37f54ecdc0edb [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.webcontents;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.VisibleForTesting;
import org.chromium.content_public.browser.AccessibilitySnapshotCallback;
import org.chromium.content_public.browser.AccessibilitySnapshotNode;
import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.ui.accessibility.AXTextStyle;
import java.util.UUID;
/**
* The WebContentsImpl Java wrapper to allow communicating with the native WebContentsImpl
* object.
*/
@JNINamespace("content")
//TODO(tedchoc): Remove the package restriction once this class moves to a non-public content
// package whose visibility will be enforced via DEPS.
/* package */ class WebContentsImpl implements WebContents {
private static final String PARCEL_VERSION_KEY = "version";
private static final String PARCEL_WEBCONTENTS_KEY = "webcontents";
private static final String PARCEL_PROCESS_GUARD_KEY = "processguard";
private static final long PARCELABLE_VERSION_ID = 0;
// Non-final for testing purposes, so resetting of the UUID can happen.
private static UUID sParcelableUUID = UUID.randomUUID();
/**
* Used to reset the internal tracking for whether or not a serialized {@link WebContents}
* was created in this process or not.
*/
@VisibleForTesting
public static void invalidateSerializedWebContentsForTesting() {
sParcelableUUID = UUID.randomUUID();
}
/**
* A {@link android.os.Parcelable.Creator} instance that is used to build
* {@link WebContentsImpl} objects from a {@link Parcel}.
*/
public static final Parcelable.Creator<WebContents> CREATOR =
new Parcelable.Creator<WebContents>() {
@Override
public WebContents createFromParcel(Parcel source) {
Bundle bundle = source.readBundle();
// Check the version.
if (bundle.getLong(PARCEL_VERSION_KEY, -1) != 0) return null;
// Check that we're in the same process.
ParcelUuid parcelUuid = bundle.getParcelable(PARCEL_PROCESS_GUARD_KEY);
if (sParcelableUUID.compareTo(parcelUuid.getUuid()) != 0) return null;
// Attempt to retrieve the WebContents object from the native pointer.
return nativeFromNativePtr(bundle.getLong(PARCEL_WEBCONTENTS_KEY));
}
@Override
public WebContents[] newArray(int size) {
return new WebContents[size];
}
};
private long mNativeWebContentsAndroid;
private NavigationController mNavigationController;
// Lazily created proxy observer for handling all Java-based WebContentsObservers.
private WebContentsObserverProxy mObserverProxy;
private WebContentsImpl(
long nativeWebContentsAndroid, NavigationController navigationController) {
mNativeWebContentsAndroid = nativeWebContentsAndroid;
mNavigationController = navigationController;
}
@CalledByNative
private static WebContentsImpl create(
long nativeWebContentsAndroid, NavigationController navigationController) {
return new WebContentsImpl(nativeWebContentsAndroid, navigationController);
}
@CalledByNative
private void clearNativePtr() {
mNativeWebContentsAndroid = 0;
mNavigationController = null;
if (mObserverProxy != null) {
mObserverProxy.destroy();
mObserverProxy = null;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// This is wrapped in a Bundle so that failed deserialization attempts don't corrupt the
// overall Parcel. If we failed a UUID or Version check and didn't read the rest of the
// fields it would corrupt the serialized stream.
Bundle data = new Bundle();
data.putLong(PARCEL_VERSION_KEY, PARCELABLE_VERSION_ID);
data.putParcelable(PARCEL_PROCESS_GUARD_KEY, new ParcelUuid(sParcelableUUID));
data.putLong(PARCEL_WEBCONTENTS_KEY, mNativeWebContentsAndroid);
dest.writeBundle(data);
}
@CalledByNative
private long getNativePointer() {
return mNativeWebContentsAndroid;
}
@Override
public void destroy() {
if (mNativeWebContentsAndroid != 0) nativeDestroyWebContents(mNativeWebContentsAndroid);
}
@Override
public boolean isDestroyed() {
return mNativeWebContentsAndroid == 0;
}
@Override
public NavigationController getNavigationController() {
return mNavigationController;
}
@Override
public String getTitle() {
return nativeGetTitle(mNativeWebContentsAndroid);
}
@Override
public String getVisibleUrl() {
return nativeGetVisibleURL(mNativeWebContentsAndroid);
}
@Override
public boolean isLoading() {
return nativeIsLoading(mNativeWebContentsAndroid);
}
@Override
public boolean isLoadingToDifferentDocument() {
return nativeIsLoadingToDifferentDocument(mNativeWebContentsAndroid);
}
@Override
public void stop() {
nativeStop(mNativeWebContentsAndroid);
}
@Override
public void cut() {
nativeCut(mNativeWebContentsAndroid);
}
@Override
public void copy() {
nativeCopy(mNativeWebContentsAndroid);
}
@Override
public void paste() {
nativePaste(mNativeWebContentsAndroid);
}
@Override
public void selectAll() {
nativeSelectAll(mNativeWebContentsAndroid);
}
@Override
public void unselect() {
// Unselect may get triggered when certain selection-related widgets
// are destroyed. As the timing for such destruction is unpredictable,
// safely guard against this case.
if (mNativeWebContentsAndroid == 0) return;
nativeUnselect(mNativeWebContentsAndroid);
}
@Override
public void insertCSS(String css) {
if (mNativeWebContentsAndroid == 0) return;
nativeInsertCSS(mNativeWebContentsAndroid, css);
}
@Override
public void onHide() {
nativeOnHide(mNativeWebContentsAndroid);
}
@Override
public void onShow() {
nativeOnShow(mNativeWebContentsAndroid);
}
@Override
public void releaseMediaPlayers() {
nativeReleaseMediaPlayers(mNativeWebContentsAndroid);
}
@Override
public int getBackgroundColor() {
return nativeGetBackgroundColor(mNativeWebContentsAndroid);
}
@Override
public void showInterstitialPage(
String url, long interstitialPageDelegateAndroid) {
nativeShowInterstitialPage(mNativeWebContentsAndroid, url, interstitialPageDelegateAndroid);
}
@Override
public boolean isShowingInterstitialPage() {
return nativeIsShowingInterstitialPage(mNativeWebContentsAndroid);
}
@Override
public boolean isReady() {
return nativeIsRenderWidgetHostViewReady(mNativeWebContentsAndroid);
}
@Override
public void exitFullscreen() {
nativeExitFullscreen(mNativeWebContentsAndroid);
}
@Override
public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
boolean animate) {
nativeUpdateTopControlsState(mNativeWebContentsAndroid, enableHiding,
enableShowing, animate);
}
@Override
public void showImeIfNeeded() {
nativeShowImeIfNeeded(mNativeWebContentsAndroid);
}
@Override
public void scrollFocusedEditableNodeIntoView() {
// The native side keeps track of whether the zoom and scroll actually occurred. It is
// more efficient to do it this way and sometimes fire an unnecessary message rather
// than synchronize with the renderer and always have an additional message.
nativeScrollFocusedEditableNodeIntoView(mNativeWebContentsAndroid);
}
@Override
public void selectWordAroundCaret() {
nativeSelectWordAroundCaret(mNativeWebContentsAndroid);
}
@Override
public String getUrl() {
return nativeGetURL(mNativeWebContentsAndroid);
}
@Override
public String getLastCommittedUrl() {
return nativeGetLastCommittedURL(mNativeWebContentsAndroid);
}
@Override
public boolean isIncognito() {
return nativeIsIncognito(mNativeWebContentsAndroid);
}
@Override
public void resumeLoadingCreatedWebContents() {
nativeResumeLoadingCreatedWebContents(mNativeWebContentsAndroid);
}
@Override
@VisibleForTesting
public void evaluateJavaScript(String script, JavaScriptCallback callback) {
nativeEvaluateJavaScript(mNativeWebContentsAndroid, script, callback);
}
@Override
public void addMessageToDevToolsConsole(int level, String message) {
nativeAddMessageToDevToolsConsole(mNativeWebContentsAndroid, level, message);
}
@Override
public boolean hasAccessedInitialDocument() {
return nativeHasAccessedInitialDocument(mNativeWebContentsAndroid);
}
@CalledByNative
private static void onEvaluateJavaScriptResult(
String jsonResult, JavaScriptCallback callback) {
callback.handleJavaScriptResult(jsonResult);
}
@Override
public int getThemeColor(int defaultColor) {
int color = nativeGetThemeColor(mNativeWebContentsAndroid);
if (color == Color.TRANSPARENT) return defaultColor;
return (color | 0xFF000000);
}
@Override
public void requestAccessibilitySnapshot(AccessibilitySnapshotCallback callback) {
nativeRequestAccessibilitySnapshot(mNativeWebContentsAndroid, callback);
}
@Override
public void resumeMediaSession() {
nativeResumeMediaSession(mNativeWebContentsAndroid);
}
@Override
public void suspendMediaSession() {
nativeSuspendMediaSession(mNativeWebContentsAndroid);
}
// root node can be null if parsing fails.
@CalledByNative
private static void onAccessibilitySnapshot(AccessibilitySnapshotNode root,
AccessibilitySnapshotCallback callback) {
callback.onAccessibilitySnapshot(root);
}
@CalledByNative
private static void addAccessibilityNodeAsChild(AccessibilitySnapshotNode parent,
AccessibilitySnapshotNode child) {
parent.addChild(child);
}
@CalledByNative
private static AccessibilitySnapshotNode createAccessibilitySnapshotNode(int x,
int y, int scrollX, int scrollY, int width, int height, String text,
int color, int bgcolor, float size, int textStyle, String className) {
AccessibilitySnapshotNode node = new AccessibilitySnapshotNode(x, y, scrollX,
scrollY, width, height, text, className);
// if size is smaller than 0, then style information does not exist.
if (size >= 0.0) {
boolean bold = (textStyle & AXTextStyle.text_style_bold) > 0;
boolean italic = (textStyle & AXTextStyle.text_style_italic) > 0;
boolean underline = (textStyle & AXTextStyle.text_style_underline) > 0;
boolean lineThrough = (textStyle & AXTextStyle.text_style_line_through) > 0;
node.setStyle(color, bgcolor, size, bold, italic, underline, lineThrough);
}
return node;
}
@Override
public void addObserver(WebContentsObserver observer) {
assert mNativeWebContentsAndroid != 0;
if (mObserverProxy == null) mObserverProxy = new WebContentsObserverProxy(this);
mObserverProxy.addObserver(observer);
}
@Override
public void removeObserver(WebContentsObserver observer) {
if (mObserverProxy == null) return;
mObserverProxy.removeObserver(observer);
}
// This is static to avoid exposing a public destroy method on the native side of this class.
private static native void nativeDestroyWebContents(long webContentsAndroidPtr);
private static native WebContents nativeFromNativePtr(long webContentsAndroidPtr);
private native String nativeGetTitle(long nativeWebContentsAndroid);
private native String nativeGetVisibleURL(long nativeWebContentsAndroid);
private native boolean nativeIsLoading(long nativeWebContentsAndroid);
private native boolean nativeIsLoadingToDifferentDocument(long nativeWebContentsAndroid);
private native void nativeStop(long nativeWebContentsAndroid);
private native void nativeCut(long nativeWebContentsAndroid);
private native void nativeCopy(long nativeWebContentsAndroid);
private native void nativePaste(long nativeWebContentsAndroid);
private native void nativeSelectAll(long nativeWebContentsAndroid);
private native void nativeUnselect(long nativeWebContentsAndroid);
private native void nativeInsertCSS(long nativeWebContentsAndroid, String css);
private native void nativeOnHide(long nativeWebContentsAndroid);
private native void nativeOnShow(long nativeWebContentsAndroid);
private native void nativeReleaseMediaPlayers(long nativeWebContentsAndroid);
private native int nativeGetBackgroundColor(long nativeWebContentsAndroid);
private native void nativeShowInterstitialPage(long nativeWebContentsAndroid,
String url, long nativeInterstitialPageDelegateAndroid);
private native boolean nativeIsShowingInterstitialPage(long nativeWebContentsAndroid);
private native boolean nativeIsRenderWidgetHostViewReady(long nativeWebContentsAndroid);
private native void nativeExitFullscreen(long nativeWebContentsAndroid);
private native void nativeUpdateTopControlsState(long nativeWebContentsAndroid,
boolean enableHiding, boolean enableShowing, boolean animate);
private native void nativeShowImeIfNeeded(long nativeWebContentsAndroid);
private native void nativeScrollFocusedEditableNodeIntoView(long nativeWebContentsAndroid);
private native void nativeSelectWordAroundCaret(long nativeWebContentsAndroid);
private native String nativeGetURL(long nativeWebContentsAndroid);
private native String nativeGetLastCommittedURL(long nativeWebContentsAndroid);
private native boolean nativeIsIncognito(long nativeWebContentsAndroid);
private native void nativeResumeLoadingCreatedWebContents(long nativeWebContentsAndroid);
private native void nativeEvaluateJavaScript(long nativeWebContentsAndroid,
String script, JavaScriptCallback callback);
private native void nativeAddMessageToDevToolsConsole(
long nativeWebContentsAndroid, int level, String message);
private native boolean nativeHasAccessedInitialDocument(
long nativeWebContentsAndroid);
private native int nativeGetThemeColor(long nativeWebContentsAndroid);
private native void nativeRequestAccessibilitySnapshot(long nativeWebContentsAndroid,
AccessibilitySnapshotCallback callback);
private native void nativeResumeMediaSession(long nativeWebContentsAndroid);
private native void nativeSuspendMediaSession(long nativeWebContentsAndroid);
}