Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/Tab.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java |
| index 44303f6b1f61093d6f9fcc6d3b5a2bdf47403ed3..1cb236912d7a9b819c60c8e4d2045917002fc33e 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java |
| @@ -24,6 +24,7 @@ import org.chromium.base.VisibleForTesting; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.TabState.WebContentsState; |
| import org.chromium.chrome.browser.banners.AppBannerManager; |
| +import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; |
| import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItemDelegate; |
| import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator; |
| import org.chromium.chrome.browser.contextmenu.ContextMenuParams; |
| @@ -53,6 +54,7 @@ import org.chromium.printing.PrintManagerDelegateImpl; |
| import org.chromium.printing.PrintingController; |
| import org.chromium.printing.PrintingControllerImpl; |
| import org.chromium.ui.base.Clipboard; |
| +import org.chromium.ui.base.LocalizationUtils; |
| import org.chromium.ui.base.PageTransition; |
| import org.chromium.ui.base.WindowAndroid; |
| import org.chromium.ui.gfx.DeviceDisplayInfo; |
| @@ -93,6 +95,7 @@ import java.util.concurrent.atomic.AtomicInteger; |
| */ |
| public class Tab { |
| public static final int INVALID_TAB_ID = -1; |
| + private static final long INVALID_TIMESTAMP = -1; |
| /** Used for logging. */ |
| private static final String TAG = "Tab"; |
| @@ -227,6 +230,22 @@ public class Tab { |
| */ |
| private boolean mIsHidden = true; |
| + /** |
| + * The last time this tab was shown or the time of its initialization if it wasn't yet shown. |
| + */ |
| + private long mTimestampMillis = INVALID_TIMESTAMP; |
| + |
| + /** |
| + * Title of the ContentViews webpage.Always update mTitle through updateTitle() so that it also |
| + * updates mIsTitleDirectionRtl correctly. |
| + */ |
| + private String mTitle; |
| + |
| + /** |
| + * Indicates if mTitle should be displayed from right to left. |
| + */ |
| + private boolean mIsTitleDirectionRtl; |
| + |
| private FullscreenManager mFullscreenManager; |
| private float mPreviousFullscreenTopControlsOffsetY = Float.NaN; |
| private float mPreviousFullscreenContentOffsetY = Float.NaN; |
| @@ -385,6 +404,11 @@ public class Tab { |
| */ |
| protected class TabContentViewClient extends ContentViewClient { |
| @Override |
| + public void onUpdateTitle(String title) { |
| + updateTitle(title); |
| + } |
| + |
| + @Override |
| public void onContextualActionBarShown() { |
| for (TabObserver observer : mObservers) { |
| observer.onContextualActionBarVisibilityChanged(Tab.this, true); |
| @@ -432,6 +456,11 @@ public class Tab { |
| } |
| @Override |
| + public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) { |
| + if (isMainFrame) didFinishPageLoad(); |
| + } |
| + |
| + @Override |
| public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode, |
| String description, String failingUrl) { |
| for (TabObserver observer : mObservers) { |
| @@ -444,6 +473,8 @@ public class Tab { |
| public void didStartProvisionalLoadForFrame(long frameId, long parentFrameId, |
| boolean isMainFrame, String validatedUrl, boolean isErrorPage, |
| boolean isIframeSrcdoc) { |
| + if (isMainFrame) didStartPageLoad(validatedUrl, isErrorPage); |
| + |
| for (TabObserver observer : mObservers) { |
| observer.onDidStartProvisionalLoadForFrame(Tab.this, frameId, parentFrameId, |
| isMainFrame, validatedUrl, isErrorPage, isIframeSrcdoc); |
| @@ -453,15 +484,27 @@ public class Tab { |
| @Override |
| public void didCommitProvisionalLoadForFrame(long frameId, boolean isMainFrame, String url, |
| int transitionType) { |
| + if (isMainFrame) { |
| + mIsTabStateDirty = true; |
| + updateTitle(); |
| + } |
| + |
| for (TabObserver observer : mObservers) { |
| observer.onDidCommitProvisionalLoadForFrame( |
| Tab.this, frameId, isMainFrame, url, transitionType); |
| } |
| + |
| + notifyPageUrlChanged(); |
| } |
| @Override |
| public void didNavigateMainFrame(String url, String baseUrl, |
| boolean isNavigationToDifferentPage, boolean isFragmentNavigation, int statusCode) { |
| + FullscreenManager fullscreenManager = getFullscreenManager(); |
| + if (isNavigationToDifferentPage && fullscreenManager != null) { |
| + fullscreenManager.setPersistentFullscreenMode(false); |
| + } |
| + |
| for (TabObserver observer : mObservers) { |
| observer.onDidNavigateMainFrame( |
| Tab.this, url, baseUrl, isNavigationToDifferentPage, |
| @@ -479,6 +522,9 @@ public class Tab { |
| @Override |
| public void didAttachInterstitialPage() { |
| + getInfoBarContainer().setVisibility(View.INVISIBLE); |
| + showRenderedPage(); |
| + |
| for (TabObserver observer : mObservers) { |
| observer.onDidAttachInterstitialPage(Tab.this); |
| } |
| @@ -486,6 +532,8 @@ public class Tab { |
| @Override |
| public void didDetachInterstitialPage() { |
| + getInfoBarContainer().setVisibility(View.VISIBLE); |
| + |
| for (TabObserver observer : mObservers) { |
| observer.onDidDetachInterstitialPage(Tab.this); |
| } |
| @@ -573,7 +621,12 @@ public class Tab { |
| mFrozenContentsState = state.contentsState; |
| mSyncId = (int) state.syncId; |
| mShouldPreserve = state.shouldPreserve; |
| + mTimestampMillis = state.timestampMillis; |
| mUrl = state.getVirtualUrlFromState(); |
| + |
| + mTitle = state.getDisplayTitleFromState(); |
| + mIsTitleDirectionRtl = LocalizationUtils.getFirstStrongCharacterDirection(mTitle) |
| + == LocalizationUtils.RIGHT_TO_LEFT; |
| } |
| /** |
| @@ -744,6 +797,7 @@ public class Tab { |
| tabState.parentId = mParentId; |
| tabState.shouldPreserve = mShouldPreserve; |
| tabState.syncId = mSyncId; |
| + tabState.timestampMillis = mTimestampMillis; |
| return tabState; |
| } |
| @@ -1011,6 +1065,10 @@ public class Tab { |
| if (mContentViewCore != null) mContentViewCore.onShow(); |
| showInternal(type); |
| + |
| + // Updating the timestamp has to happen after the showInternal() call since subclasses |
| + // may use it for logging. |
| + mTimestampMillis = System.currentTimeMillis(); |
| } finally { |
| TraceEvent.end("Tab.show"); |
| } |
| @@ -1076,6 +1134,8 @@ public class Tab { |
| * Hides the current {@link NativePage}, if any, and shows the {@link ContentViewCore}'s view. |
| */ |
| protected void showRenderedPage() { |
| + updateTitle(); |
| + |
| if (mNativePage == null) return; |
| NativePage previousNativePage = mNativePage; |
| mNativePage = null; |
| @@ -1084,10 +1144,95 @@ public class Tab { |
| } |
| /** |
| - * Initializes this {@link Tab}. |
| + * Initializes the ChromeTab after construction. |
| + * @param nativeWebContents native WebContents object if available or 0 when creating a brand |
| + * new tab |
| + * @param tabContentManager A TabContentManager instance or {@code null} if the layer should not |
| + * be attached to one. |
| + * @param initiallyHidden true iff the tab being initialized is created in background |
| */ |
| - public void initialize() { |
| - initializeNative(); |
| + public void initialize(long nativeWebContents, TabContentManager tabContentManager, |
| + boolean initiallyHidden) { |
| + try { |
| + TraceEvent.begin("Tab.initialize"); |
| + initializeNative(); |
|
Ted C
2015/01/02 18:14:45
this should go in internalInit per offline discuss
gone
2015/01/02 18:23:01
Done.
|
| + internalInit(tabContentManager); |
| + |
| + if (getFrozenContentsState() == null && getPendingLoadParams() == null) { |
| + boolean existingWebContents = nativeWebContents != 0; |
| + // There is no frozen WebContents state or pending lazy load, create new |
| + // WebContents. |
| + if (nativeWebContents == 0) { |
| + nativeWebContents = |
| + ContentViewUtil.createNativeWebContents(isIncognito(), initiallyHidden); |
| + } |
| + initContentViewCore(nativeWebContents); |
| + if (existingWebContents) { |
| + WebContents webContents = getWebContents(); |
| + assert webContents != null; |
| + if (webContents.isLoadingToDifferentDocument()) { |
| + didStartPageLoad(webContents.getUrl(), false); |
| + } |
| + } |
| + } |
| + if (mTimestampMillis == INVALID_TIMESTAMP) { |
| + mTimestampMillis = System.currentTimeMillis(); |
| + } |
| + } finally { |
| + TraceEvent.end("Tab.initialize"); |
| + } |
| + } |
| + |
|
Ted C
2015/01/02 18:14:45
Looks like an extra blank line here
gone
2015/01/02 18:23:01
Done.
|
| + |
| + /** |
| + * Handles common post-construction initialization. |
| + * @param contentViewCore ContentViewCore object to attach to the tab. |
| + * @param tabContentManager A TabContentManager instance or {@code null} if the layer should not |
| + * be attached to one. |
| + */ |
| + public void initialize(ContentViewCore contentViewCore, TabContentManager tabContentManager) { |
| + try { |
| + TraceEvent.begin("Tab.initialize"); |
| + internalInit(tabContentManager); |
| + setContentViewCore(contentViewCore); |
| + if (mTimestampMillis == INVALID_TIMESTAMP) { |
| + mTimestampMillis = System.currentTimeMillis(); |
| + } |
| + } finally { |
| + TraceEvent.end("Tab.initialize"); |
| + } |
| + } |
| + |
| + /** |
| + * Perform any subclass-specific initialization tasks. |
| + */ |
| + protected void internalInit(TabContentManager tabContentManager) { |
| + } |
| + |
| + /** |
| + * Called when a page has started loading. |
| + * @param validatedUrl URL being loaded. |
| + * @param showingErrorPage Whether an error page is being shown. |
| + */ |
| + protected void didStartPageLoad(String validatedUrl, boolean showingErrorPage) { |
| + updateTitle(); |
| + removeSadTabIfPresent(); |
| + mInfoBarContainer.onPageStarted(); |
| + |
| + if (getContentViewCore() != null) { |
| + getContentViewCore().stopCurrentAccessibilityNotifications(); |
| + } |
| + |
| + clearHungRendererState(); |
| + } |
| + |
| + /** |
| + * Called when a page has finished loading. |
| + */ |
| + protected void didFinishPageLoad() { |
| + mIsTabStateDirty = true; |
| + updateTitle(); |
| + updateFullscreenEnabledState(); |
| } |
| /** |
| @@ -1095,7 +1240,7 @@ public class Tab { |
| * subclass native counterparts instead. Subclasses should not call this via super and instead |
| * rely on the native class to create the JNI association. |
| */ |
| - protected void initializeNative() { |
| + public void initializeNative() { |
|
Ted C
2015/01/02 18:14:45
make protected per offline discussion
gone
2015/01/02 18:23:01
Added TODO since DocumentTabModelImpl needs access
|
| if (mNativeTabAndroid == 0) nativeInit(); |
| assert mNativeTabAndroid != 0; |
| } |
| @@ -1235,6 +1380,9 @@ public class Tab { |
| * this method is called. Once this call is made this {@link Tab} should no longer be used. |
| */ |
| public void destroy() { |
| + // Update the title before destroying the tab. http://b/5783092 |
| + updateTitle(); |
| + |
| for (TabObserver observer : mObservers) observer.onDestroyed(this); |
| mObservers.clear(); |
| @@ -1290,9 +1438,67 @@ public class Tab { |
| */ |
| @CalledByNative |
| public String getTitle() { |
| - if (mNativePage != null) return mNativePage.getTitle(); |
| - if (getWebContents() != null) return getWebContents().getTitle(); |
| - return ""; |
| + if (mTitle == null) updateTitle(); |
| + return mTitle; |
| + } |
| + |
| + private void updateTitle() { |
| + if (isFrozen()) return; |
| + |
| + // When restoring the tabs, the title will no longer be populated, so request it from the |
| + // ContentViewCore or NativePage (if present). |
| + String title = ""; |
| + if (mNativePage != null) title = mNativePage.getTitle(); |
| + if (getWebContents() != null) title = getWebContents().getTitle(); |
| + updateTitle(title); |
| + } |
| + |
| + /** |
| + * Cache the title for the current page. |
| + * |
| + * {@link ContentViewClient#onUpdateTitle} is unreliable, particularly for navigating backwards |
| + * and forwards in the history stack, so pull the correct title whenever the page changes. |
| + * onUpdateTitle is only called when the title of a navigation entry changes. When the user goes |
| + * back a page the navigation entry exists with the correct title, thus the title is not |
| + * actually changed, and no notification is sent. |
| + * @param title Title of the page. |
| + */ |
| + private void updateTitle(String title) { |
| + if (TextUtils.equals(mTitle, title)) return; |
| + |
| + mIsTabStateDirty = true; |
| + mTitle = title; |
| + mIsTitleDirectionRtl = LocalizationUtils.getFirstStrongCharacterDirection(title) |
| + == LocalizationUtils.RIGHT_TO_LEFT; |
| + notifyPageTitleChanged(); |
| + } |
| + |
| + protected void notifyPageTitleChanged() { |
| + RewindableIterator<TabObserver> observers = getTabObservers(); |
| + while (observers.hasNext()) { |
| + observers.next().onTitleUpdated(this); |
| + } |
| + } |
| + |
| + protected void notifyFaviconChanged() { |
| + RewindableIterator<TabObserver> observers = getTabObservers(); |
| + while (observers.hasNext()) { |
| + observers.next().onFaviconUpdated(this); |
| + } |
| + } |
| + |
| + private void notifyPageUrlChanged() { |
| + RewindableIterator<TabObserver> observers = getTabObservers(); |
| + while (observers.hasNext()) { |
| + observers.next().onUrlUpdated(this); |
| + } |
| + } |
| + |
| + /** |
| + * @return True if the tab title should be displayed from right to left. |
| + */ |
| + public boolean isTitleDirectionRtl() { |
| + return mIsTitleDirectionRtl; |
| } |
| /** |
| @@ -1493,10 +1699,15 @@ public class Tab { |
| mInfoBarContainer.removeFromParentView(); |
| } |
| mContentViewCore.destroy(); |
| - |
| mContentViewCore = null; |
| + |
| mWebContentsDelegate = null; |
| - mWebContentsObserver = null; |
| + |
| + if (mWebContentsObserver != null) { |
| + mWebContentsObserver.detachFromWebContents(); |
| + mWebContentsObserver = null; |
| + } |
| + |
| mVoiceSearchTabHelper = null; |
| assert mNativeTabAndroid != 0; |
| @@ -1731,6 +1942,13 @@ public class Tab { |
| } |
| /** |
| + * @return See {@link #mTimestampMillis}. |
| + */ |
| + protected long getTimestampMillis() { |
| + return mTimestampMillis; |
| + } |
| + |
| + /** |
| * 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}. |