| Index: chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java b/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
|
| index 7f73cac91644550d0126bbe2401d67cde1a6c9ab..6dbd1bca48a9f2fa7c8867ff989e4007a0558d6c 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
|
| @@ -4,10 +4,24 @@
|
|
|
| package org.chromium.chrome.browser;
|
|
|
| +import android.content.Context;
|
| +import android.graphics.Color;
|
| +import android.view.View;
|
| +
|
| import org.chromium.base.CalledByNative;
|
| +import org.chromium.base.ObserverList;
|
| +import org.chromium.chrome.browser.profiles.Profile;
|
| import org.chromium.content.browser.ContentView;
|
| +import org.chromium.content.browser.ContentViewClient;
|
| +import org.chromium.content.browser.ContentViewCore;
|
| +import org.chromium.content.browser.NavigationClient;
|
| +import org.chromium.content.browser.NavigationHistory;
|
| +import org.chromium.content.browser.PageInfo;
|
| +import org.chromium.content.browser.WebContentsObserverAndroid;
|
| import org.chromium.ui.WindowAndroid;
|
|
|
| +import java.util.concurrent.atomic.AtomicInteger;
|
| +
|
| /**
|
| * The basic Java representation of a tab. Contains and manages a {@link ContentView}.
|
| *
|
| @@ -19,18 +33,478 @@ import org.chromium.ui.WindowAndroid;
|
| * destroyed which will call into the native subclass and finally lead to the destruction of the
|
| * parent classes.
|
| */
|
| -public abstract class TabBase {
|
| +public abstract class TabBase implements NavigationClient {
|
| public static final int INVALID_TAB_ID = -1;
|
|
|
| - private final WindowAndroid mWindowAndroid;
|
| + /** Used for automatically generating tab ids. */
|
| + private static final AtomicInteger sIdCounter = new AtomicInteger();
|
| +
|
| private int mNativeTabAndroid;
|
|
|
| - protected TabBase(WindowAndroid window) {
|
| + /** Unique id of this tab (within its container). */
|
| + private final int mId;
|
| +
|
| + /** Whether or not this tab is an incognito tab. */
|
| + private final boolean mIncognito;
|
| +
|
| + /** The {@link Context} used to create {@link View}s or other Android components. This is
|
| + * purposely an application Context not an activity Context. */
|
| + private final Context mContext;
|
| +
|
| + /** Gives {@link TabBase} a way to interact with the Android window. */
|
| + private final WindowAndroid mWindowAndroid;
|
| +
|
| + /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */
|
| + private NativePage mNativePage;
|
| +
|
| + /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */
|
| + private ContentView mContentView;
|
| +
|
| + /**
|
| + * The {@link ContentViewCore} for the current page, provided for convenience. This always
|
| + * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is
|
| + * {@code null}.
|
| + */
|
| + private ContentViewCore mContentViewCore;
|
| +
|
| + // Observers and Delegates.
|
| + private ContentViewClient mContentViewClient;
|
| + private WebContentsObserverAndroid mWebContentsObserver;
|
| + private TabBaseChromeWebContentsDelegateAndroid mWebContentsDelegate;
|
| + private ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
|
| +
|
| + /**
|
| + * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
|
| + * {@link TabObserver}s. Meant to be overridden by subclasses.
|
| + */
|
| + public class TabBaseChromeWebContentsDelegateAndroid
|
| + extends ChromeWebContentsDelegateAndroid {
|
| + @Override
|
| + public void onLoadProgressChanged(int progress) {
|
| + for (TabObserver observer : mObservers) {
|
| + observer.onLoadProgressChanged(TabBase.this, progress);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void onUpdateUrl(String url) {
|
| + for (TabObserver observer : mObservers) observer.onUpdateUrl(TabBase.this, url);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Creates an instance of a {@link TabBase} with no id.
|
| + * @param incognito Whether or not this tab is incognito.
|
| + * @param context An instance of a {@link Context}.
|
| + * @param window An instance of a {@link WindowAndroid}.
|
| + */
|
| + public TabBase(boolean incognito, Context context, WindowAndroid window) {
|
| + this(INVALID_TAB_ID, incognito, context, window);
|
| + }
|
| +
|
| + /**
|
| + * Creates an instance of a {@link TabBase}.
|
| + * @param id The id this tab should be identified with.
|
| + * @param incognito Whether or not this tab is incognito.
|
| + * @param context An instance of a {@link Context}.
|
| + * @param window An instance of a {@link WindowAndroid}.
|
| + */
|
| + public TabBase(int id, boolean incognito, Context context, WindowAndroid window) {
|
| + mId = generateValidId(id);
|
| + mIncognito = incognito;
|
| + mContext = context != null ? context.getApplicationContext() : null;
|
| mWindowAndroid = window;
|
| }
|
|
|
| + /**
|
| + * Adds a {@link TabObserver} to be notified on {@link TabBase} changes.
|
| + * @param observer The {@link TabObserver} to add.
|
| + */
|
| + public final void addObserver(TabObserver observer) {
|
| + mObservers.addObserver(observer);
|
| + }
|
| +
|
| + /**
|
| + * Removes a {@link TabObserver}.
|
| + * @param observer The {@link TabObserver} to remove.
|
| + */
|
| + public final void removeObserver(TabObserver observer) {
|
| + mObservers.removeObserver(observer);
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not this tab has a previous navigation entry.
|
| + */
|
| + public boolean canGoBack() {
|
| + return mContentViewCore != null && mContentViewCore.canGoBack();
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not this tab has a navigation entry after the current one.
|
| + */
|
| + public boolean canGoForward() {
|
| + return mContentViewCore != null && mContentViewCore.canGoForward();
|
| + }
|
| +
|
| + /**
|
| + * Goes to the navigation entry before the current one.
|
| + */
|
| + public void goBack() {
|
| + if (mContentViewCore != null) mContentViewCore.goBack();
|
| + }
|
| +
|
| + /**
|
| + * Goes to the navigation entry after the current one.
|
| + */
|
| + public void goForward() {
|
| + if (mContentViewCore != null) mContentViewCore.goForward();
|
| + }
|
| +
|
| + @Override
|
| + public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
|
| + if (mContentViewCore != null) {
|
| + return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
|
| + } else {
|
| + return new NavigationHistory();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void goToNavigationIndex(int index) {
|
| + if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not the {@link TabBase} is currently showing an interstitial page, such as
|
| + * a bad HTTPS page.
|
| + */
|
| + public boolean isShowingInterstitialPage() {
|
| + ContentViewCore contentViewCore = getContentViewCore();
|
| + return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not the tab has something valid to render.
|
| + */
|
| + public boolean isReady() {
|
| + return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link View} displaying the current page in the tab. This might be a
|
| + * {@link ContentView} but could potentially be any instance of {@link View}. This can
|
| + * be {@code null}, if the tab is frozen or being initialized or destroyed.
|
| + */
|
| + public View getView() {
|
| + PageInfo pageInfo = getPageInfo();
|
| + return pageInfo != null ? pageInfo.getView() : null;
|
| + }
|
| +
|
| + /**
|
| + * @return The width of the content of this tab. Can be 0 if there is no content.
|
| + */
|
| + public int getWidth() {
|
| + View view = getView();
|
| + return view != null ? view.getWidth() : 0;
|
| + }
|
| +
|
| + /**
|
| + * @return The height of the content of this tab. Can be 0 if there is no content.
|
| + */
|
| + public int getHeight() {
|
| + View view = getView();
|
| + return view != null ? view.getHeight() : 0;
|
| + }
|
| +
|
| + /**
|
| + * @return The application {@link Context} associated with this tab.
|
| + */
|
| + protected Context getApplicationContext() {
|
| + return mContext;
|
| + }
|
| +
|
| + /**
|
| + * Reloads the current page content if it is a {@link ContentView}.
|
| + */
|
| + public void reload() {
|
| + // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
|
| + ContentViewCore contentViewCore = getContentViewCore();
|
| + if (contentViewCore != null) contentViewCore.reload();
|
| + }
|
| +
|
| + /**
|
| + * @return The background color of the tab.
|
| + */
|
| + public int getBackgroundColor() {
|
| + return getPageInfo() != null ? getPageInfo().getBackgroundColor() : Color.WHITE;
|
| + }
|
| +
|
| + /**
|
| + * @return The profile associated with this tab.
|
| + */
|
| + public Profile getProfile() {
|
| + if (mNativeTabAndroid == 0) return null;
|
| + return nativeGetProfileAndroid(mNativeTabAndroid);
|
| + }
|
| +
|
| + /**
|
| + * @return The id representing this tab.
|
| + */
|
| + public int getId() {
|
| + return mId;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not this tab is incognito.
|
| + */
|
| + public boolean isIncognito() {
|
| + return mIncognito;
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link ContentView} associated with the current page, or {@code null} if
|
| + * there is no current page or the current page is displayed using something besides a
|
| + * {@link ContentView}.
|
| + */
|
| + public ContentView getContentView() {
|
| + return mNativePage == null ? mContentView : null;
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link ContentViewCore} associated with the current page, or {@code null} if
|
| + * there is no current page or the current page is displayed using something besides a
|
| + * {@link ContentView}.
|
| + */
|
| + public ContentViewCore getContentViewCore() {
|
| + return mNativePage == null ? mContentViewCore : null;
|
| + }
|
| +
|
| + /**
|
| + * @return A {@link PageInfo} describing the current page. This is always not {@code null}
|
| + * except during initialization, destruction, and when the tab is frozen.
|
| + */
|
| + public PageInfo getPageInfo() {
|
| + return mNativePage != null ? mNativePage : mContentView;
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link NativePage} associated with the current page, or {@code null} if there is
|
| + * no current page or the current page is displayed using something besides
|
| + * {@link NativePage}.
|
| + */
|
| + public NativePage getNativePage() {
|
| + return mNativePage;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not the {@link TabBase} represents a {@link NativePage}.
|
| + */
|
| + public boolean isNativePage() {
|
| + return mNativePage != null;
|
| + }
|
| +
|
| + /**
|
| + * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
|
| + * currently loaded page.
|
| + * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one.
|
| + * @param reloadOnChange Reload the page if the user agent has changed.
|
| + */
|
| + public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
|
| + if (mContentViewCore != null) {
|
| + mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
|
| + */
|
| + public boolean getUseDesktopUserAgent() {
|
| + return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
|
| + * associated with the current page. There can still be a {@link ContentViewClient}
|
| + * even when there is no {@link ContentViewCore}.
|
| + */
|
| + protected ContentViewClient getContentViewClient() {
|
| + return mContentViewClient;
|
| + }
|
| +
|
| + /**
|
| + * @param client The {@link ContentViewClient} to be bound to any current or new
|
| + * {@link ContentViewCore}s associated with this {@link TabBase}.
|
| + */
|
| + protected void setContentViewClient(ContentViewClient client) {
|
| + if (mContentViewClient == client) return;
|
| +
|
| + ContentViewClient oldClient = mContentViewClient;
|
| + mContentViewClient = client;
|
| +
|
| + if (mContentViewCore == null) return;
|
| +
|
| + if (mContentViewClient != null) {
|
| + mContentViewCore.setContentViewClient(mContentViewClient);
|
| + } else if (oldClient != null) {
|
| + // We can't set a null client, but we should clear references to the last one.
|
| + mContentViewCore.setContentViewClient(new ContentViewClient());
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Shows the given {@code nativePage} if it's not already showing.
|
| + * @param nativePage The {@link NativePage} to show.
|
| + */
|
| + protected void showNativePage(NativePage nativePage) {
|
| + if (mNativePage == nativePage) return;
|
| + destroyNativePageInternal();
|
| + mNativePage = nativePage;
|
| + for (TabObserver observer : mObservers) observer.onContentChanged(this);
|
| + }
|
| +
|
| + /**
|
| + * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}.
|
| + */
|
| + protected void showRenderedPage() {
|
| + if (mNativePage == null) return;
|
| + destroyNativePageInternal();
|
| + for (TabObserver observer : mObservers) observer.onContentChanged(this);
|
| + }
|
| +
|
| + /**
|
| + * Initializes this {@link TabBase}.
|
| + */
|
| + public void initialize() { }
|
| +
|
| + /**
|
| + * A helper method to initialize a {@link ContentView} without any native WebContents pointer.
|
| + */
|
| + protected final void initContentView() {
|
| + initContentView(ContentViewUtil.createNativeWebContents(mIncognito));
|
| + }
|
| +
|
| + /**
|
| + * Completes the {@link ContentView} specific initialization around a native WebContents
|
| + * pointer. {@link #getPageInfo()} will still return the {@link NativePage} if there is one.
|
| + * All initialization that needs to reoccur after a web contents swap should be added here.
|
| + * <p />
|
| + * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
|
| + * state as this tab this call will fail.
|
| + *
|
| + * @param nativeWebContents The native web contents pointer.
|
| + */
|
| + protected void initContentView(int nativeWebContents) {
|
| + destroyNativePageInternal();
|
| +
|
| + mContentView = ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid());
|
| +
|
| + mContentViewCore = mContentView.getContentViewCore();
|
| + mWebContentsDelegate = createWebContentsDelegate();
|
| + mWebContentsObserver = createWebContentsObserverAndroid(mContentViewCore);
|
| +
|
| + if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
|
| + nativeInitWebContents(
|
| + mNativeTabAndroid, mId, mIncognito, mContentViewCore, mWebContentsDelegate);
|
| + }
|
| +
|
| + /**
|
| + * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView}
|
| + * currently associated with this {@link TabBase}. Typically, pnce this call is made this
|
| + * {@link TabBase} should no longer be used as subclasses usually destroy the native component.
|
| + */
|
| + public void destroy() {
|
| + for (TabObserver observer : mObservers) observer.onDestroyed(this);
|
| +
|
| + destroyNativePageInternal();
|
| + destroyContentView(true);
|
| + }
|
| +
|
| + private void destroyNativePageInternal() {
|
| + if (mNativePage == null) return;
|
| +
|
| + mNativePage.destroy();
|
| + mNativePage = null;
|
| + }
|
| +
|
| + /**
|
| + * Destroys the current {@link ContentView}.
|
| + * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
|
| + */
|
| + protected final void destroyContentView(boolean deleteNativeWebContents) {
|
| + if (mContentView == null) return;
|
| +
|
| + destroyContentViewInternal(mContentView);
|
| +
|
| + if (mContentViewCore != null) mContentViewCore.destroy();
|
| +
|
| + mContentView = null;
|
| + mContentViewCore = null;
|
| + mContentViewClient = null;
|
| + mWebContentsObserver = null;
|
| + nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
|
| + }
|
| +
|
| + /**
|
| + * Gives subclasses the chance to clean up some state associated with this {@link ContentView}.
|
| + * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage}
|
| + * is showing.
|
| + * @param contentView The {@link ContentView} that should have associated state cleaned up.
|
| + */
|
| + protected void destroyContentViewInternal(ContentView contentView) {
|
| + }
|
| +
|
| + /**
|
| + * A helper method to allow subclasses to build their own delegate.
|
| + * @return An instance of a {@link TabBaseChromeWebContentsDelegateAndroid}.
|
| + */
|
| + protected TabBaseChromeWebContentsDelegateAndroid createWebContentsDelegate() {
|
| + return new TabBaseChromeWebContentsDelegateAndroid();
|
| + }
|
| +
|
| + /**
|
| + * A helper method to allow subclasses to build their own observer.
|
| + * @param contentViewCore The {@link ContentViewCore} this observer should be built for.
|
| + * @return An instance of a {@link WebContentsObserverAndroid}.
|
| + */
|
| + protected WebContentsObserverAndroid createWebContentsObserverAndroid(
|
| + ContentViewCore contentViewCore) {
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * @return The {@link WindowAndroid} associated with this {@link TabBase}.
|
| + */
|
| + protected WindowAndroid getWindowAndroid() {
|
| + return mWindowAndroid;
|
| + }
|
| +
|
| + /**
|
| + * @return The current {@link TabBaseChromeWebContentsDelegateAndroid} instance.
|
| + */
|
| + protected TabBaseChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
|
| + return mWebContentsDelegate;
|
| + }
|
| +
|
| + /**
|
| + * @return The native pointer representing the native side of this {@link TabBase} object.
|
| + */
|
| @CalledByNative
|
| - private void destroyBase() {
|
| + protected int getNativePtr() {
|
| + return mNativeTabAndroid;
|
| + }
|
| +
|
| + /** This is currently called when committing a pre-rendered page. */
|
| + @CalledByNative
|
| + private void swapWebContents(final int newWebContents) {
|
| + destroyContentView(false);
|
| + initContentView(newWebContents);
|
| +
|
| + mContentViewCore.onShow();
|
| + for (TabObserver observer : mObservers) observer.onContentChanged(this);
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void clearNativePtr() {
|
| assert mNativeTabAndroid != 0;
|
| mNativeTabAndroid = 0;
|
| }
|
| @@ -41,11 +515,44 @@ public abstract class TabBase {
|
| mNativeTabAndroid = nativePtr;
|
| }
|
|
|
| - int getNativePtr() {
|
| - return mNativeTabAndroid;
|
| + /**
|
| + * Validates {@code id} and increments the internal counter to make sure future ids don't
|
| + * collide.
|
| + * @param id The current id. Maybe {@link #INVALID_TAB_ID}.
|
| + * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
|
| + */
|
| + private static int generateValidId(int id) {
|
| + if (id == INVALID_TAB_ID) id = generateNextId();
|
| + incrementIdCounterTo(id + 1);
|
| +
|
| + return id;
|
| }
|
|
|
| - protected WindowAndroid getWindowAndroid() {
|
| - return mWindowAndroid;
|
| + /**
|
| + * @return An unused id.
|
| + */
|
| + private static int generateNextId() {
|
| + return sIdCounter.getAndIncrement();
|
| }
|
| +
|
| + /**
|
| + * Ensures the counter is at least as high as the specified value. The counter should always
|
| + * point to an unused ID (which will be handed out next time a request comes in). Exposed so
|
| + * that anything externally loading tabs and ids can set enforce new tabs start at the correct
|
| + * id.
|
| + * TODO(aurimas): Investigate reducing the visiblity of this method.
|
| + * @param id The minimum id we should hand out to the next new tab.
|
| + */
|
| + public static void incrementIdCounterTo(int id) {
|
| + int diff = id - sIdCounter.get();
|
| + if (diff <= 0) return;
|
| + // It's possible idCounter has been incremented between the get above and the add below
|
| + // but that's OK, because in the worst case we'll overly increment idCounter.
|
| + sIdCounter.addAndGet(diff);
|
| + }
|
| +
|
| + private native void nativeInitWebContents(int nativeTabAndroid, int id, boolean incognito,
|
| + ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate);
|
| + private native void nativeDestroyWebContents(int nativeTabAndroid, boolean deleteNative);
|
| + private native Profile nativeGetProfileAndroid(int nativeTabAndroid);
|
| }
|
|
|