fix android content_shell

This commit is contained in:
Alexander Frick 2023-03-30 17:15:45 -05:00
parent 7b0c92dc8d
commit 59a4a5a4fd
7 changed files with 10 additions and 643 deletions

View file

@ -54,7 +54,7 @@ buildARM32 () {
# Build Thorium for ARM32
export NINJA_SUMMARIZE_BUILD=1 &&
./depot_tools/autoninja -C ~/chromium/src/out/thorium chrome_public_apk system_webview_apk -j$@ &&
./depot_tools/autoninja -C ~/chromium/src/out/thorium chrome_public_apk content_shell_apk system_webview_apk -j$@ &&
# ADD # thorium_shell_apk
mv ~/chromium/src/out/thorium/apks/ChromePublic.apk ~/chromium/src/out/thorium/apks/Thorium_Public_arm32.apk &&
@ -71,7 +71,7 @@ esac
# Build Thorium for ARM64
export NINJA_SUMMARIZE_BUILD=1 &&
# ADD # thorium_shell_apk
./depot_tools/autoninja -C ~/chromium/src/out/thorium chrome_public_apk -j$@ &&
./depot_tools/autoninja -C ~/chromium/src/out/thorium content_shell_apk chrome_public_apk -j$@ &&
mv ~/chromium/src/out/thorium/apks/ChromePublic.apk ~/chromium/src/out/thorium/apks/Thorium_Public_arm64.apk &&

View file

@ -362,7 +362,7 @@ group("gn_all") {
"//chrome/test:android_browsertests",
"//components:components_junit_tests",
"//content/public/android:content_junit_tests",
"//content/shell/android:thorium_shell_apk",
"//content/shell/android:content_shell_apk",
"//device:device_junit_tests",
"//media/gpu:video_decode_accelerator_unittest",
"//net/android:net_junit_tests",
@ -408,7 +408,7 @@ group("gn_all") {
"//chrome/android:chrome_public_unit_test_apk",
"//chrome/browser/android/examples/custom_tabs_client:custom_tabs_client_example_apk",
"//chrome/browser/android/examples/partner_browser_customizations_provider:partner_browser_customizations_example_apk",
"//content/shell/android:thorium_shell_test_apk",
"//content/shell/android:content_shell_test_apk",
]
}

View file

@ -494,7 +494,7 @@ repack("pak") {
if (is_android) {
group("thorium_shell") {
testonly = true
deps = [ "//content/shell/android:thorium_shell_apk" ]
deps = [ "//content/shell/android:content_shell_apk" ]
}
} else if (is_mac) {
tweak_info_plist("content_shell_plist") {

View file

@ -75,7 +75,7 @@ android_resources("content_shell_java_resources") {
android_library("content_shell_java") {
testonly = true
resources_package = "org.chromium.thorium_shell"
resources_package = "org.chromium.content_shell"
deps = [
":content_shell_java_resources",
":content_shell_manifest",
@ -108,7 +108,7 @@ jinja_template("content_shell_manifest") {
testonly = true
input = "shell_apk/AndroidManifest.xml.jinja2"
output = content_shell_manifest
variables = [ "manifest_package=org.chromium.thorium_shell_apk" ]
variables = [ "manifest_package=org.chromium.content_shell_apk" ]
}
jinja_template("content_shell_test_manifest") {
@ -133,7 +133,7 @@ android_library("content_shell_apk_java") {
srcjar_deps = [ ":content_javatests_aidl" ]
resources_package = "org.chromium.thorium_shell_apk"
resources_package = "org.chromium.content_shell_apk"
deps = [
":content_shell_apk_resources",
":content_shell_java",
@ -206,7 +206,7 @@ template("content_shell_apk_tmpl") {
}
}
content_shell_apk_tmpl("thorium_shell_apk") {
content_shell_apk_tmpl("content_shell_apk") {
target_type = "android_apk"
apk_name = "ThoriumShell"
android_manifest = content_shell_manifest
@ -215,7 +215,7 @@ content_shell_apk_tmpl("thorium_shell_apk") {
command_line_flags_file = "content-shell-command-line"
}
content_shell_apk_tmpl("thorium_shell_test_apk") {
content_shell_apk_tmpl("content_shell_test_apk") {
target_type = "instrumentation_test_apk"
apk_name = "ThoriumShellTest"
android_manifest = content_shell_test_manifest

View file

@ -1,421 +0,0 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.thorium_shell;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.ClipDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.components.embedder_support.view.ContentViewRenderView;
import org.chromium.content_public.browser.ActionModeCallbackHelper;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid;
/**
* Container for the various UI components that make up a shell window.
*/
@JNINamespace("content")
public class Shell extends LinearLayout {
private static final long COMPLETED_PROGRESS_TIMEOUT_MS = 200;
// Stylus handwriting: Setting this ime option instructs stylus writing service to restrict
// capturing writing events slightly outside the Url bar area. This is needed to prevent stylus
// handwriting in inputs in web content area that are very close to url bar area, from being
// committed to Url bar's Edit text. Ex: google.com search field.
private static final String IME_OPTION_RESTRICT_STYLUS_WRITING_AREA =
"restrictDirectWritingArea=true";
private final Runnable mClearProgressRunnable = new Runnable() {
@Override
public void run() {
mProgressDrawable.setLevel(0);
}
};
private WebContents mWebContents;
private NavigationController mNavigationController;
private EditText mUrlTextView;
private ImageButton mPrevButton;
private ImageButton mNextButton;
private ImageButton mStopReloadButton;
private ClipDrawable mProgressDrawable;
private long mNativeShell;
private ContentViewRenderView mContentViewRenderView;
private WindowAndroid mWindow;
private ShellViewAndroidDelegate mViewAndroidDelegate;
private boolean mLoading;
private boolean mIsFullscreen;
private Callback<Boolean> mOverlayModeChangedCallbackForTesting;
/**
* Constructor for inflating via XML.
*/
public Shell(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Set the SurfaceView being renderered to as soon as it is available.
*/
public void setContentViewRenderView(ContentViewRenderView contentViewRenderView) {
FrameLayout contentViewHolder = (FrameLayout) findViewById(R.id.contentview_holder);
if (contentViewRenderView == null) {
if (mContentViewRenderView != null) {
contentViewHolder.removeView(mContentViewRenderView);
}
} else {
contentViewHolder.addView(contentViewRenderView,
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
}
mContentViewRenderView = contentViewRenderView;
}
/**
* Initializes the Shell for use.
*
* @param nativeShell The pointer to the native Shell object.
* @param window The owning window for this shell.
*/
public void initialize(long nativeShell, WindowAndroid window) {
mNativeShell = nativeShell;
mWindow = window;
}
/**
* Closes the shell and cleans up the native instance, which will handle destroying all
* dependencies.
*/
public void close() {
if (mNativeShell == 0) return;
ShellJni.get().closeShell(mNativeShell);
}
@CalledByNative
private void onNativeDestroyed() {
mWindow = null;
mNativeShell = 0;
mWebContents = null;
}
/**
* @return Whether the Shell has been destroyed.
* @see #onNativeDestroyed()
*/
public boolean isDestroyed() {
return mNativeShell == 0;
}
/**
* @return Whether or not the Shell is loading content.
*/
public boolean isLoading() {
return mLoading;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
View toolbar = findViewById(R.id.toolbar);
mProgressDrawable = (ClipDrawable) toolbar.getBackground();
initializeUrlField();
initializeNavigationButtons();
}
private void initializeUrlField() {
mUrlTextView = (EditText) findViewById(R.id.url);
mUrlTextView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((actionId != EditorInfo.IME_ACTION_GO) && (event == null
|| event.getKeyCode() != KeyEvent.KEYCODE_ENTER
|| event.getAction() != KeyEvent.ACTION_DOWN)) {
return false;
}
loadUrl(mUrlTextView.getText().toString());
setKeyboardVisibilityForUrl(false);
getContentView().requestFocus();
return true;
}
});
mUrlTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
setKeyboardVisibilityForUrl(hasFocus);
mNextButton.setVisibility(hasFocus ? GONE : VISIBLE);
mPrevButton.setVisibility(hasFocus ? GONE : VISIBLE);
mStopReloadButton.setVisibility(hasFocus ? GONE : VISIBLE);
if (!hasFocus) {
mUrlTextView.setText(mWebContents.getVisibleUrl().getSpec());
}
}
});
mUrlTextView.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
getContentView().requestFocus();
return true;
}
return false;
}
});
mUrlTextView.setPrivateImeOptions(IME_OPTION_RESTRICT_STYLUS_WRITING_AREA);
}
/**
* Loads an URL. This will perform minimal amounts of sanitizing of the URL to attempt to
* make it valid.
*
* @param url The URL to be loaded by the shell.
*/
public void loadUrl(String url) {
if (url == null) return;
if (TextUtils.equals(url, mWebContents.getLastCommittedUrl().getSpec())) {
mNavigationController.reload(true);
} else {
mNavigationController.loadUrl(new LoadUrlParams(sanitizeUrl(url)));
}
mUrlTextView.clearFocus();
// TODO(aurimas): Remove this when crbug.com/174541 is fixed.
getContentView().clearFocus();
getContentView().requestFocus();
}
/**
* Given an URL, this performs minimal sanitizing to ensure it will be valid.
* @param url The url to be sanitized.
* @return The sanitized URL.
*/
public static String sanitizeUrl(String url) {
if (url == null) return null;
if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url;
return url;
}
private void initializeNavigationButtons() {
mPrevButton = (ImageButton) findViewById(R.id.prev);
mPrevButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mNavigationController.canGoBack()) mNavigationController.goBack();
}
});
mNextButton = (ImageButton) findViewById(R.id.next);
mNextButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mNavigationController.canGoForward()) mNavigationController.goForward();
}
});
mStopReloadButton = (ImageButton) findViewById(R.id.stop_reload_button);
mStopReloadButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mLoading) mWebContents.stop();
else mNavigationController.reload(true);
}
});
}
@SuppressWarnings("unused")
@CalledByNative
private void onUpdateUrl(String url) {
mUrlTextView.setText(url);
}
@SuppressWarnings("unused")
@CalledByNative
private void onLoadProgressChanged(double progress) {
removeCallbacks(mClearProgressRunnable);
mProgressDrawable.setLevel((int) (10000.0 * progress));
if (progress == 1.0) postDelayed(mClearProgressRunnable, COMPLETED_PROGRESS_TIMEOUT_MS);
}
@CalledByNative
private void toggleFullscreenModeForTab(boolean enterFullscreen) {
mIsFullscreen = enterFullscreen;
LinearLayout toolBar = (LinearLayout) findViewById(R.id.toolbar);
toolBar.setVisibility(enterFullscreen ? GONE : VISIBLE);
}
@CalledByNative
private boolean isFullscreenForTabOrPending() {
return mIsFullscreen;
}
@SuppressWarnings("unused")
@CalledByNative
private void setIsLoading(boolean loading) {
mLoading = loading;
if (mLoading) {
mStopReloadButton
.setImageResource(android.R.drawable.ic_menu_close_clear_cancel);
} else {
mStopReloadButton.setImageResource(R.drawable.ic_refresh);
}
}
public ShellViewAndroidDelegate getViewAndroidDelegate() {
return mViewAndroidDelegate;
}
/**
* Initializes the ContentView based on the native tab contents pointer passed in.
* @param webContents A {@link WebContents} object.
*/
@SuppressWarnings("unused")
@CalledByNative
private void initFromNativeTabContents(WebContents webContents) {
Context context = getContext();
ContentView cv =
ContentView.createContentView(context, null /* eventOffsetHandler */, webContents);
mViewAndroidDelegate = new ShellViewAndroidDelegate(cv);
assert (mWebContents != webContents);
if (mWebContents != null) mWebContents.clearNativeReference();
webContents.initialize(
"", mViewAndroidDelegate, cv, mWindow, WebContents.createDefaultInternalsHolder());
mWebContents = webContents;
SelectionPopupController.fromWebContents(webContents)
.setActionModeCallback(defaultActionCallback());
mNavigationController = mWebContents.getNavigationController();
if (getParent() != null) mWebContents.onShow();
mUrlTextView.setText(mWebContents.getVisibleUrl().getSpec());
((FrameLayout) findViewById(R.id.contentview_holder)).addView(cv,
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
cv.requestFocus();
mContentViewRenderView.setCurrentWebContents(mWebContents);
}
/**
* {link @ActionMode.Callback} that uses the default implementation in
* {@link SelectionPopupController}.
*/
private ActionMode.Callback2 defaultActionCallback() {
final ActionModeCallbackHelper helper =
SelectionPopupController.fromWebContents(mWebContents)
.getActionModeCallbackHelper();
return new ActionMode.Callback2() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
helper.onCreateActionMode(mode, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return helper.onPrepareActionMode(mode, menu);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return helper.onActionItemClicked(mode, item);
}
@Override
public void onDestroyActionMode(ActionMode mode) {
helper.onDestroyActionMode();
}
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
helper.onGetContentRect(mode, view, outRect);
}
};
}
@CalledByNative
public void setOverlayMode(boolean useOverlayMode) {
mContentViewRenderView.setOverlayVideoMode(useOverlayMode);
if (mOverlayModeChangedCallbackForTesting != null) {
mOverlayModeChangedCallbackForTesting.onResult(useOverlayMode);
}
}
public void setOverayModeChangedCallbackForTesting(Callback<Boolean> callback) {
mOverlayModeChangedCallbackForTesting = callback;
}
/**
* Enable/Disable navigation(Prev/Next) button if navigation is allowed/disallowed
* in respective direction.
* @param controlId Id of button to update
* @param enabled enable/disable value
*/
@CalledByNative
private void enableUiControl(int controlId, boolean enabled) {
if (controlId == 0) {
mPrevButton.setEnabled(enabled);
} else if (controlId == 1) {
mNextButton.setEnabled(enabled);
}
}
/**
* @return The {@link View} currently shown by this Shell.
*/
public View getContentView() {
ViewAndroidDelegate viewDelegate = mWebContents.getViewAndroidDelegate();
return viewDelegate != null ? viewDelegate.getContainerView() : null;
}
/**
* @return The {@link WebContents} currently managing the content shown by this Shell.
*/
public WebContents getWebContents() {
return mWebContents;
}
private void setKeyboardVisibilityForUrl(boolean visible) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
if (visible) {
imm.showSoftInput(mUrlTextView, InputMethodManager.SHOW_IMPLICIT);
} else {
imm.hideSoftInputFromWindow(mUrlTextView.getWindowToken(), 0);
}
}
@NativeMethods
interface Natives {
void closeShell(long shellPtr);
}
}

View file

@ -1,153 +0,0 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.thorium_shell;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.embedder_support.view.ContentViewRenderView;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
/**
* Container and generator of ShellViews.
*/
@JNINamespace("content")
public class ShellManager extends FrameLayout {
public static final String DEFAULT_SHELL_URL = "http://www.google.com";
private WindowAndroid mWindow;
private Shell mActiveShell;
private String mStartupUrl = DEFAULT_SHELL_URL;
// The target for all content rendering.
private ContentViewRenderView mContentViewRenderView;
/**
* Constructor for inflating via XML.
*/
public ShellManager(final Context context, AttributeSet attrs) {
super(context, attrs);
ShellManagerJni.get().init(this);
}
/**
* @param window The window used to generate all shells.
*/
public void setWindow(WindowAndroid window) {
assert window != null;
mWindow = window;
mContentViewRenderView = new ContentViewRenderView(getContext());
mContentViewRenderView.onNativeLibraryLoaded(window);
}
/**
* @return The window used to generate all shells.
*/
public WindowAndroid getWindow() {
return mWindow;
}
/**
* Get the ContentViewRenderView.
*/
public ContentViewRenderView getContentViewRenderView() {
return mContentViewRenderView;
}
/**
* Sets the startup URL for new shell windows.
*/
public void setStartupUrl(String url) {
mStartupUrl = url;
}
/**
* @return The currently visible shell view or null if one is not showing.
*/
public Shell getActiveShell() {
return mActiveShell;
}
/**
* Creates a new shell pointing to the specified URL.
* @param url The URL the shell should load upon creation.
*/
public void launchShell(String url) {
ThreadUtils.assertOnUiThread();
Shell previousShell = mActiveShell;
ShellManagerJni.get().launchShell(url);
if (previousShell != null) previousShell.close();
}
@SuppressWarnings("unused")
@CalledByNative
private Object createShell(long nativeShellPtr) {
if (mContentViewRenderView == null) {
mContentViewRenderView = new ContentViewRenderView(getContext());
mContentViewRenderView.onNativeLibraryLoaded(mWindow);
}
LayoutInflater inflater =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Shell shellView = (Shell) inflater.inflate(R.layout.shell_view, null);
shellView.initialize(nativeShellPtr, mWindow);
// TODO(tedchoc): Allow switching back to these inactive shells.
if (mActiveShell != null) removeShell(mActiveShell);
showShell(shellView);
return shellView;
}
private void showShell(Shell shellView) {
shellView.setContentViewRenderView(mContentViewRenderView);
addView(shellView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
mActiveShell = shellView;
WebContents webContents = mActiveShell.getWebContents();
if (webContents != null) {
mContentViewRenderView.setCurrentWebContents(webContents);
webContents.onShow();
}
}
@CalledByNative
private void removeShell(Shell shellView) {
if (shellView == mActiveShell) mActiveShell = null;
if (shellView.getParent() == null) return;
shellView.setContentViewRenderView(null);
removeView(shellView);
}
/**
* Destroys the Shell manager and associated components.
* Always called at activity exit, and potentially called by native in cases where we need to
* control the timing of mContentViewRenderView destruction. Must handle being called twice.
*/
@CalledByNative
public void destroy() {
// Remove active shell (Currently single shell support only available).
if (mActiveShell != null) {
removeShell(mActiveShell);
}
if (mContentViewRenderView != null) {
mContentViewRenderView.destroy();
mContentViewRenderView = null;
}
}
@NativeMethods
interface Natives {
void init(Object shellManagerInstance);
void launchShell(String url);
}
}

View file

@ -1,59 +0,0 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.thorium_shell;
import android.graphics.Bitmap;
import android.view.ViewGroup;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.mojom.CursorType;
/**
* Implementation of the abstract class {@link ViewAndroidDelegate} for content shell.
* Extended for testing.
*/
public class ShellViewAndroidDelegate extends ViewAndroidDelegate {
/**
* An interface delegates a {@link CallbackHelper} for cursor update. see more in {@link
* ContentViewPointerTypeTest.OnCursorUpdateHelperImpl}.
*/
public interface OnCursorUpdateHelper {
/**
* Record the last notifyCalled pointer type, see more {@link CallbackHelper#notifyCalled}.
* @param type The pointer type of the notifyCalled.
*/
void notifyCalled(int type);
}
private OnCursorUpdateHelper mOnCursorUpdateHelper;
public ShellViewAndroidDelegate(ViewGroup containerView) {
super(containerView);
}
public void setOnCursorUpdateHelper(OnCursorUpdateHelper helper) {
mOnCursorUpdateHelper = helper;
}
public OnCursorUpdateHelper getOnCursorUpdateHelper() {
return mOnCursorUpdateHelper;
}
@Override
public void onCursorChangedToCustom(Bitmap customCursorBitmap, int hotspotX, int hotspotY) {
super.onCursorChangedToCustom(customCursorBitmap, hotspotX, hotspotY);
if (mOnCursorUpdateHelper != null) {
mOnCursorUpdateHelper.notifyCalled(CursorType.CUSTOM);
}
}
@Override
public void onCursorChanged(int cursorType) {
super.onCursorChanged(cursorType);
if (mOnCursorUpdateHelper != null) {
mOnCursorUpdateHelper.notifyCalled(cursorType);
}
}
}