Commit 019a8580 authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer: add FindInPage API

New methods are added to Tab:
- beginFindInPage
- activateNextFindInPageMatch
- endFindInPage

And a new FindInPageCallback interface is introduced with one method:
- onFindResult

Also add very basic FIP controls to shell to partially exercise the
new API.

Notes:
1. The embedder provides the main find in page UI (the text input).
2. The embedder is responsible for ending the find session as
   appropriate (i.e. when the main find in page UI is dismissed),
   which cleans up the in-page UI, consisting of a results sidebar and
   text highlighting. Commonly this would happen if the user cancels the
   find session or if a navigation occurs.
3. When the active tab loses focus, the find session is ended and the
   result bar is removed.
4. It is generally expected that the embedder-provided find in page UI
   will be hosted in the top view.

TODO:
1. The top controls should be forced visible when the new web contents
   overlay view holds children. That will definitely be desired for
   tab modal dialogs, and is probably desired for find in page as well
   (at least judging by what Clank does).
2. Write tests.

Bug: 1038415
Change-Id: I6183172dc3944157aae2db5ebb0aed983a34bffc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2004072Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734691}
parent 287f1862
...@@ -749,7 +749,7 @@ public class FindToolbar extends LinearLayout { ...@@ -749,7 +749,7 @@ public class FindToolbar extends LinearLayout {
assert mFindInPageBridge != null; assert mFindInPageBridge != null;
mResultBar = new FindResultBar( mResultBar = new FindResultBar(
getContext(), mCurrentTab.getContentView(), mFindInPageBridge); getContext(), mCurrentTab.getContentView(), mWindowAndroid, mFindInPageBridge);
} else if (!visibility) { } else if (!visibility) {
if (mResultBar != null) { if (mResultBar != null) {
mResultBar.dismiss(); mResultBar.dismiss();
......
...@@ -21,7 +21,6 @@ import android.widget.FrameLayout; ...@@ -21,7 +21,6 @@ import android.widget.FrameLayout;
import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.MathUtils; import org.chromium.base.MathUtils;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.ui.KeyboardVisibilityDelegate; import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
...@@ -56,7 +55,7 @@ public class FindResultBar extends View { ...@@ -56,7 +55,7 @@ public class FindResultBar extends View {
private final int mStackedResultHeight; private final int mStackedResultHeight;
private FindInPageBridge mFindInPageBridge; private FindInPageBridge mFindInPageBridge;
private final ContentView mContentView; private final WindowAndroid mWindowAndroid;
private int mRectsVersion = -1; private int mRectsVersion = -1;
private RectF[] mMatches = new RectF[0]; private RectF[] mMatches = new RectF[0];
...@@ -83,14 +82,15 @@ public class FindResultBar extends View { ...@@ -83,14 +82,15 @@ public class FindResultBar extends View {
}; };
/** /**
* Creates an instance of a {@link FindResultBar}. * Creates an instance of a {@link FindResultBar}. Also adds it to a parent {@link FrameLayout}
* and animates itself into view.
* @param context The Context to create this {@link FindResultBar} under. * @param context The Context to create this {@link FindResultBar} under.
* @param contentView The ContentView that holds the WebContents that is being searched. The * @param contentView The FrameLayout that will hold this FindResultBar.
* WebContents must have been attached to a view hierarchy and have a WindowAndroid. * @param windowAndroid The WindowAndroid hosting the WebContents under search.
* @param findInPageBridge Facilitator for user interactions. * @param findInPageBridge Facilitator for user interactions.
*/ */
public FindResultBar( public FindResultBar(Context context, FrameLayout parent, WindowAndroid windowAndroid,
Context context, ContentView contentView, FindInPageBridge findInPageBridge) { FindInPageBridge findInPageBridge) {
super(context); super(context);
Resources res = context.getResources(); Resources res = context.getResources();
...@@ -125,8 +125,7 @@ public class FindResultBar extends View { ...@@ -125,8 +125,7 @@ public class FindResultBar extends View {
mFindInPageBridge = findInPageBridge; mFindInPageBridge = findInPageBridge;
mContentView = contentView; parent.addView(this,
mContentView.addView(this,
new FrameLayout.LayoutParams( new FrameLayout.LayoutParams(
mBarTouchWidth, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.END)); mBarTouchWidth, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.END));
setTranslationX(MathUtils.flipSignIf(mBarTouchWidth, LocalizationUtils.isLayoutRtl())); setTranslationX(MathUtils.flipSignIf(mBarTouchWidth, LocalizationUtils.isLayoutRtl()));
...@@ -134,11 +133,12 @@ public class FindResultBar extends View { ...@@ -134,11 +133,12 @@ public class FindResultBar extends View {
mVisibilityAnimation = ObjectAnimator.ofFloat(this, TRANSLATION_X, 0); mVisibilityAnimation = ObjectAnimator.ofFloat(this, TRANSLATION_X, 0);
mVisibilityAnimation.setDuration(VISIBILITY_ANIMATION_DURATION_MS); mVisibilityAnimation.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
mVisibilityAnimation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE); mVisibilityAnimation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
WindowAndroid window = mContentView.getWebContents().getTopLevelNativeWindow();
if (window == null) { mWindowAndroid = windowAndroid;
throw new IllegalArgumentException("ContentView must be attached to a window."); if (windowAndroid == null) {
throw new IllegalArgumentException("WindowAndroid must be non null.");
} }
window.startAnimationOverContent(mVisibilityAnimation); windowAndroid.startAnimationOverContent(mVisibilityAnimation);
} }
/** Dismisses this results bar by removing it from the view hierarchy. */ /** Dismisses this results bar by removing it from the view hierarchy. */
...@@ -153,8 +153,7 @@ public class FindResultBar extends View { ...@@ -153,8 +153,7 @@ public class FindResultBar extends View {
MathUtils.flipSignIf(mBarTouchWidth, LocalizationUtils.isLayoutRtl())); MathUtils.flipSignIf(mBarTouchWidth, LocalizationUtils.isLayoutRtl()));
mVisibilityAnimation.setDuration(VISIBILITY_ANIMATION_DURATION_MS); mVisibilityAnimation.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
mVisibilityAnimation.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); mVisibilityAnimation.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
mContentView.getWebContents().getTopLevelNativeWindow().startAnimationOverContent( mWindowAndroid.startAnimationOverContent(mVisibilityAnimation);
mVisibilityAnimation);
mVisibilityAnimation.addListener(new AnimatorListenerAdapter() { mVisibilityAnimation.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
......
...@@ -69,16 +69,8 @@ class FindTabHelper : public content::WebContentsObserver, ...@@ -69,16 +69,8 @@ class FindTabHelper : public content::WebContentsObserver,
find_ui_active_ = find_ui_active; find_ui_active_ = find_ui_active;
} }
// Setter for find_op_aborted_. // Used _only_ by testing to get the current request ID.
void set_find_op_aborted(bool find_op_aborted) {
find_op_aborted_ = find_op_aborted;
}
// Used _only_ by testing to get or set the current request ID.
int current_find_request_id() { return current_find_request_id_; } int current_find_request_id() { return current_find_request_id_; }
void set_current_find_request_id(int current_find_request_id) {
current_find_request_id_ = current_find_request_id;
}
// Accessor for find_text_. Used to determine if this WebContents has any // Accessor for find_text_. Used to determine if this WebContents has any
// active searches. // active searches.
......
...@@ -205,6 +205,7 @@ jumbo_static_library("weblayer_lib") { ...@@ -205,6 +205,7 @@ jumbo_static_library("weblayer_lib") {
"//components/crash/content/app", "//components/crash/content/app",
"//components/crash/content/browser", "//components/crash/content/browser",
"//components/embedder_support", "//components/embedder_support",
"//components/find_in_page",
"//components/network_time", "//components/network_time",
"//components/prefs", "//components/prefs",
"//components/safe_browsing/core:features", "//components/safe_browsing/core:features",
......
...@@ -8,6 +8,7 @@ include_rules = [ ...@@ -8,6 +8,7 @@ include_rules = [
"+components/crash/content/browser", "+components/crash/content/browser",
"+components/download/public/common", "+components/download/public/common",
"+components/embedder_support", "+components/embedder_support",
"+components/find_in_page",
"+components/network_time", "+components/network_time",
"+components/prefs", "+components/prefs",
"+components/user_prefs", "+components/user_prefs",
......
...@@ -70,6 +70,7 @@ android_library("java") { ...@@ -70,6 +70,7 @@ android_library("java") {
"//components/download/internal/common:internal_java", "//components/download/internal/common:internal_java",
"//components/embedder_support/android:application_java", "//components/embedder_support/android:application_java",
"//components/embedder_support/android:web_contents_delegate_java", "//components/embedder_support/android:web_contents_delegate_java",
"//components/find_in_page/android:java",
"//components/minidump_uploader:minidump_uploader_java", "//components/minidump_uploader:minidump_uploader_java",
"//components/spellcheck/browser/android:java", "//components/spellcheck/browser/android:java",
"//components/version_info/android:version_constants_java", "//components/version_info/android:version_constants_java",
...@@ -174,6 +175,7 @@ android_aidl("aidl") { ...@@ -174,6 +175,7 @@ android_aidl("aidl") {
"org/chromium/weblayer_private/interfaces/IDownload.aidl", "org/chromium/weblayer_private/interfaces/IDownload.aidl",
"org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl",
"org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl",
"org/chromium/weblayer_private/interfaces/IFindInPageCallbackClient.aidl",
"org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl",
"org/chromium/weblayer_private/interfaces/INavigation.aidl", "org/chromium/weblayer_private/interfaces/INavigation.aidl",
"org/chromium/weblayer_private/interfaces/INavigationController.aidl", "org/chromium/weblayer_private/interfaces/INavigationController.aidl",
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
package org.chromium.weblayer_private; package org.chromium.weblayer_private;
import android.content.Context; import android.content.Context;
import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.webkit.ValueCallback; import android.webkit.ValueCallback;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -26,8 +26,11 @@ public final class BrowserViewController ...@@ -26,8 +26,11 @@ public final class BrowserViewController
WebContentsGestureStateTracker.OnGestureStateChangedListener { WebContentsGestureStateTracker.OnGestureStateChangedListener {
private final ContentViewRenderView mContentViewRenderView; private final ContentViewRenderView mContentViewRenderView;
private final ContentView mContentView; private final ContentView mContentView;
// Child of mContentViewRenderView, holds top-view from client. // Child of mContentView, holds top-view from client.
private final TopControlsContainerView mTopControlsContainerView; private final TopControlsContainerView mTopControlsContainerView;
// Other child of mContentView, which holds views that sit on top of the web contents, such as
// tab modal dialogs.
private final FrameLayout mWebContentsOverlayView;
private TabImpl mTab; private TabImpl mTab;
...@@ -47,6 +50,7 @@ public final class BrowserViewController ...@@ -47,6 +50,7 @@ public final class BrowserViewController
windowAndroid, ContentViewRenderView.MODE_SURFACE_VIEW); windowAndroid, ContentViewRenderView.MODE_SURFACE_VIEW);
mTopControlsContainerView = mTopControlsContainerView =
new TopControlsContainerView(context, mContentViewRenderView, this); new TopControlsContainerView(context, mContentViewRenderView, this);
mTopControlsContainerView.setId(View.generateViewId());
mContentView = ContentView.createContentView( mContentView = ContentView.createContentView(
context, mTopControlsContainerView.getEventOffsetHandler()); context, mTopControlsContainerView.getEventOffsetHandler());
ViewAndroidDelegate viewAndroidDelegate = new ViewAndroidDelegate(mContentView) { ViewAndroidDelegate viewAndroidDelegate = new ViewAndroidDelegate(mContentView) {
...@@ -60,8 +64,16 @@ public final class BrowserViewController ...@@ -60,8 +64,16 @@ public final class BrowserViewController
new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY)); FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY));
mContentView.addView(mTopControlsContainerView, mContentView.addView(mTopControlsContainerView,
new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, new RelativeLayout.LayoutParams(
Gravity.FILL_HORIZONTAL | Gravity.TOP)); LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mWebContentsOverlayView = new FrameLayout(context);
RelativeLayout.LayoutParams overlayParams =
new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0);
overlayParams.addRule(RelativeLayout.BELOW, mTopControlsContainerView.getId());
overlayParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mContentView.addView(mWebContentsOverlayView, overlayParams);
windowAndroid.setAnimationPlaceholderView(mWebContentsOverlayView);
} }
public void destroy() { public void destroy() {
...@@ -79,6 +91,10 @@ public final class BrowserViewController ...@@ -79,6 +91,10 @@ public final class BrowserViewController
return mContentView; return mContentView;
} }
public FrameLayout getWebContentsOverlayView() {
return mWebContentsOverlayView;
}
public void setActiveTab(TabImpl tab) { public void setActiveTab(TabImpl tab) {
if (tab == mTab) return; if (tab == mTab) return;
......
...@@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeProvider; ...@@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeProvider;
import android.view.autofill.AutofillValue; import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout; import android.widget.RelativeLayout;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.base.TraceEvent; import org.chromium.base.TraceEvent;
...@@ -41,7 +41,7 @@ import org.chromium.ui.base.EventOffsetHandler; ...@@ -41,7 +41,7 @@ import org.chromium.ui.base.EventOffsetHandler;
* The containing view for {@link WebContents} that exists in the Android UI hierarchy and exposes * The containing view for {@link WebContents} that exists in the Android UI hierarchy and exposes
* the various {@link View} functionality to it. * the various {@link View} functionality to it.
*/ */
public class ContentView extends FrameLayout public class ContentView extends RelativeLayout
implements ViewEventSink.InternalAccessDelegate, SmartClipProvider, implements ViewEventSink.InternalAccessDelegate, SmartClipProvider,
OnHierarchyChangeListener, OnSystemUiVisibilityChangeListener { OnHierarchyChangeListener, OnSystemUiVisibilityChangeListener {
private static final String TAG = "ContentView"; private static final String TAG = "ContentView";
...@@ -114,7 +114,7 @@ public class ContentView extends FrameLayout ...@@ -114,7 +114,7 @@ public class ContentView extends FrameLayout
// The Autofill system-level infrastructure has heuristics for which Views it considers // The Autofill system-level infrastructure has heuristics for which Views it considers
// important for autofill; only these Views will be queried for their autofill // important for autofill; only these Views will be queried for their autofill
// structure on notifications that a new (virtual) View was entered. By default, // structure on notifications that a new (virtual) View was entered. By default,
// FrameLayout is not considered important for autofill. Thus, for ContentView to be // RelativeLayout is not considered important for autofill. Thus, for ContentView to be
// queried for its autofill structure, we must explicitly inform the autofill system // queried for its autofill structure, we must explicitly inform the autofill system
// that this View is important for autofill. // that this View is important for autofill.
setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES); setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES);
...@@ -435,7 +435,7 @@ public class ContentView extends FrameLayout ...@@ -435,7 +435,7 @@ public class ContentView extends FrameLayout
return mWebContents != null ? RenderCoordinates.fromWebContents(mWebContents) : null; return mWebContents != null ? RenderCoordinates.fromWebContents(mWebContents) : null;
} }
// End FrameLayout overrides. // End RelativeLayout overrides.
@Override @Override
public boolean awakenScrollBars(int startDelay, boolean invalidate) { public boolean awakenScrollBars(int startDelay, boolean invalidate) {
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
package org.chromium.weblayer_private; package org.chromium.weblayer_private;
import android.graphics.RectF;
import android.os.Build; import android.os.Build;
import android.os.RemoteException;
import android.util.AndroidRuntimeException;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.webkit.ValueCallback; import android.webkit.ValueCallback;
...@@ -15,6 +18,9 @@ import org.chromium.base.annotations.JNINamespace; ...@@ -15,6 +18,9 @@ import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.autofill.AutofillProvider; import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.autofill.AutofillProviderImpl; import org.chromium.components.autofill.AutofillProviderImpl;
import org.chromium.components.find_in_page.FindInPageBridge;
import org.chromium.components.find_in_page.FindMatchRectsDetails;
import org.chromium.components.find_in_page.FindResultBar;
import org.chromium.content_public.browser.SelectionPopupController; import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.ViewEventSink; import org.chromium.content_public.browser.ViewEventSink;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -22,6 +28,7 @@ import org.chromium.ui.base.ViewAndroidDelegate; ...@@ -22,6 +28,7 @@ import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient;
import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient; import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
import org.chromium.weblayer_private.interfaces.INavigationControllerClient; import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
import org.chromium.weblayer_private.interfaces.IObjectWrapper; import org.chromium.weblayer_private.interfaces.IObjectWrapper;
...@@ -57,6 +64,12 @@ public final class TabImpl extends ITab.Stub { ...@@ -57,6 +64,12 @@ public final class TabImpl extends ITab.Stub {
private ITabClient mClient; private ITabClient mClient;
private final int mId; private final int mId;
private IFindInPageCallbackClient mFindInPageCallbackClient;
private FindInPageBridge mFindInPageBridge;
private FindResultBar mFindResultBar;
// See usage note in {@link #onFindResultAvailable}.
boolean mWaitingForMatchRects;
private static class InternalAccessDelegateImpl private static class InternalAccessDelegateImpl
implements ViewEventSink.InternalAccessDelegate { implements ViewEventSink.InternalAccessDelegate {
@Override @Override
...@@ -183,6 +196,7 @@ public final class TabImpl extends ITab.Stub { ...@@ -183,6 +196,7 @@ public final class TabImpl extends ITab.Stub {
* Called when this TabImpl is no longer the active TabImpl. * Called when this TabImpl is no longer the active TabImpl.
*/ */
public void onDidLoseActive() { public void onDidLoseActive() {
hideFindInPageUiAndNotifyClient();
mWebContents.onHide(); mWebContents.onHide();
TabImplJni.get().setTopControlsContainerView(mNativeTab, TabImpl.this, 0); TabImplJni.get().setTopControlsContainerView(mNativeTab, TabImpl.this, 0);
} }
...@@ -276,6 +290,114 @@ public final class TabImpl extends ITab.Stub { ...@@ -276,6 +290,114 @@ public final class TabImpl extends ITab.Stub {
TabImplJni.get().executeScript(mNativeTab, script, useSeparateIsolate, nativeCallback); TabImplJni.get().executeScript(mNativeTab, script, useSeparateIsolate, nativeCallback);
} }
@Override
public boolean setFindInPageCallbackClient(IFindInPageCallbackClient client) {
StrictModeWorkaround.apply();
if (client == null) {
// Null now to avoid calling onFindEnded.
mFindInPageCallbackClient = null;
hideFindInPageUiAndNotifyClient();
return true;
}
if (mFindInPageCallbackClient != null) return false;
BrowserViewController controller = getViewController();
if (controller == null) return false;
mFindInPageCallbackClient = client;
assert mFindInPageBridge == null;
mFindInPageBridge = new FindInPageBridge(mWebContents);
assert mFindResultBar == null;
mFindResultBar =
new FindResultBar(mBrowser.getContext(), controller.getWebContentsOverlayView(),
mBrowser.getWindowAndroid(), mFindInPageBridge);
return true;
}
@Override
public void findInPage(String searchText, boolean forward) {
StrictModeWorkaround.apply();
if (mFindInPageBridge == null) return;
if (searchText.length() > 0) {
mFindInPageBridge.startFinding(searchText, forward, false);
} else {
mFindInPageBridge.stopFinding(true);
}
}
private void hideFindInPageUiAndNotifyClient() {
if (mFindInPageBridge == null) return;
mFindInPageBridge.stopFinding(true);
mFindResultBar.dismiss();
mFindResultBar = null;
mFindInPageBridge.destroy();
mFindInPageBridge = null;
try {
if (mFindInPageCallbackClient != null) mFindInPageCallbackClient.onFindEnded();
} catch (RemoteException e) {
throw new AndroidRuntimeException(e);
}
}
@CalledByNative
private static RectF createRectF(float x, float y, float right, float bottom) {
return new RectF(x, y, right, bottom);
}
@CalledByNative
private static FindMatchRectsDetails createFindMatchRectsDetails(
int version, int numRects, RectF activeRect) {
return new FindMatchRectsDetails(version, numRects, activeRect);
}
@CalledByNative
private static void setMatchRectByIndex(
FindMatchRectsDetails findMatchRectsDetails, int index, RectF rect) {
findMatchRectsDetails.rects[index] = rect;
}
@CalledByNative
private void onFindResultAvailable(
int numberOfMatches, int activeMatchOrdinal, boolean finalUpdate) {
try {
if (mFindInPageCallbackClient != null) {
// The WebLayer API deals in indices instead of ordinals.
mFindInPageCallbackClient.onFindResult(
numberOfMatches, activeMatchOrdinal - 1, finalUpdate);
}
} catch (RemoteException e) {
throw new AndroidRuntimeException(e);
}
if (mFindResultBar != null) {
mFindResultBar.onFindResult();
if (finalUpdate) {
if (numberOfMatches > 0) {
mWaitingForMatchRects = true;
mFindInPageBridge.requestFindMatchRects(mFindResultBar.getRectsVersion());
} else {
// Match rects results that correlate to an earlier call to
// requestFindMatchRects might still come in, so set this sentinel to false to
// make sure we ignore them instead of showing stale results.
mWaitingForMatchRects = false;
mFindResultBar.clearMatchRects();
}
}
}
}
@CalledByNative
private void onFindMatchRectsAvailable(FindMatchRectsDetails matchRects) {
if (mFindResultBar != null && mWaitingForMatchRects) {
mFindResultBar.setMatchRects(
matchRects.version, matchRects.rects, matchRects.activeRect);
}
}
public void destroy() { public void destroy() {
if (mTabCallbackProxy != null) { if (mTabCallbackProxy != null) {
mTabCallbackProxy.destroy(); mTabCallbackProxy.destroy();
...@@ -297,6 +419,8 @@ public final class TabImpl extends ITab.Stub { ...@@ -297,6 +419,8 @@ public final class TabImpl extends ITab.Stub {
mNewTabCallbackProxy.destroy(); mNewTabCallbackProxy.destroy();
mNewTabCallbackProxy = null; mNewTabCallbackProxy = null;
} }
hideFindInPageUiAndNotifyClient();
mFindInPageCallbackClient = null;
mNavigationController = null; mNavigationController = null;
TabImplJni.get().deleteTab(mNativeTab); TabImplJni.get().deleteTab(mNativeTab);
mNativeTab = 0; mNativeTab = 0;
......
// Copyright 2020 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.weblayer_private.interfaces;
/**
* Used to forward find in page results to the client.
*/
interface IFindInPageCallbackClient {
void onFindResult(in int numberOfMatches, in int activeMatchOrdinal, in boolean finalUpdate) = 0;
void onFindEnded() = 1;
}
...@@ -6,6 +6,7 @@ package org.chromium.weblayer_private.interfaces; ...@@ -6,6 +6,7 @@ package org.chromium.weblayer_private.interfaces;
import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient;
import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient;
import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient; import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient;
import org.chromium.weblayer_private.interfaces.INavigationController; import org.chromium.weblayer_private.interfaces.INavigationController;
import org.chromium.weblayer_private.interfaces.INavigationControllerClient; import org.chromium.weblayer_private.interfaces.INavigationControllerClient;
...@@ -31,4 +32,7 @@ interface ITab { ...@@ -31,4 +32,7 @@ interface ITab {
// restores. The id is intended for the client library to avoid creating duplicate client objects // restores. The id is intended for the client library to avoid creating duplicate client objects
// for the same ITab. // for the same ITab.
int getId() = 7; int getId() = 7;
boolean setFindInPageCallbackClient(IFindInPageCallbackClient client) = 8;
void findInPage(in String searchText, boolean forward) = 9;
} }
...@@ -22,9 +22,8 @@ class TabCallbackProxy : public TabObserver { ...@@ -22,9 +22,8 @@ class TabCallbackProxy : public TabObserver {
TabCallbackProxy(JNIEnv* env, jobject obj, Tab* tab); TabCallbackProxy(JNIEnv* env, jobject obj, Tab* tab);
~TabCallbackProxy() override; ~TabCallbackProxy() override;
// BrowserObserver: // TabObserver:
void DisplayedUrlChanged(const GURL& url) override; void DisplayedUrlChanged(const GURL& url) override;
void OnRenderProcessGone() override; void OnRenderProcessGone() override;
private: private:
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_provider.h" #include "components/autofill/core/browser/autofill_provider.h"
#include "components/find_in_page/find_tab_helper.h"
#include "components/find_in_page/find_types.h"
#include "components/sessions/content/session_tab_helper.h" #include "components/sessions/content/session_tab_helper.h"
#include "content/public/browser/file_select_listener.h" #include "content/public/browser/file_select_listener.h"
#include "content/public/browser/interstitial_page.h" #include "content/public/browser/interstitial_page.h"
...@@ -151,6 +153,9 @@ TabImpl::TabImpl(ProfileImpl* profile, ...@@ -151,6 +153,9 @@ TabImpl::TabImpl(ProfileImpl* profile,
navigation_controller_ = std::make_unique<NavigationControllerImpl>(this); navigation_controller_ = std::make_unique<NavigationControllerImpl>(this);
find_in_page::FindTabHelper::CreateForWebContents(web_contents_.get());
GetFindTabHelper()->AddObserver(this);
sessions::SessionTabHelper::CreateForWebContents( sessions::SessionTabHelper::CreateForWebContents(
web_contents_.get(), web_contents_.get(),
base::BindRepeating(&TabImpl::GetSessionServiceTabHelperDelegate, base::BindRepeating(&TabImpl::GetSessionServiceTabHelperDelegate,
...@@ -161,6 +166,8 @@ TabImpl::~TabImpl() { ...@@ -161,6 +166,8 @@ TabImpl::~TabImpl() {
if (browser_) if (browser_)
browser_->RemoveTab(this); browser_->RemoveTab(this);
GetFindTabHelper()->RemoveObserver(this);
// Destruct WebContents now to avoid it calling back when this object is // Destruct WebContents now to avoid it calling back when this object is
// partially destructed. DidFinishNavigation can be called while destroying // partially destructed. DidFinishNavigation can be called while destroying
// WebContents, so stop observing first. // WebContents, so stop observing first.
...@@ -444,6 +451,47 @@ void TabImpl::CloseContents(content::WebContents* source) { ...@@ -444,6 +451,47 @@ void TabImpl::CloseContents(content::WebContents* source) {
new_tab_delegate_->CloseTab(); new_tab_delegate_->CloseTab();
} }
void TabImpl::FindReply(content::WebContents* web_contents,
int request_id,
int number_of_matches,
const gfx::Rect& selection_rect,
int active_match_ordinal,
bool final_update) {
GetFindTabHelper()->HandleFindReply(request_id, number_of_matches,
selection_rect, active_match_ordinal,
final_update);
}
#if defined(OS_ANDROID)
// FindMatchRectsReply and OnFindResultAvailable forward find-related results to
// the Java TabImpl. The find actions themselves are initiated directly from
// Java via FindInPageBridge.
void TabImpl::FindMatchRectsReply(content::WebContents* web_contents,
int version,
const std::vector<gfx::RectF>& rects,
const gfx::RectF& active_rect) {
JNIEnv* env = base::android::AttachCurrentThread();
// Create the details object.
ScopedJavaLocalRef<jobject> details_object =
Java_TabImpl_createFindMatchRectsDetails(
env, version, rects.size(),
ScopedJavaLocalRef<jobject>(Java_TabImpl_createRectF(
env, active_rect.x(), active_rect.y(), active_rect.right(),
active_rect.bottom())));
// Add the rects.
for (size_t i = 0; i < rects.size(); ++i) {
const gfx::RectF& rect = rects[i];
Java_TabImpl_setMatchRectByIndex(
env, details_object, i,
ScopedJavaLocalRef<jobject>(Java_TabImpl_createRectF(
env, rect.x(), rect.y(), rect.right(), rect.bottom())));
}
Java_TabImpl_onFindMatchRectsAvailable(env, java_impl_, details_object);
}
#endif
void TabImpl::DidFinishNavigation( void TabImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) { content::NavigationHandle* navigation_handle) {
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
...@@ -468,6 +516,17 @@ void TabImpl::RenderProcessGone(base::TerminationStatus status) { ...@@ -468,6 +516,17 @@ void TabImpl::RenderProcessGone(base::TerminationStatus status) {
observer.OnRenderProcessGone(); observer.OnRenderProcessGone();
} }
void TabImpl::OnFindResultAvailable(content::WebContents* web_contents) {
#if defined(OS_ANDROID)
const find_in_page::FindNotificationDetails& find_result =
GetFindTabHelper()->find_result();
JNIEnv* env = base::android::AttachCurrentThread();
Java_TabImpl_onFindResultAvailable(
env, java_impl_, find_result.number_of_matches(),
find_result.active_match_ordinal(), find_result.final_update());
#endif
}
void TabImpl::OnExitFullscreen() { void TabImpl::OnExitFullscreen() {
// If |processing_enter_fullscreen_| is true, it means the callback is being // If |processing_enter_fullscreen_| is true, it means the callback is being
// called while processing EnterFullscreenModeForTab(). WebContents doesn't // called while processing EnterFullscreenModeForTab(). WebContents doesn't
...@@ -535,6 +594,10 @@ void TabImpl::InitializeAutofill() { ...@@ -535,6 +594,10 @@ void TabImpl::InitializeAutofill() {
autofill_provider_.get()); autofill_provider_.get());
} }
find_in_page::FindTabHelper* TabImpl::GetFindTabHelper() {
return find_in_page::FindTabHelper::FromWebContents(web_contents_.get());
}
sessions::SessionTabHelperDelegate* TabImpl::GetSessionServiceTabHelperDelegate( sessions::SessionTabHelperDelegate* TabImpl::GetSessionServiceTabHelperDelegate(
content::WebContents* web_contents) { content::WebContents* web_contents) {
DCHECK_EQ(web_contents, web_contents_.get()); DCHECK_EQ(web_contents, web_contents_.get());
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/find_in_page/find_result_observer.h"
#include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/common/browser_controls_state.h" #include "content/public/common/browser_controls_state.h"
...@@ -48,7 +49,8 @@ class TopControlsContainerView; ...@@ -48,7 +49,8 @@ class TopControlsContainerView;
class TabImpl : public Tab, class TabImpl : public Tab,
public content::WebContentsDelegate, public content::WebContentsDelegate,
public content::WebContentsObserver { public content::WebContentsObserver,
public find_in_page::FindResultObserver {
public: public:
// TODO(sky): investigate a better way to not have so many ifdefs. // TODO(sky): investigate a better way to not have so many ifdefs.
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
...@@ -169,12 +171,27 @@ class TabImpl : public Tab, ...@@ -169,12 +171,27 @@ class TabImpl : public Tab,
bool user_gesture, bool user_gesture,
bool* was_blocked) override; bool* was_blocked) override;
void CloseContents(content::WebContents* source) override; void CloseContents(content::WebContents* source) override;
void FindReply(content::WebContents* web_contents,
int request_id,
int number_of_matches,
const gfx::Rect& selection_rect,
int active_match_ordinal,
bool final_update) override;
#if defined(OS_ANDROID)
void FindMatchRectsReply(content::WebContents* web_contents,
int version,
const std::vector<gfx::RectF>& rects,
const gfx::RectF& active_rect) override;
#endif
// content::WebContentsObserver: // content::WebContentsObserver:
void DidFinishNavigation( void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override; content::NavigationHandle* navigation_handle) override;
void RenderProcessGone(base::TerminationStatus status) override; void RenderProcessGone(base::TerminationStatus status) override;
// find_in_page::FindResultObserver:
void OnFindResultAvailable(content::WebContents* web_contents) override;
// Called from closure supplied to delegate to exit fullscreen. // Called from closure supplied to delegate to exit fullscreen.
void OnExitFullscreen(); void OnExitFullscreen();
...@@ -182,6 +199,9 @@ class TabImpl : public Tab, ...@@ -182,6 +199,9 @@ class TabImpl : public Tab,
void InitializeAutofill(); void InitializeAutofill();
// Returns the FindTabHelper for the page, or null if none exists.
find_in_page::FindTabHelper* GetFindTabHelper();
sessions::SessionTabHelperDelegate* GetSessionServiceTabHelperDelegate( sessions::SessionTabHelperDelegate* GetSessionServiceTabHelperDelegate(
content::WebContents* web_contents); content::WebContents* web_contents);
......
...@@ -38,6 +38,8 @@ android_library("java") { ...@@ -38,6 +38,8 @@ android_library("java") {
"org/chromium/weblayer/DownloadError.java", "org/chromium/weblayer/DownloadError.java",
"org/chromium/weblayer/DownloadState.java", "org/chromium/weblayer/DownloadState.java",
"org/chromium/weblayer/ErrorPageCallback.java", "org/chromium/weblayer/ErrorPageCallback.java",
"org/chromium/weblayer/FindInPageCallback.java",
"org/chromium/weblayer/FindInPageController.java",
"org/chromium/weblayer/FullscreenCallback.java", "org/chromium/weblayer/FullscreenCallback.java",
"org/chromium/weblayer/LoadError.java", "org/chromium/weblayer/LoadError.java",
"org/chromium/weblayer/Navigation.java", "org/chromium/weblayer/Navigation.java",
......
// Copyright 2020 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.weblayer;
/**
* Informed of find in page results.
*
* @since 81
*/
public abstract class FindInPageCallback {
/**
* Called when incremental results from a find operation are ready.
*
* @param numberOfMatches The total number of matches found thus far.
* @param activeMatchIndex The index of the currently highlighted match.
* @param finalUpdate Whether this is the last find result that can be expected for the current
* find operation.
*/
public void onFindResult(int numberOfMatches, int activeMatchIndex, boolean finalUpdate) {}
/**
* Called when WebLayer has ended the find session, for example due to the Tab losing active
* status. This will not be invoked when the client ends a find session via {@link
* FindInPageController#setFindInPageCallback} with a {@code null} value.
*/
public void onFindEnded() {}
}
// Copyright 2020 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.weblayer;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.chromium.weblayer_private.interfaces.APICallException;
import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient;
import org.chromium.weblayer_private.interfaces.ITab;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
/**
* Initiates find-in-page operations.
*
* There is one FindInPageController per {@link Tab}, and only the active tab may have an active
* find session.
*
* @since 81
*/
public final class FindInPageController {
private final ITab mTab;
FindInPageController(ITab tab) {
mTab = tab;
}
/**
* Starts or ends a find session.
*
* When called with a non-null parameter, this starts a find session and displays a find result
* bar over the affected web page. When called with a null parameter, the find session will end
* and the result bar will be removed.
*
* @param callback The object that will be notified of find results, or null to end the find
* session.
* @return True if the operation succeeded. Ending a find session will always succeed, but
* starting one may fail, for example if the tab is not active or a find session is
* already started.
*/
public boolean setFindInPageCallback(@Nullable FindInPageCallback callback) {
ThreadCheck.ensureOnUiThread();
try {
FindInPageCallbackClientImpl callbackClient = null;
if (callback != null) {
callbackClient = new FindInPageCallbackClientImpl(callback);
}
return mTab.setFindInPageCallbackClient(callbackClient);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
/**
* Called to initiate an in-page text search for the given string.
*
* Results will be highlighted in context, with additional attention drawn to the "active"
* result. The position of results will also be displayed on the find result bar.
* This has no effect if there is no active find session.
*
* @param searchText The {@link String} to search for. Any pre-existing search results will be
* cleared.
* @param forward The direction to move the "active" result. This only applies when the search
* text matches that of the last search.
*/
public void find(@NonNull String searchText, boolean forward) {
ThreadCheck.ensureOnUiThread();
try {
mTab.findInPage(searchText, forward);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
private static final class FindInPageCallbackClientImpl extends IFindInPageCallbackClient.Stub {
private final FindInPageCallback mCallback;
FindInPageCallbackClientImpl(FindInPageCallback callback) {
mCallback = callback;
}
public FindInPageCallback getCallback() {
return mCallback;
}
@Override
public void onFindResult(int numberOfMatches, int activeMatchOrdinal, boolean finalUpdate) {
StrictModeWorkaround.apply();
mCallback.onFindResult(numberOfMatches, activeMatchOrdinal, finalUpdate);
}
@Override
public void onFindEnded() {
StrictModeWorkaround.apply();
mCallback.onFindEnded();
}
}
}
...@@ -44,12 +44,13 @@ public final class Tab { ...@@ -44,12 +44,13 @@ public final class Tab {
private static final Map<Integer, Tab> sTabMap = new HashMap<Integer, Tab>(); private static final Map<Integer, Tab> sTabMap = new HashMap<Integer, Tab>();
private final ITab mImpl; private final ITab mImpl;
private FullscreenCallbackClientImpl mFullscreenCallbackClient;
private final NavigationController mNavigationController; private final NavigationController mNavigationController;
private final FindInPageController mFindInPageController;
private final ObserverList<TabCallback> mCallbacks; private final ObserverList<TabCallback> mCallbacks;
private Browser mBrowser; private Browser mBrowser;
private DownloadCallbackClientImpl mDownloadCallbackClient; private DownloadCallbackClientImpl mDownloadCallbackClient;
private ErrorPageCallbackClientImpl mErrorPageCallbackClient; private ErrorPageCallbackClientImpl mErrorPageCallbackClient;
private FullscreenCallbackClientImpl mFullscreenCallbackClient;
private NewTabCallback mNewTabCallback; private NewTabCallback mNewTabCallback;
// Id from the remote side. // Id from the remote side.
private final int mId; private final int mId;
...@@ -66,6 +67,7 @@ public final class Tab { ...@@ -66,6 +67,7 @@ public final class Tab {
mCallbacks = new ObserverList<TabCallback>(); mCallbacks = new ObserverList<TabCallback>();
mNavigationController = NavigationController.create(mImpl); mNavigationController = NavigationController.create(mImpl);
mFindInPageController = new FindInPageController(mImpl);
registerTab(this); registerTab(this);
} }
...@@ -211,6 +213,12 @@ public final class Tab { ...@@ -211,6 +213,12 @@ public final class Tab {
return mNavigationController; return mNavigationController;
} }
@NonNull
public FindInPageController getFindInPageController() {
ThreadCheck.ensureOnUiThread();
return mFindInPageController;
}
public void registerTabCallback(@Nullable TabCallback callback) { public void registerTabCallback(@Nullable TabCallback callback) {
ThreadCheck.ensureOnUiThread(); ThreadCheck.ensureOnUiThread();
mCallbacks.addObserver(callback); mCallbacks.addObserver(callback);
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<!-- The background of the top-view must be opaque, otherwise it bleeds through to the
cc::Layer that mirrors the contents of the top-view. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="LabelFor,ContentDescription"
android:background="#FFc9c9c9">
<EditText
android:id="@+id/url_view"
android:inputType="textUri"
android:background="@android:color/transparent"
android:selectAllOnFocus="true"
android:imeOptions="actionGo"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_alignBottom="@+id/menu_button"
android:layout_alignTop="@+id/menu_button"
android:layout_toStartOf="@+id/menu_button"
android:layout_alignParentStart="true" />
<ImageButton
android:id="@id/menu_button"
android:src="@android:drawable/ic_menu_more"
android:background="@android:color/transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_alignParentEnd="true" />
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.Holo.Light.ProgressBar.Horizontal"
android:indeterminate="false"
android:max="100"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/url_view"
android:layout_marginTop="0dp"
android:layout_marginBottom="-10px"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 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. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText">
<item android:id="@+id/find_begin_menu_id"
android:title="Find in page" />
<item android:id="@+id/find_end_menu_id"
android:title="Clear find in page" />
</menu>
...@@ -12,27 +12,30 @@ import android.support.v4.app.Fragment; ...@@ -12,27 +12,30 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.ValueCallback; import android.webkit.ValueCallback;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import org.chromium.weblayer.Browser; import org.chromium.weblayer.Browser;
import org.chromium.weblayer.DownloadCallback; import org.chromium.weblayer.DownloadCallback;
import org.chromium.weblayer.ErrorPageCallback; import org.chromium.weblayer.ErrorPageCallback;
import org.chromium.weblayer.FindInPageCallback;
import org.chromium.weblayer.FullscreenCallback; import org.chromium.weblayer.FullscreenCallback;
import org.chromium.weblayer.Navigation;
import org.chromium.weblayer.NavigationCallback; import org.chromium.weblayer.NavigationCallback;
import org.chromium.weblayer.NavigationController; import org.chromium.weblayer.NavigationController;
import org.chromium.weblayer.NewTabCallback; import org.chromium.weblayer.NewTabCallback;
...@@ -57,10 +60,11 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -57,10 +60,11 @@ public class WebLayerShellActivity extends FragmentActivity {
private Profile mProfile; private Profile mProfile;
private Browser mBrowser; private Browser mBrowser;
private EditText mUrlView; private EditText mUrlView;
private ImageButton mMenuButton;
private ProgressBar mLoadProgressBar; private ProgressBar mLoadProgressBar;
private View mMainView; private View mMainView;
private int mMainViewId; private int mMainViewId;
private ViewGroup mTopContentsContainer; private View mTopContentsContainer;
private TabListCallback mTabListCallback; private TabListCallback mTabListCallback;
private List<Tab> mPreviousTabList = new ArrayList<>(); private List<Tab> mPreviousTabList = new ArrayList<>();
private Runnable mExitFullscreenRunnable; private Runnable mExitFullscreenRunnable;
...@@ -86,14 +90,10 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -86,14 +90,10 @@ public class WebLayerShellActivity extends FragmentActivity {
mMainView = mainView; mMainView = mainView;
setContentView(mainView); setContentView(mainView);
mUrlView = new EditText(this); mTopContentsContainer =
mUrlView.setId(View.generateViewId()); LayoutInflater.from(this).inflate(R.layout.shell_browser_controls, null);
mUrlView.setSelectAllOnFocus(true);
mUrlView.setInputType(InputType.TYPE_TEXT_VARIATION_URI); mUrlView = mTopContentsContainer.findViewById(R.id.url_view);
mUrlView.setImeOptions(EditorInfo.IME_ACTION_GO);
// The background of the top-view must be opaque, otherwise it bleeds through to the
// cc::Layer that mirrors the contents of the top-view.
mUrlView.setBackgroundColor(0xFFa9a9a9);
mUrlView.setOnEditorActionListener(new OnEditorActionListener() { mUrlView.setOnEditorActionListener(new OnEditorActionListener() {
@Override @Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
...@@ -111,22 +111,38 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -111,22 +111,38 @@ public class WebLayerShellActivity extends FragmentActivity {
} }
}); });
mLoadProgressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal); mMenuButton = mTopContentsContainer.findViewById(R.id.menu_button);
mLoadProgressBar.setIndeterminate(false); mMenuButton.setOnClickListener(new View.OnClickListener() {
mLoadProgressBar.setMax(100); @Override
mLoadProgressBar.setVisibility(View.INVISIBLE); public void onClick(View v) {
PopupMenu popup = new PopupMenu(WebLayerShellActivity.this, v);
// The progress bar sits above the URL bar in Z order and at its bottom in Y. popup.getMenuInflater().inflate(R.menu.app_menu, popup.getMenu());
mTopContentsContainer = new RelativeLayout(this); popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
mTopContentsContainer.addView(mUrlView, @Override
new RelativeLayout.LayoutParams( public boolean onMenuItemClick(MenuItem item) {
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); if (item.getItemId() == R.id.find_begin_menu_id) {
// TODO(estade): add a UI for FIP. For now, just search for "cat", or go
// to the next result if a search has already been initiated.
mBrowser.getActiveTab().getFindInPageController().setFindInPageCallback(
new FindInPageCallback() {});
mBrowser.getActiveTab().getFindInPageController().find("cat", true);
return true;
}
if (item.getItemId() == R.id.find_end_menu_id) {
mBrowser.getActiveTab().getFindInPageController().setFindInPageCallback(
null);
return true;
}
return false;
}
});
popup.show();
}
});
RelativeLayout.LayoutParams progressLayoutParams = new RelativeLayout.LayoutParams( mLoadProgressBar = mTopContentsContainer.findViewById(R.id.progress_bar);
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
progressLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mUrlView.getId());
progressLayoutParams.setMargins(0, 0, 0, -10);
mTopContentsContainer.addView(mLoadProgressBar, progressLayoutParams);
try { try {
// This ensures asynchronous initialization of WebLayer on first start of activity. // This ensures asynchronous initialization of WebLayer on first start of activity.
...@@ -213,6 +229,7 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -213,6 +229,7 @@ public class WebLayerShellActivity extends FragmentActivity {
@Override @Override
public void onNewTab(Tab newTab, @NewTabType int type) { public void onNewTab(Tab newTab, @NewTabType int type) {
setTabCallbacks(newTab, fragment); setTabCallbacks(newTab, fragment);
mBrowser.getActiveTab().getFindInPageController().setFindInPageCallback(null);
mPreviousTabList.add(mBrowser.getActiveTab()); mPreviousTabList.add(mBrowser.getActiveTab());
mBrowser.setActiveTab(newTab); mBrowser.setActiveTab(newTab);
} }
...@@ -265,6 +282,11 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -265,6 +282,11 @@ public class WebLayerShellActivity extends FragmentActivity {
} }
}); });
tab.getNavigationController().registerNavigationCallback(new NavigationCallback() { tab.getNavigationController().registerNavigationCallback(new NavigationCallback() {
@Override
public void onNavigationStarted(Navigation navigation) {
mBrowser.getActiveTab().getFindInPageController().setFindInPageCallback(null);
}
@Override @Override
public void onLoadStateChanged(boolean isLoading, boolean toDifferentDocument) { public void onLoadStateChanged(boolean isLoading, boolean toDifferentDocument) {
mLoadProgressBar.setVisibility( mLoadProgressBar.setVisibility(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment