Index: chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9c9cbc55762a8e7191073c1cf8d2ded0d5830495 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java |
@@ -0,0 +1,417 @@ |
+// Copyright 2015 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.chrome.browser.compositor.bottombar; |
+ |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.chrome.browser.ChromeActivity; |
+import org.chromium.chrome.browser.WebContentsFactory; |
+import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler; |
+import org.chromium.components.navigation_interception.InterceptNavigationDelegate; |
+import org.chromium.components.navigation_interception.NavigationParams; |
+import org.chromium.components.web_contents_delegate_android.WebContentsDelegateAndroid; |
+import org.chromium.content.browser.ContentView; |
+import org.chromium.content.browser.ContentViewClient; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content_public.browser.LoadUrlParams; |
+import org.chromium.content_public.browser.WebContents; |
+import org.chromium.content_public.browser.WebContentsObserver; |
+ |
+/** |
+ * Controls the Contextual Search Panel. |
+ */ |
+public class OverlayPanelContent { |
+ |
+ /** |
+ * The ContentViewCore that this panel will display. |
+ */ |
+ private ContentViewCore mContentViewCore; |
+ |
+ /** |
+ * The pointer to the native version of this class. |
+ */ |
+ private long mNativeOverlayPanelContentPtr; |
+ |
+ /** |
+ * Used for progress bar events. |
+ */ |
+ private final WebContentsDelegateAndroid mWebContentsDelegate; |
+ |
+ /** |
+ * The activity that this content is contained in. |
+ */ |
+ private ChromeActivity mActivity; |
+ |
+ /** |
+ * Observer used for tracking loading and navigation. |
+ */ |
+ private WebContentsObserver mWebContentsObserver; |
+ |
+ /** |
+ * If the ContentViewCore has loaded a URL. |
+ */ |
+ private boolean mDidLoadAnyUrl; |
+ |
+ /** |
+ * If the content view is currently being displayed. |
+ */ |
+ private boolean mIsContentViewShowing; |
+ |
+ /** |
+ * The ContentViewCore responsible for displaying content. |
+ */ |
+ private ContentViewClient mContentViewClient; |
+ |
+ /** |
+ * The observer used by this object to inform inplementers of different events. |
+ */ |
+ private OverlayContentDelegate mOverlayObserver; |
+ |
+ /** |
+ * Used to observe progress bar events. |
+ */ |
+ private OverlayContentProgressObserver mProgressObserver; |
+ |
+ // http://crbug.com/522266 : An instance of InterceptNavigationDelegateImpl should be kept in |
+ // java layer. Otherwise, the instance could be garbage-collected unexpectedly. |
+ private InterceptNavigationDelegate mInterceptNavigationDelegate; |
+ |
+ // ============================================================================================ |
+ // InterceptNavigationDelegateImpl |
+ // ============================================================================================ |
+ |
+ // Used to intercept intent navigations. |
+ // TODO(jeremycho): Consider creating a Tab with the Panel's ContentViewCore, |
+ // which would also handle functionality like long-press-to-paste. |
+ private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate { |
+ final ExternalNavigationHandler mExternalNavHandler = new ExternalNavigationHandler( |
+ mActivity); |
+ @Override |
+ public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { |
+ // TODO(mdjones): Rather than passing the two navigation params, instead consider |
+ // passing a boolean to make this API simpler. |
+ return !mOverlayObserver.shouldInterceptNavigation(mExternalNavHandler, |
+ navigationParams); |
+ } |
+ } |
+ |
+ // ============================================================================================ |
+ // Constructor |
+ // ============================================================================================ |
+ |
+ /** |
+ * @param overlayObserver An observer for events that occur on this content. If null is passed |
+ * for this parameter, the default one will be used. |
+ * @param progresObserver An observer for progress related events. |
+ * @param activity The ChromeActivity that contains this object. |
+ */ |
+ public OverlayPanelContent(OverlayContentDelegate overlayObserver, |
+ OverlayContentProgressObserver progressObserver, ChromeActivity activity) { |
+ mNativeOverlayPanelContentPtr = nativeInit(); |
+ mOverlayObserver = overlayObserver; |
+ mProgressObserver = progressObserver; |
+ mActivity = activity; |
+ |
+ mWebContentsDelegate = new WebContentsDelegateAndroid() { |
+ @Override |
+ public void onLoadStarted() { |
+ super.onLoadStarted(); |
+ mProgressObserver.onProgressBarStarted(); |
+ } |
+ |
+ @Override |
+ public void onLoadStopped() { |
+ super.onLoadStopped(); |
+ mProgressObserver.onProgressBarFinished(); |
+ } |
+ |
+ @Override |
+ public void onLoadProgressChanged(int progress) { |
+ super.onLoadProgressChanged(progress); |
+ mProgressObserver.onProgressBarUpdated(progress); |
+ } |
+ }; |
+ } |
+ |
+ /** |
+ * Create a largely empty OverlayPanelContent for testing. |
+ */ |
+ @VisibleForTesting |
+ public OverlayPanelContent() { |
+ // NOTE(mdjones): This constructor is intentionally sparse for testing. |
+ mWebContentsDelegate = null; |
+ } |
+ |
+ // ============================================================================================ |
+ // ContentViewCore related |
+ // ============================================================================================ |
+ |
+ /** |
+ * @param observer The OverlayContentDelegate to use. |
+ */ |
+ @VisibleForTesting |
+ public void setOverlayObserver(OverlayContentDelegate observer) { |
+ mOverlayObserver = observer; |
+ } |
+ |
+ /** |
+ * Create a new ContentViewCore that will be managed by this panel. |
+ * TODO(mdjones): Make this private and create a new instance of this class per request. |
+ */ |
+ public void createNewContentView() { |
+ // If the ContentViewCore has already been created, but never used, |
+ // then there's no need to create a new one. |
+ if (mContentViewCore != null && !mDidLoadAnyUrl) { |
+ return; |
+ } |
+ |
+ destroyContentView(); |
+ mContentViewCore = new ContentViewCore(mActivity); |
+ |
+ if (mContentViewClient == null) { |
+ mContentViewClient = new ContentViewClient(); |
+ } |
+ |
+ mContentViewCore.setContentViewClient(mContentViewClient); |
+ |
+ ContentView cv = ContentView.createContentView(mActivity, mContentViewCore); |
+ // Creates an initially hidden WebContents which gets shown when the panel is opened. |
+ mContentViewCore.initialize(cv, cv, |
+ WebContentsFactory.createWebContents(false, true), mActivity.getWindowAndroid()); |
+ |
+ // Transfers the ownership of the WebContents to the native OverlayPanelContent. |
+ nativeSetWebContents(mNativeOverlayPanelContentPtr, mContentViewCore, |
+ mWebContentsDelegate); |
+ |
+ mWebContentsObserver = |
+ new WebContentsObserver(mContentViewCore.getWebContents()) { |
+ @Override |
+ public void didStartLoading(String url) { |
+ mOverlayObserver.onContentLoadStarted(url); |
+ } |
+ |
+ @Override |
+ public void didStartProvisionalLoadForFrame(long frameId, long parentFrameId, |
+ boolean isMainFrame, String validatedUrl, boolean isErrorPage, |
+ boolean isIframeSrcdoc) { |
+ if (isMainFrame) { |
+ mOverlayObserver.onMainFrameLoadStarted(validatedUrl); |
+ } |
+ } |
+ |
+ @Override |
+ public void didNavigateMainFrame(String url, String baseUrl, |
+ boolean isNavigationToDifferentPage, boolean isNavigationInPage, |
+ int httpResultCode) { |
+ // TODO(mdjones): Instead of tracking this, create a new instance of this |
+ // class per request. |
+ mDidLoadAnyUrl = false; |
+ mOverlayObserver.onMainFrameNavigation(url, |
+ isHttpFailureCode(httpResultCode)); |
+ } |
+ |
+ @Override |
+ public void didFinishLoad(long frameId, String validatedUrl, |
+ boolean isMainFrame) { |
+ mOverlayObserver.onContentLoadFinished(); |
+ } |
+ }; |
+ |
+ mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); |
+ nativeSetInterceptNavigationDelegate(mNativeOverlayPanelContentPtr, |
+ mInterceptNavigationDelegate, mContentViewCore.getWebContents()); |
+ |
+ mOverlayObserver.onContentViewCreated(mContentViewCore); |
+ } |
+ |
+ /** |
+ * Destroy this panel's ContentViewCore. |
+ * TODO(mdjones): Make this private. |
+ */ |
+ public void destroyContentView() { |
+ if (mContentViewCore != null) { |
+ nativeDestroyWebContents(mNativeOverlayPanelContentPtr); |
+ mContentViewCore.getWebContents().destroy(); |
+ mContentViewCore.destroy(); |
+ mContentViewCore = null; |
+ if (mWebContentsObserver != null) { |
+ mWebContentsObserver.destroy(); |
+ mWebContentsObserver = null; |
+ } |
+ |
+ mOverlayObserver.onContentViewDestroyed(); |
+ } |
+ |
+ // This should be called last here. The setSearchContentViewVisibility method |
+ // will change the visibility the SearchContentView but also set the value of the |
+ // internal property mIsSearchContentViewShowing. If we call this after deleting |
+ // the SearchContentView, it will be faster, because only the internal property |
+ // will be changed, since there will be no need to change the visibility of the |
+ // SearchContentView. |
+ // |
+ // Also, this should be called outside the block above because tests will not |
+ // create a ContentView, therefore if this call is placed inside the block, |
+ // it will not be called on tests, which will cause some tests to fail. |
+ setVisibility(false); |
+ |
+ } |
+ |
+ /** |
+ * Load a URL, this will trigger creation of a new ContentViewCore. |
+ * @param url The URL that should be loaded. |
+ */ |
+ public void loadUrl(String url) { |
+ createNewContentView(); |
+ |
+ if (mContentViewCore != null && mContentViewCore.getWebContents() != null) { |
+ mDidLoadAnyUrl = true; |
+ mContentViewCore.getWebContents().getNavigationController().loadUrl( |
+ new LoadUrlParams(url)); |
+ mContentViewCore.onShow(); |
+ } |
+ } |
+ |
+ // ============================================================================================ |
+ // Utilities |
+ // ============================================================================================ |
+ |
+ /** |
+ * Calls updateTopControlsState on the ContentViewCore. |
+ * @param enableHiding Enable the toolbar's ability to hide. |
+ * @param enableShowing If the toolbar is allowed to show. |
+ * @param animate If the toolbar should animate when showing/hiding. |
+ */ |
+ public void updateTopControlsState(boolean enableHiding, boolean enableShowing, |
+ boolean animate) { |
+ if (mContentViewCore != null && mContentViewCore.getWebContents() != null) { |
+ mContentViewCore.getWebContents().updateTopControlsState(enableHiding, enableShowing, |
+ animate); |
+ } |
+ } |
+ |
+ /** |
+ * @return true if the panel loaded a URL. |
+ */ |
+ public boolean didLoadAnyUrl() { |
+ return mDidLoadAnyUrl; |
+ } |
+ |
+ /** |
+ * Reset the ContentViewCore's scroll position to (0, 0). |
+ */ |
+ public void resetContentViewScroll() { |
+ if (mContentViewCore != null) { |
+ mContentViewCore.scrollTo(0, 0); |
+ } |
+ } |
+ |
+ /** |
+ * @return The Y scroll position. |
+ */ |
+ public float getContentViewVerticalScroll() { |
+ return mContentViewCore != null |
+ ? mContentViewCore.computeVerticalScrollOffset() : -1.f; |
+ } |
+ |
+ /** |
+ * Sets the visibility of the Search Content View. |
+ * @param isVisible True to make it visible. |
+ */ |
+ public void setVisibility(boolean isVisible) { |
+ if (mIsContentViewShowing == isVisible) return; |
+ |
+ mIsContentViewShowing = isVisible; |
+ |
+ if (isVisible) { |
+ // The CVC is created with the search request, but if none was made we'll need |
+ // one in order to display an empty panel. |
+ if (mContentViewCore == null) { |
+ createNewContentView(); |
+ } |
+ if (mContentViewCore != null) mContentViewCore.onShow(); |
+ mOverlayObserver.onContentViewSeen(); |
+ } else { |
+ if (mContentViewCore != null) mContentViewCore.onHide(); |
+ destroyContentView(); |
+ } |
+ |
+ mOverlayObserver.onVisibilityChanged(isVisible); |
+ } |
+ |
+ /** |
+ * @return Whether the given HTTP result code represents a failure or not. |
+ */ |
+ private boolean isHttpFailureCode(int httpResultCode) { |
+ return httpResultCode <= 0 || httpResultCode >= 400; |
+ } |
+ |
+ /** |
+ * Set a ContentViewClient for this panel to use (will be reused for each new ContentViewCore). |
+ * @param viewClient The ContentViewClient to use. |
+ */ |
+ public void setContentViewClient(ContentViewClient viewClient) { |
+ mContentViewClient = viewClient; |
+ if (mContentViewCore != null) { |
+ mContentViewCore.setContentViewClient(mContentViewClient); |
+ } |
+ } |
+ |
+ /** |
+ * @return true if the ContentViewCore is visible on the page. |
+ */ |
+ public boolean isContentViewShowing() { |
+ return mIsContentViewShowing; |
+ } |
+ |
+ // ============================================================================================ |
+ // Methods for managing this panel's ContentViewCore. |
+ // ============================================================================================ |
+ |
+ /** |
+ * Reset this object's native pointer to 0; |
+ */ |
+ @CalledByNative |
+ private void clearNativePanelContentPtr() { |
+ assert mNativeOverlayPanelContentPtr != 0; |
+ mNativeOverlayPanelContentPtr = 0; |
+ } |
+ |
+ /** |
+ * @return This panel's ContentViewCore. |
+ */ |
+ @VisibleForTesting |
+ public ContentViewCore getContentViewCore() { |
+ return mContentViewCore; |
+ } |
+ |
+ /** |
+ * Remove the list history entry from this panel if it was within a certain timeframe. |
+ * @param historyUrl The URL to remove. |
+ * @param urlTimeMS The time the URL was navigated to. |
+ */ |
+ public void removeLastHistoryEntry(String historyUrl, long urlTimeMs) { |
+ nativeRemoveLastHistoryEntry(mNativeOverlayPanelContentPtr, historyUrl, urlTimeMs); |
+ } |
+ |
+ /** |
+ * Destroy the native component of this class. |
+ */ |
+ @VisibleForTesting |
+ public void destroy() { |
+ destroyContentView(); |
+ nativeDestroy(mNativeOverlayPanelContentPtr); |
+ } |
+ |
+ // Native calls. |
+ private native long nativeInit(); |
+ private native void nativeDestroy(long nativeOverlayPanelContent); |
+ private native void nativeRemoveLastHistoryEntry( |
+ long nativeOverlayPanelContent, String historyUrl, long urlTimeMs); |
+ private native void nativeSetWebContents(long nativeOverlayPanelContent, |
+ ContentViewCore contentViewCore, WebContentsDelegateAndroid delegate); |
+ private native void nativeDestroyWebContents(long nativeOverlayPanelContent); |
+ private native void nativeSetInterceptNavigationDelegate(long nativeOverlayPanelContent, |
+ InterceptNavigationDelegate delegate, WebContents webContents); |
+} |