1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.chrome.browser;
7 import android.app.Activity;
8 import android.content.Context;
9 import android.graphics.Bitmap;
10 import android.graphics.Color;
11 import android.view.ContextMenu;
12 import android.view.View;
14 import org.chromium.base.CalledByNative;
15 import org.chromium.base.ObserverList;
16 import org.chromium.base.TraceEvent;
17 import org.chromium.chrome.browser.banners.AppBannerManager;
18 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItemDelegate;
19 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator;
20 import org.chromium.chrome.browser.contextmenu.ContextMenuParams;
21 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
22 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulatorWrapper;
23 import org.chromium.chrome.browser.contextmenu.EmptyChromeContextMenuItemDelegate;
24 import org.chromium.chrome.browser.dom_distiller.FeedbackReporter;
25 import org.chromium.chrome.browser.infobar.AutoLoginProcessor;
26 import org.chromium.chrome.browser.infobar.InfoBarContainer;
27 import org.chromium.chrome.browser.profiles.Profile;
28 import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel;
29 import org.chromium.content.browser.ContentView;
30 import org.chromium.content.browser.ContentViewClient;
31 import org.chromium.content.browser.ContentViewCore;
32 import org.chromium.content.browser.LoadUrlParams;
33 import org.chromium.content.browser.NavigationClient;
34 import org.chromium.content.browser.NavigationHistory;
35 import org.chromium.content.browser.PageInfo;
36 import org.chromium.content.browser.WebContentsObserverAndroid;
37 import org.chromium.content_public.browser.WebContents;
38 import org.chromium.ui.base.Clipboard;
39 import org.chromium.ui.base.WindowAndroid;
41 import java.util.concurrent.atomic.AtomicInteger;
44 * The basic Java representation of a tab. Contains and manages a {@link ContentView}.
46 * Tab provides common functionality for ChromeShell Tab as well as Chrome on Android's
47 * tab. It is intended to be extended either on Java or both Java and C++, with ownership managed
50 * Extending just Java:
51 * - Just extend the class normally. Do not override initializeNative().
52 * Extending Java and C++:
53 * - Because of the inner-workings of JNI, the subclass is responsible for constructing the native
54 * subclass, which in turn constructs TabAndroid (the native counterpart to Tab), which in
55 * turn sets the native pointer for Tab. For destruction, subclasses in Java must clear
56 * their own native pointer reference, but Tab#destroy() will handle deleting the native
59 * Notes on {@link Tab#getId()}:
61 * Tabs are all generated using a static {@link AtomicInteger} which means they are unique across
62 * all {@link Activity}s running in the same {@link android.app.Application} process. Calling
63 * {@link Tab#incrementIdCounterTo(int)} will ensure new {@link Tab}s get ids greater than or equal
64 * to the parameter passed to that method. This should be used when doing things like loading
65 * persisted {@link Tab}s from disk on process start to ensure all new {@link Tab}s don't have id
67 * Some {@link Activity}s will not call this because they do not persist state, which means those
68 * ids can potentially conflict with the ones restored from persisted state depending on which
69 * {@link Activity} runs first on process start. If {@link Tab}s are ever shared across
70 * {@link Activity}s or mixed with {@link Tab}s from other {@link Activity}s conflicts can occur
71 * unless special care is taken to make sure {@link Tab#incrementIdCounterTo(int)} is called with
72 * the correct value across all affected {@link Activity}s.
74 public class Tab implements NavigationClient {
75 public static final int INVALID_TAB_ID = -1;
77 /** Used for automatically generating tab ids. */
78 private static final AtomicInteger sIdCounter = new AtomicInteger();
80 private long mNativeTabAndroid;
82 /** Unique id of this tab (within its container). */
83 private final int mId;
85 /** Whether or not this tab is an incognito tab. */
86 private final boolean mIncognito;
88 /** An Application {@link Context}. Unlike {@link #mContext}, this is the only one that is
89 * publicly exposed to help prevent leaking the {@link Activity}. */
90 private final Context mApplicationContext;
92 /** The {@link Context} used to create {@link View}s and other Android components. Unlike
93 * {@link #mApplicationContext}, this is not publicly exposed to help prevent leaking the
94 * {@link Activity}. */
95 private final Context mContext;
97 /** Gives {@link Tab} a way to interact with the Android window. */
98 private final WindowAndroid mWindowAndroid;
100 /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */
101 private NativePage mNativePage;
103 /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */
104 private ContentView mContentView;
106 /** InfoBar container to show InfoBars for this tab. */
107 private InfoBarContainer mInfoBarContainer;
109 /** Manages app banners shown for this tab. */
110 private AppBannerManager mAppBannerManager;
112 /** The sync id of the Tab if session sync is enabled. */
116 * The {@link ContentViewCore} for the current page, provided for convenience. This always
117 * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is
120 private ContentViewCore mContentViewCore;
123 * A list of Tab observers. These are used to broadcast Tab events to listeners.
125 private final ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
127 // Content layer Observers and Delegates
128 private ContentViewClient mContentViewClient;
129 private WebContentsObserverAndroid mWebContentsObserver;
130 private VoiceSearchTabHelper mVoiceSearchTabHelper;
131 private TabChromeWebContentsDelegateAndroid mWebContentsDelegate;
132 private FeedbackReporter mFeedbackReporter;
135 * If this tab was opened from another tab, store the id of the tab that
136 * caused it to be opened so that we can activate it when this tab gets
139 private int mParentId = INVALID_TAB_ID;
142 * Whether the tab should be grouped with its parent tab.
144 private boolean mGroupedWithParent = true;
147 * A default {@link ChromeContextMenuItemDelegate} that supports some of the context menu
150 protected class TabChromeContextMenuItemDelegate
151 extends EmptyChromeContextMenuItemDelegate {
152 private final Clipboard mClipboard;
155 * Builds a {@link TabChromeContextMenuItemDelegate} instance.
157 public TabChromeContextMenuItemDelegate() {
158 mClipboard = new Clipboard(getApplicationContext());
162 public boolean isIncognito() {
167 public void onSaveToClipboard(String text, boolean isUrl) {
168 mClipboard.setText(text, text);
172 public void onSaveImageToClipboard(String url) {
173 mClipboard.setHTMLText("<img src=\"" + url + "\">", url, url);
178 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
179 * {@link TabObserver}s. Meant to be overridden by subclasses.
181 public class TabChromeWebContentsDelegateAndroid
182 extends ChromeWebContentsDelegateAndroid {
184 public void onLoadProgressChanged(int progress) {
185 for (TabObserver observer : mObservers) {
186 observer.onLoadProgressChanged(Tab.this, progress);
191 public void onLoadStarted() {
192 for (TabObserver observer : mObservers) observer.onLoadStarted(Tab.this);
196 public void onLoadStopped() {
197 for (TabObserver observer : mObservers) observer.onLoadStopped(Tab.this);
201 public void onUpdateUrl(String url) {
202 for (TabObserver observer : mObservers) observer.onUpdateUrl(Tab.this, url);
206 public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) {
207 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(
211 contentViewCore.cancelPendingReload();
216 contentViewCore.continuePendingReload();
219 Activity activity = (Activity) mContext;
220 warningDialog.show(activity.getFragmentManager(), null);
224 public void toggleFullscreenModeForTab(boolean enableFullscreen) {
225 for (TabObserver observer : mObservers) {
226 observer.onToggleFullscreenMode(Tab.this, enableFullscreen);
231 public void navigationStateChanged(int flags) {
232 if ((flags & INVALIDATE_TYPE_TITLE) != 0) {
233 for (TabObserver observer : mObservers) observer.onTitleUpdated(Tab.this);
235 if ((flags & INVALIDATE_TYPE_URL) != 0) {
236 for (TabObserver observer : mObservers) observer.onUrlUpdated(Tab.this);
241 private class TabContextMenuPopulator extends ContextMenuPopulatorWrapper {
242 public TabContextMenuPopulator(ContextMenuPopulator populator) {
247 public void buildContextMenu(ContextMenu menu, Context context, ContextMenuParams params) {
248 super.buildContextMenu(menu, context, params);
249 for (TabObserver observer : mObservers) observer.onContextMenuShown(Tab.this, menu);
253 private class TabWebContentsObserverAndroid extends WebContentsObserverAndroid {
254 public TabWebContentsObserverAndroid(ContentViewCore contentViewCore) {
255 super(contentViewCore);
259 public void navigationEntryCommitted() {
260 if (getNativePage() != null) {
261 pushNativePageStateToNavigationEntry();
266 public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
267 String description, String failingUrl) {
268 for (TabObserver observer : mObservers) {
269 observer.onDidFailLoad(Tab.this, isProvisionalLoad, isMainFrame, errorCode,
270 description, failingUrl);
275 public void didStartProvisionalLoadForFrame(long frameId, long parentFrameId,
276 boolean isMainFrame, String validatedUrl, boolean isErrorPage,
277 boolean isIframeSrcdoc) {
278 for (TabObserver observer : mObservers) {
279 observer.onDidStartProvisionalLoadForFrame(Tab.this, frameId, parentFrameId,
280 isMainFrame, validatedUrl, isErrorPage, isIframeSrcdoc);
286 * Creates an instance of a {@link Tab} with no id.
287 * @param incognito Whether or not this tab is incognito.
288 * @param context An instance of a {@link Context}.
289 * @param window An instance of a {@link WindowAndroid}.
291 public Tab(boolean incognito, Context context, WindowAndroid window) {
292 this(INVALID_TAB_ID, incognito, context, window);
296 * Creates an instance of a {@link Tab}.
297 * @param id The id this tab should be identified with.
298 * @param incognito Whether or not this tab is incognito.
299 * @param context An instance of a {@link Context}.
300 * @param window An instance of a {@link WindowAndroid}.
302 public Tab(int id, boolean incognito, Context context, WindowAndroid window) {
303 this(INVALID_TAB_ID, id, incognito, context, window);
307 * Creates an instance of a {@link Tab}.
308 * @param id The id this tab should be identified with.
309 * @param parentId The id id of the tab that caused this tab to be opened.
310 * @param incognito Whether or not this tab is incognito.
311 * @param context An instance of a {@link Context}.
312 * @param window An instance of a {@link WindowAndroid}.
314 public Tab(int id, int parentId, boolean incognito, Context context, WindowAndroid window) {
315 // We need a valid Activity Context to build the ContentView with.
316 assert context == null || context instanceof Activity;
318 mId = generateValidId(id);
319 mParentId = parentId;
320 mIncognito = incognito;
321 // TODO(dtrainor): Only store application context here.
323 mApplicationContext = context != null ? context.getApplicationContext() : null;
324 mWindowAndroid = window;
328 * Adds a {@link TabObserver} to be notified on {@link Tab} changes.
329 * @param observer The {@link TabObserver} to add.
331 public final void addObserver(TabObserver observer) {
332 mObservers.addObserver(observer);
336 * Removes a {@link TabObserver}.
337 * @param observer The {@link TabObserver} to remove.
339 public final void removeObserver(TabObserver observer) {
340 mObservers.removeObserver(observer);
344 * @return Whether or not this tab has a previous navigation entry.
346 public boolean canGoBack() {
347 return mContentViewCore != null && mContentViewCore.canGoBack();
351 * @return Whether or not this tab has a navigation entry after the current one.
353 public boolean canGoForward() {
354 return mContentViewCore != null && mContentViewCore.canGoForward();
358 * Goes to the navigation entry before the current one.
360 public void goBack() {
361 if (mContentViewCore != null) mContentViewCore.goBack();
365 * Goes to the navigation entry after the current one.
367 public void goForward() {
368 if (mContentViewCore != null) mContentViewCore.goForward();
372 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
373 if (mContentViewCore != null) {
374 return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
376 return new NavigationHistory();
381 public void goToNavigationIndex(int index) {
382 if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
386 * Loads the current navigation if there is a pending lazy load (after tab restore).
388 public void loadIfNecessary() {
389 if (mContentViewCore != null) mContentViewCore.loadIfNecessary();
393 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
395 protected void requestRestoreLoad() {
396 if (mContentViewCore != null) mContentViewCore.requestRestoreLoad();
400 * Causes this tab to navigate to the specified URL.
401 * @param params parameters describing the url load. Note that it is important to set correct
402 * page transition as it is used for ranking URLs in the history so the omnibox
403 * can report suggestions correctly.
404 * @return FULL_PRERENDERED_PAGE_LOAD or PARTIAL_PRERENDERED_PAGE_LOAD if the page has been
405 * prerendered. DEFAULT_PAGE_LOAD if it had not.
407 public int loadUrl(LoadUrlParams params) {
410 // We load the URL from the tab rather than directly from the ContentView so the tab has a
411 // chance of using a prerenderer page is any.
412 int loadType = nativeLoadUrl(
415 params.getVerbatimHeaders(),
416 params.getPostData(),
417 params.getTransitionType(),
418 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
419 // Policy will be ignored for null referrer url, 0 is just a placeholder.
420 // TODO(ppi): Should we pass Referrer jobject and add JNI methods to read it from
422 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0);
426 for (TabObserver observer : mObservers) {
427 observer.onLoadUrl(this, params.getUrl(), loadType);
433 * @return Whether or not the {@link Tab} is currently showing an interstitial page, such as
436 public boolean isShowingInterstitialPage() {
437 ContentViewCore contentViewCore = getContentViewCore();
438 return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
442 * @return Whether or not the tab has something valid to render.
444 public boolean isReady() {
445 return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
449 * @return The {@link View} displaying the current page in the tab. This might be a
450 * {@link ContentView} but could potentially be any instance of {@link View}. This can
451 * be {@code null}, if the tab is frozen or being initialized or destroyed.
453 public View getView() {
454 PageInfo pageInfo = getPageInfo();
455 return pageInfo != null ? pageInfo.getView() : null;
459 * @return The width of the content of this tab. Can be 0 if there is no content.
461 public int getWidth() {
462 View view = getView();
463 return view != null ? view.getWidth() : 0;
467 * @return The height of the content of this tab. Can be 0 if there is no content.
469 public int getHeight() {
470 View view = getView();
471 return view != null ? view.getHeight() : 0;
475 * @return The application {@link Context} associated with this tab.
477 protected Context getApplicationContext() {
478 return mApplicationContext;
482 * @return The infobar container.
484 public final InfoBarContainer getInfoBarContainer() {
485 return mInfoBarContainer;
489 * Create an {@code AutoLoginProcessor} to decide how to handle login
492 protected AutoLoginProcessor createAutoLoginProcessor() {
493 return new AutoLoginProcessor() {
495 public void processAutoLoginResult(String accountName, String authToken,
496 boolean success, String result) {
502 * Prints the current page.
504 * @return Whether the printing process is started successfully.
506 public boolean print() {
507 assert mNativeTabAndroid != 0;
508 return nativePrint(mNativeTabAndroid);
512 * Reloads the current page content if it is a {@link ContentView}.
514 public void reload() {
515 // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
516 if (mContentViewCore != null) mContentViewCore.reload(true);
520 * Reloads the current page content if it is a {@link ContentView}.
521 * This version ignores the cache and reloads from the network.
523 public void reloadIgnoringCache() {
524 if (mContentViewCore != null) mContentViewCore.reloadIgnoringCache(true);
527 /** Stop the current navigation. */
528 public void stopLoading() {
529 if (mContentViewCore != null) mContentViewCore.stopLoading();
533 * @return The background color of the tab.
535 public int getBackgroundColor() {
536 return getPageInfo() != null ? getPageInfo().getBackgroundColor() : Color.WHITE;
540 * @return The web contents associated with this tab.
542 public WebContents getWebContents() {
543 if (mNativeTabAndroid == 0) return null;
544 return nativeGetWebContents(mNativeTabAndroid);
548 * @return The profile associated with this tab.
550 public Profile getProfile() {
551 if (mNativeTabAndroid == 0) return null;
552 return nativeGetProfileAndroid(mNativeTabAndroid);
556 * For more information about the uniqueness of {@link #getId()} see comments on {@link Tab}.
558 * @return The id representing this tab.
566 * @return Whether or not this tab is incognito.
568 public boolean isIncognito() {
573 * @return The {@link ContentView} associated with the current page, or {@code null} if
574 * there is no current page or the current page is displayed using something besides a
575 * {@link ContentView}.
577 public ContentView getContentView() {
578 return mNativePage == null ? mContentView : null;
582 * @return The {@link ContentViewCore} associated with the current page, or {@code null} if
583 * there is no current page or the current page is displayed using something besides a
584 * {@link ContentView}.
586 public ContentViewCore getContentViewCore() {
587 return mNativePage == null ? mContentViewCore : null;
591 * @return A {@link PageInfo} describing the current page. This is always not {@code null}
592 * except during initialization, destruction, and when the tab is frozen.
594 public PageInfo getPageInfo() {
595 return mNativePage != null ? mNativePage : mContentView;
599 * @return The {@link NativePage} associated with the current page, or {@code null} if there is
600 * no current page or the current page is displayed using something besides
601 * {@link NativePage}.
603 public NativePage getNativePage() {
608 * @return Whether or not the {@link Tab} represents a {@link NativePage}.
610 public boolean isNativePage() {
611 return mNativePage != null;
615 * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
616 * currently loaded page.
617 * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one.
618 * @param reloadOnChange Reload the page if the user agent has changed.
620 public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
621 if (mContentViewCore != null) {
622 mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
627 * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
629 public boolean getUseDesktopUserAgent() {
630 return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
634 * @return The current {ToolbarModelSecurityLevel} for the tab.
636 public int getSecurityLevel() {
637 if (mNativeTabAndroid == 0) return ToolbarModelSecurityLevel.NONE;
638 return nativeGetSecurityLevel(mNativeTabAndroid);
642 * @return The sync id of the tab if session sync is enabled, {@code 0} otherwise.
645 protected int getSyncId() {
650 * @param syncId The sync id of the tab if session sync is enabled.
653 protected void setSyncId(int syncId) {
658 * @return An {@link ObserverList.RewindableIterator} instance that points to all of
659 * the current {@link TabObserver}s on this class. Note that calling
660 * {@link java.util.Iterator#remove()} will throw an
661 * {@link UnsupportedOperationException}.
663 protected ObserverList.RewindableIterator<TabObserver> getTabObservers() {
664 return mObservers.rewindableIterator();
668 * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
669 * associated with the current page. There can still be a {@link ContentViewClient}
670 * even when there is no {@link ContentViewCore}.
672 protected ContentViewClient getContentViewClient() {
673 return mContentViewClient;
677 * @param client The {@link ContentViewClient} to be bound to any current or new
678 * {@link ContentViewCore}s associated with this {@link Tab}.
680 protected void setContentViewClient(ContentViewClient client) {
681 if (mContentViewClient == client) return;
683 ContentViewClient oldClient = mContentViewClient;
684 mContentViewClient = client;
686 if (mContentViewCore == null) return;
688 if (mContentViewClient != null) {
689 mContentViewCore.setContentViewClient(mContentViewClient);
690 } else if (oldClient != null) {
691 // We can't set a null client, but we should clear references to the last one.
692 mContentViewCore.setContentViewClient(new ContentViewClient());
697 * Triggers the showing logic for the view backing this tab.
699 protected void show() {
700 if (mContentViewCore != null) mContentViewCore.onShow();
704 * Triggers the hiding logic for the view backing the tab.
706 protected void hide() {
707 if (mContentViewCore != null) mContentViewCore.onHide();
711 * Shows the given {@code nativePage} if it's not already showing.
712 * @param nativePage The {@link NativePage} to show.
714 protected void showNativePage(NativePage nativePage) {
715 if (mNativePage == nativePage) return;
716 NativePage previousNativePage = mNativePage;
717 mNativePage = nativePage;
718 pushNativePageStateToNavigationEntry();
719 for (TabObserver observer : mObservers) observer.onContentChanged(this);
720 destroyNativePageInternal(previousNativePage);
724 * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}.
726 protected void showRenderedPage() {
727 if (mNativePage == null) return;
728 NativePage previousNativePage = mNativePage;
730 for (TabObserver observer : mObservers) observer.onContentChanged(this);
731 destroyNativePageInternal(previousNativePage);
735 * Initializes this {@link Tab}.
737 public void initialize() {
742 * Builds the native counterpart to this class. Meant to be overridden by subclasses to build
743 * subclass native counterparts instead. Subclasses should not call this via super and instead
744 * rely on the native class to create the JNI association.
746 protected void initializeNative() {
747 if (mNativeTabAndroid == 0) nativeInit();
748 assert mNativeTabAndroid != 0;
752 * A helper method to initialize a {@link ContentView} without any native WebContents pointer.
754 protected final void initContentView() {
755 initContentView(ContentViewUtil.createNativeWebContents(mIncognito));
759 * Creates and initializes the {@link ContentView}.
761 * @param nativeWebContents The native web contents pointer.
763 protected void initContentView(long nativeWebContents) {
764 setContentView(ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid()));
768 * Completes the {@link ContentView} specific initialization around a native WebContents
769 * pointer. {@link #getPageInfo()} will still return the {@link NativePage} if there is one.
770 * All initialization that needs to reoccur after a web contents swap should be added here.
772 * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
773 * state as this tab this call will fail.
775 * @param view The content view that needs to be set as active view for the tab.
777 protected void setContentView(ContentView view) {
778 NativePage previousNativePage = mNativePage;
780 destroyNativePageInternal(previousNativePage);
784 mContentViewCore = mContentView.getContentViewCore();
785 mWebContentsDelegate = createWebContentsDelegate();
786 mWebContentsObserver = new TabWebContentsObserverAndroid(mContentViewCore);
787 mVoiceSearchTabHelper = new VoiceSearchTabHelper(mContentViewCore);
789 if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
791 assert mNativeTabAndroid != 0;
792 nativeInitWebContents(
793 mNativeTabAndroid, mIncognito, mContentViewCore, mWebContentsDelegate,
794 new TabContextMenuPopulator(createContextMenuPopulator()));
796 // In the case where restoring a Tab or showing a prerendered one we already have a
797 // valid infobar container, no need to recreate one.
798 if (mInfoBarContainer == null) {
799 // The InfoBarContainer needs to be created after the ContentView has been natively
801 WebContents webContents = view.getContentViewCore().getWebContents();
802 mInfoBarContainer = new InfoBarContainer(
803 (Activity) mContext, createAutoLoginProcessor(), getId(), getContentView(),
806 mInfoBarContainer.onParentViewChanged(getId(), getContentView());
809 if (AppBannerManager.isEnabled() && mAppBannerManager == null) {
810 mAppBannerManager = new AppBannerManager(this);
813 if (FeedbackReporter.isEnabled() && mFeedbackReporter == null) {
814 mFeedbackReporter = new FeedbackReporter(this);
817 for (TabObserver observer : mObservers) observer.onContentChanged(this);
821 * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView}
822 * currently associated with this {@link Tab}. This also destroys the native counterpart
823 * to this class, which means that all subclasses should erase their native pointers after
824 * this method is called. Once this call is made this {@link Tab} should no longer be used.
826 public void destroy() {
827 for (TabObserver observer : mObservers) observer.onDestroyed(this);
830 NativePage currentNativePage = mNativePage;
832 destroyNativePageInternal(currentNativePage);
833 destroyContentView(true);
835 // Destroys the native tab after destroying the ContentView but before destroying the
836 // InfoBarContainer. The native tab should be destroyed before the infobar container as
837 // destroying the native tab cleanups up any remaining infobars. The infobar container
838 // expects all infobars to be cleaned up before its own destruction.
839 assert mNativeTabAndroid != 0;
840 nativeDestroy(mNativeTabAndroid);
841 assert mNativeTabAndroid == 0;
843 if (mInfoBarContainer != null) {
844 mInfoBarContainer.destroy();
845 mInfoBarContainer = null;
850 * @return Whether or not this Tab has a live native component.
852 public boolean isInitialized() {
853 return mNativeTabAndroid != 0;
857 * @return The url associated with the tab.
860 public String getUrl() {
861 return mContentView != null ? mContentView.getUrl() : "";
865 * @return The tab title.
868 public String getTitle() {
869 return getPageInfo() != null ? getPageInfo().getTitle() : "";
873 * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
874 * is specified or it requires the default favicon.
875 * TODO(bauerb): Upstream implementation.
877 public Bitmap getFavicon() {
882 * Restores the tab if it is frozen or crashed.
883 * @return true iff tab restore was triggered.
886 public boolean restoreIfNeeded() {
891 * @return The id of the tab that caused this tab to be opened.
893 public int getParentId() {
898 * @return Whether the tab should be grouped with its parent tab (true by default).
900 public boolean isGroupedWithParent() {
901 return mGroupedWithParent;
905 * Sets whether the tab should be grouped with its parent tab.
907 * @param groupedWithParent The new value.
908 * @see #isGroupedWithParent
910 public void setGroupedWithParent(boolean groupedWithParent) {
911 mGroupedWithParent = groupedWithParent;
914 private void destroyNativePageInternal(NativePage nativePage) {
915 if (nativePage == null) return;
916 assert getPageInfo() != nativePage : "Attempting to destroy active page.";
918 nativePage.destroy();
922 * Destroys the current {@link ContentView}.
923 * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
925 protected final void destroyContentView(boolean deleteNativeWebContents) {
926 if (mContentView == null) return;
928 destroyContentViewInternal(mContentView);
930 if (mInfoBarContainer != null && mInfoBarContainer.getParent() != null) {
931 mInfoBarContainer.removeFromParentView();
933 if (mContentViewCore != null) mContentViewCore.destroy();
936 mContentViewCore = null;
937 mWebContentsDelegate = null;
938 mWebContentsObserver = null;
939 mVoiceSearchTabHelper = null;
941 assert mNativeTabAndroid != 0;
942 nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
946 * Gives subclasses the chance to clean up some state associated with this {@link ContentView}.
947 * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage}
949 * @param contentView The {@link ContentView} that should have associated state cleaned up.
951 protected void destroyContentViewInternal(ContentView contentView) {
955 * A helper method to allow subclasses to build their own delegate.
956 * @return An instance of a {@link TabChromeWebContentsDelegateAndroid}.
958 protected TabChromeWebContentsDelegateAndroid createWebContentsDelegate() {
959 return new TabChromeWebContentsDelegateAndroid();
963 * A helper method to allow subclasses to build their own menu populator.
964 * @return An instance of a {@link ContextMenuPopulator}.
966 protected ContextMenuPopulator createContextMenuPopulator() {
967 return new ChromeContextMenuPopulator(new TabChromeContextMenuItemDelegate());
971 * @return The {@link WindowAndroid} associated with this {@link Tab}.
973 public WindowAndroid getWindowAndroid() {
974 return mWindowAndroid;
978 * @return The current {@link TabChromeWebContentsDelegateAndroid} instance.
980 protected TabChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
981 return mWebContentsDelegate;
985 * Called when the favicon of the content this tab represents changes.
988 protected void onFaviconUpdated() {
989 for (TabObserver observer : mObservers) observer.onFaviconUpdated(this);
993 * Called when the navigation entry containing the historyitem changed,
994 * for example because of a scroll offset or form field change.
997 protected void onNavEntryChanged() {
1001 * @return The native pointer representing the native side of this {@link Tab} object.
1004 protected long getNativePtr() {
1005 return mNativeTabAndroid;
1008 /** This is currently called when committing a pre-rendered page. */
1010 private void swapWebContents(
1011 final long newWebContents, boolean didStartLoad, boolean didFinishLoad) {
1012 swapContentView(ContentView.newInstance(mContext, newWebContents, getWindowAndroid()),
1013 false, didStartLoad, didFinishLoad);
1017 * Called to swap out the current view with the one passed in.
1018 * @param view The content view that should be swapped into the tab.
1019 * @param deleteOldNativeWebContents Whether to delete the native web contents of old view.
1020 * @param didStartLoad Whether WebContentsObserver::DidStartProvisionalLoadForFrame() has
1021 * already been called.
1022 * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has already been called.
1024 protected void swapContentView(ContentView view, boolean deleteOldNativeWebContents,
1025 boolean didStartLoad, boolean didFinishLoad) {
1026 int originalWidth = 0;
1027 int originalHeight = 0;
1028 if (mContentViewCore != null) {
1029 originalWidth = mContentViewCore.getViewportWidthPix();
1030 originalHeight = mContentViewCore.getViewportHeightPix();
1031 mContentViewCore.onHide();
1033 destroyContentView(deleteOldNativeWebContents);
1034 NativePage previousNativePage = mNativePage;
1036 setContentView(view);
1037 // Size of the new ContentViewCore is zero at this point. If we don't call onSizeChanged(),
1038 // next onShow() call would send a resize message with the current ContentViewCore size
1039 // (zero) to the renderer process, although the new size will be set soon.
1040 // However, this size fluttering may confuse Blink and rendered result can be broken
1041 // (see http://crbug.com/340987).
1042 mContentViewCore.onSizeChanged(originalWidth, originalHeight, 0, 0);
1043 mContentViewCore.onShow();
1044 mContentViewCore.attachImeAdapter();
1045 for (TabObserver observer : mObservers) observer.onContentChanged(this);
1046 destroyNativePageInternal(previousNativePage);
1047 for (TabObserver observer : mObservers) {
1048 observer.onWebContentsSwapped(this, didStartLoad, didFinishLoad);
1053 private void clearNativePtr() {
1054 assert mNativeTabAndroid != 0;
1055 mNativeTabAndroid = 0;
1059 private void setNativePtr(long nativePtr) {
1060 assert mNativeTabAndroid == 0;
1061 mNativeTabAndroid = nativePtr;
1065 private long getNativeInfoBarContainer() {
1066 return getInfoBarContainer().getNative();
1070 * Validates {@code id} and increments the internal counter to make sure future ids don't
1072 * @param id The current id. Maybe {@link #INVALID_TAB_ID}.
1073 * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
1075 private static int generateValidId(int id) {
1076 if (id == INVALID_TAB_ID) id = generateNextId();
1077 incrementIdCounterTo(id + 1);
1083 * @return An unused id.
1085 private static int generateNextId() {
1086 return sIdCounter.getAndIncrement();
1089 private void pushNativePageStateToNavigationEntry() {
1090 assert mNativeTabAndroid != 0 && getNativePage() != null;
1091 nativeSetActiveNavigationEntryTitleForUrl(mNativeTabAndroid, getNativePage().getUrl(),
1092 getNativePage().getTitle());
1096 * Ensures the counter is at least as high as the specified value. The counter should always
1097 * point to an unused ID (which will be handed out next time a request comes in). Exposed so
1098 * that anything externally loading tabs and ids can set enforce new tabs start at the correct
1100 * TODO(aurimas): Investigate reducing the visiblity of this method.
1101 * @param id The minimum id we should hand out to the next new tab.
1103 public static void incrementIdCounterTo(int id) {
1104 int diff = id - sIdCounter.get();
1105 if (diff <= 0) return;
1106 // It's possible idCounter has been incremented between the get above and the add below
1107 // but that's OK, because in the worst case we'll overly increment idCounter.
1108 sIdCounter.addAndGet(diff);
1111 private native void nativeInit();
1112 private native void nativeDestroy(long nativeTabAndroid);
1113 private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
1114 ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate,
1115 ContextMenuPopulator contextMenuPopulator);
1116 private native void nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative);
1117 private native WebContents nativeGetWebContents(long nativeTabAndroid);
1118 private native Profile nativeGetProfileAndroid(long nativeTabAndroid);
1119 private native int nativeLoadUrl(long nativeTabAndroid, String url, String extraHeaders,
1120 byte[] postData, int transition, String referrerUrl, int referrerPolicy);
1121 private native int nativeGetSecurityLevel(long nativeTabAndroid);
1122 private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
1124 private native boolean nativePrint(long nativeTabAndroid);