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.DomDistillerFeedbackReporter;
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.WebContentsObserverAndroid;
36 import org.chromium.content_public.browser.WebContents;
37 import org.chromium.ui.base.Clipboard;
38 import org.chromium.ui.base.WindowAndroid;
40 import java.util.concurrent.atomic.AtomicInteger;
43 * The basic Java representation of a tab. Contains and manages a {@link ContentView}.
45 * Tab provides common functionality for ChromeShell Tab as well as Chrome on Android's
46 * tab. It is intended to be extended either on Java or both Java and C++, with ownership managed
49 * Extending just Java:
50 * - Just extend the class normally. Do not override initializeNative().
51 * Extending Java and C++:
52 * - Because of the inner-workings of JNI, the subclass is responsible for constructing the native
53 * subclass, which in turn constructs TabAndroid (the native counterpart to Tab), which in
54 * turn sets the native pointer for Tab. For destruction, subclasses in Java must clear
55 * their own native pointer reference, but Tab#destroy() will handle deleting the native
58 * Notes on {@link Tab#getId()}:
60 * Tabs are all generated using a static {@link AtomicInteger} which means they are unique across
61 * all {@link Activity}s running in the same {@link android.app.Application} process. Calling
62 * {@link Tab#incrementIdCounterTo(int)} will ensure new {@link Tab}s get ids greater than or equal
63 * to the parameter passed to that method. This should be used when doing things like loading
64 * persisted {@link Tab}s from disk on process start to ensure all new {@link Tab}s don't have id
66 * Some {@link Activity}s will not call this because they do not persist state, which means those
67 * ids can potentially conflict with the ones restored from persisted state depending on which
68 * {@link Activity} runs first on process start. If {@link Tab}s are ever shared across
69 * {@link Activity}s or mixed with {@link Tab}s from other {@link Activity}s conflicts can occur
70 * unless special care is taken to make sure {@link Tab#incrementIdCounterTo(int)} is called with
71 * the correct value across all affected {@link Activity}s.
73 public class Tab implements NavigationClient {
74 public static final int INVALID_TAB_ID = -1;
76 /** Used for automatically generating tab ids. */
77 private static final AtomicInteger sIdCounter = new AtomicInteger();
79 private long mNativeTabAndroid;
81 /** Unique id of this tab (within its container). */
82 private final int mId;
84 /** Whether or not this tab is an incognito tab. */
85 private final boolean mIncognito;
87 /** An Application {@link Context}. Unlike {@link #mContext}, this is the only one that is
88 * publicly exposed to help prevent leaking the {@link Activity}. */
89 private final Context mApplicationContext;
91 /** The {@link Context} used to create {@link View}s and other Android components. Unlike
92 * {@link #mApplicationContext}, this is not publicly exposed to help prevent leaking the
93 * {@link Activity}. */
94 private final Context mContext;
96 /** Gives {@link Tab} a way to interact with the Android window. */
97 private final WindowAndroid mWindowAndroid;
99 /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */
100 private NativePage mNativePage;
102 /** InfoBar container to show InfoBars for this tab. */
103 private InfoBarContainer mInfoBarContainer;
105 /** Manages app banners shown for this tab. */
106 private AppBannerManager mAppBannerManager;
108 /** The sync id of the Tab if session sync is enabled. */
112 * The {@link ContentViewCore} showing the current page or {@code null} if the tab is frozen.
114 private ContentViewCore mContentViewCore;
117 * A list of Tab observers. These are used to broadcast Tab events to listeners.
119 private final ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
121 // Content layer Observers and Delegates
122 private ContentViewClient mContentViewClient;
123 private WebContentsObserverAndroid mWebContentsObserver;
124 private VoiceSearchTabHelper mVoiceSearchTabHelper;
125 private TabChromeWebContentsDelegateAndroid mWebContentsDelegate;
126 private DomDistillerFeedbackReporter mDomDistillerFeedbackReporter;
129 * If this tab was opened from another tab, store the id of the tab that
130 * caused it to be opened so that we can activate it when this tab gets
133 private int mParentId = INVALID_TAB_ID;
136 * Whether the tab should be grouped with its parent tab.
138 private boolean mGroupedWithParent = true;
140 private boolean mIsClosing = false;
143 * A default {@link ChromeContextMenuItemDelegate} that supports some of the context menu
146 protected class TabChromeContextMenuItemDelegate
147 extends EmptyChromeContextMenuItemDelegate {
148 private final Clipboard mClipboard;
151 * Builds a {@link TabChromeContextMenuItemDelegate} instance.
153 public TabChromeContextMenuItemDelegate() {
154 mClipboard = new Clipboard(getApplicationContext());
158 public boolean isIncognito() {
163 public void onSaveToClipboard(String text, boolean isUrl) {
164 mClipboard.setText(text, text);
168 public void onSaveImageToClipboard(String url) {
169 mClipboard.setHTMLText("<img src=\"" + url + "\">", url, url);
174 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
175 * {@link TabObserver}s. Meant to be overridden by subclasses.
177 public class TabChromeWebContentsDelegateAndroid
178 extends ChromeWebContentsDelegateAndroid {
180 public void onLoadProgressChanged(int progress) {
181 for (TabObserver observer : mObservers) {
182 observer.onLoadProgressChanged(Tab.this, progress);
187 public void onLoadStarted() {
188 for (TabObserver observer : mObservers) observer.onLoadStarted(Tab.this);
192 public void onLoadStopped() {
193 for (TabObserver observer : mObservers) observer.onLoadStopped(Tab.this);
197 public void onUpdateUrl(String url) {
198 for (TabObserver observer : mObservers) observer.onUpdateUrl(Tab.this, url);
202 public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) {
203 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(
207 contentViewCore.cancelPendingReload();
212 contentViewCore.continuePendingReload();
215 Activity activity = (Activity) mContext;
216 warningDialog.show(activity.getFragmentManager(), null);
220 public void toggleFullscreenModeForTab(boolean enableFullscreen) {
221 for (TabObserver observer : mObservers) {
222 observer.onToggleFullscreenMode(Tab.this, enableFullscreen);
227 public void navigationStateChanged(int flags) {
228 if ((flags & INVALIDATE_TYPE_TITLE) != 0) {
229 for (TabObserver observer : mObservers) observer.onTitleUpdated(Tab.this);
231 if ((flags & INVALIDATE_TYPE_URL) != 0) {
232 for (TabObserver observer : mObservers) observer.onUrlUpdated(Tab.this);
237 public void visibleSSLStateChanged() {
238 for (TabObserver observer : mObservers) observer.onSSLStateUpdated(Tab.this);
242 private class TabContextMenuPopulator extends ContextMenuPopulatorWrapper {
243 public TabContextMenuPopulator(ContextMenuPopulator populator) {
248 public void buildContextMenu(ContextMenu menu, Context context, ContextMenuParams params) {
249 super.buildContextMenu(menu, context, params);
250 for (TabObserver observer : mObservers) observer.onContextMenuShown(Tab.this, menu);
254 private class TabWebContentsObserverAndroid extends WebContentsObserverAndroid {
255 public TabWebContentsObserverAndroid(ContentViewCore contentViewCore) {
256 super(contentViewCore);
260 public void navigationEntryCommitted() {
261 if (getNativePage() != null) {
262 pushNativePageStateToNavigationEntry();
267 public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
268 String description, String failingUrl) {
269 for (TabObserver observer : mObservers) {
270 observer.onDidFailLoad(Tab.this, isProvisionalLoad, isMainFrame, errorCode,
271 description, failingUrl);
276 public void didStartProvisionalLoadForFrame(long frameId, long parentFrameId,
277 boolean isMainFrame, String validatedUrl, boolean isErrorPage,
278 boolean isIframeSrcdoc) {
279 for (TabObserver observer : mObservers) {
280 observer.onDidStartProvisionalLoadForFrame(Tab.this, frameId, parentFrameId,
281 isMainFrame, validatedUrl, isErrorPage, isIframeSrcdoc);
286 public void didChangeBrandColor(int color) {
287 for (TabObserver observer : mObservers) {
288 observer.onDidChangeBrandColor(color);
294 * Creates an instance of a {@link Tab} with no id.
295 * @param incognito Whether or not this tab is incognito.
296 * @param context An instance of a {@link Context}.
297 * @param window An instance of a {@link WindowAndroid}.
299 public Tab(boolean incognito, Context context, WindowAndroid window) {
300 this(INVALID_TAB_ID, incognito, context, window);
304 * Creates an instance of a {@link Tab}.
305 * @param id The id this tab should be identified with.
306 * @param incognito Whether or not this tab is incognito.
307 * @param context An instance of a {@link Context}.
308 * @param window An instance of a {@link WindowAndroid}.
310 public Tab(int id, boolean incognito, Context context, WindowAndroid window) {
311 this(INVALID_TAB_ID, id, incognito, context, window);
315 * Creates an instance of a {@link Tab}.
316 * @param id The id this tab should be identified with.
317 * @param parentId The id id of the tab that caused this tab to be opened.
318 * @param incognito Whether or not this tab is incognito.
319 * @param context An instance of a {@link Context}.
320 * @param window An instance of a {@link WindowAndroid}.
322 public Tab(int id, int parentId, boolean incognito, Context context, WindowAndroid window) {
323 // We need a valid Activity Context to build the ContentView with.
324 assert context == null || context instanceof Activity;
326 mId = generateValidId(id);
327 mParentId = parentId;
328 mIncognito = incognito;
329 // TODO(dtrainor): Only store application context here.
331 mApplicationContext = context != null ? context.getApplicationContext() : null;
332 mWindowAndroid = window;
336 * Adds a {@link TabObserver} to be notified on {@link Tab} changes.
337 * @param observer The {@link TabObserver} to add.
339 public void addObserver(TabObserver observer) {
340 mObservers.addObserver(observer);
344 * Removes a {@link TabObserver}.
345 * @param observer The {@link TabObserver} to remove.
347 public void removeObserver(TabObserver observer) {
348 mObservers.removeObserver(observer);
352 * @return Whether or not this tab has a previous navigation entry.
354 public boolean canGoBack() {
355 return mContentViewCore != null && mContentViewCore.canGoBack();
359 * @return Whether or not this tab has a navigation entry after the current one.
361 public boolean canGoForward() {
362 return mContentViewCore != null && mContentViewCore.canGoForward();
366 * Goes to the navigation entry before the current one.
368 public void goBack() {
369 if (mContentViewCore != null) mContentViewCore.goBack();
373 * Goes to the navigation entry after the current one.
375 public void goForward() {
376 if (mContentViewCore != null) mContentViewCore.goForward();
380 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
381 if (mContentViewCore != null) {
382 return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
384 return new NavigationHistory();
389 public void goToNavigationIndex(int index) {
390 if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
394 * Loads the current navigation if there is a pending lazy load (after tab restore).
396 public void loadIfNecessary() {
397 if (mContentViewCore != null) mContentViewCore.loadIfNecessary();
401 * Requests the current navigation to be loaded upon the next call to loadIfNecessary().
403 protected void requestRestoreLoad() {
404 if (mContentViewCore != null) mContentViewCore.requestRestoreLoad();
408 * Causes this tab to navigate to the specified URL.
409 * @param params parameters describing the url load. Note that it is important to set correct
410 * page transition as it is used for ranking URLs in the history so the omnibox
411 * can report suggestions correctly.
412 * @return FULL_PRERENDERED_PAGE_LOAD or PARTIAL_PRERENDERED_PAGE_LOAD if the page has been
413 * prerendered. DEFAULT_PAGE_LOAD if it had not.
415 public int loadUrl(LoadUrlParams params) {
418 // We load the URL from the tab rather than directly from the ContentView so the tab has a
419 // chance of using a prerenderer page is any.
420 int loadType = nativeLoadUrl(
423 params.getVerbatimHeaders(),
424 params.getPostData(),
425 params.getTransitionType(),
426 params.getReferrer() != null ? params.getReferrer().getUrl() : null,
427 // Policy will be ignored for null referrer url, 0 is just a placeholder.
428 // TODO(ppi): Should we pass Referrer jobject and add JNI methods to read it from
430 params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
431 params.getIsRendererInitiated());
435 for (TabObserver observer : mObservers) {
436 observer.onLoadUrl(this, params.getUrl(), loadType);
442 * @return Whether or not the {@link Tab} is currently showing an interstitial page, such as
445 public boolean isShowingInterstitialPage() {
446 ContentViewCore contentViewCore = getContentViewCore();
447 return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
451 * @return Whether or not the tab has something valid to render.
453 public boolean isReady() {
454 return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
458 * @return The {@link View} displaying the current page in the tab. This might be a
459 * native view or a placeholder view for content rendered by the compositor.
460 * This can be {@code null}, if the tab is frozen or being initialized or destroyed.
462 public View getView() {
463 return mNativePage != null ? mNativePage.getView() :
464 (mContentViewCore != null ? mContentViewCore.getContainerView() : null);
468 * @return The width of the content of this tab. Can be 0 if there is no content.
470 public int getWidth() {
471 View view = getView();
472 return view != null ? view.getWidth() : 0;
476 * @return The height of the content of this tab. Can be 0 if there is no content.
478 public int getHeight() {
479 View view = getView();
480 return view != null ? view.getHeight() : 0;
484 * @return The application {@link Context} associated with this tab.
486 protected Context getApplicationContext() {
487 return mApplicationContext;
491 * @return The infobar container.
493 public final InfoBarContainer getInfoBarContainer() {
494 return mInfoBarContainer;
498 * Create an {@code AutoLoginProcessor} to decide how to handle login
501 protected AutoLoginProcessor createAutoLoginProcessor() {
502 return new AutoLoginProcessor() {
504 public void processAutoLoginResult(String accountName, String authToken,
505 boolean success, String result) {
511 * Prints the current page.
513 * @return Whether the printing process is started successfully.
515 public boolean print() {
516 assert mNativeTabAndroid != 0;
517 return nativePrint(mNativeTabAndroid);
521 * Reloads the current page content.
523 public void reload() {
524 // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
525 if (mContentViewCore != null) mContentViewCore.reload(true);
529 * Reloads the current page content.
530 * This version ignores the cache and reloads from the network.
532 public void reloadIgnoringCache() {
533 if (mContentViewCore != null) mContentViewCore.reloadIgnoringCache(true);
536 /** Stop the current navigation. */
537 public void stopLoading() {
538 if (mContentViewCore != null) mContentViewCore.stopLoading();
542 * @return The background color of the tab.
544 public int getBackgroundColor() {
545 if (mNativePage != null) return mNativePage.getBackgroundColor();
546 if (mContentViewCore != null) return mContentViewCore.getBackgroundColor();
551 * @return The web contents associated with this tab.
553 public WebContents getWebContents() {
554 if (mNativeTabAndroid == 0) return null;
555 return nativeGetWebContents(mNativeTabAndroid);
559 * @return The profile associated with this tab.
561 public Profile getProfile() {
562 if (mNativeTabAndroid == 0) return null;
563 return nativeGetProfileAndroid(mNativeTabAndroid);
567 * For more information about the uniqueness of {@link #getId()} see comments on {@link Tab}.
569 * @return The id representing this tab.
577 * @return Whether or not this tab is incognito.
579 public boolean isIncognito() {
584 * @return The {@link ContentViewCore} associated with the current page, or {@code null} if
585 * there is no current page or the current page is displayed using a native view.
587 public ContentViewCore getContentViewCore() {
588 return mNativePage == null ? mContentViewCore : null;
592 * @return The {@link NativePage} associated with the current page, or {@code null} if there is
593 * no current page or the current page is displayed using something besides
594 * {@link NativePage}.
596 public NativePage getNativePage() {
601 * @return Whether or not the {@link Tab} represents a {@link NativePage}.
603 public boolean isNativePage() {
604 return mNativePage != null;
608 * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
609 * currently loaded page.
610 * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one.
611 * @param reloadOnChange Reload the page if the user agent has changed.
613 public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
614 if (mContentViewCore != null) {
615 mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
620 * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
622 public boolean getUseDesktopUserAgent() {
623 return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
627 * @return The current {ToolbarModelSecurityLevel} for the tab.
629 public int getSecurityLevel() {
630 if (mNativeTabAndroid == 0) return ToolbarModelSecurityLevel.NONE;
631 return nativeGetSecurityLevel(mNativeTabAndroid);
635 * @return The sync id of the tab if session sync is enabled, {@code 0} otherwise.
638 protected int getSyncId() {
643 * @param syncId The sync id of the tab if session sync is enabled.
646 protected void setSyncId(int syncId) {
651 * @return An {@link ObserverList.RewindableIterator} instance that points to all of
652 * the current {@link TabObserver}s on this class. Note that calling
653 * {@link java.util.Iterator#remove()} will throw an
654 * {@link UnsupportedOperationException}.
656 protected ObserverList.RewindableIterator<TabObserver> getTabObservers() {
657 return mObservers.rewindableIterator();
661 * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
662 * associated with the current page. There can still be a {@link ContentViewClient}
663 * even when there is no {@link ContentViewCore}.
665 protected ContentViewClient getContentViewClient() {
666 return mContentViewClient;
670 * @param client The {@link ContentViewClient} to be bound to any current or new
671 * {@link ContentViewCore}s associated with this {@link Tab}.
673 protected void setContentViewClient(ContentViewClient client) {
674 if (mContentViewClient == client) return;
676 ContentViewClient oldClient = mContentViewClient;
677 mContentViewClient = client;
679 if (mContentViewCore == null) return;
681 if (mContentViewClient != null) {
682 mContentViewCore.setContentViewClient(mContentViewClient);
683 } else if (oldClient != null) {
684 // We can't set a null client, but we should clear references to the last one.
685 mContentViewCore.setContentViewClient(new ContentViewClient());
690 * Triggers the showing logic for the view backing this tab.
692 protected void show() {
693 if (mContentViewCore != null) mContentViewCore.onShow();
697 * Triggers the hiding logic for the view backing the tab.
699 protected void hide() {
700 if (mContentViewCore != null) mContentViewCore.onHide();
704 * Shows the given {@code nativePage} if it's not already showing.
705 * @param nativePage The {@link NativePage} to show.
707 protected void showNativePage(NativePage nativePage) {
708 if (mNativePage == nativePage) return;
709 NativePage previousNativePage = mNativePage;
710 mNativePage = nativePage;
711 pushNativePageStateToNavigationEntry();
712 for (TabObserver observer : mObservers) observer.onContentChanged(this);
713 destroyNativePageInternal(previousNativePage);
717 * Replaces the current NativePage with a empty stand-in for a NativePage. This can be used
718 * to reduce memory pressure.
720 public void freezeNativePage() {
721 if (mNativePage == null || mNativePage instanceof FrozenNativePage) return;
722 assert mNativePage.getView().getParent() == null : "Cannot freeze visible native page";
723 mNativePage = FrozenNativePage.freeze(mNativePage);
727 * Hides the current {@link NativePage}, if any, and shows the {@link ContentViewCore}'s view.
729 protected void showRenderedPage() {
730 if (mNativePage == null) return;
731 NativePage previousNativePage = mNativePage;
733 for (TabObserver observer : mObservers) observer.onContentChanged(this);
734 destroyNativePageInternal(previousNativePage);
738 * Initializes this {@link Tab}.
740 public void initialize() {
745 * Builds the native counterpart to this class. Meant to be overridden by subclasses to build
746 * subclass native counterparts instead. Subclasses should not call this via super and instead
747 * rely on the native class to create the JNI association.
749 protected void initializeNative() {
750 if (mNativeTabAndroid == 0) nativeInit();
751 assert mNativeTabAndroid != 0;
755 * A helper method to initialize a {@link ContentViewCore} without any
756 * native WebContents pointer.
758 protected final void initContentViewCore() {
759 initContentViewCore(ContentViewUtil.createNativeWebContents(mIncognito));
763 * Creates and initializes the {@link ContentViewCore}.
765 * @param nativeWebContents The native web contents pointer.
767 protected void initContentViewCore(long nativeWebContents) {
768 ContentViewCore cvc = new ContentViewCore(mContext);
769 ContentView cv = ContentView.newInstance(mContext, cvc);
770 cvc.initialize(cv, cv, nativeWebContents, getWindowAndroid());
771 setContentViewCore(cvc);
775 * Completes the {@link ContentViewCore} specific initialization around a native WebContents
776 * pointer. {@link #getNativePage()} will still return the {@link NativePage} if there is one.
777 * All initialization that needs to reoccur after a web contents swap should be added here.
779 * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
780 * state as this tab this call will fail.
782 * @param cvc The content view core that needs to be set as active view for the tab.
784 protected void setContentViewCore(ContentViewCore cvc) {
785 NativePage previousNativePage = mNativePage;
787 destroyNativePageInternal(previousNativePage);
789 mContentViewCore = cvc;
791 mWebContentsDelegate = createWebContentsDelegate();
792 mWebContentsObserver = new TabWebContentsObserverAndroid(mContentViewCore);
793 mVoiceSearchTabHelper = new VoiceSearchTabHelper(mContentViewCore);
795 if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
797 assert mNativeTabAndroid != 0;
798 nativeInitWebContents(
799 mNativeTabAndroid, mIncognito, mContentViewCore, mWebContentsDelegate,
800 new TabContextMenuPopulator(createContextMenuPopulator()));
802 // In the case where restoring a Tab or showing a prerendered one we already have a
803 // valid infobar container, no need to recreate one.
804 if (mInfoBarContainer == null) {
805 // The InfoBarContainer needs to be created after the ContentView has been natively
807 WebContents webContents = mContentViewCore.getWebContents();
808 mInfoBarContainer = new InfoBarContainer(
809 (Activity) mContext, createAutoLoginProcessor(), getId(),
810 mContentViewCore.getContainerView(), webContents);
812 mInfoBarContainer.onParentViewChanged(getId(), mContentViewCore.getContainerView());
815 if (AppBannerManager.isEnabled() && mAppBannerManager == null) {
816 mAppBannerManager = new AppBannerManager(this);
819 if (DomDistillerFeedbackReporter.isEnabled() && mDomDistillerFeedbackReporter == null) {
820 mDomDistillerFeedbackReporter = new DomDistillerFeedbackReporter(this);
823 for (TabObserver observer : mObservers) observer.onContentChanged(this);
827 * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentViewCore}
828 * currently associated with this {@link Tab}. This also destroys the native counterpart
829 * to this class, which means that all subclasses should erase their native pointers after
830 * this method is called. Once this call is made this {@link Tab} should no longer be used.
832 public void destroy() {
833 for (TabObserver observer : mObservers) observer.onDestroyed(this);
836 NativePage currentNativePage = mNativePage;
838 destroyNativePageInternal(currentNativePage);
839 destroyContentViewCore(true);
841 // Destroys the native tab after destroying the ContentView but before destroying the
842 // InfoBarContainer. The native tab should be destroyed before the infobar container as
843 // destroying the native tab cleanups up any remaining infobars. The infobar container
844 // expects all infobars to be cleaned up before its own destruction.
845 assert mNativeTabAndroid != 0;
846 nativeDestroy(mNativeTabAndroid);
847 assert mNativeTabAndroid == 0;
849 if (mInfoBarContainer != null) {
850 mInfoBarContainer.destroy();
851 mInfoBarContainer = null;
856 * @return Whether or not this Tab has a live native component.
858 public boolean isInitialized() {
859 return mNativeTabAndroid != 0;
863 * @return The url associated with the tab.
866 public String getUrl() {
867 return mContentViewCore != null ? mContentViewCore.getUrl() : "";
871 * @return The tab title.
874 public String getTitle() {
875 if (mNativePage != null) return mNativePage.getTitle();
876 if (mContentViewCore != null) return mContentViewCore.getTitle();
881 * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon
882 * is specified or it requires the default favicon.
883 * TODO(bauerb): Upstream implementation.
885 public Bitmap getFavicon() {
890 * Loads the tab if it's not loaded (e.g. because it was killed in background).
891 * @return true iff tab load was triggered
894 public boolean loadIfNeeded() {
899 * @return Whether or not the tab is in the closing process.
901 public boolean isClosing() {
906 * @param closing Whether or not the tab is in the closing process.
908 public void setClosing(boolean closing) {
909 mIsClosing = closing;
913 * @return The id of the tab that caused this tab to be opened.
915 public int getParentId() {
920 * @return Whether the tab should be grouped with its parent tab (true by default).
922 public boolean isGroupedWithParent() {
923 return mGroupedWithParent;
927 * Sets whether the tab should be grouped with its parent tab.
929 * @param groupedWithParent The new value.
930 * @see #isGroupedWithParent
932 public void setGroupedWithParent(boolean groupedWithParent) {
933 mGroupedWithParent = groupedWithParent;
936 private void destroyNativePageInternal(NativePage nativePage) {
937 if (nativePage == null) return;
938 assert nativePage != mNativePage : "Attempting to destroy active page.";
940 nativePage.destroy();
944 * Destroys the current {@link ContentViewCore}.
945 * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
947 protected final void destroyContentViewCore(boolean deleteNativeWebContents) {
948 if (mContentViewCore == null) return;
950 destroyContentViewCoreInternal(mContentViewCore);
952 if (mInfoBarContainer != null && mInfoBarContainer.getParent() != null) {
953 mInfoBarContainer.removeFromParentView();
955 mContentViewCore.destroy();
957 mContentViewCore = null;
958 mWebContentsDelegate = null;
959 mWebContentsObserver = null;
960 mVoiceSearchTabHelper = null;
962 assert mNativeTabAndroid != 0;
963 nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
967 * Gives subclasses the chance to clean up some state associated with this
968 * {@link ContentViewCore}. This is because {@link #getContentViewCore()}
969 * can return {@code null} if a {@link NativePage} is showing.
971 * @param cvc The {@link ContentViewCore} that should have associated state
974 protected void destroyContentViewCoreInternal(ContentViewCore cvc) {
978 * A helper method to allow subclasses to build their own delegate.
979 * @return An instance of a {@link TabChromeWebContentsDelegateAndroid}.
981 protected TabChromeWebContentsDelegateAndroid createWebContentsDelegate() {
982 return new TabChromeWebContentsDelegateAndroid();
986 * A helper method to allow subclasses to build their own menu populator.
987 * @return An instance of a {@link ContextMenuPopulator}.
989 protected ContextMenuPopulator createContextMenuPopulator() {
990 return new ChromeContextMenuPopulator(new TabChromeContextMenuItemDelegate());
994 * @return The {@link WindowAndroid} associated with this {@link Tab}.
996 public WindowAndroid getWindowAndroid() {
997 return mWindowAndroid;
1001 * @return The current {@link TabChromeWebContentsDelegateAndroid} instance.
1003 protected TabChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
1004 return mWebContentsDelegate;
1008 * Called when the favicon of the content this tab represents changes.
1011 protected void onFaviconUpdated() {
1012 for (TabObserver observer : mObservers) observer.onFaviconUpdated(this);
1016 * Called when the navigation entry containing the historyitem changed,
1017 * for example because of a scroll offset or form field change.
1020 protected void onNavEntryChanged() {
1024 * @return The native pointer representing the native side of this {@link Tab} object.
1027 protected long getNativePtr() {
1028 return mNativeTabAndroid;
1031 /** This is currently called when committing a pre-rendered page. */
1033 private void swapWebContents(
1034 long newWebContents, boolean didStartLoad, boolean didFinishLoad) {
1035 ContentViewCore cvc = new ContentViewCore(mContext);
1036 ContentView cv = ContentView.newInstance(mContext, cvc);
1037 cvc.initialize(cv, cv, newWebContents, getWindowAndroid());
1038 swapContentViewCore(cvc, false, didStartLoad, didFinishLoad);
1042 * Called to swap out the current view with the one passed in.
1044 * @param newContentViewCore The content view that should be swapped into the tab.
1045 * @param deleteOldNativeWebContents Whether to delete the native web
1046 * contents of old view.
1047 * @param didStartLoad Whether
1048 * WebContentsObserver::DidStartProvisionalLoadForFrame() has
1049 * already been called.
1050 * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has
1051 * already been called.
1053 protected void swapContentViewCore(ContentViewCore newContentViewCore,
1054 boolean deleteOldNativeWebContents, boolean didStartLoad, boolean didFinishLoad) {
1055 int originalWidth = 0;
1056 int originalHeight = 0;
1057 if (mContentViewCore != null) {
1058 originalWidth = mContentViewCore.getViewportWidthPix();
1059 originalHeight = mContentViewCore.getViewportHeightPix();
1060 mContentViewCore.onHide();
1062 destroyContentViewCore(deleteOldNativeWebContents);
1063 NativePage previousNativePage = mNativePage;
1065 setContentViewCore(newContentViewCore);
1066 // Size of the new ContentViewCore is zero at this point. If we don't call onSizeChanged(),
1067 // next onShow() call would send a resize message with the current ContentViewCore size
1068 // (zero) to the renderer process, although the new size will be set soon.
1069 // However, this size fluttering may confuse Blink and rendered result can be broken
1070 // (see http://crbug.com/340987).
1071 mContentViewCore.onSizeChanged(originalWidth, originalHeight, 0, 0);
1072 mContentViewCore.onShow();
1073 mContentViewCore.attachImeAdapter();
1074 destroyNativePageInternal(previousNativePage);
1075 for (TabObserver observer : mObservers) {
1076 observer.onWebContentsSwapped(this, didStartLoad, didFinishLoad);
1081 private void clearNativePtr() {
1082 assert mNativeTabAndroid != 0;
1083 mNativeTabAndroid = 0;
1087 private void setNativePtr(long nativePtr) {
1088 assert mNativeTabAndroid == 0;
1089 mNativeTabAndroid = nativePtr;
1093 private long getNativeInfoBarContainer() {
1094 return getInfoBarContainer().getNative();
1098 * Validates {@code id} and increments the internal counter to make sure future ids don't
1100 * @param id The current id. Maybe {@link #INVALID_TAB_ID}.
1101 * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
1103 private static int generateValidId(int id) {
1104 if (id == INVALID_TAB_ID) id = generateNextId();
1105 incrementIdCounterTo(id + 1);
1111 * @return An unused id.
1113 private static int generateNextId() {
1114 return sIdCounter.getAndIncrement();
1117 private void pushNativePageStateToNavigationEntry() {
1118 assert mNativeTabAndroid != 0 && getNativePage() != null;
1119 nativeSetActiveNavigationEntryTitleForUrl(mNativeTabAndroid, getNativePage().getUrl(),
1120 getNativePage().getTitle());
1124 * Ensures the counter is at least as high as the specified value. The counter should always
1125 * point to an unused ID (which will be handed out next time a request comes in). Exposed so
1126 * that anything externally loading tabs and ids can set enforce new tabs start at the correct
1128 * TODO(aurimas): Investigate reducing the visiblity of this method.
1129 * @param id The minimum id we should hand out to the next new tab.
1131 public static void incrementIdCounterTo(int id) {
1132 int diff = id - sIdCounter.get();
1133 if (diff <= 0) return;
1134 // It's possible idCounter has been incremented between the get above and the add below
1135 // but that's OK, because in the worst case we'll overly increment idCounter.
1136 sIdCounter.addAndGet(diff);
1139 private native void nativeInit();
1140 private native void nativeDestroy(long nativeTabAndroid);
1141 private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito,
1142 ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate,
1143 ContextMenuPopulator contextMenuPopulator);
1144 private native void nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative);
1145 private native WebContents nativeGetWebContents(long nativeTabAndroid);
1146 private native Profile nativeGetProfileAndroid(long nativeTabAndroid);
1147 private native int nativeLoadUrl(long nativeTabAndroid, String url, String extraHeaders,
1148 byte[] postData, int transition, String referrerUrl, int referrerPolicy,
1149 boolean isRendererInitiated);
1150 private native int nativeGetSecurityLevel(long nativeTabAndroid);
1151 private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
1153 private native boolean nativePrint(long nativeTabAndroid);